mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-13 08:01:05 +01:00
Add dwindle layout
This commit is contained in:
parent
3efdb9d73f
commit
6f57d8d413
8 changed files with 209 additions and 134 deletions
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
262
src/layout.rs
262
src/layout.rs
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
20
src/state.rs
20
src/state.rs
|
@ -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| {
|
||||
|
|
|
@ -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
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue