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::{
25    io::FileLoadError,
26    log::Log,
27    type_traits::prelude::*,
28    visitor::{prelude::*, VisitError, Visitor},
29    ImmutableString, Uuid,
30};
31use fyrox_resource::{
32    io::ResourceIo,
33    loader::{BoxedLoaderFuture, LoaderPayload, ResourceLoader},
34    manager::ResourceManager,
35    state::LoadError,
36    Resource, ResourceData,
37};
38use std::{
39    error::Error,
40    fmt::{Display, Formatter},
41    path::{Path, PathBuf},
42    sync::Arc,
43};
44
45/// An error that may occur during tile set resource loading.
46#[derive(Debug)]
47pub enum StyleResourceError {
48    /// An i/o error has occurred.
49    Io(FileLoadError),
50
51    /// An error that may occur due to version incompatibilities.
52    Visit(VisitError),
53}
54
55impl Display for StyleResourceError {
56    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57        match self {
58            Self::Io(v) => {
59                write!(f, "A file load error has occurred {v:?}")
60            }
61            Self::Visit(v) => {
62                write!(
63                    f,
64                    "An error that may occur due to version incompatibilities. {v:?}"
65                )
66            }
67        }
68    }
69}
70
71impl From<FileLoadError> for StyleResourceError {
72    fn from(e: FileLoadError) -> Self {
73        Self::Io(e)
74    }
75}
76
77impl From<VisitError> for StyleResourceError {
78    fn from(e: VisitError) -> Self {
79        Self::Visit(e)
80    }
81}
82
83impl ResourceData for Style {
84    fn type_uuid(&self) -> Uuid {
85        <Self as TypeUuidProvider>::type_uuid()
86    }
87
88    fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
89        let mut visitor = Visitor::new();
90        self.visit("Style", &mut visitor)?;
91        visitor.save_binary(path)?;
92        Ok(())
93    }
94
95    fn can_be_saved(&self) -> bool {
96        true
97    }
98}
99
100/// A loader for style resource.
101pub struct StyleLoader {
102    /// Resource manager handle.
103    pub resource_manager: ResourceManager,
104}
105
106impl ResourceLoader for StyleLoader {
107    fn extensions(&self) -> &[&str] {
108        &["style"]
109    }
110
111    fn data_type_uuid(&self) -> Uuid {
112        <Style as TypeUuidProvider>::type_uuid()
113    }
114
115    fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
116        let resource_manager = self.resource_manager.clone();
117        Box::pin(async move {
118            let tile_set = Style::from_file(&path, io.as_ref(), resource_manager)
119                .await
120                .map_err(LoadError::new)?;
121            Ok(LoaderPayload::new(tile_set))
122        })
123    }
124}
125
126/// Style resource.
127pub type StyleResource = Resource<Style>;
128
129/// Extension methods for [`StyleResource`].
130pub trait StyleResourceExt {
131    /// Same as [`Style::set`].
132    fn set(&self, name: impl Into<ImmutableString>, property: impl Into<StyleProperty>);
133
134    /// Same as [`Style::get`].
135    fn get<P>(&self, name: impl Into<ImmutableString>) -> Option<P>
136    where
137        StyleProperty: IntoPrimitive<P>;
138
139    /// Same as [`Style::get_or`].
140    fn get_or<P>(&self, name: impl Into<ImmutableString>, default: P) -> P
141    where
142        StyleProperty: IntoPrimitive<P>;
143
144    /// Same as [`Style::get_or_default`].
145    fn get_or_default<P>(&self, name: impl Into<ImmutableString>) -> P
146    where
147        P: Default,
148        StyleProperty: IntoPrimitive<P>;
149
150    /// Same as [`Style::property`].
151    fn property<P>(&self, name: impl Into<ImmutableString>) -> StyledProperty<P>
152    where
153        P: Default,
154        StyleProperty: IntoPrimitive<P>;
155}
156
157impl StyleResourceExt for StyleResource {
158    fn set(&self, name: impl Into<ImmutableString>, property: impl Into<StyleProperty>) {
159        let mut state = self.state();
160        if let Some(data) = state.data() {
161            data.set(name, property);
162        } else {
163            Log::err("Unable to set style property, because the resource is invalid!")
164        }
165    }
166
167    fn get<P>(&self, name: impl Into<ImmutableString>) -> Option<P>
168    where
169        StyleProperty: IntoPrimitive<P>,
170    {
171        let state = self.state();
172        if let Some(data) = state.data_ref() {
173            data.get(name)
174        } else {
175            Log::err("Unable to get style property, because the resource is invalid!");
176            None
177        }
178    }
179
180    fn get_or<P>(&self, name: impl Into<ImmutableString>, default: P) -> P
181    where
182        StyleProperty: IntoPrimitive<P>,
183    {
184        let state = self.state();
185        if let Some(data) = state.data_ref() {
186            data.get_or(name, default)
187        } else {
188            Log::err("Unable to get style property, because the resource is invalid!");
189            default
190        }
191    }
192
193    fn get_or_default<P>(&self, name: impl Into<ImmutableString>) -> P
194    where
195        P: Default,
196        StyleProperty: IntoPrimitive<P>,
197    {
198        let state = self.state();
199        if let Some(data) = state.data_ref() {
200            data.get_or_default(name)
201        } else {
202            Log::err("Unable to get style property, because the resource is invalid!");
203            P::default()
204        }
205    }
206
207    fn property<P>(&self, name: impl Into<ImmutableString>) -> StyledProperty<P>
208    where
209        P: Default,
210        StyleProperty: IntoPrimitive<P>,
211    {
212        let state = self.state();
213        if let Some(data) = state.data_ref() {
214            data.property(name)
215        } else {
216            Log::err("Unable to get style property, because the resource is invalid!");
217            Default::default()
218        }
219    }
220}