fix: focus find on window focus if text box did not have focus

This commit is contained in:
Ashley Wulber 2025-10-24 13:58:54 -04:00 committed by Michael Murphy
parent d7454b8685
commit 73446cdafc
2 changed files with 54 additions and 4 deletions

View file

@ -351,6 +351,7 @@ pub enum Message {
DialogMessage(DialogMessage), DialogMessage(DialogMessage),
Find(Option<bool>), Find(Option<bool>),
FindCaseSensitive(bool), FindCaseSensitive(bool),
FindFocused(bool),
FindNext, FindNext,
FindPrevious, FindPrevious,
FindReplace, FindReplace,
@ -466,7 +467,7 @@ pub struct App {
auto_scroll: Option<f32>, auto_scroll: Option<f32>,
dialog_opt: Option<Dialog<Message>>, dialog_opt: Option<Dialog<Message>>,
dialog_page_opt: Option<DialogPage>, dialog_page_opt: Option<DialogPage>,
find_opt: Option<bool>, find_opt: Option<FindField>,
find_replace_id: widget::Id, find_replace_id: widget::Id,
find_replace_value: String, find_replace_value: String,
find_search_id: widget::Id, find_search_id: widget::Id,
@ -480,6 +481,12 @@ pub struct App {
modifiers: Modifiers, modifiers: Modifiers,
} }
#[derive(Debug, Clone, Copy)]
struct FindField {
replace: bool,
has_focus: bool,
}
impl App { impl App {
pub fn active_tab(&self) -> Option<&Tab> { pub fn active_tab(&self) -> Option<&Tab> {
self.tab_model.active_data() self.tab_model.active_data()
@ -780,7 +787,12 @@ impl App {
} }
_ => Task::none(), _ => Task::none(),
} }
} else if self.find_opt.is_some() { } else if self.find_opt.is_some_and(
|FindField {
replace: _,
has_focus,
}| has_focus,
) {
widget::text_input::focus(self.find_search_id.clone()) widget::text_input::focus(self.find_search_id.clone())
} else { } else {
widget::text_input::focus(self.text_box_id.clone()) widget::text_input::focus(self.text_box_id.clone())
@ -1811,7 +1823,10 @@ impl Application for App {
} }
} }
Message::Find(find_opt) => { Message::Find(find_opt) => {
self.find_opt = find_opt; self.find_opt = find_opt.map(|f| FindField {
replace: f,
has_focus: true,
});
// Focus correct input // Focus correct input
return self.update_focus(); return self.update_focus();
@ -1938,6 +1953,14 @@ impl Application for App {
config_set!(find_wrap_around, find_wrap_around); config_set!(find_wrap_around, find_wrap_around);
return self.update_config(); return self.update_config();
} }
Message::FindFocused(has_focus) => {
if let Some(f) = self.find_opt.as_mut() {
*f = FindField {
replace: f.replace,
has_focus,
};
}
}
Message::GitProjectStatus(project_status) => { Message::GitProjectStatus(project_status) => {
self.git_project_status = Some(project_status); self.git_project_status = Some(project_status);
} }
@ -2809,6 +2832,7 @@ impl Application for App {
Some(Tab::Editor(tab)) => { Some(Tab::Editor(tab)) => {
let mut text_box = text_box(&tab.editor, self.config.metrics(tab.zoom_adj())) let mut text_box = text_box(&tab.editor, self.config.metrics(tab.zoom_adj()))
.id(self.text_box_id.clone()) .id(self.text_box_id.clone())
.on_focus(Message::FindFocused(false))
.on_auto_scroll(Message::AutoScroll) .on_auto_scroll(Message::AutoScroll)
.on_changed(Message::TabChanged(tab_id)) .on_changed(Message::TabChanged(tab_id))
.has_context_menu(tab.context_menu.is_some()) .has_context_menu(tab.context_menu.is_some())
@ -2918,7 +2942,11 @@ impl Application for App {
None => {} None => {}
} }
if let Some(replace) = &self.find_opt { if let Some(FindField {
replace,
has_focus: _,
}) = &self.find_opt
{
let find_input = let find_input =
widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value) widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value)
.id(self.find_search_id.clone()) .id(self.find_search_id.clone())
@ -2930,6 +2958,7 @@ impl Application for App {
Message::FindNext Message::FindNext
} }
}) })
.on_focus(Message::FindFocused(true))
.width(Length::Fixed(320.0)) .width(Length::Fixed(320.0))
.trailing_icon( .trailing_icon(
button::custom(icon_cache_get("edit-clear-symbolic", 16)) button::custom(icon_cache_get("edit-clear-symbolic", 16))

View file

@ -44,6 +44,7 @@ pub struct TextBox<'a, Message> {
padding: Padding, padding: Padding,
on_auto_scroll: Option<Box<dyn Fn(Option<f32>) -> Message + 'a>>, on_auto_scroll: Option<Box<dyn Fn(Option<f32>) -> Message + 'a>>,
on_changed: Option<Message>, on_changed: Option<Message>,
on_focus: Option<Message>,
click_timing: Duration, click_timing: Duration,
has_context_menu: bool, has_context_menu: bool,
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
@ -63,6 +64,7 @@ where
padding: Padding::new(0.0), padding: Padding::new(0.0),
on_auto_scroll: None, on_auto_scroll: None,
on_changed: None, on_changed: None,
on_focus: None,
click_timing: Duration::from_millis(500), click_timing: Duration::from_millis(500),
has_context_menu: false, has_context_menu: false,
on_context_menu: None, on_context_menu: None,
@ -118,6 +120,11 @@ where
self.line_numbers = true; self.line_numbers = true;
self self
} }
pub fn on_focus(mut self, on_focus: Message) -> Self {
self.on_focus = Some(on_focus);
self
}
} }
pub fn text_box<'a, Message>( pub fn text_box<'a, Message>(
@ -948,6 +955,13 @@ where
} }
} }
if let Some(on_focus) = self.on_focus.as_ref()
&& state.emit_focus
{
state.emit_focus = false;
shell.publish(on_focus.clone());
}
let mut status = Status::Ignored; let mut status = Status::Ignored;
match event { match event {
Event::Keyboard(KeyEvent::KeyPressed { Event::Keyboard(KeyEvent::KeyPressed {
@ -1034,6 +1048,10 @@ where
if let Some(p) = cursor_position.position_in(layout.bounds()) { if let Some(p) = cursor_position.position_in(layout.bounds()) {
state.is_focused = true; state.is_focused = true;
if let Some(on_focus) = self.on_focus.as_ref() {
shell.publish(on_focus.clone());
}
// Handle left click drag // Handle left click drag
if let Button::Left = button { if let Button::Left = button {
let x_logical = p.x - self.padding.left; let x_logical = p.x - self.padding.left;
@ -1263,6 +1281,7 @@ pub struct State {
dragging: Option<Dragging>, dragging: Option<Dragging>,
editor_offset_x: Cell<i32>, editor_offset_x: Cell<i32>,
is_focused: bool, is_focused: bool,
emit_focus: bool,
scale_factor: Cell<f32>, scale_factor: Cell<f32>,
scrollbar_v_rect: Cell<Rectangle<f32>>, scrollbar_v_rect: Cell<Rectangle<f32>>,
scrollbar_h_rect: Cell<Option<Rectangle<f32>>>, scrollbar_h_rect: Cell<Option<Rectangle<f32>>>,
@ -1278,6 +1297,7 @@ impl State {
dragging: None, dragging: None,
editor_offset_x: Cell::new(0), editor_offset_x: Cell::new(0),
is_focused: false, is_focused: false,
emit_focus: false,
scale_factor: Cell::new(1.0), scale_factor: Cell::new(1.0),
scrollbar_v_rect: Cell::new(Rectangle::default()), scrollbar_v_rect: Cell::new(Rectangle::default()),
scrollbar_h_rect: Cell::new(None), scrollbar_h_rect: Cell::new(None),
@ -1293,6 +1313,7 @@ impl operation::Focusable for State {
fn focus(&mut self) { fn focus(&mut self) {
self.is_focused = true; self.is_focused = true;
self.emit_focus = true;
} }
fn unfocus(&mut self) { fn unfocus(&mut self) {