DEVELOPMENT ENVIRONMENT

~liljamo/bevy_dice

aa20709b30f5338d93187601846717ceaba4cc79 — Jonni Liljamo 2 days ago 4e633cd
feat(demo): replace egui combobox with bevy UI dropdown
M crates/bevy_dice_demo/Cargo.toml => crates/bevy_dice_demo/Cargo.toml +2 -1
@@ 9,6 9,7 @@ debug = [
  "bevy_dice/debug",
  "bevy/bevy_ui_debug",
  "avian3d/debug-plugin",
  "dep:bevy_egui",
  "dep:bevy-inspector-egui",
]
wasm = [


@@ 41,7 42,7 @@ bevy_embedded_assets = { version = "0.13", default-features = false }
bevy_egui = { version = "0.34", default-features = false, features = [
  "render",
  "default_fonts",
] }
], optional = true }
bevy-inspector-egui = { version = "0.31", optional = true }
console_error_panic_hook = { version = "0.1", optional = true }
web-time = { version = "1", optional = true }

M crates/bevy_dice_demo/src/main.rs => crates/bevy_dice_demo/src/main.rs +14 -25
@@ 11,7 11,8 @@ use std::collections::HashMap;
use avian3d::prelude::*;
use bevy::{asset::LoadState, prelude::*};
use bevy_dice::{DicePlugin, DicePluginConfig, Die, DieResult, DieVariant, ThrowDie};
use bevy_egui::{EguiContextPass, EguiContexts, EguiPlugin, egui};
#[cfg(feature = "debug")]
use bevy_egui::EguiPlugin;
use bevy_embedded_assets::EmbeddedAssetPlugin;
#[cfg(feature = "debug")]
use bevy_inspector_egui::quick::WorldInspectorPlugin;


@@ 47,11 48,11 @@ fn main() {
        PhysicsDebugPlugin::default(),
    ));

    #[cfg(feature = "debug")]
    app.add_plugins((
        EguiPlugin {
            enable_multipass_for_primary_context: true,
        },
        #[cfg(feature = "debug")]
        WorldInspectorPlugin::default(),
    ));



@@ 70,12 71,12 @@ fn main() {
        .add_systems(Update, wait_for_assets_to_load)
        .add_systems(OnEnter(ExampleState::Running), setup);

    app.init_resource::<UIState>()
        .add_systems(EguiContextPass, ui);
    app.init_resource::<State>();

    app.add_systems(Update, rotate_light);

    app.add_observer(throw_die)
    app.add_observer(variant_selected)
        .add_observer(throw_die)
        .add_observer(despawn_dice)
        .add_observer(show_die_result);



@@ 167,12 168,12 @@ struct ExampleAssets {
}

#[derive(Resource)]
struct UIState {
struct State {
    selected_variant: DieVariant,
    die_id: u32,
}

impl Default for UIState {
impl Default for State {
    fn default() -> Self {
        Self {
            selected_variant: DieVariant::D6,


@@ 181,35 182,23 @@ impl Default for UIState {
    }
}

fn ui(mut contexts: EguiContexts, mut r_ui_state: ResMut<UIState>) {
    egui::Window::new("Demo").show(contexts.ctx_mut(), |ui| {
        egui::ComboBox::from_label("")
            .selected_text(format!("{:?}", r_ui_state.selected_variant))
            .show_ui(ui, |ui| {
                for variant in DieVariant::VALUES {
                    ui.selectable_value(
                        &mut r_ui_state.selected_variant,
                        variant,
                        format!("{:?}", variant),
                    );
                }
            });
    });
fn variant_selected(trigger: Trigger<ui::VariantSelected>, mut r_state: ResMut<State>) {
    r_state.selected_variant = trigger.0;
}

fn throw_die(
    _trigger: Trigger<ui::ThrowPressed>,
    mut commands: Commands,
    mut r_ui_state: ResMut<UIState>,
    mut r_state: ResMut<State>,
) {
    commands.trigger(ThrowDie {
        id: r_ui_state.die_id,
        variant: r_ui_state.selected_variant,
        id: r_state.die_id,
        variant: r_state.selected_variant,
        angular_velocity: None,
        position: Some(Vec3::new(0.0, 4.0, 0.0)),
        rotation: None,
    });
    r_ui_state.die_id += 1;
    r_state.die_id += 1;
}

fn despawn_dice(

M crates/bevy_dice_demo/src/ui.rs => crates/bevy_dice_demo/src/ui.rs +216 -22
@@ 5,19 5,32 @@
 */

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

const THROW_COLOR: Srgba = LIME_400;
const DESPAWN_COLOR: Srgba = RED_400;
const VARIANT_COLOR: Srgba = AMBER_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_systems(Startup, setup).add_systems(
            Update,
            (
                button_interaction,
                variant_dropdown_interaction,
                variant_button_interaction,
            ),
        );

        app.add_event::<ThrowPressed>()
        app.add_event::<ToggleVariantButtons>()
            .add_event::<VariantSelected>()
            .add_event::<ThrowPressed>()
            .add_event::<DespawnPressed>();

        app.add_observer(toggle_variant_buttons)
            .add_observer(set_selected_variant_text);
    }
}



@@ 68,25 81,6 @@ fn button(function: ButtonFunction, text: impl Into<String>, color: Srgba) -> im
    )
}

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<


@@ 117,3 111,203 @@ fn button_interaction(
        }
    }
}

#[derive(Component)]
struct VariantDropdown;

#[derive(Component)]
struct SelectedVariantText;

#[derive(Event)]
struct ToggleVariantButtons;

#[derive(Component)]
struct VariantButton(DieVariant);

#[derive(Event)]
pub struct VariantSelected(pub DieVariant);

fn variant_button(variant: VariantButton, text: impl Into<String>) -> impl Bundle {
    (
        variant,
        Button,
        Node {
            width: Val::Px(120.0),
            height: Val::Px(40.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(VARIANT_COLOR)),
        BorderColor(Color::from(VARIANT_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,
        ),
        Visibility::Hidden,
        children![(
            Text::new(text),
            TextFont {
                font_size: 18.0,
                ..Default::default()
            },
            TextColor(Color::BLACK),
        ),],
    )
}

fn variant_dropdown_interaction(
    mut commands: Commands,
    q_interaction: Query<
        (&Interaction, &mut BackgroundColor),
        (Changed<Interaction>, With<VariantDropdown>),
    >,
) {
    for (interaction, mut background) in q_interaction {
        match *interaction {
            Interaction::Pressed => {
                commands.trigger(ToggleVariantButtons);
                *background = VARIANT_COLOR.darker(0.3).into();
            }
            Interaction::Hovered => {
                *background = VARIANT_COLOR.darker(0.1).into();
            }
            Interaction::None => {
                *background = VARIANT_COLOR.into();
            }
        }
    }
}

fn toggle_variant_buttons(
    _trigger: Trigger<ToggleVariantButtons>,
    mut q_variant_button_visibilities: Query<&mut Visibility, With<VariantButton>>,
) {
    for mut visibility in &mut q_variant_button_visibilities {
        visibility.toggle_visible_hidden();
    }
}

fn variant_button_interaction(
    mut commands: Commands,
    q_interaction: Query<
        (&Interaction, &mut BackgroundColor, &VariantButton),
        (Changed<Interaction>, With<Button>),
    >,
) {
    for (interaction, mut background, variant_button) in q_interaction {
        match *interaction {
            Interaction::Pressed => {
                commands.trigger(ToggleVariantButtons);
                commands.trigger(VariantSelected(variant_button.0));
                *background = VARIANT_COLOR.darker(0.3).into();
            }
            Interaction::Hovered => {
                *background = VARIANT_COLOR.darker(0.1).into();
            }
            Interaction::None => {
                *background = VARIANT_COLOR.into();
            }
        }
    }
}

fn set_selected_variant_text(
    trigger: Trigger<VariantSelected>,
    mut q_text: Query<&mut Text, With<SelectedVariantText>>,
) {
    if let Ok(mut text) = q_text.single_mut() {
        **text = format!("{:?}", trigger.0);
    }
}

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

    let dropdown = commands
        .spawn((
            Node {
                width: Val::Percent(100.0),
                flex_direction: FlexDirection::Column,
                overflow: Overflow::scroll_y(),
                ..Default::default()
            },
            children![(
                VariantDropdown,
                Button,
                Node {
                    width: Val::Px(120.0),
                    height: Val::Px(40.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(8.0)),
                    ..Default::default()
                },
                BackgroundColor(Color::from(VARIANT_COLOR)),
                BorderColor(Color::from(VARIANT_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![(
                    SelectedVariantText,
                    // D6 is the default.
                    Text::new("D6"),
                    TextFont {
                        font_size: 18.0,
                        ..Default::default()
                    },
                    TextColor(Color::BLACK),
                ),],
            )],
        ))
        .id();

    for variant in DieVariant::VALUES {
        commands.entity(dropdown).with_child(variant_button(
            VariantButton(variant),
            format!("{:?}", variant),
        ));
    }

    let layout = commands
        .spawn((Node {
            width: Val::Percent(100.0),
            height: Val::Percent(100.0),
            flex_direction: FlexDirection::Column,
            align_items: AlignItems::FlexEnd,
            justify_content: JustifyContent::SpaceBetween,
            padding: UiRect::all(Val::Px(8.0)),
            ..Default::default()
        },))
        .id();
    commands.entity(layout).add_children(&[dropdown, buttons]);
}