Mouse drag while clicked should cancel any mouse link actions
Fixes #7077 This follows pretty standard behavior across native or popular applications on both platforms macOS and Linux. The basic behavior is that if you do a mouse down event and then drag the mouse beyond the current character, then any mouse up actions are canceled (beyond emiting the event itself). This fixes a specific scenario where you could do the following: 1. Click anywhere (mouse down) 2. Drag over a valid link 3. Press command/control (to activate the link) 4. Release the mouse button (mouse up) 5. The link is triggered Now, step 3 and step 5 do not happen. Links are not even highlighted in this scenario. This matches iTerm2 on macOS which has a similar command-to-activate-links behavior.pull/7080/head
parent
66636195f1
commit
6d3f97ec1e
100
src/Surface.zig
100
src/Surface.zig
|
|
@ -1031,9 +1031,64 @@ fn mouseRefreshLinks(
|
||||||
// If the position is outside our viewport, do nothing
|
// If the position is outside our viewport, do nothing
|
||||||
if (pos.x < 0 or pos.y < 0) return;
|
if (pos.x < 0 or pos.y < 0) return;
|
||||||
|
|
||||||
|
// Update the last point that we checked for links so we don't
|
||||||
|
// recheck if the mouse moves some pixels to the same point.
|
||||||
self.mouse.link_point = pos_vp;
|
self.mouse.link_point = pos_vp;
|
||||||
|
|
||||||
if (try self.linkAtPos(pos)) |link| {
|
// We use an arena for everything below to make things easy to clean up.
|
||||||
|
// In the case we don't do any allocs this is very cheap to setup
|
||||||
|
// (effectively just struct init).
|
||||||
|
var arena = ArenaAllocator.init(self.alloc);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
// Get our link at the current position. This returns null if there
|
||||||
|
// isn't a link OR if we shouldn't be showing links for some reason
|
||||||
|
// (see further comments for cases).
|
||||||
|
const link_: ?apprt.action.MouseOverLink = link: {
|
||||||
|
// If we clicked and our mouse moved cells then we never
|
||||||
|
// highlight links until the mouse is unclicked. This follows
|
||||||
|
// standard macOS and Linux behavior where a click and drag cancels
|
||||||
|
// mouse actions.
|
||||||
|
const left_idx = @intFromEnum(input.MouseButton.left);
|
||||||
|
if (self.mouse.click_state[left_idx] == .press) click: {
|
||||||
|
const pin = self.mouse.left_click_pin orelse break :click;
|
||||||
|
const click_pt = self.io.terminal.screen.pages.pointFromPin(
|
||||||
|
.viewport,
|
||||||
|
pin.*,
|
||||||
|
) orelse break :click;
|
||||||
|
|
||||||
|
if (!click_pt.coord().eql(pos_vp)) {
|
||||||
|
log.debug("mouse moved while left click held, ignoring link hover", .{});
|
||||||
|
break :link null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const link = (try self.linkAtPos(pos)) orelse break :link null;
|
||||||
|
switch (link[0]) {
|
||||||
|
.open => {
|
||||||
|
const str = try self.io.terminal.screen.selectionString(alloc, .{
|
||||||
|
.sel = link[1],
|
||||||
|
.trim = false,
|
||||||
|
});
|
||||||
|
break :link .{ .url = str };
|
||||||
|
},
|
||||||
|
|
||||||
|
._open_osc8 => {
|
||||||
|
// Show the URL in the status bar
|
||||||
|
const pin = link[1].start();
|
||||||
|
const uri = self.osc8URI(pin) orelse {
|
||||||
|
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
||||||
|
break :link null;
|
||||||
|
};
|
||||||
|
break :link .{ .url = uri };
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we found a link, setup our internal state and notify the
|
||||||
|
// apprt so it can highlight it.
|
||||||
|
if (link_) |link| {
|
||||||
self.renderer_state.mouse.point = pos_vp;
|
self.renderer_state.mouse.point = pos_vp;
|
||||||
self.mouse.over_link = true;
|
self.mouse.over_link = true;
|
||||||
self.renderer_state.terminal.screen.dirty.hyperlink_hover = true;
|
self.renderer_state.terminal.screen.dirty.hyperlink_hover = true;
|
||||||
|
|
@ -1042,38 +1097,18 @@ fn mouseRefreshLinks(
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
.pointer,
|
.pointer,
|
||||||
);
|
);
|
||||||
|
_ = try self.rt_app.performAction(
|
||||||
switch (link[0]) {
|
.{ .surface = self },
|
||||||
.open => {
|
.mouse_over_link,
|
||||||
const str = try self.io.terminal.screen.selectionString(self.alloc, .{
|
link,
|
||||||
.sel = link[1],
|
);
|
||||||
.trim = false,
|
|
||||||
});
|
|
||||||
defer self.alloc.free(str);
|
|
||||||
_ = try self.rt_app.performAction(
|
|
||||||
.{ .surface = self },
|
|
||||||
.mouse_over_link,
|
|
||||||
.{ .url = str },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
._open_osc8 => link: {
|
|
||||||
// Show the URL in the status bar
|
|
||||||
const pin = link[1].start();
|
|
||||||
const uri = self.osc8URI(pin) orelse {
|
|
||||||
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
|
||||||
break :link;
|
|
||||||
};
|
|
||||||
_ = try self.rt_app.performAction(
|
|
||||||
.{ .surface = self },
|
|
||||||
.mouse_over_link,
|
|
||||||
.{ .url = uri },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
} else if (over_link) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No link, if we're previously over a link then we need to clear
|
||||||
|
// the over-link apprt state.
|
||||||
|
if (over_link) {
|
||||||
_ = try self.rt_app.performAction(
|
_ = try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
|
|
@ -1085,6 +1120,7 @@ fn mouseRefreshLinks(
|
||||||
.{ .url = "" },
|
.{ .url = "" },
|
||||||
);
|
);
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue