App::take_blur_request, and a press on a focused select_on_focus field positions the cursor
Two gaps found while using trust's address box on a phone, both about what happens to a text field once it holds the focus. Dropping the focus. An app could ask for the focus of a widget, through `App::take_focus_request`, but had no way to give it up. A browser needs to: once an address is submitted the field has done its job, yet it kept the focus, so the on-screen keyboard stayed over the page that had just been asked for, and came back whenever the window was raised, since ltk enables the input method again on the text-input `enter` that follows while a text field holds the focus. `App::take_blur_request` is the counterpart: return `true` once and the run loop calls `set_focus( keyboard_focus, None )`, the same call the back request already uses to blur a text field, so the text-input disable and `on_text_input_focused( false )` come with it. It is handled just before the focus request, so an app that asks for both in one cycle ends up focused, which is what "open a new tab and type" needs; a pending `focus_retry` from an earlier cycle is cleared, or it would hand the focus straight back to the widget it was taken from. Positioning the cursor in a `select_on_focus` field. `handle_text_pointer_down` and `handle_text_pointer_drag` returned at once for a field built with `.select_on_focus( true )`, so that the press that focuses it keeps the whole-value selection `set_focus` has just installed. The guard did not distinguish that press from a later one: in a field that already had the focus, no press ever placed the cursor and no drag ever selected a range, with a finger or with a mouse. That suits a numeric picker poorly and an address box not at all, where correcting one character of a long URL meant retyping it. `AppData` gains `press_on_focused`, computed in both press paths, touch and pointer, right before their `set_focus`: whether the press landed on the widget that already held the focus. The two guards now apply only when it did not. The first press on such a field selects everything, as before; a press on it while focused collapses the selection to the byte offset under it, and a drag from there extends the selection. Not changed. `handle_text_select_word` keeps its `select_on_focus` guard, so a double press on such a field still leaves the whole value selected rather than a word. The right-button path, which opens the context menu without touching the selection, never reached these functions and is unaffected. `set_focus` still moves the cursor to the end and reselects when it is called for the widget that already has the focus; a programmatic focus request on a focused `select_on_focus` field therefore selects everything again, which is what an address-box shortcut wants.
This commit is contained in:
@@ -865,6 +865,9 @@ pub trait App: 'static
|
||||
/// view before it is focused. A widget that never appears is dropped.
|
||||
fn take_focus_request( &mut self ) -> Option<crate::types::WidgetId> { None }
|
||||
|
||||
/// Return `true` once to drop the keyboard focus, which folds the on-screen keyboard; a [`take_focus_request`](Self::take_focus_request) in the same cycle wins.
|
||||
fn take_blur_request( &mut self ) -> bool { false }
|
||||
|
||||
/// Called on every pointer motion (mouse move, button press, button
|
||||
/// release). `x`, `y` are in physical pixels of the main surface,
|
||||
/// the same coordinate space as widget rects. Default: no-op.
|
||||
|
||||
@@ -259,6 +259,8 @@ pub struct AppData<A: App>
|
||||
/// Surface currently holding keyboard focus (updated on keyboard
|
||||
/// enter/leave).
|
||||
pub keyboard_focus: SurfaceFocus,
|
||||
/// The current press landed on the widget that already held the focus, not the press that gives it.
|
||||
pub press_on_focused: bool,
|
||||
/// Per-touch-id tracking of which surface each active touch belongs to
|
||||
/// (populated on `down`, cleared on `up`/`cancel`).
|
||||
pub touch_focus: HashMap<i32, SurfaceFocus>,
|
||||
|
||||
@@ -362,6 +362,7 @@ pub( crate ) fn try_run<A: App>( mut app: A ) -> Result<(), RunError>
|
||||
canvas_pool: Vec::new(),
|
||||
pointer_focus: SurfaceFocus::Main,
|
||||
keyboard_focus: SurfaceFocus::Main,
|
||||
press_on_focused: false,
|
||||
touch_focus: std::collections::HashMap::new(),
|
||||
cached_view: None,
|
||||
cached_overlays: None,
|
||||
@@ -912,6 +913,14 @@ pub( crate ) fn try_run<A: App>( mut app: A ) -> Result<(), RunError>
|
||||
}
|
||||
}
|
||||
|
||||
if data.app.take_blur_request()
|
||||
{
|
||||
let qh = data.qh.clone();
|
||||
let focus = data.keyboard_focus;
|
||||
data.set_focus( focus, None, &qh );
|
||||
data.focus_retry = None;
|
||||
}
|
||||
|
||||
// Focus the widget with the requested WidgetId if the app requests it.
|
||||
// Walks main + every overlay because the targeted widget can live
|
||||
// on any of them (a search field on a launcher overlay, a text
|
||||
|
||||
@@ -127,7 +127,7 @@ impl<A: App> AppData<A>
|
||||
find_handlers( &self.surface( focus ).frame.widget_rects, idx ),
|
||||
Some( WidgetHandlers::TextEdit { select_on_focus: true, .. } ),
|
||||
);
|
||||
if select_on_focus { return; }
|
||||
if select_on_focus && !self.press_on_focused { return; }
|
||||
let ( rect, value, multiline, secure, align, font_size ) = match self.text_input_geometry( focus, idx )
|
||||
{
|
||||
Some( v ) => v,
|
||||
@@ -161,7 +161,7 @@ impl<A: App> AppData<A>
|
||||
find_handlers( &self.surface( focus ).frame.widget_rects, idx ),
|
||||
Some( WidgetHandlers::TextEdit { select_on_focus: true, .. } ),
|
||||
);
|
||||
if select_on_focus { return; }
|
||||
if select_on_focus && !self.press_on_focused { return; }
|
||||
let ( rect, value, multiline, secure, align, font_size ) = match self.text_input_geometry( focus, idx )
|
||||
{
|
||||
Some( v ) => v,
|
||||
|
||||
@@ -181,6 +181,7 @@ impl<A: App> AppData<A>
|
||||
ss.needs_redraw = true;
|
||||
result
|
||||
};
|
||||
self.press_on_focused = outcome.hit_idx.is_some() && self.surface( focus ).focused_idx == outcome.hit_idx;
|
||||
self.set_focus( focus, outcome.hit_idx, qh );
|
||||
if let Some( msg ) = outcome.initial_slider_msg
|
||||
{
|
||||
|
||||
@@ -159,6 +159,7 @@ impl<A: App> TouchHandler for AppData<A>
|
||||
ss.needs_redraw = true;
|
||||
result
|
||||
};
|
||||
self.press_on_focused = outcome.hit_idx.is_some() && self.surface( focus ).focused_idx == outcome.hit_idx;
|
||||
self.set_focus( focus, outcome.hit_idx, qh );
|
||||
if let Some( msg ) = outcome.initial_slider_msg
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user