Skip to main content

fyrox_ui/style/
mod.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#![warn(missing_docs)]
22
23//! Style allows to change the visual appearance of widgets in a centralized manner. It can be considered
24//! as a storage for properties that define visual appearance. See [`Style`] docs for more info
25//! and usage examples.
26
27pub mod resource;
28
29use crate::{
30    brush::Brush,
31    button::Button,
32    check_box::CheckBox,
33    core::{
34        color::Color, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*,
35        ImmutableString, Uuid,
36    },
37    dropdown_list::DropdownList,
38    style::resource::{StyleResource, StyleResourceError, StyleResourceExt},
39    toggle::ToggleButton,
40    Thickness,
41};
42use fyrox_resource::untyped::ResourceKind;
43use fyrox_resource::{
44    io::ResourceIo,
45    manager::{BuiltInResource, ResourceManager},
46};
47use fyrox_texture::TextureResource;
48use std::{
49    any::{Any, TypeId},
50    sync::LazyLock,
51};
52use std::{
53    ops::{Deref, DerefMut},
54    path::Path,
55    sync::Arc,
56};
57use strum_macros::{AsRefStr, EnumString, VariantNames};
58
59/// A set of potential values for styled properties.
60#[derive(Visit, Reflect, Debug, Clone, TypeUuidProvider, AsRefStr, EnumString, VariantNames)]
61#[type_uuid(id = "85b8c1e4-03a2-4a28-acb4-1850d1a29227")]
62pub enum StyleProperty {
63    /// A numeric property.
64    Number(f32),
65    /// A thickness property that defines the width of four sides of a rectangles.
66    Thickness(Thickness),
67    /// A color property.
68    Color(Color),
69    /// A brush property, that defines how to render an arbitrary surface (solid, with gradient, etc.).
70    Brush(Brush),
71    /// A texture property. Could be used together with [`crate::image::Image`] widget or [`crate::nine_patch::NinePatch`]
72    /// widget.
73    Texture(TextureResource),
74}
75
76impl Default for StyleProperty {
77    fn default() -> Self {
78        Self::Number(0.0)
79    }
80}
81
82impl StyleProperty {
83    /// Returns type id of the actual value stored in the property. The set of potential types is
84    /// finite (see [`StyleProperty`] declaration).
85    pub fn value_type_id(&self) -> TypeId {
86        match self {
87            StyleProperty::Number(v) => v.type_id(),
88            StyleProperty::Thickness(v) => v.type_id(),
89            StyleProperty::Color(v) => v.type_id(),
90            StyleProperty::Brush(v) => v.type_id(),
91            StyleProperty::Texture(v) => v.type_id(),
92        }
93    }
94}
95
96/// A trait that provides a method that translates [`StyleProperty`] into a specific primitive value.
97pub trait IntoPrimitive<T> {
98    /// Tries to convert self into a primitive value `T`.
99    fn into_primitive(self) -> Option<T>;
100}
101
102macro_rules! impl_casts {
103    ($ty:ty => $var:ident) => {
104        impl From<$ty> for StyleProperty {
105            fn from(value: $ty) -> Self {
106                Self::$var(value)
107            }
108        }
109
110        impl IntoPrimitive<$ty> for StyleProperty {
111            fn into_primitive(self) -> Option<$ty> {
112                if let StyleProperty::$var(value) = self {
113                    Some(value)
114                } else {
115                    None
116                }
117            }
118        }
119    };
120}
121
122impl_casts!(f32 => Number);
123impl_casts!(Thickness => Thickness);
124impl_casts!(Color => Color);
125impl_casts!(Brush => Brush);
126impl_casts!(TextureResource => Texture);
127
128/// Default style of the library.
129pub static DEFAULT_STYLE: LazyLock<BuiltInResource<Style>> = LazyLock::new(|| {
130    BuiltInResource::new_no_source(
131        "__DEFAULT_STYLE__",
132        StyleResource::new_ok(
133            uuid!("1e0716e8-e728-491c-a65b-ca11b15048be"),
134            ResourceKind::External,
135            Style::dark_style(),
136        ),
137    )
138});
139
140/// A property, that can bind its value to a style. Why can't we just fetch the actual value from
141/// the style and why do we need to store the value as well? The answer is flexibility. In this
142/// approach, style becomes not necessary and the value can be hardcoded. Also, the values of such
143/// properties can be updated individually.
144#[derive(Clone, Debug, Reflect, Default)]
145#[reflect(bounds = "T: Reflect + Clone")]
146pub struct StyledProperty<T> {
147    /// Property value.
148    pub property: T,
149    /// Name of the property in a style table.
150    #[reflect(read_only, display_name = "Property Name")]
151    pub name: ImmutableString,
152}
153
154impl<T> From<T> for StyledProperty<T> {
155    fn from(property: T) -> Self {
156        Self {
157            property,
158            name: Default::default(),
159        }
160    }
161}
162
163impl<T: PartialEq> PartialEq for StyledProperty<T> {
164    fn eq(&self, other: &Self) -> bool {
165        self.property == other.property
166    }
167}
168
169impl<T> StyledProperty<T> {
170    /// Creates a new styled property with the given value and the name.
171    pub fn new(property: T, name: impl Into<ImmutableString>) -> Self {
172        Self {
173            property,
174            name: name.into(),
175        }
176    }
177
178    /// Tries to sync the property value with its respective value in the given style. This method
179    /// will fail if the given style does not contain the property.
180    pub fn update(&mut self, style: &StyleResource)
181    where
182        StyleProperty: IntoPrimitive<T>,
183    {
184        if let Some(property) = style.get(self.name.clone()) {
185            self.property = property;
186        }
187    }
188}
189
190impl<T> Deref for StyledProperty<T> {
191    type Target = T;
192
193    fn deref(&self) -> &Self::Target {
194        &self.property
195    }
196}
197
198impl<T> DerefMut for StyledProperty<T> {
199    fn deref_mut(&mut self) -> &mut Self::Target {
200        &mut self.property
201    }
202}
203
204impl<T: Visit> Visit for StyledProperty<T> {
205    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
206        self.property.visit(name, visitor)
207    }
208}
209
210/// Named style property container.
211#[derive(Visit, Reflect, Clone, Default, Debug, TypeUuidProvider)]
212#[type_uuid(id = "6238f37c-c067-4dd1-be67-6a8bb8853a59")]
213pub struct StylePropertyContainer {
214    /// Name of the property.
215    pub name: ImmutableString,
216    /// The actual value of the property.
217    pub value: StyleProperty,
218}
219
220/// Style is a simple container for a named properties. Styles can be based on some other styles, thus
221/// allowing cascaded styling. Such cascading allows to define some base style with common properties
222/// and then create any number of derived styles. For example, you can define a style for Button widget
223/// with corner radius, font size, border thickness and then create two derived styles for light and
224/// dark themes that will define colors and brushes. Light or dark theme does not affect all of those
225/// base properties, but has different colors.
226///
227/// Styles can contain only specific types of properties (see [`StyleProperty`] enumeration), any
228/// more complex properties can be built using these primitives.
229///
230/// There are three major ways of widget styling:
231///
232/// 1) During widget building stage - this way involves [`crate::BuildContext`]'s style field. This
233/// field defines a style for all widgets that will be built with the context.
234/// 2) Message-based style changes - this way is based on [`crate::widget::WidgetMessage::Style`] message
235/// that can be sent to a particular widget (or hierarchy) to force them to update styled properties.
236/// 3) Global style changes - this way is based on [`crate::UserInterface::set_style`] method, that
237/// sends the specified style to all widgets, forcing them to update styled properties.
238///
239/// The most used methods are 1 and 3. The following examples should clarify how to use these
240/// approaches.
241///
242/// ## Examples
243///
244/// The following example shows how to use a style during widget building stage:
245///
246/// ```rust
247/// # use fyrox_ui::{
248/// #     button::{Button, ButtonBuilder},
249/// #     style::{resource::StyleResource, Style},
250/// #     widget::WidgetBuilder,
251/// #     Thickness, UserInterface,
252/// # };
253/// #
254/// fn build_with_style(ui: &mut UserInterface) {
255///     // The context will use UI style by default. You can override it using `ui.set_style(..)`.
256///     let ctx = &mut ui.build_ctx();
257///
258///     // Create a style resource first and assign it to the build context. All widgets built with
259///     // the context will use this style.
260///     let style = Style::light_style()
261///         .with(Button::CORNER_RADIUS, 6.0f32)
262///         .with(Button::BORDER_THICKNESS, Thickness::uniform(3.0));
263///
264///     ctx.style = StyleResource::new_embedded(style);
265///
266///     // The button will have a corner radius of 6.0 points and border thickness of 3.0 points on
267///     // each side.
268///     ButtonBuilder::new(WidgetBuilder::new()).build(ctx);
269/// }
270/// ```
271///
272/// To change UI style globally after it was built, use something like this:
273///
274/// ```rust
275/// use fyrox_ui::{
276///     button::Button,
277///     style::{resource::StyleResource, Style},
278///     Thickness, UserInterface,
279/// };
280///
281/// fn apply_style(ui: &mut UserInterface) {
282///     let style = Style::light_style()
283///         .with(Button::CORNER_RADIUS, 3.0f32)
284///         .with(Button::BORDER_THICKNESS, Thickness::uniform(1.0));
285///
286///     ui.set_style(StyleResource::new_embedded(style));
287/// }
288/// ```
289#[derive(Visit, Reflect, Clone, Default, Debug, TypeUuidProvider)]
290#[type_uuid(id = "38a63b49-d765-4c01-8fb5-202cc43d607e")]
291pub struct Style {
292    parent: Option<StyleResource>,
293    properties: Vec<StylePropertyContainer>,
294}
295
296impl Style {
297    /// The name of the darkest brush.
298    pub const BRUSH_DARKEST: &'static str = "Global.Brush.Darkest";
299    /// The name of the darker brush.
300    pub const BRUSH_DARKER: &'static str = "Global.Brush.Darker";
301    /// The name of the dark brush.
302    pub const BRUSH_DARK: &'static str = "Global.Brush.Dark";
303    /// The name of the primary brush that is used for the major amount of surface.
304    pub const BRUSH_PRIMARY: &'static str = "Global.Brush.Primary";
305    /// The name of the slightly lighter primary brush.
306    pub const BRUSH_LIGHTER_PRIMARY: &'static str = "Global.Brush.LighterPrimary";
307    /// The name of the light brush.
308    pub const BRUSH_LIGHT: &'static str = "Global.Brush.Light";
309    /// The name of the lighter brush.
310    pub const BRUSH_LIGHTER: &'static str = "Global.Brush.Lighter";
311    /// The name of the lightest brush.
312    pub const BRUSH_LIGHTEST: &'static str = "Global.Brush.Lightest";
313    /// The name of the bright brush.
314    pub const BRUSH_BRIGHT: &'static str = "Global.Brush.Bright";
315    /// The name of the brightest brush.
316    pub const BRUSH_BRIGHTEST: &'static str = "Global.Brush.Brightest";
317    /// The name of the bright blue brush.
318    pub const BRUSH_BRIGHT_BLUE: &'static str = "Global.Brush.BrightBlue";
319    /// The name of the dim blue brush.
320    pub const BRUSH_DIM_BLUE: &'static str = "Global.Brush.DimBlue";
321    /// The name of the text brush.
322    pub const BRUSH_TEXT: &'static str = "Global.Brush.Text";
323    /// The name of the foreground brush.
324    pub const BRUSH_FOREGROUND: &'static str = "Global.Brush.Foreground";
325    /// The name of the information brush.
326    pub const BRUSH_INFORMATION: &'static str = "Global.Brush.Information";
327    /// The name of the warning brush.
328    pub const BRUSH_WARNING: &'static str = "Global.Brush.Warning";
329    /// The name of the error brush.
330    pub const BRUSH_ERROR: &'static str = "Global.Brush.Error";
331    /// The name of the ok brush.
332    pub const BRUSH_OK: &'static str = "Global.Brush.Ok";
333    /// The name of the highlight brush used to highlight widgets with keyboard focus.
334    pub const BRUSH_HIGHLIGHT: &'static str = "Global.Brush.Highlight";
335    /// The name of the font size property.
336    pub const FONT_SIZE: &'static str = "Global.Font.Size";
337    /// The name of the normal state brush of the `ok` context action.
338    pub const BRUSH_OK_NORMAL: &'static str = "Global.Brush.Ok.Normal";
339    /// The name of the pressed state brush of the `ok` context action.
340    pub const BRUSH_OK_PRESSED: &'static str = "Global.Brush.Ok.Pressed";
341    /// The name of the hover state brush of the `ok` context action.
342    pub const BRUSH_OK_HOVER: &'static str = "Global.Brush.Ok.Hover";
343    /// The name of the normal state brush of the `cancel` context action.
344    pub const BRUSH_CANCEL_NORMAL: &'static str = "Global.Brush.Cancel.Normal";
345    /// The name of the pressed state brush of the `cancel` context action.
346    pub const BRUSH_CANCEL_PRESSED: &'static str = "Global.Brush.Cancel.Pressed";
347    /// The name of the hover state brush of the `cancel` context action.
348    pub const BRUSH_CANCEL_HOVER: &'static str = "Global.Brush.Cancel.Hover";
349
350    fn base_style() -> Style {
351        let mut style = Self::default();
352
353        style
354            .set(Self::FONT_SIZE, 14.0f32)
355            .merge(&Button::style())
356            .merge(&CheckBox::style())
357            .merge(&DropdownList::style())
358            .merge(&ToggleButton::style());
359
360        style
361    }
362
363    /// Creates a new dark style.
364    pub fn dark_style() -> Style {
365        let mut style = Self::base_style();
366        style
367            .set(Self::BRUSH_DARKEST, Brush::Solid(Color::repeat_opaque(20)))
368            .set(Self::BRUSH_DARKER, Brush::Solid(Color::repeat_opaque(30)))
369            .set(Self::BRUSH_DARK, Brush::Solid(Color::repeat_opaque(40)))
370            .set(Self::BRUSH_PRIMARY, Brush::Solid(Color::repeat_opaque(50)))
371            .set(
372                Self::BRUSH_LIGHTER_PRIMARY,
373                Brush::Solid(Color::repeat_opaque(60)),
374            )
375            .set(Self::BRUSH_LIGHT, Brush::Solid(Color::repeat_opaque(70)))
376            .set(Self::BRUSH_LIGHTER, Brush::Solid(Color::repeat_opaque(85)))
377            .set(
378                Self::BRUSH_LIGHTEST,
379                Brush::Solid(Color::repeat_opaque(100)),
380            )
381            .set(Self::BRUSH_BRIGHT, Brush::Solid(Color::repeat_opaque(130)))
382            .set(
383                Self::BRUSH_BRIGHTEST,
384                Brush::Solid(Color::repeat_opaque(160)),
385            )
386            .set(
387                Self::BRUSH_BRIGHT_BLUE,
388                Brush::Solid(Color::opaque(80, 118, 178)),
389            )
390            .set(
391                Self::BRUSH_HIGHLIGHT,
392                Brush::Solid(Color::opaque(80, 118, 178)),
393            )
394            .set(
395                Self::BRUSH_DIM_BLUE,
396                Brush::Solid(Color::opaque(66, 99, 149)),
397            )
398            .set(Self::BRUSH_TEXT, Brush::Solid(Color::opaque(190, 190, 190)))
399            .set(Self::BRUSH_FOREGROUND, Brush::Solid(Color::WHITE))
400            .set(Self::BRUSH_INFORMATION, Brush::Solid(Color::ANTIQUE_WHITE))
401            .set(Self::BRUSH_WARNING, Brush::Solid(Color::GOLD))
402            .set(Self::BRUSH_ERROR, Brush::Solid(Color::RED))
403            .set(Self::BRUSH_OK, Brush::Solid(Color::GREEN))
404            .set(
405                Self::BRUSH_OK_NORMAL,
406                Brush::Solid(Color::opaque(0, 130, 0)),
407            )
408            .set(Self::BRUSH_OK_HOVER, Brush::Solid(Color::opaque(0, 150, 0)))
409            .set(
410                Self::BRUSH_OK_PRESSED,
411                Brush::Solid(Color::opaque(0, 170, 0)),
412            )
413            .set(
414                Self::BRUSH_CANCEL_NORMAL,
415                Brush::Solid(Color::opaque(130, 0, 0)),
416            )
417            .set(
418                Self::BRUSH_CANCEL_HOVER,
419                Brush::Solid(Color::opaque(150, 0, 0)),
420            )
421            .set(
422                Self::BRUSH_CANCEL_PRESSED,
423                Brush::Solid(Color::opaque(170, 0, 0)),
424            );
425        style
426    }
427
428    /// Creates a new light style.
429    pub fn light_style() -> Style {
430        let mut style = Self::base_style();
431        style
432            .set(Self::BRUSH_DARKEST, Brush::Solid(Color::repeat_opaque(140)))
433            .set(Self::BRUSH_DARKER, Brush::Solid(Color::repeat_opaque(150)))
434            .set(Self::BRUSH_DARK, Brush::Solid(Color::repeat_opaque(160)))
435            .set(Self::BRUSH_PRIMARY, Brush::Solid(Color::repeat_opaque(170)))
436            .set(
437                Self::BRUSH_LIGHTER_PRIMARY,
438                Brush::Solid(Color::repeat_opaque(180)),
439            )
440            .set(Self::BRUSH_LIGHT, Brush::Solid(Color::repeat_opaque(190)))
441            .set(Self::BRUSH_LIGHTER, Brush::Solid(Color::repeat_opaque(205)))
442            .set(
443                Self::BRUSH_LIGHTEST,
444                Brush::Solid(Color::repeat_opaque(220)),
445            )
446            .set(Self::BRUSH_BRIGHT, Brush::Solid(Color::repeat_opaque(40)))
447            .set(
448                Self::BRUSH_BRIGHTEST,
449                Brush::Solid(Color::repeat_opaque(30)),
450            )
451            .set(
452                Self::BRUSH_BRIGHT_BLUE,
453                Brush::Solid(Color::opaque(80, 118, 178)),
454            )
455            .set(
456                Self::BRUSH_HIGHLIGHT,
457                Brush::Solid(Color::opaque(80, 118, 178)),
458            )
459            .set(
460                Self::BRUSH_DIM_BLUE,
461                Brush::Solid(Color::opaque(66, 99, 149)),
462            )
463            .set(Self::BRUSH_TEXT, Brush::Solid(Color::repeat_opaque(0)))
464            .set(Self::BRUSH_FOREGROUND, Brush::Solid(Color::WHITE))
465            .set(Self::BRUSH_INFORMATION, Brush::Solid(Color::ROYAL_BLUE))
466            .set(
467                Self::BRUSH_WARNING,
468                Brush::Solid(Color::opaque(255, 242, 0)),
469            )
470            .set(Self::BRUSH_ERROR, Brush::Solid(Color::RED));
471        style
472    }
473
474    /// The same as [`Self::set`], but takes self as value and essentially allows chained calls in
475    /// builder-like style:
476    ///
477    /// ```rust
478    /// # use fyrox_core::color::Color;
479    /// # use fyrox_ui::brush::Brush;
480    /// # use fyrox_ui::style::Style;
481    /// Style::default()
482    ///     .with("SomeProperty", 0.2f32)
483    ///     .with("SomeOtherProperty", Brush::Solid(Color::WHITE));
484    /// ```
485    pub fn with(
486        mut self,
487        name: impl Into<ImmutableString>,
488        property: impl Into<StyleProperty>,
489    ) -> Self {
490        self.set(name, property);
491        self
492    }
493
494    /// Sets the parent style for this style. Parent style will be used in an attempt to fetch properties
495    /// that aren't present in this style.
496    pub fn set_parent(&mut self, parent: Option<StyleResource>) {
497        self.parent = parent;
498    }
499
500    /// Returns parent style of this style.
501    pub fn parent(&self) -> Option<&StyleResource> {
502        self.parent.as_ref()
503    }
504
505    /// Returns an index of the variable with the given name.
506    pub fn index_of(&self, name: &ImmutableString) -> Option<usize> {
507        self.properties
508            .binary_search_by(|v| v.name.cached_hash().cmp(&name.cached_hash()))
509            .ok()
510    }
511
512    /// Checks if there's a variable with the given name.
513    pub fn contains(&self, name: &ImmutableString) -> bool {
514        self.index_of(name).is_some()
515    }
516
517    /// Merges current style with some other style. This method does not overwrite existing values,
518    /// instead it only adds missing values from the other style.
519    pub fn merge(&mut self, other: &Self) -> &mut Self {
520        for other_property in other.properties.iter() {
521            if !self.contains(&other_property.name) {
522                self.set(other_property.name.clone(), other_property.value.clone());
523            }
524        }
525        self
526    }
527
528    /// Registers a new property with the given name and value:
529    ///
530    /// ```rust
531    /// # use fyrox_core::color::Color;
532    /// # use fyrox_ui::brush::Brush;
533    /// # use fyrox_ui::style::Style;
534    /// let mut style = Style::default();
535    /// style
536    ///     .set("SomeProperty", 0.2f32)
537    ///     .set("SomeOtherProperty", Brush::Solid(Color::WHITE));
538    /// ```
539    pub fn set(
540        &mut self,
541        name: impl Into<ImmutableString>,
542        value: impl Into<StyleProperty>,
543    ) -> &mut Self {
544        let name = name.into();
545        let value = value.into();
546
547        if let Some(existing_index) = self.index_of(&name) {
548            self.properties[existing_index] = StylePropertyContainer { name, value };
549        } else {
550            let index = self
551                .properties
552                .partition_point(|h| h.name.cached_hash() < name.cached_hash());
553            self.properties
554                .insert(index, StylePropertyContainer { name, value });
555        }
556
557        self
558    }
559
560    /// Tries to fetch a property with the given name. If the property is not found, this method will
561    /// try to search in the parent style (the search is recursive).
562    pub fn get_raw(&self, name: impl Into<ImmutableString>) -> Option<StyleProperty> {
563        let name = name.into();
564        let index = self.index_of(&name)?;
565        if let Some(container) = self.properties.get(index) {
566            return Some(container.value.clone());
567        } else if let Some(parent) = self.parent.as_ref() {
568            let state = parent.state();
569            if let Some(data) = state.data_ref() {
570                return data.get_raw(name);
571            }
572        }
573        None
574    }
575
576    /// Tries to fetch a property with the given name and perform type casting to the requested type.
577    /// If the property is not found, this method will try to search in the parent style (the search
578    /// is recursive).
579    pub fn get<P>(&self, name: impl Into<ImmutableString>) -> Option<P>
580    where
581        StyleProperty: IntoPrimitive<P>,
582    {
583        self.get_raw(name)
584            .and_then(|property| property.into_primitive())
585    }
586
587    /// Tries to fetch a property with the given name. If the property is not found, this method will
588    /// try to search in the parent style (the search is recursive). If there's no such property at
589    /// all, this method will return its default value (define by [`Default`] trait).
590    pub fn get_or_default<P>(&self, name: impl Into<ImmutableString>) -> P
591    where
592        P: Default,
593        StyleProperty: IntoPrimitive<P>,
594    {
595        self.get_raw(name)
596            .and_then(|property| property.into_primitive())
597            .unwrap_or_default()
598    }
599
600    /// Tries to fetch a property with the given name or, if not found, returns the given default value.
601    pub fn get_or<P>(&self, name: impl Into<ImmutableString>, default: P) -> P
602    where
603        StyleProperty: IntoPrimitive<P>,
604    {
605        self.get(name).unwrap_or(default)
606    }
607
608    /// Tries to find a property with the given name or takes the default value of the property's type
609    /// and wraps it into [`StyledProperty`], essentially binding the value to the style property.
610    pub fn property<P>(&self, name: impl Into<ImmutableString>) -> StyledProperty<P>
611    where
612        P: Default,
613        StyleProperty: IntoPrimitive<P>,
614    {
615        let name = name.into();
616        StyledProperty::new(self.get_or_default(name.clone()), name)
617    }
618
619    /// Tries to load a style from the given path.
620    pub async fn from_file(
621        path: &Path,
622        io: &dyn ResourceIo,
623        resource_manager: ResourceManager,
624    ) -> Result<Self, StyleResourceError> {
625        let bytes = io.load_file(path).await?;
626        let mut visitor = Visitor::load_from_memory(&bytes)?;
627        visitor.blackboard.register(Arc::new(resource_manager));
628        let mut style = Style::default();
629        style.visit("Style", &mut visitor)?;
630        Ok(style)
631    }
632
633    /// Returns an immutable reference to the internal container with the style properties.
634    /// Keep in mind that the returned container contains only the properties of the current
635    /// style! Properties of the parent style(s) should be obtained separately.
636    pub fn inner(&self) -> &Vec<StylePropertyContainer> {
637        &self.properties
638    }
639
640    /// Collects all the properties in the current and ancestor style chain. Returns a hash map with
641    /// all property values with their names. Basically, this method merges all the styles with their
642    /// ancestor style chain.
643    pub fn all_properties(&self) -> Self {
644        let mut properties = self
645            .parent
646            .as_ref()
647            .map(|parent| parent.data_ref().all_properties())
648            .unwrap_or_default();
649        for property in self.properties.iter() {
650            properties.set(property.name.clone(), property.value.clone());
651        }
652        properties
653    }
654}
655
656#[cfg(test)]
657mod test {
658    use crate::brush::Brush;
659    use crate::style::Style;
660    use fyrox_core::color::Color;
661    use fyrox_core::ImmutableString;
662
663    #[test]
664    fn test_style() {
665        let mut style = Style::default();
666        style
667            .set("A", 0.2f32)
668            .set("D", 0.1f32)
669            .set("B", Brush::Solid(Color::WHITE))
670            .set("C", Brush::Solid(Color::WHITE));
671        assert_eq!(style.index_of(&ImmutableString::new("A")), Some(3));
672        assert_eq!(style.index_of(&ImmutableString::new("B")), Some(2));
673        assert_eq!(style.index_of(&ImmutableString::new("C")), Some(1));
674        assert_eq!(style.index_of(&ImmutableString::new("D")), Some(0));
675    }
676}