From 9b10e98ab181746b9764c3f60c2f0eeadb80630c Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Thu, 1 Oct 2026 14:38:17 +0200 Subject: [PATCH] yoda: copie a la selection vers le presse-papiers, a la Ghostty La selection n'allait que dans la selection primaire (clic du milieu) ; pour la coller avec Ctrl+V ailleurs, il fallait Ctrl+Shift+C. Reprise de copy-on-select = clipboard de Ghostty, le reglage de l'ancienne config Ghostty de Lionel. - Config copy_on_select_clipboard (desactive par defaut, comme l'amont) ; case dans Reglages > Avance. - terminal_box::on_selection_done : publie a la fin d'un geste de selection fait dans le panneau (glisser, double/triple clic, Shift+ clic), pas a chaque relachement du bouton gauche. La copie primaire existante part elle a tout relachement dans la fenetre : la recopier dans le presse-papiers ecraserait, au simple clic sur un onglet, ce qu'on vient de copier ailleurs avec une vieille selection. - Message::SelectionDone : copie la selection de cet onglet ; une selection vide (simple clic) ne donne pas de texte (range_simple). Co-Authored-By: Claude Opus 5.5 --- i18n/en/cosmic_term.ftl | 2 ++ i18n/fr/cosmic_term.ftl | 2 ++ src/config.rs | 6 ++++++ src/main.rs | 33 +++++++++++++++++++++++++++++++++ src/terminal_box.rs | 13 +++++++++++++ 5 files changed, 56 insertions(+) diff --git a/i18n/en/cosmic_term.ftl b/i18n/en/cosmic_term.ftl index b32e7ad..aab041c 100644 --- a/i18n/en/cosmic_term.ftl +++ b/i18n/en/cosmic_term.ftl @@ -68,6 +68,8 @@ window-header = Window header show-header-description = Reveal the header from the right-click menu tab-new-inherit-working-directory = New tabs and windows use current directory tab-new-inherit-working-directory-description = Open new tabs and windows in the active tab's working directory +copy-on-select-clipboard = Copy selection to clipboard +copy-on-select-clipboard-description = In addition to the primary selection (middle click), like copy-on-select = clipboard in Ghostty ### Keyboard shortcuts add-another-keybinding = Add another keybinding diff --git a/i18n/fr/cosmic_term.ftl b/i18n/fr/cosmic_term.ftl index bb3a00a..5d2d82e 100644 --- a/i18n/fr/cosmic_term.ftl +++ b/i18n/fr/cosmic_term.ftl @@ -172,5 +172,7 @@ comment = Émulateur de terminal pour le bureau COSMIC keywords = Commande;Shell;Terminal;CLI; tab-new-inherit-working-directory = Les nouveaux onglets et fenêtres utilisent le répertoire actuel tab-new-inherit-working-directory-description = Ouvrir les nouveaux onglets et fenêtres dans le répertoire de travail de l'onglet actif +copy-on-select-clipboard = Copier la sélection dans le presse-papiers +copy-on-select-clipboard-description = En plus de la sélection primaire (clic du milieu), comme copy-on-select = clipboard dans Ghostty show-pane-borders = Afficher les bordures des panneaux unfocused-pane-dim = Atténuation des panneaux inactifs diff --git a/src/config.rs b/src/config.rs index 5b434bc..2137165 100644 --- a/src/config.rs +++ b/src/config.rs @@ -242,6 +242,11 @@ pub struct Config { pub focus_follow_mouse: bool, #[serde(default)] pub tab_new_inherit_working_directory: bool, + /// [yoda] Copier aussi la sélection dans le presse-papiers, et pas + /// seulement dans la sélection primaire (`copy-on-select = clipboard` de + /// Ghostty). + #[serde(default)] + pub copy_on_select_clipboard: bool, pub default_profile: Option, #[serde(default)] pub shortcuts_custom: Shortcuts, @@ -264,6 +269,7 @@ impl Default for Config { dim_font_weight: Weight::NORMAL.0, focus_follow_mouse: false, tab_new_inherit_working_directory: false, + copy_on_select_clipboard: false, font_name: "Noto Sans Mono".to_string(), font_size: 14, font_size_zoom_step_mul_100: 100, diff --git a/src/main.rs b/src/main.rs index f5c6eb2..d69e5f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -600,6 +600,8 @@ pub enum Message { SyntaxTheme(ColorSchemeKind, usize), SystemThemeChange, TabNewInheritWorkingDirectory(bool), + CopyOnSelectClipboard(bool), + SelectionDone(pane_grid::Pane, segmented_button::Entity), TabActivate(segmented_button::Entity), TabActivateJump(usize), TabClose(Option), @@ -1759,6 +1761,14 @@ impl App { self.config.tab_new_inherit_working_directory, Message::TabNewInheritWorkingDirectory, ), + ) + .add( + widget::settings::item::builder(fl!("copy-on-select-clipboard")) + .description(fl!("copy-on-select-clipboard-description")) + .toggler( + self.config.copy_on_select_clipboard, + Message::CopyOnSelectClipboard, + ), ); widget::settings::view_column(vec![ @@ -3404,6 +3414,24 @@ impl Application for App { return self.update_config(); } } + Message::CopyOnSelectClipboard(copy_on_select_clipboard) => { + if copy_on_select_clipboard != self.config.copy_on_select_clipboard { + config_set!(copy_on_select_clipboard, copy_on_select_clipboard); + } + } + Message::SelectionDone(pane, entity) => { + // [yoda] Copie à la sélection vers le presse-papiers ; la + // sélection primaire, elle, suit le relâchement du bouton. + if self.config.copy_on_select_clipboard + && let Some(tab_model) = self.pane_model.panes.get(pane) + && let Some(terminal) = tab_model.data::>(entity) + { + let text = terminal.lock().unwrap().term.lock().selection_to_string(); + if let Some(text) = text.filter(|text| !text.is_empty()) { + return clipboard::write(text); + } + } + } Message::TabNewInheritWorkingDirectory(tab_new_inherit_working_directory) => { config_set!( tab_new_inherit_working_directory, @@ -4298,6 +4326,11 @@ impl Application for App { terminal_box = terminal_box.on_mouse_enter(move || Message::MouseEnter(pane)); } + if self.config.copy_on_select_clipboard { + terminal_box = + terminal_box.on_selection_done(move || Message::SelectionDone(pane, entity)); + } + // [yoda] Voile sur les panneaux non focalisés (à la Ghostty), seulement // quand il y en a plusieurs : on voit d'un coup d'œil où l'on tape. if let Some(dim) = self.config.unfocused_pane_dim_ratio() diff --git a/src/terminal_box.rs b/src/terminal_box.rs index 6efd65b..c43d440 100644 --- a/src/terminal_box.rs +++ b/src/terminal_box.rs @@ -123,6 +123,7 @@ pub struct TerminalBox<'a, Message> { dim: Option, mouse_inside_boundary: Option, on_middle_click: Option Message + 'a>>, + on_selection_done: Option Message + 'a>>, on_open_hyperlink: Option Message + 'a>>, on_window_focused: Option Message + 'a>>, on_window_unfocused: Option Message + 'a>>, @@ -151,6 +152,7 @@ where dim: None, mouse_inside_boundary: None, on_middle_click: None, + on_selection_done: None, key_binds, on_open_hyperlink: None, on_window_focused: None, @@ -208,6 +210,14 @@ where self } + /// [yoda] Fin d'un geste de sélection fait dans ce panneau (glisser, double + /// ou triple clic, Shift+clic) : le moment de copier la sélection. Un + /// simple clic ailleurs dans la fenêtre ne le déclenche pas. + pub fn on_selection_done(mut self, on_selection_done: impl Fn() -> Message + 'a) -> Self { + self.on_selection_done = Some(Box::new(on_selection_done)); + self + } + pub fn on_middle_click(mut self, on_middle_click: impl Fn() -> Message + 'a) -> Self { self.on_middle_click = Some(Box::new(on_middle_click)); self @@ -1581,6 +1591,9 @@ where } } terminal.needs_update = true; + if let Some(on_selection_done) = &self.on_selection_done { + shell.publish(on_selection_done()); + } } if let Some(p) = cursor_position.position_in(layout.bounds()) { let x = p.x - self.padding.left;