Add dwindle layout

This commit is contained in:
Ottatop 2023-07-11 21:07:51 -05:00 committed by Ottatop
parent 3efdb9d73f
commit 6f57d8d413
8 changed files with 209 additions and 134 deletions

View file

@ -112,7 +112,7 @@ function output.get_by_res(width, height)
return outputs
end
---Get the currently focused output. This is currently the one with the cursor on it.
---Get the currently focused output. This is currently implemented as the one with the cursor on it.
---@return Output|nil output The output, or nil if none are focused.
function output.get_focused()
SendMsg({

View file

@ -47,12 +47,10 @@ pub enum Msg {
},
// Tag management
// FIXME: tag_id should not be a string
ToggleTag {
output_name: String,
tag_name: String,
},
// FIXME: tag_id should not be a string
SwitchToTag {
output_name: String,
tag_name: String,
@ -91,6 +89,7 @@ pub enum Msg {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RequestId(pub u32);
#[allow(clippy::enum_variant_names)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
/// Messages that require a server response, usually to provide some data.
pub enum Request {

View file

@ -47,7 +47,6 @@ use smithay::{
use crate::{
backend::Backend,
layout::{Layout, LayoutVec},
state::{ClientState, State, WithState},
window::window_state::WindowResizeState,
};
@ -230,10 +229,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
self.space.outputs().next(),
) {
(Some(output), _) | (None, Some(output)) => output.with_state(|state| {
let output_tags = state
.focused_tags()
.map(|tag| tag.clone())
.collect::<Vec<_>>();
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
if !output_tags.is_empty() {
output_tags
} else if let Some(first_tag) = state.tags.first() {
@ -265,13 +261,15 @@ impl<B: Backend> XdgShellHandler for State<B> {
self.loop_handle.insert_idle(move |data| {
if let Some(focused_output) = &data.state.focus_state.focused_output {
focused_output.with_state(|state| {
data.state
.windows
.to_master_stack(
let first_tag = state.focused_tags().next();
if let Some(first_tag) = first_tag {
first_tag.layout().layout(
data.state.windows.clone(),
state.focused_tags().cloned().collect(),
&data.state.space,
focused_output,
state.focused_tags().map(|tag| tag.clone()).collect(),
)
.layout(&data.state.space, focused_output);
);
}
});
}
});
@ -282,12 +280,15 @@ impl<B: Backend> XdgShellHandler for State<B> {
self.windows.retain(|window| window.toplevel() != &surface);
if let Some(focused_output) = self.focus_state.focused_output.as_ref() {
focused_output.with_state(|state| {
self.windows
.to_master_stack(
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(),
&self.space,
focused_output,
state.focused_tags().map(|tag| tag.clone()).collect(),
)
.layout(&self.space, focused_output);
);
}
});
}

View file

@ -5,7 +5,7 @@
// SPDX-License-Identifier: MPL-2.0
use smithay::{
desktop::{space::SpaceElement, Space, Window},
desktop::{Space, Window},
output::Output,
utils::{Logical, Size},
};
@ -24,127 +24,187 @@ pub enum Direction {
Bottom,
}
pub trait Layout<S: SpaceElement> {
fn layout(&self, space: &Space<S>, output: &Output);
// TODO: couple this with the layouts
#[derive(Debug, Clone, Copy)]
pub enum Layout {
MasterStack,
Dwindle,
}
pub struct MasterStack<S: SpaceElement> {
inner: Vec<S>,
output: Output,
}
impl Layout {
pub fn layout(
&self,
windows: Vec<Window>,
tags: Vec<Tag>,
space: &Space<Window>,
output: &Output,
) {
let windows = filter_windows(&windows, tags);
match self {
Layout::MasterStack => {
let master = windows.first();
let stack = windows.iter().skip(1);
impl MasterStack<Window> {
pub fn master(&self) -> Option<&Window> {
self.inner.first()
}
let Some(master) = master else { return };
pub fn stack(&self) -> impl Iterator<Item = &Window> {
self.inner.iter().skip(1)
}
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
fn layout_stack(&self, space: &Space<Window>, output: &Output) {
let stack_count = self.stack().count();
let output_loc = output.current_location();
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
let stack_count = stack.clone().count();
let output_loc = output.current_location();
if stack_count == 0 {
// one window
master.toplevel().with_pending_state(|state| {
state.size = Some(output_geo.size);
});
let height = output_geo.size.h / stack_count as i32;
master.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
master.toplevel().send_configure(),
(output_loc.x, output_loc.y).into(),
);
});
} else {
let new_master_size: Size<i32, Logical> =
(output_geo.size.w / 2, output_geo.size.h).into();
master.toplevel().with_pending_state(|state| {
state.size = Some(new_master_size);
});
master.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
master.toplevel().send_configure(),
(output_loc.x, output_loc.y).into(),
);
});
for (i, win) in self.stack().enumerate() {
win.toplevel().with_pending_state(|state| {
state.size = Some((output_geo.size.w / 2, height).into());
});
let stack_count = stack_count;
win.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
win.toplevel().send_configure(),
(
output_geo.size.w / 2 + output_loc.x,
i as i32 * height + output_loc.y,
)
.into(),
);
});
}
}
}
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
impl Layout<Window> for MasterStack<Window> {
fn layout(&self, space: &Space<Window>, output: &Output) {
let Some(master) = self.master() else {
return;
};
let output_loc = output.current_location();
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
let height = output_geo.size.h / stack_count as i32;
let output_loc = output.current_location();
for (i, win) in stack.enumerate() {
win.toplevel().with_pending_state(|state| {
state.size = Some((output_geo.size.w / 2, height).into());
});
if self.stack().count() == 0 {
// one window
master.toplevel().with_pending_state(|state| {
state.size = Some(output_geo.size);
});
win.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
win.toplevel().send_configure(),
(
output_geo.size.w / 2 + output_loc.x,
i as i32 * height + output_loc.y,
)
.into(),
);
});
}
}
}
Layout::Dwindle => {
let mut iter = windows.windows(2).peekable();
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
master.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
master.toplevel().send_configure(),
(output_loc.x, output_loc.y).into(),
);
});
} else {
let new_master_size: Size<i32, Logical> =
(output_geo.size.w / 2, output_geo.size.h).into();
master.toplevel().with_pending_state(|state| {
state.size = Some(new_master_size);
});
master.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
master.toplevel().send_configure(),
(output_loc.x, output_loc.y).into(),
);
});
let output_loc = output.current_location();
self.layout_stack(space, output);
}
}
}
if iter.peek().is_none() {
if let Some(window) = windows.first() {
window.toplevel().with_pending_state(|state| {
state.size = Some(output_geo.size);
});
pub struct Dwindle<S: SpaceElement> {
inner: Vec<S>,
output: Output,
}
window.with_state(|state| {
state.resize_state = WindowResizeState::WaitingForAck(
window.toplevel().send_configure(),
(output_loc.x, output_loc.y).into(),
);
});
}
} else {
let mut div_factor_w = 1;
let mut div_factor_h = 1;
let mut x_factor_1: f32;
let mut y_factor_1: f32;
let mut x_factor_2: f32 = 0.0;
let mut y_factor_2: f32 = 0.0;
impl Layout<Window> for Dwindle<Window> {
fn layout(&self, space: &Space<Window>, output: &Output) {
todo!()
}
}
for (i, wins) in iter.enumerate() {
let win1 = &wins[0];
let win2 = &wins[1];
pub trait LayoutVec<S: SpaceElement> {
/// Interpret this vec as a master-stack layout.
fn to_master_stack(&self, output: &Output, tags: Vec<Tag>) -> MasterStack<S>;
fn to_dwindle(&self, output: &Output, tags: Vec<Tag>) -> Dwindle<S>;
}
if i % 2 == 0 {
div_factor_w *= 2;
} else {
div_factor_h *= 2;
}
impl LayoutVec<Window> for Vec<Window> {
fn to_master_stack(&self, output: &Output, tags: Vec<Tag>) -> MasterStack<Window> {
MasterStack {
inner: filter_windows(self, tags),
output: output.clone(), // TODO: get rid of?
}
}
win1.toplevel().with_pending_state(|state| {
let new_size = (
output_geo.size.w / div_factor_w,
output_geo.size.h / div_factor_h,
)
.into();
state.size = Some(new_size);
});
win2.toplevel().with_pending_state(|state| {
let new_size = (
output_geo.size.w / div_factor_w,
output_geo.size.h / div_factor_h,
)
.into();
state.size = Some(new_size);
});
fn to_dwindle(&self, output: &Output, tags: Vec<Tag>) -> Dwindle<Window> {
Dwindle {
inner: filter_windows(self, tags),
output: output.clone(),
x_factor_1 = x_factor_2;
y_factor_1 = y_factor_2;
if i % 2 == 0 {
x_factor_2 += (1.0 - x_factor_2) / 2.0;
} else {
y_factor_2 += (1.0 - y_factor_2) / 2.0;
}
win1.with_state(|state| {
let new_loc = (
(output_geo.size.w as f32 * x_factor_1 + output_loc.x as f32)
as i32,
(output_geo.size.h as f32 * y_factor_1 + output_loc.y as f32)
as i32,
)
.into();
state.resize_state = WindowResizeState::WaitingForAck(
win1.toplevel().send_configure(),
new_loc,
);
});
win2.with_state(|state| {
let new_loc = (
(output_geo.size.w as f32 * x_factor_2 + output_loc.x as f32)
as i32,
(output_geo.size.h as f32 * y_factor_2 + output_loc.y as f32)
as i32,
)
.into();
state.resize_state = WindowResizeState::WaitingForAck(
win2.toplevel().send_configure(),
new_loc,
);
});
}
}
}
}
}
}

View file

@ -38,7 +38,7 @@ impl WithState for Output {
}
impl OutputState {
pub fn focused_tags(&mut self) -> impl Iterator<Item = &mut Tag> {
self.tags.iter_mut().filter(|tag| tag.active())
pub fn focused_tags(&self) -> impl Iterator<Item = &Tag> {
self.tags.iter().filter(|tag| tag.active())
}
}

View file

@ -21,7 +21,6 @@ use crate::{
},
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
layout::{Layout, LayoutVec},
tag::Tag,
window::{window_state::WindowResizeState, WindowProperties},
};
@ -187,7 +186,7 @@ impl<B: Backend> State<B> {
.with_state(|op_state| {
let tag = op_state.tags.iter().find(|tag| tag.name() == tag_id);
if let Some(tag) = tag {
if state.tags.contains(&tag) {
if state.tags.contains(tag) {
state.tags.retain(|tg| tg != tag);
} else {
state.tags.push(tag.clone());
@ -638,15 +637,18 @@ impl<B: Backend> State<B> {
pub fn re_layout(&mut self, output: &Output) {
let windows = self.windows.iter().filter(|win| {
win.with_state(|state| state.tags.iter().any(|tag| self.output_for_tag(tag).is_some_and(|op| &op == output)))
}).cloned().collect::<Vec<_>>();
win.with_state(|state| state.tags.iter().any(|tag| self.output_for_tag(tag).is_some_and(|op| &op == output)))
}).cloned().collect::<Vec<_>>();
let (render, do_not_render) = output.with_state(|state| {
self.windows
.to_master_stack(
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(),
&self.space,
output,
state.focused_tags().map(|tag| tag.clone()).collect(),
)
.layout(&self.space, output);
);
}
windows.iter().cloned().partition::<Vec<_>, _>(|win| {
win.with_state(|win_state| {

View file

@ -15,6 +15,7 @@ use smithay::output::Output;
use crate::{
backend::Backend,
layout::Layout,
state::{State, WithState},
};
@ -37,7 +38,8 @@ struct TagInner {
name: String,
/// Whether this tag is active or not.
active: bool,
// TODO: layout
/// What layout this tag has.
layout: Layout,
}
impl PartialEq for TagInner {
@ -67,6 +69,10 @@ impl Tag {
pub fn set_active(&mut self, active: bool) {
self.0.borrow_mut().active = active;
}
pub fn layout(&self) -> Layout {
self.0.borrow().layout
}
}
impl Tag {
@ -75,6 +81,7 @@ impl Tag {
id: TagId::next(),
name,
active: false,
layout: Layout::Dwindle, // TODO: get from config
})))
}
}

View file

@ -14,7 +14,7 @@ use smithay::{
use crate::{
backend::Backend,
grab::{move_grab::MoveSurfaceGrab, resize_grab::ResizeSurfaceGrab},
state::State,
state::{State, WithState},
};
pub fn move_request<B: Backend>(
@ -94,6 +94,9 @@ pub fn resize_request<B: Backend>(
if let Some(start_data) = crate::pointer::pointer_grab_start_data(&pointer, wl_surface, serial)
{
let window = state.window_for_surface(wl_surface).unwrap();
if window.with_state(|state| state.floating.is_tiled()) {
return;
}
let initial_window_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size;
@ -124,13 +127,16 @@ pub fn resize_request_force<B: Backend>(
edges: xdg_toplevel::ResizeEdge,
button_used: u32,
) {
println!("resize_request_force started with edges {:?}", edges);
let wl_surface = surface.wl_surface();
let pointer = seat.get_pointer().unwrap();
let window = state.window_for_surface(wl_surface).unwrap();
if window.with_state(|state| state.floating.is_tiled()) {
return;
}
let initial_window_loc = state.space.element_location(&window).unwrap();
let initial_window_size = window.geometry().size;