Add default-floating-position window rule

This commit is contained in:
Ivan Molodetskikh
2024-12-29 10:39:21 +03:00
parent 744955ba69
commit 3c2e1554c6
6 changed files with 78 additions and 11 deletions
+15
View File
@@ -1019,6 +1019,8 @@ pub struct WindowRule {
pub block_out_from: Option<BlockOutFrom>,
#[knuffel(child, unwrap(argument))]
pub variable_refresh_rate: Option<bool>,
#[knuffel(child)]
pub default_floating_position: Option<FoIPosition>,
}
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
@@ -1082,6 +1084,14 @@ pub struct BorderRule {
pub inactive_gradient: Option<Gradient>,
}
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct FoIPosition {
#[knuffel(property)]
pub x: FloatOrInt<-65535, 65535>,
#[knuffel(property)]
pub y: FloatOrInt<-65535, 65535>,
}
#[derive(Debug, Default, PartialEq)]
pub struct Binds(pub Vec<Bind>);
@@ -3192,6 +3202,7 @@ mod tests {
open-floating false
open-focused true
default-window-height { fixed 500; }
default-floating-position x=100 y=-200
focus-ring {
off
@@ -3476,6 +3487,10 @@ mod tests {
open_floating: Some(false),
open_focused: Some(true),
default_window_height: Some(DefaultPresetSize(Some(PresetSize::Fixed(500)))),
default_floating_position: Some(FoIPosition {
x: FloatOrInt(100.),
y: FloatOrInt(-200.),
}),
focus_ring: BorderRule {
off: true,
width: Some(FloatOrInt(3.)),
+8 -6
View File
@@ -415,12 +415,14 @@ impl<W: LayoutElement> FloatingSpace<W> {
}
}
let pos = tile
.floating_pos
.map(|pos| self.scale_by_working_area(pos))
.unwrap_or_else(|| {
center_preferring_top_left_in_area(self.working_area, tile.tile_size())
});
let pos = tile.floating_pos.map(|pos| self.scale_by_working_area(pos));
let pos = pos.or_else(|| {
tile.default_floating_logical_pos()
.map(|pos| pos + self.working_area.loc)
});
let pos = pos.unwrap_or_else(|| {
center_preferring_top_left_in_area(self.working_area, tile.tile_size())
});
let data = Data::new(self.working_area, &tile, pos);
self.data.insert(idx, data);
+5
View File
@@ -999,6 +999,11 @@ impl<W: LayoutElement> Tile<W> {
&self.options
}
pub fn default_floating_logical_pos(&self) -> Option<Point<f64, Logical>> {
let pos = self.window().rules().default_floating_position?;
Some(Point::from((pos.x.0, pos.y.0)))
}
#[cfg(test)]
pub fn view_size(&self) -> Size<f64, Logical> {
self.view_size
+15 -4
View File
@@ -1123,7 +1123,9 @@ impl<W: LayoutElement> Workspace<W> {
removed.tile.stop_move_animations();
// Come up with a default floating position close to the tile position.
if removed.tile.floating_pos.is_none() {
if removed.tile.floating_pos.is_none()
&& removed.tile.default_floating_logical_pos().is_none()
{
let offset = if self.options.center_focused_column == CenterFocusedColumn::Always {
Point::from((0., 0.))
} else {
@@ -1214,17 +1216,26 @@ impl<W: LayoutElement> Workspace<W> {
};
let working_area_loc = self.floating.working_area().loc;
let pos = tile
.floating_pos
.map(|pos| self.floating.scale_by_working_area(pos));
let pos = pos.or_else(|| {
tile.default_floating_logical_pos()
.map(|pos| pos + working_area_loc)
});
// If there's no stored floating position, we can only set both components at once, not
// adjust.
let Some(pos) = tile.floating_pos.or_else(|| {
let pos = pos.or_else(|| {
(matches!(x, PositionChange::SetFixed(_))
&& matches!(y, PositionChange::SetFixed(_)))
.then_some(Point::default())
}) else {
});
let Some(mut pos) = pos else {
return;
};
let mut pos = self.floating.scale_by_working_area(pos);
match x {
PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x,
PositionChange::AdjustFixed(x) => pos.x += x,
+11 -1
View File
@@ -1,6 +1,8 @@
use std::cmp::{max, min};
use niri_config::{BlockOutFrom, BorderRule, CornerRadius, Match, PresetSize, WindowRule};
use niri_config::{
BlockOutFrom, BorderRule, CornerRadius, FoIPosition, Match, PresetSize, WindowRule,
};
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
use smithay::utils::{Logical, Size};
use smithay::wayland::compositor::with_states;
@@ -41,6 +43,9 @@ pub struct ResolvedWindowRules {
/// - `Some(Some(width))`: set to a particular height.
pub default_height: Option<Option<PresetSize>>,
/// Default floating position for this window.
pub default_floating_position: Option<FoIPosition>,
/// Output to open this window on.
pub open_on_output: Option<String>,
@@ -137,6 +142,7 @@ impl ResolvedWindowRules {
Self {
default_width: None,
default_height: None,
default_floating_position: None,
open_on_output: None,
open_on_workspace: None,
open_maximized: None,
@@ -219,6 +225,10 @@ impl ResolvedWindowRules {
resolved.default_height = Some(x.0);
}
if let Some(x) = rule.default_floating_position {
resolved.default_floating_position = Some(x);
}
if let Some(x) = rule.open_on_output.as_deref() {
open_on_output = Some(x);
}
+24
View File
@@ -52,6 +52,7 @@ window-rule {
block-out-from "screencast"
// block-out-from "screen-capture"
variable-refresh-rate true
default-floating-position x=100 y=200
focus-ring {
// off
@@ -507,6 +508,29 @@ window-rule {
}
```
#### `default-floating-position`
<sup>Since: next release</sup>
Set the initial position for this window when it opens on, or moves to the floating layout.
Afterward, the window will remember its last floating position.
By default, new floating windows open at the center of the screen, and windows from the tiling layout open close to their visual screen position.
The position uses logical coordinates relative to the working area.
For example, if you have a bar at the top, then `x=0 y=0` will put the top-left corner of the window directly below the bar.
```kdl
// Open the Firefox picture-in-picture window at the top-left corner of the screen
// with a small gap.
window-rule {
match app-id="firefox$" title="^Picture-in-Picture$"
default-floating-position x=32 y=32
}
```
#### `draw-border-with-background`
Override whether the border and the focus ring draw with a background.