Merge pull request #1431 from mitchellh/mouse-mods-alt

core: handle mouse capture events with link highlighting
This commit is contained in:
Mitchell Hashimoto
2024-02-01 09:20:04 -08:00
committed by GitHub
5 changed files with 36 additions and 9 deletions

View File

@ -854,7 +854,7 @@ fn modsChanged(self: *Surface, mods: input.Mods) void {
{ {
self.renderer_state.mutex.lock(); self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock(); defer self.renderer_state.mutex.unlock();
self.renderer_state.mouse.mods = mods; self.renderer_state.mouse.mods = self.mouseModsWithCapture(self.mouse.mods);
} }
self.queueRender() catch |err| { self.queueRender() catch |err| {
@ -2372,6 +2372,9 @@ fn linkAtPos(
break :mouse_pt viewport_point.toScreen(&self.io.terminal.screen); break :mouse_pt viewport_point.toScreen(&self.io.terminal.screen);
}; };
// Get our comparison mods
const mouse_mods = self.mouseModsWithCapture(self.mouse.mods);
// Get the line we're hovering over. // Get the line we're hovering over.
const line = self.io.terminal.screen.getLine(mouse_pt) orelse const line = self.io.terminal.screen.getLine(mouse_pt) orelse
return null; return null;
@ -2382,7 +2385,7 @@ fn linkAtPos(
for (self.config.links) |link| { for (self.config.links) |link| {
switch (link.highlight) { switch (link.highlight) {
.always, .hover => {}, .always, .hover => {},
.always_mods, .hover_mods => |v| if (!v.equal(self.mouse.mods)) continue, .always_mods, .hover_mods => |v| if (!v.equal(mouse_mods)) continue,
} }
var it = strmap.searchIterator(link.regex); var it = strmap.searchIterator(link.regex);
@ -2398,6 +2401,25 @@ fn linkAtPos(
return null; return null;
} }
/// This returns the mouse mods to consider for link highlighting or
/// other purposes taking into account when shift is pressed for releasing
/// the mouse from capture.
///
/// The renderer state mutex must be held.
fn mouseModsWithCapture(self: *Surface, mods: input.Mods) input.Mods {
// In any of these scenarios, whatever mods are set (even shift)
// are preserved.
if (self.io.terminal.flags.mouse_event == .none) return mods;
if (!mods.shift) return mods;
if (self.mouseShiftCapture(false)) return mods;
// We have mouse capture, shift set, and we're not allowed to capture
// shift, so we can clear shift.
var final = mods;
final.shift = false;
return final;
}
/// Attempt to invoke the action of any link that is under the /// Attempt to invoke the action of any link that is under the
/// given position. /// given position.
/// ///

View File

@ -36,6 +36,11 @@ pub const Highlight = union(enum) {
/// hovering or always. For always, all links will be highlighted /// hovering or always. For always, all links will be highlighted
/// when the mods are pressed regardless of if the mouse is hovering /// when the mods are pressed regardless of if the mouse is hovering
/// over them. /// over them.
///
/// Note that if "shift" is specified here, this will NEVER match in
/// TUI programs that capture mouse events. "Shift" with mouse capture
/// escapes the mouse capture but strips the "shift" so it can't be
/// detected.
always_mods: Mods, always_mods: Mods,
hover_mods: Mods, hover_mods: Mods,
}; };

View File

@ -1551,12 +1551,12 @@ fn rebuildCells(
const arena_alloc = arena.allocator(); const arena_alloc = arena.allocator();
// Create our match set for the links. // Create our match set for the links.
var link_match_set = try self.config.links.matchSet( var link_match_set: link.MatchSet = if (mouse.point) |mouse_pt| try self.config.links.matchSet(
arena_alloc, arena_alloc,
screen, screen,
mouse.point orelse .{}, mouse_pt,
mouse.mods, mouse.mods,
); ) else .{};
// Determine our x/y range for preedit. We don't want to render anything // Determine our x/y range for preedit. We don't want to render anything
// here because we will render the preedit separately. // here because we will render the preedit separately.

View File

@ -987,12 +987,12 @@ pub fn rebuildCells(
self.gl_cells_written = 0; self.gl_cells_written = 0;
// Create our match set for the links. // Create our match set for the links.
var link_match_set = try self.config.links.matchSet( var link_match_set: link.MatchSet = if (mouse.point) |mouse_pt| try self.config.links.matchSet(
arena_alloc, arena_alloc,
screen, screen,
mouse.point orelse .{}, mouse_pt,
mouse.mods, mouse.mods,
); ) else .{};
// Determine our x/y range for preedit. We don't want to render anything // Determine our x/y range for preedit. We don't want to render anything
// here because we will render the preedit separately. // here because we will render the preedit separately.

View File

@ -139,7 +139,7 @@ pub const MatchSet = struct {
/// The matches. /// The matches.
/// ///
/// Important: this must be in left-to-right top-to-bottom order. /// Important: this must be in left-to-right top-to-bottom order.
matches: []const terminal.Selection, matches: []const terminal.Selection = &.{},
i: usize = 0, i: usize = 0,
pub fn deinit(self: *MatchSet, alloc: Allocator) void { pub fn deinit(self: *MatchSet, alloc: Allocator) void {