Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 29 additions & 36 deletions crates/bevy_feathers/src/controls/disclosure_toggle.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use bevy_app::{App, Plugin, PreUpdate};
use bevy_ecs::{
entity::Entity,
hierarchy::Children,
lifecycle::RemovedComponents,
query::{Added, Has, Or, With},
reflect::ReflectComponent,
schedule::IntoScheduleConfigs,
system::{Query, Res},
system::{Commands, Query},
};
use bevy_input_focus::tab_navigation::TabIndex;
use bevy_math::Rot2;
Expand All @@ -14,15 +15,14 @@ use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_scene::{bsn, Scene, SceneComponent};
use bevy_ui::{
px, widget::ImageNode, AlignItems, Checked, Display, InteractionDisabled, JustifyContent, Node,
UiTransform,
px, AlignItems, Checked, Display, InteractionDisabled, JustifyContent, Node, UiTransform,
};
use bevy_ui_widgets::Checkbox;
use bevy_window::SystemCursorIcon;

use crate::{
constants::icons, cursor::EntityCursor, display::icon, focus::FocusIndicator, theme::UiTheme,
tokens,
constants::icons, cursor::EntityCursor, display::icon, focus::FocusIndicator,
theme::InheritableThemeTextColor, tokens,
};

/// A toggle button which shows a chevron that points either right or down, used to expand or
Expand All @@ -47,6 +47,7 @@ impl FeathersDisclosureToggle {
Checkbox
EntityCursor::System(SystemCursorIcon::Pointer)
FocusIndicator
InheritableThemeTextColor(tokens::BUTTON_TEXT)
TabIndex(0)
Children [
icon(icons::CHEVRON_RIGHT)
Expand All @@ -58,32 +59,27 @@ impl FeathersDisclosureToggle {
fn update_toggle_styles(
mut q_toggle: Query<
(
Entity,
Has<InteractionDisabled>,
Has<Checked>,
&mut UiTransform,
&Children,
&InheritableThemeTextColor,
),
(
With<FeathersDisclosureToggle>,
Or<(Added<Checkbox>, Added<Checked>, Added<InteractionDisabled>)>,
),
>,
mut q_icon: Query<&mut ImageNode>,
theme: Res<UiTheme>,
mut commands: Commands,
) {
for (disabled, checked, mut transform, children) in q_toggle.iter_mut() {
let Some(child_id) = children.first() else {
continue;
};
let Ok(mut icon_child) = q_icon.get_mut(*child_id) else {
continue;
};
for (ent, disabled, checked, mut transform, text_color) in q_toggle.iter_mut() {
set_toggle_styles(
ent,
disabled,
checked,
transform.as_mut(),
&mut icon_child,
&theme,
text_color,
&mut commands,
);
}
}
Expand All @@ -94,53 +90,50 @@ fn update_toggle_styles_remove(
Has<InteractionDisabled>,
Has<Checked>,
&mut UiTransform,
&Children,
&InheritableThemeTextColor,
),
With<FeathersDisclosureToggle>,
>,
mut q_icon: Query<&mut ImageNode>,
mut removed_disabled: RemovedComponents<InteractionDisabled>,
mut removed_checked: RemovedComponents<Checked>,
theme: Res<UiTheme>,
mut commands: Commands,
) {
removed_disabled
.read()
.chain(removed_checked.read())
.for_each(|ent| {
if let Ok((disabled, checked, mut transform, children)) = q_toggle.get_mut(ent) {
let Some(child_id) = children.first() else {
return;
};
let Ok(mut icon_child) = q_icon.get_mut(*child_id) else {
return;
};
if let Ok((disabled, checked, mut transform, text_color)) = q_toggle.get_mut(ent) {
set_toggle_styles(
ent,
disabled,
checked,
transform.as_mut(),
&mut icon_child,
&theme,
text_color,
&mut commands,
);
}
});
}

fn set_toggle_styles(
entity: Entity,
disabled: bool,
checked: bool,
transform: &mut UiTransform,
image_node: &mut ImageNode,
theme: &Res<'_, UiTheme>,
text_color: &InheritableThemeTextColor,
commands: &mut Commands,
) {
// It's effectively the same color as the caption of a "plain" variant tool button with an icon.
let icon_color = match disabled {
true => theme.color(&tokens::BUTTON_TEXT_DISABLED),
false => theme.color(&tokens::BUTTON_TEXT),
let new_text_color_token = match disabled {
true => tokens::BUTTON_TEXT_DISABLED,
false => tokens::BUTTON_TEXT,
};

// Change icon color
if image_node.color != icon_color {
image_node.color = icon_color;
if new_text_color_token != text_color.0 {
commands
.entity(entity)
.insert(InheritableThemeTextColor(new_text_color_token));
}

match checked {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_feathers/src/controls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl Plugin for ControlsPlugin {
DisclosureTogglePlugin,
ListViewPlugin,
MenuPlugin,
NumberInputPlugin,
RadioPlugin,
ScrollbarPlugin,
SliderPlugin,
Expand Down
49 changes: 48 additions & 1 deletion crates/bevy_feathers/src/controls/number_input.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::{f32::consts::PI, ops::Range};

use bevy_app::PropagateOver;
use bevy_app::{Plugin, PreUpdate, PropagateOver};
use bevy_color::Color;
use bevy_ecs::{
change_detection::DetectChanges,
component::Component,
entity::Entity,
event::EntityEvent,
Expand All @@ -11,6 +12,7 @@ use bevy_ecs::{
observer::On,
query::{Has, With},
reflect::ReflectComponent,
schedule::IntoScheduleConfigs,
system::{Commands, Query, Res, ResMut},
};
use bevy_input::{
Expand All @@ -26,6 +28,7 @@ use bevy_picking::{
events::{Cancel, Drag, DragEnd, DragStart, Pointer, Press, Release},
hover::Hovered,
pointer::PointerButton,
PickingSystems,
};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
Expand Down Expand Up @@ -1190,3 +1193,47 @@ fn trigger_value_change(
}),
}
}

/// Re-apply the slidebar gradient colors for every number input when the theme changes.
fn update_slidebar_styles_theme(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aw darn, I was hoping I could do the number input purely with observers - I wanted to see if it was possible. No such luck I guess :(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I was hunting around looking for the plugin! :)

q_children: Query<&Children>,
q_number_input: Query<(Entity, Has<InteractionDisabled>), With<FeathersNumberInput>>,
mut q_text_input: Query<(&Hovered, &mut BackgroundGradient)>,
theme: Res<UiTheme>,
mut commands: Commands,
) {
if !theme.is_changed() {
return;
}
for (root_entity, is_disabled) in q_number_input.iter() {
let Some(text_entity) = q_children
.iter_descendants(root_entity)
.find(|e| q_text_input.contains(*e))
else {
continue;
};
if let Ok((&Hovered(hovered), mut gradient)) = q_text_input.get_mut(text_entity) {
set_slidebar_styles(
text_entity,
&theme,
is_disabled,
false,
hovered,
&mut gradient,
&mut commands,
);
}
}
}

/// Plugin which keeps number-input slidebar colors in sync with the theme.
pub struct NumberInputPlugin;

impl Plugin for NumberInputPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_systems(
PreUpdate,
update_slidebar_styles_theme.in_set(PickingSystems::Last),
);
}
}
33 changes: 33 additions & 0 deletions crates/bevy_feathers/src/controls/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use bevy_app::{Plugin, PreUpdate};
use bevy_color::Color;
use bevy_ecs::{
bundle::Bundle,
change_detection::DetectChanges,
children,
component::Component,
entity::Entity,
Expand Down Expand Up @@ -271,6 +272,37 @@ fn update_slider_styles_remove(
});
}

/// Re-apply slider styles to every slider when the theme changes.
fn update_slider_styles_theme(
mut q_sliders: Query<
(
Entity,
Has<InteractionDisabled>,
Has<Pressed>,
&Hovered,
&mut BackgroundGradient,
),
With<FeathersSlider>,
>,
theme: Res<UiTheme>,
mut commands: Commands,
) {
if !theme.is_changed() {
return;
}
for (slider_ent, disabled, pressed, hovered, mut gradient) in q_sliders.iter_mut() {
set_slider_styles(
slider_ent,
&theme,
disabled,
pressed,
hovered.0,
gradient.as_mut(),
&mut commands,
);
}
}

fn set_slider_styles(
slider_ent: Entity,
theme: &Res<'_, UiTheme>,
Expand Down Expand Up @@ -376,6 +408,7 @@ impl Plugin for SliderPlugin {
(
update_slider_styles,
update_slider_styles_remove,
update_slider_styles_theme,
update_slider_pos,
)
.in_set(PickingSystems::Last),
Expand Down
36 changes: 36 additions & 0 deletions crates/bevy_feathers/src/display/icon.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
//! BSN Scene for loading images and displaying them as [`ImageNode`]s.
use bevy_ecs::query::{Changed, With};
use bevy_ecs::reflect::ReflectComponent;
use bevy_ecs::{component::Component, system::Query};
use bevy_reflect::Reflect;
use bevy_scene::{bsn, Scene};
use bevy_text::TextColor;
use bevy_ui::{px, widget::ImageNode, Node};

use crate::theme::ThemedText;

/// Marker to tint an icon's ImageNode by the text color.
#[derive(Component, Default, Clone, Reflect)]
#[reflect(Component)]
#[require(ThemedText)]
pub struct ThemedIcon;

/// Template which displays an icon.
pub fn icon(image: &'static str) -> impl Scene {
bsn! {
Expand All @@ -11,5 +24,28 @@ pub fn icon(image: &'static str) -> impl Scene {
ImageNode {
image: image
}
ThemedIcon
}
}

/// Template which displays an icon and doesn't tint it by the text color.
pub fn icon_untinted(image: &'static str) -> impl Scene {
bsn! {
Node {
height: px(14),
}
ImageNode {
image: image
}
}
}

pub(crate) fn update_themed_icons(
mut q_icons: Query<(&mut ImageNode, &TextColor), (With<ThemedIcon>, Changed<TextColor>)>,
) {
for (mut image, color) in &mut q_icons {
if image.color != color.0 {
image.color = color.0;
}
}
}
18 changes: 12 additions & 6 deletions crates/bevy_feathers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,18 @@ impl Plugin for FeathersCorePlugin {
bevy_window::SystemCursorIcon::Default,
)));

app.add_systems(PostUpdate, theme::update_theme)
.add_observer(theme::on_changed_background)
.add_observer(theme::on_changed_border)
.add_observer(theme::on_changed_font_color)
.add_observer(theme::on_changed_text_color)
.add_observer(font_styles::on_changed_font);
app.add_systems(
PostUpdate,
(
theme::update_theme,
display::update_themed_icons.after(PropagateSet::<TextColor>::default()),
),
)
.add_observer(theme::on_changed_background)
.add_observer(theme::on_changed_border)
.add_observer(theme::on_changed_font_color)
.add_observer(theme::on_changed_text_color)
.add_observer(font_styles::on_changed_font);

app.init_resource::<AlphaPatternResource>();
}
Expand Down
Loading