diff --git a/src/Surface.zig b/src/Surface.zig index 98c344927e..cee125a33e 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -235,7 +235,8 @@ const DerivedConfig = struct { clipboard_trim_trailing_spaces: bool, clipboard_paste_protection: bool, clipboard_paste_bracketed_safe: bool, - copy_on_select: configpkg.CopyOnSelect, + copy_on_select: configpkg.CopyOnMouseAction, + copy_on_right_click: configpkg.CopyOnMouseAction, confirm_close_surface: configpkg.ConfirmCloseSurface, cursor_click_to_move: bool, desktop_notifications: bool, @@ -297,6 +298,7 @@ const DerivedConfig = struct { .clipboard_paste_protection = config.@"clipboard-paste-protection", .clipboard_paste_bracketed_safe = config.@"clipboard-paste-bracketed-safe", .copy_on_select = config.@"copy-on-select", + .copy_on_right_click = config.@"copy-on-right-click", .confirm_close_surface = config.@"confirm-close-surface", .cursor_click_to_move = config.@"cursor-click-to-move", .desktop_notifications = config.@"desktop-notifications", @@ -1405,20 +1407,7 @@ fn clipboardWrite(self: *const Surface, data: []const u8, loc: apprt.Clipboard) /// Set the selection contents. /// /// This must be called with the renderer mutex held. -fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { - const prev_ = self.io.terminal.screen.selection; - try self.io.terminal.screen.select(sel_); - - // If copy on select is false then exit early. - if (self.config.copy_on_select == .false) return; - - // Set our selection clipboard. If the selection is cleared we do not - // clear the clipboard. If the selection is set, we only set the clipboard - // again if it changed, since setting the clipboard can be an expensive - // operation. - const sel = sel_ orelse return; - if (prev_) |prev| if (sel.eql(prev)) return; - +fn copyOnMouseAction(self: *Surface, sel: terminal.Selection, action: configpkg.CopyOnMouseAction) !void { const buf = self.io.terminal.screen.selectionString(self.alloc, .{ .sel = sel, .trim = self.config.clipboard_trim_trailing_spaces, @@ -1430,7 +1419,7 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { // Set the clipboard. This is not super DRY but it is clear what // we're doing for each setting without being clever. - switch (self.config.copy_on_select) { + switch (action) { .false => unreachable, // handled above with an early exit // Both standard and selection clipboards are set. @@ -1469,6 +1458,22 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { } } +fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { + const prev_ = self.io.terminal.screen.selection; + try self.io.terminal.screen.select(sel_); + + // If copy on select is false then exit early. + if (self.config.copy_on_select == .false) return; + + // Clear the clipboard. If the selection is set, we only set the clipboard + // again if it changed, since setting the clipboard can be an expensive + // operation. + const sel = sel_ orelse return; + if (prev_) |prev| if (sel.eql(prev)) return; + + try self.copyOnMouseAction(sel, self.config.copy_on_select); +} + /// Change the cell size for the terminal grid. This can happen as /// a result of changing the font size at runtime. fn setCellSize(self: *Surface, size: renderer.CellSize) !void { @@ -3074,6 +3079,7 @@ pub fn mouseButtonCallback( // are supported by our two main apprts so we always do this. If we // want to be careful in the future we can add a function to apprts // that let's us know. + if (button == .right and action == .press) sel: { self.renderer_state.mutex.lock(); defer self.renderer_state.mutex.unlock(); @@ -3103,14 +3109,17 @@ pub fn mouseButtonCallback( // If we already have a selection and the selection contains // where we clicked then we don't want to modify the selection. if (self.io.terminal.screen.selection) |prev_sel| { - if (prev_sel.contains(screen, pin)) break :sel; - + if (prev_sel.contains(screen, pin)) { + try self.copyOnMouseAction(prev_sel, self.config.copy_on_right_click); + break :sel; + } // The selection doesn't contain our pin, so we create a new // word selection where we clicked. } const sel = screen.selectWord(pin) orelse break :sel; try self.setSelection(sel); + try self.copyOnMouseAction(sel, self.config.copy_on_right_click); try self.queueRender(); } diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 9a8c4513db..c1fa1ed759 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1464,7 +1464,7 @@ fn gtkMouseDown( // If a right click isn't consumed, mouseButtonCallback selects the hovered // word and returns false. We can use this to handle the context menu // opening under normal scenarios. - if (!consumed and button == .right) { + if (!consumed and button == .right and self.app.config.@"copy-on-right-click" == .false) { self.showContextMenu(@floatCast(x), @floatCast(y)); } } diff --git a/src/config.zig b/src/config.zig index a8ffe2ec7b..aa88c616dc 100644 --- a/src/config.zig +++ b/src/config.zig @@ -15,7 +15,7 @@ pub const formatEntry = formatter.formatEntry; // Field types pub const ClipboardAccess = Config.ClipboardAccess; pub const ConfirmCloseSurface = Config.ConfirmCloseSurface; -pub const CopyOnSelect = Config.CopyOnSelect; +pub const CopyOnMouseAction = Config.CopyOnMouseAction; pub const CustomShaderAnimation = Config.CustomShaderAnimation; pub const FontSyntheticStyle = Config.FontSyntheticStyle; pub const FontStyle = Config.FontStyle; diff --git a/src/config/Config.zig b/src/config/Config.zig index 5a47cb1b3b..d5e5204833 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1506,12 +1506,28 @@ keybind: Keybinds = .{}, /// paste is always enabled even if this is `false`. /// /// The default value is true on Linux and macOS. -@"copy-on-select": CopyOnSelect = switch (builtin.os.tag) { +@"copy-on-select": CopyOnMouseAction = switch (builtin.os.tag) { .linux => .true, .macos => .true, else => .false, }, +/// Whether to copy selected text to the clipboard on right mouse click. `true` +/// will prefer to copy to the selection clipboard, otherwise it will copy to +/// the system clipboard. +/// +/// The value `clipboard` will always copy text to the selection clipboard +/// as well as the system clipboard. +/// +/// Middle-click paste will always use the selection clipboard. Middle-click +/// paste is always enabled even if this is `false`. +/// +/// The default value is false for all systems. +/// +/// At present this will only disable the context menu on Linux in the future +/// this should also disable the context menu on macOS +@"copy-on-right-click": CopyOnMouseAction = .false, + /// The time in milliseconds between clicks to consider a click a repeat /// (double, triple, etc.) or an entirely new single click. A value of zero will /// use a platform-specific default. The default on macOS is determined by the @@ -5764,7 +5780,7 @@ pub const RepeatableLink = struct { }; /// Options for copy on select behavior. -pub const CopyOnSelect = enum { +pub const CopyOnMouseAction = enum { /// Disables copy on select entirely. false,