Path: blob/main/crates/bevy_feathers/src/controls/button.rs
9441 views
use bevy_app::{Plugin, PreUpdate};1use bevy_ecs::{2bundle::Bundle,3component::Component,4entity::Entity,5hierarchy::{ChildOf, Children},6lifecycle::RemovedComponents,7query::{Added, Changed, Has, Or},8reflect::ReflectComponent,9schedule::IntoScheduleConfigs,10spawn::{SpawnRelated, SpawnableList},11system::{Commands, Query},12};13use bevy_input_focus::tab_navigation::TabIndex;14use bevy_picking::{hover::Hovered, PickingSystems};15use bevy_reflect::{prelude::ReflectDefault, Reflect};16use bevy_text::FontSize;17use bevy_ui::{AlignItems, InteractionDisabled, JustifyContent, Node, Pressed, UiRect, Val};18use bevy_ui_widgets::Button;1920use crate::{21constants::{fonts, size},22cursor::EntityCursor,23font_styles::InheritableFont,24handle_or_path::HandleOrPath,25rounded_corners::RoundedCorners,26theme::{ThemeBackgroundColor, ThemeFontColor},27tokens,28};2930/// Color variants for buttons. This also functions as a component used by the dynamic styling31/// system to identify which entities are buttons.32#[derive(Component, Default, Clone, Reflect, Debug, PartialEq, Eq)]33#[reflect(Component, Clone, Default)]34pub enum ButtonVariant {35/// The standard button appearance36#[default]37Normal,38/// A button with a more prominent color, this is used for "call to action" buttons,39/// default buttons for dialog boxes, and so on.40Primary,41}4243/// Parameters for the button template, passed to [`button`] function.44#[derive(Default)]45pub struct ButtonProps {46/// Color variant for the button.47pub variant: ButtonVariant,48/// Rounded corners options49pub corners: RoundedCorners,50}5152/// Template function to spawn a button.53///54/// # Arguments55/// * `props` - construction properties for the button.56/// * `overrides` - a bundle of components that are merged in with the normal button components.57/// * `children` - a [`SpawnableList`] of child elements, such as a label or icon for the button.58///59/// # Emitted events60/// * [`bevy_ui_widgets::Activate`] when any of the following happens:61/// * the pointer is released while hovering over the button.62/// * the ENTER or SPACE key is pressed while the button has keyboard focus.63///64/// These events can be disabled by adding an [`bevy_ui::InteractionDisabled`] component to the entity65pub fn button<C: SpawnableList<ChildOf> + Send + Sync + 'static, B: Bundle>(66props: ButtonProps,67overrides: B,68children: C,69) -> impl Bundle {70(71Node {72height: size::ROW_HEIGHT,73justify_content: JustifyContent::Center,74align_items: AlignItems::Center,75padding: UiRect::axes(Val::Px(8.0), Val::Px(0.)),76flex_grow: 1.0,77border_radius: props.corners.to_border_radius(4.0),78..Default::default()79},80Button,81props.variant,82Hovered::default(),83EntityCursor::System(bevy_window::SystemCursorIcon::Pointer),84TabIndex(0),85ThemeBackgroundColor(tokens::BUTTON_BG),86ThemeFontColor(tokens::BUTTON_TEXT),87InheritableFont {88font: HandleOrPath::Path(fonts::REGULAR.to_owned()),89font_size: FontSize::Px(14.0),90},91overrides,92Children::spawn(children),93)94}9596fn update_button_styles(97q_buttons: Query<98(99Entity,100&ButtonVariant,101Has<InteractionDisabled>,102Has<Pressed>,103&Hovered,104&ThemeBackgroundColor,105&ThemeFontColor,106),107Or<(108Changed<Hovered>,109Changed<ButtonVariant>,110Added<Pressed>,111Added<InteractionDisabled>,112)>,113>,114mut commands: Commands,115) {116for (button_ent, variant, disabled, pressed, hovered, bg_color, font_color) in q_buttons.iter()117{118set_button_styles(119button_ent,120variant,121disabled,122pressed,123hovered.0,124bg_color,125font_color,126&mut commands,127);128}129}130131fn update_button_styles_remove(132q_buttons: Query<(133Entity,134&ButtonVariant,135Has<InteractionDisabled>,136Has<Pressed>,137&Hovered,138&ThemeBackgroundColor,139&ThemeFontColor,140)>,141mut removed_disabled: RemovedComponents<InteractionDisabled>,142mut removed_pressed: RemovedComponents<Pressed>,143mut commands: Commands,144) {145removed_disabled146.read()147.chain(removed_pressed.read())148.for_each(|ent| {149if let Ok((button_ent, variant, disabled, pressed, hovered, bg_color, font_color)) =150q_buttons.get(ent)151{152set_button_styles(153button_ent,154variant,155disabled,156pressed,157hovered.0,158bg_color,159font_color,160&mut commands,161);162}163});164}165166fn set_button_styles(167button_ent: Entity,168variant: &ButtonVariant,169disabled: bool,170pressed: bool,171hovered: bool,172bg_color: &ThemeBackgroundColor,173font_color: &ThemeFontColor,174commands: &mut Commands,175) {176let bg_token = match (variant, disabled, pressed, hovered) {177(ButtonVariant::Normal, true, _, _) => tokens::BUTTON_BG_DISABLED,178(ButtonVariant::Normal, false, true, _) => tokens::BUTTON_BG_PRESSED,179(ButtonVariant::Normal, false, false, true) => tokens::BUTTON_BG_HOVER,180(ButtonVariant::Normal, false, false, false) => tokens::BUTTON_BG,181(ButtonVariant::Primary, true, _, _) => tokens::BUTTON_PRIMARY_BG_DISABLED,182(ButtonVariant::Primary, false, true, _) => tokens::BUTTON_PRIMARY_BG_PRESSED,183(ButtonVariant::Primary, false, false, true) => tokens::BUTTON_PRIMARY_BG_HOVER,184(ButtonVariant::Primary, false, false, false) => tokens::BUTTON_PRIMARY_BG,185};186187let font_color_token = match (variant, disabled) {188(ButtonVariant::Normal, true) => tokens::BUTTON_TEXT_DISABLED,189(ButtonVariant::Normal, false) => tokens::BUTTON_TEXT,190(ButtonVariant::Primary, true) => tokens::BUTTON_PRIMARY_TEXT_DISABLED,191(ButtonVariant::Primary, false) => tokens::BUTTON_PRIMARY_TEXT,192};193194let cursor_shape = match disabled {195true => bevy_window::SystemCursorIcon::NotAllowed,196false => bevy_window::SystemCursorIcon::Pointer,197};198199// Change background color200if bg_color.0 != bg_token {201commands202.entity(button_ent)203.insert(ThemeBackgroundColor(bg_token));204}205206// Change font color207if font_color.0 != font_color_token {208commands209.entity(button_ent)210.insert(ThemeFontColor(font_color_token));211}212213// Change cursor shape214commands215.entity(button_ent)216.insert(EntityCursor::System(cursor_shape));217}218219/// Plugin which registers the systems for updating the button styles.220pub struct ButtonPlugin;221222impl Plugin for ButtonPlugin {223fn build(&self, app: &mut bevy_app::App) {224app.add_systems(225PreUpdate,226(update_button_styles, update_button_styles_remove).in_set(PickingSystems::Last),227);228}229}230231232