core: surface now tracks left click pin

This commit is contained in:
Mitchell Hashimoto
2024-03-10 16:57:41 -07:00
parent 3caf6779a5
commit 8ccc30da10
2 changed files with 44 additions and 20 deletions

View File

@ -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();
} }
}, },

View File

@ -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;