libghostty: selection gesture release event
parent
5ac8e6569a
commit
3fd2c66a04
|
|
@ -402,6 +402,9 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
/** Press event for ghostty_selection_gesture_press(). */
|
/** Press event for ghostty_selection_gesture_press(). */
|
||||||
GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_PRESS = 0,
|
GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_PRESS = 0,
|
||||||
|
|
||||||
|
/** Release event for ghostty_selection_gesture_release(). */
|
||||||
|
GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_RELEASE = 1,
|
||||||
|
|
||||||
GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||||
} GhosttySelectionGestureEventType;
|
} GhosttySelectionGestureEventType;
|
||||||
|
|
||||||
|
|
@ -414,7 +417,12 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
* @ingroup selection
|
* @ingroup selection
|
||||||
*/
|
*/
|
||||||
typedef enum GHOSTTY_ENUM_TYPED {
|
typedef enum GHOSTTY_ENUM_TYPED {
|
||||||
/** Grid reference under the pointer: GhosttyGridRef*. */
|
/**
|
||||||
|
* Grid reference under the pointer: GhosttyGridRef*.
|
||||||
|
*
|
||||||
|
* Required for PRESS events. Optional for RELEASE events; when unset or
|
||||||
|
* cleared, release records that the pointer did not map to a valid cell.
|
||||||
|
*/
|
||||||
GHOSTTY_SELECTION_GESTURE_EVENT_OPT_REF = 0,
|
GHOSTTY_SELECTION_GESTURE_EVENT_OPT_REF = 0,
|
||||||
|
|
||||||
/** Surface-space pointer position: GhosttySurfacePosition*. */
|
/** Surface-space pointer position: GhosttySurfacePosition*. */
|
||||||
|
|
@ -508,6 +516,12 @@ GHOSTTY_API GhosttyResult ghostty_selection_gesture_event_set(
|
||||||
* GHOSTTY_SELECTION_GESTURE_EVENT_OPT_REF set before calling this function.
|
* GHOSTTY_SELECTION_GESTURE_EVENT_OPT_REF set before calling this function.
|
||||||
* All other press options use their initialized defaults when unset or cleared.
|
* All other press options use their initialized defaults when unset or cleared.
|
||||||
*
|
*
|
||||||
|
* For GHOSTTY_SELECTION_GESTURE_EVENT_TYPE_RELEASE, only
|
||||||
|
* GHOSTTY_SELECTION_GESTURE_EVENT_OPT_REF is valid. It is optional; if unset or
|
||||||
|
* cleared, release records that the pointer did not map to a valid cell. Release
|
||||||
|
* events update gesture state but do not produce a selection, so this function
|
||||||
|
* returns GHOSTTY_NO_VALUE after applying them.
|
||||||
|
*
|
||||||
* The returned selection is not installed as the terminal's current selection.
|
* The returned selection is not installed as the terminal's current selection.
|
||||||
* It is a snapshot with the same lifetime rules as GhosttySelection.
|
* It is a snapshot with the same lifetime rules as GhosttySelection.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ const EventWrapper = struct {
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
event: union(EventType) {
|
event: union(EventType) {
|
||||||
press: SelectionGesture.Press,
|
press: SelectionGesture.Press,
|
||||||
|
release: SelectionGesture.Release,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Press.pin has no safe sentinel value: PageList.Pin contains a non-null
|
// Press.pin has no safe sentinel value: PageList.Pin contains a non-null
|
||||||
|
|
@ -51,6 +52,7 @@ const EventWrapper = struct {
|
||||||
fn init(self: *EventWrapper, event_type: EventType) void {
|
fn init(self: *EventWrapper, event_type: EventType) void {
|
||||||
self.event = switch (event_type) {
|
self.event = switch (event_type) {
|
||||||
.press => .{ .press = self.defaultPress() },
|
.press => .{ .press = self.defaultPress() },
|
||||||
|
.release => .{ .release = self.defaultRelease() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +69,11 @@ const EventWrapper = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn defaultRelease(self: *EventWrapper) SelectionGesture.Release {
|
||||||
|
_ = self;
|
||||||
|
return .{ .pin = null };
|
||||||
|
}
|
||||||
|
|
||||||
fn deinit(self: *EventWrapper) void {
|
fn deinit(self: *EventWrapper) void {
|
||||||
if (self.word_boundary_codepoints) |cps| {
|
if (self.word_boundary_codepoints) |cps| {
|
||||||
if (cps.len > 0) self.alloc.free(cps);
|
if (cps.len > 0) self.alloc.free(cps);
|
||||||
|
|
@ -109,6 +116,7 @@ pub const Data = enum(c_int) {
|
||||||
/// C: GhosttySelectionGestureEventType
|
/// C: GhosttySelectionGestureEventType
|
||||||
pub const EventType = enum(c_int) {
|
pub const EventType = enum(c_int) {
|
||||||
press = 0,
|
press = 0,
|
||||||
|
release = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// C: GhosttySelectionGestureEventOption
|
/// C: GhosttySelectionGestureEventOption
|
||||||
|
|
@ -222,6 +230,10 @@ pub fn handle_event(
|
||||||
} else if (sel == null) return .no_value;
|
} else if (sel == null) return .no_value;
|
||||||
return .success;
|
return .success;
|
||||||
},
|
},
|
||||||
|
.release => |release| {
|
||||||
|
wrapper.gesture.release(t, release);
|
||||||
|
return .no_value;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,6 +336,7 @@ fn eventSetTyped(
|
||||||
const event = event_ orelse return .invalid_value;
|
const event = event_ orelse return .invalid_value;
|
||||||
return switch (event.event) {
|
return switch (event.event) {
|
||||||
.press => |*press| pressSetTyped(event, press, option, value),
|
.press => |*press| pressSetTyped(event, press, option, value),
|
||||||
|
.release => |*release| releaseSetTyped(release, option, value),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,6 +405,32 @@ fn pressSetTyped(
|
||||||
return .success;
|
return .success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn releaseSetTyped(
|
||||||
|
release: *SelectionGesture.Release,
|
||||||
|
comptime option: EventOption,
|
||||||
|
value: ?*const option.Type(),
|
||||||
|
) Result {
|
||||||
|
switch (option) {
|
||||||
|
.ref => {
|
||||||
|
const v = value orelse {
|
||||||
|
release.pin = null;
|
||||||
|
return .success;
|
||||||
|
};
|
||||||
|
release.pin = v.toPin() orelse return .invalid_value;
|
||||||
|
},
|
||||||
|
|
||||||
|
.position,
|
||||||
|
.repeat_distance,
|
||||||
|
.time_ns,
|
||||||
|
.repeat_interval_ns,
|
||||||
|
.word_boundary_codepoints,
|
||||||
|
.behaviors,
|
||||||
|
=> return .invalid_value,
|
||||||
|
}
|
||||||
|
|
||||||
|
return .success;
|
||||||
|
}
|
||||||
|
|
||||||
fn clearPressCodepoints(event: *EventWrapper, press: *SelectionGesture.Press) void {
|
fn clearPressCodepoints(event: *EventWrapper, press: *SelectionGesture.Press) void {
|
||||||
if (event.word_boundary_codepoints) |cps| {
|
if (event.word_boundary_codepoints) |cps| {
|
||||||
if (cps.len > 0) event.alloc.free(cps);
|
if (cps.len > 0) event.alloc.free(cps);
|
||||||
|
|
@ -716,6 +755,104 @@ test "selection gesture event null output still reports no selection" {
|
||||||
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, press_event, null));
|
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, press_event, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "selection gesture event applies release" {
|
||||||
|
var terminal: terminal_c.Terminal = null;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&terminal,
|
||||||
|
.{ .cols = 5, .rows = 2, .max_scrollback = 10_000 },
|
||||||
|
));
|
||||||
|
defer terminal_c.free(terminal);
|
||||||
|
|
||||||
|
var gesture: Gesture = null;
|
||||||
|
try testing.expectEqual(Result.success, new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&gesture,
|
||||||
|
));
|
||||||
|
defer free(gesture, terminal);
|
||||||
|
|
||||||
|
var press_event: Event = null;
|
||||||
|
try testing.expectEqual(Result.success, event_new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&press_event,
|
||||||
|
.press,
|
||||||
|
));
|
||||||
|
defer event_free(press_event);
|
||||||
|
|
||||||
|
var release_event: Event = null;
|
||||||
|
try testing.expectEqual(Result.success, event_new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&release_event,
|
||||||
|
.release,
|
||||||
|
));
|
||||||
|
defer event_free(release_event);
|
||||||
|
|
||||||
|
var ref: grid_ref.CGridRef = undefined;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.grid_ref(terminal, .{
|
||||||
|
.tag = .active,
|
||||||
|
.value = .{ .active = .{ .x = 1, .y = 0 } },
|
||||||
|
}, &ref));
|
||||||
|
try testing.expectEqual(Result.success, event_set(press_event, .ref, &ref));
|
||||||
|
try testing.expectEqual(Result.success, event_set(release_event, .ref, &ref));
|
||||||
|
|
||||||
|
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, press_event, null));
|
||||||
|
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, release_event, null));
|
||||||
|
|
||||||
|
var dragged = true;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .dragged, &dragged));
|
||||||
|
try testing.expect(!dragged);
|
||||||
|
|
||||||
|
const pos: types.SurfacePosition = .{ .x = 0, .y = 0 };
|
||||||
|
try testing.expectEqual(Result.invalid_value, event_set(release_event, .position, &pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "selection gesture release without ref marks dragged" {
|
||||||
|
var terminal: terminal_c.Terminal = null;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&terminal,
|
||||||
|
.{ .cols = 5, .rows = 2, .max_scrollback = 10_000 },
|
||||||
|
));
|
||||||
|
defer terminal_c.free(terminal);
|
||||||
|
|
||||||
|
var gesture: Gesture = null;
|
||||||
|
try testing.expectEqual(Result.success, new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&gesture,
|
||||||
|
));
|
||||||
|
defer free(gesture, terminal);
|
||||||
|
|
||||||
|
var press_event: Event = null;
|
||||||
|
try testing.expectEqual(Result.success, event_new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&press_event,
|
||||||
|
.press,
|
||||||
|
));
|
||||||
|
defer event_free(press_event);
|
||||||
|
|
||||||
|
var release_event: Event = null;
|
||||||
|
try testing.expectEqual(Result.success, event_new(
|
||||||
|
&lib.alloc.test_allocator,
|
||||||
|
&release_event,
|
||||||
|
.release,
|
||||||
|
));
|
||||||
|
defer event_free(release_event);
|
||||||
|
|
||||||
|
var ref: grid_ref.CGridRef = undefined;
|
||||||
|
try testing.expectEqual(Result.success, terminal_c.grid_ref(terminal, .{
|
||||||
|
.tag = .active,
|
||||||
|
.value = .{ .active = .{ .x = 1, .y = 0 } },
|
||||||
|
}, &ref));
|
||||||
|
try testing.expectEqual(Result.success, event_set(press_event, .ref, &ref));
|
||||||
|
|
||||||
|
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, press_event, null));
|
||||||
|
try testing.expectEqual(Result.no_value, handle_event(gesture, terminal, release_event, null));
|
||||||
|
|
||||||
|
var dragged = false;
|
||||||
|
try testing.expectEqual(Result.success, get(gesture, terminal, .dragged, &dragged));
|
||||||
|
try testing.expect(dragged);
|
||||||
|
}
|
||||||
|
|
||||||
test "selection gesture free null" {
|
test "selection gesture free null" {
|
||||||
free(null, null);
|
free(null, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue