cursor: implement implicit grabs
When a button is held down and the cursor leaves a surface, events now continue to be sent to the client. This allows e.g. dragging a scroll bar from outside the surface.
This commit is contained in:
parent
91052f7477
commit
0c4e3295b1
2 changed files with 55 additions and 29 deletions
|
@ -42,52 +42,66 @@ const ResizeData = struct {
|
||||||
|
|
||||||
const Mode = union(enum) {
|
const Mode = union(enum) {
|
||||||
passthrough: void,
|
passthrough: void,
|
||||||
|
down: *View,
|
||||||
move: *View,
|
move: *View,
|
||||||
resize: ResizeData,
|
resize: ResizeData,
|
||||||
|
|
||||||
/// Enter move or resize mode
|
/// Enter move or resize mode
|
||||||
fn enter(self: *Self, mode: @TagType(Mode), event: *c.wlr_event_pointer_button, view: *View) void {
|
fn enter(self: *Self, mode: @TagType(Mode), event: *c.wlr_event_pointer_button, view: *View) void {
|
||||||
std.debug.assert(self.mode == .passthrough);
|
|
||||||
|
|
||||||
log.debug(.cursor, "enter {} mode", .{@tagName(mode)});
|
log.debug(.cursor, "enter {} mode", .{@tagName(mode)});
|
||||||
|
|
||||||
const cur_box = &view.current.box;
|
switch (mode) {
|
||||||
self.mode = switch (mode) {
|
|
||||||
.passthrough => unreachable,
|
.passthrough => unreachable,
|
||||||
.move => .{ .move = view },
|
.down => self.mode = .{ .down = view },
|
||||||
.resize => .{
|
.move, .resize => {
|
||||||
.resize = .{
|
const cur_box = &view.current.box;
|
||||||
.view = view,
|
self.mode = switch (mode) {
|
||||||
.x_offset = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x),
|
.passthrough, .down => unreachable,
|
||||||
.y_offset = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y),
|
.move => .{ .move = view },
|
||||||
},
|
.resize => .{
|
||||||
|
.resize = .{
|
||||||
|
.view = view,
|
||||||
|
.x_offset = cur_box.x + @intCast(i32, cur_box.width) - @floatToInt(i32, self.wlr_cursor.x),
|
||||||
|
.y_offset = cur_box.y + @intCast(i32, cur_box.height) - @floatToInt(i32, self.wlr_cursor.y),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Automatically float all views being moved by the pointer
|
||||||
|
if (!view.current.float) {
|
||||||
|
view.pending.float = true;
|
||||||
|
// Start a transaction to apply the pending state of the grabbed
|
||||||
|
// view and rearrange the layout to fill the hole.
|
||||||
|
view.output.root.arrange();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cursor focus, so that the surface does not receive events
|
||||||
|
c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
|
||||||
|
|
||||||
|
c.wlr_xcursor_manager_set_cursor_image(
|
||||||
|
self.wlr_xcursor_manager,
|
||||||
|
if (mode == .move) "move" else "se-resize",
|
||||||
|
self.wlr_cursor,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
// Automatically float all views being moved by the pointer
|
|
||||||
if (!view.current.float) {
|
|
||||||
view.pending.float = true;
|
|
||||||
// Start a transaction to apply the pending state of the grabbed
|
|
||||||
// view and rearrange the layout to fill the hole.
|
|
||||||
view.output.root.arrange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cursor focus, so that the surface does not receive events
|
|
||||||
c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
|
|
||||||
|
|
||||||
c.wlr_xcursor_manager_set_cursor_image(
|
|
||||||
self.wlr_xcursor_manager,
|
|
||||||
if (mode == .move) "move" else "se-resize",
|
|
||||||
self.wlr_cursor,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return from move/resize to passthrough
|
/// Return from down/move/resize to passthrough
|
||||||
fn leave(self: *Self, event: *c.wlr_event_pointer_button) void {
|
fn leave(self: *Self, event: *c.wlr_event_pointer_button) void {
|
||||||
std.debug.assert(self.mode != .passthrough);
|
std.debug.assert(self.mode != .passthrough);
|
||||||
|
|
||||||
log.debug(.cursor, "leave {} mode", .{@tagName(self.mode)});
|
log.debug(.cursor, "leave {} mode", .{@tagName(self.mode)});
|
||||||
|
|
||||||
|
// If we were in down mode, we need pass along the release event
|
||||||
|
if (self.mode == .down)
|
||||||
|
_ = c.wlr_seat_pointer_notify_button(
|
||||||
|
self.seat.wlr_seat,
|
||||||
|
event.time_msec,
|
||||||
|
event.button,
|
||||||
|
event.state,
|
||||||
|
);
|
||||||
|
|
||||||
self.mode = .passthrough;
|
self.mode = .passthrough;
|
||||||
passthrough(self, event.time_msec);
|
passthrough(self, event.time_msec);
|
||||||
}
|
}
|
||||||
|
@ -100,6 +114,15 @@ const Mode = union(enum) {
|
||||||
c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
|
c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
|
||||||
passthrough(self, time);
|
passthrough(self, time);
|
||||||
},
|
},
|
||||||
|
.down => |view| {
|
||||||
|
c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
|
||||||
|
c.wlr_seat_pointer_notify_motion(
|
||||||
|
self.seat.wlr_seat,
|
||||||
|
time,
|
||||||
|
self.wlr_cursor.x - @intToFloat(f64, view.current.box.x),
|
||||||
|
self.wlr_cursor.y - @intToFloat(f64, view.current.box.y),
|
||||||
|
);
|
||||||
|
},
|
||||||
.move => |view| {
|
.move => |view| {
|
||||||
var output_width: c_int = undefined;
|
var output_width: c_int = undefined;
|
||||||
var output_height: c_int = undefined;
|
var output_height: c_int = undefined;
|
||||||
|
@ -364,6 +387,8 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
Mode.enter(self, .down, event, view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ pub fn isCursorActionTarget(self: Self, view: *View) bool {
|
||||||
const seat = &node.data;
|
const seat = &node.data;
|
||||||
switch (seat.cursor.mode) {
|
switch (seat.cursor.mode) {
|
||||||
.passthrough => {},
|
.passthrough => {},
|
||||||
|
.down => |target_view| if (target_view == view) break true,
|
||||||
.move => |target_view| if (target_view == view) break true,
|
.move => |target_view| if (target_view == view) break true,
|
||||||
.resize => |data| if (data.view == view) break true,
|
.resize => |data| if (data.view == view) break true,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue