Add Focus Follows Mouse

This commit is contained in:
Mattias Eriksson 2024-01-22 16:35:23 +01:00 • committed by Jeremy Soller
parent d6946d40bd
commit c749a02df8
5 changed files with 49 additions and 3 deletions

View file

@ -24,10 +24,10 @@ palette = "0.7"
git = "https://github.com/pop-os/cosmic-text.git" git = "https://github.com/pop-os/cosmic-text.git"
[dependencies.libcosmic] [dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic.git" #git = "https://github.com/pop-os/libcosmic.git"
default-features = false default-features = false
features = ["tokio", "winit"] features = ["tokio", "winit"]
#path = "../libcosmic" path = "../libcosmic"
[features] [features]
default = ["wgpu"] default = ["wgpu"]

View file

@ -2,6 +2,7 @@
## Settings ## Settings
settings = Settings settings = Settings
focus-follow-mouse = Focus Follow Mouse
### Appearance ### Appearance
appearance = Appearance appearance = Appearance

View file

@ -43,6 +43,7 @@ pub struct Config {
pub use_bright_bold: bool, pub use_bright_bold: bool,
pub syntax_theme_dark: String, pub syntax_theme_dark: String,
pub syntax_theme_light: String, pub syntax_theme_light: String,
pub focus_follow_mouse: bool,
} }
impl Default for Config { impl Default for Config {
@ -60,6 +61,7 @@ impl Default for Config {
use_bright_bold: false, use_bright_bold: false,
syntax_theme_dark: "COSMIC Dark".to_string(), syntax_theme_dark: "COSMIC Dark".to_string(),
syntax_theme_light: "COSMIC Light".to_string(), syntax_theme_light: "COSMIC Light".to_string(),
focus_follow_mouse: false,
} }
} }
} }

View file

@ -260,6 +260,7 @@ pub enum Message {
PaneDragged(pane_grid::DragEvent), PaneDragged(pane_grid::DragEvent),
PaneResized(pane_grid::ResizeEvent), PaneResized(pane_grid::ResizeEvent),
Modifiers(Modifiers), Modifiers(Modifiers),
MouseEnter(pane_grid::Pane),
Paste(Option<segmented_button::Entity>), Paste(Option<segmented_button::Entity>),
PasteValue(Option<segmented_button::Entity>, String), PasteValue(Option<segmented_button::Entity>, String),
SelectAll(Option<segmented_button::Entity>), SelectAll(Option<segmented_button::Entity>),
@ -284,6 +285,7 @@ pub enum Message {
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
ZoomReset, ZoomReset,
FocusFollowMouse(bool),
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@ -637,6 +639,10 @@ impl App {
.add( .add(
widget::settings::item::builder(fl!("show-headerbar")) widget::settings::item::builder(fl!("show-headerbar"))
.toggler(self.config.show_headerbar, Message::ShowHeaderBar), .toggler(self.config.show_headerbar, Message::ShowHeaderBar),
)
.add(
widget::settings::item::builder(fl!("focus-follow-mouse"))
.toggler(self.config.focus_follow_mouse, Message::FocusFollowMouse),
); );
widget::settings::view_column(vec![settings_view.into()]).into() widget::settings::view_column(vec![settings_view.into()]).into()
@ -1047,6 +1053,10 @@ impl Application for App {
Message::Modifiers(modifiers) => { Message::Modifiers(modifiers) => {
self.modifiers = modifiers; self.modifiers = modifiers;
} }
Message::MouseEnter(pane) => {
self.pane_model.focus = pane;
return self.update_focus();
}
Message::PaneClicked(pane) => { Message::PaneClicked(pane) => {
self.pane_model.focus = pane; self.pane_model.focus = pane;
return self.update_title(Some(pane)); return self.update_title(Some(pane));
@ -1125,6 +1135,12 @@ impl Application for App {
return self.save_config(); return self.save_config();
} }
} }
Message::FocusFollowMouse(focus_follow_mouse) => {
if focus_follow_mouse != self.config.focus_follow_mouse {
self.config.focus_follow_mouse = focus_follow_mouse;
return self.save_config();
}
}
Message::SystemThemeModeChange(_theme_mode) => { Message::SystemThemeModeChange(_theme_mode) => {
return self.update_config(); return self.update_config();
} }
@ -1403,10 +1419,15 @@ impl Application for App {
.unwrap_or_else(widget::Id::unique); .unwrap_or_else(widget::Id::unique);
match tab_model.data::<Mutex<Terminal>>(entity) { match tab_model.data::<Mutex<Terminal>>(entity) {
Some(terminal) => { Some(terminal) => {
let terminal_box = terminal_box(terminal).id(terminal_id).on_context_menu( let mut terminal_box = terminal_box(terminal).id(terminal_id).on_context_menu(
move |position_opt| Message::TabContextMenu(entity, position_opt), move |position_opt| Message::TabContextMenu(entity, position_opt),
); );
if self.config.focus_follow_mouse {
terminal_box =
terminal_box.on_mouse_enter(move || Message::MouseEnter(pane));
}
let context_menu = { let context_menu = {
let terminal = terminal.lock().unwrap(); let terminal = terminal.lock().unwrap();
terminal.context_menu terminal.context_menu

View file

@ -47,6 +47,8 @@ pub struct TerminalBox<'a, Message> {
click_timing: Duration, click_timing: Duration,
context_menu: Option<Point>, context_menu: Option<Point>,
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
on_mouse_enter: Option<Box<dyn Fn() -> Message + 'a>>,
mouse_inside_boundary: Option<bool>,
} }
impl<'a, Message> TerminalBox<'a, Message> impl<'a, Message> TerminalBox<'a, Message>
@ -61,6 +63,8 @@ where
click_timing: Duration::from_millis(500), click_timing: Duration::from_millis(500),
context_menu: None, context_menu: None,
on_context_menu: None, on_context_menu: None,
on_mouse_enter: None,
mouse_inside_boundary: None,
} }
} }
@ -91,6 +95,11 @@ where
self.on_context_menu = Some(Box::new(on_context_menu)); self.on_context_menu = Some(Box::new(on_context_menu));
self self
} }
pub fn on_mouse_enter(mut self, on_mouse_enter: impl Fn() -> Message + 'a) -> Self {
self.on_mouse_enter = Some(Box::new(on_mouse_enter));
self
}
} }
pub fn terminal_box<'a, Message>(terminal: &'a Mutex<Terminal>) -> TerminalBox<'a, Message> pub fn terminal_box<'a, Message>(terminal: &'a Mutex<Terminal>) -> TerminalBox<'a, Message>
@ -998,6 +1007,19 @@ where
status = Status::Captured; status = Status::Captured;
} }
Event::Mouse(MouseEvent::CursorMoved { .. }) => { Event::Mouse(MouseEvent::CursorMoved { .. }) => {
if let Some(on_mouse_enter) = &self.on_mouse_enter {
let mouse_is_inside = cursor_position.position_in(layout.bounds()).is_some();
if let Some(known_is_inside) = self.mouse_inside_boundary {
if mouse_is_inside != known_is_inside {
if mouse_is_inside {
shell.publish(on_mouse_enter());
}
self.mouse_inside_boundary = Some(mouse_is_inside);
}
} else {
self.mouse_inside_boundary = Some(mouse_is_inside);
}
}
if let Some(dragging) = &state.dragging { if let Some(dragging) = &state.dragging {
if let Some(p) = cursor_position.position() { if let Some(p) = cursor_position.position() {
let x = (p.x - layout.bounds().x) - self.padding.left; let x = (p.x - layout.bounds().x) - self.padding.left;