mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Add spiral layout
This commit is contained in:
parent
6f57d8d413
commit
f1508350e3
9 changed files with 202 additions and 8 deletions
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
105
src/layout.rs
105
src/layout.rs
|
@ -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)
|
||||
|
|
12
src/state.rs
12
src/state.rs
|
@ -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
|
||||
|
|
|
@ -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
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue