Skip to main content

fyrox_ui/style/
resource.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Contains all types related to shared style resource.
22
23use crate::style::{IntoPrimitive, Style, StyleProperty, StyledProperty};
24use fyrox_core::visitor::error::VisitError;
25use fyrox_core::{
26    io::FileError,
27    log::Log,
28    type_traits::prelude::*,
29    visitor::{prelude::*, Visitor},
30    ImmutableString, Uuid,
31};
32use fyrox_resource::{
33    io::ResourceIo,
34    loader::{BoxedLoaderFuture, LoaderPayload, ResourceLoader},
35    manager::ResourceManager,
36    state::LoadError,
37    Resource, ResourceData,
38};
39use std::{
40    error::Error,
41    fmt::{Display, Formatter},
42    path::{Path, PathBuf},
43    sync::Arc,
44};
45
46/// An error that may occur during tile set resource loading.
47#[derive(Debug)]
48pub enum StyleResourceError {
49    /// An i/o error has occurred.
50    Io(FileError),
51
52    /// An error that may occur due to version incompatibilities.
53    Visit(VisitError),
54}
55
56impl Display for StyleResourceError {
57    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58        match self {
59            Self::Io(v) => {
60                write!(f, "A file load error has occurred {v:?}")
61            }
62            Self::Visit(v) => {
63                write!(
64                    f,
65                    "An error that may occur due to version incompatibilities. {v:?}"
66                )
67            }
68        }
69    }
70}
71
72impl From<FileError> for StyleResourceError {
73    fn from(e: FileError) -> Self {
74        Self::Io(e)
75    }
76}
77
78impl From<VisitError> for StyleResourceError {
79    fn from(e: VisitError) -> Self {
80        Self::Visit(e)
81    }
82}
83
84impl ResourceData for Style {
85    fn type_uuid(&self) -> Uuid {
86        <Self as TypeUuidProvider>::type_uuid()
87    }
88
89    fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
90        let mut visitor = Visitor::new();
91        self.visit("Style", &mut visitor)?;
92        visitor.save_ascii_to_file(path)?;
93        Ok(())
94    }
95
96    fn can_be_saved(&self) -> bool {
97        true
98    }
99
100    fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
101        Some(Box::new(self.clone()))
102    }
103}
104
105/// A loader for style resource.
106pub struct StyleLoader {
107    /// Resource manager handle.
108    pub resource_manager: ResourceManager,
109}
110
111impl ResourceLoader for StyleLoader {
112    fn extensions(&self) -> &[&str] {
113        &["style"]
114    }
115
116    fn is_native_extension(&self, ext: &str) -> bool {
117        fyrox_core::cmp_strings_case_insensitive(ext, "style")
118    }
119
120    fn data_type_uuid(&self) -> Uuid {
121        <Style as TypeUuidProvider>::type_uuid()
122    }
123
124    fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
125        let resource_manager = self.resource_manager.clone();
126        Box::pin(async move {
127            let tile_set = Style::from_file(&path, io.as_ref(), resource_manager)
128                .await
129                .map_err(LoadError::new)?;
130            Ok(LoaderPayload::new(tile_set))
131        })
132    }
133}
134
135/// Style resource.
136pub type StyleResource = Resource<Style>;
137
138/// Extension methods for [`StyleResource`].
139pub trait StyleResourceExt {
140    /// Same as [`Style::set`].
141    fn set(&self, name: impl Into<ImmutableString>, property: impl Into<StyleProperty>);
142
143    /// Same as [`Style::get`].
144    fn get<P>(&self, name: impl Into<ImmutableString>) -> Option<P>
145    where
146        StyleProperty: IntoPrimitive<P>;
147
148    /// Same as [`Style::get_or`].
149    fn get_or<P>(&self, name: impl Into<ImmutableString>, default: P) -> P
150    where
151        StyleProperty: IntoPrimitive<P>;
152
153    /// Same as [`Style::get_or_default`].
154    fn get_or_default<P>(&self, name: impl Into<ImmutableString>) -> P
155    where
156        P: Default,
157        StyleProperty: IntoPrimitive<P>;
158
159    /// Same as [`Style::property`].
160    fn property<P>(&self, name: impl Into<ImmutableString>) -> StyledProperty<P>
161    where
162        P: Default,
163        StyleProperty: IntoPrimitive<P>;
164}
165
166impl StyleResourceExt for StyleResource {
167    fn set(&self, name: impl Into<ImmutableString>, property: impl Into<StyleProperty>) {
168        let mut state = self.state();
169        if let Some(data) = state.data() {
170            data.set(name, property);
171        } else {
172            Log::err("Unable to set style property, because the resource is invalid!")
173        }
174    }
175
176    fn get<P>(&self, name: impl Into<ImmutableString>) -> Option<P>
177    where
178        StyleProperty: IntoPrimitive<P>,
179    {
180        let state = self.state();
181        if let Some(data) = state.data_ref() {
182            data.get(name)
183        } else {
184            Log::err("Unable to get style property, because the resource is invalid!");
185            None
186        }
187    }
188
189    fn get_or<P>(&self, name: impl Into<ImmutableString>, default: P) -> P
190    where
191        StyleProperty: IntoPrimitive<P>,
192    {
193        let state = self.state();
194        if let Some(data) = state.data_ref() {
195            data.get_or(name, default)
196        } else {
197            Log::err("Unable to get style property, because the resource is invalid!");
198            default
199        }
200    }
201
202    fn get_or_default<P>(&self, name: impl Into<ImmutableString>) -> P
203    where
204        P: Default,
205        StyleProperty: IntoPrimitive<P>,
206    {
207        let state = self.state();
208        if let Some(data) = state.data_ref() {
209            data.get_or_default(name)
210        } else {
211            Log::err("Unable to get style property, because the resource is invalid!");
212            P::default()
213        }
214    }
215
216    fn property<P>(&self, name: impl Into<ImmutableString>) -> StyledProperty<P>
217    where
218        P: Default,
219        StyleProperty: IntoPrimitive<P>,
220    {
221        let state = self.state();
222        if let Some(data) = state.data_ref() {
223            data.property(name)
224        } else {
225            Log::err("Unable to get style property, because the resource is invalid!");
226            Default::default()
227        }
228    }
229}