diff --git a/example.rs b/example.rs new file mode 100644 index 0000000..e69de29 diff --git a/text/0000-focus-and-metadata .md b/text/0000-focus-and-metadata .md new file mode 100644 index 0000000..6431b98 --- /dev/null +++ b/text/0000-focus-and-metadata .md @@ -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>) -> Self { + Button { + attributes: ElementAttributes::new(), + content: content.into(), + on_press: None, + width: Length::Shrink, + height: Length::Shrink, + padding: Padding::new(5), + style: ::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, + attributes: HashMap, +} + +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. \ No newline at end of file diff --git a/text/0000-keyboard-navigation.md b/text/0000-keyboard-navigation.md new file mode 100644 index 0000000..64ab638 --- /dev/null +++ b/text/0000-keyboard-navigation.md @@ -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. +``` diff --git a/text/0000-shared-widget-state.md b/text/0000-shared-widget-state.md new file mode 100644 index 0000000..5a3aa1f --- /dev/null +++ b/text/0000-shared-widget-state.md @@ -0,0 +1,415 @@ +# Global Store: Focus + +>>> ## Status: Discussing +>>> I am still discovering the best way to do this with Iced. I will update this RFC as I learn more, as I am lacking critical knowledge around the widget lifecycle and messaging. + +## Summary + +This proposal introduces a global store concept to facilitate shared state between widgets. The store is designed to be a single source of truth for a given widget tree. It is managed by a centralized controller and is accessible to all widgets in the application. The store is meant to be used as a domain oriented (widgets) state management tool and should not be used as a replacement for existing state management patterns. + +## Motivation +### Why are we doing this? +Many projects need mouse, keyboard, and or gamepad interactions with Iced widgets. This will require a robust solution for focus management. I have found managing replicated state across the widgets to be an unruly pattern. I hope this RFC can help discover the best possible course of action for Iced even if it just starts a more serious conversation to another iteration of approach. + +I have created an example of implementing focus with the current Iced API. The example can be found [Here]( +https://github.com/jaunkst/iced/blob/core_widget_focus/examples/keyboard_navigation/src/main.rs). This example is a work in progress and is not meant to be a final solution. It is meant to be a starting point for discussion. It reveals some of the challenges of implementing focus with the current Iced API. + +One of the challenges is keeping focus state in sync across the entire widget tree. This is a problem because the widget tree is mutable. The current solution is to replicate focus state across all focusable widgets in the widget tree. This is a viable solution but it is possible there are better approaches in handling focus. + +### What use cases does it support? +Creating a core widget library that supports mouse, keyboard, and or gamepad interactions. +Supporting future shared state requirements. + +### What is the expected outcome? +We should be able to implement keyboard driven events to fully navigate and interact with an Iced UI with an agreed apon pattern, open discovery to a new RFC, or update the existing widgets to support this under the existing approach. + +It would be desirable to implement a store pattern. This would allow focus state to be shared across the entire widget tree. It would also allow focus state to be updated in a single place and make it easier to keep state in sync across the entire widget tree. + +# Lets Set Our "Focus" On The Store +It is important that we implement a healthy pattern to manage focus state across the entire widget tree. This will allow us to implement keyboard driven events to fully navigate and interact with an Iced UI. + +The focus store is made up for a few parts. +- A state structure that stores the current focus state. +- A read trait that allows widgets to query the current focus state. +- A write trait that allows widgets to update the current focus state. + + + +> GOOD! + +The widget would implement the read trait and the application would implement the write trait. This will allow the application to update the focus state and the widgets to query the focus state. Since the application is the only one that can update the focus state it can also handle the keyboard events and update the focus state. + +```mermaid + graph LR; + Application--Call-->UIWrite; + UIWrite([UIWrite])--Write-->State[(State)]; + UIQuery([UIQuery])--Signal-->Widget + Widget-->Draw{{Draw}} + Widget--UpdateMessage-->Application; + State-->UIQuery; +``` + + +> NOT SO GOOD ! + +Widgets could also implement the write trait if they need to update the focus state. This would be useful for widgets that are not focusable. For example a button widget that would not be focusable but it could be used to navigate the widget tree. However this is usally bad practice and should be avoided. +```mermaid + graph LR; + UIWrite([UIWrite])--Write-->State[(State)]; + UIQuery([UIQuery])--Signal-->Widget + State-->UIQuery; + Widget--Call-->UIWrite; + Widget-->Draw{{Draw}} +``` + + +The first thing we need to implement is the write trait at the application level. This will allow the widget to update the focus state in the application state, but also allow Application itself to update the focus state on keyboard events. + +> ## Application Level + +A default implementation is provided so we can simply do this to implement the write trait. +```rs +struct State {} +impl UIWrite for State {} +``` + +However you could implement the trait yourself or override the default implementation if you want to customize the behavior. + +Lets take a moment and look at the default implementation. It will look alot like a reducer pattern. The default implementation will take the current state update the focus state and update the global lock state. This is important because the widget tree is mutable and we need to prevent the focus state from being updated by multiple widgets at the same time. + +```rs + +pub trait UIUpdate { + fn set_focus_id(id: &Id) { + UI_STATE.lock_mut().focus_id = Some(id.clone()); + } + + fn unset_focus_id(id: &Id) { + if State::is_focused(&Some(id.clone())) { + let mut lock = UI_STATE.lock_mut(); + *lock = UIState { + focus_id: None, + ..*lock + }; + } + } + + fn clear_focus_id() { + let mut lock = UI_STATE.lock_mut(); + *lock = UIState { + focus_id: None, + ..*lock + }; + } +} + + +``` + +Next we will implement a message type that will be used to update the focus state from a widget event like a button click. +```rs +pub enum Message { + Focus(Id), + Unfocus(Id), +} + +fn update(&mut self, message: Message) -> Command { + match message { + Message::Focus(id:Id) => { + UIWrite::set_focus(&id); + } + Message::Unfocus(id:Id) => { + UIWrite::unset_focus(&id); + } + } +} +``` + +Now we need a unique Id for each focusable widget. This Id should be stored somewhere with your widgets internal state. +> ## Widget Level +```rs +struct State { + id: Id +} + +imple State { + fn new() -> State { + State { + id: Some(Id::unique()), + ..State::default() + } + } +} +``` + +We also implement the default read trait for on our widget and being watching for state changes. + +```rs +struct State {} +impl UIQuery for State {} +``` + +We should display the focused widget in some way to the user. This can be done by adding a border around the widget or changing the color of the widget. + +Your draw function should implement something like this. + +```rs +// Is the mouse over the widget? +let is_hovered = bounds.contains(cursor_position); + +// Is the widget focused? +let is_focused = state.is_focused(); + +// Determain the current state of the widget and set the style accordingly. +let mut styling = if !is_enabled { + style_sheet.disabled(style) +} else { + match (is_focused, is_hovered) { + (true, true) => style_sheet.focused_hovered(style), + (true, false) => style_sheet.focused(style), + (false, true) => style_sheet.hovered(style), + (false, false) => style_sheet.active(style), + } +}; + +``` + +By following this pattern we should be able to coordinate the focus state between the application and the widgets. This will allow us to implement keyboard driven events to fully navigate and interact with an Iced UI. While providing a healthy pattern that could be used for other shared state requirements in the future. + +This is a common pattern that is used in other UI frameworks. +### Keyboard Navigation + +The first thing we need to do is implement a way to navigate the widget tree with the keyboard. This can be done by implementing the `Application` trait for your application `State` struct. This trait will be used to handle keyboard events and update the focus state of the widget tree through the subscription and update methods. + +```rs +fn subscription(&self) -> Subscription { + subscription::events_with(|event, status| match (event, status) { + ( + iced::Event::Keyboard(keyboard::Event::KeyPressed { + key_code: keyboard::KeyCode::Tab, + modifiers: _, + .. + }), + iced::event::Status::Ignored, + ) => Some(Message::TabPressed), + _ => None, + }) +} + +fn update(&mut self, message: Message) -> Command { + match message { + Message::TabPressed => UIWrite::focus_next(), + } +} +``` + +### Gamepad Navigation + +Gamepad navigation is achived by remapping gamepad events to keyboard events. In this example I will be using an imaginary crate to handle gamepad events. This crate would be used to map gamepad events and emit them as a `Message` to your application. + +```rs +fn update(&mut self, message: Message) -> Command { + match message { + Message::DUp => UIWrite::focus_up(), + Message::DDown => UIWrite::focus_down(), + Message::DLeft => UIWrite::focus_prev(), + Message::DRight => UIWrite::focus_next(), + _ => (), + } +} +``` +As seen above we have mapped the directional pad on the gamepad to the keyboard events. This will allow us to navigate the widget tree with the gamepad. + +For a compelte mapping we would need to map the gamepad messages to keyboard events. At this this time we do not desire to solve for a multi-platform keyboard virualization. In this scenerio linux I could use the evdev, or gilrs crate to map the gamepad events to keyboard events. On windows it could use the winapi crate to map the gamepad events to keyboard events. On mac the core-graphics crate to map the gamepad events to keyboard events. This is out of scope for this RFC. + +> # Implementation strategy +> ## The details of how we store and retrieve the focus state should be discussed here. The following code is an example of how this could be implemented. This is not meant to be a final solution but a starting point for discussion. + +> +> This is the most important part of the RFC. +> +> Explain the design in sufficient detail that: +> - Its interaction with other features is clear. +> - It is reasonably clear how the feature would be implemented. +> - Corner cases are dissected by example. + +### UIQuery Trait +The `UIQuery` trait will be used to query the focus state of the widget tree. + +```rs +pub trait UIQuery { + fn set_focus_id(id: &Id); + fn unset_focus_id(id: &Id); + fn clear_focus_id(); +} +``` + +Its implementation might look like so +```rs + fn get_focus_id() -> Option { + UI_STATE.lock_ref().focus_id.clone() + } + + fn is_focused(id: &Option) -> bool { + if id.is_none() { + return false; + } + &Self::get_focus_id() == id + } + + fn signal() -> MutableSignalCloned { + UI_STATE.signal_cloned() + } +``` + + +### UIWrite Trait +The `UIWrite` trait will be used to store the `Id` of the widget and provide methods to update state. + +```rs +pub trait UIWrite { + pub fn focus(&mut self); + pub fn unfocus(&mut self); +} +``` + +And its implementation would look something like this. +```rs +struct State {} +impl UIQuery for State {} + +pub trait UIWriter { + fn set_focus_id(id: &Id) { + UI_STATE.lock_mut().focus_id = Some(id.clone()); + } + + fn unset_focus_id(id: &Id) { + if State::is_focused(&Some(id.clone())) { + let mut lock = UI_STATE.lock_mut(); + *lock = UIState { + focus_id: None, + ..*lock + }; + } + } + + fn clear_focus_id() { + let mut lock = UI_STATE.lock_mut(); + *lock = UIState { + focus_id: None, + ..*lock + }; + } +} +``` + +### UIWriter Focus Methods +This is an example of how we could implement the default focus methods on the `UIWrite` trait. + +```rs +pub fn focus_next() { + // Take 1 from the current focus id. + let mut focus = FocusQuery::focus_id(); + let mut next = None; + + if let Some(id) = focus { + let mut ids = IDS.with(|ids| ids.borrow().clone()); + let mut index = ids.iter().position(|i| i == &id).unwrap_or(0); + + if index < ids.len() - 1 { + index += 1; + } else { + index = 0; + } + + next = Some(ids[index].clone()); + } else { + let ids = IDS.with(|ids| ids.borrow().clone()); + next = Some(ids[0].clone()); + } + + if let Some(id) = next { + focus.replace(Some(id)); + } + + // update the ui state + UIWriter::set_focus_id(focus.clone()); +} +``` +> The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. + +As your can see from the examples above, the `UIWriter` trait will be used to update the focus state of a widget. The `UIQuery` trait will be used to recieve the focus state. The `focus_next`, `focus_prev`, `focus_up`, and `focus_down` methods will be used to update the focus state of the widget tree. + +## How We Teach This +There are many simular existing guides and tutorials that can be used to teach this feature. +- This implementation is a feature rich variant of this pattern https://ngneat.github.io/elf/ +- It is similar to Redux, but easier to use by avoiding use of actions and dispatchers. +- I can write documentation and best practices as part of the definition of done. + +## Drawbacks +> Why should we *not* do this? + +- If we desire complete purity and do not wish to share state between widgets, this will not work. However, I believe that this is a good compromise between purity and usability. +- If we desire to use a dispatchers and actions pattern, this will not work. +- This will not support decorating widgets not owned by the user with focusability. We would need to consider how to implement a handler pattern or something for this. This is also something that could be added later as a feature of `Widget`. + +## Rationale and alternatives + +> Why is this design the best in the space of possible designs? + +- Very easy to use and understand. +- Easy to replicate the pattern without getting it wrong. +- Flexible and can be used to implement focus on any widget. +- Allows for the user to implement their own focus logic if they desire. +- It wouldn't be a difficult guide to write. Easy to learn, easy to use. + +> What other designs have been considered and what is the rationale for not choosing them? + +- I have considered using a `Focus` widget to handle focus. This would allow the user to wrap any widget in a `Focus` widget and it would handle focus for that widget. This would be a bit more complicated to use and would require the user to wrap every widget they want to be focusable in a `Focus` widget. This also can leans to a much more complex and object oriented design. And would create diffcult patterns to follow. See Flutters: https://docs.flutter.dev/development/ui/advanced/focus +- I am in the favor of using a chain of responsiblity pattern or a handler pattern. This would allow the user to add focusability to any widget they desire in place of a complex proxy widget pattern. + +> What is the impact of not doing this? + +If we do not implement this, we will have to implement focus state on a widget by widget basis to achive this functionality. Or we will have to implement a dispatcher and action pattern to handle focus state. + +The last option is to continue with the pattern we have now. This will be the easiest to implement but will be the most defect prone especially with third-party widgets bring in unknown internal state. + +## [Optional] Prior art + +> This section is intended to encourage you as an author to think about the lessons from other languages, 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. + +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 is a common pattern in GUI frameworks. Here is one recent implementation. +- https://ngneat.github.io/elf/ + +Redux or MobX are patterns are similar to this proposal. They are also used to store state in a global store. This proposal is similar to those patterns but it does not rely on action or dispatchers. It also does not require the user to implement their own actions and dispatchers. While promoting reusability. + +> 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. + +- https://netbasal.com/introducing-akita-a-new-state-management-pattern-for-angular-applications-f2f0fab5a8 +- https://engineering.datorama.com/akita-react-hooks-a-recipe-for-sensational-state-management-2fd077c6237c + +I don't think we are designing an entirely new pattern here. We are just applying a pattern that is already used in other domains to the GUI domain. We do not intend to reinvent the wheel or solve for a complete state management solution to application developers. + +However it could be the start of a nice state management solution as it matures. I believe it is a strong foundation for a state management solution. + +## Unresolved questions + +> What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What is the best data structure and lifetime for the focus store? +- I need a better understanding of the draw method and how it is called. I am not sure if it is called on every frame or if it is called when the widget is focused. If it is called on every frame, then we will need to modify the proposal to allow for that. I noticed that Iced does not redraw when watching my profiler so I think its safe to assume the draw is reactive to the state of the widget. + +> What parts of the design do you expect to resolve through the implementation of this feature beore stabilization? +- The exact data structure and lifetime for the focus store. I really hope to get some feedback on this proposal and I hope to get some help implementing this feature. Especially with the draw method. I am not sure how to implement this feature without knowing how the draw method is called. + +> 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? +- Styling the focused widget. I think this is out of scope for this proposal. I have noticed that composing `Appearances` isn't very easy. I think this is something that should be addressed in the future. For example being in focus and hovered at the same time isnt as easy as merging the two appearances. This is due to the fact that rust doesn't offer a great way to destructure multiple structs at once. + +> ## [Optional] Future possibilities + +- Developing and exposing a shared state pattern that can be used to store state in a global store. This would allow for the user to easily implement presistant state in their applications via a common pattern. This would obviously take another RFC and it might be an opportunity to create a more robust solution. +- This pattern can also reach full time travel debugging capabilities. This is a feature that is not currently supported by Iced but could be added in the future.