Open view relative to mouse, take 2

This commit is contained in:
Alexander Rosenberg 2024-11-01 22:08:20 -07:00
parent eab34c7c03
commit d66decb7c4
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
7 changed files with 58 additions and 56 deletions

View File

@ -1,6 +1,6 @@
function __riverctl_completion () function __riverctl_completion ()
{ {
local rule_actions="float no-float ssd csd tags output position dimensions fullscreen no-fullscreen" local rule_actions="float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen"
if [ "${COMP_CWORD}" -eq 1 ] if [ "${COMP_CWORD}" -eq 1 ]
then then
OPTS=" \ OPTS=" \

View File

@ -94,7 +94,7 @@ complete -c riverctl -n '__fish_seen_subcommand_from set-cursor-warp'
complete -c riverctl -n '__fish_seen_subcommand_from list-rules' -n '__fish_riverctl_complete_arg 2' -a 'float ssd tags output position dimensions fullscreen' complete -c riverctl -n '__fish_seen_subcommand_from list-rules' -n '__fish_riverctl_complete_arg 2' -a 'float ssd tags output position dimensions fullscreen'
# Options and subcommands for 'rule-add' and 'rule-del' # Options and subcommands for 'rule-add' and 'rule-del'
set -l rule_actions float no-float ssd csd tags output position dimensions fullscreen no-fullscreen set -l rule_actions float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen
complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o app-id' -o 'app-id' -r complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o app-id' -o 'app-id' -r
complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o title' -o 'title' -r complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'not __fish_seen_argument -o title' -o 'title' -r
complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'test (math (count (commandline -opc)) % 2) -eq 0' -a "$rule_actions" complete -c riverctl -n '__fish_seen_subcommand_from rule-add rule-del' -n "not __fish_seen_subcommand_from $rule_actions" -n 'test (math (count (commandline -opc)) % 2) -eq 0' -a "$rule_actions"

View File

@ -207,7 +207,7 @@ _riverctl()
# In case of a new rule added in river, we just need # In case of a new rule added in river, we just need
# to add it to the third option between '()', # to add it to the third option between '()',
# i.e (float no-float <new-option>) # i.e (float no-float <new-option>)
_arguments '1: :(-app-id -title)' '2: : ' ':: :(float no-float ssd csd tags output position dimensions fullscreen no-fullscreen)' _arguments '1: :(-app-id -title)' '2: : ' ':: :(float no-float ssd csd tags output position relative-position dimensions fullscreen no-fullscreen)'
;; ;;
list-rules) _alternative 'arguments:args:(float ssd tags output position dimensions fullscreen)' ;; list-rules) _alternative 'arguments:args:(float ssd tags output position dimensions fullscreen)' ;;
*) return 0 ;; *) return 0 ;;

View File

@ -300,9 +300,11 @@ matches everything while _\*\*_ and the empty string are invalid.
serial is unknown, the word "Unknown" is used instead. serial is unknown, the word "Unknown" is used instead.
- *position*: Set the initial position of the view, clamping to the bounds - *position*: Set the initial position of the view, clamping to the bounds
of the output. Requires x and y coordinates of the view as arguments, both of the output. Requires x and y coordinates of the view as arguments, both
of which must be non-negative. Optionally, the string "mouse" can appear of which must be non-negative. Applies only to new views.
as the only argument. In this case, the view will appear at the mouse's - *relative-position*: Set the position of the view relative to
position. Applies only to new views. something. Requires the anchor and the x and y coordinates of the
view. The coordinates are either positive or negative numbers that are
relative to the anchor. Applies only to new views.
- *dimensions*: Set the initial dimensions of the view, clamping to the - *dimensions*: Set the initial dimensions of the view, clamping to the
constraints of the view. Requires width and height of the view as constraints of the view. Requires width and height of the view as
arguments, both of which must be non-negative. Applies only to new views. arguments, both of which must be non-negative. Applies only to new views.

View File

@ -58,19 +58,15 @@ pub const HideCursorWhenTypingMode = enum {
enabled, enabled,
}; };
pub const PositionType = enum { pub const Anchor = enum {
absolute, absolute,
at_mouse, mouse,
}; };
pub const Position = struct { pub const Position = struct {
x: u31, anchor: Anchor,
y: u31, x: i31,
}; y: i31,
pub const FloatPosition = union(PositionType) {
absolute: Position,
at_mouse,
}; };
pub const Dimensions = struct { pub const Dimensions = struct {
@ -108,7 +104,7 @@ rules: struct {
ssd: RuleList(bool) = .{}, ssd: RuleList(bool) = .{},
tags: RuleList(u32) = .{}, tags: RuleList(u32) = .{},
output: RuleList([]const u8) = .{}, output: RuleList([]const u8) = .{},
position: RuleList(FloatPosition) = .{}, position: RuleList(Position) = .{},
dimensions: RuleList(Dimensions) = .{}, dimensions: RuleList(Dimensions) = .{},
fullscreen: RuleList(bool) = .{}, fullscreen: RuleList(bool) = .{},
tearing: RuleList(bool) = .{}, tearing: RuleList(bool) = .{},

View File

@ -678,17 +678,18 @@ pub fn map(view: *View) !void {
server.input_manager.defaultSeat().focused_output; server.input_manager.defaultSeat().focused_output;
if (server.config.rules.position.match(view)) |position| { if (server.config.rules.position.match(view)) |position| {
switch (position) { var base_x: i31 = 0;
.absolute => |pos| { var base_y: i31 = 0;
view.pending.box.x = pos.x; switch (position.anchor) {
view.pending.box.y = pos.y; .absolute => {},
}, .mouse => {
.at_mouse => { const cursor = server.input_manager.defaultSeat().wlr_seat.pointer_state;
const cursor = server.input_manager.defaultSeat().cursor.wlr_cursor; base_x = @intCast(@as(i31, @intFromFloat(cursor.sx)));
view.pending.box.x = @as(c_int, @intFromFloat(cursor.x)); base_y = @intCast(@as(i31, @intFromFloat(cursor.sy)));
view.pending.box.y = @as(c_int, @intFromFloat(cursor.y));
}, },
} }
view.pending.box.x = base_x + position.x;
view.pending.box.y = base_y + position.y;
} else if (output) |o| { } else if (output) |o| {
// Center the initial pending box on the output // Center the initial pending box on the output
view.pending.box.x = @divTrunc(@max(0, o.usable_box.width - view.pending.box.width), 2); view.pending.box.x = @divTrunc(@max(0, o.usable_box.width - view.pending.box.width), 2);

View File

@ -26,6 +26,7 @@ const util = @import("../util.zig");
const Error = @import("../command.zig").Error; const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig"); const Seat = @import("../Seat.zig");
const View = @import("../View.zig"); const View = @import("../View.zig");
const Anchor = @import("../Config.zig").Anchor;
const Action = enum { const Action = enum {
float, float,
@ -35,6 +36,7 @@ const Action = enum {
tags, tags,
output, output,
position, position,
@"relative-position",
dimensions, dimensions,
fullscreen, fullscreen,
@"no-fullscreen", @"no-fullscreen",
@ -54,17 +56,11 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption; const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
var pos_is_mouse = false;
const positional_arguments_count: u8 = switch (action) { const positional_arguments_count: u8 = switch (action) {
.float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1, .float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1,
.tags, .output => 2, .tags, .output => 2,
.dimensions => 3, .position, .dimensions => 3,
.position => blk: { .@"relative-position" => 4,
if (result.args.len >= 2 and std.mem.eql(u8, result.args[1], "mouse")) {
pos_is_mouse = true;
break :blk 2;
} else break :blk 3;
},
}; };
if (result.args.len > positional_arguments_count) return Error.TooManyArguments; if (result.args.len > positional_arguments_count) return Error.TooManyArguments;
if (result.args.len < positional_arguments_count) return Error.NotEnoughArguments; if (result.args.len < positional_arguments_count) return Error.NotEnoughArguments;
@ -118,24 +114,34 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
}); });
}, },
.position => { .position => {
if (pos_is_mouse) { const x = try fmt.parseInt(i31, result.args[1], 10);
try server.config.rules.position.add(.{ const y = try fmt.parseInt(i31, result.args[2], 10);
.app_id_glob = app_id_glob, if (x < 0 or y < 0) return Error.OutOfBounds;
.title_glob = title_glob, try server.config.rules.position.add(.{
.value = .at_mouse, .app_id_glob = app_id_glob,
}); .title_glob = title_glob,
} else { .value = .{
const x = try fmt.parseInt(u31, result.args[1], 10); .anchor = .absolute,
const y = try fmt.parseInt(u31, result.args[2], 10); .x = @intCast(x),
try server.config.rules.position.add(.{ .y = @intCast(y),
.app_id_glob = app_id_glob, },
.title_glob = title_glob, });
.value = .{ .absolute = .{ },
.x = x, .@"relative-position" => {
.y = y, const anchor = std.meta.stringToEnum(Anchor, result.args[1]) orelse return Error.UnknownOption;
} }, // force the use of the normal position command for absolute positions
}); if (anchor == .absolute) return Error.UnknownOption;
} const x_off = try fmt.parseInt(i31, result.args[2], 10);
const y_off = try fmt.parseInt(i31, result.args[3], 10);
try server.config.rules.position.add(.{
.app_id_glob = app_id_glob,
.title_glob = title_glob,
.value = .{
.anchor = anchor,
.x = x_off,
.y = y_off,
},
});
}, },
.dimensions => { .dimensions => {
const width = try fmt.parseInt(u31, result.args[1], 10); const width = try fmt.parseInt(u31, result.args[1], 10);
@ -193,7 +199,7 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
util.gpa.free(output_rule); util.gpa.free(output_rule);
} }
}, },
.position => { .position, .@"relative-position" => {
_ = server.config.rules.position.del(rule); _ = server.config.rules.position.del(rule);
}, },
.dimensions => { .dimensions => {
@ -292,10 +298,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
for (server.config.rules.position.rules.items) |rule| { for (server.config.rules.position.rules.items) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer);
try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer);
switch (rule.value) { try writer.print("{s},{d},{d}", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y });
.absolute => |pos| try writer.print("{d},{d}\n", .{ pos.x, pos.y }),
.at_mouse => try writer.print("mouse\n", .{}),
}
} }
}, },
.dimensions => { .dimensions => {