mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
floating: Implement toggle-width/height actions
This commit is contained in:
+103
-1
@@ -2,6 +2,7 @@ use std::cmp::max;
|
|||||||
use std::iter::zip;
|
use std::iter::zip;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use niri_config::PresetSize;
|
||||||
use niri_ipc::SizeChange;
|
use niri_ipc::SizeChange;
|
||||||
use smithay::backend::renderer::gles::GlesRenderer;
|
use smithay::backend::renderer::gles::GlesRenderer;
|
||||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};
|
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};
|
||||||
@@ -9,7 +10,7 @@ use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};
|
|||||||
use super::closing_window::{ClosingWindow, ClosingWindowRenderElement};
|
use super::closing_window::{ClosingWindow, ClosingWindowRenderElement};
|
||||||
use super::scrolling::ColumnWidth;
|
use super::scrolling::ColumnWidth;
|
||||||
use super::tile::{Tile, TileRenderElement, TileRenderSnapshot};
|
use super::tile::{Tile, TileRenderElement, TileRenderSnapshot};
|
||||||
use super::workspace::InteractiveResize;
|
use super::workspace::{InteractiveResize, ResolvedSize};
|
||||||
use super::{
|
use super::{
|
||||||
ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile, SizeFrac,
|
ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile, SizeFrac,
|
||||||
};
|
};
|
||||||
@@ -597,6 +598,89 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_window_width(&mut self, id: Option<&W::Id>) {
|
||||||
|
let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let idx = self.idx_of(&id).unwrap();
|
||||||
|
|
||||||
|
let view_size = self.working_area.size.w;
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[idx];
|
||||||
|
let preset_idx = if let Some(idx) = tile.floating_preset_width_idx {
|
||||||
|
(idx + 1) % self.options.preset_column_widths.len()
|
||||||
|
} else {
|
||||||
|
let current_window = tile.window_expected_or_current_size().w;
|
||||||
|
let current_tile = tile.tile_expected_or_current_size().w;
|
||||||
|
|
||||||
|
self.options
|
||||||
|
.preset_column_widths
|
||||||
|
.iter()
|
||||||
|
.position(|preset| {
|
||||||
|
let resolved = preset.resolve_no_gaps(&self.options, view_size);
|
||||||
|
match resolved {
|
||||||
|
// Some allowance for fractional scaling purposes.
|
||||||
|
ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
|
||||||
|
ResolvedSize::Window(resolved) => current_window + 1. < resolved,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let preset = self.options.preset_column_widths[preset_idx];
|
||||||
|
let window_width = match preset.resolve_no_gaps(&self.options, view_size) {
|
||||||
|
ResolvedSize::Tile(w) => tile.window_width_for_tile_width(w),
|
||||||
|
ResolvedSize::Window(w) => w,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_width = window_width.round().clamp(1., 100000.) as i32;
|
||||||
|
self.set_window_width(Some(&id), SizeChange::SetFixed(window_width), true);
|
||||||
|
|
||||||
|
self.tiles[idx].floating_preset_width_idx = Some(preset_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_window_height(&mut self, id: Option<&W::Id>) {
|
||||||
|
let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let idx = self.idx_of(&id).unwrap();
|
||||||
|
|
||||||
|
let view_size = self.working_area.size.h;
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[idx];
|
||||||
|
let preset_idx = if let Some(idx) = tile.floating_preset_height_idx {
|
||||||
|
(idx + 1) % self.options.preset_window_heights.len()
|
||||||
|
} else {
|
||||||
|
let current_window = tile.window_expected_or_current_size().h;
|
||||||
|
let current_tile = tile.tile_expected_or_current_size().h;
|
||||||
|
|
||||||
|
self.options
|
||||||
|
.preset_window_heights
|
||||||
|
.iter()
|
||||||
|
.position(|preset| {
|
||||||
|
let resolved = resolve_preset_size(*preset, view_size);
|
||||||
|
match resolved {
|
||||||
|
// Some allowance for fractional scaling purposes.
|
||||||
|
ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,
|
||||||
|
ResolvedSize::Window(resolved) => current_window + 1. < resolved,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let preset = self.options.preset_window_heights[preset_idx];
|
||||||
|
let window_height = match resolve_preset_size(preset, view_size) {
|
||||||
|
ResolvedSize::Tile(h) => tile.window_height_for_tile_height(h),
|
||||||
|
ResolvedSize::Window(h) => h,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_height = window_height.round().clamp(1., 100000.) as i32;
|
||||||
|
self.set_window_height(Some(&id), SizeChange::SetFixed(window_height), true);
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[idx];
|
||||||
|
tile.floating_preset_height_idx = Some(preset_idx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_window_width(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {
|
pub fn set_window_width(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {
|
||||||
let Some(id) = id.or(self.active_window_id.as_ref()) else {
|
let Some(id) = id.or(self.active_window_id.as_ref()) else {
|
||||||
return;
|
return;
|
||||||
@@ -609,6 +693,8 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let tile = &mut self.tiles[idx];
|
let tile = &mut self.tiles[idx];
|
||||||
|
tile.floating_preset_width_idx = None;
|
||||||
|
|
||||||
let win = tile.window_mut();
|
let win = tile.window_mut();
|
||||||
let min_size = win.min_size();
|
let min_size = win.min_size();
|
||||||
let max_size = win.max_size();
|
let max_size = win.max_size();
|
||||||
@@ -635,6 +721,8 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let tile = &mut self.tiles[idx];
|
let tile = &mut self.tiles[idx];
|
||||||
|
tile.floating_preset_height_idx = None;
|
||||||
|
|
||||||
let win = tile.window_mut();
|
let win = tile.window_mut();
|
||||||
let min_size = win.min_size();
|
let min_size = win.min_size();
|
||||||
let max_size = win.max_size();
|
let max_size = win.max_size();
|
||||||
@@ -1004,6 +1092,13 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
assert_eq!(self.scale, tile.scale());
|
assert_eq!(self.scale, tile.scale());
|
||||||
tile.verify_invariants();
|
tile.verify_invariants();
|
||||||
|
|
||||||
|
if let Some(idx) = tile.floating_preset_width_idx {
|
||||||
|
assert!(idx < self.options.preset_column_widths.len());
|
||||||
|
}
|
||||||
|
if let Some(idx) = tile.floating_preset_height_idx {
|
||||||
|
assert!(idx < self.options.preset_window_heights.len());
|
||||||
|
}
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!tile.window().is_pending_fullscreen(),
|
!tile.window().is_pending_fullscreen(),
|
||||||
"floating windows cannot be fullscreen"
|
"floating windows cannot be fullscreen"
|
||||||
@@ -1055,3 +1150,10 @@ fn compute_toplevel_bounds(
|
|||||||
))
|
))
|
||||||
.to_i32_floor()
|
.to_i32_floor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_preset_size(preset: PresetSize, view_size: f64) -> ResolvedSize {
|
||||||
|
match preset {
|
||||||
|
PresetSize::Proportion(proportion) => ResolvedSize::Tile(view_size * proportion),
|
||||||
|
PresetSize::Fixed(width) => ResolvedSize::Window(f64::from(width)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2832,6 +2832,16 @@ impl ColumnWidth {
|
|||||||
ColumnWidth::Fixed(width) => 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 {
|
impl From<PresetSize> for ColumnWidth {
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ pub struct Tile<W: LayoutElement> {
|
|||||||
/// the window starts out in the tiling layout.
|
/// the window starts out in the tiling layout.
|
||||||
pub(super) floating_pos: Option<Point<f64, SizeFrac>>,
|
pub(super) floating_pos: Option<Point<f64, SizeFrac>>,
|
||||||
|
|
||||||
|
/// Currently selected preset width index when this tile is floating.
|
||||||
|
pub(super) floating_preset_width_idx: Option<usize>,
|
||||||
|
|
||||||
|
/// Currently selected preset height index when this tile is floating.
|
||||||
|
pub(super) floating_preset_height_idx: Option<usize>,
|
||||||
|
|
||||||
/// The animation upon opening a window.
|
/// The animation upon opening a window.
|
||||||
open_animation: Option<OpenAnimation>,
|
open_animation: Option<OpenAnimation>,
|
||||||
|
|
||||||
@@ -143,6 +149,8 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
unfullscreen_to_floating: false,
|
unfullscreen_to_floating: false,
|
||||||
floating_window_size: None,
|
floating_window_size: None,
|
||||||
floating_pos: None,
|
floating_pos: None,
|
||||||
|
floating_preset_width_idx: None,
|
||||||
|
floating_preset_height_idx: None,
|
||||||
open_animation: None,
|
open_animation: None,
|
||||||
resize_animation: None,
|
resize_animation: None,
|
||||||
move_x_animation: None,
|
move_x_animation: None,
|
||||||
@@ -157,6 +165,14 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_config(&mut self, scale: f64, options: Rc<Options>) {
|
pub fn update_config(&mut self, scale: f64, options: Rc<Options>) {
|
||||||
|
// If preset widths or heights changed, clear our stored preset index.
|
||||||
|
if self.options.preset_column_widths != options.preset_column_widths {
|
||||||
|
self.floating_preset_width_idx = None;
|
||||||
|
}
|
||||||
|
if self.options.preset_window_heights != options.preset_window_heights {
|
||||||
|
self.floating_preset_height_idx = None;
|
||||||
|
}
|
||||||
|
|
||||||
self.scale = scale;
|
self.scale = scale;
|
||||||
self.options = options;
|
self.options = options;
|
||||||
|
|
||||||
@@ -475,6 +491,25 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tile_expected_or_current_size(&self) -> Size<f64, Logical> {
|
||||||
|
let mut size = self.window_expected_or_current_size();
|
||||||
|
|
||||||
|
if self.is_fullscreen {
|
||||||
|
// Normally we'd just return the fullscreen size here, but this makes things a bit
|
||||||
|
// nicer if a fullscreen window is bigger than the fullscreen size for some reason.
|
||||||
|
size.w = f64::max(size.w, self.fullscreen_size.w);
|
||||||
|
size.h = f64::max(size.h, self.fullscreen_size.h);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(width) = self.effective_border_width() {
|
||||||
|
size.w += width * 2.;
|
||||||
|
size.h += width * 2.;
|
||||||
|
}
|
||||||
|
|
||||||
|
size
|
||||||
|
}
|
||||||
|
|
||||||
pub fn window_size(&self) -> Size<f64, Logical> {
|
pub fn window_size(&self) -> Size<f64, Logical> {
|
||||||
let mut size = self.window.size().to_f64();
|
let mut size = self.window.size().to_f64();
|
||||||
size = size
|
size = size
|
||||||
@@ -483,6 +518,15 @@ impl<W: LayoutElement> Tile<W> {
|
|||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn window_expected_or_current_size(&self) -> Size<f64, Logical> {
|
||||||
|
let size = self.window.expected_size();
|
||||||
|
let mut size = size.unwrap_or_else(|| self.window.size()).to_f64();
|
||||||
|
size = size
|
||||||
|
.to_physical_precise_round(self.scale)
|
||||||
|
.to_logical(self.scale);
|
||||||
|
size
|
||||||
|
}
|
||||||
|
|
||||||
fn animated_window_size(&self) -> Size<f64, Logical> {
|
fn animated_window_size(&self) -> Size<f64, Logical> {
|
||||||
let mut size = self.window_size();
|
let mut size = self.window_size();
|
||||||
|
|
||||||
|
|||||||
@@ -952,11 +952,11 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_width(&mut self) {
|
pub fn toggle_width(&mut self) {
|
||||||
// TODO
|
|
||||||
if self.floating_is_active.get() {
|
if self.floating_is_active.get() {
|
||||||
return;
|
self.floating.toggle_window_width(None);
|
||||||
|
} else {
|
||||||
|
self.scrolling.toggle_width();
|
||||||
}
|
}
|
||||||
self.scrolling.toggle_width();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_full_width(&mut self) {
|
pub fn toggle_full_width(&mut self) {
|
||||||
@@ -998,10 +998,10 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
if window.map_or(self.floating_is_active.get(), |id| {
|
if window.map_or(self.floating_is_active.get(), |id| {
|
||||||
self.floating.has_window(id)
|
self.floating.has_window(id)
|
||||||
}) {
|
}) {
|
||||||
// TODO
|
self.floating.toggle_window_height(window);
|
||||||
return;
|
} else {
|
||||||
|
self.scrolling.toggle_window_height(window);
|
||||||
}
|
}
|
||||||
self.scrolling.toggle_window_height(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
|
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
|
||||||
|
|||||||
Reference in New Issue
Block a user