ui_texture_atlas_slice/
ui_texture_atlas_slice.rs1use bevy::{
5 color::palettes::css::{GOLD, ORANGE},
6 picking::hover::Hovered,
7 platform::collections::HashSet,
8 prelude::*,
9 ui::{widget::NodeImageMode, Pressed},
10 ui_widgets::Button,
11};
12
13fn main() {
14 App::new()
15 .add_plugins(DefaultPlugins)
16 .add_systems(Startup, setup)
17 .add_systems(Update, button_system)
18 .run();
19}
20
21fn button_system(
28 mut buttons: Query<
29 (
30 Entity,
31 Option<Ref<Pressed>>,
32 Ref<Hovered>,
33 &mut ImageNode,
34 &Children,
35 ),
36 With<Button>,
37 >,
38 mut removed_pressed: RemovedComponents<Pressed>,
39 mut text_query: Query<&mut Text>,
40) {
41 let just_unpressed: HashSet<Entity> = removed_pressed.read().collect();
43 for (entity, pressed, hovered, mut image, children) in &mut buttons {
44 let changed = hovered.is_changed()
45 || pressed.as_ref().is_some_and(Ref::is_changed)
46 || just_unpressed.contains(&entity);
47 if changed {
48 set_button_style(
49 pressed.is_some(),
50 hovered.get(),
51 &mut image,
52 children,
53 &mut text_query,
54 );
55 }
56 }
57}
58
59fn set_button_style(
61 pressed: bool,
62 hovered: bool,
63 image: &mut ImageNode,
64 children: &Children,
65 text_query: &mut Query<&mut Text>,
66) {
67 let mut text = text_query.get_mut(children[0]).unwrap();
68 match (pressed, hovered) {
69 (true, _) => {
70 **text = "Press".to_string();
71 if let Some(atlas) = &mut image.texture_atlas {
72 atlas.index = (atlas.index + 1) % 30;
73 }
74 image.color = GOLD.into();
75 }
76 (false, true) => {
77 **text = "Hover".to_string();
78 image.color = ORANGE.into();
79 }
80 (false, false) => {
81 **text = "Button".to_string();
82 image.color = Color::WHITE;
83 }
84 }
85}
86
87fn setup(
88 mut commands: Commands,
89 asset_server: Res<AssetServer>,
90 mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
91) {
92 let texture_handle = asset_server.load("textures/fantasy_ui_borders/border_sheet.png");
93 let atlas_layout =
94 TextureAtlasLayout::from_grid(UVec2::new(50, 50), 6, 6, Some(UVec2::splat(2)), None);
95 let atlas_layout_handle = texture_atlases.add(atlas_layout);
96
97 let slicer = TextureSlicer {
98 border: BorderRect::all(24.0),
99 center_scale_mode: SliceScaleMode::Stretch,
100 sides_scale_mode: SliceScaleMode::Stretch,
101 max_corner_scale: 1.0,
102 };
103 commands.spawn(Camera2d);
105 commands
106 .spawn(Node {
107 width: percent(100),
108 height: percent(100),
109 align_items: AlignItems::Center,
110 justify_content: JustifyContent::Center,
111 ..default()
112 })
113 .with_children(|parent| {
114 for (idx, [w, h]) in [
115 (0, [150.0, 150.0]),
116 (7, [300.0, 150.0]),
117 (13, [150.0, 300.0]),
118 ] {
119 parent
120 .spawn((
121 Button,
122 Hovered::default(),
123 ImageNode::from_atlas_image(
124 texture_handle.clone(),
125 TextureAtlas {
126 index: idx,
127 layout: atlas_layout_handle.clone(),
128 },
129 )
130 .with_mode(NodeImageMode::Sliced(slicer.clone())),
131 Node {
132 width: px(w),
133 height: px(h),
134 justify_content: JustifyContent::Center,
136 align_items: AlignItems::Center,
138 margin: px(20).all(),
139 ..default()
140 },
141 ))
142 .with_children(|parent| {
143 parent.spawn((
144 Text::new("Button"),
145 TextFont {
146 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
147 font_size: FontSize::Px(33.0),
148 ..default()
149 },
150 TextColor(Color::srgb(0.9, 0.9, 0.9)),
151 ));
152 });
153 }
154 });
155}