Path: blob/main/crates/bevy_feathers/src/controls/button.rs
6596 views
use bevy_app::{Plugin, PreUpdate};1use bevy_core_widgets::{Activate, Callback, CoreButton};2use bevy_ecs::{3bundle::Bundle,4component::Component,5entity::Entity,6hierarchy::{ChildOf, Children},7lifecycle::RemovedComponents,8query::{Added, Changed, Has, Or},9reflect::ReflectComponent,10schedule::IntoScheduleConfigs,11spawn::{SpawnRelated, SpawnableList},12system::{Commands, In, Query},13};14use bevy_input_focus::tab_navigation::TabIndex;15use bevy_picking::{hover::Hovered, PickingSystems};16use bevy_reflect::{prelude::ReflectDefault, Reflect};17use bevy_ui::{AlignItems, InteractionDisabled, JustifyContent, Node, Pressed, UiRect, Val};1819use crate::{20constants::{fonts, size},21cursor::EntityCursor,22font_styles::InheritableFont,23handle_or_path::HandleOrPath,24rounded_corners::RoundedCorners,25theme::{ThemeBackgroundColor, ThemeFontColor},26tokens,27};2829/// Color variants for buttons. This also functions as a component used by the dynamic styling30/// system to identify which entities are buttons.31#[derive(Component, Default, Clone, Reflect, Debug, PartialEq, Eq)]32#[reflect(Component, Clone, Default)]33pub enum ButtonVariant {34/// The standard button appearance35#[default]36Normal,37/// A button with a more prominent color, this is used for "call to action" buttons,38/// default buttons for dialog boxes, and so on.39Primary,40}4142/// Parameters for the button template, passed to [`button`] function.43#[derive(Default)]44pub struct ButtonProps {45/// Color variant for the button.46pub variant: ButtonVariant,47/// Rounded corners options48pub corners: RoundedCorners,49/// Click handler50pub on_click: Callback<In<Activate>>,51}5253/// Template function to spawn a button.54///55/// # Arguments56/// * `props` - construction properties for the button.57/// * `overrides` - a bundle of components that are merged in with the normal button components.58/// * `children` - a [`SpawnableList`] of child elements, such as a label or icon for the button.59pub fn button<C: SpawnableList<ChildOf> + Send + Sync + 'static, B: Bundle>(60props: ButtonProps,61overrides: B,62children: C,63) -> impl Bundle {64(65Node {66height: size::ROW_HEIGHT,67justify_content: JustifyContent::Center,68align_items: AlignItems::Center,69padding: UiRect::axes(Val::Px(8.0), Val::Px(0.)),70flex_grow: 1.0,71..Default::default()72},73CoreButton {74on_activate: props.on_click,75},76props.variant,77Hovered::default(),78EntityCursor::System(bevy_window::SystemCursorIcon::Pointer),79TabIndex(0),80props.corners.to_border_radius(4.0),81ThemeBackgroundColor(tokens::BUTTON_BG),82ThemeFontColor(tokens::BUTTON_TEXT),83InheritableFont {84font: HandleOrPath::Path(fonts::REGULAR.to_owned()),85font_size: 14.0,86},87overrides,88Children::spawn(children),89)90}9192fn update_button_styles(93q_buttons: Query<94(95Entity,96&ButtonVariant,97Has<InteractionDisabled>,98Has<Pressed>,99&Hovered,100&ThemeBackgroundColor,101&ThemeFontColor,102),103Or<(104Changed<Hovered>,105Changed<ButtonVariant>,106Added<Pressed>,107Added<InteractionDisabled>,108)>,109>,110mut commands: Commands,111) {112for (button_ent, variant, disabled, pressed, hovered, bg_color, font_color) in q_buttons.iter()113{114set_button_styles(115button_ent,116variant,117disabled,118pressed,119hovered.0,120bg_color,121font_color,122&mut commands,123);124}125}126127fn update_button_styles_remove(128q_buttons: Query<(129Entity,130&ButtonVariant,131Has<InteractionDisabled>,132Has<Pressed>,133&Hovered,134&ThemeBackgroundColor,135&ThemeFontColor,136)>,137mut removed_disabled: RemovedComponents<InteractionDisabled>,138mut removed_pressed: RemovedComponents<Pressed>,139mut commands: Commands,140) {141removed_disabled142.read()143.chain(removed_pressed.read())144.for_each(|ent| {145if let Ok((button_ent, variant, disabled, pressed, hovered, bg_color, font_color)) =146q_buttons.get(ent)147{148set_button_styles(149button_ent,150variant,151disabled,152pressed,153hovered.0,154bg_color,155font_color,156&mut commands,157);158}159});160}161162fn set_button_styles(163button_ent: Entity,164variant: &ButtonVariant,165disabled: bool,166pressed: bool,167hovered: bool,168bg_color: &ThemeBackgroundColor,169font_color: &ThemeFontColor,170commands: &mut Commands,171) {172let bg_token = match (variant, disabled, pressed, hovered) {173(ButtonVariant::Normal, true, _, _) => tokens::BUTTON_BG_DISABLED,174(ButtonVariant::Normal, false, true, _) => tokens::BUTTON_BG_PRESSED,175(ButtonVariant::Normal, false, false, true) => tokens::BUTTON_BG_HOVER,176(ButtonVariant::Normal, false, false, false) => tokens::BUTTON_BG,177(ButtonVariant::Primary, true, _, _) => tokens::BUTTON_PRIMARY_BG_DISABLED,178(ButtonVariant::Primary, false, true, _) => tokens::BUTTON_PRIMARY_BG_PRESSED,179(ButtonVariant::Primary, false, false, true) => tokens::BUTTON_PRIMARY_BG_HOVER,180(ButtonVariant::Primary, false, false, false) => tokens::BUTTON_PRIMARY_BG,181};182183let font_color_token = match (variant, disabled) {184(ButtonVariant::Normal, true) => tokens::BUTTON_TEXT_DISABLED,185(ButtonVariant::Normal, false) => tokens::BUTTON_TEXT,186(ButtonVariant::Primary, true) => tokens::BUTTON_PRIMARY_TEXT_DISABLED,187(ButtonVariant::Primary, false) => tokens::BUTTON_PRIMARY_TEXT,188};189190let cursor_shape = match disabled {191true => bevy_window::SystemCursorIcon::NotAllowed,192false => bevy_window::SystemCursorIcon::Pointer,193};194195// Change background color196if bg_color.0 != bg_token {197commands198.entity(button_ent)199.insert(ThemeBackgroundColor(bg_token));200}201202// Change font color203if font_color.0 != font_color_token {204commands205.entity(button_ent)206.insert(ThemeFontColor(font_color_token));207}208209// Change cursor shape210commands211.entity(button_ent)212.insert(EntityCursor::System(cursor_shape));213}214215/// Plugin which registers the systems for updating the button styles.216pub struct ButtonPlugin;217218impl Plugin for ButtonPlugin {219fn build(&self, app: &mut bevy_app::App) {220app.add_systems(221PreUpdate,222(update_button_styles, update_button_styles_remove).in_set(PickingSystems::Last),223);224}225}226227228