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 ()
{
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 ]
then
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'
# 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 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"

View File

@ -207,7 +207,7 @@ _riverctl()
# In case of a new rule added in river, we just need
# to add it to the third option between '()',
# 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)' ;;
*) 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.
- *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 which must be non-negative. Optionally, the string "mouse" can appear
as the only argument. In this case, the view will appear at the mouse's
position. Applies only to new views.
of which must be non-negative. Applies only to new views.
- *relative-position*: Set the position of the view relative to
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
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.

View File

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

View File

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

@ -26,6 +26,7 @@ const util = @import("../util.zig");
const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig");
const View = @import("../View.zig");
const Anchor = @import("../Config.zig").Anchor;
const Action = enum {
float,
@ -35,6 +36,7 @@ const Action = enum {
tags,
output,
position,
@"relative-position",
dimensions,
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;
var pos_is_mouse = false;
const positional_arguments_count: u8 = switch (action) {
.float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1,
.tags, .output => 2,
.dimensions => 3,
.position => blk: {
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;
},
.position, .dimensions => 3,
.@"relative-position" => 4,
};
if (result.args.len > positional_arguments_count) return Error.TooManyArguments;
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 => {
if (pos_is_mouse) {
const x = try fmt.parseInt(i31, result.args[1], 10);
const y = try fmt.parseInt(i31, result.args[2], 10);
if (x < 0 or y < 0) return Error.OutOfBounds;
try server.config.rules.position.add(.{
.app_id_glob = app_id_glob,
.title_glob = title_glob,
.value = .at_mouse,
.value = .{
.anchor = .absolute,
.x = @intCast(x),
.y = @intCast(y),
},
});
} else {
const x = try fmt.parseInt(u31, result.args[1], 10);
const y = try fmt.parseInt(u31, result.args[2], 10);
},
.@"relative-position" => {
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 = .{ .absolute = .{
.x = x,
.y = y,
} },
.value = .{
.anchor = anchor,
.x = x_off,
.y = y_off,
},
});
}
},
.dimensions => {
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);
}
},
.position => {
.position, .@"relative-position" => {
_ = server.config.rules.position.del(rule);
},
.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| {
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);
switch (rule.value) {
.absolute => |pos| try writer.print("{d},{d}\n", .{ pos.x, pos.y }),
.at_mouse => try writer.print("mouse\n", .{}),
}
try writer.print("{s},{d},{d}", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y });
}
},
.dimensions => {