mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Initial WIP floating window implementation
This commit is contained in:
@@ -983,6 +983,8 @@ pub struct WindowRule {
|
|||||||
pub open_maximized: Option<bool>,
|
pub open_maximized: Option<bool>,
|
||||||
#[knuffel(child, unwrap(argument))]
|
#[knuffel(child, unwrap(argument))]
|
||||||
pub open_fullscreen: Option<bool>,
|
pub open_fullscreen: Option<bool>,
|
||||||
|
#[knuffel(child, unwrap(argument))]
|
||||||
|
pub open_floating: Option<bool>,
|
||||||
|
|
||||||
// Rules applied dynamically.
|
// Rules applied dynamically.
|
||||||
#[knuffel(child, unwrap(argument))]
|
#[knuffel(child, unwrap(argument))]
|
||||||
@@ -1254,6 +1256,10 @@ pub enum Action {
|
|||||||
MoveWorkspaceToMonitorRight,
|
MoveWorkspaceToMonitorRight,
|
||||||
MoveWorkspaceToMonitorDown,
|
MoveWorkspaceToMonitorDown,
|
||||||
MoveWorkspaceToMonitorUp,
|
MoveWorkspaceToMonitorUp,
|
||||||
|
ToggleWindowFloating,
|
||||||
|
#[knuffel(skip)]
|
||||||
|
ToggleWindowFloatingById(u64),
|
||||||
|
SwitchFocusBetweenFloatingAndTiling,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<niri_ipc::Action> for Action {
|
impl From<niri_ipc::Action> for Action {
|
||||||
@@ -1386,6 +1392,13 @@ impl From<niri_ipc::Action> for Action {
|
|||||||
niri_ipc::Action::ToggleDebugTint {} => Self::ToggleDebugTint,
|
niri_ipc::Action::ToggleDebugTint {} => Self::ToggleDebugTint,
|
||||||
niri_ipc::Action::DebugToggleOpaqueRegions {} => Self::DebugToggleOpaqueRegions,
|
niri_ipc::Action::DebugToggleOpaqueRegions {} => Self::DebugToggleOpaqueRegions,
|
||||||
niri_ipc::Action::DebugToggleDamage {} => Self::DebugToggleDamage,
|
niri_ipc::Action::DebugToggleDamage {} => Self::DebugToggleDamage,
|
||||||
|
niri_ipc::Action::ToggleWindowFloating { id: None } => Self::ToggleWindowFloating,
|
||||||
|
niri_ipc::Action::ToggleWindowFloating { id: Some(id) } => {
|
||||||
|
Self::ToggleWindowFloatingById(id)
|
||||||
|
}
|
||||||
|
niri_ipc::Action::SwitchFocusBetweenFloatingAndTiling {} => {
|
||||||
|
Self::SwitchFocusBetweenFloatingAndTiling
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3128,6 +3141,7 @@ mod tests {
|
|||||||
open-on-output "eDP-1"
|
open-on-output "eDP-1"
|
||||||
open-maximized true
|
open-maximized true
|
||||||
open-fullscreen false
|
open-fullscreen false
|
||||||
|
open-floating false
|
||||||
|
|
||||||
focus-ring {
|
focus-ring {
|
||||||
off
|
off
|
||||||
@@ -3406,6 +3420,7 @@ mod tests {
|
|||||||
open_on_output: Some("eDP-1".to_owned()),
|
open_on_output: Some("eDP-1".to_owned()),
|
||||||
open_maximized: Some(true),
|
open_maximized: Some(true),
|
||||||
open_fullscreen: Some(false),
|
open_fullscreen: Some(false),
|
||||||
|
open_floating: Some(false),
|
||||||
focus_ring: BorderRule {
|
focus_ring: BorderRule {
|
||||||
off: true,
|
off: true,
|
||||||
width: Some(FloatOrInt(3.)),
|
width: Some(FloatOrInt(3.)),
|
||||||
|
|||||||
@@ -430,6 +430,16 @@ pub enum Action {
|
|||||||
DebugToggleOpaqueRegions {},
|
DebugToggleOpaqueRegions {},
|
||||||
/// Toggle visualization of output damage.
|
/// Toggle visualization of output damage.
|
||||||
DebugToggleDamage {},
|
DebugToggleDamage {},
|
||||||
|
/// Move the focused window between the floating and the tiling layout.
|
||||||
|
ToggleWindowFloating {
|
||||||
|
/// Id of the window to move.
|
||||||
|
///
|
||||||
|
/// If `None`, uses the focused window.
|
||||||
|
#[cfg_attr(feature = "clap", arg(long))]
|
||||||
|
id: Option<u64>,
|
||||||
|
},
|
||||||
|
/// Toggles the focus between the floating and the tiling layout.
|
||||||
|
SwitchFocusBetweenFloatingAndTiling {},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change in window or column size.
|
/// Change in window or column size.
|
||||||
|
|||||||
@@ -159,11 +159,20 @@ impl Layout {
|
|||||||
|
|
||||||
fn add_window(&mut self, mut window: TestWindow, width: Option<ColumnWidth>) {
|
fn add_window(&mut self, mut window: TestWindow, width: Option<ColumnWidth>) {
|
||||||
let ws = self.layout.active_workspace().unwrap();
|
let ws = self.layout.active_workspace().unwrap();
|
||||||
window.request_size(ws.new_window_size(width, window.rules()), false, None);
|
window.request_size(
|
||||||
|
ws.new_window_size(width, false, window.rules()),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
window.communicate();
|
window.communicate();
|
||||||
|
|
||||||
self.layout
|
self.layout.add_window(
|
||||||
.add_window(window.clone(), width, false, ActivateWindow::default());
|
window.clone(),
|
||||||
|
width,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
ActivateWindow::default(),
|
||||||
|
);
|
||||||
self.windows.push(window);
|
self.windows.push(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,11 +183,15 @@ impl Layout {
|
|||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
) {
|
) {
|
||||||
let ws = self.layout.active_workspace().unwrap();
|
let ws = self.layout.active_workspace().unwrap();
|
||||||
window.request_size(ws.new_window_size(width, window.rules()), false, None);
|
window.request_size(
|
||||||
|
ws.new_window_size(width, false, window.rules()),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
window.communicate();
|
window.communicate();
|
||||||
|
|
||||||
self.layout
|
self.layout
|
||||||
.add_window_right_of(right_of.id(), window.clone(), width, false);
|
.add_window_right_of(right_of.id(), window.clone(), width, false, false);
|
||||||
self.windows.push(window);
|
self.windows.push(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -474,6 +474,10 @@ binds {
|
|||||||
Mod+Shift+Minus { set-window-height "-10%"; }
|
Mod+Shift+Minus { set-window-height "-10%"; }
|
||||||
Mod+Shift+Equal { set-window-height "+10%"; }
|
Mod+Shift+Equal { set-window-height "+10%"; }
|
||||||
|
|
||||||
|
// Move the focused window between the floating and the tiling layout.
|
||||||
|
Mod+V { toggle-window-floating; }
|
||||||
|
Mod+Shift+V { switch-focus-between-floating-and-tiling; }
|
||||||
|
|
||||||
// Actions to switch layouts.
|
// Actions to switch layouts.
|
||||||
// Note: if you uncomment these, make sure you do NOT have
|
// Note: if you uncomment these, make sure you do NOT have
|
||||||
// a matching layout switch hotkey configured in xkb options above.
|
// a matching layout switch hotkey configured in xkb options above.
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ impl CompositorHandler for State {
|
|||||||
(ResolvedWindowRules::empty(), None, false, None, None)
|
(ResolvedWindowRules::empty(), None, false, None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// moment, that is here.
|
||||||
|
let is_floating = rules.compute_open_floating(toplevel);
|
||||||
|
|
||||||
let parent = toplevel
|
let parent = toplevel
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(|parent| self.niri.layout.find_window_and_output(&parent))
|
.and_then(|parent| self.niri.layout.find_window_and_output(&parent))
|
||||||
@@ -160,15 +165,20 @@ impl CompositorHandler for State {
|
|||||||
//
|
//
|
||||||
// FIXME: do we want to use activate here? How do we want things to behave
|
// FIXME: do we want to use activate here? How do we want things to behave
|
||||||
// exactly?
|
// exactly?
|
||||||
self.niri
|
self.niri.layout.add_window_right_of(
|
||||||
.layout
|
&p,
|
||||||
.add_window_right_of(&p, mapped, width, is_full_width)
|
mapped,
|
||||||
|
width,
|
||||||
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
|
)
|
||||||
} else if let Some(workspace_name) = &workspace_name {
|
} else if let Some(workspace_name) = &workspace_name {
|
||||||
self.niri.layout.add_window_to_named_workspace(
|
self.niri.layout.add_window_to_named_workspace(
|
||||||
workspace_name,
|
workspace_name,
|
||||||
mapped,
|
mapped,
|
||||||
width,
|
width,
|
||||||
is_full_width,
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
activate,
|
activate,
|
||||||
)
|
)
|
||||||
} else if let Some(output) = &output {
|
} else if let Some(output) = &output {
|
||||||
@@ -177,13 +187,18 @@ impl CompositorHandler for State {
|
|||||||
mapped,
|
mapped,
|
||||||
width,
|
width,
|
||||||
is_full_width,
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
activate,
|
activate,
|
||||||
);
|
);
|
||||||
Some(output)
|
Some(output)
|
||||||
} else {
|
} else {
|
||||||
self.niri
|
self.niri.layout.add_window(
|
||||||
.layout
|
mapped,
|
||||||
.add_window(mapped, width, is_full_width, activate)
|
width,
|
||||||
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
|
activate,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(output) = output.cloned() {
|
if let Some(output) = output.cloned() {
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ impl XdgShellHandler for State {
|
|||||||
toplevel.with_pending_state(|state| {
|
toplevel.with_pending_state(|state| {
|
||||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||||
});
|
});
|
||||||
ws.configure_new_window(&unmapped.window, None, rules);
|
ws.configure_new_window(&unmapped.window, None, false, rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We already sent the initial configure, so we need to reconfigure.
|
// We already sent the initial configure, so we need to reconfigure.
|
||||||
@@ -553,7 +553,13 @@ impl XdgShellHandler for State {
|
|||||||
} else {
|
} else {
|
||||||
*width
|
*width
|
||||||
};
|
};
|
||||||
ws.configure_new_window(&unmapped.window, configure_width, rules);
|
let is_floating = rules.compute_open_floating(&toplevel);
|
||||||
|
ws.configure_new_window(
|
||||||
|
&unmapped.window,
|
||||||
|
configure_width,
|
||||||
|
is_floating,
|
||||||
|
rules,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We already sent the initial configure, so we need to reconfigure.
|
// We already sent the initial configure, so we need to reconfigure.
|
||||||
@@ -815,6 +821,7 @@ impl State {
|
|||||||
|
|
||||||
let mut width = None;
|
let mut width = None;
|
||||||
let is_full_width = rules.open_maximized.unwrap_or(false);
|
let is_full_width = rules.open_maximized.unwrap_or(false);
|
||||||
|
let is_floating = rules.compute_open_floating(toplevel);
|
||||||
|
|
||||||
// Tell the surface the preferred size and bounds for its likely output.
|
// Tell the surface the preferred size and bounds for its likely output.
|
||||||
let ws = rules
|
let ws = rules
|
||||||
@@ -843,7 +850,7 @@ impl State {
|
|||||||
} else {
|
} else {
|
||||||
width
|
width
|
||||||
};
|
};
|
||||||
ws.configure_new_window(window, configure_width, &rules);
|
ws.configure_new_window(window, configure_width, is_floating, &rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user prefers no CSD, it's a reasonable assumption that they would prefer to get
|
// If the user prefers no CSD, it's a reasonable assumption that they would prefer to get
|
||||||
|
|||||||
@@ -1294,6 +1294,25 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action::ToggleWindowFloating => {
|
||||||
|
self.niri.layout.toggle_window_floating(None);
|
||||||
|
// FIXME: granular
|
||||||
|
self.niri.queue_redraw_all();
|
||||||
|
}
|
||||||
|
Action::ToggleWindowFloatingById(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_window_floating(Some(&window));
|
||||||
|
// FIXME: granular
|
||||||
|
self.niri.queue_redraw_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::SwitchFocusBetweenFloatingAndTiling => {
|
||||||
|
self.niri.layout.switch_focus_floating_tiling();
|
||||||
|
// FIXME: granular
|
||||||
|
self.niri.queue_redraw_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,815 @@
|
|||||||
|
use std::cmp::{max, min};
|
||||||
|
use std::iter::zip;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use niri_ipc::SizeChange;
|
||||||
|
use smithay::backend::renderer::gles::GlesRenderer;
|
||||||
|
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};
|
||||||
|
|
||||||
|
use super::closing_window::{ClosingWindow, ClosingWindowRenderElement};
|
||||||
|
use super::scrolling::ColumnWidth;
|
||||||
|
use super::tile::{Tile, TileRenderElement, TileRenderSnapshot};
|
||||||
|
use super::workspace::InteractiveResize;
|
||||||
|
use super::{ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile};
|
||||||
|
use crate::animation::{Animation, Clock};
|
||||||
|
use crate::niri_render_elements;
|
||||||
|
use crate::render_helpers::renderer::NiriRenderer;
|
||||||
|
use crate::render_helpers::RenderTarget;
|
||||||
|
use crate::utils::transaction::TransactionBlocker;
|
||||||
|
use crate::utils::ResizeEdge;
|
||||||
|
use crate::window::ResolvedWindowRules;
|
||||||
|
|
||||||
|
/// Space for floating windows.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FloatingSpace<W: LayoutElement> {
|
||||||
|
/// Tiles in top-to-bottom order.
|
||||||
|
tiles: Vec<Tile<W>>,
|
||||||
|
|
||||||
|
/// Extra per-tile data.
|
||||||
|
data: Vec<Data>,
|
||||||
|
|
||||||
|
/// Id of the active window.
|
||||||
|
///
|
||||||
|
/// The active window is not necessarily the topmost window. Focus-follows-mouse should
|
||||||
|
/// activate a window, but not bring it to the top, because that's very annoying.
|
||||||
|
///
|
||||||
|
/// This is always set to `Some()` when `tiles` isn't empty.
|
||||||
|
active_window_id: Option<W::Id>,
|
||||||
|
|
||||||
|
/// Ongoing interactive resize.
|
||||||
|
interactive_resize: Option<InteractiveResize<W>>,
|
||||||
|
|
||||||
|
/// Windows in the closing animation.
|
||||||
|
closing_windows: Vec<ClosingWindow>,
|
||||||
|
|
||||||
|
/// Working area for this space.
|
||||||
|
working_area: Rectangle<f64, Logical>,
|
||||||
|
|
||||||
|
/// Scale of the output the space is on (and rounds its sizes to).
|
||||||
|
scale: f64,
|
||||||
|
|
||||||
|
/// Clock for driving animations.
|
||||||
|
clock: Clock,
|
||||||
|
|
||||||
|
/// Configurable properties of the layout.
|
||||||
|
options: Rc<Options>,
|
||||||
|
}
|
||||||
|
|
||||||
|
niri_render_elements! {
|
||||||
|
FloatingSpaceRenderElement<R> => {
|
||||||
|
Tile = TileRenderElement<R>,
|
||||||
|
ClosingWindow = ClosingWindowRenderElement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Size-relative units.
|
||||||
|
struct SizeFrac;
|
||||||
|
|
||||||
|
/// Extra per-tile data.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct Data {
|
||||||
|
/// Position relative to the working area.
|
||||||
|
pos: Point<f64, SizeFrac>,
|
||||||
|
|
||||||
|
/// Cached position in logical coordinates.
|
||||||
|
///
|
||||||
|
/// Not rounded to physical pixels.
|
||||||
|
logical_pos: Point<f64, Logical>,
|
||||||
|
|
||||||
|
/// Cached actual size of the tile.
|
||||||
|
size: Size<f64, Logical>,
|
||||||
|
|
||||||
|
/// Working area used for conversions.
|
||||||
|
working_area: Rectangle<f64, Logical>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn new<W: LayoutElement>(
|
||||||
|
working_area: Rectangle<f64, Logical>,
|
||||||
|
tile: &Tile<W>,
|
||||||
|
logical_pos: Point<f64, Logical>,
|
||||||
|
) -> Self {
|
||||||
|
let mut rv = Self {
|
||||||
|
pos: Point::default(),
|
||||||
|
logical_pos: Point::default(),
|
||||||
|
size: Size::default(),
|
||||||
|
working_area,
|
||||||
|
};
|
||||||
|
rv.update(tile);
|
||||||
|
rv.set_logical_pos(logical_pos);
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recompute_logical_pos(&mut self) {
|
||||||
|
let mut logical_pos = Point::from((self.pos.x, self.pos.y));
|
||||||
|
logical_pos.x *= self.working_area.size.w;
|
||||||
|
logical_pos.y *= self.working_area.size.h;
|
||||||
|
logical_pos += self.working_area.loc;
|
||||||
|
self.logical_pos = logical_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_config(&mut self, working_area: Rectangle<f64, Logical>) {
|
||||||
|
if self.working_area == working_area {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.working_area = working_area;
|
||||||
|
self.recompute_logical_pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update<W: LayoutElement>(&mut self, tile: &Tile<W>) {
|
||||||
|
self.size = tile.tile_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_logical_pos(&mut self, logical_pos: Point<f64, Logical>) {
|
||||||
|
let pos = logical_pos - self.working_area.loc;
|
||||||
|
let mut pos = Point::from((pos.x, pos.y));
|
||||||
|
pos.x /= f64::max(self.working_area.size.w, 1.0);
|
||||||
|
pos.y /= f64::max(self.working_area.size.h, 1.0);
|
||||||
|
|
||||||
|
self.pos = pos;
|
||||||
|
|
||||||
|
// This should get close to the same result as what we started with.
|
||||||
|
self.recompute_logical_pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn verify_invariants(&self) {
|
||||||
|
let mut temp = *self;
|
||||||
|
temp.recompute_logical_pos();
|
||||||
|
assert_eq!(
|
||||||
|
self.logical_pos, temp.logical_pos,
|
||||||
|
"cached logical pos must be up to date"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: LayoutElement> FloatingSpace<W> {
|
||||||
|
pub fn new(
|
||||||
|
working_area: Rectangle<f64, Logical>,
|
||||||
|
scale: f64,
|
||||||
|
clock: Clock,
|
||||||
|
options: Rc<Options>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
tiles: Vec::new(),
|
||||||
|
data: Vec::new(),
|
||||||
|
active_window_id: None,
|
||||||
|
interactive_resize: None,
|
||||||
|
closing_windows: Vec::new(),
|
||||||
|
working_area,
|
||||||
|
scale,
|
||||||
|
clock,
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_config(
|
||||||
|
&mut self,
|
||||||
|
working_area: Rectangle<f64, Logical>,
|
||||||
|
scale: f64,
|
||||||
|
options: Rc<Options>,
|
||||||
|
) {
|
||||||
|
for (tile, data) in zip(&mut self.tiles, &mut self.data) {
|
||||||
|
tile.update_config(scale, options.clone());
|
||||||
|
data.update(tile);
|
||||||
|
data.update_config(working_area);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.working_area = working_area;
|
||||||
|
self.scale = scale;
|
||||||
|
self.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_shaders(&mut self) {
|
||||||
|
for tile in &mut self.tiles {
|
||||||
|
tile.update_shaders();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance_animations(&mut self) {
|
||||||
|
for tile in &mut self.tiles {
|
||||||
|
tile.advance_animations();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.closing_windows.retain_mut(|closing| {
|
||||||
|
closing.advance_animations();
|
||||||
|
closing.are_animations_ongoing()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn are_animations_ongoing(&self) -> bool {
|
||||||
|
self.tiles.iter().any(Tile::are_animations_ongoing) || !self.closing_windows.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
||||||
|
let active = self.active_window_id.clone();
|
||||||
|
for (tile, offset) in self.tiles_with_offsets_mut() {
|
||||||
|
let id = tile.window().id();
|
||||||
|
let is_active = is_active && Some(id) == active.as_ref();
|
||||||
|
|
||||||
|
let mut tile_view_rect = view_rect;
|
||||||
|
tile_view_rect.loc -= offset + tile.render_offset();
|
||||||
|
tile.update(is_active, tile_view_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {
|
||||||
|
self.tiles.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {
|
||||||
|
self.tiles.iter_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles_with_offsets(&self) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> + '_ {
|
||||||
|
let offsets = self.data.iter().map(|d| d.logical_pos);
|
||||||
|
zip(&self.tiles, offsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles_with_offsets_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> + '_ {
|
||||||
|
let offsets = self.data.iter().map(|d| d.logical_pos);
|
||||||
|
zip(&mut self.tiles, offsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles_with_render_positions(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> {
|
||||||
|
let scale = self.scale;
|
||||||
|
self.tiles_with_offsets().map(move |(tile, offset)| {
|
||||||
|
let pos = offset + tile.render_offset();
|
||||||
|
// Round to physical pixels.
|
||||||
|
let pos = pos.to_physical_precise_round(scale).to_logical(scale);
|
||||||
|
(tile, pos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tiles_with_render_positions_mut(
|
||||||
|
&mut self,
|
||||||
|
round: bool,
|
||||||
|
) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {
|
||||||
|
let scale = self.scale;
|
||||||
|
self.tiles_with_offsets_mut().map(move |(tile, offset)| {
|
||||||
|
let mut pos = offset + tile.render_offset();
|
||||||
|
// Round to physical pixels.
|
||||||
|
if round {
|
||||||
|
pos = pos.to_physical_precise_round(scale).to_logical(scale);
|
||||||
|
}
|
||||||
|
(tile, pos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {
|
||||||
|
let border_config = rules.border.resolve_against(self.options.border);
|
||||||
|
compute_toplevel_bounds(border_config, self.working_area.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the geometry of the active tile relative to and clamped to the working area.
|
||||||
|
///
|
||||||
|
/// During animations, assumes the final tile position.
|
||||||
|
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
|
||||||
|
let (tile, offset) = self.tiles_with_offsets().next()?;
|
||||||
|
|
||||||
|
let tile_size = tile.tile_size();
|
||||||
|
let tile_rect = Rectangle::from_loc_and_size(offset, tile_size);
|
||||||
|
|
||||||
|
self.working_area.intersection(tile_rect)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {
|
||||||
|
for (tile, pos) in self.tiles_with_offsets() {
|
||||||
|
if tile.window().id() == id {
|
||||||
|
// TODO: intersect with working area width.
|
||||||
|
let width = tile.window_size().w;
|
||||||
|
let height = self.working_area.size.h;
|
||||||
|
|
||||||
|
let mut target = Rectangle::from_loc_and_size((0., 0.), (width, height));
|
||||||
|
target.loc.y -= pos.y;
|
||||||
|
target.loc.y -= tile.window_loc().y;
|
||||||
|
|
||||||
|
return Some(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idx_of(&self, id: &W::Id) -> Option<usize> {
|
||||||
|
self.tiles.iter().position(|tile| tile.window().id() == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, id: &W::Id) -> bool {
|
||||||
|
self.idx_of(id).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_window(&self) -> Option<&W> {
|
||||||
|
let id = self.active_window_id.as_ref()?;
|
||||||
|
self.tiles
|
||||||
|
.iter()
|
||||||
|
.find(|tile| tile.window().id() == id)
|
||||||
|
.map(Tile::window)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_window(&self, id: &W::Id) -> bool {
|
||||||
|
self.tiles.iter().any(|tile| tile.window().id() == id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.tiles.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_tile(&mut self, tile: Tile<W>, pos: Option<Point<f64, Logical>>, activate: bool) {
|
||||||
|
self.add_tile_at(0, tile, pos, activate);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_tile_at(
|
||||||
|
&mut self,
|
||||||
|
idx: usize,
|
||||||
|
mut tile: Tile<W>,
|
||||||
|
pos: Option<Point<f64, Logical>>,
|
||||||
|
activate: bool,
|
||||||
|
) {
|
||||||
|
tile.update_config(self.scale, self.options.clone());
|
||||||
|
|
||||||
|
if tile.window().is_pending_fullscreen() {
|
||||||
|
tile.window_mut()
|
||||||
|
.request_size(Size::from((0, 0)), true, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
if activate || self.tiles.is_empty() {
|
||||||
|
self.active_window_id = Some(tile.window().id().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pos = pos.unwrap_or_else(|| {
|
||||||
|
let area_size = self.working_area.size.to_point();
|
||||||
|
let tile_size = tile.tile_size().to_point();
|
||||||
|
let offset = (area_size - tile_size).downscale(2.);
|
||||||
|
self.working_area.loc + offset
|
||||||
|
});
|
||||||
|
// TODO: also nudge pos + size inside the area.
|
||||||
|
// TODO: smart padding (if doesn't fit, try without padding).
|
||||||
|
pos.x = f64::max(pos.x, self.working_area.loc.x + 8.);
|
||||||
|
pos.y = f64::max(pos.y, self.working_area.loc.y + 8.);
|
||||||
|
|
||||||
|
let data = Data::new(self.working_area, &tile, pos);
|
||||||
|
self.data.insert(idx, data);
|
||||||
|
self.tiles.insert(idx, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_tile_above(&mut self, above: &W::Id, tile: Tile<W>) {
|
||||||
|
// Activate the new window if above was active.
|
||||||
|
let activate = Some(above) == self.active_window_id.as_ref();
|
||||||
|
|
||||||
|
let idx = self.idx_of(above).unwrap();
|
||||||
|
|
||||||
|
let above_pos = self.data[idx].logical_pos;
|
||||||
|
let above_size = self.data[idx].size;
|
||||||
|
let pos = above_pos + (above_size.to_point() - tile.tile_size().to_point()).downscale(2.);
|
||||||
|
|
||||||
|
self.add_tile_at(idx, tile, Some(pos), activate);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_active_tile(&mut self) -> Option<RemovedTile<W>> {
|
||||||
|
let id = self.active_window_id.clone()?;
|
||||||
|
Some(self.remove_tile(&id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_tile(&mut self, id: &W::Id) -> RemovedTile<W> {
|
||||||
|
let idx = self.idx_of(id).unwrap();
|
||||||
|
self.remove_tile_by_idx(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_tile_by_idx(&mut self, idx: usize) -> RemovedTile<W> {
|
||||||
|
let tile = self.tiles.remove(idx);
|
||||||
|
self.data.remove(idx);
|
||||||
|
|
||||||
|
if self.tiles.is_empty() {
|
||||||
|
self.active_window_id = None;
|
||||||
|
} else if Some(tile.window().id()) == self.active_window_id.as_ref() {
|
||||||
|
// The active tile was removed, make the topmost tile active.
|
||||||
|
self.active_window_id = Some(self.tiles[0].window().id().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop interactive resize.
|
||||||
|
if let Some(resize) = &self.interactive_resize {
|
||||||
|
if tile.window().id() == &resize.window {
|
||||||
|
self.interactive_resize = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = ColumnWidth::Fixed(tile.window_size().w);
|
||||||
|
RemovedTile {
|
||||||
|
tile,
|
||||||
|
// TODO
|
||||||
|
width,
|
||||||
|
is_full_width: false,
|
||||||
|
is_floating: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_window_without_raising(&mut self, id: &W::Id) -> bool {
|
||||||
|
if !self.contains(id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.active_window_id = Some(id.clone());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_window(&mut self, id: &W::Id) -> bool {
|
||||||
|
let Some(idx) = self.idx_of(id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile = self.tiles.remove(idx);
|
||||||
|
let data = self.data.remove(idx);
|
||||||
|
self.tiles.insert(0, tile);
|
||||||
|
self.data.insert(0, data);
|
||||||
|
self.active_window_id = Some(id.clone());
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_close_animation_for_window(
|
||||||
|
&mut self,
|
||||||
|
renderer: &mut GlesRenderer,
|
||||||
|
id: &W::Id,
|
||||||
|
blocker: TransactionBlocker,
|
||||||
|
) {
|
||||||
|
let (tile, tile_pos) = self
|
||||||
|
.tiles_with_render_positions_mut(false)
|
||||||
|
.find(|(tile, _)| tile.window().id() == id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let Some(snapshot) = tile.take_unmap_snapshot() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile_size = tile.tile_size();
|
||||||
|
|
||||||
|
self.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_close_animation_for_tile(
|
||||||
|
&mut self,
|
||||||
|
renderer: &mut GlesRenderer,
|
||||||
|
snapshot: TileRenderSnapshot,
|
||||||
|
tile_size: Size<f64, Logical>,
|
||||||
|
tile_pos: Point<f64, Logical>,
|
||||||
|
blocker: TransactionBlocker,
|
||||||
|
) {
|
||||||
|
let anim = Animation::new(
|
||||||
|
self.clock.clone(),
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
0.,
|
||||||
|
self.options.animations.window_close.anim,
|
||||||
|
);
|
||||||
|
|
||||||
|
let blocker = if self.options.disable_transactions {
|
||||||
|
TransactionBlocker::completed()
|
||||||
|
} else {
|
||||||
|
blocker
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale = Scale::from(self.scale);
|
||||||
|
let res = ClosingWindow::new(
|
||||||
|
renderer, snapshot, scale, tile_size, tile_pos, blocker, anim,
|
||||||
|
);
|
||||||
|
match res {
|
||||||
|
Ok(closing) => {
|
||||||
|
self.closing_windows.push(closing);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("error creating a closing window animation: {err:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_window_width(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {
|
||||||
|
let Some(id) = id.or(self.active_window_id.as_ref()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let idx = self.idx_of(id).unwrap();
|
||||||
|
|
||||||
|
let SizeChange::SetFixed(mut win_width) = change else {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[idx];
|
||||||
|
let win = tile.window_mut();
|
||||||
|
let min_w = win.min_size().w;
|
||||||
|
let max_w = win.max_size().w;
|
||||||
|
|
||||||
|
if max_w > 0 {
|
||||||
|
win_width = min(win_width, max_w);
|
||||||
|
}
|
||||||
|
if min_w > 0 {
|
||||||
|
win_width = max(win_width, min_w);
|
||||||
|
}
|
||||||
|
win_width = max(1, win_width);
|
||||||
|
|
||||||
|
let win_height = win
|
||||||
|
.requested_size()
|
||||||
|
.map(|size| size.h)
|
||||||
|
// If we requested height = 0, then switch to the current height.
|
||||||
|
.filter(|h| *h != 0)
|
||||||
|
.unwrap_or_else(|| win.size().h);
|
||||||
|
let win_size = Size::from((win_width, win_height));
|
||||||
|
win.request_size(win_size, animate, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_window_height(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {
|
||||||
|
let Some(id) = id.or(self.active_window_id.as_ref()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let idx = self.idx_of(id).unwrap();
|
||||||
|
|
||||||
|
let SizeChange::SetFixed(mut win_height) = change else {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[idx];
|
||||||
|
let win = tile.window_mut();
|
||||||
|
let min_h = win.min_size().h;
|
||||||
|
let max_h = win.max_size().h;
|
||||||
|
|
||||||
|
if max_h > 0 {
|
||||||
|
win_height = min(win_height, max_h);
|
||||||
|
}
|
||||||
|
if min_h > 0 {
|
||||||
|
win_height = max(win_height, min_h);
|
||||||
|
}
|
||||||
|
win_height = max(1, win_height);
|
||||||
|
|
||||||
|
let win_width = win
|
||||||
|
.requested_size()
|
||||||
|
.map(|size| size.w)
|
||||||
|
// If we requested width = 0, then switch to the current width.
|
||||||
|
.filter(|w| *w != 0)
|
||||||
|
.unwrap_or_else(|| win.size().w);
|
||||||
|
let win_size = Size::from((win_width, win_height));
|
||||||
|
win.request_size(win_size, animate, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_window(&mut self, id: &W::Id, serial: Option<Serial>) -> bool {
|
||||||
|
let Some(tile_idx) = self.idx_of(id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile = &mut self.tiles[tile_idx];
|
||||||
|
let data = &mut self.data[tile_idx];
|
||||||
|
|
||||||
|
let resize = tile.window_mut().interactive_resize_data();
|
||||||
|
|
||||||
|
// Do this before calling update_window() so it can get up-to-date info.
|
||||||
|
if let Some(serial) = serial {
|
||||||
|
tile.window_mut().update_interactive_resize(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev_size = data.size;
|
||||||
|
|
||||||
|
tile.update_window();
|
||||||
|
data.update(tile);
|
||||||
|
|
||||||
|
// When resizing by top/left edge, update the position accordingly.
|
||||||
|
if let Some(resize) = resize {
|
||||||
|
let mut offset = Point::from((0., 0.));
|
||||||
|
if resize.edges.contains(ResizeEdge::LEFT) {
|
||||||
|
offset.x += prev_size.w - data.size.w;
|
||||||
|
}
|
||||||
|
if resize.edges.contains(ResizeEdge::TOP) {
|
||||||
|
offset.y += prev_size.h - data.size.h;
|
||||||
|
}
|
||||||
|
data.set_logical_pos(data.logical_pos + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_elements<R: NiriRenderer>(
|
||||||
|
&self,
|
||||||
|
renderer: &mut R,
|
||||||
|
view_rect: Rectangle<f64, Logical>,
|
||||||
|
scale: Scale<f64>,
|
||||||
|
target: RenderTarget,
|
||||||
|
) -> Vec<FloatingSpaceRenderElement<R>> {
|
||||||
|
let mut rv = Vec::new();
|
||||||
|
|
||||||
|
// Draw the closing windows on top of the other windows.
|
||||||
|
//
|
||||||
|
// FIXME: I guess this should rather preserve the stacking order when the window is closed.
|
||||||
|
for closing in self.closing_windows.iter().rev() {
|
||||||
|
let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target);
|
||||||
|
rv.push(elem.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let active = self.active_window_id.clone();
|
||||||
|
for (tile, tile_pos) in self.tiles_with_render_positions() {
|
||||||
|
// For the active tile, draw the focus ring.
|
||||||
|
let focus_ring = Some(tile.window().id()) == active.as_ref();
|
||||||
|
|
||||||
|
rv.extend(
|
||||||
|
tile.render(renderer, tile_pos, scale, focus_ring, target)
|
||||||
|
.map(Into::into),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {
|
||||||
|
if self.interactive_resize.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tile = self
|
||||||
|
.tiles
|
||||||
|
.iter_mut()
|
||||||
|
.find(|tile| tile.window().id() == &window)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let original_window_size = tile.window_size();
|
||||||
|
|
||||||
|
let resize = InteractiveResize {
|
||||||
|
window,
|
||||||
|
original_window_size,
|
||||||
|
data: InteractiveResizeData { edges },
|
||||||
|
};
|
||||||
|
self.interactive_resize = Some(resize);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interactive_resize_update(
|
||||||
|
&mut self,
|
||||||
|
window: &W::Id,
|
||||||
|
delta: Point<f64, Logical>,
|
||||||
|
) -> bool {
|
||||||
|
let Some(resize) = &self.interactive_resize else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if window != &resize.window {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let original_window_size = resize.original_window_size;
|
||||||
|
let edges = resize.data.edges;
|
||||||
|
|
||||||
|
if edges.intersects(ResizeEdge::LEFT_RIGHT) {
|
||||||
|
let mut dx = delta.x;
|
||||||
|
if edges.contains(ResizeEdge::LEFT) {
|
||||||
|
dx = -dx;
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_width = (original_window_size.w + dx).round() as i32;
|
||||||
|
self.set_window_width(Some(window), SizeChange::SetFixed(window_width), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if edges.intersects(ResizeEdge::TOP_BOTTOM) {
|
||||||
|
let mut dy = delta.y;
|
||||||
|
if edges.contains(ResizeEdge::TOP) {
|
||||||
|
dy = -dy;
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_height = (original_window_size.h + dy).round() as i32;
|
||||||
|
self.set_window_height(Some(window), SizeChange::SetFixed(window_height), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {
|
||||||
|
let Some(resize) = &self.interactive_resize else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(window) = window {
|
||||||
|
if window != &resize.window {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.interactive_resize = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&mut self, is_active: bool) {
|
||||||
|
let active = self.active_window_id.clone();
|
||||||
|
for tile in &mut self.tiles {
|
||||||
|
let win = tile.window_mut();
|
||||||
|
|
||||||
|
win.set_active_in_column(true);
|
||||||
|
|
||||||
|
let is_active = is_active && Some(win.id()) == active.as_ref();
|
||||||
|
win.set_activated(is_active);
|
||||||
|
|
||||||
|
let resize_data = self
|
||||||
|
.interactive_resize
|
||||||
|
.as_ref()
|
||||||
|
.filter(|resize| &resize.window == win.id())
|
||||||
|
.map(|resize| resize.data);
|
||||||
|
win.set_interactive_resize(resize_data);
|
||||||
|
|
||||||
|
let border_config = win.rules().border.resolve_against(self.options.border);
|
||||||
|
let bounds = compute_toplevel_bounds(border_config, self.working_area.size);
|
||||||
|
win.set_bounds(bounds);
|
||||||
|
|
||||||
|
// If transactions are disabled, also disable combined throttling, for more
|
||||||
|
// intuitive behavior.
|
||||||
|
let intent = if self.options.disable_resize_throttling {
|
||||||
|
ConfigureIntent::CanSend
|
||||||
|
} else {
|
||||||
|
win.configure_intent()
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
intent,
|
||||||
|
ConfigureIntent::CanSend | ConfigureIntent::ShouldSend
|
||||||
|
) {
|
||||||
|
win.send_pending_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
win.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn working_area(&self) -> Rectangle<f64, Logical> {
|
||||||
|
self.working_area
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn clock(&self) -> &Clock {
|
||||||
|
&self.clock
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn options(&self) -> &Rc<Options> {
|
||||||
|
&self.options
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn verify_invariants(&self) {
|
||||||
|
assert!(self.scale > 0.);
|
||||||
|
assert!(self.scale.is_finite());
|
||||||
|
assert_eq!(self.tiles.len(), self.data.len());
|
||||||
|
|
||||||
|
for (tile, data) in zip(&self.tiles, &self.data) {
|
||||||
|
assert!(Rc::ptr_eq(&self.options, &tile.options));
|
||||||
|
assert_eq!(self.clock, tile.clock);
|
||||||
|
assert_eq!(self.scale, tile.scale());
|
||||||
|
tile.verify_invariants();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
!tile.window().is_pending_fullscreen(),
|
||||||
|
"floating windows cannot be fullscreen"
|
||||||
|
);
|
||||||
|
|
||||||
|
data.verify_invariants();
|
||||||
|
|
||||||
|
let mut data2 = *data;
|
||||||
|
data2.update(tile);
|
||||||
|
data2.update_config(self.working_area);
|
||||||
|
assert_eq!(data, &data2, "tile data must be up to date");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(id) = &self.active_window_id {
|
||||||
|
assert!(!self.tiles.is_empty());
|
||||||
|
assert!(self.contains(id), "active window must be present in tiles");
|
||||||
|
} else {
|
||||||
|
assert!(self.tiles.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(resize) = &self.interactive_resize {
|
||||||
|
assert!(
|
||||||
|
self.contains(&resize.window),
|
||||||
|
"interactive resize window must be present in tiles"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_toplevel_bounds(
|
||||||
|
border_config: niri_config::Border,
|
||||||
|
working_area_size: Size<f64, Logical>,
|
||||||
|
) -> Size<i32, Logical> {
|
||||||
|
let mut border = 0.;
|
||||||
|
if !border_config.off {
|
||||||
|
border = border_config.width.0 * 2.;
|
||||||
|
}
|
||||||
|
|
||||||
|
Size::from((
|
||||||
|
f64::max(working_area_size.w - border, 1.),
|
||||||
|
f64::max(working_area_size.h - border, 1.),
|
||||||
|
))
|
||||||
|
.to_i32_floor()
|
||||||
|
}
|
||||||
+215
-21
@@ -66,6 +66,7 @@ use crate::utils::{output_matches_name, output_size, round_logical_in_physical_m
|
|||||||
use crate::window::ResolvedWindowRules;
|
use crate::window::ResolvedWindowRules;
|
||||||
|
|
||||||
pub mod closing_window;
|
pub mod closing_window;
|
||||||
|
pub mod floating;
|
||||||
pub mod focus_ring;
|
pub mod focus_ring;
|
||||||
pub mod insert_hint_element;
|
pub mod insert_hint_element;
|
||||||
pub mod monitor;
|
pub mod monitor;
|
||||||
@@ -357,6 +358,8 @@ pub struct RemovedTile<W: LayoutElement> {
|
|||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
/// Whether the column the tile was in was full-width.
|
/// Whether the column the tile was in was full-width.
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
/// Whether the tile was floating.
|
||||||
|
is_floating: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether to activate a newly added window.
|
/// Whether to activate a newly added window.
|
||||||
@@ -744,6 +747,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: pos
|
||||||
pub fn add_window_by_idx(
|
pub fn add_window_by_idx(
|
||||||
&mut self,
|
&mut self,
|
||||||
monitor_idx: usize,
|
monitor_idx: usize,
|
||||||
@@ -752,6 +756,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
activate: bool,
|
activate: bool,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
let MonitorSet::Normal {
|
let MonitorSet::Normal {
|
||||||
monitors,
|
monitors,
|
||||||
@@ -762,7 +767,14 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
panic!()
|
panic!()
|
||||||
};
|
};
|
||||||
|
|
||||||
monitors[monitor_idx].add_window(workspace_idx, window, activate, width, is_full_width);
|
monitors[monitor_idx].add_window(
|
||||||
|
workspace_idx,
|
||||||
|
window,
|
||||||
|
activate,
|
||||||
|
width,
|
||||||
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
|
);
|
||||||
|
|
||||||
if activate {
|
if activate {
|
||||||
*active_monitor_idx = monitor_idx;
|
*active_monitor_idx = monitor_idx;
|
||||||
@@ -776,6 +788,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
activate: ActivateWindow,
|
activate: ActivateWindow,
|
||||||
) -> Option<&Output> {
|
) -> Option<&Output> {
|
||||||
let width = self.resolve_default_width(&window, width);
|
let width = self.resolve_default_width(&window, width);
|
||||||
@@ -814,7 +827,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
mon.add_window(ws_idx, window, activate, width, is_full_width);
|
mon.add_window(ws_idx, window, activate, width, is_full_width, is_floating);
|
||||||
Some(&mon.output)
|
Some(&mon.output)
|
||||||
}
|
}
|
||||||
MonitorSet::NoOutputs { workspaces } => {
|
MonitorSet::NoOutputs { workspaces } => {
|
||||||
@@ -827,7 +840,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let activate = activate.map_smart(|| true);
|
let activate = activate.map_smart(|| true);
|
||||||
ws.add_window(window, activate, width, is_full_width);
|
ws.add_window(window, activate, width, is_full_width, is_floating);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -864,6 +877,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
activate: ActivateWindow,
|
activate: ActivateWindow,
|
||||||
) -> Option<&Output> {
|
) -> Option<&Output> {
|
||||||
let width = self.resolve_default_width(&window, width);
|
let width = self.resolve_default_width(&window, width);
|
||||||
@@ -888,6 +902,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
activate,
|
activate,
|
||||||
width,
|
width,
|
||||||
is_full_width,
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
);
|
);
|
||||||
Some(&mon.output)
|
Some(&mon.output)
|
||||||
}
|
}
|
||||||
@@ -902,7 +917,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
&mut workspaces[0]
|
&mut workspaces[0]
|
||||||
};
|
};
|
||||||
let activate = activate.map_smart(|| true);
|
let activate = activate.map_smart(|| true);
|
||||||
ws.add_window(window, activate, width, is_full_width);
|
ws.add_window(window, activate, width, is_full_width, is_floating);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -919,16 +934,24 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) -> Option<&Output> {
|
) -> Option<&Output> {
|
||||||
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
|
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
|
||||||
if right_of == move_.tile.window().id() {
|
if right_of == move_.tile.window().id() {
|
||||||
let output = move_.output.clone();
|
let output = move_.output.clone();
|
||||||
let activate = ActivateWindow::default();
|
let activate = ActivateWindow::default();
|
||||||
if self.monitor_for_output(&output).is_some() {
|
if self.monitor_for_output(&output).is_some() {
|
||||||
self.add_window_on_output(&output, window, width, is_full_width, activate);
|
self.add_window_on_output(
|
||||||
|
&output,
|
||||||
|
window,
|
||||||
|
width,
|
||||||
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
|
activate,
|
||||||
|
);
|
||||||
return Some(&self.monitor_for_output(&output).unwrap().output);
|
return Some(&self.monitor_for_output(&output).unwrap().output);
|
||||||
} else {
|
} else {
|
||||||
return self.add_window(window, width, is_full_width, activate);
|
return self.add_window(window, width, is_full_width, is_floating, activate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -942,7 +965,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
.find(|mon| mon.workspaces.iter().any(|ws| ws.has_window(right_of)))
|
.find(|mon| mon.workspaces.iter().any(|ws| ws.has_window(right_of)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
mon.add_window_right_of(right_of, window, width, is_full_width);
|
mon.add_window_right_of(right_of, window, width, is_full_width, is_floating);
|
||||||
Some(&mon.output)
|
Some(&mon.output)
|
||||||
}
|
}
|
||||||
MonitorSet::NoOutputs { workspaces } => {
|
MonitorSet::NoOutputs { workspaces } => {
|
||||||
@@ -950,7 +973,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|ws| ws.has_window(right_of))
|
.find(|ws| ws.has_window(right_of))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ws.add_window_right_of(right_of, window, width, is_full_width);
|
ws.add_window_right_of(right_of, window, width, is_full_width, is_floating);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -963,6 +986,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
activate: ActivateWindow,
|
activate: ActivateWindow,
|
||||||
) {
|
) {
|
||||||
let width = self.resolve_default_width(&window, width);
|
let width = self.resolve_default_width(&window, width);
|
||||||
@@ -998,6 +1022,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
activate,
|
activate,
|
||||||
width,
|
width,
|
||||||
is_full_width,
|
is_full_width,
|
||||||
|
is_floating,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,6 +1049,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
tile: move_.tile,
|
tile: move_.tile,
|
||||||
width: move_.width,
|
width: move_.width,
|
||||||
is_full_width: move_.is_full_width,
|
is_full_width: move_.is_full_width,
|
||||||
|
is_floating: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1390,6 +1416,42 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn activate_window_without_raising(&mut self, window: &W::Id) {
|
||||||
|
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
|
||||||
|
if move_.tile.window().id() == window {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let MonitorSet::Normal {
|
||||||
|
monitors,
|
||||||
|
active_monitor_idx,
|
||||||
|
..
|
||||||
|
} = &mut self.monitor_set
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (monitor_idx, mon) in monitors.iter_mut().enumerate() {
|
||||||
|
for (workspace_idx, ws) in mon.workspaces.iter_mut().enumerate() {
|
||||||
|
if ws.activate_window_without_raising(window) {
|
||||||
|
*active_monitor_idx = monitor_idx;
|
||||||
|
|
||||||
|
// If currently in the middle of a vertical swipe between the target workspace
|
||||||
|
// and some other, don't switch the workspace.
|
||||||
|
match &mon.workspace_switch {
|
||||||
|
Some(WorkspaceSwitch::Gesture(gesture))
|
||||||
|
if gesture.current_idx.floor() == workspace_idx as f64
|
||||||
|
|| gesture.current_idx.ceil() == workspace_idx as f64 => {}
|
||||||
|
_ => mon.switch_workspace(workspace_idx),
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn activate_output(&mut self, output: &Output) {
|
pub fn activate_output(&mut self, output: &Output) {
|
||||||
let MonitorSet::Normal {
|
let MonitorSet::Normal {
|
||||||
monitors,
|
monitors,
|
||||||
@@ -2596,6 +2658,36 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
workspace.reset_window_height(window);
|
workspace.reset_window_height(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toggle_window_floating(&mut self, window: Option<&W::Id>) {
|
||||||
|
if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {
|
||||||
|
if window.is_none() || window == Some(move_.tile.window().id()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let workspace = if let Some(window) = window {
|
||||||
|
Some(
|
||||||
|
self.workspaces_mut()
|
||||||
|
.find(|ws| ws.has_window(window))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.active_workspace_mut()
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(workspace) = workspace else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
workspace.toggle_window_floating(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_focus_floating_tiling(&mut self) {
|
||||||
|
let Some(workspace) = self.active_workspace_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
workspace.switch_focus_floating_tiling();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_output(&mut self, output: &Output) {
|
pub fn focus_output(&mut self, output: &Output) {
|
||||||
if let MonitorSet::Normal {
|
if let MonitorSet::Normal {
|
||||||
monitors,
|
monitors,
|
||||||
@@ -2680,6 +2772,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
activate,
|
activate,
|
||||||
removed.width,
|
removed.width,
|
||||||
removed.is_full_width,
|
removed.is_full_width,
|
||||||
|
removed.is_floating,
|
||||||
);
|
);
|
||||||
|
|
||||||
let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set else {
|
let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set else {
|
||||||
@@ -2706,6 +2799,12 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
|
|
||||||
let current = &mut monitors[*active_monitor_idx];
|
let current = &mut monitors[*active_monitor_idx];
|
||||||
let ws = current.active_workspace();
|
let ws = current.active_workspace();
|
||||||
|
|
||||||
|
if ws.floating_is_active() {
|
||||||
|
self.move_to_output(None, output, None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let Some(column) = ws.remove_active_column() else {
|
let Some(column) = ws.remove_active_column() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -3040,10 +3139,17 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
.band(sq_dist / INTERACTIVE_MOVE_START_THRESHOLD);
|
.band(sq_dist / INTERACTIVE_MOVE_START_THRESHOLD);
|
||||||
|
|
||||||
let tile = self
|
let (is_floating, tile) = self
|
||||||
.workspaces_mut()
|
.workspaces_mut()
|
||||||
.flat_map(|ws| ws.tiles_mut())
|
.find(|ws| ws.has_window(&window_id))
|
||||||
.find(|tile| *tile.window().id() == window_id)
|
.map(|ws| {
|
||||||
|
(
|
||||||
|
ws.is_floating(&window_id),
|
||||||
|
ws.tiles_mut()
|
||||||
|
.find(|tile| *tile.window().id() == window_id)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tile.interactive_move_offset = pointer_delta.upscale(factor);
|
tile.interactive_move_offset = pointer_delta.upscale(factor);
|
||||||
|
|
||||||
@@ -3054,7 +3160,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
pointer_ratio_within_window,
|
pointer_ratio_within_window,
|
||||||
});
|
});
|
||||||
|
|
||||||
if sq_dist < INTERACTIVE_MOVE_START_THRESHOLD {
|
if !is_floating && sq_dist < INTERACTIVE_MOVE_START_THRESHOLD {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3063,8 +3169,8 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
// to the pointer. Otherwise, we just teleport it as the layout code is not aware
|
// to the pointer. Otherwise, we just teleport it as the layout code is not aware
|
||||||
// of monitor positions.
|
// of monitor positions.
|
||||||
//
|
//
|
||||||
// FIXME: with floating layer, the layout code will know about monitor positions,
|
// FIXME: when and if the layout code knows about monitor positions, this will be
|
||||||
// so this will be potentially animatable.
|
// potentially animatable.
|
||||||
let mut tile_pos = None;
|
let mut tile_pos = None;
|
||||||
if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {
|
if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {
|
||||||
if let Some((mon, (ws, ws_offset))) = monitors.iter().find_map(|mon| {
|
if let Some((mon, (ws, ws_offset))) = monitors.iter().find_map(|mon| {
|
||||||
@@ -3087,6 +3193,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
mut tile,
|
mut tile,
|
||||||
width,
|
width,
|
||||||
is_full_width,
|
is_full_width,
|
||||||
|
is_floating: _,
|
||||||
} = self.remove_window(window, Transaction::new()).unwrap();
|
} = self.remove_window(window, Transaction::new()).unwrap();
|
||||||
|
|
||||||
tile.stop_move_animations();
|
tile.stop_move_animations();
|
||||||
@@ -3105,6 +3212,7 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
|
|
||||||
// Unfullscreen and let the window pick a natural size.
|
// Unfullscreen and let the window pick a natural size.
|
||||||
//
|
//
|
||||||
|
// TODO
|
||||||
// When we have floating, we will want to always send a (0, 0) size here, not just
|
// When we have floating, we will want to always send a (0, 0) size here, not just
|
||||||
// to unfullscreen. However, when implementing that, remember to check how GTK
|
// to unfullscreen. However, when implementing that, remember to check how GTK
|
||||||
// tiled window size restoration works. It seems to remember *some* last size with
|
// tiled window size restoration works. It seems to remember *some* last size with
|
||||||
@@ -3265,6 +3373,10 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
InsertPosition::Floating => {
|
||||||
|
let pos = move_.tile_render_location() - offset;
|
||||||
|
mon.add_floating_tile(ws_idx, move_.tile, Some(pos), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed because empty_workspace_above_first could have modified the idx
|
// needed because empty_workspace_above_first could have modified the idx
|
||||||
@@ -3911,6 +4023,8 @@ mod tests {
|
|||||||
(0f64..).prop_map(SizeChange::SetProportion),
|
(0f64..).prop_map(SizeChange::SetProportion),
|
||||||
any::<i32>().prop_map(SizeChange::AdjustFixed),
|
any::<i32>().prop_map(SizeChange::AdjustFixed),
|
||||||
any::<f64>().prop_map(SizeChange::AdjustProportion),
|
any::<f64>().prop_map(SizeChange::AdjustProportion),
|
||||||
|
// Interactive resize can have negative values here.
|
||||||
|
Just(SizeChange::SetFixed(-100)),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3925,11 +4039,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn arbitrary_min_max_size() -> impl Strategy<Value = (Size<i32, Logical>, Size<i32, Logical>)> {
|
fn arbitrary_min_max_size() -> impl Strategy<Value = (Size<i32, Logical>, Size<i32, Logical>)> {
|
||||||
(arbitrary_min_max(), arbitrary_min_max()).prop_map(|((min_w, max_w), (min_h, max_h))| {
|
prop_oneof![
|
||||||
let min_size = Size::from((min_w, min_h));
|
5 => (arbitrary_min_max(), arbitrary_min_max()).prop_map(
|
||||||
let max_size = Size::from((max_w, max_h));
|
|((min_w, max_w), (min_h, max_h))| {
|
||||||
(min_size, max_size)
|
let min_size = Size::from((min_w, min_h));
|
||||||
})
|
let max_size = Size::from((max_w, max_h));
|
||||||
|
(min_size, max_size)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
1 => arbitrary_min_max().prop_map(|(w, h)| {
|
||||||
|
let size = Size::from((w, h));
|
||||||
|
(size, size)
|
||||||
|
}),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arbitrary_view_offset_gesture_delta() -> impl Strategy<Value = f64> {
|
fn arbitrary_view_offset_gesture_delta() -> impl Strategy<Value = f64> {
|
||||||
@@ -4103,6 +4225,11 @@ mod tests {
|
|||||||
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
|
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
|
||||||
id: Option<usize>,
|
id: Option<usize>,
|
||||||
},
|
},
|
||||||
|
ToggleWindowFloating {
|
||||||
|
#[proptest(strategy = "proptest::option::of(1..=5usize)")]
|
||||||
|
id: Option<usize>,
|
||||||
|
},
|
||||||
|
SwitchFocusFloatingTiling,
|
||||||
Communicate(#[proptest(strategy = "1..=5usize")] usize),
|
Communicate(#[proptest(strategy = "1..=5usize")] usize),
|
||||||
Refresh {
|
Refresh {
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
@@ -4294,7 +4421,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
||||||
layout.add_window(win, None, false, ActivateWindow::default());
|
let is_floating = min_max_size.0.h > 0 && min_max_size.0.h == min_max_size.1.h;
|
||||||
|
layout.add_window(win, None, false, is_floating, ActivateWindow::default());
|
||||||
}
|
}
|
||||||
Op::AddWindowRightOf {
|
Op::AddWindowRightOf {
|
||||||
id,
|
id,
|
||||||
@@ -4350,7 +4478,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
||||||
layout.add_window_right_of(&right_of_id, win, None, false);
|
let is_floating = min_max_size.0.h > 0 && min_max_size.0.h == min_max_size.1.h;
|
||||||
|
layout.add_window_right_of(&right_of_id, win, None, false, is_floating);
|
||||||
}
|
}
|
||||||
Op::AddWindowToNamedWorkspace {
|
Op::AddWindowToNamedWorkspace {
|
||||||
id,
|
id,
|
||||||
@@ -4411,11 +4540,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
let win = TestWindow::new(id, bbox, min_max_size.0, min_max_size.1);
|
||||||
|
let is_floating = min_max_size.0.h > 0 && min_max_size.0.h == min_max_size.1.h;
|
||||||
layout.add_window_to_named_workspace(
|
layout.add_window_to_named_workspace(
|
||||||
&ws_name,
|
&ws_name,
|
||||||
win,
|
win,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
is_floating,
|
||||||
ActivateWindow::default(),
|
ActivateWindow::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4575,6 +4706,13 @@ mod tests {
|
|||||||
let id = id.filter(|id| layout.has_window(id));
|
let id = id.filter(|id| layout.has_window(id));
|
||||||
layout.reset_window_height(id.as_ref());
|
layout.reset_window_height(id.as_ref());
|
||||||
}
|
}
|
||||||
|
Op::ToggleWindowFloating { id } => {
|
||||||
|
let id = id.filter(|id| layout.has_window(id));
|
||||||
|
layout.toggle_window_floating(id.as_ref());
|
||||||
|
}
|
||||||
|
Op::SwitchFocusFloatingTiling => {
|
||||||
|
layout.switch_focus_floating_tiling();
|
||||||
|
}
|
||||||
Op::Communicate(id) => {
|
Op::Communicate(id) => {
|
||||||
let mut update = false;
|
let mut update = false;
|
||||||
|
|
||||||
@@ -6191,6 +6329,62 @@ mod tests {
|
|||||||
check_ops(&ops);
|
check_ops(&ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_width_fixed_negative() {
|
||||||
|
let ops = [
|
||||||
|
Op::AddOutput(3),
|
||||||
|
Op::AddWindow {
|
||||||
|
id: 3,
|
||||||
|
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||||
|
min_max_size: Default::default(),
|
||||||
|
},
|
||||||
|
Op::ToggleWindowFloating { id: Some(3) },
|
||||||
|
Op::SetColumnWidth(SizeChange::SetFixed(-100)),
|
||||||
|
];
|
||||||
|
check_ops(&ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_height_fixed_negative() {
|
||||||
|
let ops = [
|
||||||
|
Op::AddOutput(3),
|
||||||
|
Op::AddWindow {
|
||||||
|
id: 3,
|
||||||
|
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||||
|
min_max_size: Default::default(),
|
||||||
|
},
|
||||||
|
Op::ToggleWindowFloating { id: Some(3) },
|
||||||
|
Op::SetWindowHeight {
|
||||||
|
id: None,
|
||||||
|
change: SizeChange::SetFixed(-100),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
check_ops(&ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interactive_resize_to_negative() {
|
||||||
|
let ops = [
|
||||||
|
Op::AddOutput(3),
|
||||||
|
Op::AddWindow {
|
||||||
|
id: 3,
|
||||||
|
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||||
|
min_max_size: Default::default(),
|
||||||
|
},
|
||||||
|
Op::ToggleWindowFloating { id: Some(3) },
|
||||||
|
Op::InteractiveResizeBegin {
|
||||||
|
window: 3,
|
||||||
|
edges: ResizeEdge::BOTTOM_RIGHT,
|
||||||
|
},
|
||||||
|
Op::InteractiveResizeUpdate {
|
||||||
|
window: 3,
|
||||||
|
dx: -10000.,
|
||||||
|
dy: -10000.,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
check_ops(&ops);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn windows_on_other_workspaces_remain_activated() {
|
fn windows_on_other_workspaces_remain_activated() {
|
||||||
let ops = [
|
let ops = [
|
||||||
|
|||||||
+51
-2
@@ -225,10 +225,11 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
activate: bool,
|
activate: bool,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
let workspace = &mut self.workspaces[workspace_idx];
|
let workspace = &mut self.workspaces[workspace_idx];
|
||||||
|
|
||||||
workspace.add_window(window, activate, width, is_full_width);
|
workspace.add_window(window, activate, width, is_full_width, is_floating);
|
||||||
|
|
||||||
// After adding a new window, workspace becomes this output's own.
|
// After adding a new window, workspace becomes this output's own.
|
||||||
workspace.original_output = OutputId::new(&self.output);
|
workspace.original_output = OutputId::new(&self.output);
|
||||||
@@ -253,6 +254,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
let workspace_idx = self
|
let workspace_idx = self
|
||||||
.workspaces
|
.workspaces
|
||||||
@@ -261,7 +263,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let workspace = &mut self.workspaces[workspace_idx];
|
let workspace = &mut self.workspaces[workspace_idx];
|
||||||
|
|
||||||
workspace.add_window_right_of(right_of, window, width, is_full_width);
|
workspace.add_window_right_of(right_of, window, width, is_full_width, is_floating);
|
||||||
|
|
||||||
// After adding a new window, workspace becomes this output's own.
|
// After adding a new window, workspace becomes this output's own.
|
||||||
workspace.original_output = OutputId::new(&self.output);
|
workspace.original_output = OutputId::new(&self.output);
|
||||||
@@ -322,6 +324,35 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_floating_tile(
|
||||||
|
&mut self,
|
||||||
|
mut workspace_idx: usize,
|
||||||
|
tile: Tile<W>,
|
||||||
|
pos: Option<Point<f64, Logical>>,
|
||||||
|
activate: bool,
|
||||||
|
) {
|
||||||
|
let workspace = &mut self.workspaces[workspace_idx];
|
||||||
|
|
||||||
|
workspace.add_floating_tile(tile, pos, activate);
|
||||||
|
|
||||||
|
// After adding a new window, workspace becomes this output's own.
|
||||||
|
workspace.original_output = OutputId::new(&self.output);
|
||||||
|
|
||||||
|
if workspace_idx == self.workspaces.len() - 1 {
|
||||||
|
// Insert a new empty workspace.
|
||||||
|
self.add_workspace_bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.options.empty_workspace_above_first && workspace_idx == 0 {
|
||||||
|
self.add_workspace_top();
|
||||||
|
workspace_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if activate {
|
||||||
|
self.activate_workspace(workspace_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_tile_to_column(
|
pub fn add_tile_to_column(
|
||||||
&mut self,
|
&mut self,
|
||||||
workspace_idx: usize,
|
workspace_idx: usize,
|
||||||
@@ -505,6 +536,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
true,
|
true,
|
||||||
removed.width,
|
removed.width,
|
||||||
removed.is_full_width,
|
removed.is_full_width,
|
||||||
|
removed.is_floating,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,6 +559,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
true,
|
true,
|
||||||
removed.width,
|
removed.width,
|
||||||
removed.is_full_width,
|
removed.is_full_width,
|
||||||
|
removed.is_floating,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +598,7 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
activate,
|
activate,
|
||||||
removed.width,
|
removed.width,
|
||||||
removed.is_full_width,
|
removed.is_full_width,
|
||||||
|
removed.is_floating,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.workspace_switch.is_none() {
|
if self.workspace_switch.is_none() {
|
||||||
@@ -581,6 +615,11 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||||
|
if workspace.floating_is_active() {
|
||||||
|
self.move_to_workspace_up();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let Some(column) = workspace.remove_active_column() else {
|
let Some(column) = workspace.remove_active_column() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -597,6 +636,11 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||||
|
if workspace.floating_is_active() {
|
||||||
|
self.move_to_workspace_down();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let Some(column) = workspace.remove_active_column() else {
|
let Some(column) = workspace.remove_active_column() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -613,6 +657,11 @@ impl<W: LayoutElement> Monitor<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let workspace = &mut self.workspaces[source_workspace_idx];
|
let workspace = &mut self.workspaces[source_workspace_idx];
|
||||||
|
if workspace.floating_is_active() {
|
||||||
|
self.move_to_workspace(None, idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let Some(column) = workspace.remove_active_column() else {
|
let Some(column) = workspace.remove_active_column() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
+33
-8
@@ -26,6 +26,11 @@ use crate::window::ResolvedWindowRules;
|
|||||||
/// Amount of touchpad movement to scroll the view for the width of one working area.
|
/// Amount of touchpad movement to scroll the view for the width of one working area.
|
||||||
const VIEW_GESTURE_WORKING_AREA_MOVEMENT: f64 = 1200.;
|
const VIEW_GESTURE_WORKING_AREA_MOVEMENT: f64 = 1200.;
|
||||||
|
|
||||||
|
/// Maximum distance to the target position for an interactively moved window.
|
||||||
|
///
|
||||||
|
/// If the distance is higher than this, the window will instead remain floating.
|
||||||
|
const WINDOW_INSERT_MAX_DISTANCE: f64 = 50.;
|
||||||
|
|
||||||
/// A scrollable-tiling space for windows.
|
/// A scrollable-tiling space for windows.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScrollingSpace<W: LayoutElement> {
|
pub struct ScrollingSpace<W: LayoutElement> {
|
||||||
@@ -102,6 +107,7 @@ niri_render_elements! {
|
|||||||
pub enum InsertPosition {
|
pub enum InsertPosition {
|
||||||
NewColumn(usize),
|
NewColumn(usize),
|
||||||
InColumn(usize, usize),
|
InColumn(usize, usize),
|
||||||
|
Floating,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -359,6 +365,10 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
self.columns.iter_mut().flat_map(|col| col.tiles.iter_mut())
|
self.columns.iter_mut().flat_map(|col| col.tiles.iter_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.columns.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn active_window(&self) -> Option<&W> {
|
pub fn active_window(&self) -> Option<&W> {
|
||||||
if self.columns.is_empty() {
|
if self.columns.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
@@ -652,9 +662,9 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
self.insert_hint = None;
|
self.insert_hint = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_insert_position(&self, pos: Point<f64, Logical>) -> InsertPosition {
|
fn compute_insert_position(&self, pos: Point<f64, Logical>) -> (InsertPosition, f64) {
|
||||||
if self.columns.is_empty() {
|
if self.columns.is_empty() {
|
||||||
return InsertPosition::NewColumn(0);
|
return (InsertPosition::NewColumn(0), pos.x.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = pos.x + self.view_pos();
|
let x = pos.x + self.view_pos();
|
||||||
@@ -665,7 +675,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
|
|
||||||
// Insert position is before the first column.
|
// Insert position is before the first column.
|
||||||
if x < 0. {
|
if x < 0. {
|
||||||
return InsertPosition::NewColumn(0);
|
return (InsertPosition::NewColumn(0), -x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the closest gap between columns.
|
// Find the closest gap between columns.
|
||||||
@@ -685,7 +695,8 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
|
|
||||||
// Insert position is past the last column.
|
// Insert position is past the last column.
|
||||||
if col_idx == self.columns.len() {
|
if col_idx == self.columns.len() {
|
||||||
return InsertPosition::NewColumn(closest_col_idx);
|
// col_x should be what we expect if pos.x is past the last column.
|
||||||
|
return (InsertPosition::NewColumn(closest_col_idx), x - col_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the closest gap between tiles.
|
// Find the closest gap between tiles.
|
||||||
@@ -700,12 +711,23 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
let vert_dist = (col_x - x).abs();
|
let vert_dist = (col_x - x).abs();
|
||||||
let hor_dist = (tile_off.y - y).abs();
|
let hor_dist = (tile_off.y - y).abs();
|
||||||
if vert_dist <= hor_dist {
|
if vert_dist <= hor_dist {
|
||||||
InsertPosition::NewColumn(closest_col_idx)
|
(InsertPosition::NewColumn(closest_col_idx), vert_dist)
|
||||||
} else {
|
} else {
|
||||||
InsertPosition::InColumn(col_idx, closest_tile_idx)
|
(
|
||||||
|
InsertPosition::InColumn(col_idx, closest_tile_idx),
|
||||||
|
hor_dist,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_insert_position(&self, pos: Point<f64, Logical>) -> InsertPosition {
|
||||||
|
let (position, distance) = self.compute_insert_position(pos);
|
||||||
|
if distance > WINDOW_INSERT_MAX_DISTANCE {
|
||||||
|
return InsertPosition::Floating;
|
||||||
|
}
|
||||||
|
position
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_tile(
|
pub fn add_tile(
|
||||||
&mut self,
|
&mut self,
|
||||||
col_idx: Option<usize>,
|
col_idx: Option<usize>,
|
||||||
@@ -894,6 +916,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
tile: column.tiles.remove(tile_idx),
|
tile: column.tiles.remove(tile_idx),
|
||||||
width: column.width,
|
width: column.width,
|
||||||
is_full_width: column.is_full_width,
|
is_full_width: column.is_full_width,
|
||||||
|
is_floating: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,6 +952,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
tile,
|
tile,
|
||||||
width: column.width,
|
width: column.width,
|
||||||
is_full_width: column.is_full_width,
|
is_full_width: column.is_full_width,
|
||||||
|
is_floating: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
column.active_tile_idx = min(column.active_tile_idx, column.tiles.len() - 1);
|
column.active_tile_idx = min(column.active_tile_idx, column.tiles.len() - 1);
|
||||||
@@ -1244,7 +1268,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
self.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
self.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_close_animation_for_tile(
|
fn start_close_animation_for_tile(
|
||||||
&mut self,
|
&mut self,
|
||||||
renderer: &mut GlesRenderer,
|
renderer: &mut GlesRenderer,
|
||||||
snapshot: TileRenderSnapshot,
|
snapshot: TileRenderSnapshot,
|
||||||
@@ -1934,6 +1958,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
let loc = Point::from((self.column_x(column_index), y));
|
let loc = Point::from((self.column_x(column_index), y));
|
||||||
Rectangle::from_loc_and_size(loc, size)
|
Rectangle::from_loc_and_size(loc, size)
|
||||||
}
|
}
|
||||||
|
InsertPosition::Floating => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// First window on an empty workspace will cancel out any view offset. Replicate this
|
// First window on an empty workspace will cancel out any view offset. Replicate this
|
||||||
@@ -2738,7 +2763,7 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
.iter()
|
.iter()
|
||||||
.flat_map(|col| &col.tiles)
|
.flat_map(|col| &col.tiles)
|
||||||
.any(|tile| tile.window().id() == &resize.window),
|
.any(|tile| tile.window().id() == &resize.window),
|
||||||
"interactive resize window must be present on the workspace"
|
"interactive resize window must be present in the layout"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+378
-34
@@ -10,6 +10,7 @@ use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;
|
|||||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||||
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};
|
use smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};
|
||||||
|
|
||||||
|
use super::floating::{FloatingSpace, FloatingSpaceRenderElement};
|
||||||
use super::scrolling::{
|
use super::scrolling::{
|
||||||
Column, ColumnWidth, InsertHint, InsertPosition, ScrollingSpace, ScrollingSpaceRenderElement,
|
Column, ColumnWidth, InsertHint, InsertPosition, ScrollingSpace, ScrollingSpaceRenderElement,
|
||||||
};
|
};
|
||||||
@@ -29,6 +30,12 @@ pub struct Workspace<W: LayoutElement> {
|
|||||||
/// The scrollable-tiling layout.
|
/// The scrollable-tiling layout.
|
||||||
scrolling: ScrollingSpace<W>,
|
scrolling: ScrollingSpace<W>,
|
||||||
|
|
||||||
|
/// The floating layout.
|
||||||
|
floating: FloatingSpace<W>,
|
||||||
|
|
||||||
|
/// Whether the floating layout is active instead of the scrolling layout.
|
||||||
|
floating_is_active: bool,
|
||||||
|
|
||||||
/// The original output of this workspace.
|
/// The original output of this workspace.
|
||||||
///
|
///
|
||||||
/// Most of the time this will be the workspace's current output, however, after an output
|
/// Most of the time this will be the workspace's current output, however, after an output
|
||||||
@@ -112,6 +119,7 @@ impl WorkspaceId {
|
|||||||
niri_render_elements! {
|
niri_render_elements! {
|
||||||
WorkspaceRenderElement<R> => {
|
WorkspaceRenderElement<R> => {
|
||||||
Scrolling = ScrollingSpaceRenderElement<R>,
|
Scrolling = ScrollingSpaceRenderElement<R>,
|
||||||
|
Floating = FloatingSpaceRenderElement<R>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,8 +178,17 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
options.clone(),
|
options.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let floating = FloatingSpace::new(
|
||||||
|
working_area,
|
||||||
|
scale.fractional_scale(),
|
||||||
|
clock.clone(),
|
||||||
|
options.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scrolling,
|
scrolling,
|
||||||
|
floating,
|
||||||
|
floating_is_active: false,
|
||||||
original_output,
|
original_output,
|
||||||
scale,
|
scale,
|
||||||
transform: output.current_transform(),
|
transform: output.current_transform(),
|
||||||
@@ -213,8 +230,17 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
options.clone(),
|
options.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let floating = FloatingSpace::new(
|
||||||
|
working_area,
|
||||||
|
scale.fractional_scale(),
|
||||||
|
clock.clone(),
|
||||||
|
options.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scrolling,
|
scrolling,
|
||||||
|
floating,
|
||||||
|
floating_is_active: false,
|
||||||
output: None,
|
output: None,
|
||||||
scale,
|
scale,
|
||||||
transform: Transform::Normal,
|
transform: Transform::Normal,
|
||||||
@@ -255,18 +281,24 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
|
|
||||||
pub fn advance_animations(&mut self) {
|
pub fn advance_animations(&mut self) {
|
||||||
self.scrolling.advance_animations();
|
self.scrolling.advance_animations();
|
||||||
|
self.floating.advance_animations();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn are_animations_ongoing(&self) -> bool {
|
pub fn are_animations_ongoing(&self) -> bool {
|
||||||
self.scrolling.are_animations_ongoing()
|
self.scrolling.are_animations_ongoing() || self.floating.are_animations_ongoing()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn are_transitions_ongoing(&self) -> bool {
|
pub fn are_transitions_ongoing(&self) -> bool {
|
||||||
self.scrolling.are_transitions_ongoing()
|
self.scrolling.are_transitions_ongoing() || self.floating.are_animations_ongoing()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_render_elements(&mut self, is_active: bool) {
|
pub fn update_render_elements(&mut self, is_active: bool) {
|
||||||
self.scrolling.update_render_elements(is_active);
|
self.scrolling
|
||||||
|
.update_render_elements(is_active && !self.floating_is_active);
|
||||||
|
|
||||||
|
let view_rect = Rectangle::from_loc_and_size((0., 0.), self.view_size);
|
||||||
|
self.floating
|
||||||
|
.update_render_elements(is_active && self.floating_is_active, view_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_config(&mut self, base_options: Rc<Options>) {
|
pub fn update_config(&mut self, base_options: Rc<Options>) {
|
||||||
@@ -280,12 +312,19 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
options.clone(),
|
options.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.floating.update_config(
|
||||||
|
self.working_area,
|
||||||
|
self.scale.fractional_scale(),
|
||||||
|
options.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
self.base_options = base_options;
|
self.base_options = base_options;
|
||||||
self.options = options;
|
self.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_shaders(&mut self) {
|
pub fn update_shaders(&mut self) {
|
||||||
self.scrolling.update_shaders();
|
self.scrolling.update_shaders();
|
||||||
|
self.floating.update_shaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn windows(&self) -> impl Iterator<Item = &W> + '_ {
|
pub fn windows(&self) -> impl Iterator<Item = &W> + '_ {
|
||||||
@@ -297,11 +336,19 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {
|
pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {
|
||||||
self.scrolling.tiles()
|
let scrolling = self.scrolling.tiles();
|
||||||
|
let floating = self.floating.tiles();
|
||||||
|
scrolling.chain(floating)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {
|
pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {
|
||||||
self.scrolling.tiles_mut()
|
let scrolling = self.scrolling.tiles_mut();
|
||||||
|
let floating = self.floating.tiles_mut();
|
||||||
|
scrolling.chain(floating)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_floating(&self, id: &W::Id) -> bool {
|
||||||
|
self.floating.has_window(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_output(&self) -> Option<&Output> {
|
pub fn current_output(&self) -> Option<&Output> {
|
||||||
@@ -309,7 +356,11 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_window(&self) -> Option<&W> {
|
pub fn active_window(&self) -> Option<&W> {
|
||||||
self.scrolling.active_window()
|
if self.floating_is_active {
|
||||||
|
self.floating.active_window()
|
||||||
|
} else {
|
||||||
|
self.scrolling.active_window()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_active_fullscreen(&self) -> bool {
|
pub fn is_active_fullscreen(&self) -> bool {
|
||||||
@@ -391,6 +442,11 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
scale.fractional_scale(),
|
scale.fractional_scale(),
|
||||||
self.options.clone(),
|
self.options.clone(),
|
||||||
);
|
);
|
||||||
|
self.floating.update_config(
|
||||||
|
working_area,
|
||||||
|
scale.fractional_scale(),
|
||||||
|
self.options.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if scale_transform_changed {
|
if scale_transform_changed {
|
||||||
@@ -410,6 +466,7 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
activate: bool,
|
activate: bool,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
let tile = Tile::new(
|
let tile = Tile::new(
|
||||||
window,
|
window,
|
||||||
@@ -417,7 +474,12 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
self.clock.clone(),
|
self.clock.clone(),
|
||||||
self.options.clone(),
|
self.options.clone(),
|
||||||
);
|
);
|
||||||
self.add_tile(None, tile, activate, width, is_full_width);
|
|
||||||
|
if is_floating {
|
||||||
|
self.add_floating_tile(tile, None, activate);
|
||||||
|
} else {
|
||||||
|
self.add_tile(None, tile, activate, width, is_full_width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_tile(
|
pub fn add_tile(
|
||||||
@@ -431,6 +493,24 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
self.enter_output_for_window(tile.window());
|
self.enter_output_for_window(tile.window());
|
||||||
self.scrolling
|
self.scrolling
|
||||||
.add_tile(col_idx, tile, activate, width, is_full_width, None);
|
.add_tile(col_idx, tile, activate, width, is_full_width, None);
|
||||||
|
|
||||||
|
if activate {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_floating_tile(
|
||||||
|
&mut self,
|
||||||
|
tile: Tile<W>,
|
||||||
|
pos: Option<Point<f64, Logical>>,
|
||||||
|
activate: bool,
|
||||||
|
) {
|
||||||
|
self.enter_output_for_window(tile.window());
|
||||||
|
self.floating.add_tile(tile, pos, activate);
|
||||||
|
|
||||||
|
if activate || self.scrolling.is_empty() {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_tile_to_column(
|
pub fn add_tile_to_column(
|
||||||
@@ -443,6 +523,10 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
self.enter_output_for_window(tile.window());
|
self.enter_output_for_window(tile.window());
|
||||||
self.scrolling
|
self.scrolling
|
||||||
.add_tile_to_column(col_idx, tile_idx, tile, activate);
|
.add_tile_to_column(col_idx, tile_idx, tile, activate);
|
||||||
|
|
||||||
|
if activate {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_window_right_of(
|
pub fn add_window_right_of(
|
||||||
@@ -451,6 +535,8 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
window: W,
|
window: W,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
// TODO: smarter enum, so you can override is_floating = false for floating right_of.
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
let tile = Tile::new(
|
let tile = Tile::new(
|
||||||
window,
|
window,
|
||||||
@@ -458,7 +544,7 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
self.clock.clone(),
|
self.clock.clone(),
|
||||||
self.options.clone(),
|
self.options.clone(),
|
||||||
);
|
);
|
||||||
self.add_tile_right_of(right_of, tile, width, is_full_width);
|
self.add_tile_right_of(right_of, tile, width, is_full_width, is_floating);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_tile_right_of(
|
pub fn add_tile_right_of(
|
||||||
@@ -467,10 +553,36 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
tile: Tile<W>,
|
tile: Tile<W>,
|
||||||
width: ColumnWidth,
|
width: ColumnWidth,
|
||||||
is_full_width: bool,
|
is_full_width: bool,
|
||||||
|
is_floating: bool,
|
||||||
) {
|
) {
|
||||||
self.enter_output_for_window(tile.window());
|
self.enter_output_for_window(tile.window());
|
||||||
self.scrolling
|
|
||||||
.add_tile_right_of(right_of, tile, width, is_full_width);
|
let floating_has_window = self.floating.has_window(right_of);
|
||||||
|
if is_floating || floating_has_window {
|
||||||
|
if floating_has_window {
|
||||||
|
self.floating.add_tile_above(right_of, tile);
|
||||||
|
} else {
|
||||||
|
let activate = self.scrolling.active_window().unwrap().id() == right_of;
|
||||||
|
// FIXME: use static pos
|
||||||
|
let (right_of_tile, render_pos) = self
|
||||||
|
.scrolling
|
||||||
|
.tiles_with_render_positions()
|
||||||
|
.find(|(tile, _)| tile.window().id() == right_of)
|
||||||
|
.unwrap();
|
||||||
|
// Position the new tile in the center above the right_of tile. Think a dialog
|
||||||
|
// opening on top of a window.
|
||||||
|
let pos = render_pos
|
||||||
|
+ (right_of_tile.tile_size().to_point() - tile.tile_size().to_point())
|
||||||
|
.downscale(2.);
|
||||||
|
self.floating.add_tile(tile, Some(pos), activate);
|
||||||
|
if activate {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.scrolling
|
||||||
|
.add_tile_right_of(right_of, tile, width, is_full_width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_column(&mut self, column: Column<W>, activate: bool) {
|
pub fn add_column(&mut self, column: Column<W>, activate: bool) {
|
||||||
@@ -479,29 +591,66 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.scrolling.add_column(None, column, activate, None);
|
self.scrolling.add_column(None, column, activate, None);
|
||||||
|
|
||||||
|
if activate {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_focus_floating_tiling_after_removing(&mut self, removed_from_floating: bool) {
|
||||||
|
if removed_from_floating {
|
||||||
|
if self.floating.is_empty() {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Scrolling should remain focused if both are empty.
|
||||||
|
if self.scrolling.is_empty() && !self.floating.is_empty() {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_tile(&mut self, id: &W::Id, transaction: Transaction) -> RemovedTile<W> {
|
pub fn remove_tile(&mut self, id: &W::Id, transaction: Transaction) -> RemovedTile<W> {
|
||||||
let removed = self.scrolling.remove_tile(id, transaction);
|
let mut from_floating = false;
|
||||||
|
let removed = if self.floating.has_window(id) {
|
||||||
|
from_floating = true;
|
||||||
|
self.floating.remove_tile(id)
|
||||||
|
} else {
|
||||||
|
self.scrolling.remove_tile(id, transaction)
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(output) = &self.output {
|
if let Some(output) = &self.output {
|
||||||
removed.tile.window().output_leave(output);
|
removed.tile.window().output_leave(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.update_focus_floating_tiling_after_removing(from_floating);
|
||||||
|
|
||||||
removed
|
removed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_active_tile(&mut self, transaction: Transaction) -> Option<RemovedTile<W>> {
|
pub fn remove_active_tile(&mut self, transaction: Transaction) -> Option<RemovedTile<W>> {
|
||||||
let removed = self.scrolling.remove_active_tile(transaction)?;
|
let from_floating = self.floating_is_active;
|
||||||
|
let removed = if self.floating_is_active {
|
||||||
|
self.floating.remove_active_tile()?
|
||||||
|
} else {
|
||||||
|
self.scrolling.remove_active_tile(transaction)?
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(output) = &self.output {
|
if let Some(output) = &self.output {
|
||||||
removed.tile.window().output_leave(output);
|
removed.tile.window().output_leave(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.update_focus_floating_tiling_after_removing(from_floating);
|
||||||
|
|
||||||
Some(removed)
|
Some(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_active_column(&mut self) -> Option<Column<W>> {
|
pub fn remove_active_column(&mut self) -> Option<Column<W>> {
|
||||||
|
let from_floating = self.floating_is_active;
|
||||||
|
if self.floating_is_active {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let column = self.scrolling.remove_active_column()?;
|
let column = self.scrolling.remove_active_column()?;
|
||||||
|
|
||||||
if let Some(output) = &self.output {
|
if let Some(output) = &self.output {
|
||||||
@@ -510,6 +659,8 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.update_focus_floating_tiling_after_removing(from_floating);
|
||||||
|
|
||||||
Some(column)
|
Some(column)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,15 +678,21 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
pub fn new_window_size(
|
pub fn new_window_size(
|
||||||
&self,
|
&self,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
|
is_floating: bool,
|
||||||
rules: &ResolvedWindowRules,
|
rules: &ResolvedWindowRules,
|
||||||
) -> Size<i32, Logical> {
|
) -> Size<i32, Logical> {
|
||||||
self.scrolling.new_window_size(width, rules)
|
if is_floating {
|
||||||
|
Size::from((0, 0))
|
||||||
|
} else {
|
||||||
|
self.scrolling.new_window_size(width, rules)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_new_window(
|
pub fn configure_new_window(
|
||||||
&self,
|
&self,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
width: Option<ColumnWidth>,
|
width: Option<ColumnWidth>,
|
||||||
|
is_floating: bool,
|
||||||
rules: &ResolvedWindowRules,
|
rules: &ResolvedWindowRules,
|
||||||
) {
|
) {
|
||||||
window.with_surfaces(|surface, data| {
|
window.with_surfaces(|surface, data| {
|
||||||
@@ -549,10 +706,14 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
if state.states.contains(xdg_toplevel::State::Fullscreen) {
|
if state.states.contains(xdg_toplevel::State::Fullscreen) {
|
||||||
state.size = Some(self.view_size.to_i32_round());
|
state.size = Some(self.view_size.to_i32_round());
|
||||||
} else {
|
} else {
|
||||||
state.size = Some(self.new_window_size(width, rules));
|
state.size = Some(self.new_window_size(width, is_floating, rules));
|
||||||
}
|
}
|
||||||
|
|
||||||
state.bounds = Some(self.scrolling.toplevel_bounds(rules));
|
if is_floating {
|
||||||
|
state.bounds = Some(self.floating.toplevel_bounds(rules));
|
||||||
|
} else {
|
||||||
|
state.bounds = Some(self.scrolling.toplevel_bounds(rules));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,10 +790,16 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) {
|
pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) {
|
||||||
|
if window.map_or(self.floating_is_active, |id| self.floating.has_window(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.scrolling.consume_or_expel_window_left(window);
|
self.scrolling.consume_or_expel_window_left(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) {
|
pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) {
|
||||||
|
if window.map_or(self.floating_is_active, |id| self.floating.has_window(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.scrolling.consume_or_expel_window_right(window);
|
self.scrolling.consume_or_expel_window_right(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,29 +824,113 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_column_width(&mut self, change: SizeChange) {
|
pub fn set_column_width(&mut self, change: SizeChange) {
|
||||||
self.scrolling.set_column_width(change);
|
if self.floating_is_active {
|
||||||
|
self.floating.set_window_width(None, change, true);
|
||||||
|
} else {
|
||||||
|
self.scrolling.set_column_width(change);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_height(&mut self, window: Option<&W::Id>, change: SizeChange) {
|
pub fn set_window_height(&mut self, window: Option<&W::Id>, change: SizeChange) {
|
||||||
self.scrolling.set_window_height(window, change);
|
if window.map_or(self.floating_is_active, |id| self.floating.has_window(id)) {
|
||||||
|
self.floating.set_window_height(window, change, true);
|
||||||
|
} else {
|
||||||
|
self.scrolling.set_window_height(window, change);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_window_height(&mut self, window: Option<&W::Id>) {
|
pub fn reset_window_height(&mut self, window: Option<&W::Id>) {
|
||||||
|
if window.map_or(self.floating_is_active, |id| self.floating.has_window(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.scrolling.reset_window_height(window);
|
self.scrolling.reset_window_height(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_window_height(&mut self, window: Option<&W::Id>) {
|
pub fn toggle_window_height(&mut self, window: Option<&W::Id>) {
|
||||||
|
if window.map_or(self.floating_is_active, |id| self.floating.has_window(id)) {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.scrolling.toggle_window_height(window);
|
self.scrolling.toggle_window_height(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
|
pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {
|
||||||
|
if self.floating.has_window(window) {
|
||||||
|
if is_fullscreen {
|
||||||
|
self.toggle_window_floating(Some(window));
|
||||||
|
} else {
|
||||||
|
// Floating windows are never fullscreen, so this is an unfullscreen request for an
|
||||||
|
// already unfullscreen window.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
self.scrolling.set_fullscreen(window, is_fullscreen);
|
self.scrolling.set_fullscreen(window, is_fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_fullscreen(&mut self, window: &W::Id) {
|
pub fn toggle_fullscreen(&mut self, window: &W::Id) {
|
||||||
|
if self.floating.has_window(window) {
|
||||||
|
self.toggle_window_floating(Some(window));
|
||||||
|
}
|
||||||
self.scrolling.toggle_fullscreen(window);
|
self.scrolling.toggle_fullscreen(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
let Some(id) = id.cloned().or(active_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, render_pos) = self
|
||||||
|
.tiles_with_render_positions()
|
||||||
|
.find(|(tile, _)| *tile.window().id() == id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if self.floating.has_window(&id) {
|
||||||
|
let removed = self.floating.remove_tile(&id);
|
||||||
|
// FIXME: compute closest pos?
|
||||||
|
self.scrolling.add_tile(
|
||||||
|
None,
|
||||||
|
removed.tile,
|
||||||
|
target_is_active,
|
||||||
|
removed.width,
|
||||||
|
removed.is_full_width,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
if target_is_active {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut removed = self.scrolling.remove_tile(&id, Transaction::new());
|
||||||
|
removed.tile.stop_move_animations();
|
||||||
|
let pos = render_pos + Point::from((50., 50.));
|
||||||
|
self.floating
|
||||||
|
.add_tile(removed.tile, Some(pos), target_is_active);
|
||||||
|
if target_is_active {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tile, new_render_pos) = self
|
||||||
|
.tiles_with_render_positions_mut(false)
|
||||||
|
.find(|(tile, _)| *tile.window().id() == id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
tile.animate_move_from(render_pos - new_render_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_focus_floating_tiling(&mut self) {
|
||||||
|
if self.floating.is_empty() {
|
||||||
|
// If floating is empty, keep focus on scrolling.
|
||||||
|
return;
|
||||||
|
} else if self.scrolling.is_empty() {
|
||||||
|
// If floating isn't empty but scrolling is, keep focus on floating.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.floating_is_active = !self.floating_is_active;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_windows(&self) -> bool {
|
pub fn has_windows(&self) -> bool {
|
||||||
self.windows().next().is_some()
|
self.windows().next().is_some()
|
||||||
}
|
}
|
||||||
@@ -699,22 +950,34 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
pub fn tiles_with_render_positions(
|
pub fn tiles_with_render_positions(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> {
|
) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> {
|
||||||
self.scrolling.tiles_with_render_positions()
|
let scrolling = self.scrolling.tiles_with_render_positions();
|
||||||
|
let floating = self.floating.tiles_with_render_positions();
|
||||||
|
floating.chain(scrolling)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tiles_with_render_positions_mut(
|
pub fn tiles_with_render_positions_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
round: bool,
|
round: bool,
|
||||||
) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {
|
) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {
|
||||||
self.scrolling.tiles_with_render_positions_mut(round)
|
let scrolling = self.scrolling.tiles_with_render_positions_mut(round);
|
||||||
|
let floating = self.floating.tiles_with_render_positions_mut(round);
|
||||||
|
floating.chain(scrolling)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
|
pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {
|
||||||
self.scrolling.active_tile_visual_rectangle()
|
if self.floating_is_active {
|
||||||
|
self.floating.active_tile_visual_rectangle()
|
||||||
|
} else {
|
||||||
|
self.scrolling.active_tile_visual_rectangle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn popup_target_rect(&self, window: &W::Id) -> Option<Rectangle<f64, Logical>> {
|
pub fn popup_target_rect(&self, window: &W::Id) -> Option<Rectangle<f64, Logical>> {
|
||||||
self.scrolling.popup_target_rect(window)
|
if self.floating.has_window(window) {
|
||||||
|
self.floating.popup_target_rect(window)
|
||||||
|
} else {
|
||||||
|
self.scrolling.popup_target_rect(window)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_elements<R: NiriRenderer>(
|
pub fn render_elements<R: NiriRenderer>(
|
||||||
@@ -724,7 +987,15 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
) -> impl Iterator<Item = WorkspaceRenderElement<R>> {
|
) -> impl Iterator<Item = WorkspaceRenderElement<R>> {
|
||||||
let scale = Scale::from(self.scale.fractional_scale());
|
let scale = Scale::from(self.scale.fractional_scale());
|
||||||
let scrolling = self.scrolling.render_elements(renderer, scale, target);
|
let scrolling = self.scrolling.render_elements(renderer, scale, target);
|
||||||
scrolling.into_iter().map(WorkspaceRenderElement::from)
|
let scrolling = scrolling.into_iter().map(WorkspaceRenderElement::from);
|
||||||
|
|
||||||
|
let view_rect = Rectangle::from_loc_and_size((0., 0.), self.view_size);
|
||||||
|
let floating = self
|
||||||
|
.floating
|
||||||
|
.render_elements(renderer, view_rect, scale, target);
|
||||||
|
let floating = floating.into_iter().map(WorkspaceRenderElement::from);
|
||||||
|
|
||||||
|
floating.chain(scrolling)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_above_top_layer(&self) -> bool {
|
pub fn render_above_top_layer(&self) -> bool {
|
||||||
@@ -760,8 +1031,13 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
window: &W::Id,
|
window: &W::Id,
|
||||||
blocker: TransactionBlocker,
|
blocker: TransactionBlocker,
|
||||||
) {
|
) {
|
||||||
self.scrolling
|
if self.floating.has_window(window) {
|
||||||
.start_close_animation_for_window(renderer, window, blocker);
|
self.floating
|
||||||
|
.start_close_animation_for_window(renderer, window, blocker);
|
||||||
|
} else {
|
||||||
|
self.scrolling
|
||||||
|
.start_close_animation_for_window(renderer, window, blocker);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_close_animation_for_tile(
|
pub fn start_close_animation_for_tile(
|
||||||
@@ -772,10 +1048,7 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
tile_pos: Point<f64, Logical>,
|
tile_pos: Point<f64, Logical>,
|
||||||
blocker: TransactionBlocker,
|
blocker: TransactionBlocker,
|
||||||
) {
|
) {
|
||||||
// FIXME: when floating happens, put this on the floating layer. It's used for interactive
|
self.floating
|
||||||
// move, which is floating rather than scrolling.
|
|
||||||
let tile_pos = tile_pos + Point::from((self.scrolling.view_pos(), 0.));
|
|
||||||
self.scrolling
|
|
||||||
.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,19 +1102,47 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {
|
pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {
|
||||||
self.scrolling.update_window(window, serial);
|
if !self.floating.update_window(window, serial) {
|
||||||
|
self.scrolling.update_window(window, serial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh(&mut self, is_active: bool) {
|
pub fn refresh(&mut self, is_active: bool) {
|
||||||
self.scrolling.refresh(is_active);
|
self.scrolling
|
||||||
|
.refresh(is_active && !self.floating_is_active);
|
||||||
|
self.floating.refresh(is_active && self.floating_is_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {
|
pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {
|
||||||
|
if self.floating.has_window(window) {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
|
||||||
self.scrolling.scroll_amount_to_activate(window)
|
self.scrolling.scroll_amount_to_activate(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_window(&mut self, window: &W::Id) -> bool {
|
pub fn activate_window(&mut self, window: &W::Id) -> bool {
|
||||||
self.scrolling.activate_window(window)
|
if self.floating.activate_window(window) {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
true
|
||||||
|
} else if self.scrolling.activate_window(window) {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_window_without_raising(&mut self, window: &W::Id) -> bool {
|
||||||
|
if self.floating.activate_window_without_raising(window) {
|
||||||
|
self.floating_is_active = true;
|
||||||
|
true
|
||||||
|
} else if self.scrolling.activate_window(window) {
|
||||||
|
self.floating_is_active = false;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_insert_hint(&mut self, insert_hint: InsertHint) {
|
pub fn set_insert_hint(&mut self, insert_hint: InsertHint) {
|
||||||
@@ -876,7 +1177,11 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {
|
pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {
|
||||||
self.scrolling.interactive_resize_begin(window, edges)
|
if self.floating.has_window(&window) {
|
||||||
|
self.floating.interactive_resize_begin(window, edges)
|
||||||
|
} else {
|
||||||
|
self.scrolling.interactive_resize_begin(window, edges)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interactive_resize_update(
|
pub fn interactive_resize_update(
|
||||||
@@ -884,11 +1189,28 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
window: &W::Id,
|
window: &W::Id,
|
||||||
delta: Point<f64, Logical>,
|
delta: Point<f64, Logical>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.scrolling.interactive_resize_update(window, delta)
|
if self.floating.has_window(window) {
|
||||||
|
self.floating.interactive_resize_update(window, delta)
|
||||||
|
} else {
|
||||||
|
self.scrolling.interactive_resize_update(window, delta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {
|
pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {
|
||||||
self.scrolling.interactive_resize_end(window);
|
if let Some(window) = window {
|
||||||
|
if self.floating.has_window(window) {
|
||||||
|
self.floating.interactive_resize_end(Some(window));
|
||||||
|
} else {
|
||||||
|
self.scrolling.interactive_resize_end(Some(window));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.floating.interactive_resize_end(None);
|
||||||
|
self.scrolling.interactive_resize_end(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn floating_is_active(&self) -> bool {
|
||||||
|
self.floating_is_active
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -896,6 +1218,11 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
&self.scrolling
|
&self.scrolling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn floating(&self) -> &FloatingSpace<W> {
|
||||||
|
&self.floating
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn verify_invariants(&self, move_win_id: Option<&W::Id>) {
|
pub fn verify_invariants(&self, move_win_id: Option<&W::Id>) {
|
||||||
use approx::assert_abs_diff_eq;
|
use approx::assert_abs_diff_eq;
|
||||||
@@ -911,6 +1238,23 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
assert!(Rc::ptr_eq(&self.options, self.scrolling.options()));
|
assert!(Rc::ptr_eq(&self.options, self.scrolling.options()));
|
||||||
self.scrolling.verify_invariants(self.working_area);
|
self.scrolling.verify_invariants(self.working_area);
|
||||||
|
|
||||||
|
assert_eq!(self.working_area, self.floating.working_area());
|
||||||
|
assert_eq!(&self.clock, self.floating.clock());
|
||||||
|
assert!(Rc::ptr_eq(&self.options, self.floating.options()));
|
||||||
|
self.floating.verify_invariants();
|
||||||
|
|
||||||
|
if self.floating.is_empty() {
|
||||||
|
assert!(
|
||||||
|
!self.floating_is_active,
|
||||||
|
"when floating is empty it must never be active"
|
||||||
|
);
|
||||||
|
} else if self.scrolling.is_empty() {
|
||||||
|
assert!(
|
||||||
|
self.floating_is_active,
|
||||||
|
"when scrolling is empty but floating isn't, floating should be active"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (tile, tile_pos) in self.tiles_with_render_positions() {
|
for (tile, tile_pos) in self.tiles_with_render_positions() {
|
||||||
if Some(tile.window().id()) != move_win_id {
|
if Some(tile.window().id()) != move_win_id {
|
||||||
assert_eq!(tile.interactive_move_offset, Point::from((0., 0.)));
|
assert_eq!(tile.interactive_move_offset, Point::from((0., 0.)));
|
||||||
|
|||||||
+1
-1
@@ -4813,7 +4813,7 @@ impl Niri {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.layout.activate_window(window);
|
self.layout.activate_window_without_raising(window);
|
||||||
self.layer_shell_on_demand_focus = None;
|
self.layer_shell_on_demand_focus = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
@@ -12,3 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,7 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+3
-3
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,7 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
@@ -11,3 +11,4 @@ initial configure:
|
|||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 936 × 1048, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
@@ -11,3 +11,4 @@ initial configure:
|
|||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 936 × 1048, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -8,6 +8,6 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
@@ -12,3 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,7 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+3
-3
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 936 × 1048, bounds: 1888 × 1048, states: []
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,7 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
@@ -11,3 +11,4 @@ initial configure:
|
|||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
@@ -11,3 +11,4 @@ initial configure:
|
|||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]
|
size: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1920 × 1080, states: [Fullscreen]
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 616 × 688, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
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]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+1
@@ -12,3 +12,4 @@ size: 616 × 688, bounds: 1248 × 688, states: []
|
|||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
size: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-1
@@ -8,7 +8,8 @@ final monitor: headless-2
|
|||||||
final workspace: 0 (ws-2)
|
final workspace: 0 (ws-2)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
size: 1920 × 1080, bounds: 1888 × 1048, states: [Fullscreen]
|
||||||
|
size: 0 × 0, bounds: 1920 × 1080, states: []
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+2
-2
@@ -11,5 +11,5 @@ initial configure:
|
|||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 616 × 688, bounds: 1248 × 688, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
+3
-3
@@ -8,8 +8,8 @@ final monitor: headless-1
|
|||||||
final workspace: 0 (ws-1)
|
final workspace: 0 (ws-1)
|
||||||
|
|
||||||
initial configure:
|
initial configure:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
|
|
||||||
post-map configures:
|
post-map configures:
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: []
|
size: 0 × 0, bounds: 1280 × 720, states: []
|
||||||
size: 616 × 688, bounds: 1248 × 688, states: [Activated]
|
size: 0 × 0, bounds: 1280 × 720, states: [Activated]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user