mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-14 08:01:14 +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)
|
tag.toggle("1", op)
|
||||||
end)
|
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()
|
input.keybind({ mod_key }, keys.KEY_1, function()
|
||||||
tag.switch_to("1")
|
tag.switch_to("1")
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
---@field SwitchToTag { output_name: string, tag_name: string }
|
---@field SwitchToTag { output_name: string, tag_name: string }
|
||||||
---@field AddTags { output_name: string, tags: string[] }
|
---@field AddTags { output_name: string, tags: string[] }
|
||||||
---@field RemoveTags { output_name: string, tags: string[] }
|
---@field RemoveTags { output_name: string, tags: string[] }
|
||||||
|
---@field SetLayout { output_name: string, tag_name: string, layout: Layout }
|
||||||
--Outputs
|
--Outputs
|
||||||
---@field ConnectForAllOutputs { callback_id: integer }
|
---@field ConnectForAllOutputs { callback_id: integer }
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: MPL-2.0
|
-- 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 = {}
|
local tag = {}
|
||||||
|
|
||||||
---Add tags.
|
---Add tags.
|
||||||
|
@ -121,4 +126,30 @@ function tag.switch_to(name, output)
|
||||||
end
|
end
|
||||||
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
|
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
|
// 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
|
// 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)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
|
||||||
pub struct CallbackId(pub u32);
|
pub struct CallbackId(pub u32);
|
||||||
|
@ -65,6 +68,11 @@ pub enum Msg {
|
||||||
output_name: String,
|
output_name: String,
|
||||||
tags: Vec<String>,
|
tags: Vec<String>,
|
||||||
},
|
},
|
||||||
|
SetLayout {
|
||||||
|
output_name: String,
|
||||||
|
tag_name: String,
|
||||||
|
layout: Layout,
|
||||||
|
},
|
||||||
|
|
||||||
// Output management
|
// Output management
|
||||||
ConnectForAllOutputs {
|
ConnectForAllOutputs {
|
||||||
|
|
|
@ -19,7 +19,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::Interest,
|
calloop::Interest,
|
||||||
wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
protocol::{wl_buffer::WlBuffer, wl_seat::WlSeat, wl_surface::WlSurface},
|
protocol::{wl_buffer::WlBuffer, wl_seat::WlSeat, wl_surface::WlSurface},
|
||||||
Client, Resource,
|
Client, Resource,
|
||||||
|
@ -223,6 +223,12 @@ impl<B: Backend> XdgShellHandler for State<B> {
|
||||||
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||||
let window = Window::new(surface);
|
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| {
|
window.with_state(|state| {
|
||||||
state.tags = match (
|
state.tags = match (
|
||||||
&self.focus_state.focused_output,
|
&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
|
// TODO: couple this with the layouts
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Layout {
|
pub enum Layout {
|
||||||
MasterStack,
|
MasterStack,
|
||||||
Dwindle,
|
Dwindle,
|
||||||
|
Spiral,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
|
@ -176,6 +177,108 @@ impl Layout {
|
||||||
y_factor_2 += (1.0 - y_factor_2) / 2.0;
|
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| {
|
win1.with_state(|state| {
|
||||||
let new_loc = (
|
let new_loc = (
|
||||||
(output_geo.size.w as f32 * x_factor_1 + output_loc.x as f32)
|
(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 } => {
|
Msg::ConnectForAllOutputs { callback_id } => {
|
||||||
let stream = self
|
let stream = self
|
||||||
|
|
|
@ -66,13 +66,17 @@ impl Tag {
|
||||||
self.0.borrow().active
|
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;
|
self.0.borrow_mut().active = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(&self) -> Layout {
|
pub fn layout(&self) -> Layout {
|
||||||
self.0.borrow().layout
|
self.0.borrow().layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_layout(&self, layout: Layout) {
|
||||||
|
self.0.borrow_mut().layout = layout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
|
@ -81,7 +85,7 @@ impl Tag {
|
||||||
id: TagId::next(),
|
id: TagId::next(),
|
||||||
name,
|
name,
|
||||||
active: false,
|
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
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use smithay::{
|
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,
|
wayland::seat::WaylandFocus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,10 +50,17 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
|
||||||
|
|
||||||
window.toplevel().send_pending_configure();
|
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_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 => {
|
Float::Floating => {
|
||||||
window_state.floating = Float::Tiled(Some((
|
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(),
|
state.space.element_location(window).unwrap(),
|
||||||
window.geometry().size,
|
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();
|
let output = state.focus_state.focused_output.clone().unwrap();
|
||||||
state.re_layout(&output);
|
state.re_layout(&output);
|
||||||
|
|
||||||
let output = state.focus_state.focused_output.as_ref().unwrap();
|
|
||||||
let render = output.with_state(|op_state| {
|
let render = output.with_state(|op_state| {
|
||||||
state
|
state
|
||||||
.windows
|
.windows
|
||||||
|
|
Loading…
Reference in a new issue