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

Global Store: Focus #18

Open
wants to merge 100 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
3a50f2f
keyboard navigation rfc initial commit
jaunkst Nov 30, 2022
1f2cd83
keyboard events example cleanup
jaunkst Dec 1, 2022
bbcab92
keyboard events example cleanup
jaunkst Dec 1, 2022
44b25f7
keyboard events example cleanup
jaunkst Dec 1, 2022
8f33f54
keyboard events example cleanup
jaunkst Dec 2, 2022
0267c32
keyboard events example cleanup
jaunkst Dec 2, 2022
0873f40
keyboard events example cleanup
jaunkst Dec 2, 2022
ec3b47e
keyboard events example cleanup
jaunkst Dec 2, 2022
0dd0e0c
keyboard events example cleanup
jaunkst Dec 2, 2022
9f1d2bd
keyboard events example cleanup
jaunkst Dec 2, 2022
a7751c6
keyboard events example cleanup
jaunkst Dec 2, 2022
499125c
keyboard events example cleanup
jaunkst Dec 2, 2022
5c7a14d
keyboard events example cleanup
jaunkst Dec 2, 2022
625734f
keyboard events example cleanup
jaunkst Dec 2, 2022
97b1534
keyboard events example cleanup
jaunkst Dec 2, 2022
53e7915
keyboard events example cleanup
jaunkst Dec 2, 2022
6ea0716
keyboard events example cleanup
jaunkst Dec 2, 2022
2f142d4
keyboard events example cleanup
jaunkst Dec 2, 2022
3fbe1a8
keyboard events example cleanup
jaunkst Dec 2, 2022
079e93d
keyboard events example cleanup
jaunkst Dec 2, 2022
9dc35a2
keyboard events example cleanup
jaunkst Dec 2, 2022
ef324c6
keyboard events example cleanup
jaunkst Dec 2, 2022
1e667ee
keyboard events example cleanup
jaunkst Dec 2, 2022
16135d8
keyboard events example cleanup
jaunkst Dec 2, 2022
bce8e99
keyboard events example cleanup
jaunkst Dec 2, 2022
5846e49
keyboard events example cleanup
jaunkst Dec 2, 2022
de5d571
Update 0000-shared-widget-state.md
jaunkst Dec 2, 2022
8c488f0
Update 0000-shared-widget-state.md
jaunkst Dec 2, 2022
1dd4b4d
keyboard events example cleanup
jaunkst Dec 2, 2022
0870dc8
keyboard events example cleanup
jaunkst Dec 2, 2022
aced5e1
keyboard events example cleanup
jaunkst Dec 2, 2022
6f622e6
keyboard events example cleanup
jaunkst Dec 2, 2022
cf52c7a
keyboard events example cleanup
jaunkst Dec 2, 2022
defba7b
keyboard events example cleanup
jaunkst Dec 2, 2022
8d4bab8
keyboard events example cleanup
jaunkst Dec 2, 2022
b471377
keyboard events example cleanup
jaunkst Dec 2, 2022
b47287f
keyboard events example cleanup
jaunkst Dec 2, 2022
ed8a599
keyboard events example cleanup
jaunkst Dec 2, 2022
c2d3e6d
focus metadata RFC added
jaunkst Dec 3, 2022
77bcdc3
focus metadata RFC added
jaunkst Dec 3, 2022
f94f606
focus metadata RFC added
jaunkst Dec 3, 2022
efa37cc
focus metadata RFC added
jaunkst Dec 3, 2022
288e139
focus metadata RFC added
jaunkst Dec 3, 2022
74f0acb
focus metadata RFC added
jaunkst Dec 3, 2022
8731d33
focus metadata RFC added
jaunkst Dec 3, 2022
d704b74
focus metadata RFC added
jaunkst Dec 3, 2022
a501983
focus metadata RFC added
jaunkst Dec 3, 2022
71ffc7e
focus metadata RFC added
jaunkst Dec 3, 2022
9508e31
focus metadata RFC added
jaunkst Dec 3, 2022
7dcde27
focus metadata RFC added
jaunkst Dec 3, 2022
ed900ba
focus metadata RFC added
jaunkst Dec 3, 2022
272428f
focus metadata RFC added
jaunkst Dec 3, 2022
48b50dd
focus metadata RFC added
jaunkst Dec 3, 2022
a5400de
focus metadata RFC added
jaunkst Dec 3, 2022
3130ecc
focus metadata RFC added
jaunkst Dec 3, 2022
659549b
focus metadata RFC added
jaunkst Dec 3, 2022
9010183
focus metadata RFC added
jaunkst Dec 3, 2022
a2171e2
focus metadata RFC added
jaunkst Dec 3, 2022
def1347
focus metadata RFC added
jaunkst Dec 3, 2022
34fabba
focus metadata RFC added
jaunkst Dec 3, 2022
9921aef
focus metadata RFC added
jaunkst Dec 3, 2022
6e5953d
focus metadata RFC added
jaunkst Dec 3, 2022
74333e0
focus metadata RFC added
jaunkst Dec 3, 2022
87199d9
focus metadata RFC added
jaunkst Dec 3, 2022
6302803
focus metadata RFC added
jaunkst Dec 3, 2022
6d6efd9
focus metadata RFC added
jaunkst Dec 3, 2022
5259a12
focus metadata RFC added
jaunkst Dec 3, 2022
fe2f0e2
focus metadata RFC added
jaunkst Dec 3, 2022
a848e45
focus metadata RFC added
jaunkst Dec 3, 2022
df0b097
focus metadata RFC added
jaunkst Dec 3, 2022
ee45607
focus metadata RFC added
jaunkst Dec 3, 2022
ec24cc5
focus metadata RFC added
jaunkst Dec 3, 2022
9407ad3
focus metadata RFC added
jaunkst Dec 3, 2022
44ddade
focus metadata RFC added
jaunkst Dec 4, 2022
43a4424
focus metadata RFC added
jaunkst Dec 4, 2022
5965a6c
focus metadata RFC added
jaunkst Dec 4, 2022
b2025d8
focus metadata RFC added
jaunkst Dec 4, 2022
8920809
focus metadata RFC added
jaunkst Dec 4, 2022
9232352
focus metadata RFC added
jaunkst Dec 4, 2022
5fc6fd7
focus metadata RFC added
jaunkst Dec 4, 2022
8c92fbb
focus metadata RFC added
jaunkst Dec 4, 2022
f827d8d
focus metadata RFC added
jaunkst Dec 4, 2022
3b47fc4
focus metadata RFC added
jaunkst Dec 4, 2022
ddca8c9
focus metadata RFC added
jaunkst Dec 4, 2022
85b52ce
focus metadata RFC added
jaunkst Dec 4, 2022
9271b56
focus metadata RFC added
jaunkst Dec 4, 2022
199c2cc
focus metadata RFC added
jaunkst Dec 4, 2022
fbd5ed9
focus metadata RFC added
jaunkst Dec 4, 2022
398ac82
focus metadata RFC added
jaunkst Dec 4, 2022
98e402d
focus metadata RFC added
jaunkst Dec 4, 2022
cd0b3f7
focus metadata RFC added
jaunkst Dec 4, 2022
2ed5ba9
focus metadata RFC added
jaunkst Dec 4, 2022
da763b5
focus metadata RFC added
jaunkst Dec 4, 2022
bae3b6d
focus metadata RFC added
jaunkst Dec 4, 2022
7daa634
focus metadata RFC added
jaunkst Dec 4, 2022
68701a8
focus metadata RFC added
jaunkst Dec 4, 2022
383aca0
focus metadata RFC added
jaunkst Dec 4, 2022
45d4f55
focus metadata RFC added
jaunkst Dec 4, 2022
9b50478
focus metadata RFC added
jaunkst Dec 4, 2022
5240860
focus metadata RFC added
jaunkst Dec 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added example.rs
Empty file.
238 changes: 238 additions & 0 deletions text/0000-focus-and-metadata .md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Focus & Attributes
>>> ## Status: Discovery

## Summary

This proposal is for a shared attributes API. It is intended to be used by the focus system, but is not limited to that use case. It is also intended to be used by any system that needs to store or retrieve attributes on an element.

## Motivation

Why are we doing this?
To support keyboard navigation and focus management in the runtime in a way that is consistent with the browser runtime.

What use cases does it support?
- Accessibility and keyboard navigation.
- Gamepad navigation.
- Element Attributes

What is the expected outcome?
A consistent API for storing and retrieving attributes on elements.
Keyboard navigation and focus management that is consistent with the browser runtime.

It isn't intended to be a 1 to 1 parity. It is intended to be a consistent API that can be used to build a consistent experience.

## Guide-level explanation

The goal is to design an API will be familiar to the developer and an a default experience that is an analogous the browser runtime.

By doing so we are more likely to meet the expectations of the developer. The browser runtime is the most common runtime for web developers and is has very robust and researched implementation.

> Explaining the feature largely in terms of examples.

The `ElementAttributes` API is an agreed contract that internal systems and other widgets can interface with. At this time I am not considering custom, or data-* attributes.

### Example

The `ElementAttributes` holds a prescribed set of properties that can be used to determine the focus order of the element. The `ElementAttributes` also holds a `focusable` property that can be used to determine if the element is focusable or not.

We should make the implementation of the `ElementAttributes` as simple as possible.
Could this be done automatically? Can we get a handle to the attributes from the widget if we did so?

```rs
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
Button {
attributes: ElementAttributes::new(),
content: content.into(),
on_press: None,
width: Length::Shrink,
height: Length::Shrink,
padding: Padding::new(5),
style: <Renderer::Theme as StyleSheet>::Style::default(),
}
}

pub fn focusable(&mut self, focusable: bool) -> &mut Self {
self.attributes.focusable = focusable;
self
}

pub fn focus_order(&mut self, focus_order: u32) -> &mut Self {
self.attributes.focus_order = focus_order;
self
}

pub fn is_focused(&self) -> bool {
Element::is_focused(&self.attributes)
}
```
### Styling

A default focus style would be applied to all focusable widgets. It can be overridden by the user if they want to by defining a custom style for the focused state.

To override the default styling we can use the `ElementAttributes` to access the attributes.

```rs
pub fn draw(
&self,
renderer: &mut Renderer,
bounds: Rectangle,
cursor_position: Point,
viewport: &Rectangle,
) -> Renderer::Output {
let is_mouse_over = bounds.contains(cursor_position);
let is_focused = self.attributes.is_focused();

let mut styling = if !is_enabled {
style_sheet.disabled(style)
} else {
match (is_focused, is_hovered) {
(true, false) => style_sheet.focused(style),
(false, true) => style_sheet.hovered(style),
(false, false) => style_sheet.active(style),
}
};
}
```

> Explaining how iced programmers should *think* about the feature, and how it should impact the way they use iced. It should explain the impact as concretely as possible.

`ElementAttributes` is where you can inform the runtime how to handle your widget.
Setting attributes like `focusable`, `focus_order`, or `autofocus` will allow the runtime to handle keyboard navigation and focus management in a way that is consistent with the browser runtime.

This gives everyone a specification to work from. It also gives us a way to build a consistent experience.

## Implementation strategy

The basic idea is to place shared attributes in some sort of Mutex, or RwLock. This will allow us to share the attributes between the runtime and the widget. The runtime will be able to update the attributes and the widget will be able to read the attributes. Widgets or the Application should be able to query or update the attributes.

A cache should be implemented to to reduce locking and improve performance. The cache should be invalidated when the attributes is updated.

I am unsure we need to support a bag of attributes. I think we can get away with a set of attributes that are defined in the `ElementAttributes` struct. Data attributes can be added later if needed if we need to support sharing data between widgets.

The `ElementAttributes` could look something like this.

```rs
#[derive(Debug, Clone)]
pub struct ElementAttributes {
id: Id,
focusable: bool,
focus_order: i32
}
```


The `InternalElementState` is stored in a `RwLock`. The struct will look something like this. This attributes could expand in the future to support more accessibility features like ARIA.

```rs
pub struct InternalElementState {
focused_id: Option<Id>,
attributes: HashMap<Id, ElementAttributes>,
}

lazy_static!(
pub static ref INTERNAL_ELEMENT_STATE: InternalElementState = {
InternalElementState {
focused_id: None,
attributes: HashMap::new(),
}
};
);
```


> Application Scope

- The application should be responsible for managing the focus.
- The application should be responsible for setting the focus on the first element.
- The application will also be responsible for setting the focus on the next element when the `Tab` key is pressed.
- The application will also be responsible for setting the focus on the previous element when the `Shift + Tab` key is pressed.

> Query

The `ElementAttributes` will provide a other global methods that will allow us to close the gap between the browser runtime and iced. We should be able to query the attributes in the application or the widget.

## Drawbacks

The drawback of this proposal is that it follows the browser runtime. This may not be the best solution for iced. The browser runtime is not the best runtime for all applications. This may not be the best solution for all applications.

However there is nothing to stop someone from not using this API and implementing their own focus management system.

We should not do this if it is not possible to provide a good experience for the following use cases.
- Accessibility and keyboard navigation.
- Gamepad navigation.
- Developer Experience
- User Experience
- Performance
- Maintainability

## Rationale and alternatives

> Why is this design the best in the space of possible designs?

It is the best design because it is the most similar to the browser runtime. This will allow developers to use the same patterns they are used to. The developer and the user will not have a jagged learning curve. This will allow developers to focus on the application logic and not the focus management. For the users of Iced applications they will have a friendly and familiar experiences out of the box.

> What other designs have been considered and what is the rationale for not choosing them?

In my previous RFC I attempted to solve the need of a global state via a global state management solution. Specifically one focused on UI state. The downside to that solution is it adds an additional state management solution to the mix. This will add complexity to the application. I think state management should be isolated to another initiative.

I also researched flutters `Focus Widget` based solution. As discussed in my previous RFC it is complex in nature and requires a proxy widget to wrap your widgets in.

> What is the impact of not doing this?

The impact of not doing this is that we will not be able to provide a good experience for the following use cases.

- Accessibility and keyboard navigation.
- Gamepad navigation.
- Developer Experience
- User Experience
- Maintainability


## [Optional] Prior art

>Discuss prior art, both the good and the bad, in relation to this proposal.
A few examples of what this can include are:
> Does this feature exist in other GUI toolkits and what experience have their community had?

This feature exists in the browser runtime. This is the most familiar experience for developers.

> Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background.

Browser focus management is a complex topic. I have found the following resources to be helpful.

- [W3C Focus Order:](https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-order.html)
- [ARIA Developing a Keyboard Interface](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface)


## Unresolved questions

> What parts of the design do you expect to resolve through the RFC process before this gets merged?

- Is the the direction we should be headed in? Is there a better solution? Is there a better way to implement this?

- How do we rerender the widget when the attributes changes?

> What parts of the design do you expect to resolve through the implementation of this feature before stabilization?

- Default styling for focus. We will need to set the default styling for focus if a focus style is not provided. This will allow us to provide a good experience out of the box.

- We may need to optimize the what the exact data structure will be for the shared state. And what strategies we will have the best characteristics for locking and caching the shared state.

> What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?

- We should consider how to handle parent child relationships. This may be a future feature. We may need to always create a attributes handle for all widgets in the future.

- Hover state is also something that could be added to the attributes.

## [Optional] Future possibilities

Expanding on this concept to support more accesibility features like ARIA.
Other widget attributes that could be handled by the runtime.

- Z-Index: The renderer could use this to determine the order of the widgets. This could be used to implement a stacking context.

- Visibility: This would also allow us to skip rendering and state updates if the widget is not visible. This would be a great performance improvement. Especially for large applications or infinite scroll applications.

- Cached Bounds: This would allow us to cache the bounds of the widget. This would allow us to skip the layout pass if the widget is not visible. This would be a great performance improvement. Especially for large applications or infinite scroll applications.

- Data Bags: This would allow us to share data between widgets.
159 changes: 159 additions & 0 deletions text/0000-keyboard-navigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Feature Name

## Summary

Implement focus for essential components to enable keyboard / gamepad / accessibility input navigation and interactions.

## Motivation

Why are we doing this? What use cases does it support? What is the expected outcome?
- Why?
- To support nagivgating between focusable widgets.
- What use cases does it support?
- Keyboard navigation and interactions
- What is the expected outcome?
- As a user I should be able to navigate the widget tree with different stratagies
- Neighbor Strategy (Current Implementation)

## Guide-level explanation

Explain the proposal as if it was already included in the library and you were teaching it to another iced programmer. That generally means:

- Introducing new named concepts.

## The following widgets support focus based navigation and interactions
- text_input : When this widget has focus it will allow you to input text.
- button:
- With focus it will 'click' with space, enter or num enter
- Esc will clear focus
- pick_list:
- With focus it will 'open' with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- With focus and is 'open' you may navigate the options with KeyCode::Up & KeyCode::Down
- With focus and is 'open' you may select an option with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- Esc will close and clear focus
- toggler:
- With focus it will 'toggle' with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- Esc will exit focus
- slider:
- With focus it will toggle 'grab' with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- With focus and is 'grabbed' you may step the values the options with KeyCode::Left & KeyCode::Right (Or other directions if verticality is a implemented.)
- Esc will ungrab and clear focus
- radio:
- With focus it will 'select' current option with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- Esc will clear focus
- checkbox
- With focus it will 'check' with KeyCode::Space, KeyCode::Enter, or KeyCode::NumpadEnter
- Esc will clear focus
- scrollable
- With focus it will 'scroll' with KeyCode::Space, KeyCode::PageDown, KeyCode::PageUp, KeyCode::Home ect..
- (Questionable) If enabled scrollable will scroll to a child if the child gains focus and it is out of view.



## Implementation strategy

Only one widget should have focus at a given time. At this time widgets are storing their own focus state.
Focus navigation is implemented based on this RFC and current implementation in text_input.

https://github.com/iced-rs/rfcs/blob/master/text/0007-widget-operations.md

https://github.com/iced-rs/iced/blob/master/native/src/widget/text_input.rs


## Drawbacks

The current implementation is using the path created by this RFC.
https://github.com/iced-rs/rfcs/blob/master/text/0007-widget-operations.md

The drawbacks to staying is that we can create unstable UI states by allowing multiple widgets to own their own focus states.
The drawbacks to moving to a global focus is that we will need to do more discovery and prolong the benefits of reaping the pattern we already have in place.

## Rationale and alternatives

- Why is this design the best in the space of possible designs?
- We should
- What other designs have been considered and what is the rationale for not choosing them?
- A global state design as discussed in this RFC would be ideal.
- I am not convienced that we cannot maintain the current API for the most part and strangle each widgets internal focus state away to the global atomic. We would implement focus() functions that would interact with the global atomic focus_id.
- What is the impact of not doing this?
- Staying as is means we can move forward with adding focus to the essential widgets.
- Keeping multiple sources of truth will probably lead to buggy UX as more components are introduced into the system.


## [Optional] Prior art

Discuss prior art, both the good and the bad, in relation to this proposal.
A few examples of what this can include are:

- Does this feature exist in other GUI toolkits and what experience have their community had?
- Yes, Its a very important feature for critical adoption.

This section is intended to encourage you as an author to think about the lessons from other toolkits, provide readers of your RFC with a fuller picture.
If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages.

Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC.
Please also take into consideration that iced sometimes intentionally diverges from common toolkit features.


## Unresolved questions

- Can we wait on global state in favor of enabling a critical feature for existing essential components?
- Ideally we could update the current implementation in a way that allows us to strangle the underlying details of global focus_id in a way with minimal or zero changes to the current exposed intergrations API.


## [Optional] Future possibilities

How would we implement a global state?

A global atomic id is used to track the current focused widget id?
All focusable widgets are given an atomic id.
```rs
static FOCUSED_ID: AtomicUsize = AtomicUsize::new(0);
```

Custom ids are not longer provided but retrieved. The application should keep its own map of atomics.
Custom string identifiers will return when `Once cell` is stable and `unsafe` is no longer required.
```rs
iced::widget::id();
```

## global functions are available to change current focus
```rs
iced::widget::count() // a structure that has the current focus_id, and number of focusables
iced::widget::focus(usize) // focus on a widget
iced::widget::unfocus() // release global focus as usize 0
```

Tab Style Navigation (Neighbor Strategy)
```rs
iced::widget::focus_next() // next sibling
iced::widget::focus_previous() // prev sibling
```

## Gamepad / Accessibility Navigation (Cardinal)
This strategy is based on screen space position of focusable widgets. A cardinal graph is built only for focusable widgets for fast navigation and avoid unesessary sqrt.

Gamepad & Accessibility navigation and interactions
- Keyboard navigation provides the lowest order implementation
- Emulated events could be sent to the enable accessiblity & gamepad interactions


## New global functions would be available to change current focus

```rs
iced::widget::focus_up() // up from current focus
iced::widget::focus_down() // down from current focus
iced::widget::focus_right() // right of current focus
iced::widget::focus_left() // left of current focus
```



## To support non-native devices like a gamepad or accessibility device these events can be emitted artifically.
- Keyboard(keyboard::Event)
- Mouse(mouse::Event)
- Touch(touch::Event)

```
No idea what this API needs to look like at this time.
```
Loading