Skip to content
This repository has been archived by the owner on May 12, 2022. It is now read-only.

Commit

Permalink
feat(ui): handle key input in widgets based on ButtonMixin
Browse files Browse the repository at this point in the history
This includes `Button`, `Checkbox`, and `RadioButton`.
  • Loading branch information
yvt committed May 19, 2020
1 parent fe5b359 commit 2d116af
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 7 deletions.
81 changes: 78 additions & 3 deletions tcw3/src/ui/mixins/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use flags_macro::flags;
use std::{cell::Cell, rc::Rc};

use crate::{
pal,
pal::Wm,
uicore::{HViewRef, MouseDragListener},
uicore::{HViewRef, KeyEvent, MouseDragListener},
};

/// A view listener mix-in that allows the client to implement the behaviour of
Expand Down Expand Up @@ -37,6 +38,13 @@ bitflags! {
struct StateFlags: u8 {
const DRAG = 1;
const PRESS = 1 << 1;
const KEY_PRESS = 1 << 2;
}
}

impl StateFlags {
fn is_pressed(self) -> bool {
self.intersects(StateFlags::PRESS | StateFlags::KEY_PRESS)
}
}

Expand All @@ -56,6 +64,24 @@ impl ButtonMixin {
}
}

/// Handles [`ViewListener::focus_leave`].
///
/// [`ViewListener::focus_leave`]: crate::uicore::ViewListener::focus_leave
pub fn focus_leave(
&self,
wm: Wm,
view: HViewRef<'_>,
listener: Box<dyn ButtonListener + 'static>,
) {
// Cancel key press
self.inner.set_state(
&*listener,
wm,
view,
self.inner.state.get() - StateFlags::KEY_PRESS,
);
}

/// Handles [`ViewListener::mouse_drag`].
///
/// [`ViewListener::mouse_drag`]: crate::uicore::ViewListener::mouse_drag
Expand All @@ -69,6 +95,51 @@ impl ButtonMixin {
})
}

pub fn key_down(
&self,
wm: Wm,
view: HViewRef<'_>,
e: &KeyEvent<'_>,
listener: Box<dyn ButtonListener + 'static>,
) -> bool {
if e.translate_accel(&ACCEL_TABLE) == Some(ACTION_PRESS) {
self.inner.set_state(
&*listener,
wm,
view,
self.inner.state.get() | StateFlags::KEY_PRESS,
);
true
} else {
false
}
}

pub fn key_up(
&self,
wm: Wm,
view: HViewRef<'_>,
e: &KeyEvent<'_>,
listener: Box<dyn ButtonListener + 'static>,
) -> bool {
if e.translate_accel(&ACCEL_TABLE) == Some(ACTION_PRESS) {
if self.inner.state.get().contains(StateFlags::KEY_PRESS) {
self.inner.set_state(
&*listener,
wm,
view,
self.inner.state.get() - StateFlags::KEY_PRESS,
);
listener.activate(wm, view);
true
} else {
false
}
} else {
false
}
}

/// Get a flag indicating if the push button is currently pressed.
///
/// This method returns `true` if the push button is currently pressed down
Expand All @@ -78,10 +149,14 @@ impl ButtonMixin {
/// button to be drawn. The client should listen to changes in this value by
/// implementing [`ButtonListener::update`].
pub fn is_pressed(&self) -> bool {
self.inner.state.get().contains(StateFlags::PRESS)
self.inner.state.get().is_pressed()
}
}

const ACTION_PRESS: pal::ActionId = 0;
static ACCEL_TABLE: pal::AccelTable =
pal::accel_table![(ACTION_PRESS, windows(" "), macos(" "), gtk(" "))];

struct MouseDragListenerImpl {
inner: Rc<Inner>,
client_listener: Box<dyn ButtonListener + 'static>,
Expand Down Expand Up @@ -145,7 +220,7 @@ impl Inner {
view: HViewRef<'_>,
new_flags: StateFlags,
) {
let should_call_update = (new_flags ^ self.state.get()).contains(StateFlags::PRESS);
let should_call_update = new_flags.is_pressed() != self.state.get().is_pressed();
self.state.set(new_flags);
if should_call_update {
listener.update(wm, view);
Expand Down
32 changes: 28 additions & 4 deletions tcw3/src/ui/views/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
theming::{roles, ClassSet, HElem, Manager, StyledBox, Widget},
views::Label,
},
uicore::{HView, HViewRef, Sub, ViewFlags, ViewListener},
uicore::{HView, HViewRef, KeyEvent, Sub, ViewFlags, ViewListener},
};

/// A push button widget.
Expand Down Expand Up @@ -136,7 +136,21 @@ struct ButtonViewListener {
inner: Rc<Inner>,
}

impl ButtonViewListener {
fn build_button_mixin_listener(&self) -> Box<dyn crate::ui::mixins::button::ButtonListener> {
Box::new(ButtonMixinListener {
inner: Rc::clone(&self.inner),
})
}
}

impl ViewListener for ButtonViewListener {
fn focus_leave(&self, wm: pal::Wm, view: HViewRef<'_>) {
self.inner
.button_mixin
.focus_leave(wm, view, self.build_button_mixin_listener())
}

fn mouse_drag(
&self,
_: pal::Wm,
Expand All @@ -146,9 +160,19 @@ impl ViewListener for ButtonViewListener {
) -> Box<dyn crate::uicore::MouseDragListener> {
self.inner
.button_mixin
.mouse_drag(Box::new(ButtonMixinListener {
inner: Rc::clone(&self.inner),
}))
.mouse_drag(self.build_button_mixin_listener())
}

fn key_down(&self, wm: pal::Wm, view: HViewRef<'_>, e: &KeyEvent<'_>) -> bool {
self.inner
.button_mixin
.key_down(wm, view, e, self.build_button_mixin_listener())
}

fn key_up(&self, wm: pal::Wm, view: HViewRef<'_>, e: &KeyEvent<'_>) -> bool {
self.inner
.button_mixin
.key_up(wm, view, e, self.build_button_mixin_listener())
}
}

Expand Down

0 comments on commit 2d116af

Please sign in to comment.