Implement maximize-to-edges (true Wayland maximize)

This commit is contained in:
Ivan Molodetskikh
2025-09-02 08:07:22 +03:00
parent e5d4e7c1b1
commit e1fad994da
251 changed files with 4858 additions and 280 deletions
+7
View File
@@ -303,6 +303,9 @@ pub enum Action {
#[knuffel(skip)]
SwitchPresetWindowHeightBackById(u64),
MaximizeColumn,
MaximizeWindowToEdges,
#[knuffel(skip)]
MaximizeWindowToEdgesById(u64),
SetColumnWidth(#[knuffel(argument, str)] SizeChange),
ExpandColumnToAvailableWidth,
SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget),
@@ -568,6 +571,10 @@ impl From<niri_ipc::Action> for Action {
Self::SwitchPresetWindowHeightBackById(id)
}
niri_ipc::Action::MaximizeColumn {} => Self::MaximizeColumn,
niri_ipc::Action::MaximizeWindowToEdges { id: None } => Self::MaximizeWindowToEdges,
niri_ipc::Action::MaximizeWindowToEdges { id: Some(id) } => {
Self::MaximizeWindowToEdgesById(id)
}
niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change),
niri_ipc::Action::ExpandColumnToAvailableWidth {} => Self::ExpandColumnToAvailableWidth,
niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout),
+1
View File
@@ -1578,6 +1578,7 @@ mod tests {
open_maximized: Some(
true,
),
open_maximized_to_edges: None,
open_fullscreen: Some(
false,
),
+2
View File
@@ -24,6 +24,8 @@ pub struct WindowRule {
#[knuffel(child, unwrap(argument))]
pub open_maximized: Option<bool>,
#[knuffel(child, unwrap(argument))]
pub open_maximized_to_edges: Option<bool>,
#[knuffel(child, unwrap(argument))]
pub open_fullscreen: Option<bool>,
#[knuffel(child, unwrap(argument))]
pub open_floating: Option<bool>,
+8
View File
@@ -713,6 +713,14 @@ pub enum Action {
},
/// Toggle the maximized state of the focused column.
MaximizeColumn {},
/// Toggle the maximized-to-edges state of the focused window.
MaximizeWindowToEdges {
/// Id of the window to maximize.
///
/// If `None`, uses the focused window.
#[cfg_attr(feature = "clap", arg(long))]
id: Option<u64>,
},
/// Change the width of the focused column.
SetColumnWidth {
/// How to change the width.
+3 -3
View File
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::time::Duration;
use niri::animation::Clock;
use niri::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _, Options};
use niri::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _, Options, SizingMode};
use niri::render_helpers::RenderTarget;
use niri_config::{Color, OutputName, PresetSize};
use smithay::backend::renderer::element::RenderElement;
@@ -168,7 +168,7 @@ impl Layout {
let max_size = window.max_size();
window.request_size(
ws.new_window_size(width, None, false, window.rules(), (min_size, max_size)),
false,
SizingMode::Normal,
false,
None,
);
@@ -197,7 +197,7 @@ impl Layout {
let max_size = window.max_size();
window.request_size(
ws.new_window_size(width, None, false, window.rules(), (min_size, max_size)),
false,
SizingMode::Normal,
false,
None,
);
+5 -5
View File
@@ -1,4 +1,4 @@
use niri::layout::LayoutElement;
use niri::layout::{LayoutElement, SizingMode};
use niri::render_helpers::RenderTarget;
use smithay::backend::renderer::element::RenderElement;
use smithay::backend::renderer::gles::GlesRenderer;
@@ -14,14 +14,14 @@ pub struct Window {
impl Window {
pub fn freeform(args: Args) -> Self {
let mut window = TestWindow::freeform(0);
window.request_size(args.size, false, false, None);
window.request_size(args.size, SizingMode::Normal, false, None);
window.communicate();
Self { window }
}
pub fn fixed_size(args: Args) -> Self {
let mut window = TestWindow::fixed_size(0);
window.request_size(args.size, false, false, None);
window.request_size(args.size, SizingMode::Normal, false, None);
window.communicate();
Self { window }
}
@@ -29,7 +29,7 @@ impl Window {
pub fn fixed_size_with_csd_shadow(args: Args) -> Self {
let mut window = TestWindow::fixed_size(0);
window.set_csd_shadow_width(64);
window.request_size(args.size, false, false, None);
window.request_size(args.size, SizingMode::Normal, false, None);
window.communicate();
Self { window }
}
@@ -38,7 +38,7 @@ impl Window {
impl TestCase for Window {
fn resize(&mut self, width: i32, height: i32) {
self.window
.request_size(Size::from((width, height)), false, false, None);
.request_size(Size::from((width, height)), SizingMode::Normal, false, None);
self.window.communicate();
}
+9 -9
View File
@@ -4,7 +4,7 @@ use std::rc::Rc;
use niri::layout::{
ConfigureIntent, InteractiveResizeData, LayoutElement, LayoutElementRenderElement,
LayoutElementRenderSnapshot,
LayoutElementRenderSnapshot, SizingMode,
};
use niri::render_helpers::offscreen::OffscreenData;
use niri::render_helpers::renderer::NiriRenderer;
@@ -24,7 +24,7 @@ struct TestWindowInner {
min_size: Size<i32, Logical>,
max_size: Size<i32, Logical>,
buffer: SolidColorBuffer,
pending_fullscreen: bool,
pending_sizing_mode: SizingMode,
csd_shadow_width: i32,
csd_shadow_buffer: SolidColorBuffer,
}
@@ -50,7 +50,7 @@ impl TestWindow {
min_size,
max_size,
buffer,
pending_fullscreen: false,
pending_sizing_mode: SizingMode::Normal,
csd_shadow_width: 0,
csd_shadow_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.3]),
})),
@@ -182,12 +182,12 @@ impl LayoutElement for TestWindow {
fn request_size(
&mut self,
size: Size<i32, Logical>,
is_fullscreen: bool,
mode: SizingMode,
_animate: bool,
_transaction: Option<Transaction>,
) {
self.inner.borrow_mut().requested_size = Some(size);
self.inner.borrow_mut().pending_fullscreen = is_fullscreen;
self.inner.borrow_mut().pending_sizing_mode = mode;
}
fn min_size(&self) -> Size<i32, Logical> {
@@ -232,12 +232,12 @@ impl LayoutElement for TestWindow {
fn send_pending_configure(&mut self) {}
fn is_fullscreen(&self) -> bool {
false
fn pending_sizing_mode(&self) -> SizingMode {
self.inner.borrow().pending_sizing_mode
}
fn is_pending_fullscreen(&self) -> bool {
self.inner.borrow().pending_fullscreen
fn sizing_mode(&self) -> SizingMode {
SizingMode::Normal
}
fn requested_size(&self) -> Option<Size<i32, Logical>> {
+64 -27
View File
@@ -20,7 +20,7 @@ use smithay::{delegate_compositor, delegate_shm};
use super::xdg_shell::add_mapped_toplevel_pre_commit_hook;
use crate::handlers::XDG_ACTIVATION_TOKEN_TIMEOUT;
use crate::layout::{ActivateWindow, AddWindowTarget};
use crate::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _};
use crate::niri::{CastTarget, ClientState, LockState, State};
use crate::utils::transaction::Transaction;
use crate::utils::{is_mapped, send_scale_transform};
@@ -91,35 +91,59 @@ impl CompositorHandler for State {
let toplevel = window.toplevel().expect("no X11 support");
let (rules, width, height, is_full_width, output, workspace_id) =
if let InitialConfigureState::Configured {
let (
rules,
width,
height,
is_full_width,
output,
workspace_id,
is_pending_maximized,
) = if let InitialConfigureState::Configured {
rules,
width,
height,
floating_width: _,
floating_height: _,
is_full_width,
output,
workspace_name,
is_pending_maximized,
} = state
{
// Check that the output is still connected.
let output =
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
// Check that the workspace still exists.
let workspace_id = workspace_name
.as_deref()
.and_then(|n| self.niri.layout.find_workspace_by_name(n))
.map(|(_, ws)| ws.id());
(
rules,
width,
height,
floating_width: _,
floating_height: _,
is_full_width,
output,
workspace_name,
} = state
{
// Check that the output is still connected.
let output =
output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());
// Check that the workspace still exists.
let workspace_id = workspace_name
.as_deref()
.and_then(|n| self.niri.layout.find_workspace_by_name(n))
.map(|(_, ws)| ws.id());
(rules, width, height, is_full_width, output, workspace_id)
} else {
// Can happen when a surface unmaps by attaching a null buffer while
// there are in-flight pending configures.
debug!("window mapped without proper initial configure");
(ResolvedWindowRules::empty(), None, None, false, None, None)
};
workspace_id,
is_pending_maximized,
)
} else {
// Can happen when a surface unmaps by attaching a null buffer while
// there are in-flight pending configures.
debug!("window mapped without proper initial configure");
(
ResolvedWindowRules::empty(),
None,
None,
false,
None,
None,
false,
)
};
// The GTK about dialog sets min/max size after the initial configure but
// before mapping, so we need to compute open_floating at the last possible
@@ -169,7 +193,7 @@ impl CompositorHandler for State {
.map(|(mapped, _)| mapped.window.clone());
// The mapped pre-commit hook deals with dma-bufs on its own.
self.remove_default_dmabuf_pre_commit_hook(toplevel.wl_surface());
self.remove_default_dmabuf_pre_commit_hook(surface);
let hook = add_mapped_toplevel_pre_commit_hook(toplevel);
let mapped = Mapped::new(window, rules, hook);
let window = mapped.window.clone();
@@ -193,8 +217,21 @@ impl CompositorHandler for State {
is_floating,
activate,
);
let output = output.cloned();
if let Some(output) = output.cloned() {
// The window state cannot contain Fullscreen and Maximized at once. Therefore,
// if the window ended up fullscreen, then we only know that it is also
// maximized from the is_pending_maximized variable. Tell the layout about it
// here so that unfullscreening the window makes it maximized.
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(surface) {
if mapped.pending_sizing_mode().is_fullscreen() && is_pending_maximized {
self.niri.layout.set_maximized(&window, true);
}
} else {
error!("layout is missing the window that we just added");
}
if let Some(output) = output {
self.niri.layout.start_open_animation_for_window(&window);
let new_focus = self.niri.layout.focus().map(|m| &m.window);
+14
View File
@@ -550,6 +550,20 @@ impl ForeignToplevelHandler for State {
self.niri.layout.set_fullscreen(&window, false);
}
}
fn set_maximized(&mut self, wl_surface: WlSurface) {
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
let window = mapped.window.clone();
self.niri.layout.set_maximized(&window, true);
}
}
fn unset_maximized(&mut self, wl_surface: WlSurface) {
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
let window = mapped.window.clone();
self.niri.layout.set_maximized(&window, false);
}
}
}
delegate_foreign_toplevel!(State);
+222 -12
View File
@@ -428,18 +428,205 @@ impl XdgShellHandler for State {
});
}
fn maximize_request(&mut self, surface: ToplevelSurface) {
// FIXME
fn maximize_request(&mut self, toplevel: ToplevelSurface) {
if let Some((mapped, _)) = self
.niri
.layout
.find_window_and_output_mut(toplevel.wl_surface())
{
// A configure is required in response to this event regardless if there are pending
// changes.
mapped.set_needs_configure();
// A configure is required in response to this event. However, if an initial configure
// wasn't sent, then we will send this as part of the initial configure later.
if surface.is_initial_configure_sent() {
surface.send_configure();
let window = mapped.window.clone();
self.niri.layout.set_maximized(&window, true);
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
match &mut unmapped.state {
InitialConfigureState::NotConfigured {
wants_maximized, ..
} => {
*wants_maximized = true;
// The required configure will be the initial configure.
}
InitialConfigureState::Configured {
rules,
output,
is_pending_maximized,
..
} => {
// Figure out the monitor following a similar logic to initial configure.
// FIXME: deduplicate.
let mon = output
.as_ref()
.and_then(|o| self.niri.layout.monitor_for_output(o))
.map(|mon| (mon, false))
// If not, check if we have a parent with a monitor.
.or_else(|| {
toplevel
.parent()
.and_then(|parent| self.niri.layout.find_window_and_output(&parent))
.and_then(|(_win, output)| output)
.and_then(|o| self.niri.layout.monitor_for_output(o))
.map(|mon| (mon, true))
})
// If not, fall back to the active monitor.
.or_else(|| {
self.niri
.layout
.active_monitor_ref()
.map(|mon| (mon, false))
});
*output = mon
.filter(|(_, parent)| !parent)
.map(|(mon, _)| mon.output().clone());
let mon = mon.map(|(mon, _)| mon);
let ws = mon
.map(|mon| mon.active_workspace_ref())
.or_else(|| self.niri.layout.active_workspace());
if let Some(ws) = ws {
// If the window is pending fullscreen, then this will do nothing. But
// that's expected: the window remains fullscreen, and we simply remember
// that it is now pending maximized.
*is_pending_maximized = true;
toplevel.with_pending_state(|state| {
if !state.states.contains(xdg_toplevel::State::Fullscreen) {
state.states.set(xdg_toplevel::State::Maximized);
}
});
ws.configure_new_window(&unmapped.window, None, None, false, rules);
}
// We already sent the initial configure, so we need to reconfigure.
toplevel.send_configure();
}
}
} else {
error!("couldn't find the toplevel in maximize_request()");
toplevel.send_configure();
}
}
fn unmaximize_request(&mut self, _surface: ToplevelSurface) {
// FIXME
fn unmaximize_request(&mut self, toplevel: ToplevelSurface) {
if let Some((mapped, _)) = self
.niri
.layout
.find_window_and_output_mut(toplevel.wl_surface())
{
// A configure is required in response to this event regardless if there are pending
// changes.
mapped.set_needs_configure();
let window = mapped.window.clone();
self.niri.layout.set_maximized(&window, false);
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
match &mut unmapped.state {
InitialConfigureState::NotConfigured {
wants_maximized, ..
} => {
*wants_maximized = false;
// The required configure will be the initial configure.
}
InitialConfigureState::Configured {
rules,
width,
height,
floating_width,
floating_height,
is_full_width,
output,
workspace_name,
is_pending_maximized,
} => {
// Figure out the monitor following a similar logic to initial configure.
// FIXME: deduplicate.
let mon = workspace_name
.as_deref()
.and_then(|name| self.niri.layout.monitor_for_workspace(name))
.map(|mon| (mon, false));
let mon = mon.or_else(|| {
output
.as_ref()
.and_then(|o| self.niri.layout.monitor_for_output(o))
.map(|mon| (mon, false))
// If not, check if we have a parent with a monitor.
.or_else(|| {
toplevel
.parent()
.and_then(|parent| {
self.niri.layout.find_window_and_output(&parent)
})
.and_then(|(_win, output)| output)
.and_then(|o| self.niri.layout.monitor_for_output(o))
.map(|mon| (mon, true))
})
// If not, fall back to the active monitor.
.or_else(|| {
self.niri
.layout
.active_monitor_ref()
.map(|mon| (mon, false))
})
});
*output = mon
.filter(|(_, parent)| !parent)
.map(|(mon, _)| mon.output().clone());
let mon = mon.map(|(mon, _)| mon);
let ws = workspace_name
.as_deref()
.and_then(|name| mon.map(|mon| mon.find_named_workspace(name)))
.unwrap_or_else(|| {
mon.map(|mon| mon.active_workspace_ref())
.or_else(|| self.niri.layout.active_workspace())
});
if let Some(ws) = ws {
// If the window is pending fullscreen, then this will do nothing since
// then the Maximized state is already unset. But that's expected: the
// window remains fullscreen, and we simply remember that it is no
// longer pending maximized.
*is_pending_maximized = false;
toplevel.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
});
let is_floating = rules.compute_open_floating(&toplevel);
let configure_width = if is_floating {
*floating_width
} else if *is_full_width {
Some(PresetSize::Proportion(1.))
} else {
*width
};
let configure_height = if is_floating {
*floating_height
} else {
*height
};
ws.configure_new_window(
&unmapped.window,
configure_width,
configure_height,
is_floating,
rules,
);
}
// We already sent the initial configure, so we need to reconfigure.
toplevel.send_configure();
}
}
} else {
error!("couldn't find the toplevel in unmaximize_request()");
toplevel.send_configure();
}
}
fn fullscreen_request(
@@ -474,7 +661,9 @@ impl XdgShellHandler for State {
self.niri.layout.set_fullscreen(&window, true);
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
match &mut unmapped.state {
InitialConfigureState::NotConfigured { wants_fullscreen } => {
InitialConfigureState::NotConfigured {
wants_fullscreen, ..
} => {
*wants_fullscreen = Some(requested_output);
// The required configure will be the initial configure.
@@ -517,6 +706,7 @@ impl XdgShellHandler for State {
if let Some(ws) = ws {
toplevel.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Fullscreen);
state.states.unset(xdg_toplevel::State::Maximized);
});
ws.configure_new_window(&unmapped.window, None, None, false, rules);
}
@@ -545,7 +735,9 @@ impl XdgShellHandler for State {
self.niri.layout.set_fullscreen(&window, false);
} else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {
match &mut unmapped.state {
InitialConfigureState::NotConfigured { wants_fullscreen } => {
InitialConfigureState::NotConfigured {
wants_fullscreen, ..
} => {
*wants_fullscreen = None;
// The required configure will be the initial configure.
@@ -559,6 +751,7 @@ impl XdgShellHandler for State {
is_full_width,
output,
workspace_name,
is_pending_maximized,
} => {
// Figure out the monitor following a similar logic to initial configure.
// FIXME: deduplicate.
@@ -608,6 +801,10 @@ impl XdgShellHandler for State {
if let Some(ws) = ws {
toplevel.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
if *is_pending_maximized {
state.states.set(xdg_toplevel::State::Maximized);
}
});
let is_floating = rules.compute_open_floating(&toplevel);
@@ -858,7 +1055,11 @@ impl State {
let Unmapped { window, state, .. } = unmapped;
let InitialConfigureState::NotConfigured { wants_fullscreen } = state else {
let InitialConfigureState::NotConfigured {
wants_fullscreen,
wants_maximized,
} = state
else {
error!("window must not be already configured in send_initial_configure()");
return;
};
@@ -934,14 +1135,22 @@ impl State {
.or_else(|| self.niri.layout.active_workspace())
});
let mut is_pending_maximized = false;
if let Some(ws) = ws {
// Set a fullscreen state based on window request and window rule.
// Set a fullscreen and maximized state based on window request and window rule.
is_pending_maximized = (*wants_maximized && rules.open_maximized_to_edges.is_none())
|| rules.open_maximized_to_edges == Some(true);
if (wants_fullscreen.is_some() && rules.open_fullscreen.is_none())
|| rules.open_fullscreen == Some(true)
{
toplevel.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Fullscreen);
});
} else if is_pending_maximized {
toplevel.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Maximized);
});
}
width = ws.resolve_default_width(rules.default_width, false);
@@ -979,6 +1188,7 @@ impl State {
is_full_width,
output,
workspace_name: ws.and_then(|w| w.name().cloned()),
is_pending_maximized,
};
trace!(surface = %toplevel.wl_surface().id(), "sending initial configure");
+17
View File
@@ -1514,6 +1514,23 @@ impl State {
Action::MaximizeColumn => {
self.niri.layout.toggle_full_width();
}
Action::MaximizeWindowToEdges => {
let focus = self.niri.layout.focus().map(|m| m.window.clone());
if let Some(window) = focus {
self.niri.layout.toggle_maximized(&window);
// FIXME: granular
self.niri.queue_redraw_all();
}
}
Action::MaximizeWindowToEdgesById(id) => {
let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);
let window = window.map(|(_, m)| m.window.clone());
if let Some(window) = window {
self.niri.layout.toggle_maximized(&window);
// FIXME: granular
self.niri.queue_redraw_all();
}
}
Action::FocusMonitorLeft => {
if let Some(output) = self.niri.output_left() {
self.niri.layout.focus_output(&output);
+8 -5
View File
@@ -413,8 +413,8 @@ impl<W: LayoutElement> FloatingSpace<W> {
// unfullscreen it.
let floating_size = tile.floating_window_size;
let win = tile.window_mut();
let mut size = if win.is_pending_fullscreen() {
// If the window was fullscreen without a floating size, ask for (0, 0).
let mut size = if !win.pending_sizing_mode().is_normal() {
// If the window was fullscreen or maximized without a floating size, ask for (0, 0).
floating_size.unwrap_or_default()
} else {
// If the window wasn't fullscreen without a floating size (e.g. it was tiled before),
@@ -1312,6 +1312,8 @@ impl<W: LayoutElement> FloatingSpace<W> {
assert_eq!(self.tiles.len(), self.data.len());
for (i, (tile, data)) in zip(&self.tiles, &self.data).enumerate() {
use crate::layout::SizingMode;
assert!(Rc::ptr_eq(&self.options, &tile.options));
assert_eq!(self.view_size, tile.view_size());
assert_eq!(self.clock, tile.clock);
@@ -1325,9 +1327,10 @@ impl<W: LayoutElement> FloatingSpace<W> {
assert!(idx < self.options.layout.preset_window_heights.len());
}
assert!(
!tile.window().is_pending_fullscreen(),
"floating windows cannot be fullscreen"
assert_eq!(
tile.window().pending_sizing_mode(),
SizingMode::Normal,
"floating windows cannot be maximized or fullscreen"
);
data.verify_invariants();
+64 -9
View File
@@ -118,6 +118,13 @@ niri_render_elements! {
pub type LayoutElementRenderSnapshot =
RenderSnapshot<BakedBuffer<TextureBuffer<GlesTexture>>, BakedBuffer<SolidColorBuffer>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizingMode {
Normal,
Maximized,
Fullscreen,
}
pub trait LayoutElement {
/// Type that can be used as a unique ID of this element.
type Id: PartialEq + std::fmt::Debug + Clone;
@@ -185,14 +192,14 @@ pub trait LayoutElement {
fn request_size(
&mut self,
size: Size<i32, Logical>,
is_fullscreen: bool,
mode: SizingMode,
animate: bool,
transaction: Option<Transaction>,
);
/// Requests the element to change size once, clearing the request afterwards.
fn request_size_once(&mut self, size: Size<i32, Logical>, animate: bool) {
self.request_size(size, false, animate, None);
self.request_size(size, SizingMode::Normal, animate, None);
}
fn min_size(&self) -> Size<i32, Logical>;
@@ -214,15 +221,15 @@ pub trait LayoutElement {
fn configure_intent(&self) -> ConfigureIntent;
fn send_pending_configure(&mut self);
/// Whether the element is currently fullscreen.
/// The element's current sizing mode.
///
/// This will *not* switch immediately after a [`LayoutElement::request_size()`] call.
fn is_fullscreen(&self) -> bool;
fn sizing_mode(&self) -> SizingMode;
/// Whether we're requesting the element to be fullscreen.
/// The sizing mode that we're requesting the element to assume.
///
/// This *will* switch immediately after a [`LayoutElement::request_size()`] call.
fn is_pending_fullscreen(&self) -> bool;
fn pending_sizing_mode(&self) -> SizingMode;
/// Size previously requested through [`LayoutElement::request_size()`].
fn requested_size(&self) -> Option<Size<i32, Logical>>;
@@ -240,7 +247,7 @@ pub trait LayoutElement {
///
/// The default impl is for testing only, it will not preserve the window's own size changes.
fn expected_size(&self) -> Option<Size<i32, Logical>> {
if self.is_fullscreen() {
if self.sizing_mode().is_fullscreen() {
return None;
}
@@ -502,6 +509,23 @@ struct OverviewGesture {
value: f64,
}
impl SizingMode {
#[must_use]
pub fn is_normal(&self) -> bool {
matches!(self, Self::Normal)
}
#[must_use]
pub fn is_fullscreen(&self) -> bool {
matches!(self, Self::Fullscreen)
}
#[must_use]
pub fn is_maximized(&self) -> bool {
matches!(self, Self::Maximized)
}
}
impl<W: LayoutElement> InteractiveMoveState<W> {
fn moving(&self) -> Option<&InteractiveMoveData<W>> {
match self {
@@ -2321,7 +2345,7 @@ impl<W: LayoutElement> Layout<W> {
}
InteractiveMoveState::Moving(move_) => {
assert_eq!(self.clock, move_.tile.clock);
assert!(!move_.tile.window().is_pending_fullscreen());
assert!(move_.tile.window().pending_sizing_mode().is_normal());
move_.tile.verify_invariants();
@@ -3456,7 +3480,7 @@ impl<W: LayoutElement> Layout<W> {
pub fn toggle_windowed_fullscreen(&mut self, id: &W::Id) {
let (_, window) = self.windows().find(|(_, win)| win.id() == id).unwrap();
if window.is_pending_fullscreen() {
if window.pending_sizing_mode().is_fullscreen() {
// Remove the real fullscreen.
for ws in self.workspaces_mut() {
if ws.has_window(id) {
@@ -3474,6 +3498,36 @@ impl<W: LayoutElement> Layout<W> {
});
}
pub fn set_maximized(&mut self, id: &W::Id, maximize: bool) {
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
if move_.tile.window().id() == id {
return;
}
}
for ws in self.workspaces_mut() {
if ws.has_window(id) {
ws.set_maximized(id, maximize);
return;
}
}
}
pub fn toggle_maximized(&mut self, id: &W::Id) {
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
if move_.tile.window().id() == id {
return;
}
}
for ws in self.workspaces_mut() {
if ws.has_window(id) {
ws.toggle_maximized(id);
return;
}
}
}
pub fn workspace_switch_gesture_begin(&mut self, output: &Output, is_touchpad: bool) {
let monitors = match &mut self.monitor_set {
MonitorSet::Normal { monitors, .. } => monitors,
@@ -3845,6 +3899,7 @@ impl<W: LayoutElement> Layout<W> {
.find(|ws| ws.has_window(&window_id))
.unwrap();
ws.set_fullscreen(window, false);
ws.set_maximized(window, false);
let RemovedTile {
mut tile,
+278 -85
View File
@@ -18,6 +18,7 @@ use super::workspace::{InteractiveResize, ResolvedSize};
use super::{ConfigureIntent, HitType, InteractiveResizeData, LayoutElement, Options, RemovedTile};
use crate::animation::{Animation, Clock};
use crate::input::swipe_tracker::SwipeTracker;
use crate::layout::SizingMode;
use crate::niri_render_elements;
use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::RenderTarget;
@@ -62,8 +63,8 @@ pub struct ScrollingSpace<W: LayoutElement> {
/// The value is the view offset that the previous column had before, to restore it.
activate_prev_column_on_removal: Option<f64>,
/// View offset to restore after unfullscreening.
view_offset_before_fullscreen: Option<f64>,
/// View offset to restore after unfullscreening or unmaximizing.
view_offset_to_restore: Option<f64>,
/// Windows in the closing animation.
closing_windows: Vec<ClosingWindow>,
@@ -177,6 +178,13 @@ pub struct Column<W: LayoutElement> {
/// take some time to catch up and actually unfullscreen.
is_pending_fullscreen: bool,
/// Whether this column is going to be maximized.
///
/// Can be `true` together with `is_pending_fullscreen`, which means that the column is
/// effectively pending fullscreen, but unfullscreening should go back to maximized state,
/// rather than normal.
is_pending_maximized: bool,
/// How this column displays and arranges windows.
display_mode: ColumnDisplay,
@@ -192,6 +200,11 @@ pub struct Column<W: LayoutElement> {
/// Latest known working area for this column's workspace.
working_area: Rectangle<f64, Logical>,
/// Working area for this column's workspace excluding struts.
///
/// Used for maximize-to-edges.
parent_area: Rectangle<f64, Logical>,
/// Scale of the output the column is on (and rounds its sizes to).
scale: f64,
@@ -285,7 +298,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
interactive_resize: None,
view_offset: ViewOffset::Static(0.),
activate_prev_column_on_removal: None,
view_offset_before_fullscreen: None,
view_offset_to_restore: None,
closing_windows: Vec::new(),
view_size,
working_area,
@@ -306,7 +319,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let working_area = compute_working_area(parent_area, scale, options.layout.struts);
for (column, data) in zip(&mut self.columns, &mut self.data) {
column.update_config(view_size, working_area, scale, options.clone());
column.update_config(view_size, working_area, parent_area, scale, options.clone());
data.update(column);
}
@@ -440,7 +453,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
let col = &self.columns[self.active_column_idx];
col.is_pending_fullscreen
col.pending_sizing_mode().is_fullscreen()
}
pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {
@@ -533,24 +546,25 @@ impl<W: LayoutElement> ScrollingSpace<W> {
target_x: Option<f64>,
col_x: f64,
width: f64,
is_fullscreen: bool,
mode: SizingMode,
) -> f64 {
if is_fullscreen {
if mode.is_fullscreen() {
return 0.;
}
let (area, padding) = if mode.is_maximized() {
(self.parent_area, 0.)
} else {
(self.working_area, self.options.layout.gaps)
};
let target_x = target_x.unwrap_or_else(|| self.target_view_pos());
let new_offset = compute_new_view_offset(
target_x + self.working_area.loc.x,
self.working_area.size.w,
col_x,
width,
self.options.layout.gaps,
);
let new_offset =
compute_new_view_offset(target_x + area.loc.x, area.size.w, col_x, width, padding);
// Non-fullscreen windows are always offset at least by the working area position.
new_offset - self.working_area.loc.x
new_offset - area.loc.x
}
fn compute_new_view_offset_centered(
@@ -558,18 +572,24 @@ impl<W: LayoutElement> ScrollingSpace<W> {
target_x: Option<f64>,
col_x: f64,
width: f64,
is_fullscreen: bool,
mode: SizingMode,
) -> f64 {
if is_fullscreen {
return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen);
if mode.is_fullscreen() {
return self.compute_new_view_offset_fit(target_x, col_x, width, mode);
}
let area = if mode.is_maximized() {
self.parent_area
} else {
self.working_area
};
// Columns wider than the view are left-aligned (the fit code can deal with that).
if self.working_area.size.w <= width {
return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen);
if area.size.w <= width {
return self.compute_new_view_offset_fit(target_x, col_x, width, mode);
}
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x
-(area.size.w - width) / 2. - area.loc.x
}
fn compute_new_view_offset_for_column_fit(&self, target_x: Option<f64>, idx: usize) -> f64 {
@@ -578,7 +598,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
target_x,
self.column_x(idx),
col.width(),
col.is_fullscreen(),
col.sizing_mode(),
)
}
@@ -592,7 +612,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
target_x,
self.column_x(idx),
col.width(),
col.is_fullscreen(),
col.sizing_mode(),
)
}
@@ -633,6 +653,8 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let target_col_x = self.column_x(idx);
let target_col_width = self.columns[idx].width();
// NOTE: This logic won't work entirely correctly with small fixed-size maximized
// windows (they have a different area and padding).
let total_width = if source_col_x < target_col_x {
// Source is left from target.
target_col_x - source_col_x + target_col_width
@@ -770,7 +792,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
// A different column was activated; reset the flag.
self.activate_prev_column_on_removal = None;
self.view_offset_before_fullscreen = None;
self.view_offset_to_restore = None;
self.interactive_resize = None;
}
}
@@ -855,6 +877,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
tile,
self.view_size,
self.working_area,
self.parent_area,
self.scale,
width,
is_full_width,
@@ -956,6 +979,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
column.update_config(
self.view_size,
self.working_area,
self.parent_area,
self.scale,
self.options.clone(),
);
@@ -1059,15 +1083,15 @@ impl<W: LayoutElement> ScrollingSpace<W> {
tile.animate_alpha(0., 1., movement_config);
}
let was_fullscreen = column.is_fullscreen();
let was_normal = column.sizing_mode().is_normal();
let tile = column.tiles.remove(tile_idx);
column.data.remove(tile_idx);
// If an active column became non-fullscreen after removing the tile, clear the stored
// unfullscreen offset.
if column_idx == self.active_column_idx && was_fullscreen && !column.is_fullscreen() {
self.view_offset_before_fullscreen = None;
if column_idx == self.active_column_idx && !was_normal && column.sizing_mode().is_normal() {
self.view_offset_to_restore = None;
}
// If one window is left, reset its weight to 1.
@@ -1171,7 +1195,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
if column_idx == self.active_column_idx {
self.view_offset_before_fullscreen = None;
self.view_offset_to_restore = None;
}
if self.columns.is_empty() {
@@ -1225,7 +1249,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
.enumerate()
.find(|(_, col)| col.contains(window))
.unwrap();
let was_fullscreen = column.is_fullscreen();
let was_normal = column.sizing_mode().is_normal();
let prev_origin = column.tiles_origin();
let (tile_idx, tile) = column
@@ -1337,9 +1361,9 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
// When the active column goes fullscreen, store the view offset to restore later.
let is_fullscreen = self.columns[col_idx].is_fullscreen();
if !was_fullscreen && is_fullscreen {
self.view_offset_before_fullscreen = Some(self.view_offset.stationary());
let is_normal = self.columns[col_idx].sizing_mode().is_normal();
if was_normal && !is_normal {
self.view_offset_to_restore = Some(self.view_offset.stationary());
}
// Upon unfullscreening, restore the view offset.
@@ -1348,11 +1372,11 @@ impl<W: LayoutElement> ScrollingSpace<W> {
// will unfullscreen one by one, and the column width will shrink only when the
// last tile unfullscreens. This is when we want to restore the view offset,
// otherwise it will immediately reset back by the animate_view_offset below.
let unfullscreen_offset = if was_fullscreen && !is_fullscreen {
let unfullscreen_offset = if !was_normal && is_normal {
// Take the value unconditionally, even if the view is currently frozen by
// a view gesture. It shouldn't linger around because it's only valid for this
// particular unfullscreen.
self.view_offset_before_fullscreen.take()
self.view_offset_to_restore.take()
} else {
None
};
@@ -2165,6 +2189,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
if col.display_mode != ColumnDisplay::Tabbed && col.tiles.len() > 1 {
let window = col.tiles[col.active_tile_idx].window().id().clone();
self.set_fullscreen(&window, false);
self.set_maximized(&window, false);
}
}
@@ -2307,6 +2332,10 @@ impl<W: LayoutElement> ScrollingSpace<W> {
iter::once(active_pos).chain(offsets)
}
pub fn columns(&self) -> impl Iterator<Item = &Column<W>> {
self.columns.iter()
}
fn columns_mut(&mut self) -> impl Iterator<Item = (&mut Column<W>, f64)> + '_ {
let offsets = self.column_xs(self.data.iter().copied());
zip(&mut self.columns, offsets)
@@ -2474,7 +2503,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
// Adjust for place-within-column tab indicator.
let origin_x = col.tiles_origin().x;
let extra_w = if is_tabbed && !col.is_fullscreen() {
let extra_w = if is_tabbed && col.sizing_mode().is_normal() {
col.tab_indicator.extra_size(col.tiles.len(), col.scale).w
} else {
0.
@@ -2491,9 +2520,14 @@ impl<W: LayoutElement> ScrollingSpace<W> {
// effect here.
if self.columns.is_empty() {
let view_offset = if self.is_centering_focused_column() {
self.compute_new_view_offset_centered(Some(0.), 0., hint_area.size.w, false)
self.compute_new_view_offset_centered(
Some(0.),
0.,
hint_area.size.w,
SizingMode::Normal,
)
} else {
self.compute_new_view_offset_fit(Some(0.), 0., hint_area.size.w, false)
self.compute_new_view_offset_fit(Some(0.), 0., hint_area.size.w, SizingMode::Normal)
};
hint_area.loc.x -= view_offset;
} else {
@@ -2693,7 +2727,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
}
let col = &mut self.columns[self.active_column_idx];
if col.is_pending_fullscreen || col.is_full_width {
if !col.pending_sizing_mode().is_normal() || col.is_full_width {
return;
}
@@ -2709,6 +2743,9 @@ impl<W: LayoutElement> ScrollingSpace<W> {
return;
}
// NOTE: This logic won't work entirely correctly with small fixed-size maximized windows
// (they have a different area and padding).
// Consider the end of an ongoing animation because that's what compute to fit does too.
let view_x = self.target_view_pos();
let working_x = self.working_area.loc.x;
@@ -2812,6 +2849,37 @@ impl<W: LayoutElement> ScrollingSpace<W> {
true
}
pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) -> bool {
let mut col_idx = self
.columns
.iter()
.position(|col| col.contains(window))
.unwrap();
if maximize == self.columns[col_idx].is_pending_maximized {
return false;
}
let mut col = &mut self.columns[col_idx];
let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;
cancel_resize_for_column(&mut self.interactive_resize, col);
if maximize && (col.tiles.len() > 1 && !is_tabbed) {
// This wasn't the only window in its column; extract it into a separate column.
self.consume_or_expel_window_right(Some(window));
col_idx += 1;
col = &mut self.columns[col_idx];
}
col.set_maximized(maximize);
// With place_within_column, the tab indicator changes the column size immediately.
self.data[col_idx].update(col);
true
}
pub fn render_above_top_layer(&self) -> bool {
// Render above the top layer if we're on a fullscreen window and the view is stationary.
if self.columns.is_empty() {
@@ -2822,7 +2890,9 @@ impl<W: LayoutElement> ScrollingSpace<W> {
return false;
}
self.columns[self.active_column_idx].is_fullscreen()
self.columns[self.active_column_idx]
.sizing_mode()
.is_fullscreen()
}
pub fn render_elements<R: NiriRenderer>(
@@ -2902,7 +2972,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let col_render_off = col.render_offset();
// Hit the tab indicator.
if col.display_mode == ColumnDisplay::Tabbed && !col.is_fullscreen() {
if col.display_mode == ColumnDisplay::Tabbed && col.sizing_mode().is_normal() {
let col_pos = view_off + col_off + col_render_off;
let col_pos = col_pos.to_physical_precise_round(scale).to_logical(scale);
@@ -3134,20 +3204,26 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let mut snapping_points = Vec::new();
let left_strut = self.working_area.loc.x;
let right_strut = self.view_size.w - self.working_area.size.w - self.working_area.loc.x;
if self.is_centering_focused_column() {
let mut col_x = 0.;
for (col_idx, col) in self.columns.iter().enumerate() {
let col_w = col.width();
let mode = col.sizing_mode();
let view_pos = if col.is_fullscreen() {
let area = if mode.is_maximized() {
self.parent_area
} else {
self.working_area
};
let left_strut = area.loc.x;
let view_pos = if mode.is_fullscreen() {
col_x
} else if self.working_area.size.w <= col_w {
} else if area.size.w <= col_w {
col_x - left_strut
} else {
col_x - (self.working_area.size.w - col_w) / 2. - left_strut
col_x - (area.size.w - col_w) / 2. - left_strut
};
snapping_points.push(Snap { view_pos, col_idx });
@@ -3160,34 +3236,50 @@ impl<W: LayoutElement> ScrollingSpace<W> {
);
let view_width = self.view_size.w;
let working_area_width = self.working_area.size.w;
let gaps = self.options.layout.gaps;
let snap_points =
|col_x, col: &Column<W>, prev_col_w: Option<f64>, next_col_w: Option<f64>| {
let col_w = col.width();
let mode = col.sizing_mode();
let area = if mode.is_maximized() {
self.parent_area
} else {
self.working_area
};
let left_strut = area.loc.x;
let right_strut = self.view_size.w - area.size.w - area.loc.x;
// Normal columns align with the working area, but fullscreen columns align with
// the view size.
if col.is_fullscreen() {
if mode.is_fullscreen() {
let left = col_x;
let right = col_x + col_w;
let right = left + col_w;
(left, right)
} else {
// Logic from compute_new_view_offset.
let padding = ((working_area_width - col_w) / 2.).clamp(0., gaps);
let padding = if mode.is_maximized() {
0.
} else {
((area.size.w - col_w) / 2.).clamp(0., gaps)
};
let center = if self.working_area.size.w <= col_w {
let center = if area.size.w <= col_w {
col_x - left_strut
} else {
col_x - (self.working_area.size.w - col_w) / 2. - left_strut
col_x - (area.size.w - col_w) / 2. - left_strut
};
let is_overflowing = |adj_col_w: Option<f64>| {
center_on_overflow
&& adj_col_w
.filter(|adj_col_w| {
// NOTE: This logic won't work entirely correctly with small
// fixed-size maximized windows (they have a different area
// and padding).
center_on_overflow
&& adj_col_w + 3.0 * gaps + col_w > working_area_width
&& adj_col_w + 3.0 * gaps + col_w > area.size.w
})
.is_some()
};
@@ -3211,6 +3303,14 @@ impl<W: LayoutElement> ScrollingSpace<W> {
//
// It's ok if leftmost_snap is > rightmost_snap (this happens if the columns on a
// workspace total up to less than the workspace width).
// The first column's left snap isn't actually guaranteed to be the *leftmost* snap.
// With weird enough left strut and perhaps a maximized small fixed-size window, you
// can make the second window's left snap be further to the left than the first
// window's. The same goes for the rightmost snap.
//
// This isn't actually a big problem because it's very much an obscure edge case. Just
// need to make sure the code doesn't panic when that happens.
let leftmost_snap = snap_points(
0.,
&self.columns[0],
@@ -3295,16 +3395,28 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let col = &self.columns[col_idx];
let col_x = self.column_x(col_idx);
let col_w = col.width();
let mode = col.sizing_mode();
if col.is_fullscreen() {
let area = if mode.is_maximized() {
self.parent_area
} else {
self.working_area
};
let left_strut = area.loc.x;
if mode.is_fullscreen() {
if target_snap.view_pos + self.view_size.w < col_x + col_w {
break;
}
} else {
let padding = ((self.working_area.size.w - col_w) / 2.)
.clamp(0., self.options.layout.gaps);
if target_snap.view_pos + left_strut + self.working_area.size.w
< col_x + col_w + padding
let padding = if mode.is_maximized() {
0.
} else {
((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps)
};
if target_snap.view_pos + left_strut + area.size.w < col_x + col_w + padding
{
break;
}
@@ -3317,14 +3429,27 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let col = &self.columns[col_idx];
let col_x = self.column_x(col_idx);
let col_w = col.width();
let mode = col.sizing_mode();
if col.is_fullscreen() {
let area = if mode.is_maximized() {
self.parent_area
} else {
self.working_area
};
let left_strut = area.loc.x;
if mode.is_fullscreen() {
if col_x < target_snap.view_pos {
break;
}
} else {
let padding = ((self.working_area.size.w - col_w) / 2.)
.clamp(0., self.options.layout.gaps);
let padding = if mode.is_maximized() {
0.
} else {
((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps)
};
if col_x - padding < target_snap.view_pos + left_strut {
break;
}
@@ -3339,7 +3464,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let delta = active_col_x - new_col_x;
if self.active_column_idx != new_col_idx {
self.view_offset_before_fullscreen = None;
self.view_offset_to_restore = None;
}
self.active_column_idx = new_col_idx;
@@ -3391,7 +3516,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
.find(|col| col.contains(&window))
.unwrap();
if col.is_pending_fullscreen {
if !col.pending_sizing_mode().is_normal() {
return false;
}
@@ -3637,11 +3762,11 @@ impl<W: LayoutElement> ScrollingSpace<W> {
let col = &self.columns[self.active_column_idx];
if self.view_offset_before_fullscreen.is_some() {
if self.view_offset_to_restore.is_some() {
assert!(
col.is_fullscreen(),
"when view_offset_before_fullscreen is set, \
the active column must be fullscreen"
!col.sizing_mode().is_normal(),
"when view_offset_to_restore is set, \
the active column must be fullscreen or maximized"
);
}
}
@@ -3796,6 +3921,7 @@ impl<W: LayoutElement> Column<W> {
tile: Tile<W>,
view_size: Size<f64, Logical>,
working_area: Rectangle<f64, Logical>,
parent_area: Rectangle<f64, Logical>,
scale: f64,
width: ColumnWidth,
is_full_width: bool,
@@ -3815,29 +3941,33 @@ impl<W: LayoutElement> Column<W> {
width,
preset_width_idx: None,
is_full_width,
is_pending_maximized: false,
is_pending_fullscreen: false,
display_mode,
tab_indicator: TabIndicator::new(options.layout.tab_indicator),
move_animation: None,
view_size,
working_area,
parent_area,
scale,
clock: tile.clock.clone(),
options,
};
let is_pending_fullscreen = tile.window().is_pending_fullscreen();
let pending_sizing_mode = tile.window().pending_sizing_mode();
rv.add_tile_at(0, tile);
if is_pending_fullscreen {
rv.set_fullscreen(true);
match pending_sizing_mode {
SizingMode::Normal => (),
SizingMode::Maximized => rv.set_maximized(true),
SizingMode::Fullscreen => rv.set_fullscreen(true),
}
// Animate the tab indicator for new columns.
if display_mode == ColumnDisplay::Tabbed
&& !rv.options.layout.tab_indicator.hide_when_single_tab
&& !rv.is_fullscreen()
&& rv.sizing_mode().is_normal()
{
// Usually new columns are created together with window movement actions. For new
// windows, we handle that in start_open_animation().
@@ -3852,12 +3982,16 @@ impl<W: LayoutElement> Column<W> {
&mut self,
view_size: Size<f64, Logical>,
working_area: Rectangle<f64, Logical>,
parent_area: Rectangle<f64, Logical>,
scale: f64,
options: Rc<Options>,
) {
let mut update_sizes = false;
if self.view_size != view_size || self.working_area != working_area {
if self.view_size != view_size
|| self.working_area != working_area
|| self.parent_area != parent_area
{
update_sizes = true;
}
@@ -3895,6 +4029,7 @@ impl<W: LayoutElement> Column<W> {
.update_config(options.layout.tab_indicator);
self.view_size = view_size;
self.working_area = working_area;
self.parent_area = parent_area;
self.scale = scale;
self.options = options;
@@ -3954,7 +4089,7 @@ impl<W: LayoutElement> Column<W> {
// you don't want that to happen in fullscreen. Also, laying things out correctly when the
// tab indicator is within the column and the column goes fullscreen, would require too
// many changes to the code for too little benefit (it's mostly invisible anyway).
let enabled = self.display_mode == ColumnDisplay::Tabbed && !self.is_fullscreen();
let enabled = self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal();
self.tab_indicator.update_render_elements(
enabled,
@@ -3967,6 +4102,24 @@ impl<W: LayoutElement> Column<W> {
);
}
pub fn is_pending_fullscreen(&self) -> bool {
self.is_pending_fullscreen
}
pub fn is_pending_maximized(&self) -> bool {
self.is_pending_maximized
}
pub fn pending_sizing_mode(&self) -> SizingMode {
if self.is_pending_fullscreen {
SizingMode::Fullscreen
} else if self.is_pending_maximized {
SizingMode::Maximized
} else {
SizingMode::Normal
}
}
pub fn render_offset(&self) -> Point<f64, Logical> {
let mut offset = Point::from((0., 0.));
@@ -4040,7 +4193,7 @@ impl<W: LayoutElement> Column<W> {
/// - is_fullscreen() can suddenly change when consuming/expelling a fullscreen tile into/from a
/// non-fullscreen column. This can influence the code that saves/restores the unfullscreen
/// view offset.
fn is_fullscreen(&self) -> bool {
fn sizing_mode(&self) -> SizingMode {
// Behaviors that we want:
//
// 1. The common case: single tile in a column. Assume no animations. Fullscreening the tile
@@ -4058,7 +4211,23 @@ impl<W: LayoutElement> Column<W> {
// mode change applies instantly).
//
// The logic that satisfies these behaviors is to check if *any* tile is fullscreen.
self.tiles.iter().any(|tile| tile.is_fullscreen())
let mut any_fullscreen = false;
let mut any_maximized = false;
for tile in &self.tiles {
match tile.sizing_mode() {
SizingMode::Normal => (),
SizingMode::Maximized => any_maximized = true,
SizingMode::Fullscreen => any_fullscreen = true,
}
}
if any_fullscreen {
SizingMode::Fullscreen
} else if any_maximized {
SizingMode::Maximized
} else {
SizingMode::Normal
}
}
pub fn contains(&self, window: &W::Id) -> bool {
@@ -4102,6 +4271,7 @@ impl<W: LayoutElement> Column<W> {
if self.display_mode != ColumnDisplay::Tabbed {
self.is_pending_fullscreen = false;
self.is_pending_maximized = false;
}
self.data
@@ -4213,7 +4383,8 @@ impl<W: LayoutElement> Column<W> {
}
fn update_tile_sizes_with_transaction(&mut self, animate: bool, transaction: Transaction) {
if self.is_pending_fullscreen {
let sizing_mode = self.pending_sizing_mode();
if matches!(sizing_mode, SizingMode::Fullscreen | SizingMode::Maximized) {
for (tile_idx, tile) in self.tiles.iter_mut().enumerate() {
// In tabbed mode, only the visible window participates in the transaction.
let is_active = tile_idx == self.active_tile_idx;
@@ -4223,7 +4394,11 @@ impl<W: LayoutElement> Column<W> {
Some(transaction.clone())
};
tile.request_fullscreen(animate, transaction);
if matches!(sizing_mode, SizingMode::Fullscreen) {
tile.request_fullscreen(animate, transaction);
} else {
tile.request_maximized(self.parent_area.size, animate, transaction);
}
}
return;
}
@@ -4510,7 +4685,7 @@ impl<W: LayoutElement> Column<W> {
.map(NotNan::into_inner)
.unwrap();
if self.display_mode == ColumnDisplay::Tabbed && !self.is_fullscreen() {
if self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal() {
let extra_size = self.tab_indicator.extra_size(self.tiles.len(), self.scale);
tiles_width += extra_size.w;
}
@@ -4875,6 +5050,19 @@ impl<W: LayoutElement> Column<W> {
self.update_tile_sizes(true);
}
fn set_maximized(&mut self, maximize: bool) {
if self.is_pending_maximized == maximize {
return;
}
if maximize {
assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed);
}
self.is_pending_maximized = maximize;
self.update_tile_sizes(true);
}
fn set_column_display(&mut self, display: ColumnDisplay) {
if self.display_mode == display {
return;
@@ -4936,8 +5124,13 @@ impl<W: LayoutElement> Column<W> {
fn tiles_origin(&self) -> Point<f64, Logical> {
let mut origin = Point::from((0., 0.));
if self.is_fullscreen() {
return origin;
match self.sizing_mode() {
SizingMode::Normal => (),
SizingMode::Maximized => {
origin.y += self.parent_area.loc.y;
return origin;
}
SizingMode::Fullscreen => return origin,
}
origin.y += self.working_area.loc.y + self.options.layout.gaps;
@@ -5096,7 +5289,7 @@ impl<W: LayoutElement> Column<W> {
// Animate the appearance of the tab indicator.
if self.display_mode == ColumnDisplay::Tabbed
&& !self.is_fullscreen()
&& self.sizing_mode().is_normal()
&& self.tiles.len() == 1
&& !self.tab_indicator.config().hide_when_single_tab
{
@@ -5119,7 +5312,7 @@ impl<W: LayoutElement> Column<W> {
assert!(self.active_tile_idx < self.tiles.len());
assert_eq!(self.tiles.len(), self.data.len());
if self.is_pending_fullscreen {
if !self.pending_sizing_mode().is_normal() {
assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed);
}
@@ -5151,8 +5344,8 @@ impl<W: LayoutElement> Column<W> {
assert_eq!(self.clock, tile.clock);
assert_eq!(self.scale, tile.scale());
assert_eq!(
self.is_pending_fullscreen,
tile.window().is_pending_fullscreen()
self.pending_sizing_mode(),
tile.window().pending_sizing_mode()
);
assert_eq!(self.view_size, tile.view_size());
tile.verify_invariants();
@@ -5178,7 +5371,7 @@ impl<W: LayoutElement> Column<W> {
tile.tile_height_for_window_height(f64::from(requested_size.h));
let min_tile_height = f64::max(1., tile.min_size_nonfullscreen().h);
if !self.is_pending_fullscreen
if self.pending_sizing_mode().is_normal()
&& self.scale.round() == self.scale
&& working_size.h.round() == working_size.h
&& gaps.round() == gaps
+142 -21
View File
@@ -33,9 +33,9 @@ struct TestWindowInner {
forced_size: Cell<Option<Size<i32, Logical>>>,
min_size: Size<i32, Logical>,
max_size: Size<i32, Logical>,
pending_fullscreen: Cell<bool>,
pending_sizing_mode: Cell<SizingMode>,
pending_activated: Cell<bool>,
is_fullscreen: Cell<bool>,
sizing_mode: Cell<SizingMode>,
is_windowed_fullscreen: Cell<bool>,
is_pending_windowed_fullscreen: Cell<bool>,
animate_next_configure: Cell<bool>,
@@ -81,9 +81,9 @@ impl TestWindow {
forced_size: Cell::new(None),
min_size: params.min_max_size.0,
max_size: params.min_max_size.1,
pending_fullscreen: Cell::new(false),
pending_sizing_mode: Cell::new(SizingMode::Normal),
pending_activated: Cell::new(false),
is_fullscreen: Cell::new(false),
sizing_mode: Cell::new(SizingMode::Normal),
is_windowed_fullscreen: Cell::new(false),
is_pending_windowed_fullscreen: Cell::new(false),
animate_next_configure: Cell::new(false),
@@ -126,8 +126,8 @@ impl TestWindow {
self.0.animate_next_configure.set(false);
if self.0.is_fullscreen.get() != self.0.pending_fullscreen.get() {
self.0.is_fullscreen.set(self.0.pending_fullscreen.get());
if self.0.sizing_mode.get() != self.0.pending_sizing_mode.get() {
self.0.sizing_mode.set(self.0.pending_sizing_mode.get());
changed = true;
}
@@ -175,7 +175,7 @@ impl LayoutElement for TestWindow {
fn request_size(
&mut self,
size: Size<i32, Logical>,
is_fullscreen: bool,
mode: SizingMode,
_animate: bool,
_transaction: Option<Transaction>,
) {
@@ -184,9 +184,9 @@ impl LayoutElement for TestWindow {
self.0.animate_next_configure.set(true);
}
self.0.pending_fullscreen.set(is_fullscreen);
self.0.pending_sizing_mode.set(mode);
if is_fullscreen {
if mode.is_fullscreen() {
self.0.is_pending_windowed_fullscreen.set(false);
}
}
@@ -235,20 +235,12 @@ impl LayoutElement for TestWindow {
fn set_floating(&mut self, _floating: bool) {}
fn is_fullscreen(&self) -> bool {
if self.0.is_windowed_fullscreen.get() {
return false;
}
self.0.is_fullscreen.get()
fn sizing_mode(&self) -> SizingMode {
self.0.sizing_mode.get()
}
fn is_pending_fullscreen(&self) -> bool {
if self.0.is_pending_windowed_fullscreen.get() {
return false;
}
self.0.pending_fullscreen.get()
fn pending_sizing_mode(&self) -> SizingMode {
self.0.pending_sizing_mode.get()
}
fn requested_size(&self) -> Option<Size<i32, Logical>> {
@@ -585,6 +577,10 @@ enum Op {
id: Option<usize>,
},
MaximizeColumn,
MaximizeWindowToEdges {
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
id: Option<usize>,
},
SetColumnWidth(#[proptest(strategy = "arbitrary_size_change()")] SizeChange),
SetWindowWidth {
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
@@ -1310,6 +1306,16 @@ impl Op {
layout.toggle_window_height(id.as_ref(), false);
}
Op::MaximizeColumn => layout.toggle_full_width(),
Op::MaximizeWindowToEdges { id } => {
let id = id.or_else(|| layout.focus().map(|win| *win.id()));
let Some(id) = id else {
return;
};
if !layout.has_window(&id) {
return;
}
layout.toggle_maximized(&id);
}
Op::SetColumnWidth(change) => layout.set_column_width(change),
Op::SetWindowWidth { id, change } => {
let id = id.filter(|id| layout.has_window(id));
@@ -1674,6 +1680,9 @@ fn operations_dont_panic() {
Op::FullscreenWindow(1),
Op::FullscreenWindow(2),
Op::FullscreenWindow(3),
Op::MaximizeWindowToEdges { id: Some(1) },
Op::MaximizeWindowToEdges { id: Some(2) },
Op::MaximizeWindowToEdges { id: Some(3) },
Op::FocusColumnLeft,
Op::FocusColumnRight,
Op::FocusColumnRightOrFirst,
@@ -1829,6 +1838,9 @@ fn operations_from_starting_state_dont_panic() {
Op::FullscreenWindow(1),
Op::FullscreenWindow(2),
Op::FullscreenWindow(3),
Op::MaximizeWindowToEdges { id: Some(1) },
Op::MaximizeWindowToEdges { id: Some(2) },
Op::MaximizeWindowToEdges { id: Some(3) },
Op::SetFullscreenWindow {
window: 1,
is_fullscreen: false,
@@ -3492,6 +3504,115 @@ fn move_column_to_workspace_focus_false_on_floating_window() {
assert_eq!(monitors[0].active_workspace_idx, 0);
}
#[test]
fn restore_to_floating_persists_across_fullscreen_maximize() {
let ops = [
Op::AddOutput(1),
Op::AddWindow {
params: TestWindowParams::new(1),
},
Op::ToggleWindowFloating { id: None },
// Maximize then fullscreen.
Op::MaximizeWindowToEdges { id: None },
Op::FullscreenWindow(1),
// Unfullscreen.
Op::FullscreenWindow(1),
];
let mut layout = check_ops(ops);
// Unfullscreening should return the window to the maximized state.
let scrolling = layout.active_workspace().unwrap().scrolling();
assert!(scrolling.tiles().next().is_some());
let ops = [
// Unmaximize.
Op::MaximizeWindowToEdges { id: None },
];
check_ops_on_layout(&mut layout, ops);
// Unmaximize should return the window back to floating.
let scrolling = layout.active_workspace().unwrap().scrolling();
assert!(scrolling.tiles().next().is_none());
}
#[test]
fn unmaximize_during_fullscreen_does_not_float() {
let ops = [
Op::AddOutput(1),
Op::AddWindow {
params: TestWindowParams::new(1),
},
Op::ToggleWindowFloating { id: None },
// Maximize then fullscreen.
Op::MaximizeWindowToEdges { id: None },
Op::FullscreenWindow(1),
// Unmaximize.
Op::MaximizeWindowToEdges { id: None },
];
let mut layout = check_ops(ops);
// Unmaximize shouldn't have changed the window state since it's fullscreen.
let scrolling = layout.active_workspace().unwrap().scrolling();
assert!(scrolling.tiles().next().is_some());
let ops = [
// Unfullscreen.
Op::FullscreenWindow(1),
];
check_ops_on_layout(&mut layout, ops);
// Unfullscreen should return the window back to floating.
let scrolling = layout.active_workspace().unwrap().scrolling();
assert!(scrolling.tiles().next().is_none());
}
#[test]
fn move_column_to_workspace_maximize_and_fullscreen() {
let ops = [
Op::AddOutput(1),
Op::AddWindow {
params: TestWindowParams::new(1),
},
Op::MaximizeWindowToEdges { id: None },
Op::FullscreenWindow(1),
Op::MoveColumnToWorkspaceDown(true),
Op::FullscreenWindow(1),
];
let layout = check_ops(ops);
let (_, win) = layout.windows().next().unwrap();
// Unfullscreening should return to maximized because the window was maximized before.
assert_eq!(win.pending_sizing_mode(), SizingMode::Maximized);
}
#[test]
fn move_window_to_workspace_maximize_and_fullscreen() {
let ops = [
Op::AddOutput(1),
Op::AddWindow {
params: TestWindowParams::new(1),
},
Op::MaximizeWindowToEdges { id: None },
Op::FullscreenWindow(1),
Op::MoveWindowToWorkspaceDown(true),
Op::FullscreenWindow(1),
];
let layout = check_ops(ops);
let (_, win) = layout.windows().next().unwrap();
// Unfullscreening should return to maximized because the window was maximized before.
//
// FIXME: it currently doesn't because windows themselves can only be either fullscreen or
// maximized. So when a window is fullscreen, whether it is also maximized or not is stored in
// the column. MoveWindowToWorkspace removes the window from the column and this information is
// forgotten.
assert_eq!(win.pending_sizing_mode(), SizingMode::Normal);
}
fn parent_id_causes_loop(layout: &Layout<TestWindow>, id: usize, mut parent_id: usize) -> bool {
if parent_id == id {
return true;
+124 -47
View File
@@ -16,6 +16,7 @@ use super::{
SizeFrac, RESIZE_ANIMATION_THRESHOLD,
};
use crate::animation::{Animation, Clock};
use crate::layout::SizingMode;
use crate::niri_render_elements;
use crate::render_helpers::border::BorderRenderElement;
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
@@ -45,17 +46,17 @@ pub struct Tile<W: LayoutElement> {
/// The shadow around the window.
shadow: Shadow,
/// Whether this tile is fullscreen.
/// This tile's current sizing mode.
///
/// This will update only when the `window` actually goes fullscreen, rather than right away,
/// to avoid black backdrop flicker before the window has had a chance to resize.
is_fullscreen: bool,
/// This will update only when the `window` actually goes maximized or fullscreen, rather than
/// right away, to avoid black backdrop flicker before the window has had a chance to resize.
sizing_mode: SizingMode,
/// The black backdrop for fullscreen windows.
fullscreen_backdrop: SolidColorBuffer,
/// Whether the tile should float upon unfullscreening.
pub(super) unfullscreen_to_floating: bool,
pub(super) restore_to_floating: bool,
/// The size that the window should assume when going floating.
///
@@ -146,6 +147,8 @@ struct ResizeAnimation {
// Note that this can be set even if this specific resize is between two non-fullscreen states,
// for example when issuing a new resize during an unfullscreen resize.
fullscreen_progress: Option<Animation>,
// Similar to above but for fullscreen-or-maximized.
expanded_progress: Option<Animation>,
}
#[derive(Debug)]
@@ -178,16 +181,16 @@ impl<W: LayoutElement> Tile<W> {
let border_config = options.layout.border.merged_with(&rules.border);
let focus_ring_config = options.layout.focus_ring.merged_with(&rules.focus_ring);
let shadow_config = options.layout.shadow.merged_with(&rules.shadow);
let is_fullscreen = window.is_fullscreen();
let sizing_mode = window.sizing_mode();
Self {
window,
border: FocusRing::new(border_config.into()),
focus_ring: FocusRing::new(focus_ring_config),
shadow: Shadow::new(shadow_config),
is_fullscreen,
sizing_mode,
fullscreen_backdrop: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),
unfullscreen_to_floating: false,
restore_to_floating: false,
floating_window_size: None,
floating_pos: None,
floating_preset_width_idx: None,
@@ -248,8 +251,8 @@ impl<W: LayoutElement> Tile<W> {
}
pub fn update_window(&mut self) {
let was_fullscreen = self.is_fullscreen;
self.is_fullscreen = self.window.is_fullscreen();
let prev_sizing_mode = self.sizing_mode;
self.sizing_mode = self.window.sizing_mode();
if let Some(animate_from) = self.window.take_animation_snapshot() {
let params = if let Some(resize) = self.resize_animation.take() {
@@ -265,10 +268,10 @@ impl<W: LayoutElement> Tile<W> {
size.h = size_from.h + (size.h - size_from.h) * val;
let mut tile_size = animate_from.size;
if was_fullscreen {
if prev_sizing_mode.is_fullscreen() {
tile_size.w = f64::max(tile_size.w, self.view_size.w);
tile_size.h = f64::max(tile_size.h, self.view_size.h);
} else if !self.border.is_off() {
} else if prev_sizing_mode.is_normal() && !self.border.is_off() {
let width = self.border.width();
tile_size.w += width * 2.;
tile_size.h += width * 2.;
@@ -280,29 +283,56 @@ impl<W: LayoutElement> Tile<W> {
let fullscreen_from = resize
.fullscreen_progress
.map(|anim| anim.clamped_value().clamp(0., 1.))
.unwrap_or(if was_fullscreen { 1. } else { 0. });
.unwrap_or(if prev_sizing_mode.is_fullscreen() {
1.
} else {
0.
});
let expanded_from = resize
.expanded_progress
.map(|anim| anim.clamped_value().clamp(0., 1.))
.unwrap_or(if prev_sizing_mode.is_normal() { 0. } else { 1. });
// Also try to reuse the existing offscreen buffer if we have one.
(size, tile_size, fullscreen_from, resize.offscreen)
(
size,
tile_size,
fullscreen_from,
expanded_from,
resize.offscreen,
)
} else {
let size = animate_from.size;
// Compute like in tile_size().
let mut tile_size = size;
if was_fullscreen {
if prev_sizing_mode.is_fullscreen() {
tile_size.w = f64::max(tile_size.w, self.view_size.w);
tile_size.h = f64::max(tile_size.h, self.view_size.h);
} else if !self.border.is_off() {
} else if prev_sizing_mode.is_normal() && !self.border.is_off() {
let width = self.border.width();
tile_size.w += width * 2.;
tile_size.h += width * 2.;
}
let fullscreen_from = if was_fullscreen { 1. } else { 0. };
let fullscreen_from = if prev_sizing_mode.is_fullscreen() {
1.
} else {
0.
};
(size, tile_size, fullscreen_from, OffscreenBuffer::default())
let expanded_from = if prev_sizing_mode.is_normal() { 0. } else { 1. };
(
size,
tile_size,
fullscreen_from,
expanded_from,
OffscreenBuffer::default(),
)
};
let (size_from, tile_size_from, fullscreen_from, offscreen) = params;
let (size_from, tile_size_from, fullscreen_from, expanded_from, offscreen) = params;
let change = self.window.size().to_f64().to_point() - size_from.to_point();
let change = f64::max(change.x.abs(), change.y.abs());
@@ -318,9 +348,16 @@ impl<W: LayoutElement> Tile<W> {
self.options.animations.window_resize.anim,
);
let fullscreen_to = if self.is_fullscreen { 1. } else { 0. };
let fullscreen_to = if self.sizing_mode.is_fullscreen() {
1.
} else {
0.
};
let expanded_to = if self.sizing_mode.is_normal() { 0. } else { 1. };
let fullscreen_progress = (fullscreen_from != fullscreen_to)
.then(|| anim.restarted(fullscreen_from, fullscreen_to, 0.));
let expanded_progress = (expanded_from != expanded_to)
.then(|| anim.restarted(expanded_from, expanded_to, 0.));
self.resize_animation = Some(ResizeAnimation {
anim,
@@ -329,6 +366,7 @@ impl<W: LayoutElement> Tile<W> {
offscreen,
tile_size_from,
fullscreen_progress,
expanded_progress,
});
} else {
self.resize_animation = None;
@@ -406,7 +444,7 @@ impl<W: LayoutElement> Tile<W> {
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
let rules = self.window.rules();
let animated_tile_size = self.animated_tile_size();
let fullscreen_progress = self.fullscreen_progress();
let expanded_progress = self.expanded_progress();
let draw_border_with_background = rules
.draw_border_with_background
@@ -425,7 +463,7 @@ impl<W: LayoutElement> Tile<W> {
.map_or(CornerRadius::default(), |radius| {
radius.expanded_by(border_width as f32)
})
.scaled_by(1. - fullscreen_progress as f32);
.scaled_by(1. - expanded_progress as f32);
self.border.update_render_elements(
border_window_size,
is_active,
@@ -437,7 +475,7 @@ impl<W: LayoutElement> Tile<W> {
),
radius,
self.scale,
1. - fullscreen_progress as f32,
1. - expanded_progress as f32,
);
let radius = if self.visual_border_width().is_some() {
@@ -446,17 +484,17 @@ impl<W: LayoutElement> Tile<W> {
rules
.geometry_corner_radius
.unwrap_or_default()
.scaled_by(1. - fullscreen_progress as f32)
.scaled_by(1. - expanded_progress as f32)
};
self.shadow.update_render_elements(
animated_tile_size,
is_active,
radius,
self.scale,
1. - fullscreen_progress as f32,
1. - expanded_progress as f32,
);
let draw_focus_ring_with_background = if self.border.is_off() && fullscreen_progress < 1. {
let draw_focus_ring_with_background = if self.border.is_off() {
draw_border_with_background
} else {
false
@@ -470,7 +508,7 @@ impl<W: LayoutElement> Tile<W> {
view_rect,
radius,
self.scale,
1. - fullscreen_progress as f32,
1. - expanded_progress as f32,
);
self.fullscreen_backdrop.resize(animated_tile_size);
@@ -609,8 +647,8 @@ impl<W: LayoutElement> Tile<W> {
&mut self.window
}
pub fn is_fullscreen(&self) -> bool {
self.is_fullscreen
pub fn sizing_mode(&self) -> SizingMode {
self.sizing_mode
}
fn fullscreen_progress(&self) -> f64 {
@@ -620,16 +658,30 @@ impl<W: LayoutElement> Tile<W> {
}
}
if self.is_fullscreen {
if self.sizing_mode.is_fullscreen() {
1.
} else {
0.
}
}
fn expanded_progress(&self) -> f64 {
if let Some(resize) = &self.resize_animation {
if let Some(anim) = &resize.expanded_progress {
return anim.clamped_value().clamp(0., 1.);
}
}
if self.sizing_mode.is_normal() {
0.
} else {
1.
}
}
/// Returns `None` if the border is hidden and `Some(width)` if it should be shown.
pub fn effective_border_width(&self) -> Option<f64> {
if self.is_fullscreen {
if !self.sizing_mode.is_normal() {
return None;
}
@@ -645,10 +697,10 @@ impl<W: LayoutElement> Tile<W> {
return None;
}
let fullscreen_progress = self.fullscreen_progress();
let expanded_progress = self.expanded_progress();
// Only hide the border when fully fullscreen to avoid jarring border appearance.
if fullscreen_progress == 1. {
// Only hide the border when fully expanded to avoid jarring border appearance.
if expanded_progress == 1. {
return None;
}
@@ -688,7 +740,7 @@ impl<W: LayoutElement> Tile<W> {
pub fn tile_size(&self) -> Size<f64, Logical> {
let mut size = self.window_size();
if self.is_fullscreen {
if self.sizing_mode.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.view_size.w);
@@ -707,7 +759,7 @@ impl<W: LayoutElement> Tile<W> {
pub fn tile_expected_or_current_size(&self) -> Size<f64, Logical> {
let mut size = self.window_expected_or_current_size();
if self.is_fullscreen {
if self.sizing_mode.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.view_size.w);
@@ -836,8 +888,12 @@ impl<W: LayoutElement> Tile<W> {
// The size request has to be i32 unfortunately, due to Wayland. We floor here instead of
// round to avoid situations where proportionally-sized columns don't fit on the screen
// exactly.
self.window
.request_size(size.to_i32_floor(), false, animate, transaction);
self.window.request_size(
size.to_i32_floor(),
SizingMode::Normal,
animate,
transaction,
);
}
pub fn tile_width_for_window_width(&self, size: f64) -> f64 {
@@ -872,9 +928,27 @@ impl<W: LayoutElement> Tile<W> {
}
}
pub fn request_maximized(
&mut self,
size: Size<f64, Logical>,
animate: bool,
transaction: Option<Transaction>,
) {
self.window.request_size(
size.to_i32_round(),
SizingMode::Maximized,
animate,
transaction,
);
}
pub fn request_fullscreen(&mut self, animate: bool, transaction: Option<Transaction>) {
self.window
.request_size(self.view_size.to_i32_round(), true, animate, transaction);
self.window.request_size(
self.view_size.to_i32_round(),
SizingMode::Fullscreen,
animate,
transaction,
);
}
pub fn min_size_nonfullscreen(&self) -> Size<f64, Logical> {
@@ -933,6 +1007,7 @@ impl<W: LayoutElement> Tile<W> {
let scale = Scale::from(self.scale);
let fullscreen_progress = self.fullscreen_progress();
let expanded_progress = self.expanded_progress();
let win_alpha = if self.window.is_ignoring_opacity_window_rule() {
1.
@@ -968,7 +1043,7 @@ impl<W: LayoutElement> Tile<W> {
let radius = rules
.geometry_corner_radius
.unwrap_or_default()
.scaled_by(1. - fullscreen_progress as f32);
.scaled_by(1. - expanded_progress as f32);
// If we're resizing, try to render a shader, or a fallback.
let mut resize_shader = None;
@@ -1153,7 +1228,7 @@ impl<W: LayoutElement> Tile<W> {
.map_or(CornerRadius::default(), |radius| {
radius.expanded_by(border_width as f32)
})
.scaled_by(1. - fullscreen_progress as f32);
.scaled_by(1. - expanded_progress as f32);
let size = self.fullscreen_backdrop.size();
let color = self.fullscreen_backdrop.color();
@@ -1191,13 +1266,15 @@ impl<W: LayoutElement> Tile<W> {
});
let rv = rv.chain(elem.into_iter().flatten());
// Hide the focus ring when fullscreened. It's not normally visible anyway due to being
// outside the monitor, but it is visible in the overview (which is a bit weird).
let elem = (focus_ring && fullscreen_progress < 1.)
// Hide the focus ring when maximized/fullscreened. It's not normally visible anyway due to
// being outside the monitor or obscured by a solid colored bar, but it is visible under
// semitransparent bars in maximized state (which is a bit weird) and in the overview (also
// a bit weird).
let elem = (focus_ring && expanded_progress < 1.)
.then(|| self.focus_ring.render(renderer, location).map(Into::into));
let rv = rv.chain(elem.into_iter().flatten());
let elem = (fullscreen_progress < 1.)
let elem = (expanded_progress < 1.)
.then(|| self.shadow.render(renderer, location).map(Into::into));
rv.chain(elem.into_iter().flatten())
}
@@ -1328,7 +1405,7 @@ impl<W: LayoutElement> Tile<W> {
pub fn verify_invariants(&self) {
use approx::assert_abs_diff_eq;
assert_eq!(self.is_fullscreen, self.window.is_fullscreen());
assert_eq!(self.sizing_mode, self.window.sizing_mode());
let scale = self.scale;
let size = self.tile_size();
+106 -25
View File
@@ -611,16 +611,16 @@ impl<W: LayoutElement> Workspace<W> {
is_floating: bool,
) {
self.enter_output_for_window(tile.window());
tile.unfullscreen_to_floating = is_floating;
tile.restore_to_floating = is_floating;
match target {
WorkspaceAddWindowTarget::Auto => {
// Don't steal focus from an active fullscreen window.
let activate = activate.map_smart(|| !self.is_active_pending_fullscreen());
// If the tile is pending fullscreen, open it in the scrolling layout where it can
// go fullscreen.
if is_floating && !tile.window().is_pending_fullscreen() {
// If the tile is pending maximized or fullscreen, open it in the scrolling layout
// where it can do that.
if is_floating && tile.window().pending_sizing_mode().is_normal() {
self.floating.add_tile(tile, activate);
if activate || self.scrolling.is_empty() {
@@ -649,7 +649,7 @@ impl<W: LayoutElement> Workspace<W> {
let floating_has_window = self.floating.has_window(next_to);
if is_floating && !tile.window().is_pending_fullscreen() {
if is_floating && tile.window().pending_sizing_mode().is_normal() {
if floating_has_window {
self.floating.add_tile_above(next_to, tile, activate);
} else {
@@ -869,6 +869,8 @@ impl<W: LayoutElement> Workspace<W> {
toplevel.with_pending_state(|state| {
if state.states.contains(xdg_toplevel::State::Fullscreen) {
state.size = Some(self.view_size.to_i32_round());
} else if state.states.contains(xdg_toplevel::State::Maximized) {
state.size = Some(self.working_area.size.to_i32_round());
} else {
let size =
self.new_window_size(width, height, is_floating, rules, (min_size, max_size));
@@ -1257,10 +1259,10 @@ impl<W: LayoutElement> Workspace<W> {
}
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
let mut unfullscreen_to_floating = false;
let mut restore_to_floating = false;
if self.floating.has_window(window) {
if is_fullscreen {
unfullscreen_to_floating = true;
restore_to_floating = true;
self.toggle_window_floating(Some(window));
} else {
// Floating windows are never fullscreen, so this is an unfullscreen request for an
@@ -1271,30 +1273,44 @@ impl<W: LayoutElement> Workspace<W> {
// The window is in the scrolling layout and we're requesting an unfullscreen. If it is
// indeed fullscreen (i.e. this isn't a duplicate unfullscreen request), then we may
// need to unfullscreen into floating.
let tile = self
let col = self
.scrolling
.tiles()
.find(|tile| tile.window().id() == window)
.columns()
.find(|col| col.contains(window))
.unwrap();
if tile.window().is_pending_fullscreen() && tile.unfullscreen_to_floating {
// Unfullscreen and float in one call so it has a chance to notice and request a
// (0, 0) size, rather than the scrolling column size.
self.toggle_window_floating(Some(window));
return;
// When going from fullscreen to maximized, don't consider restore_to_floating yet.
if col.is_pending_fullscreen() && !col.is_pending_maximized() {
let (tile, _) = col
.tiles()
.find(|(tile, _)| tile.window().id() == window)
.unwrap();
if tile.restore_to_floating {
// Unfullscreen and float in one call so it has a chance to notice and request a
// (0, 0) size, rather than the scrolling column size.
self.toggle_window_floating(Some(window));
return;
}
}
}
let changed = self.scrolling.set_fullscreen(window, is_fullscreen);
let tile = self
.scrolling
.tiles()
.find(|tile| tile.window().id() == window)
.unwrap();
let was_normal = tile.window().pending_sizing_mode().is_normal();
// When going to fullscreen, remember if we should unfullscreen to floating.
if changed && is_fullscreen {
let tile = self
.scrolling
.tiles_mut()
.find(|tile| tile.window().id() == window)
.unwrap();
self.scrolling.set_fullscreen(window, is_fullscreen);
tile.unfullscreen_to_floating = unfullscreen_to_floating;
// When going from normal to fullscreen, remember if we should unfullscreen to floating.
let tile = self
.scrolling
.tiles_mut()
.find(|tile| tile.window().id() == window)
.unwrap();
if was_normal && !tile.window().pending_sizing_mode().is_normal() {
tile.restore_to_floating = restore_to_floating;
}
}
@@ -1303,10 +1319,75 @@ impl<W: LayoutElement> Workspace<W> {
.tiles()
.find(|tile| tile.window().id() == window)
.unwrap();
let current = tile.window().is_pending_fullscreen();
let current = tile.window().pending_sizing_mode().is_fullscreen();
self.set_fullscreen(window, !current);
}
pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) {
let mut restore_to_floating = false;
if self.floating.has_window(window) {
if maximize {
restore_to_floating = true;
self.toggle_window_floating(Some(window));
} else {
// Floating windows are never maximized, so this is an unmaximize request for an
// already unmaximized window.
return;
}
} else if !maximize {
// The window is in the scrolling layout and we're requesting to unmaximize. If it is
// indeed maximized (i.e. this isn't a duplicate unmaximize request), then we may
// need to unmaximize into floating.
let tile = self
.scrolling
.tiles()
.find(|tile| tile.window().id() == window)
.unwrap();
// The tile cannot unmaximize into fullscreen (pending_sizing_mode() will be fullscreen
// in that case and not maximized), so this check works.
if tile.window().pending_sizing_mode().is_maximized() && tile.restore_to_floating {
// Unmaximize and float in one call so it has a chance to notice and request a
// (0, 0) size, rather than the scrolling column size.
self.toggle_window_floating(Some(window));
return;
}
}
let tile = self
.scrolling
.tiles()
.find(|tile| tile.window().id() == window)
.unwrap();
let was_normal = tile.window().pending_sizing_mode().is_normal();
self.scrolling.set_maximized(window, maximize);
// When going from normal to maximized, remember if we should unmaximize to floating.
let tile = self
.scrolling
.tiles_mut()
.find(|tile| tile.window().id() == window)
.unwrap();
if was_normal && !tile.window().pending_sizing_mode().is_normal() {
tile.restore_to_floating = restore_to_floating;
}
}
pub fn toggle_maximized(&mut self, window: &W::Id) {
let mut current = false;
// We have to check the column property in case the window is in the scrolling layout and
// both maximized and fullscreen. In this case, only the column knows whether it's
// maximized.
//
// In the floating layout, windows cannot be maximized.
if let Some(col) = self.scrolling.columns().find(|col| col.contains(window)) {
current = col.is_pending_maximized();
}
self.set_maximized(window, !current);
}
pub fn toggle_window_floating(&mut self, id: Option<&W::Id>) {
let active_id = self.active_window().map(|win| win.id().clone());
let target_is_active = id.map_or(true, |id| Some(id) == active_id.as_ref());
+7 -6
View File
@@ -2339,7 +2339,7 @@ impl Niri {
let compositor_state = CompositorState::new_v6::<State>(&display_handle);
let xdg_shell_state = XdgShellState::new_with_capabilities::<State>(
&display_handle,
[WmCapabilities::Fullscreen],
[WmCapabilities::Fullscreen, WmCapabilities::Maximize],
);
let xdg_decoration_state =
XdgDecorationState::new_with_filter::<State, _>(&display_handle, |client| {
@@ -5545,11 +5545,12 @@ impl Niri {
let _span = tracy_client::span!("Niri::screenshot_window");
let scale = Scale::from(output.current_scale().fractional_scale());
let alpha = if mapped.is_fullscreen() || mapped.is_ignoring_opacity_window_rule() {
1.
} else {
mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.)
};
let alpha =
if mapped.sizing_mode().is_fullscreen() || mapped.is_ignoring_opacity_window_rule() {
1.
} else {
mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.)
};
// FIXME: pointer.
let elements = mapped.render(
renderer,
+6 -2
View File
@@ -37,6 +37,8 @@ pub trait ForeignToplevelHandler {
fn close(&mut self, wl_surface: WlSurface);
fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>);
fn unset_fullscreen(&mut self, wl_surface: WlSurface);
fn set_maximized(&mut self, wl_surface: WlSurface);
fn unset_maximized(&mut self, wl_surface: WlSurface);
}
struct ToplevelData {
@@ -388,8 +390,10 @@ where
let surface = surface.clone();
match request {
zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => (),
zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => (),
zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => state.set_maximized(surface),
zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => {
state.unset_maximized(surface)
}
zwlr_foreign_toplevel_handle_v1::Request::SetMinimized => (),
zwlr_foreign_toplevel_handle_v1::Request::UnsetMinimized => (),
zwlr_foreign_toplevel_handle_v1::Request::Activate { .. } => {
+8
View File
@@ -357,6 +357,14 @@ impl Window {
self.xdg_toplevel.unset_fullscreen();
}
pub fn set_maximized(&self) {
self.xdg_toplevel.set_maximized();
}
pub fn unset_maximized(&self) {
self.xdg_toplevel.unset_maximized();
}
pub fn set_parent(&self, parent: Option<&XdgToplevel>) {
self.xdg_toplevel.set_parent(parent);
}
+279
View File
@@ -458,6 +458,55 @@ fn interactive_move_unfullscreen_to_floating_restores_size() {
);
}
#[test]
fn interactive_move_unmaximize_to_floating_restores_size() {
let (mut f, id, surface) = set_up();
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// Change size while we're floating and commit.
let window = f.client(id).window(&surface);
window.set_size(200, 200);
window.ack_last_and_commit();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
let niri = f.niri();
let mapped = niri.layout.windows().next().unwrap().1;
let window = mapped.window.clone();
niri.layout.set_maximized(&window, true);
f.double_roundtrip(id);
// This should request a maximized size.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]"
);
// Start an interactive move which causes an unmaximize into floating.
let output = f.niri_output(1);
let niri = f.niri();
let mapped = niri.layout.windows().next().unwrap().1;
let window = mapped.window.clone();
niri.layout
.interactive_move_begin(window.clone(), &output, Point::default());
niri.layout.interactive_move_update(
&window,
Point::from((1000., 0.)),
output,
Point::default(),
);
f.double_roundtrip(id);
// This should request the stored floating size (200 × 200).
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn resize_during_interactive_move_propagates_to_floating() {
let (mut f, id, surface) = set_up();
@@ -791,6 +840,66 @@ fn floating_doesnt_store_fullscreen_size() {
);
}
#[test]
fn floating_doesnt_store_maximized_size() {
let mut f = Fixture::new();
f.add_output(1, (1920, 1080));
f.add_output(2, (1280, 720));
// Open a window maximized.
let id = f.add_client();
let window = f.client(id).create_window();
let surface = window.surface.clone();
window.set_maximized();
window.commit();
f.roundtrip(id);
let window = f.client(id).window(&surface);
window.attach_new_buffer();
window.set_size(1920, 1080);
window.ack_last_and_commit();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
// Make it floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// This should request 0 × 0 to unmaximize.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 0 × 0, bounds: 1920 × 1080, states: [Activated]"
);
// Without committing, make it tiling again. We never committed while floating, so there's no
// floating size to remember.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// This should request the tiled size.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1048, bounds: 1888 × 1048, states: [Activated]"
);
// Commit in response.
let window = f.client(id).window(&surface);
window.set_size(100, 100);
window.ack_last_and_commit();
f.roundtrip(id);
// Make the window floating again.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// This shouldn't request any size change, particularly not the maximized size.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 100 × 100, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn floating_respects_non_fixed_min_max_rule() {
let config = r##"
@@ -888,6 +997,33 @@ fn unfullscreen_to_floating_doesnt_send_extra_configure() {
);
}
#[test]
fn unmaximize_to_floating_doesnt_send_extra_configure() {
let (mut f, id, surface) = set_up();
// Make it floating.
f.niri().layout.toggle_window_floating(None);
f.roundtrip(id);
// Maximize.
let window = f.client(id).window(&surface);
window.set_maximized();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
// Unmaximzie via the window request which requires a configure response.
let window = f.client(id).window(&surface);
window.unset_maximized();
f.double_roundtrip(id);
// This should configure only once and not twice.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn unfullscreen_to_same_size_floating() {
let (mut f, id, surface) = set_up();
@@ -926,6 +1062,44 @@ fn unfullscreen_to_same_size_floating() {
);
}
#[test]
fn unmaximize_to_same_size_floating() {
let (mut f, id, surface) = set_up();
// Make it floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// Change size to the same as maximized, make niri remember it.
let window = f.client(id).window(&surface);
window.set_size(1920, 1080);
window.ack_last_and_commit();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
// Maximize.
let window = f.client(id).window(&surface);
window.set_maximized();
f.double_roundtrip(id);
// The maximize configure.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]"
);
// Unmaximize into floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// We should see a configure with the same size and no maximized state.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn unfullscreen_to_same_size_windowed_fullscreen_floating() {
let (mut f, id, surface) = set_up();
@@ -967,6 +1141,67 @@ fn unfullscreen_to_same_size_windowed_fullscreen_floating() {
);
}
#[test]
fn unmaximize_to_same_size_windowed_fullscreen_floating() {
let (mut f, id, surface) = set_up();
let mapped = f.niri().layout.windows().next().unwrap().1;
let window_id = mapped.window.clone();
// Make it floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// Change size to the same as maximized, make niri remember it.
let window = f.client(id).window(&surface);
window.set_size(1920, 1080);
window.ack_last_and_commit();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
// Maximize.
let window = f.client(id).window(&surface);
window.set_maximized();
f.double_roundtrip(id);
// The maximize configure.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]"
);
// Enable windowed-fullscreen.
f.niri().layout.toggle_windowed_fullscreen(&window_id);
f.double_roundtrip(id);
// The windowed-fullscreen configure.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]"
);
// Go back to windowed-fullscreen floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// Should send configure because the bounds have changed.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Fullscreen]"
);
// Disable windowed-fullscreen.
f.niri().layout.toggle_windowed_fullscreen(&window_id);
f.double_roundtrip(id);
// Should send configure dropping the Fullscreen state.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn unfullscreen_to_same_size_same_bounds_floating() {
let config = r##"
@@ -1011,6 +1246,50 @@ layout {
);
}
#[test]
fn unmaximize_to_same_size_same_bounds_floating() {
let config = r##"
layout {
gaps 0
}
"##;
let config = Config::parse_mem(config).unwrap();
let (mut f, id, surface) = set_up_with_config(config);
// Make it floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// Change size to the same as fullscreen, make niri remember it.
let window = f.client(id).window(&surface);
window.set_size(1920, 1080);
window.ack_last_and_commit();
f.double_roundtrip(id);
let _ = f.client(id).window(&surface).recent_configures();
// Maximize.
let window = f.client(id).window(&surface);
window.set_maximized();
f.double_roundtrip(id);
// The maximize configure.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Maximized]"
);
// Unmaximize into floating.
f.niri().layout.toggle_window_floating(None);
f.double_roundtrip(id);
// We should see a configure with the same size and no Maximized state.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn repeated_size_request() {
let (mut f, id, surface) = set_up();
+41 -1
View File
@@ -127,7 +127,7 @@ fn windowed_fullscreen_chain() {
let mapped = f.niri().layout.windows().next().unwrap().1;
format!(
"fs {}, wfs {}",
mapped.is_fullscreen(),
mapped.sizing_mode().is_fullscreen(),
mapped.is_windowed_fullscreen()
)
};
@@ -255,3 +255,43 @@ fn interactive_move_unfullscreen_to_scrolling_restores_size() {
@"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]"
);
}
#[test]
fn interactive_move_unmaximize_to_scrolling_restores_size() {
let (mut f, id, surface) = set_up();
let _ = f.client(id).window(&surface).recent_configures();
let niri = f.niri();
let mapped = niri.layout.windows().next().unwrap().1;
let window = mapped.window.clone();
niri.layout.set_maximized(&window, true);
f.double_roundtrip(id);
// This should request a maximized size.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]"
);
// Start an interactive move which causes an unmaximize.
let output = f.niri_output(1);
let niri = f.niri();
let mapped = niri.layout.windows().next().unwrap().1;
let window = mapped.window.clone();
niri.layout
.interactive_move_begin(window.clone(), &output, Point::default());
niri.layout.interactive_move_update(
&window,
Point::from((1000., 0.)),
output,
Point::default(),
);
f.double_roundtrip(id);
// This should request the tiled size.
assert_snapshot!(
f.client(id).window(&surface).format_recent_configures(),
@"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]"
);
}
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "config:\nwindow-rule {\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,18 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,12 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "config:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,18 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,17 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,17 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,17 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,12 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "config:\nwindow-rule {\n open-fullscreen false\n open-maximized-to-edges true\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,18 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,17 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,12 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BN\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nwant maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,14 @@
---
source: src/tests/window_opening.rs
description: "want maximized: A\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,11 @@
---
source: src/tests/window_opening.rs
description: "want maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: []
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,13 @@
---
source: src/tests/window_opening.rs
description: "want maximized: B\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "want maximized: BU\nconfig:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,10 @@
---
source: src/tests/window_opening.rs
description: "config:\nwindow-rule {\n open-fullscreen false\n}"
expression: snapshot
---
initial configure:
size: 616 × 688, bounds: 1248 × 688, states: []
post-map configures:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,18 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: A\nconfig:\nwindow-rule {\n open-fullscreen true\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]
unmaximize configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
@@ -0,0 +1,15 @@
---
source: src/tests/window_opening.rs
description: "want fullscreen: AN\nwant maximized: AU\nconfig:\nwindow-rule {\n open-fullscreen true\n open-maximized-to-edges false\n}"
expression: snapshot
---
initial configure:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
post-map configures:
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
unfullscreen configure:
size: 616 × 688, bounds: 1248 × 688, states: [Activated]

Some files were not shown because too many files have changed in this diff Show More