Restructure layout-ing

This commit is contained in:
Ottatop 2023-08-11 10:08:38 -05:00
parent 77695c666e
commit 275d968e77
11 changed files with 440 additions and 247 deletions

View file

@ -228,7 +228,8 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
None, None,
); );
layer_map_for_output(&output).arrange(); layer_map_for_output(&output).arrange();
state.re_layout(&output); state.update_windows(&output);
// state.re_layout(&output);
} }
WinitEvent::Focus(_) => {} WinitEvent::Focus(_) => {}
WinitEvent::Input(input_evt) => { WinitEvent::Input(input_evt) => {
@ -299,6 +300,7 @@ pub fn run_winit() -> Result<(), Box<dyn Error>> {
state state
.tags .tags
.iter() .iter()
.filter(|tag| tag.active())
.find(|tag| tag.fullscreen_window().is_some()) .find(|tag| tag.fullscreen_window().is_some())
.cloned() .cloned()
}) })

View file

@ -20,7 +20,7 @@ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
window::{ window::{
window_state::{Float, WindowResizeState}, window_state::{LocationRequestState, Status},
WindowElement, WindowElement,
}, },
}; };
@ -35,21 +35,22 @@ pub struct MoveSurfaceGrab<S: SeatHandler> {
impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> { impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
fn motion( fn motion(
&mut self, &mut self,
data: &mut State<B>, state: &mut State<B>,
handle: &mut PointerInnerHandle<'_, State<B>>, handle: &mut PointerInnerHandle<'_, State<B>>,
_focus: Option<(<State<B> as SeatHandler>::PointerFocus, Point<i32, Logical>)>, _focus: Option<(<State<B> as SeatHandler>::PointerFocus, Point<i32, Logical>)>,
event: &MotionEvent, event: &MotionEvent,
) { ) {
handle.motion(data, None, event); handle.motion(state, None, event);
if !self.window.alive() { if !self.window.alive() {
handle.unset_grab(data, event.serial, event.time); handle.unset_grab(state, event.serial, event.time);
return; return;
} }
data.space.raise_element(&self.window, false); state.space.raise_element(&self.window, false);
if let WindowElement::X11(surface) = &self.window { if let WindowElement::X11(surface) = &self.window {
data.xwm state
.xwm
.as_mut() .as_mut()
.expect("no xwm") .expect("no xwm")
.raise_window(surface) .raise_window(surface)
@ -59,17 +60,17 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
// tracing::info!("window geo is: {:?}", self.window.geometry()); // tracing::info!("window geo is: {:?}", self.window.geometry());
// tracing::info!("loc is: {:?}", data.space.element_location(&self.window)); // tracing::info!("loc is: {:?}", data.space.element_location(&self.window));
let tiled = self.window.with_state(|state| state.floating.is_tiled()); let tiled = self.window.with_state(|state| state.status.is_tiled());
if tiled { if tiled {
// INFO: this is being used instead of space.element_under(event.location) because that // INFO: this is being used instead of space.element_under(event.location) because that
// | uses the bounding box, which is different from the actual geometry // | uses the bounding box, which is different from the actual geometry
let window_under = data let window_under = state
.space .space
.elements() .elements()
.rev() .rev()
.find(|&win| { .find(|&win| {
if let Some(loc) = data.space.element_location(win) { if let Some(loc) = state.space.element_location(win) {
let size = win.geometry().size; let size = win.geometry().size;
let rect = Rectangle { size, loc }; let rect = Rectangle { size, loc };
rect.contains(event.location.to_i32_round()) rect.contains(event.location.to_i32_round())
@ -84,28 +85,36 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
return; return;
} }
let is_floating = window_under.with_state(|state| state.floating.is_floating()); let is_floating = window_under.with_state(|state| state.status.is_floating());
if is_floating { if is_floating {
return; return;
} }
let has_pending_resize = window_under let has_pending_resize = window_under.with_state(|state| {
.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle)); !matches!(state.loc_request_state, LocationRequestState::Idle)
});
if has_pending_resize { if has_pending_resize {
return; return;
} }
data.swap_window_positions(&self.window, &window_under); state.swap_window_positions(&self.window, &window_under);
} }
} else { } else {
let delta = event.location - self.start_data.location; let delta = event.location - self.start_data.location;
let new_loc = (self.initial_window_loc.to_f64() + delta).to_i32_round(); let new_loc = (self.initial_window_loc.to_f64() + delta).to_i32_round();
data.space.map_element(self.window.clone(), new_loc, true); state.space.map_element(self.window.clone(), new_loc, true);
let size = state
.space
.element_geometry(&self.window)
.expect("window wasn't mapped")
.size;
self.window.with_state(|state| { self.window.with_state(|state| {
if state.floating.is_floating() { if state.status.is_floating() {
state.floating = Float::Floating(new_loc); state.status = Status::Floating(Rectangle::from_loc_and_size(new_loc, size));
} }
}); });
if let WindowElement::X11(surface) = &self.window { if let WindowElement::X11(surface) = &self.window {

View file

@ -18,7 +18,7 @@ use smithay::{
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
window::{window_state::Float, WindowElement}, window::{window_state::Status, WindowElement},
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -325,10 +325,15 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
if new_loc.x.is_some() || new_loc.y.is_some() { if new_loc.x.is_some() || new_loc.y.is_some() {
state.space.map_element(window.clone(), window_loc, false); state.space.map_element(window.clone(), window_loc, false);
let size = state
.space
.element_geometry(&window)
.expect("called element_geometry on unmapped window")
.size;
window.with_state(|state| { window.with_state(|state| {
if state.floating.is_floating() { if state.status.is_floating() {
state.floating = Float::Floating(window_loc); state.status = Status::Floating(Rectangle::from_loc_and_size(window_loc, size));
} }
}); });
@ -360,7 +365,7 @@ pub fn resize_request_client<B: Backend>(
return; return;
}; };
if window.with_state(|state| state.floating.is_tiled()) { if window.with_state(|state| state.status.is_tiled()) {
return; return;
} }
@ -407,7 +412,7 @@ pub fn resize_request_server<B: Backend>(
return; return;
}; };
if window.with_state(|state| state.floating.is_tiled()) { if window.with_state(|state| state.status.is_tiled()) {
return; return;
} }

View file

@ -50,7 +50,7 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{CalloopData, ClientState, State, WithState}, state::{CalloopData, ClientState, State, WithState},
window::{window_state::WindowResizeState, WindowElement}, window::{window_state::LocationRequestState, WindowElement},
}; };
impl<B: Backend> BufferHandler for State<B> { impl<B: Backend> BufferHandler for State<B> {
@ -120,8 +120,8 @@ impl<B: Backend> CompositorHandler for State<B> {
if let Some(window) = self.window_for_surface(surface) { if let Some(window) = self.window_for_surface(surface) {
window.with_state(|state| { window.with_state(|state| {
if let WindowResizeState::Acknowledged(new_pos) = state.resize_state { if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state {
state.resize_state = WindowResizeState::Idle; state.loc_request_state = LocationRequestState::Idle;
if window.is_x11() { if window.is_x11() {
tracing::warn!("did something with X11 window here"); tracing::warn!("did something with X11 window here");
} }
@ -443,7 +443,8 @@ impl<B: Backend> WlrLayerShellHandler for State<B> {
// TODO: instead of deferring by 1 cycle, actually check if the surface has committed // TODO: instead of deferring by 1 cycle, actually check if the surface has committed
// | before re-layouting // | before re-layouting
self.loop_handle.insert_idle(move |data| { self.loop_handle.insert_idle(move |data| {
data.state.re_layout(&output); data.state.update_windows(&output);
// data.state.re_layout(&output);
}); });
} }
@ -466,7 +467,8 @@ impl<B: Backend> WlrLayerShellHandler for State<B> {
// | before re-layouting // | before re-layouting
if let Some(output) = output { if let Some(output) = output {
self.loop_handle.insert_idle(move |data| { self.loop_handle.insert_idle(move |data| {
data.state.re_layout(&output); data.state.update_windows(&output);
// data.state.re_layout(&output);
}); });
} }
} }

View file

@ -30,7 +30,7 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{State, WithState}, state::{State, WithState},
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER}, window::{window_state::LocationRequestState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
}; };
impl<B: Backend> XdgShellHandler for State<B> { impl<B: Backend> XdgShellHandler for State<B> {
@ -95,17 +95,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
self.windows.push(window.clone()); self.windows.push(window.clone());
// self.space.map_element(window.clone(), (0, 0), true); // self.space.map_element(window.clone(), (0, 0), true);
if let Some(focused_output) = self.focus_state.focused_output.clone() { if let Some(focused_output) = self.focus_state.focused_output.clone() {
focused_output.with_state(|state| { self.update_windows(&focused_output);
let first_tag = state.focused_tags().next();
if let Some(first_tag) = first_tag {
first_tag.layout().layout(
self.windows.clone(),
state.focused_tags().cloned().collect(),
&mut self.space,
&focused_output,
);
}
});
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst); BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
tracing::debug!( tracing::debug!(
"blocker {}", "blocker {}",
@ -277,12 +267,13 @@ impl<B: Backend> XdgShellHandler for State<B> {
fn ack_configure(&mut self, surface: WlSurface, configure: Configure) { fn ack_configure(&mut self, surface: WlSurface, configure: Configure) {
if let Some(window) = self.window_for_surface(&surface) { if let Some(window) = self.window_for_surface(&surface) {
window.with_state(|state| { window.with_state(|state| {
if let WindowResizeState::Requested(serial, new_loc) = state.resize_state { if let LocationRequestState::Requested(serial, new_loc) = state.loc_request_state {
match &configure { match &configure {
Configure::Toplevel(configure) => { Configure::Toplevel(configure) => {
if configure.serial >= serial { if configure.serial >= serial {
// tracing::debug!("acked configure, new loc is {:?}", new_loc); // tracing::debug!("acked configure, new loc is {:?}", new_loc);
state.resize_state = WindowResizeState::Acknowledged(new_loc); state.loc_request_state =
LocationRequestState::Acknowledged(new_loc);
if let Some(focused_output) = if let Some(focused_output) =
self.focus_state.focused_output.clone() self.focus_state.focused_output.clone()
{ {

View file

@ -28,7 +28,7 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{CalloopData, WithState}, state::{CalloopData, WithState},
window::{window_state::Float, WindowBlocker, WindowElement, BLOCKER_COUNTER}, window::{window_state::Status, WindowBlocker, WindowElement, BLOCKER_COUNTER},
}; };
impl<B: Backend> XwmHandler for CalloopData<B> { impl<B: Backend> XwmHandler for CalloopData<B> {
@ -128,7 +128,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
if should_float(surface) { if should_float(surface) {
window.with_state(|state| { window.with_state(|state| {
state.floating = Float::Floating(loc); state.status = Status::Floating(bbox);
}); });
} }
@ -276,9 +276,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
self.state self.state
.windows .windows
.retain(|elem| win.wl_surface() != elem.wl_surface()); .retain(|elem| win.wl_surface() != elem.wl_surface());
if win.with_state(|state| state.floating.is_tiled()) { if win.with_state(|state| state.status.is_tiled()) {
if let Some(output) = win.output(&self.state) { if let Some(output) = win.output(&self.state) {
self.state.re_layout(&output); self.state.update_windows(&output);
// self.state.re_layout(&output);
} }
} }
} }

View file

@ -78,6 +78,7 @@ impl<B: Backend> State<B> {
[output] [output]
.into_iter() .into_iter()
.flat_map(|op| op.with_state(|state| state.tags.clone())) .flat_map(|op| op.with_state(|state| state.tags.clone()))
.filter(|tag| tag.active())
.find(|tag| tag.fullscreen_window().is_some()) .find(|tag| tag.fullscreen_window().is_some())
.and_then(|tag| tag.fullscreen_window()) .and_then(|tag| tag.fullscreen_window())
.map(|window| (FocusTarget::from(window), output_geo.loc)) .map(|window| (FocusTarget::from(window), output_geo.loc))

View file

@ -4,16 +4,133 @@ use itertools::{Either, Itertools};
use smithay::{ use smithay::{
desktop::{layer_map_for_output, Space}, desktop::{layer_map_for_output, Space},
output::Output, output::Output,
utils::{Logical, Rectangle, Size}, utils::{Logical, Point, Rectangle, Size},
}; };
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
tag::Tag, tag::Tag,
window::WindowElement, window::{
window_state::{LocationRequestState, Status},
WindowElement,
},
}; };
// -------------------------------------------
impl<B: Backend> State<B> {
/// Compute the positions and sizes of tiled windows on
/// `output` according to the provided [`Layout`].
///
/// This will call `request_size_change` on tiled windows.
fn tile_windows(&self, output: &Output, windows: Vec<WindowElement>, layout: Layout) {
let Some(rect) = self.space.output_geometry(output).map(|op_geo| {
let map = layer_map_for_output(output);
if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
op_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(op_geo.loc + zone.loc, zone.size)
}
}) else {
// TODO: maybe default to something like 800x800 like in anvil so people still see
// | windows open
tracing::error!("Failed to get output geometry");
return;
};
match layout {
Layout::MasterStack => master_stack(windows, rect),
Layout::Dwindle => dwindle(windows, rect),
Layout::Spiral => spiral(windows, rect),
layout @ (Layout::CornerTopLeft
| Layout::CornerTopRight
| Layout::CornerBottomLeft
| Layout::CornerBottomRight) => corner(&layout, windows, rect),
}
}
pub fn update_windows(&mut self, output: &Output) {
let Some(layout) = output.with_state(|state| {
state.focused_tags().next().cloned().map(|tag| tag.layout())
}) else { return };
let (windows_on_foc_tags, windows_not_on_foc_tags): (Vec<_>, _) =
output.with_state(|state| {
let focused_tags = state.focused_tags().collect::<Vec<_>>();
self.windows.iter().cloned().partition(|win| {
win.with_state(|state| state.tags.iter().any(|tg| focused_tags.contains(&tg)))
})
});
let tiled_windows = windows_on_foc_tags
.iter()
.filter(|win| win.with_state(|state| state.status.is_tiled()))
.cloned()
.collect::<Vec<_>>();
self.tile_windows(output, tiled_windows, layout);
let output_geo = self.space.output_geometry(output).expect("no output geo");
for window in windows_on_foc_tags.iter() {
match window.with_state(|state| state.status) {
Status::Fullscreen(_) => {
window.change_geometry(output_geo);
}
Status::Maximized(_) => {
let map = layer_map_for_output(output);
let geo = if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the
// | output res, even when there are no layer surfaces mapped. In this case, we
// | just return the output geometry.
output_geo
} else {
let zone = map.non_exclusive_zone();
tracing::debug!("non_exclusive_zone is {zone:?}");
Rectangle::from_loc_and_size(output_geo.loc + zone.loc, zone.size)
};
window.change_geometry(geo);
}
_ => (),
}
}
for window in windows_on_foc_tags.iter() {
window.with_state(|state| {
if let LocationRequestState::Sent(loc) = state.loc_request_state {
match &window {
WindowElement::Wayland(window) => {
let serial = window.toplevel().send_configure();
state.loc_request_state = LocationRequestState::Requested(serial, loc);
}
WindowElement::X11(_) => {
// already configured, just need to map
// maybe wait for all wayland windows to commit before mapping
self.space.map_element(window.clone(), loc, false);
state.loc_request_state = LocationRequestState::Idle;
}
}
}
});
}
self.loop_handle.insert_idle(|data| {
crate::state::schedule_on_commit(data, windows_on_foc_tags, |dt| {
for win in windows_not_on_foc_tags {
dt.state.space.unmap_elem(&win);
}
})
});
}
}
// -------------------------------------------
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Layout { pub enum Layout {
MasterStack, MasterStack,
@ -57,22 +174,18 @@ impl Layout {
tracing::debug!("Laying out with rect {rect:?}"); tracing::debug!("Laying out with rect {rect:?}");
match self { match self {
Layout::MasterStack => master_stack(windows, space, rect), Layout::MasterStack => master_stack(windows, rect),
Layout::Dwindle => dwindle(windows, space, rect), Layout::Dwindle => dwindle(windows, rect),
Layout::Spiral => spiral(windows, space, rect), Layout::Spiral => spiral(windows, rect),
layout @ (Layout::CornerTopLeft layout @ (Layout::CornerTopLeft
| Layout::CornerTopRight | Layout::CornerTopRight
| Layout::CornerBottomLeft | Layout::CornerBottomLeft
| Layout::CornerBottomRight) => corner(layout, windows, space, rect), | Layout::CornerBottomRight) => corner(layout, windows, rect),
} }
} }
} }
fn master_stack( fn master_stack(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
let size = rect.size; let size = rect.size;
let loc = rect.loc; let loc = rect.loc;
@ -85,11 +198,11 @@ fn master_stack(
if stack_count == 0 { if stack_count == 0 {
// one window // one window
master.request_size_change(space, loc, size); master.change_geometry(Rectangle::from_loc_and_size(loc, size));
} else { } else {
let loc = (loc.x, loc.y).into(); let loc: Point<i32, Logical> = (loc.x, loc.y).into();
let new_master_size: Size<i32, Logical> = (size.w / 2, size.h).into(); let new_master_size: Size<i32, Logical> = (size.w / 2, size.h).into();
master.request_size_change(space, loc, new_master_size); master.change_geometry(Rectangle::from_loc_and_size(loc, new_master_size));
let stack_count = stack_count; let stack_count = stack_count;
@ -105,20 +218,15 @@ fn master_stack(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in stack.enumerate() { for (i, win) in stack.enumerate() {
win.request_size_change( win.change_geometry(Rectangle::from_loc_and_size(
space, Point::from((size.w / 2 + loc.x, y_s[i] + loc.y)),
(size.w / 2 + loc.x, y_s[i] + loc.y).into(), Size::from((size.w / 2, i32::max(heights[i], 40))),
(size.w / 2, i32::max(heights[i], 40)).into(), ));
);
} }
} }
} }
fn dwindle( fn dwindle(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
let size = rect.size; let size = rect.size;
let loc = rect.loc; let loc = rect.loc;
@ -126,7 +234,7 @@ fn dwindle(
if iter.peek().is_none() { if iter.peek().is_none() {
if let Some(window) = windows.first() { if let Some(window) = windows.first() {
window.request_size_change(space, loc, size); window.change_geometry(Rectangle::from_loc_and_size(loc, size));
} }
} else { } else {
let mut win1_size = size; let mut win1_size = size;
@ -146,41 +254,35 @@ fn dwindle(
Slice::Right => { Slice::Right => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space,
win1_loc, win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
); ));
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
Slice::Below => { Slice::Below => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space,
win1_loc, win1_loc,
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
); ));
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
} }
} }
} }
} }
fn spiral( fn spiral(windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
let size = rect.size; let size = rect.size;
let loc = rect.loc; let loc = rect.loc;
@ -188,7 +290,7 @@ fn spiral(
if iter.peek().is_none() { if iter.peek().is_none() {
if let Some(window) = windows.first() { if let Some(window) = windows.first() {
window.request_size_change(space, loc, size); window.change_geometry(Rectangle::from_loc_and_size(loc, size));
} }
} else { } else {
let mut win1_loc = loc; let mut win1_loc = loc;
@ -217,80 +319,73 @@ fn spiral(
Slice::Above => { Slice::Above => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space, Point::from((win1_loc.x, win1_loc.y + height_partition)),
(win1_loc.x, win1_loc.y + height_partition).into(), Size::from((win1_size.w, i32::max(win1_size.h - height_partition, 40))),
(win1_size.w, i32::max(win1_size.h - height_partition, 40)).into(), ));
);
win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
Slice::Below => { Slice::Below => {
let height_partition = win1_size.h / 2; let height_partition = win1_size.h / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space,
win1_loc, win1_loc,
(win1_size.w, win1_size.h - i32::max(height_partition, 40)).into(), Size::from((win1_size.w, win1_size.h - i32::max(height_partition, 40))),
); ));
win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into(); win1_loc = (win1_loc.x, win1_loc.y + (win1_size.h - height_partition)).into();
win1_size = (win1_size.w, i32::max(height_partition, 40)).into(); win1_size = (win1_size.w, i32::max(height_partition, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
Slice::Left => { Slice::Left => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space, Point::from((win1_loc.x + width_partition, win1_loc.y)),
(win1_loc.x + width_partition, win1_loc.y).into(), Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), ));
);
win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
Slice::Right => { Slice::Right => {
let width_partition = win1_size.w / 2; let width_partition = win1_size.w / 2;
win1.request_size_change( win1.change_geometry(Rectangle::from_loc_and_size(
space,
win1_loc, win1_loc,
(win1_size.w - width_partition, i32::max(win1_size.h, 40)).into(), Size::from((win1_size.w - width_partition, i32::max(win1_size.h, 40))),
); ));
win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into(); win1_loc = (win1_loc.x + (win1_size.w - width_partition), win1_loc.y).into();
win1_size = (width_partition, i32::max(win1_size.h, 40)).into(); win1_size = (width_partition, i32::max(win1_size.h, 40)).into();
win2.request_size_change(space, win1_loc, win1_size); win2.change_geometry(Rectangle::from_loc_and_size(win1_loc, win1_size));
} }
} }
} }
} }
} }
fn corner( fn corner(layout: &Layout, windows: Vec<WindowElement>, rect: Rectangle<i32, Logical>) {
layout: &Layout,
windows: Vec<WindowElement>,
space: &mut Space<WindowElement>,
rect: Rectangle<i32, Logical>,
) {
let size = rect.size; let size = rect.size;
let loc = rect.loc; let loc = rect.loc;
match windows.len() { match windows.len() {
0 => (), 0 => (),
1 => { 1 => {
windows[0].request_size_change(space, loc, size); windows[0].change_geometry(rect);
} }
2 => { 2 => {
windows[0].request_size_change(space, loc, (size.w / 2, size.h).into()); windows[0].change_geometry(Rectangle::from_loc_and_size(
loc,
Size::from((size.w / 2, size.h)),
));
windows[1].request_size_change( windows[1].change_geometry(Rectangle::from_loc_and_size(
space, Point::from((loc.x + size.w / 2, loc.y)),
(loc.x + size.w / 2, loc.y).into(), Size::from((size.w / 2, size.h)),
(size.w / 2, size.h).into(), ));
);
} }
_ => { _ => {
let mut windows = windows.into_iter(); let mut windows = windows.into_iter();
@ -306,9 +401,8 @@ fn corner(
let div_factor = 2; let div_factor = 2;
corner.request_size_change( corner.change_geometry(Rectangle::from_loc_and_size(
space, Point::from(match layout {
match layout {
Layout::CornerTopLeft => (loc.x, loc.y), Layout::CornerTopLeft => (loc.x, loc.y),
Layout::CornerTopRight => (loc.x + size.w - size.w / div_factor, loc.y), Layout::CornerTopRight => (loc.x + size.w - size.w / div_factor, loc.y),
Layout::CornerBottomLeft => (loc.x, loc.y + size.h - size.h / div_factor), Layout::CornerBottomLeft => (loc.x, loc.y + size.h - size.h / div_factor),
@ -317,10 +411,9 @@ fn corner(
loc.y + size.h - size.h / div_factor, loc.y + size.h - size.h / div_factor,
), ),
_ => unreachable!(), _ => unreachable!(),
} }),
.into(), Size::from((size.w / div_factor, size.h / div_factor)),
(size.w / div_factor, size.h / div_factor).into(), ));
);
let vert_stack_count = vert_stack.len(); let vert_stack_count = vert_stack.len();
@ -336,19 +429,17 @@ fn corner(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in vert_stack.iter().enumerate() { for (i, win) in vert_stack.iter().enumerate() {
win.request_size_change( win.change_geometry(Rectangle::from_loc_and_size(
space, Point::from((
(
match layout { match layout {
Layout::CornerTopLeft | Layout::CornerBottomLeft => size.w / 2 + loc.x, Layout::CornerTopLeft | Layout::CornerBottomLeft => size.w / 2 + loc.x,
Layout::CornerTopRight | Layout::CornerBottomRight => loc.x, Layout::CornerTopRight | Layout::CornerBottomRight => loc.x,
_ => unreachable!(), _ => unreachable!(),
}, },
y_s[i] + loc.y, y_s[i] + loc.y,
) )),
.into(), Size::from((size.w / 2, i32::max(heights[i], 40))),
(size.w / 2, i32::max(heights[i], 40)).into(), ));
);
} }
let horiz_stack_count = horiz_stack.len(); let horiz_stack_count = horiz_stack.len();
@ -365,18 +456,16 @@ fn corner(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for (i, win) in horiz_stack.iter().enumerate() { for (i, win) in horiz_stack.iter().enumerate() {
win.request_size_change( win.change_geometry(Rectangle::from_loc_and_size(
space, Point::from(match layout {
match layout {
Layout::CornerTopLeft => (x_s[i] + loc.x, loc.y + size.h / 2), Layout::CornerTopLeft => (x_s[i] + loc.x, loc.y + size.h / 2),
Layout::CornerTopRight => (x_s[i] + loc.x + size.w / 2, loc.y + size.h / 2), Layout::CornerTopRight => (x_s[i] + loc.x + size.w / 2, loc.y + size.h / 2),
Layout::CornerBottomLeft => (x_s[i] + loc.x, loc.y), Layout::CornerBottomLeft => (x_s[i] + loc.x, loc.y),
Layout::CornerBottomRight => (x_s[i] + loc.x + size.w / 2, loc.y), Layout::CornerBottomRight => (x_s[i] + loc.x + size.w / 2, loc.y),
_ => unreachable!(), _ => unreachable!(),
} }),
.into(), Size::from((i32::max(widths[i], 1), size.h / 2)),
(i32::max(widths[i], 1), size.h / 2).into(), ));
);
} }
} }
} }
@ -385,7 +474,7 @@ fn corner(
fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> { fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> {
windows windows
.iter() .iter()
.filter(|window| window.with_state(|state| state.floating.is_tiled())) .filter(|window| window.with_state(|state| state.status.is_tiled()))
.filter(|window| { .filter(|window| {
window.with_state(|state| { window.with_state(|state| {
for tag in state.tags.iter() { for tag in state.tags.iter() {
@ -421,6 +510,7 @@ impl<B: Backend> State<B> {
.focused_output .focused_output
.clone() .clone()
.expect("no focused output"); .expect("no focused output");
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
} }

View file

@ -21,7 +21,7 @@ use crate::{
grab::resize_grab::ResizeSurfaceState, grab::resize_grab::ResizeSurfaceState,
tag::Tag, tag::Tag,
window::{ window::{
window_state::{Float, WindowResizeState}, window_state::{LocationRequestState, Status},
WindowElement, WindowElement,
}, },
}; };
@ -187,7 +187,8 @@ impl<B: Backend> State<B> {
state.tags = vec![tag.clone()]; state.tags = vec![tag.clone()];
}); });
let Some(output) = tag.output(self) else { return }; let Some(output) = tag.output(self) else { return };
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
Msg::ToggleTagOnWindow { window_id, tag_id } => { Msg::ToggleTagOnWindow { window_id, tag_id } => {
let Some(window) = window_id.window(self) else { return }; let Some(window) = window_id.window(self) else { return };
@ -202,14 +203,16 @@ impl<B: Backend> State<B> {
}); });
let Some(output) = tag.output(self) else { return }; let Some(output) = tag.output(self) else { return };
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
Msg::ToggleTag { tag_id } => { Msg::ToggleTag { tag_id } => {
tracing::debug!("ToggleTag"); tracing::debug!("ToggleTag");
if let Some(tag) = tag_id.tag(self) { if let Some(tag) = tag_id.tag(self) {
tag.set_active(!tag.active()); tag.set_active(!tag.active());
if let Some(output) = tag.output(self) { if let Some(output) = tag.output(self) {
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
} }
} }
@ -222,7 +225,8 @@ impl<B: Backend> State<B> {
} }
tag.set_active(true); tag.set_active(true);
}); });
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
Msg::AddTags { Msg::AddTags {
output_name, output_name,
@ -252,7 +256,8 @@ impl<B: Backend> State<B> {
let Some(tag) = tag_id.tag(self) else { return }; let Some(tag) = tag_id.tag(self) else { return };
tag.set_layout(layout); tag.set_layout(layout);
let Some(output) = tag.output(self) else { return }; let Some(output) = tag.output(self) else { return };
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
Msg::ConnectForAllOutputs { callback_id } => { Msg::ConnectForAllOutputs { callback_id } => {
@ -288,7 +293,8 @@ impl<B: Backend> State<B> {
output.change_current_state(None, None, None, Some(loc)); output.change_current_state(None, None, None, Some(loc));
self.space.map_output(&output, loc); self.space.map_output(&output, loc);
tracing::debug!("mapping output {} to {loc:?}", output.name()); tracing::debug!("mapping output {} to {loc:?}", output.name());
self.re_layout(&output); self.update_windows(&output);
// self.re_layout(&output);
} }
Msg::Quit => { Msg::Quit => {
@ -358,7 +364,7 @@ impl<B: Backend> State<B> {
}); });
let floating = window let floating = window
.as_ref() .as_ref()
.map(|win| win.with_state(|state| state.floating.is_floating())); .map(|win| win.with_state(|state| state.status.is_floating()));
let focused = window.as_ref().and_then(|win| { let focused = window.as_ref().and_then(|win| {
self.focus_state self.focus_state
.current_focus() // TODO: actual focus .current_focus() // TODO: actual focus
@ -645,14 +651,6 @@ impl<B: Backend> State<B> {
} }
pub fn re_layout(&mut self, output: &Output) { pub fn re_layout(&mut self, output: &Output) {
for win in self.windows.iter() {
if win.is_wayland() {
win.with_state(|state| {
tracing::debug!("{:?}", state.resize_state);
})
}
}
let windows = self let windows = self
.windows .windows
.iter() .iter()
@ -705,16 +703,20 @@ impl<B: Backend> State<B> {
} }
} }
for (win, loc) in clone.into_iter().filter_map(|win| { for (win, rect) in
match win.with_state(|state| state.floating.clone()) { clone
Float::Tiled(_) => None, .into_iter()
Float::Floating(loc) => Some((win, loc)), .filter_map(|win| match win.with_state(|state| state.status) {
} Status::Floating(loc) => Some((win, loc)),
}) { _ => None,
})
{
if let WindowElement::X11(surface) = &win { if let WindowElement::X11(surface) = &win {
if !surface.is_override_redirect() {
surface.set_mapped(true).expect("failed to map x11 win"); surface.set_mapped(true).expect("failed to map x11 win");
} }
dt.state.space.map_element(win, loc, false); }
dt.state.space.map_element(win, rect.loc, false);
} }
}); });
}); });
@ -731,7 +733,8 @@ pub fn schedule_on_commit<F, B: Backend>(
F: FnOnce(&mut CalloopData<B>) + 'static, F: FnOnce(&mut CalloopData<B>) + 'static,
{ {
for window in windows.iter().filter(|win| win.alive()) { for window in windows.iter().filter(|win| win.alive()) {
if window.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle)) { if window.with_state(|state| !matches!(state.loc_request_state, LocationRequestState::Idle))
{
data.state.loop_handle.insert_idle(|data| { data.state.loop_handle.insert_idle(|data| {
schedule_on_commit(data, windows, on_commit); schedule_on_commit(data, windows, on_commit);
}); });

View file

@ -40,7 +40,7 @@ use crate::{
state::{State, WithState}, state::{State, WithState},
}; };
use self::window_state::{Float, WindowElementState, WindowResizeState}; use self::window_state::{LocationRequestState, Status, WindowElementState};
pub mod window_state; pub mod window_state;
@ -180,6 +180,29 @@ impl WindowElement {
} }
} }
/// Send a geometry change without mapping windows or sending
/// configures to Wayland windows.
///
/// Xwayland windows will still receive a configure.
// TODO: ^ does that make things flicker?
pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) {
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.size = Some(new_geo.size);
});
}
WindowElement::X11(surface) => {
surface
.configure(new_geo)
.expect("failed to configure x11 win");
}
}
self.with_state(|state| {
state.loc_request_state = LocationRequestState::Sent(new_geo.loc);
});
}
/// Request a size and loc change. /// Request a size and loc change.
pub fn request_size_change( pub fn request_size_change(
&self, &self,
@ -193,8 +216,8 @@ impl WindowElement {
state.size = Some(new_size); state.size = Some(new_size);
}); });
self.with_state(|state| { self.with_state(|state| {
state.resize_state = state.loc_request_state =
WindowResizeState::Requested(window.toplevel().send_configure(), new_loc) LocationRequestState::Requested(window.toplevel().send_configure(), new_loc)
}); });
} }
WindowElement::X11(surface) => { WindowElement::X11(surface) => {
@ -245,6 +268,107 @@ impl WindowElement {
pub fn is_x11(&self) -> bool { pub fn is_x11(&self) -> bool {
matches!(self, Self::X11(..)) matches!(self, Self::X11(..))
} }
/// Set this window to floating.
///
/// This will change the size of the window only.
/// Call `State.update_windows` to perform mapping.
pub fn set_floating(&self) {
let status = self.with_state(|state| state.status);
match status {
Status::Floating(_) => (),
Status::Tiled(rect) | Status::Fullscreen(rect) | Status::Maximized(rect) => {
if let Some(rect) = rect {
self.change_geometry(rect);
self.with_state(|state| state.status = Status::Floating(rect));
} else {
// TODO: is this the same as from the space? prolly not
let geo = self.geometry();
self.with_state(|state| state.status = Status::Floating(geo));
}
}
}
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.unset(xdg_toplevel::State::TiledTop);
state.states.unset(xdg_toplevel::State::TiledBottom);
state.states.unset(xdg_toplevel::State::TiledLeft);
state.states.unset(xdg_toplevel::State::TiledRight);
});
}
}
/// Call compute_tiled_windows after this
pub fn set_tiled(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Tiled(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
pub fn set_fullscreen(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Fullscreen(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.set(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
pub fn set_maximized(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Maximized(Some(geo)));
if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::Maximized);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
} }
impl IsAlive for WindowElement { impl IsAlive for WindowElement {
@ -480,54 +604,10 @@ impl<B: Backend> State<B> {
/// Toggle a window's floating status. /// Toggle a window's floating status.
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) { pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) {
let mut resize: Option<_> = None; if window.with_state(|state| state.status.is_floating()) {
window.with_state(|window_state| { window.set_tiled();
match window_state.floating { } else {
Float::Tiled(prev_loc_and_size) => { window.set_floating();
if let Some((prev_loc, prev_size)) = prev_loc_and_size {
resize = Some((prev_loc, prev_size));
}
window_state.floating =
Float::Floating(resize.map(|(point, _)| point).unwrap_or_else(|| {
state
.space
.element_location(window)
.unwrap_or((0, 0).into())
}));
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| {
tl_state.states.unset(xdg_toplevel::State::TiledTop);
tl_state.states.unset(xdg_toplevel::State::TiledBottom);
tl_state.states.unset(xdg_toplevel::State::TiledLeft);
tl_state.states.unset(xdg_toplevel::State::TiledRight);
});
} // TODO: tiled states for x11
}
Float::Floating(current_loc) => {
window_state.floating = Float::Tiled(Some((
// We get the location this way because window.geometry().loc
// doesn't seem to be the actual location
// TODO: maybe store the location in state
current_loc,
window.geometry().size,
)));
if let WindowElement::Wayland(window) = window {
window.toplevel().with_pending_state(|tl_state| {
tl_state.states.set(xdg_toplevel::State::TiledTop);
tl_state.states.set(xdg_toplevel::State::TiledBottom);
tl_state.states.set(xdg_toplevel::State::TiledLeft);
tl_state.states.set(xdg_toplevel::State::TiledRight);
});
}
}
}
});
if let Some((prev_loc, prev_size)) = resize {
window.request_size_change(&mut state.space, prev_loc, prev_size);
} }
// TODO: don't use the focused output, use the one the window is on // TODO: don't use the focused output, use the one the window is on
@ -536,8 +616,9 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
.focused_output .focused_output
.clone() .clone()
.expect("no focused output"); .expect("no focused output");
state.re_layout(&output); // state.re_layout(&output);
state.update_windows(&output);
let render = output.with_state(|op_state| { let render = output.with_state(|op_state| {
state state
.windows .windows
@ -545,7 +626,7 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
.cloned() .cloned()
.filter(|win| { .filter(|win| {
win.with_state(|win_state| { win.with_state(|win_state| {
if win_state.floating.is_floating() { if win_state.status.is_floating() {
return true; return true;
} }
for tag in win_state.tags.iter() { for tag in win_state.tags.iter() {

View file

@ -2,13 +2,12 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
fmt,
sync::atomic::{AtomicU32, Ordering}, sync::atomic::{AtomicU32, Ordering},
}; };
use smithay::{ use smithay::{
desktop::Window, desktop::Window,
utils::{Logical, Point, Serial, Size}, utils::{Logical, Point, Rectangle, Serial},
}; };
use crate::{ use crate::{
@ -67,10 +66,10 @@ impl WithState for Window {
pub struct WindowElementState { pub struct WindowElementState {
/// The id of this window. /// The id of this window.
pub id: WindowId, pub id: WindowId,
/// Whether the window is floating or tiled. /// Whether the window is floating, tiled, fullscreen, or maximized.
pub floating: Float, pub status: Status,
/// The window's resize state. See [WindowResizeState] for more. /// The window's resize state. See [WindowResizeState] for more.
pub resize_state: WindowResizeState, pub loc_request_state: LocationRequestState,
/// What tags the window is currently on. /// What tags the window is currently on.
pub tags: Vec<Tag>, pub tags: Vec<Tag>,
} }
@ -99,11 +98,12 @@ pub struct WindowElementState {
/// [`resize_state`]: WindowState#structfield.resize_state /// [`resize_state`]: WindowState#structfield.resize_state
/// [`XdgShellHandler.ack_configure()`]: smithay::wayland::shell::xdg::XdgShellHandler#method.ack_configure /// [`XdgShellHandler.ack_configure()`]: smithay::wayland::shell::xdg::XdgShellHandler#method.ack_configure
/// [`CompositorHandler.commit()`]: smithay::wayland::compositor::CompositorHandler#tymethod.commit /// [`CompositorHandler.commit()`]: smithay::wayland::compositor::CompositorHandler#tymethod.commit
#[derive(Default, Clone)] #[derive(Debug, Default, Clone)]
pub enum WindowResizeState { pub enum LocationRequestState {
/// The window doesn't need to be moved. /// The window doesn't need to be moved.
#[default] #[default]
Idle, Idle,
Sent(Point<i32, Logical>),
/// The window has received a configure request with a new size. The desired location and the /// The window has received a configure request with a new size. The desired location and the
/// configure request's serial should be provided here. /// configure request's serial should be provided here.
Requested(Serial, Point<i32, Logical>), Requested(Serial, Point<i32, Logical>),
@ -114,24 +114,16 @@ pub enum WindowResizeState {
Acknowledged(Point<i32, Logical>), Acknowledged(Point<i32, Logical>),
} }
impl fmt::Debug for WindowResizeState { /// Whether the window is floating, tiled, fullscreen, or maximized.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[derive(Debug, Clone, Copy)]
match self { pub enum Status {
Self::Idle => write!(f, "Idle"), Floating(Rectangle<i32, Logical>),
Self::Requested(_arg0, _arg1) => write!(f, "Requested"), Tiled(Option<Rectangle<i32, Logical>>),
Self::Acknowledged(_arg0) => write!(f, "Acknowledged"), Fullscreen(Option<Rectangle<i32, Logical>>),
} Maximized(Option<Rectangle<i32, Logical>>),
}
} }
#[derive(Debug, Clone)] impl Status {
pub enum Float {
/// The previous location and size of the window when it was floating, if any.
Tiled(Option<(Point<i32, Logical>, Size<i32, Logical>)>),
Floating(Point<i32, Logical>),
}
impl Float {
/// Returns `true` if the float is [`Tiled`]. /// Returns `true` if the float is [`Tiled`].
/// ///
/// [`Tiled`]: Float::Tiled /// [`Tiled`]: Float::Tiled
@ -147,6 +139,22 @@ impl Float {
pub fn is_floating(&self) -> bool { pub fn is_floating(&self) -> bool {
matches!(self, Self::Floating(_)) matches!(self, Self::Floating(_))
} }
/// Returns `true` if the status is [`Fullscreen`].
///
/// [`Fullscreen`]: Status::Fullscreen
#[must_use]
pub fn is_fullscreen(&self) -> bool {
matches!(self, Self::Fullscreen(..))
}
/// Returns `true` if the status is [`Maximized`].
///
/// [`Maximized`]: Status::Maximized
#[must_use]
pub fn is_maximized(&self) -> bool {
matches!(self, Self::Maximized(..))
}
} }
impl WindowElementState { impl WindowElementState {
@ -161,8 +169,8 @@ impl Default for WindowElementState {
Self { Self {
// INFO: I think this will assign the id on use of the state, not on window spawn. // INFO: I think this will assign the id on use of the state, not on window spawn.
id: WindowId::next(), id: WindowId::next(),
floating: Float::Tiled(None), status: Status::Tiled(None),
resize_state: WindowResizeState::Idle, loc_request_state: LocationRequestState::Idle,
tags: vec![], tags: vec![],
} }
} }