mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
Take border into account for fixed preset-column-width for tiled windows
This commit is contained in:
@@ -2,10 +2,9 @@ use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri::animation::Clock;
|
||||
use niri::layout::scrolling::ColumnWidth;
|
||||
use niri::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _, Options};
|
||||
use niri::render_helpers::RenderTarget;
|
||||
use niri_config::{Color, FloatOrInt, OutputName};
|
||||
use niri_config::{Color, FloatOrInt, OutputName, PresetSize};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::desktop::layer_map_for_output;
|
||||
@@ -84,13 +83,13 @@ impl Layout {
|
||||
pub fn open_in_between(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.3)));
|
||||
rv.layout.activate_window(&0);
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.add_window(win.clone(), Some(PresetSize::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(win.id());
|
||||
});
|
||||
|
||||
@@ -103,7 +102,7 @@ impl Layout {
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
let win = TestWindow::freeform(delay as usize);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.add_window(win.clone(), Some(PresetSize::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(win.id());
|
||||
});
|
||||
}
|
||||
@@ -117,7 +116,7 @@ impl Layout {
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
let win = TestWindow::freeform(delay as usize);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.5)));
|
||||
l.add_window(win.clone(), Some(PresetSize::Proportion(0.5)));
|
||||
l.layout.start_open_animation_for_window(win.id());
|
||||
});
|
||||
}
|
||||
@@ -128,13 +127,13 @@ impl Layout {
|
||||
pub fn open_to_the_left(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.3)));
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
let right_of = l.windows[0].clone();
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(PresetSize::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(win.id());
|
||||
});
|
||||
|
||||
@@ -144,20 +143,20 @@ impl Layout {
|
||||
pub fn open_to_the_left_big(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.8)));
|
||||
rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.8)));
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
let right_of = l.windows[0].clone();
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(ColumnWidth::Proportion(0.5)));
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(PresetSize::Proportion(0.5)));
|
||||
l.layout.start_open_animation_for_window(win.id());
|
||||
});
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
fn add_window(&mut self, mut window: TestWindow, width: Option<ColumnWidth>) {
|
||||
fn add_window(&mut self, mut window: TestWindow, width: Option<PresetSize>) {
|
||||
let ws = self.layout.active_workspace().unwrap();
|
||||
let min_size = window.min_size();
|
||||
let max_size = window.max_size();
|
||||
@@ -184,7 +183,7 @@ impl Layout {
|
||||
&mut self,
|
||||
right_of: &TestWindow,
|
||||
mut window: TestWindow,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
) {
|
||||
let ws = self.layout.active_workspace().unwrap();
|
||||
let min_size = window.min_size();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::cell::Cell;
|
||||
|
||||
use calloop::Interest;
|
||||
use niri_config::PresetSize;
|
||||
use smithay::desktop::{
|
||||
find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, utils, LayerSurface,
|
||||
PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy, Window,
|
||||
@@ -42,7 +43,6 @@ use crate::input::resize_grab::ResizeGrab;
|
||||
use crate::input::touch_move_grab::TouchMoveGrab;
|
||||
use crate::input::touch_resize_grab::TouchResizeGrab;
|
||||
use crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME};
|
||||
use crate::layout::scrolling::ColumnWidth;
|
||||
use crate::niri::{PopupGrabState, State};
|
||||
use crate::utils::transaction::Transaction;
|
||||
use crate::utils::{get_monotonic_time, output_matches_name, send_scale_transform, ResizeEdge};
|
||||
@@ -591,7 +591,7 @@ impl XdgShellHandler for State {
|
||||
let configure_width = if is_floating {
|
||||
*floating_width
|
||||
} else if *is_full_width {
|
||||
Some(ColumnWidth::Proportion(1.))
|
||||
Some(PresetSize::Proportion(1.))
|
||||
} else {
|
||||
*width
|
||||
};
|
||||
@@ -917,7 +917,7 @@ impl State {
|
||||
let configure_width = if is_floating {
|
||||
floating_width
|
||||
} else if is_full_width {
|
||||
Some(ColumnWidth::Proportion(1.))
|
||||
Some(PresetSize::Proportion(1.))
|
||||
} else {
|
||||
width
|
||||
};
|
||||
|
||||
+19
-44
@@ -623,7 +623,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
||||
.preset_column_widths
|
||||
.iter()
|
||||
.position(|preset| {
|
||||
let resolved = preset.resolve_no_gaps(&self.options, available_size);
|
||||
let resolved = resolve_preset_size(*preset, available_size);
|
||||
match resolved {
|
||||
// Some allowance for fractional scaling purposes.
|
||||
ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
|
||||
@@ -634,13 +634,7 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
||||
};
|
||||
|
||||
let preset = self.options.preset_column_widths[preset_idx];
|
||||
let change = match preset {
|
||||
ColumnWidth::Proportion(prop) => SizeChange::SetProportion(prop * 100.),
|
||||
ColumnWidth::Fixed(fixed) => SizeChange::SetFixed(fixed.round() as i32),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.set_window_width(Some(&id), change, true);
|
||||
self.set_window_width(Some(&id), SizeChange::from(preset), true);
|
||||
|
||||
self.tiles[idx].floating_preset_width_idx = Some(preset_idx);
|
||||
|
||||
@@ -1138,53 +1132,34 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_width(&self, width: ColumnWidth) -> ResolvedSize {
|
||||
width.resolve_no_gaps(&self.options, self.working_area.size.w)
|
||||
}
|
||||
|
||||
pub fn resolve_height(&self, height: PresetSize) -> ResolvedSize {
|
||||
resolve_preset_size(height, self.working_area.size.h)
|
||||
}
|
||||
|
||||
pub fn new_window_size(
|
||||
&self,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
height: Option<PresetSize>,
|
||||
rules: &ResolvedWindowRules,
|
||||
) -> Size<i32, Logical> {
|
||||
let border = rules.border.resolve_against(self.options.border);
|
||||
|
||||
let width = if let Some(width) = width {
|
||||
let width = match self.resolve_width(width) {
|
||||
ResolvedSize::Tile(mut size) => {
|
||||
if !border.off {
|
||||
size -= border.width.0 * 2.;
|
||||
let resolve = |size: Option<PresetSize>, working_area_size: f64| {
|
||||
if let Some(size) = size {
|
||||
let size = match resolve_preset_size(size, working_area_size) {
|
||||
ResolvedSize::Tile(mut size) => {
|
||||
if !border.off {
|
||||
size -= border.width.0 * 2.;
|
||||
}
|
||||
size
|
||||
}
|
||||
size
|
||||
}
|
||||
ResolvedSize::Window(size) => size,
|
||||
};
|
||||
ResolvedSize::Window(size) => size,
|
||||
};
|
||||
|
||||
max(1, width.floor() as i32)
|
||||
} else {
|
||||
0
|
||||
max(1, size.floor() as i32)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let height = if let Some(height) = height {
|
||||
let height = match self.resolve_height(height) {
|
||||
ResolvedSize::Tile(mut size) => {
|
||||
if !border.off {
|
||||
size -= border.width.0 * 2.;
|
||||
}
|
||||
size
|
||||
}
|
||||
ResolvedSize::Window(size) => size,
|
||||
};
|
||||
|
||||
max(1, height.floor() as i32)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let width = resolve(width, self.working_area.size.w);
|
||||
let height = resolve(height, self.working_area.size.h);
|
||||
|
||||
Size::from((width, height))
|
||||
}
|
||||
|
||||
+31
-41
@@ -316,10 +316,10 @@ pub struct Options {
|
||||
pub center_focused_column: CenterFocusedColumn,
|
||||
pub always_center_single_column: bool,
|
||||
pub empty_workspace_above_first: bool,
|
||||
/// Column widths that `toggle_width()` switches between.
|
||||
pub preset_column_widths: Vec<ColumnWidth>,
|
||||
/// Column or window widths that `toggle_width()` switches between.
|
||||
pub preset_column_widths: Vec<PresetSize>,
|
||||
/// Initial width for new columns.
|
||||
pub default_column_width: Option<ColumnWidth>,
|
||||
pub default_column_width: Option<PresetSize>,
|
||||
/// Window height that `toggle_window_height()` switches between.
|
||||
pub preset_window_heights: Vec<PresetSize>,
|
||||
pub animations: niri_config::Animations,
|
||||
@@ -341,9 +341,9 @@ impl Default for Options {
|
||||
always_center_single_column: false,
|
||||
empty_workspace_above_first: false,
|
||||
preset_column_widths: vec![
|
||||
ColumnWidth::Proportion(1. / 3.),
|
||||
ColumnWidth::Proportion(0.5),
|
||||
ColumnWidth::Proportion(2. / 3.),
|
||||
PresetSize::Proportion(1. / 3.),
|
||||
PresetSize::Proportion(0.5),
|
||||
PresetSize::Proportion(2. / 3.),
|
||||
],
|
||||
default_column_width: None,
|
||||
animations: Default::default(),
|
||||
@@ -492,12 +492,7 @@ impl Options {
|
||||
let preset_column_widths = if layout.preset_column_widths.is_empty() {
|
||||
Options::default().preset_column_widths
|
||||
} else {
|
||||
layout
|
||||
.preset_column_widths
|
||||
.iter()
|
||||
.copied()
|
||||
.map(ColumnWidth::from)
|
||||
.collect()
|
||||
layout.preset_column_widths.clone()
|
||||
};
|
||||
let preset_window_heights = if layout.preset_window_heights.is_empty() {
|
||||
Options::default().preset_window_heights
|
||||
@@ -505,13 +500,13 @@ impl Options {
|
||||
layout.preset_window_heights.clone()
|
||||
};
|
||||
|
||||
// Missing default_column_width maps to Some(ColumnWidth::Proportion(0.5)),
|
||||
// Missing default_column_width maps to Some(PresetSize::Proportion(0.5)),
|
||||
// while present, but empty, maps to None.
|
||||
let default_column_width = layout
|
||||
.default_column_width
|
||||
.as_ref()
|
||||
.map(|w| w.0.map(ColumnWidth::from))
|
||||
.unwrap_or(Some(ColumnWidth::Proportion(0.5)));
|
||||
.map(|w| w.0)
|
||||
.unwrap_or(Some(PresetSize::Proportion(0.5)));
|
||||
|
||||
Self {
|
||||
gaps: layout.gaps.0,
|
||||
@@ -855,14 +850,14 @@ impl<W: LayoutElement> Layout<W> {
|
||||
&mut self,
|
||||
window: W,
|
||||
target: AddWindowTarget<W>,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
height: Option<PresetSize>,
|
||||
is_full_width: bool,
|
||||
is_floating: bool,
|
||||
activate: ActivateWindow,
|
||||
) -> Option<&Output> {
|
||||
let resolved_width = self.resolve_default_width(&window, width, is_floating);
|
||||
let resolved_height = height.map(SizeChange::from);
|
||||
let scrolling_width = self.resolve_scrolling_width(&window, width);
|
||||
let scrolling_height = height.map(SizeChange::from);
|
||||
let id = window.id().clone();
|
||||
|
||||
match &mut self.monitor_set {
|
||||
@@ -933,7 +928,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
window,
|
||||
target,
|
||||
activate,
|
||||
resolved_width,
|
||||
scrolling_width,
|
||||
is_full_width,
|
||||
is_floating,
|
||||
);
|
||||
@@ -944,7 +939,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
|
||||
// Set the default height for scrolling windows.
|
||||
if !is_floating {
|
||||
if let Some(change) = resolved_height {
|
||||
if let Some(change) = scrolling_height {
|
||||
let ws = mon
|
||||
.workspaces
|
||||
.iter_mut()
|
||||
@@ -1005,14 +1000,14 @@ impl<W: LayoutElement> Layout<W> {
|
||||
tile,
|
||||
target,
|
||||
activate,
|
||||
resolved_width,
|
||||
scrolling_width,
|
||||
is_full_width,
|
||||
is_floating,
|
||||
);
|
||||
|
||||
// Set the default height for scrolling windows.
|
||||
if !is_floating {
|
||||
if let Some(change) = resolved_height {
|
||||
if let Some(change) = scrolling_height {
|
||||
ws.set_window_height(Some(&id), change);
|
||||
}
|
||||
}
|
||||
@@ -4283,28 +4278,23 @@ impl<W: LayoutElement> Layout<W> {
|
||||
self.windows().any(|(_, win)| win.id() == window)
|
||||
}
|
||||
|
||||
fn resolve_default_width(
|
||||
&self,
|
||||
window: &W,
|
||||
width: Option<ColumnWidth>,
|
||||
is_floating: bool,
|
||||
) -> ColumnWidth {
|
||||
let mut width = width.unwrap_or_else(|| ColumnWidth::Fixed(f64::from(window.size().w)));
|
||||
if is_floating {
|
||||
return width;
|
||||
}
|
||||
fn resolve_scrolling_width(&self, window: &W, width: Option<PresetSize>) -> ColumnWidth {
|
||||
let width = width.unwrap_or_else(|| PresetSize::Fixed(window.size().w));
|
||||
match width {
|
||||
PresetSize::Fixed(fixed) => {
|
||||
let mut fixed = f64::from(fixed);
|
||||
|
||||
// Add border width to account for the issue that the scrolling layout currently doesn't
|
||||
// take borders into account for fixed sizes.
|
||||
if let ColumnWidth::Fixed(w) = &mut width {
|
||||
let rules = window.rules();
|
||||
let border_config = rules.border.resolve_against(self.options.border);
|
||||
if !border_config.off {
|
||||
*w += border_config.width.0 * 2.;
|
||||
// Add border width since ColumnWidth includes borders.
|
||||
let rules = window.rules();
|
||||
let border = rules.border.resolve_against(self.options.border);
|
||||
if !border.off {
|
||||
fixed += border.width.0 * 2.;
|
||||
}
|
||||
|
||||
ColumnWidth::Fixed(fixed)
|
||||
}
|
||||
PresetSize::Proportion(prop) => ColumnWidth::Proportion(prop),
|
||||
}
|
||||
|
||||
width
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+69
-70
@@ -162,6 +162,9 @@ pub struct Column<W: LayoutElement> {
|
||||
/// upon unfullscreening and untoggling full-width.
|
||||
width: ColumnWidth,
|
||||
|
||||
/// Currently selected preset width index.
|
||||
preset_width_idx: Option<usize>,
|
||||
|
||||
/// Whether this column is full-width.
|
||||
is_full_width: bool,
|
||||
|
||||
@@ -209,8 +212,6 @@ pub enum ColumnWidth {
|
||||
Proportion(f64),
|
||||
/// Fixed width in logical pixels.
|
||||
Fixed(f64),
|
||||
/// One of the preset widths.
|
||||
Preset(usize),
|
||||
}
|
||||
|
||||
/// Height of a window in a column.
|
||||
@@ -417,22 +418,24 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
||||
|
||||
pub fn new_window_size(
|
||||
&self,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
height: Option<PresetSize>,
|
||||
rules: &ResolvedWindowRules,
|
||||
) -> Size<i32, Logical> {
|
||||
let border = rules.border.resolve_against(self.options.border);
|
||||
|
||||
let width = if let Some(width) = width {
|
||||
let is_fixed = matches!(width, ColumnWidth::Fixed(_));
|
||||
let width = if let Some(size) = width {
|
||||
let size = match resolve_preset_size(size, &self.options, self.working_area.size.w) {
|
||||
ResolvedSize::Tile(mut size) => {
|
||||
if !border.off {
|
||||
size -= border.width.0 * 2.;
|
||||
}
|
||||
size
|
||||
}
|
||||
ResolvedSize::Window(size) => size,
|
||||
};
|
||||
|
||||
let mut width = width.resolve(&self.options, self.working_area.size.w);
|
||||
|
||||
if !is_fixed && !border.off {
|
||||
width -= border.width.0 * 2.;
|
||||
}
|
||||
|
||||
max(1, width.floor() as i32)
|
||||
max(1, size.floor() as i32)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -2173,7 +2176,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
||||
}
|
||||
|
||||
let col = &mut self.columns[self.active_column_idx];
|
||||
col.toggle_width();
|
||||
col.toggle_width(None);
|
||||
|
||||
cancel_resize_for_column(&mut self.interactive_resize, col);
|
||||
}
|
||||
@@ -2266,18 +2269,21 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
||||
return;
|
||||
}
|
||||
|
||||
let col = if let Some(window) = window {
|
||||
let (col, tile_idx) = if let Some(window) = window {
|
||||
self.columns
|
||||
.iter_mut()
|
||||
.find(|col| col.contains(window))
|
||||
.find_map(|col| {
|
||||
col.tiles
|
||||
.iter()
|
||||
.position(|tile| tile.window().id() == window)
|
||||
.map(|tile_idx| (col, Some(tile_idx)))
|
||||
})
|
||||
.unwrap()
|
||||
} else {
|
||||
&mut self.columns[self.active_column_idx]
|
||||
(&mut self.columns[self.active_column_idx], None)
|
||||
};
|
||||
|
||||
// FIXME: when we fix preset fixed width to be per-window, this should also be made
|
||||
// window-specific to resolve the correct window width.
|
||||
col.toggle_width();
|
||||
col.toggle_width(tile_idx);
|
||||
|
||||
cancel_resize_for_column(&mut self.interactive_resize, col);
|
||||
}
|
||||
@@ -3103,22 +3109,9 @@ impl ColumnWidth {
|
||||
ColumnWidth::Proportion(proportion) => {
|
||||
(view_width - options.gaps) * proportion - options.gaps
|
||||
}
|
||||
ColumnWidth::Preset(idx) => {
|
||||
options.preset_column_widths[idx].resolve(options, view_width)
|
||||
}
|
||||
ColumnWidth::Fixed(width) => width,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_no_gaps(self, options: &Options, view_width: f64) -> ResolvedSize {
|
||||
match self {
|
||||
ColumnWidth::Proportion(proportion) => ResolvedSize::Tile(view_width * proportion),
|
||||
ColumnWidth::Preset(idx) => {
|
||||
options.preset_column_widths[idx].resolve_no_gaps(options, view_width)
|
||||
}
|
||||
ColumnWidth::Fixed(width) => ResolvedSize::Window(width),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PresetSize> for ColumnWidth {
|
||||
@@ -3152,6 +3145,7 @@ impl<W: LayoutElement> Column<W> {
|
||||
data: vec![],
|
||||
active_tile_idx: 0,
|
||||
width,
|
||||
preset_width_idx: None,
|
||||
is_full_width,
|
||||
is_fullscreen: false,
|
||||
move_animation: None,
|
||||
@@ -3186,11 +3180,9 @@ impl<W: LayoutElement> Column<W> {
|
||||
update_sizes = true;
|
||||
}
|
||||
|
||||
// If preset widths changed, make our width non-preset.
|
||||
// If preset widths changed, clear our stored preset index.
|
||||
if self.options.preset_column_widths != options.preset_column_widths {
|
||||
if let ColumnWidth::Preset(idx) = self.width {
|
||||
self.width = self.options.preset_column_widths[idx];
|
||||
}
|
||||
self.preset_width_idx = None;
|
||||
}
|
||||
|
||||
// If preset heights changed, make our heights non-preset.
|
||||
@@ -3224,12 +3216,6 @@ impl<W: LayoutElement> Column<W> {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_width(&mut self, width: ColumnWidth, animate: bool) {
|
||||
self.width = width;
|
||||
self.is_full_width = false;
|
||||
self.update_tile_sizes(animate);
|
||||
}
|
||||
|
||||
pub fn advance_animations(&mut self) {
|
||||
if let Some(anim) = &mut self.move_animation {
|
||||
if anim.is_done() {
|
||||
@@ -3673,30 +3659,42 @@ impl<W: LayoutElement> Column<W> {
|
||||
true
|
||||
}
|
||||
|
||||
fn toggle_width(&mut self) {
|
||||
let width = if self.is_full_width {
|
||||
ColumnWidth::Proportion(1.)
|
||||
fn toggle_width(&mut self, tile_idx: Option<usize>) {
|
||||
let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);
|
||||
|
||||
let preset_idx = if self.is_full_width {
|
||||
None
|
||||
} else {
|
||||
self.width
|
||||
self.preset_width_idx
|
||||
};
|
||||
|
||||
let idx = match width {
|
||||
ColumnWidth::Preset(idx) => (idx + 1) % self.options.preset_column_widths.len(),
|
||||
_ => {
|
||||
let current = self.width();
|
||||
self.options
|
||||
.preset_column_widths
|
||||
.iter()
|
||||
.position(|prop| {
|
||||
let resolved = prop.resolve(&self.options, self.working_area.size.w);
|
||||
let preset_idx = if let Some(idx) = preset_idx {
|
||||
(idx + 1) % self.options.preset_column_widths.len()
|
||||
} else {
|
||||
let tile = &self.tiles[tile_idx];
|
||||
let current_window = tile.window_expected_or_current_size().w;
|
||||
let current_tile = tile.tile_expected_or_current_size().w;
|
||||
|
||||
let available_size = self.working_area.size.w;
|
||||
|
||||
self.options
|
||||
.preset_column_widths
|
||||
.iter()
|
||||
.position(|prop| {
|
||||
let resolved = resolve_preset_size(*prop, &self.options, available_size);
|
||||
match resolved {
|
||||
// Some allowance for fractional scaling purposes.
|
||||
current + 1. < resolved
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
|
||||
ResolvedSize::Window(resolved) => current_window + 1. < resolved,
|
||||
}
|
||||
})
|
||||
.unwrap_or(0)
|
||||
};
|
||||
let width = ColumnWidth::Preset(idx);
|
||||
self.set_width(width, true);
|
||||
|
||||
let preset = self.options.preset_column_widths[preset_idx];
|
||||
self.set_column_width(SizeChange::from(preset), Some(tile_idx), true);
|
||||
|
||||
self.preset_width_idx = Some(preset_idx);
|
||||
}
|
||||
|
||||
fn toggle_full_width(&mut self) {
|
||||
@@ -3705,18 +3703,13 @@ impl<W: LayoutElement> Column<W> {
|
||||
}
|
||||
|
||||
fn set_column_width(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {
|
||||
let width = if self.is_full_width {
|
||||
let current = if self.is_full_width {
|
||||
ColumnWidth::Proportion(1.)
|
||||
} else {
|
||||
self.width
|
||||
};
|
||||
|
||||
let current_px = width.resolve(&self.options, self.working_area.size.w);
|
||||
|
||||
let current = match width {
|
||||
ColumnWidth::Preset(idx) => self.options.preset_column_widths[idx],
|
||||
current => current,
|
||||
};
|
||||
let current_px = current.resolve(&self.options, self.working_area.size.w);
|
||||
|
||||
// FIXME: fix overflows then remove limits.
|
||||
const MAX_PX: f64 = 100000.;
|
||||
@@ -3755,10 +3748,12 @@ impl<W: LayoutElement> Column<W> {
|
||||
let proportion = (current + delta / 100.).clamp(0., MAX_F);
|
||||
ColumnWidth::Proportion(proportion)
|
||||
}
|
||||
(ColumnWidth::Preset(_), _) => unreachable!(),
|
||||
};
|
||||
|
||||
self.set_width(width, animate);
|
||||
self.width = width;
|
||||
self.preset_width_idx = None;
|
||||
self.is_full_width = false;
|
||||
self.update_tile_sizes(animate);
|
||||
}
|
||||
|
||||
fn set_window_height(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {
|
||||
@@ -4043,6 +4038,10 @@ impl<W: LayoutElement> Column<W> {
|
||||
assert_eq!(self.tiles.len(), 1);
|
||||
}
|
||||
|
||||
if let Some(idx) = self.preset_width_idx {
|
||||
assert!(idx < self.options.preset_column_widths.len());
|
||||
}
|
||||
|
||||
let tile_count = self.tiles.len();
|
||||
if tile_count == 1 {
|
||||
if let WindowHeight::Auto { weight } = self.data[0].height {
|
||||
|
||||
@@ -3002,6 +3002,71 @@ fn move_workspace_to_same_monitor_doesnt_reorder() {
|
||||
assert_eq!(counts, &[1, 2, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_column_width_fixed_correct_with_border() {
|
||||
let ops = [
|
||||
Op::AddOutput(0),
|
||||
Op::AddWindow {
|
||||
params: TestWindowParams::new(0),
|
||||
},
|
||||
Op::SwitchPresetColumnWidth,
|
||||
];
|
||||
|
||||
let options = Options {
|
||||
preset_column_widths: vec![PresetSize::Fixed(500)],
|
||||
..Default::default()
|
||||
};
|
||||
let mut layout = check_ops_with_options(options, &ops);
|
||||
|
||||
let win = layout.windows().next().unwrap().1;
|
||||
assert_eq!(win.requested_size().unwrap().w, 500);
|
||||
|
||||
// Add border.
|
||||
let options = Options {
|
||||
preset_column_widths: vec![PresetSize::Fixed(500)],
|
||||
border: niri_config::Border {
|
||||
off: false,
|
||||
width: FloatOrInt(5.),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
layout.update_options(options);
|
||||
|
||||
// With border, the window gets less size.
|
||||
let win = layout.windows().next().unwrap().1;
|
||||
assert_eq!(win.requested_size().unwrap().w, 490);
|
||||
|
||||
// However, preset fixed width will still work correctly.
|
||||
layout.toggle_width();
|
||||
let win = layout.windows().next().unwrap().1;
|
||||
assert_eq!(win.requested_size().unwrap().w, 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_column_width_reset_after_set_width() {
|
||||
let ops = [
|
||||
Op::AddOutput(0),
|
||||
Op::AddWindow {
|
||||
params: TestWindowParams::new(0),
|
||||
},
|
||||
Op::SwitchPresetColumnWidth,
|
||||
Op::SetWindowWidth {
|
||||
id: None,
|
||||
change: SizeChange::AdjustFixed(-10),
|
||||
},
|
||||
Op::SwitchPresetColumnWidth,
|
||||
];
|
||||
|
||||
let options = Options {
|
||||
preset_column_widths: vec![PresetSize::Fixed(500), PresetSize::Fixed(1000)],
|
||||
..Default::default()
|
||||
};
|
||||
let layout = check_ops_with_options(options, &ops);
|
||||
let win = layout.windows().next().unwrap().1;
|
||||
assert_eq!(win.requested_size().unwrap().w, 500);
|
||||
}
|
||||
|
||||
fn parent_id_causes_loop(layout: &Layout<TestWindow>, id: usize, mut parent_id: usize) -> bool {
|
||||
if parent_id == id {
|
||||
return true;
|
||||
|
||||
@@ -713,9 +713,9 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
pub fn resolve_default_width(
|
||||
&self,
|
||||
default_width: Option<Option<ColumnWidth>>,
|
||||
default_width: Option<Option<PresetSize>>,
|
||||
is_floating: bool,
|
||||
) -> Option<ColumnWidth> {
|
||||
) -> Option<PresetSize> {
|
||||
match default_width {
|
||||
Some(Some(width)) => Some(width),
|
||||
Some(None) => None,
|
||||
@@ -740,7 +740,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
pub fn new_window_size(
|
||||
&self,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
height: Option<PresetSize>,
|
||||
is_floating: bool,
|
||||
rules: &ResolvedWindowRules,
|
||||
@@ -772,7 +772,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
pub fn configure_new_window(
|
||||
&self,
|
||||
window: &Window,
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
height: Option<PresetSize>,
|
||||
is_floating: bool,
|
||||
rules: &ResolvedWindowRules,
|
||||
|
||||
+3
-8
@@ -11,7 +11,6 @@ use smithay::wayland::shell::xdg::{
|
||||
SurfaceCachedState, ToplevelSurface, XdgToplevelSurfaceRoleAttributes,
|
||||
};
|
||||
|
||||
use crate::layout::scrolling::ColumnWidth;
|
||||
use crate::utils::with_toplevel_role;
|
||||
|
||||
pub mod mapped;
|
||||
@@ -35,7 +34,7 @@ pub struct ResolvedWindowRules {
|
||||
/// - `None`: unset (global default should be used).
|
||||
/// - `Some(None)`: set to empty (window picks its own width).
|
||||
/// - `Some(Some(width))`: set to a particular width.
|
||||
pub default_width: Option<Option<ColumnWidth>>,
|
||||
pub default_width: Option<Option<PresetSize>>,
|
||||
|
||||
/// Default height for this window.
|
||||
///
|
||||
@@ -230,12 +229,8 @@ impl ResolvedWindowRules {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(x) = rule
|
||||
.default_column_width
|
||||
.as_ref()
|
||||
.map(|d| d.0.map(ColumnWidth::from))
|
||||
{
|
||||
resolved.default_width = Some(x);
|
||||
if let Some(x) = rule.default_column_width {
|
||||
resolved.default_width = Some(x.0);
|
||||
}
|
||||
|
||||
if let Some(x) = rule.default_window_height {
|
||||
|
||||
@@ -5,7 +5,6 @@ use smithay::wayland::shell::xdg::ToplevelSurface;
|
||||
use smithay::wayland::xdg_activation::XdgActivationTokenData;
|
||||
|
||||
use super::ResolvedWindowRules;
|
||||
use crate::layout::scrolling::ColumnWidth;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Unmapped {
|
||||
@@ -34,7 +33,7 @@ pub enum InitialConfigureState {
|
||||
/// Resolved scrolling default width for this window.
|
||||
///
|
||||
/// `None` means that the window will pick its own width.
|
||||
width: Option<ColumnWidth>,
|
||||
width: Option<PresetSize>,
|
||||
|
||||
/// Resolved scrolling default height for this window.
|
||||
///
|
||||
@@ -44,7 +43,7 @@ pub enum InitialConfigureState {
|
||||
/// Resolved floating default width for this window.
|
||||
///
|
||||
/// `None` means that the window will pick its own width.
|
||||
floating_width: Option<ColumnWidth>,
|
||||
floating_width: Option<PresetSize>,
|
||||
|
||||
/// Resolved floating default height for this window.
|
||||
///
|
||||
|
||||
@@ -146,7 +146,7 @@ layout {
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Currently, due to an oversight, a preset `fixed` width does not take borders into account in the tiling layout.
|
||||
> Until next release, a preset `fixed` width does not take borders into account in the tiling layout.
|
||||
> I.e., preset `fixed 1000` with 4-wide borders will make the window 992 logical pixels wide.
|
||||
> This may eventually be corrected.
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user