Move focus between text fields with tab and shift-tab
This commit is contained in:
parent
b17e6b3260
commit
e7fd11f1aa
4 changed files with 93 additions and 37 deletions
61
TODO.md
61
TODO.md
|
@ -4,7 +4,8 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
|
||||
## Top priority
|
||||
|
||||
* Refactor graphics layers and areas so one don't have to register LayerId:s.
|
||||
* Egui-web copy-paste
|
||||
* Egui-web fetch
|
||||
|
||||
## Other
|
||||
|
||||
|
@ -13,14 +14,6 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
* [ ] Tooltip widget: Something that looks like this: (?) :that shows text on hover.
|
||||
* [ ] ui.info_button().on_hover_text("More info here");
|
||||
* [ ] Allow adding multiple tooltips to the same widget, showing them all one after the other.
|
||||
* [ ] Text input
|
||||
* [x] Input
|
||||
* [x] Text focus
|
||||
* [x] Cursor movement
|
||||
* [x] Text selection
|
||||
* [x] Clipboard copy/paste
|
||||
* [ ] Text edit undo
|
||||
* [ ] Move focus with tab
|
||||
* [ ] Vertical slider
|
||||
* [/] Color picker
|
||||
* [x] linear rgb <-> sRGB
|
||||
|
@ -32,21 +25,13 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
* [ ] Additive blending aware color picker
|
||||
* [ ] Premultiplied alpha is a bit of a pain in the ass. Maybe rethink this a bit.
|
||||
* [ ] Hue wheel
|
||||
* Containers
|
||||
* [ ] Scroll areas
|
||||
* [x] Vertical scrolling
|
||||
* [x] Scroll-wheel input
|
||||
* [x] Drag background to scroll
|
||||
* [x] Kinetic scrolling
|
||||
* [ ] Horizontal scrolling
|
||||
* Input
|
||||
* [x] Distinguish between clicks and drags
|
||||
* [x] Double-click
|
||||
* [x] Text
|
||||
* [x] Get modifier keys
|
||||
* [x] Modifier keys
|
||||
* [ ] Support all mouse buttons
|
||||
* [ ] Distinguish between touch input and mouse input
|
||||
* [ ] Keyboard shortcuts (copy, paste, undo, select-all, ...?)
|
||||
* Text
|
||||
* [/] Unicode
|
||||
* [x] Shared mutable expanding texture map
|
||||
|
@ -91,26 +76,14 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
* [ ] Ask Egui if an event requires repainting
|
||||
* [ ] Only repaint when mouse is over a Egui window (or is pressed and there is an active widget)
|
||||
|
||||
## Backends
|
||||
|
||||
* [ ] Extract egui_app as egui_backend
|
||||
|
||||
* egui_glium
|
||||
* egui_web
|
||||
* [ ] async HTTP requests
|
||||
* [ ] egui_bitmap: slow reference rasterizer for tests
|
||||
* Port https://github.com/emilk/imgui_software_renderer
|
||||
* Less important: fast rasterizer for embedded 🤷♀️
|
||||
* [ ] egui_terminal (think ncurses)
|
||||
* [ ] replace `round_to_pixel` with `round_to_X` where user can select X to be e.g. width of a letter
|
||||
* [ ] egui_svg: No idea what this would be for :)
|
||||
## Integrations
|
||||
|
||||
### egui_web
|
||||
|
||||
* [x] Scroll input
|
||||
* [x] Change to resize cursor on hover
|
||||
* [x] Port most code to Rust
|
||||
* [x] Read url fragment and redirect to a subpage (e.g. different examples apps)]
|
||||
* [ ] Copy/paste support
|
||||
* [ ] Async HTTP requests
|
||||
* [ ] Fix WebGL colors/blending (try EXT_sRGB)
|
||||
* [ ] Embeddability
|
||||
|
@ -121,6 +94,17 @@ TODO-list for the Egui project. If you looking for something to do, look here.
|
|||
* Different Egui instances, same app
|
||||
* Allows very nice web integration
|
||||
|
||||
### Other
|
||||
|
||||
* [ ] Extract egui::app as own library (egui_framework ?)
|
||||
* [ ] egui_bitmap: slow reference rasterizer for tests
|
||||
* Port https://github.com/emilk/imgui_software_renderer
|
||||
* Less important: fast rasterizer for embedded 🤷♀️
|
||||
* [ ] egui_terminal (think ncurses)
|
||||
* [ ] replace `round_to_pixel` with `round_to_X` where user can select X to be e.g. width of a letter
|
||||
* [ ] egui_svg: No idea what this would be for :)
|
||||
|
||||
|
||||
## Modularity
|
||||
|
||||
* [x] `trait Widget` (`Label`, `Slider`, `Checkbox`, ...)
|
||||
|
@ -163,13 +147,24 @@ Ability to do a search for any widget. The search works even for collapsed regio
|
|||
* [x] Collapsing header region
|
||||
* [x] Tooltip
|
||||
* [x] Movable/resizable windows
|
||||
* [x] Kinetic windows
|
||||
* [x] Add support for clicking hyperlinks
|
||||
* [x] Text input
|
||||
* [x] Input
|
||||
* [x] Text focus
|
||||
* [x] Cursor movement
|
||||
* [x] Text selection
|
||||
* [x] Clipboard copy/paste
|
||||
* [x] Move focus with tab
|
||||
* [x] Text edit undo
|
||||
* Containers
|
||||
* [x] Vertical slider
|
||||
* [x] Resize any side and corner on windows
|
||||
* [x] Fix autoshrink
|
||||
* [x] Automatic positioning of new windows
|
||||
* [x] Vertical scroll areas
|
||||
* [x] Scroll-wheel input
|
||||
* [x] Drag background to scroll
|
||||
* [x] Kinetic scrolling
|
||||
* Simple animations
|
||||
* Clip rects
|
||||
* [x] Separate Ui::clip_rect from Ui::rect
|
||||
|
|
|
@ -205,7 +205,7 @@ impl Context {
|
|||
}
|
||||
|
||||
fn begin_frame_mut(&mut self, new_raw_input: RawInput) {
|
||||
self.memory().begin_frame(&self.input);
|
||||
self.memory().begin_frame(&self.input, &new_raw_input);
|
||||
|
||||
self.input = std::mem::take(&mut self.input).begin_frame(new_raw_input);
|
||||
*self.available_rect.lock() = Some(self.input.screen_rect());
|
||||
|
|
|
@ -83,6 +83,18 @@ pub(crate) struct Interaction {
|
|||
/// What had keyboard focus previous frame?
|
||||
pub kb_focus_id_previous_frame: Option<Id>,
|
||||
|
||||
/// If set, the next widget that is interested in kb_focus will automatically get it.
|
||||
/// Probably because the user pressed Tab.
|
||||
pub kb_focus_give_to_next: bool,
|
||||
|
||||
/// The last widget interested in kb focus.
|
||||
pub kb_focus_last_interested: Option<Id>,
|
||||
|
||||
/// Set at the beginning of the frame, set to `false` when "used".
|
||||
pressed_tab: bool,
|
||||
/// Set at the beginning of the frame, set to `false` when "used".
|
||||
pressed_shift_tab: bool,
|
||||
|
||||
/// HACK: windows have low priority on dragging.
|
||||
/// This is so that if you drag a slider in a window,
|
||||
/// the slider will steal the drag away from the window.
|
||||
|
@ -104,7 +116,11 @@ impl Interaction {
|
|||
self.click_id.is_some() || self.drag_id.is_some()
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, prev_input: &crate::input::InputState) {
|
||||
fn begin_frame(
|
||||
&mut self,
|
||||
prev_input: &crate::input::InputState,
|
||||
new_input: &crate::input::RawInput,
|
||||
) {
|
||||
self.kb_focus_id_previous_frame = self.kb_focus_id;
|
||||
self.click_interest = false;
|
||||
self.drag_interest = false;
|
||||
|
@ -118,13 +134,34 @@ impl Interaction {
|
|||
self.click_id = None;
|
||||
self.drag_id = None;
|
||||
}
|
||||
|
||||
self.pressed_tab = false;
|
||||
self.pressed_shift_tab = false;
|
||||
for event in &new_input.events {
|
||||
if let crate::input::Event::Key {
|
||||
key: crate::input::Key::Tab,
|
||||
pressed: true,
|
||||
modifiers,
|
||||
} = event
|
||||
{
|
||||
if modifiers.shift {
|
||||
self.pressed_shift_tab = true;
|
||||
} else {
|
||||
self.pressed_tab = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub(crate) fn begin_frame(&mut self, prev_input: &crate::input::InputState) {
|
||||
pub(crate) fn begin_frame(
|
||||
&mut self,
|
||||
prev_input: &crate::input::InputState,
|
||||
new_input: &crate::input::RawInput,
|
||||
) {
|
||||
self.used_ids.clear();
|
||||
self.interaction.begin_frame(prev_input);
|
||||
self.interaction.begin_frame(prev_input, new_input);
|
||||
|
||||
if !prev_input.mouse.down {
|
||||
self.window_interaction = None;
|
||||
|
@ -169,6 +206,26 @@ impl Memory {
|
|||
}
|
||||
}
|
||||
|
||||
/// Register this widget as being interested in getting keyboard focus.
|
||||
/// This will allow the user to select it with tab and shift-tab.
|
||||
pub fn interested_in_kb_focus(&mut self, id: Id) {
|
||||
if self.interaction.kb_focus_give_to_next {
|
||||
self.interaction.kb_focus_id = Some(id);
|
||||
self.interaction.kb_focus_give_to_next = false;
|
||||
} else if self.has_kb_focus(id) {
|
||||
if self.interaction.pressed_tab {
|
||||
self.interaction.kb_focus_id = None;
|
||||
self.interaction.kb_focus_give_to_next = true;
|
||||
self.interaction.pressed_tab = false;
|
||||
} else if self.interaction.pressed_shift_tab {
|
||||
self.interaction.kb_focus_id = self.interaction.kb_focus_last_interested;
|
||||
self.interaction.pressed_shift_tab = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.interaction.kb_focus_last_interested = Some(id);
|
||||
}
|
||||
|
||||
/// Stop editing of active `TextEdit` (if any).
|
||||
pub fn stop_text_input(&mut self) {
|
||||
self.interaction.kb_focus_id = None;
|
||||
|
|
|
@ -252,6 +252,10 @@ impl<'t> Widget for TextEdit<'t> {
|
|||
};
|
||||
let response = ui.interact(rect, id, sense);
|
||||
|
||||
if enabled {
|
||||
ui.memory().interested_in_kb_focus(id);
|
||||
}
|
||||
|
||||
if enabled {
|
||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||
// TODO: triple-click to select whole paragraph
|
||||
|
|
Loading…
Reference in a new issue