Skip to content

Commit

Permalink
Allow Button widgets to be focusable
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismanning committed Jan 19, 2025
1 parent dcd4abd commit 6f630f3
Showing 1 changed file with 79 additions and 6 deletions.
85 changes: 79 additions & 6 deletions widget/src/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
//! }
//! ```
use crate::core::border::{self, Border};
use crate::core::keyboard;
use crate::core::layout;
use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::theme::palette;
use crate::core::touch;
use crate::core::widget::operation::Focusable;
use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation;
use crate::core::window;
Expand Down Expand Up @@ -82,6 +84,7 @@ where
clip: bool,
class: Theme::Class<'a>,
status: Option<Status>,
focusable: bool,
}

enum OnPress<'a, Message> {
Expand Down Expand Up @@ -119,6 +122,7 @@ where
clip: false,
class: Theme::default(),
status: None,
focusable: false,
}
}

Expand Down Expand Up @@ -197,11 +201,33 @@ where
self.class = class.into();
self
}

/// Sets whether the [`Button`] is focusable.
#[must_use]
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = focusable;
self
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
struct State {
is_pressed: bool,
is_focused: bool,
}

impl Focusable for State {
fn is_focused(&self) -> bool {
self.is_focused
}

fn focus(&mut self) {
self.is_focused = true;
}

fn unfocus(&mut self) {
self.is_focused = false;
}
}

impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
Expand Down Expand Up @@ -262,6 +288,10 @@ where
renderer: &Renderer,
operation: &mut dyn Operation,
) {
if self.focusable {
let state = tree.state.downcast_mut::<State>();
operation.focusable(None, layout.bounds(), state);
}
operation.container(None, layout.bounds(), &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
Expand Down Expand Up @@ -336,6 +366,38 @@ where

state.is_pressed = false;
}
Event::Keyboard(keyboard::Event::KeyPressed {ref key, ..}) if self.focusable => {
match key {

Check failure on line 370 in widget/src/button.rs

View workflow job for this annotation

GitHub Actions / all

this `match` can be collapsed into the outer `match`
keyboard::Key::Named(keyboard::key::Named::Enter)
| keyboard::Key::Named(keyboard::key::Named::Space) => {
if self.on_press.is_some() {
let state = tree.state.downcast_mut::<State>();
if state.is_focused {
state.is_pressed = true;

shell.capture_event();
}
}
}
_ => {}
}
}
Event::Keyboard(keyboard::Event::KeyReleased {ref key, ..}) if self.focusable => {
match key {

Check failure on line 386 in widget/src/button.rs

View workflow job for this annotation

GitHub Actions / all

this `match` can be collapsed into the outer `match`
keyboard::Key::Named(keyboard::key::Named::Enter)
| keyboard::Key::Named(keyboard::key::Named::Space) => {
if let Some(on_press) = &self.on_press {
let state = tree.state.downcast_mut::<State>();
if state.is_focused && state.is_pressed {
state.is_pressed = false;
shell.publish(on_press.get());
shell.capture_event();
}
}
}
_ => {}
}
}
_ => {}
}

Expand All @@ -349,6 +411,15 @@ where
} else {
Status::Hovered
}
} else if self.focusable {
let state = tree.state.downcast_ref::<State>();
if state.is_pressed {
Status::Pressed
} else if state.is_focused {
Status::Focused
} else {
Status::Active
}
} else {
Status::Active
};
Expand Down Expand Up @@ -474,6 +545,8 @@ pub enum Status {
Pressed,
/// The [`Button`] cannot be pressed.
Disabled,
/// The [`Button`] is focused.
Focused,
}

/// The style of a button.
Expand Down Expand Up @@ -595,7 +668,7 @@ pub fn primary(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
background: Some(Background::Color(palette.primary.base.color)),
..base
},
Expand All @@ -610,7 +683,7 @@ pub fn secondary(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
background: Some(Background::Color(palette.secondary.strong.color)),
..base
},
Expand All @@ -625,7 +698,7 @@ pub fn success(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
background: Some(Background::Color(palette.success.strong.color)),
..base
},
Expand All @@ -640,7 +713,7 @@ pub fn warning(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
background: Some(Background::Color(palette.warning.strong.color)),
..base
},
Expand All @@ -655,7 +728,7 @@ pub fn danger(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
background: Some(Background::Color(palette.danger.strong.color)),
..base
},
Expand All @@ -674,7 +747,7 @@ pub fn text(theme: &Theme, status: Status) -> Style {

match status {
Status::Active | Status::Pressed => base,
Status::Hovered => Style {
Status::Hovered | Status::Focused => Style {
text_color: palette.background.base.text.scale_alpha(0.8),
..base
},
Expand Down

0 comments on commit 6f630f3

Please sign in to comment.