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(
|
addTrackingArea(NSTrackingArea(
|
||||||
rect: frame,
|
rect: frame,
|
||||||
options: [
|
options: [
|
||||||
|
.mouseEnteredAndExited,
|
||||||
.mouseMoved,
|
.mouseMoved,
|
||||||
|
|
||||||
// Only send mouse events that happen in our visible (not obscured) rect
|
// Only send mouse events that happen in our visible (not obscured) rect
|
||||||
@ -486,6 +487,14 @@ extension Ghostty {
|
|||||||
super.rightMouseUp(with: event)
|
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) {
|
override func mouseMoved(with event: NSEvent) {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
|
|
||||||
|
@ -3032,6 +3032,11 @@ pub fn mousePressureCallback(
|
|||||||
|
|
||||||
/// Cursor position callback.
|
/// 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
|
/// The mods parameter is optional because some apprts do not provide
|
||||||
/// modifier information on cursor position events. If mods is null then
|
/// 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
|
/// 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();
|
crash.sentry.thread_state = self.crashThreadState();
|
||||||
defer crash.sentry.thread_state = null;
|
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
|
// Always show the mouse again if it is hidden
|
||||||
if (self.mouse.hidden) self.showMouse();
|
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, "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(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, "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(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-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);
|
_ = 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(
|
fn gtkMouseScroll(
|
||||||
_: *c.GtkEventControllerScroll,
|
_: *c.GtkEventControllerScroll,
|
||||||
x: c.gdouble,
|
x: c.gdouble,
|
||||||
|
Reference in New Issue
Block a user