DEVELOPMENT ENVIRONMENT

~liljamo/bevy_dice

ref: 4e633cd65152b4ddf2aab70fada8eb0d7a931ff7 bevy_dice/crates/bevy_dice_demo/src/ui.rs -rw-r--r-- 3.3 KiB
4e633cd6Jonni Liljamo feat(demo): deduplicate button code 3 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
 * Copyright (C) 2025 Jonni Liljamo <jonni@liljamo.com>
 *
 * This file is licensed under MIT, see LICENSE for more information.
 */

use bevy::{color::palettes::tailwind::*, prelude::*};

const THROW_COLOR: Srgba = LIME_400;
const DESPAWN_COLOR: Srgba = RED_400;

pub struct UIPlugin;

impl Plugin for UIPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, setup)
            .add_systems(Update, button_interaction);

        app.add_event::<ThrowPressed>()
            .add_event::<DespawnPressed>();
    }
}

#[derive(Component)]
enum ButtonFunction {
    Throw,
    Despawn,
}

#[derive(Event)]
pub struct ThrowPressed;

#[derive(Event)]
pub struct DespawnPressed;

fn button(function: ButtonFunction, text: impl Into<String>, color: Srgba) -> impl Bundle {
    (
        function,
        Button,
        Node {
            width: Val::Px(180.0),
            height: Val::Px(60.0),
            border: UiRect::all(Val::Px(2.0)),
            flex_direction: FlexDirection::Column,
            justify_content: JustifyContent::Center,
            align_items: AlignItems::Center,
            margin: UiRect::bottom(Val::Px(4.0)),
            ..Default::default()
        },
        BackgroundColor(Color::from(color)),
        BorderColor(Color::from(color.darker(0.3))),
        BorderRadius::all(Val::Px(10.0)),
        BoxShadow::new(
            Color::BLACK.with_alpha(0.8),
            Val::Px(4.0),
            Val::Px(4.0),
            Val::DEFAULT,
            Val::DEFAULT,
        ),
        children![(
            Text::new(text),
            TextFont {
                font_size: 18.0,
                ..Default::default()
            },
            TextColor(Color::BLACK),
        ),],
    )
}

fn setup(mut commands: Commands) {
    commands.spawn((
        Node {
            width: Val::Percent(100.0),
            height: Val::Percent(100.0),
            flex_direction: FlexDirection::Row,
            align_items: AlignItems::FlexEnd,
            justify_content: JustifyContent::SpaceEvenly,
            padding: UiRect::all(Val::Px(8.0)),
            overflow: Overflow::scroll_y(),
            ..Default::default()
        },
        children![
            button(ButtonFunction::Throw, "Throw", THROW_COLOR),
            button(ButtonFunction::Despawn, "Despawn Dice", DESPAWN_COLOR),
        ],
    ));
}

fn button_interaction(
    mut commands: Commands,
    q_interaction: Query<
        (&Interaction, &mut BackgroundColor, &ButtonFunction),
        (Changed<Interaction>, With<Button>),
    >,
) {
    for (interaction, mut background, function) in q_interaction {
        let color = match function {
            ButtonFunction::Throw => THROW_COLOR,
            ButtonFunction::Despawn => DESPAWN_COLOR,
        };

        match *interaction {
            Interaction::Pressed => {
                match function {
                    ButtonFunction::Throw => commands.trigger(ThrowPressed),
                    ButtonFunction::Despawn => commands.trigger(DespawnPressed),
                };
                *background = color.darker(0.3).into();
            }
            Interaction::Hovered => {
                *background = color.darker(0.1).into();
            }
            Interaction::None => {
                *background = color.into();
            }
        }
    }
}