mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 09:16:11 +03:00
Merge pull request #2400 from ghostty-org/mouse-exit
Detect mouse leave events, use it to reset hover states
This commit is contained in:
@ -354,6 +354,7 @@ extension Ghostty {
|
||||
addTrackingArea(NSTrackingArea(
|
||||
rect: frame,
|
||||
options: [
|
||||
.mouseEnteredAndExited,
|
||||
.mouseMoved,
|
||||
|
||||
// Only send mouse events that happen in our visible (not obscured) rect
|
||||
@ -486,6 +487,14 @@ extension Ghostty {
|
||||
super.rightMouseUp(with: event)
|
||||
}
|
||||
|
||||
override func mouseExited(with event: NSEvent) {
|
||||
guard let surface = self.surface else { return }
|
||||
|
||||
// Negative values indicate cursor has left the viewport
|
||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
ghostty_surface_mouse_pos(surface, -1, -1, mods)
|
||||
}
|
||||
|
||||
override func mouseMoved(with event: NSEvent) {
|
||||
guard let surface = self.surface else { return }
|
||||
|
||||
|
@ -3032,6 +3032,11 @@ pub fn mousePressureCallback(
|
||||
|
||||
/// Cursor position callback.
|
||||
///
|
||||
/// Send negative x or y values to indicate the cursor is outside the
|
||||
/// viewport. The magnitude of the negative values are meaningless;
|
||||
/// they are only used to indicate the cursor is outside the viewport.
|
||||
/// It's important to do this to ensure hover states are cleared.
|
||||
///
|
||||
/// The mods parameter is optional because some apprts do not provide
|
||||
/// modifier information on cursor position events. If mods is null then
|
||||
/// we'll use the last known mods. This is usually accurate since mod events
|
||||
@ -3047,6 +3052,36 @@ pub fn cursorPosCallback(
|
||||
crash.sentry.thread_state = self.crashThreadState();
|
||||
defer crash.sentry.thread_state = null;
|
||||
|
||||
// If the position is negative, it is outside our viewport and
|
||||
// we need to clear any hover states.
|
||||
if (pos.x < 0 or pos.y < 0) {
|
||||
// Reset our hyperlink state
|
||||
self.mouse.link_point = null;
|
||||
if (self.mouse.over_link) {
|
||||
self.mouse.over_link = false;
|
||||
try self.rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.mouse_shape,
|
||||
self.io.terminal.mouse_shape,
|
||||
);
|
||||
try self.rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.mouse_over_link,
|
||||
.{ .url = "" },
|
||||
);
|
||||
try self.queueRender();
|
||||
}
|
||||
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
|
||||
// No mouse point so we don't highlight links
|
||||
self.renderer_state.mouse.point = null;
|
||||
self.renderer_state.terminal.screen.dirty.hyperlink_hover = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Always show the mouse again if it is hidden
|
||||
if (self.mouse.hidden) self.showMouse();
|
||||
|
||||
|
@ -526,6 +526,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
|
||||
_ = c.g_signal_connect_data(gesture_click, "pressed", c.G_CALLBACK(>kMouseDown), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(gesture_click, "released", c.G_CALLBACK(>kMouseUp), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(ec_motion, "motion", c.G_CALLBACK(>kMouseMotion), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(ec_motion, "leave", c.G_CALLBACK(>kMouseLeave), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(ec_scroll, "scroll", c.G_CALLBACK(>kMouseScroll), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(im_context, "preedit-start", c.G_CALLBACK(>kInputPreeditStart), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(im_context, "preedit-changed", c.G_CALLBACK(>kInputPreeditChanged), self, null, c.G_CONNECT_DEFAULT);
|
||||
@ -1344,6 +1345,22 @@ fn gtkMouseMotion(
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkMouseLeave(
|
||||
ec: *c.GtkEventControllerMotion,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
const self = userdataSelf(ud.?);
|
||||
|
||||
// Get our modifiers
|
||||
const event = c.gtk_event_controller_get_current_event(@ptrCast(ec));
|
||||
const gtk_mods = c.gdk_event_get_modifier_state(event);
|
||||
const mods = translateMods(gtk_mods);
|
||||
self.core_surface.cursorPosCallback(.{ .x = -1, .y = -1 }, mods) catch |err| {
|
||||
log.err("error in cursor pos callback err={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkMouseScroll(
|
||||
_: *c.GtkEventControllerScroll,
|
||||
x: c.gdouble,
|
||||
|
Reference in New Issue
Block a user