Skip to content

Commit

Permalink
Focus handling for buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
Exidex committed Apr 15, 2024
1 parent e7926f9 commit d228ed6
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 8 deletions.
5 changes: 5 additions & 0 deletions style/src/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub trait StyleSheet {
/// Produces the active [`Appearance`] of a button.
fn active(&self, style: &Self::Style) -> Appearance;

/// Produces the focused [`Appearance`] of a button.
fn focused(&self, style: &Self::Style, _is_active: bool) -> Appearance {
self.hovered(style)
}

/// Produces the hovered [`Appearance`] of a button.
fn hovered(&self, style: &Self::Style) -> Appearance {
let active = self.active(style);
Expand Down
109 changes: 101 additions & 8 deletions widget/src/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
//!
//! A [`Button`] has some local [`State`].
use crate::core::event::{self, Event};
use crate::core::layout;
use crate::core::keyboard::key;
use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::touch;
use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation;
use crate::core::widget::{operation, Operation};
use crate::core::{keyboard, layout, widget};
use crate::core::{
Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle,
Shell, Size, Vector, Widget,
Expand Down Expand Up @@ -56,6 +57,7 @@ where
Theme: StyleSheet,
Renderer: crate::core::Renderer,
{
id: Option<Id>,
content: Element<'a, Message, Theme, Renderer>,
on_press: Option<Message>,
width: Length,
Expand All @@ -78,6 +80,7 @@ where
let size = content.as_widget().size_hint();

Button {
id: None,
content,
on_press: None,
width: size.width.fluid(),
Expand All @@ -88,6 +91,12 @@ where
}
}

/// Sets the [`Id`] of the [`Button`].
pub fn id(mut self, id: Id) -> Self {
self.id = Some(id);
self
}

/// Sets the width of the [`Button`].
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
Expand Down Expand Up @@ -189,6 +198,9 @@ where
renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
let state = tree.state.downcast_mut::<State>();
operation.focusable(state, self.id.as_ref().map(|id| &id.0));

operation.container(None, layout.bounds(), &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
Expand Down Expand Up @@ -312,14 +324,75 @@ where
/// The local state of a [`Button`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct State {
is_focused: bool,
is_pressed: bool,
is_active: bool,
}

impl State {
/// Creates a new [`State`].
pub fn new() -> State {
State::default()
}

fn is_focused(&self) -> bool {
self.is_focused
}

fn pressed(&mut self) {
self.is_focused = true;
self.is_pressed = true;
self.is_active = true;
}

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

fn reset(&mut self) {
self.is_pressed = false;
self.is_active = false;
self.is_focused = false;
}
}

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

fn focus(&mut self) {
State::reset(self);
State::focus(self)
}

fn unfocus(&mut self) {
Self::reset(self)
}
}

/// The identifier of a [`Button`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Id(widget::Id);

impl Id {
/// Creates a custom [`Id`].
pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
Self(widget::Id::new(id))
}

/// Creates a unique [`Id`].
///
/// This function produces a different [`Id`] every time it is called.
pub fn unique() -> Self {
Self(widget::Id::unique())
}
}

impl From<Id> for widget::Id {
fn from(id: Id) -> Self {
id.0
}
}

/// Processes the given [`Event`] and updates the [`State`] of a [`Button`]
Expand All @@ -338,12 +411,13 @@ pub fn update<'a, Message: Clone>(
if on_press.is_some() {
let bounds = layout.bounds();

let state = state();
if cursor.is_over(bounds) {
let state = state();

state.is_pressed = true;
state.pressed();

return event::Status::Captured;
} else {
state.reset();
}
}
}
Expand All @@ -353,7 +427,7 @@ pub fn update<'a, Message: Clone>(
let state = state();

if state.is_pressed {
state.is_pressed = false;
state.reset();

let bounds = layout.bounds();

Expand All @@ -368,7 +442,21 @@ pub fn update<'a, Message: Clone>(
Event::Touch(touch::Event::FingerLost { .. }) => {
let state = state();

state.is_pressed = false;
state.reset();
}
Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
match key.as_ref() {
keyboard::Key::Named(key::Named::Enter) => {
let state = state();
if state.is_focused() {
if let Some(on_press) = on_press.clone() {
shell.publish(on_press);
state.pressed();
}
}
}
_ => {}
}
}
_ => {}
}
Expand Down Expand Up @@ -402,7 +490,12 @@ where
theme.hovered(style)
}
} else {
theme.active(style)
let state = state();
if state.is_focused {
theme.focused(style, state.is_active)
} else {
theme.active(style)
}
};

if styling.background.is_some()
Expand Down

0 comments on commit d228ed6

Please sign in to comment.