mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-02-11 08:48:22 +01:00
Restructure layout-ing
This commit is contained in:
parent
77695c666e
commit
275d968e77
11 changed files with 440 additions and 247 deletions
|
@ -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()
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
288
src/layout.rs
288
src/layout.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
src/state.rs
51
src/state.rs
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
187
src/window.rs
187
src/window.rs
|
@ -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() {
|
||||||
|
|
|
@ -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![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue