Add spiral layout

This commit is contained in:
Ottatop 2023-07-12 18:50:41 -05:00 committed by Ottatop
parent 6f57d8d413
commit f1508350e3
9 changed files with 202 additions and 8 deletions

View file

@ -65,6 +65,19 @@ require("pinnacle").setup(function(pinnacle)
tag.toggle("1", op)
end)
---@type Layout[]
local layouts = { "MasterStack", "Dwindle", "Spiral" }
local index = 1
input.keybind({ mod_key }, keys.space, function()
tag.set_layout("1", layouts[index])
if index + 1 > #layouts then
index = 1
else
index = index + 1
end
end)
input.keybind({ mod_key }, keys.KEY_1, function()
tag.switch_to("1")
end)

View file

@ -23,6 +23,7 @@
---@field SwitchToTag { output_name: string, tag_name: string }
---@field AddTags { output_name: string, tags: string[] }
---@field RemoveTags { output_name: string, tags: string[] }
---@field SetLayout { output_name: string, tag_name: string, layout: Layout }
--Outputs
---@field ConnectForAllOutputs { callback_id: integer }

View file

@ -4,6 +4,11 @@
--
-- SPDX-License-Identifier: MPL-2.0
---@alias Layout
---| "MasterStack" # One master window on the left with all other windows stacked to the right.
---| "Dwindle" # Windows split in half towards the bottom right corner.
---| "Spiral" # Windows split in half in a spiral.
local tag = {}
---Add tags.
@ -121,4 +126,30 @@ function tag.switch_to(name, output)
end
end
---Set a layout for the tag on the specified output. If there is none, set it for the tag on the currently focused one.
---@param name string The name of the tag.
---@param layout Layout The layout.
---@param output Output? The output.
function tag.set_layout(name, layout, output)
if output ~= nil then
SendMsg({
SetLayout = {
output_name = output.name,
tag_name = name,
layout = layout,
},
})
else
local op = require("output").get_focused()
if op ~= nil then
SendMsg({
SetLayout = {
output_name = op.name,
tag_name = name,
layout = layout,
},
})
end
end
end
return tag

View file

@ -7,7 +7,10 @@
// The MessagePack format for these is a one-element map where the element's key is the enum name and its
// value is a map of the enum's values
use crate::window::{window_state::WindowId, WindowProperties};
use crate::{
layout::Layout,
window::{window_state::WindowId, WindowProperties},
};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32);
@ -65,6 +68,11 @@ pub enum Msg {
output_name: String,
tags: Vec<String>,
},
SetLayout {
output_name: String,
tag_name: String,
layout: Layout,
},
// Output management
ConnectForAllOutputs {

View file

@ -19,7 +19,7 @@ use smithay::{
},
reexports::{
calloop::Interest,
wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
wayland_server::{
protocol::{wl_buffer::WlBuffer, wl_seat::WlSeat, wl_surface::WlSurface},
Client, Resource,
@ -223,6 +223,12 @@ impl<B: Backend> XdgShellHandler for State<B> {
fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = Window::new(surface);
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);
});
window.with_state(|state| {
state.tags = match (
&self.focus_state.focused_output,

View file

@ -25,10 +25,11 @@ pub enum Direction {
}
// TODO: couple this with the layouts
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Layout {
MasterStack,
Dwindle,
Spiral,
}
impl Layout {
@ -176,6 +177,108 @@ impl Layout {
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,
);
});
}
}
}
Layout::Spiral => {
let mut iter = windows.windows(2).peekable();
let Some(output_geo) = space.output_geometry(output) else {
tracing::error!("could not get output geometry");
return;
};
let output_loc = output.current_location();
if iter.peek().is_none() {
if let Some(window) = windows.first() {
window.toplevel().with_pending_state(|state| {
state.size = Some(output_geo.size);
});
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 = 0.0;
let mut y_factor_1: f32;
let mut x_factor_2: f32 = 0.0;
let mut y_factor_2: f32;
fn series(n: u32) -> f32 {
(0..n)
.map(|n| (-1i32).pow(n) as f32 * (1.0 / 2.0_f32.powi(n as i32)))
.sum()
}
for (i, wins) in iter.enumerate() {
let win1 = &wins[0];
let win2 = &wins[1];
if i % 2 == 0 {
div_factor_w *= 2;
} else {
div_factor_h *= 2;
}
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);
});
y_factor_1 = x_factor_1;
y_factor_2 = x_factor_2;
x_factor_1 = {
let first = (i / 4) * 2;
let indices = [first, first + 2, first + 3, first + 2];
series(indices[i % 4] as u32)
};
x_factor_2 = series((i as u32 / 4 + 1) * 2);
win1.with_state(|state| {
let new_loc = (
(output_geo.size.w as f32 * x_factor_1 + output_loc.x as f32)

View file

@ -271,6 +271,18 @@ impl<B: Backend> State<B> {
});
}
}
Msg::SetLayout { output_name, tag_name, layout } => {
let output = self.space.outputs().find(|op| op.name() == output_name).cloned();
if let Some(output) = output {
output.with_state(|state| {
if let Some(tag) = state.tags.iter_mut().find(|tag| tag.name() == tag_name) {
tag.set_layout(layout);
}
});
self.re_layout(&output);
}
}
Msg::ConnectForAllOutputs { callback_id } => {
let stream = self

View file

@ -66,13 +66,17 @@ impl Tag {
self.0.borrow().active
}
pub fn set_active(&mut self, active: bool) {
pub fn set_active(&self, active: bool) {
self.0.borrow_mut().active = active;
}
pub fn layout(&self) -> Layout {
self.0.borrow().layout
}
pub fn set_layout(&self, layout: Layout) {
self.0.borrow_mut().layout = layout;
}
}
impl Tag {
@ -81,7 +85,7 @@ impl Tag {
id: TagId::next(),
name,
active: false,
layout: Layout::Dwindle, // TODO: get from config
layout: Layout::MasterStack, // TODO: get from config
})))
}
}

View file

@ -5,7 +5,11 @@
// SPDX-License-Identifier: MPL-2.0
use smithay::{
desktop::Window, reexports::wayland_server::protocol::wl_surface::WlSurface,
desktop::Window,
reexports::{
wayland_protocols::xdg::shell::server::xdg_toplevel,
wayland_server::protocol::wl_surface::WlSurface,
},
wayland::seat::WaylandFocus,
};
@ -46,10 +50,17 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
window.toplevel().send_pending_configure();
state.space.map_element(window.clone(), prev_loc, false); // TODO: should it activate?
state.space.map_element(window.clone(), prev_loc, false);
// TODO: should it activate?
}
window_state.floating = Float::Floating;
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);
});
}
Float::Floating => {
window_state.floating = Float::Tiled(Some((
@ -58,6 +69,12 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
state.space.element_location(window).unwrap(),
window.geometry().size,
)));
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);
});
}
}
});
@ -65,7 +82,6 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
let output = state.focus_state.focused_output.clone().unwrap();
state.re_layout(&output);
let output = state.focus_state.focused_output.as_ref().unwrap();
let render = output.with_state(|op_state| {
state
.windows