mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Add default-floating-position window rule
This commit is contained in:
@@ -1019,6 +1019,8 @@ pub struct WindowRule {
|
|||||||
pub block_out_from: Option<BlockOutFrom>,
|
pub block_out_from: Option<BlockOutFrom>,
|
||||||
#[knuffel(child, unwrap(argument))]
|
#[knuffel(child, unwrap(argument))]
|
||||||
pub variable_refresh_rate: Option<bool>,
|
pub variable_refresh_rate: Option<bool>,
|
||||||
|
#[knuffel(child)]
|
||||||
|
pub default_floating_position: Option<FoIPosition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
||||||
@@ -1082,6 +1084,14 @@ pub struct BorderRule {
|
|||||||
pub inactive_gradient: Option<Gradient>,
|
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)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub struct Binds(pub Vec<Bind>);
|
pub struct Binds(pub Vec<Bind>);
|
||||||
|
|
||||||
@@ -3192,6 +3202,7 @@ mod tests {
|
|||||||
open-floating false
|
open-floating false
|
||||||
open-focused true
|
open-focused true
|
||||||
default-window-height { fixed 500; }
|
default-window-height { fixed 500; }
|
||||||
|
default-floating-position x=100 y=-200
|
||||||
|
|
||||||
focus-ring {
|
focus-ring {
|
||||||
off
|
off
|
||||||
@@ -3476,6 +3487,10 @@ mod tests {
|
|||||||
open_floating: Some(false),
|
open_floating: Some(false),
|
||||||
open_focused: Some(true),
|
open_focused: Some(true),
|
||||||
default_window_height: Some(DefaultPresetSize(Some(PresetSize::Fixed(500)))),
|
default_window_height: Some(DefaultPresetSize(Some(PresetSize::Fixed(500)))),
|
||||||
|
default_floating_position: Some(FoIPosition {
|
||||||
|
x: FloatOrInt(100.),
|
||||||
|
y: FloatOrInt(-200.),
|
||||||
|
}),
|
||||||
focus_ring: BorderRule {
|
focus_ring: BorderRule {
|
||||||
off: true,
|
off: true,
|
||||||
width: Some(FloatOrInt(3.)),
|
width: Some(FloatOrInt(3.)),
|
||||||
|
|||||||
@@ -415,12 +415,14 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = tile
|
let pos = tile.floating_pos.map(|pos| self.scale_by_working_area(pos));
|
||||||
.floating_pos
|
let pos = pos.or_else(|| {
|
||||||
.map(|pos| self.scale_by_working_area(pos))
|
tile.default_floating_logical_pos()
|
||||||
.unwrap_or_else(|| {
|
.map(|pos| pos + self.working_area.loc)
|
||||||
center_preferring_top_left_in_area(self.working_area, tile.tile_size())
|
});
|
||||||
});
|
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);
|
let data = Data::new(self.working_area, &tile, pos);
|
||||||
self.data.insert(idx, data);
|
self.data.insert(idx, data);
|
||||||
|
|||||||
@@ -999,6 +999,11 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
&self.options
|
&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)]
|
#[cfg(test)]
|
||||||
pub fn view_size(&self) -> Size<f64, Logical> {
|
pub fn view_size(&self) -> Size<f64, Logical> {
|
||||||
self.view_size
|
self.view_size
|
||||||
|
|||||||
+15
-4
@@ -1123,7 +1123,9 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
removed.tile.stop_move_animations();
|
removed.tile.stop_move_animations();
|
||||||
|
|
||||||
// Come up with a default floating position close to the tile position.
|
// 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 {
|
let offset = if self.options.center_focused_column == CenterFocusedColumn::Always {
|
||||||
Point::from((0., 0.))
|
Point::from((0., 0.))
|
||||||
} else {
|
} else {
|
||||||
@@ -1214,17 +1216,26 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let working_area_loc = self.floating.working_area().loc;
|
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
|
// If there's no stored floating position, we can only set both components at once, not
|
||||||
// adjust.
|
// adjust.
|
||||||
let Some(pos) = tile.floating_pos.or_else(|| {
|
let pos = pos.or_else(|| {
|
||||||
(matches!(x, PositionChange::SetFixed(_))
|
(matches!(x, PositionChange::SetFixed(_))
|
||||||
&& matches!(y, PositionChange::SetFixed(_)))
|
&& matches!(y, PositionChange::SetFixed(_)))
|
||||||
.then_some(Point::default())
|
.then_some(Point::default())
|
||||||
}) else {
|
});
|
||||||
|
|
||||||
|
let Some(mut pos) = pos else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut pos = self.floating.scale_by_working_area(pos);
|
|
||||||
match x {
|
match x {
|
||||||
PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x,
|
PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x,
|
||||||
PositionChange::AdjustFixed(x) => pos.x += x,
|
PositionChange::AdjustFixed(x) => pos.x += x,
|
||||||
|
|||||||
+11
-1
@@ -1,6 +1,8 @@
|
|||||||
use std::cmp::{max, min};
|
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::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
||||||
use smithay::utils::{Logical, Size};
|
use smithay::utils::{Logical, Size};
|
||||||
use smithay::wayland::compositor::with_states;
|
use smithay::wayland::compositor::with_states;
|
||||||
@@ -41,6 +43,9 @@ pub struct ResolvedWindowRules {
|
|||||||
/// - `Some(Some(width))`: set to a particular height.
|
/// - `Some(Some(width))`: set to a particular height.
|
||||||
pub default_height: Option<Option<PresetSize>>,
|
pub default_height: Option<Option<PresetSize>>,
|
||||||
|
|
||||||
|
/// Default floating position for this window.
|
||||||
|
pub default_floating_position: Option<FoIPosition>,
|
||||||
|
|
||||||
/// Output to open this window on.
|
/// Output to open this window on.
|
||||||
pub open_on_output: Option<String>,
|
pub open_on_output: Option<String>,
|
||||||
|
|
||||||
@@ -137,6 +142,7 @@ impl ResolvedWindowRules {
|
|||||||
Self {
|
Self {
|
||||||
default_width: None,
|
default_width: None,
|
||||||
default_height: None,
|
default_height: None,
|
||||||
|
default_floating_position: None,
|
||||||
open_on_output: None,
|
open_on_output: None,
|
||||||
open_on_workspace: None,
|
open_on_workspace: None,
|
||||||
open_maximized: None,
|
open_maximized: None,
|
||||||
@@ -219,6 +225,10 @@ impl ResolvedWindowRules {
|
|||||||
resolved.default_height = Some(x.0);
|
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() {
|
if let Some(x) = rule.open_on_output.as_deref() {
|
||||||
open_on_output = Some(x);
|
open_on_output = Some(x);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ window-rule {
|
|||||||
block-out-from "screencast"
|
block-out-from "screencast"
|
||||||
// block-out-from "screen-capture"
|
// block-out-from "screen-capture"
|
||||||
variable-refresh-rate true
|
variable-refresh-rate true
|
||||||
|
default-floating-position x=100 y=200
|
||||||
|
|
||||||
focus-ring {
|
focus-ring {
|
||||||
// off
|
// 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`
|
#### `draw-border-with-background`
|
||||||
|
|
||||||
Override whether the border and the focus ring draw with a background.
|
Override whether the border and the focus ring draw with a background.
|
||||||
|
|||||||
Reference in New Issue
Block a user