Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating windows default proportional position #1168

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions niri-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,14 +1336,16 @@ pub struct TabIndicatorRule {
pub inactive_gradient: Option<Gradient>,
}

#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
pub struct FloatingPosition {
#[knuffel(property)]
pub x: FloatOrInt<-65535, 65535>,
#[knuffel(property)]
pub y: FloatOrInt<-65535, 65535>,
#[knuffel(property, default)]
pub relative_to: RelativeTo,
#[knuffel(child, unwrap(argument))]
pub x: Option<FloatOrInt<-65000, 65000>>,
#[knuffel(child, unwrap(argument))]
pub y: Option<FloatOrInt<-65000, 65000>>,
#[knuffel(child, unwrap(argument))]
pub relative_to: Option<RelativeTo>,
#[knuffel(child, unwrap(argument))]
pub mode: Option<PositionMode>,
}

#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]
Expand All @@ -1355,6 +1357,13 @@ pub enum RelativeTo {
BottomRight,
}

#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum PositionMode {
#[default]
Absolute,
Relative,
}

#[derive(Debug, Default, PartialEq)]
pub struct Binds(pub Vec<Bind>);

Expand Down Expand Up @@ -3727,7 +3736,12 @@ mod tests {
open-focused true
default-window-height { fixed 500; }
default-column-display "tabbed"
default-floating-position x=100 y=-200 relative-to="bottom-left"
default-floating-position {
x 100
y -200
relative-to "bottom-left"
mode "absolute"
}

focus-ring {
off
Expand All @@ -3742,6 +3756,7 @@ mod tests {
tab-indicator {
active-color "#f00"
}

}

layer-rule {
Expand Down
22 changes: 22 additions & 0 deletions resources/default-config.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,28 @@ window-rule {
clip-to-geometry true
}

// Example: a floating drop down terminal with proportional size and position
// (This example rule is commented out with a "/-" in front.)
/-window-rule {
match app-id="dropdown"
// open focused and floating
open-focused true
open-floating true
// no corner radius
geometry-corner-radius 0
// half screen height
default-window-height { proportion 0.500; }
// 80% screen width
default-column-width { proportion 0.8; }
//
default-floating-position {
relative-to "top-left"
mode "relative" // proportional position in order to match multi display setup
x 0.1 // 10% left of the screen
y 0
}
}

binds {
// Keys consist of modifiers separated by + signs, followed by an XKB key name
// in the end. To find an XKB name for a particular key, you may use a program
Expand Down
32 changes: 29 additions & 3 deletions src/layout/floating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::cmp::max;
use std::iter::zip;
use std::rc::Rc;

use niri_config::{PresetSize, RelativeTo};
use niri_config::{PositionMode, PresetSize, RelativeTo};
use niri_ipc::{PositionChange, SizeChange};
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};
Expand Down Expand Up @@ -1201,11 +1201,37 @@ impl<W: LayoutElement> FloatingSpace<W> {
let pos = tile.floating_pos.map(|pos| self.scale_by_working_area(pos));
pos.or_else(|| {
tile.window().rules().default_floating_position.map(|pos| {
let relative_to = pos.relative_to;
let mode = match pos.mode {
Some(p) => p,
None => PositionMode::Absolute,
};
let relative_to = match pos.relative_to {
Some(p) => p,
None => RelativeTo::TopLeft,
};
let size = tile.tile_size();
let area = self.working_area;

let mut pos = Point::from((pos.x.0, pos.y.0));
let x = match pos.x {
Some(p) => p.0,
None => 0.0,
};
let y = match pos.y {
Some(p) => p.0,
None => 0.0,
};

let transform = |p: f64, dim: f64, mode| -> f64 {
match mode {
PositionMode::Absolute => p,
PositionMode::Relative => p * dim,
}
};

let mut pos = Point::from((
transform(x, self.working_area.size.w, mode),
transform(y, self.working_area.size.h, mode),
));
if relative_to == RelativeTo::TopRight || relative_to == RelativeTo::BottomRight {
pos.x = area.size.w - size.w - pos.x;
}
Expand Down
22 changes: 16 additions & 6 deletions wiki/Configuration:-Window-Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ window-rule {
// block-out-from "screen-capture"
variable-refresh-rate true
default-column-display "tabbed"
default-floating-position x=100 y=200 relative-to="bottom-left"
default-floating-position {
mode "fixed" // fixed: size in px; proportional: relative size to the work area
relative-to "bottom-left"
x 100
y 200
}
scroll-factor 0.75

focus-ring {
Expand Down Expand Up @@ -609,22 +614,27 @@ 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.
The coordinates can be absolutes (default) or relatives to the size of the working area, by setting `mode` to one of these values: `absolute`, `relative`
By default, they are relative to the top-left corner of the working area, but you can change this by setting `relative-to` to one of these values: `top-left`, `top-right`, `bottom-left`, `bottom-right`.

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.
If instead you write `x=0 y=0 relative-to="top-right"`, then the top-right corner of the window will align with the top-right corner of the workspace, also directly below the bar.
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.
If instead you write `{ x 0; y 0; relative-to "top-right"; }`, then the top-right corner of the window will align with the top-right corner of the workspace, also directly below the bar.

The coordinates change direction based on `relative-to`.
For example, by default (top-left), `x=100 y=200` will put the window 100 pixels to the right and 200 pixels down from the top-left corner.
If you use `x=100 y=200 relative-to="bottom-left"`, it will put the window 100 pixels to the right and 200 pixels *up* from the bottom-left corner.
For example, by default (top-left), `{ x 100; y 200; }` will put the window 100 pixels to the right and 200 pixels down from the top-left corner.
If you use `{ x 100; y 200; relative-to "bottom-left"; }`, it will put the window 100 pixels to the right and 200 pixels *up* from the bottom-left corner.

If you want to use proportinal coordinates you can do it by setting `mode "relative"`.
For example `{ x 0.1; y 0.05; mode "relative"; }` will put the windows 10% of the working area width to the right and 5% of the working area height down from the top-left corner.
This is particularly useful if combined with proportional window size.

```kdl
// Open the Firefox picture-in-picture window at the bottom-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 relative-to="bottom-left"
default-floating-position { mode "absolute"; x 32; y 32; relative-to "bottom-left"; }
}
```

Expand Down