mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
core: surface now tracks left click pin
This commit is contained in:
@ -157,7 +157,8 @@ const Mouse = struct {
|
|||||||
/// The point at which the left mouse click happened. This is in screen
|
/// The point at which the left mouse click happened. This is in screen
|
||||||
/// coordinates so that scrolling preserves the location.
|
/// coordinates so that scrolling preserves the location.
|
||||||
//TODO(paged-terminal)
|
//TODO(paged-terminal)
|
||||||
//left_click_point: terminal.point.ScreenPoint = .{},
|
left_click_pin: ?*terminal.Pin = null,
|
||||||
|
left_click_screen: terminal.ScreenType = .primary,
|
||||||
|
|
||||||
/// The starting xpos/ypos of the left click. Note that if scrolling occurs,
|
/// The starting xpos/ypos of the left click. Note that if scrolling occurs,
|
||||||
/// these will point to different "cells", but the xpos/ypos will stay
|
/// these will point to different "cells", but the xpos/ypos will stay
|
||||||
@ -1049,9 +1050,9 @@ fn clipboardWrite(self: *const Surface, data: []const u8, loc: apprt.Clipboard)
|
|||||||
/// Set the selection contents.
|
/// Set the selection contents.
|
||||||
///
|
///
|
||||||
/// This must be called with the renderer mutex held.
|
/// This must be called with the renderer mutex held.
|
||||||
fn setSelection(self: *Surface, sel_: ?terminal.Selection) void {
|
fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void {
|
||||||
const prev_ = self.io.terminal.screen.selection;
|
const prev_ = self.io.terminal.screen.selection;
|
||||||
self.io.terminal.screen.selection = sel_;
|
try self.io.terminal.screen.select(sel_);
|
||||||
|
|
||||||
// Determine the clipboard we want to copy selection to, if it is enabled.
|
// Determine the clipboard we want to copy selection to, if it is enabled.
|
||||||
const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) {
|
const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) {
|
||||||
@ -1541,7 +1542,7 @@ pub fn keyCallback(
|
|||||||
if (!event.key.modifier()) {
|
if (!event.key.modifier()) {
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
self.setSelection(null);
|
try self.setSelection(null);
|
||||||
try self.io.terminal.scrollViewport(.{ .bottom = {} });
|
try self.io.terminal.scrollViewport(.{ .bottom = {} });
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
@ -1748,7 +1749,7 @@ pub fn scrollCallback(
|
|||||||
// The selection can occur if the user uses the shift mod key to
|
// The selection can occur if the user uses the shift mod key to
|
||||||
// override mouse grabbing from the window.
|
// override mouse grabbing from the window.
|
||||||
if (self.io.terminal.flags.mouse_event != .none) {
|
if (self.io.terminal.flags.mouse_event != .none) {
|
||||||
self.setSelection(null);
|
try self.setSelection(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in alternate screen with alternate scroll enabled, then
|
// If we're in alternate screen with alternate scroll enabled, then
|
||||||
@ -1762,7 +1763,7 @@ pub fn scrollCallback(
|
|||||||
if (y.delta_unsigned > 0) {
|
if (y.delta_unsigned > 0) {
|
||||||
// When we send mouse events as cursor keys we always
|
// When we send mouse events as cursor keys we always
|
||||||
// clear the selection.
|
// clear the selection.
|
||||||
self.setSelection(null);
|
try self.setSelection(null);
|
||||||
|
|
||||||
const seq = if (self.io.terminal.modes.get(.cursor_keys)) seq: {
|
const seq = if (self.io.terminal.modes.get(.cursor_keys)) seq: {
|
||||||
// cursor key: application mode
|
// cursor key: application mode
|
||||||
@ -2219,7 +2220,7 @@ pub fn mouseButtonCallback(
|
|||||||
// In any other mouse button scenario without shift pressed we
|
// In any other mouse button scenario without shift pressed we
|
||||||
// clear the selection since the underlying application can handle
|
// clear the selection since the underlying application can handle
|
||||||
// that in any way (i.e. "scrolling").
|
// that in any way (i.e. "scrolling").
|
||||||
self.setSelection(null);
|
try self.setSelection(null);
|
||||||
|
|
||||||
// We also set the left click count to 0 so that if mouse reporting
|
// We also set the left click count to 0 so that if mouse reporting
|
||||||
// is disabled in the middle of press (before release) we don't
|
// is disabled in the middle of press (before release) we don't
|
||||||
@ -2261,15 +2262,31 @@ pub fn mouseButtonCallback(
|
|||||||
// For left button clicks we always record some information for
|
// For left button clicks we always record some information for
|
||||||
// selection/highlighting purposes.
|
// selection/highlighting purposes.
|
||||||
if (button == .left and action == .press) click: {
|
if (button == .left and action == .press) click: {
|
||||||
// TODO(paged-terminal)
|
|
||||||
if (true) break :click;
|
|
||||||
|
|
||||||
self.renderer_state.mutex.lock();
|
self.renderer_state.mutex.lock();
|
||||||
defer self.renderer_state.mutex.unlock();
|
defer self.renderer_state.mutex.unlock();
|
||||||
|
const t: *terminal.Terminal = self.renderer_state.terminal;
|
||||||
|
const screen = &self.renderer_state.terminal.screen;
|
||||||
|
|
||||||
const pos = try self.rt_surface.getCursorPos();
|
const pos = try self.rt_surface.getCursorPos();
|
||||||
const pt_viewport = self.posToViewport(pos.x, pos.y);
|
const pin = pin: {
|
||||||
const pt_screen = pt_viewport.toScreen(&self.io.terminal.screen);
|
const pt_viewport = self.posToViewport(pos.x, pos.y);
|
||||||
|
const pin = screen.pages.pin(.{
|
||||||
|
.viewport = .{
|
||||||
|
.x = pt_viewport.x,
|
||||||
|
.y = pt_viewport.y,
|
||||||
|
},
|
||||||
|
}) orelse {
|
||||||
|
// Weird... our viewport x/y that we just converted isn't
|
||||||
|
// found in our pages. This is probably a bug but we don't
|
||||||
|
// want to crash in releases because its harmless. So, we
|
||||||
|
// only assert in debug mode.
|
||||||
|
if (comptime std.debug.runtime_safety) unreachable;
|
||||||
|
break :click;
|
||||||
|
};
|
||||||
|
|
||||||
|
break :pin try screen.pages.trackPin(pin);
|
||||||
|
};
|
||||||
|
errdefer screen.pages.untrackPin(pin);
|
||||||
|
|
||||||
// If we move our cursor too much between clicks then we reset
|
// If we move our cursor too much between clicks then we reset
|
||||||
// the multi-click state.
|
// the multi-click state.
|
||||||
@ -2283,8 +2300,14 @@ pub fn mouseButtonCallback(
|
|||||||
if (distance > max_distance) self.mouse.left_click_count = 0;
|
if (distance > max_distance) self.mouse.left_click_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(paged-terminal): untrack previous pin across screens
|
||||||
|
if (self.mouse.left_click_pin) |prev| {
|
||||||
|
screen.pages.untrackPin(prev);
|
||||||
|
}
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
self.mouse.left_click_point = pt_screen;
|
self.mouse.left_click_pin = pin;
|
||||||
|
self.mouse.left_click_screen = t.active_screen;
|
||||||
self.mouse.left_click_xpos = pos.x;
|
self.mouse.left_click_xpos = pos.x;
|
||||||
self.mouse.left_click_ypos = pos.y;
|
self.mouse.left_click_ypos = pos.y;
|
||||||
|
|
||||||
@ -2314,16 +2337,16 @@ pub fn mouseButtonCallback(
|
|||||||
1 => {
|
1 => {
|
||||||
// If we have a selection, clear it. This always happens.
|
// If we have a selection, clear it. This always happens.
|
||||||
if (self.io.terminal.screen.selection != null) {
|
if (self.io.terminal.screen.selection != null) {
|
||||||
self.setSelection(null);
|
try self.setSelection(null);
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Double click, select the word under our mouse
|
// Double click, select the word under our mouse
|
||||||
2 => {
|
2 => {
|
||||||
const sel_ = self.io.terminal.screen.selectWord(self.mouse.left_click_point);
|
const sel_ = self.io.terminal.screen.selectWord(pin.*);
|
||||||
if (sel_) |sel| {
|
if (sel_) |sel| {
|
||||||
self.setSelection(sel);
|
try self.setSelection(sel);
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2331,11 +2354,11 @@ pub fn mouseButtonCallback(
|
|||||||
// Triple click, select the line under our mouse
|
// Triple click, select the line under our mouse
|
||||||
3 => {
|
3 => {
|
||||||
const sel_ = if (mods.ctrl)
|
const sel_ = if (mods.ctrl)
|
||||||
self.io.terminal.screen.selectOutput(self.mouse.left_click_point)
|
self.io.terminal.screen.selectOutput(pin.*)
|
||||||
else
|
else
|
||||||
self.io.terminal.screen.selectLine(self.mouse.left_click_point);
|
self.io.terminal.screen.selectLine(pin.*);
|
||||||
if (sel_) |sel| {
|
if (sel_) |sel| {
|
||||||
self.setSelection(sel);
|
try self.setSelection(sel);
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3304,7 +3327,7 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
.select_all => {
|
.select_all => {
|
||||||
const sel = self.io.terminal.screen.selectAll();
|
const sel = self.io.terminal.screen.selectAll();
|
||||||
if (sel) |s| {
|
if (sel) |s| {
|
||||||
self.setSelection(s);
|
try self.setSelection(s);
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -32,6 +32,7 @@ pub const PageList = @import("PageList.zig");
|
|||||||
pub const Parser = @import("Parser.zig");
|
pub const Parser = @import("Parser.zig");
|
||||||
pub const Pin = PageList.Pin;
|
pub const Pin = PageList.Pin;
|
||||||
pub const Screen = @import("Screen.zig");
|
pub const Screen = @import("Screen.zig");
|
||||||
|
pub const ScreenType = Terminal.ScreenType;
|
||||||
pub const Selection = @import("Selection.zig");
|
pub const Selection = @import("Selection.zig");
|
||||||
pub const Terminal = @import("Terminal.zig");
|
pub const Terminal = @import("Terminal.zig");
|
||||||
pub const Stream = stream.Stream;
|
pub const Stream = stream.Stream;
|
||||||
|
Reference in New Issue
Block a user