Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename Widget::spatial_nav → nav_next and auto-implement #345

Merged
merged 11 commits into from
Aug 10, 2022
16 changes: 9 additions & 7 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ pub trait Layout {
/// #[derive(Debug)]
/// #[widget {
/// layout = button: self.label;
/// key_nav = true;
/// navigable = true;
/// hover_highlight = true;
/// }]
/// pub struct TextButton<M: Clone + Debug + 'static> {
Expand Down Expand Up @@ -436,11 +436,11 @@ pub trait Widget: WidgetChildren + Layout {
let _ = mgr;
}

/// Is this widget navigable via Tab key?
/// Is this widget navigable via <kbd>Tab</kbd> key?
///
/// Defaults to `false`.
#[inline]
fn key_nav(&self) -> bool {
fn navigable(&self) -> bool {
false
}

Expand Down Expand Up @@ -472,17 +472,19 @@ pub trait Widget: WidgetChildren + Layout {
/// Both `from` and the return value use the widget index, as used by
/// [`WidgetChildren::get_child`].
///
/// The default implementation often suffices: it will navigate through
/// children in order.
/// Default implementation:
///
/// - Generated from `#[widget]`'s layout property, if used
/// - Otherwise, iterate through children in order of definition
#[inline]
fn spatial_nav(
fn nav_next(
&mut self,
mgr: &mut ConfigMgr,
reverse: bool,
from: Option<usize>,
) -> Option<usize> {
let _ = mgr;
crate::util::spatial_nav(reverse, from, self.num_children())
crate::util::nav_next(reverse, from, self.num_children())
}

/// Pre-event-handler
Expand Down
6 changes: 4 additions & 2 deletions crates/kas-core/src/event/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,9 @@ impl<'a> EventMgr<'a> {
{
if let Some(layer) = self.accel_layers.get(&id) {
// but only when Alt is held or alt-bypass is enabled:
if self.modifiers.alt() || layer.0 {
if self.modifiers == ModifiersState::ALT
|| layer.0 && self.modifiers == ModifiersState::empty()
{
if let Some(id) = layer.1.get(&vkey).cloned() {
target = Some(id);
n = i;
Expand All @@ -535,7 +537,7 @@ impl<'a> EventMgr<'a> {
if let Some(id) = target {
if widget
.find_widget(&id)
.map(|w| w.key_nav())
.map(|w| w.navigable())
.unwrap_or(false)
{
self.set_nav_focus(id.clone(), true);
Expand Down
10 changes: 5 additions & 5 deletions crates/kas-core/src/event/manager/config_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl<'a> ConfigMgr<'a> {
/// Advance the keyboard navigation focus
///
/// If some widget currently has nav focus, this will give focus to the next
/// (or previous) widget under `widget` where [`Widget::key_nav`]
/// (or previous) widget under `widget` where [`Widget::navigable`]
/// returns true; otherwise this will give focus to the first (or last)
/// such widget.
///
Expand Down Expand Up @@ -184,12 +184,12 @@ impl<'a> ConfigMgr<'a> {
{
return Some(id);
}
} else if !widget.eq_id(focus) && widget.key_nav() {
} else if !widget.eq_id(focus) && widget.navigable() {
return Some(widget.id());
}

loop {
if let Some(index) = widget.spatial_nav(mgr, rev, child) {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
Expand All @@ -212,7 +212,7 @@ impl<'a> ConfigMgr<'a> {
}

loop {
if let Some(index) = widget.spatial_nav(mgr, rev, child) {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
Expand All @@ -221,7 +221,7 @@ impl<'a> ConfigMgr<'a> {
}
child = Some(index);
} else {
return if !widget.eq_id(focus) && widget.key_nav() {
return if !widget.eq_id(focus) && widget.navigable() {
Some(widget.id())
} else {
None
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/event/manager/mgr_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ impl EventState {

/// Set the keyboard navigation focus directly
///
/// Normally, [`Widget::key_nav`] will be true for the specified
/// Normally, [`Widget::navigable`] will be true for the specified
/// widget, but this is not required, e.g. a `ScrollLabel` can receive focus
/// on text selection with the mouse. (Currently such widgets will receive
/// events like any other with nav focus, but this may change.)
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/event/manager/mgr_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ impl<'a> EventMgr<'a> {
// No mouse grab but have a hover target
if self.config.mouse_nav_focus() {
if let Some(w) = widget.find_widget(&start_id) {
if w.key_nav() {
if w.navigable() {
self.set_nav_focus(w.id(), false);
}
}
Expand All @@ -454,7 +454,7 @@ impl<'a> EventMgr<'a> {
if let Some(id) = start_id.as_ref() {
if self.config.touch_nav_focus() {
if let Some(w) = widget.find_widget(id) {
if w.key_nav() {
if w.navigable() {
self.set_nav_focus(w.id(), false);
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for TryFormat<'a, T> {
}
}

/// Generic implementation of [`crate::Widget::spatial_nav`]
pub fn spatial_nav(reverse: bool, from: Option<usize>, len: usize) -> Option<usize> {
/// Generic implementation of [`crate::Widget::nav_next`]
pub fn nav_next(reverse: bool, from: Option<usize>, len: usize) -> Option<usize> {
let last = len.wrapping_sub(1);
if last == usize::MAX {
return None;
Expand Down
35 changes: 16 additions & 19 deletions crates/kas-macros/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use crate::make_layout;
use proc_macro2::{Span, TokenStream};
use proc_macro_error::{abort, emit_error};
use proc_macro_error::abort;
use quote::quote_spanned;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
Expand Down Expand Up @@ -159,7 +159,7 @@ mod kw {
custom_keyword!(align);
custom_keyword!(halign);
custom_keyword!(valign);
custom_keyword!(key_nav);
custom_keyword!(navigable);
custom_keyword!(hover_highlight);
custom_keyword!(cursor_icon);
custom_keyword!(handle);
Expand Down Expand Up @@ -187,31 +187,30 @@ pub struct ExprToken {

#[derive(Debug, Default)]
pub struct WidgetArgs {
pub key_nav: Option<TokenStream>,
pub navigable: Option<TokenStream>,
pub hover_highlight: Option<BoolToken>,
pub cursor_icon: Option<ExprToken>,
pub derive: Option<Member>,
pub layout: Option<make_layout::Tree>,
pub layout: Option<(Span, make_layout::Tree)>,
}

impl Parse for WidgetArgs {
fn parse(content: ParseStream) -> Result<Self> {
let mut key_nav = None;
let mut navigable = None;
let mut hover_highlight = None;
let mut cursor_icon = None;
let mut kw_derive = None;
let mut derive = None;
let mut kw_layout = None;
let mut layout = None;

while !content.is_empty() {
let lookahead = content.lookahead1();
if lookahead.peek(kw::key_nav) && key_nav.is_none() {
let span = content.parse::<kw::key_nav>()?.span();
if lookahead.peek(kw::navigable) && navigable.is_none() {
let span = content.parse::<kw::navigable>()?.span();
let _: Eq = content.parse()?;
let value = content.parse::<syn::LitBool>()?;
key_nav = Some(quote_spanned! {span=>
fn key_nav(&self) -> bool { #value }
navigable = Some(quote_spanned! {span=>
fn navigable(&self) -> bool { #value }
});
} else if lookahead.peek(kw::hover_highlight) && hover_highlight.is_none() {
hover_highlight = Some(BoolToken {
Expand All @@ -232,27 +231,25 @@ impl Parse for WidgetArgs {
let _: Token![.] = content.parse()?;
derive = Some(content.parse()?);
} else if lookahead.peek(kw::layout) && layout.is_none() {
kw_layout = Some(content.parse::<kw::layout>()?);
let kw = content.parse::<kw::layout>()?;
let _: Eq = content.parse()?;
layout = Some(content.parse()?);
layout = Some((kw.span, content.parse()?));
} else {
return Err(lookahead.error());
}

let _ = content.parse::<Token![;]>()?;
}

if let Some(derive) = kw_derive {
if let Some(layout) = kw_layout {
emit_error!(
layout, "incompatible with derive";
note = derive.span() => "this derive"
);
if let Some(_derive) = kw_derive {
if let Some((span, _)) = layout {
return Err(Error::new(span, "incompatible with widget derive"));
// note = derive.span() => "this derive"
}
}

Ok(WidgetArgs {
key_nav,
navigable,
hover_highlight,
cursor_icon,
derive,
Expand Down
Loading