Remove old layout system completely

This commit is contained in:
Ottatop 2024-03-15 21:12:05 -05:00
parent a98777c11e
commit 75852551e2
8 changed files with 3 additions and 408 deletions

View file

@ -24,7 +24,6 @@ build = {
["pinnacle.output"] = "pinnacle/output.lua",
["pinnacle.process"] = "pinnacle/process.lua",
["pinnacle.tag"] = "pinnacle/tag.lua",
["pinnacle.tag.layout"] = "pinnacle/tag/layout.lua",
["pinnacle.window"] = "pinnacle/window.lua",
["pinnacle.util"] = "pinnacle/util.lua",
["pinnacle.signal"] = "pinnacle/signal.lua",

View file

@ -14,9 +14,6 @@ local rpc_types = {
OutputConnect = {
response_type = "OutputConnectResponse",
},
Layout = {
response_type = "LayoutResponse",
},
WindowPointerEnter = {
response_type = "WindowPointerEnterResponse",
},
@ -65,17 +62,6 @@ local signals = {
---@type fun(response: table)
on_response = nil,
},
Layout = {
---@nodoc
---@type H2Stream?
sender = nil,
---@nodoc
---@type (fun(tag: TagHandle, windows: WindowHandle[]))[]
callbacks = {},
---@nodoc
---@type fun(response: table)
on_response = nil,
},
WindowPointerEnter = {
---@nodoc
---@type H2Stream?
@ -108,17 +94,6 @@ signals.OutputConnect.on_response = function(response)
end
end
signals.Layout.on_response = function(response)
---@diagnostic disable-next-line: invisible
local window_handles = require("pinnacle.window").handle.new_from_table(response.window_ids or {})
---@diagnostic disable-next-line: invisible
local tag_handle = require("pinnacle.tag").handle.new(response.tag_id)
for _, callback in ipairs(signals.Layout.callbacks) do
callback(tag_handle, window_handles)
end
end
signals.WindowPointerEnter.on_response = function(response)
---@diagnostic disable-next-line: invisible
local window_handle = require("pinnacle.window").handle.new(response.window_id)

View file

@ -17,7 +17,6 @@ local rpc_types = {
response_type = "AddResponse",
},
Remove = {},
SetLayout = {},
Get = {
response_type = "GetResponse",
},
@ -211,165 +210,6 @@ function tag.remove(tags)
client.unary_request(build_grpc_request_params("Remove", { tag_ids = ids }))
end
---@class LayoutCycler
---@field next fun(output: OutputHandle?)
---@field prev fun(output: OutputHandle?)
---Create a layout cycler that will cycle layouts on the given output.
---
---This returns a `LayoutCycler` table with two fields, both functions that take in an optional `OutputHandle`:
--- - `next`: Cycle to the next layout on the given output
--- - `prev`: Cycle to the previous layout on the given output
---
---If the output isn't specified then the focused one will be used.
---
---Internally, this will only change the layout of the first active tag on the output
---because that is the one that determines the layout.
---
---### Example
---```lua
--- ---@type LayoutCycler[]
---local layouts = {
--- "master_stack",
--- "dwindle",
--- "corner_top_left",
--- "corner_top_right".
---} -- Only cycle between these four layouts
---
---local layout_cycler = Tag.new_layout_cycler()
---
--- -- Assume the focused output starts with the "master_stack" layout
---layout_cycler.next() -- Layout is now "dwindle"
---layout_cycler.next() -- Layout is now "corner_top_left"
---layout_cycler.next() -- Layout is now "corner_top_right"
---layout_cycler.next() -- Layout is now "master_stack"
---layout_cycler.next() -- Layout is now "dwindle"
---
--- -- Cycling on another output
---layout_cycler.next(Output.get_by_name("eDP-1"))
---layout_cycler.prev(Output.get_by_name("HDMI-1"))
---```
---
---@param layouts LayoutOld[]
---
---@return LayoutCycler
function tag.new_layout_cycler(layouts)
local indices = {}
if #layouts == 0 then
return {
next = function(_) end,
prev = function(_) end,
}
end
---@type LayoutCycler
return {
next = function(output)
---@diagnostic disable-next-line: redefined-local
local output = output or require("pinnacle.output").get_focused()
if not output then
return
end
local tags = output:props().tags or {}
for _, tg in ipairs(tags) do
if tg:props().active then
local id = tg.id
if #layouts == 1 then
indices[id] = 1
elseif indices[id] == nil then
indices[id] = 2
else
if indices[id] + 1 > #layouts then
indices[id] = 1
else
indices[id] = indices[id] + 1
end
end
tg:set_layout(layouts[indices[id]])
break
end
end
end,
prev = function(output)
---@diagnostic disable-next-line: redefined-local
local output = output or require("pinnacle.output").get_focused()
if not output then
return
end
local tags = output:props().tags or {}
for _, tg in ipairs(tags) do
if tg:props().active then
local id = tg.id
if #layouts == 1 then
indices[id] = 1
elseif indices[id] == nil then
indices[id] = #layouts - 1
else
if indices[id] - 1 < 1 then
indices[id] = #layouts
else
indices[id] = indices[id] - 1
end
end
tg:set_layout(layouts[indices[id]])
break
end
end
end,
}
end
local signal_name_to_SignalName = {
layout = "Layout",
}
---@class TagSignal Signals related to tag events.
---@field layout fun(tag: TagHandle, windows: WindowHandle[])? The compositor requested a layout of the given tiled windows. You'll also receive the first active tag.
---Connect to a tag signal.
---
---The compositor sends signals about various events. Use this function to run a callback when
---some tag signal occurs.
---
---This function returns a table of signal handles with each handle stored at the same key used
---to connect to the signal. See `SignalHandles` for more information.
---
---# Example
---```lua
---Tag.connect_signal({
--- layout = function(tag, windows)
--- print("Compositor requested a layout")
--- end
---})
---```
---
---@param signals TagSignal The signal you want to connect to
---
---@return SignalHandles signal_handles Handles to every signal you connected to wrapped in a table, with keys being the same as the connected signal.
---
---@see SignalHandles.disconnect_all - To disconnect from these signals
function tag.connect_signal(signals)
---@diagnostic disable-next-line: invisible
local handles = require("pinnacle.signal").handles.new({})
for signal, callback in pairs(signals) do
require("pinnacle.signal").add_callback(signal_name_to_SignalName[signal], callback)
---@diagnostic disable-next-line: invisible
local handle = require("pinnacle.signal").handle.new(signal_name_to_SignalName[signal], callback)
handles[signal] = handle
end
return handles
end
---Remove this tag.
---
---### Example
@ -384,45 +224,6 @@ function TagHandle:remove()
client.unary_request(build_grpc_request_params("Remove", { tag_ids = { self.id } }))
end
local layout_name_to_code = {
master_stack = 1,
dwindle = 2,
spiral = 3,
corner_top_left = 4,
corner_top_right = 5,
corner_bottom_left = 6,
corner_bottom_right = 7,
}
---@alias LayoutOld
---| "master_stack" # 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.
---| "corner_top_left" # One main corner window in the top left with a column of windows on the right and a row on the bottom.
---| "corner_top_right" # One main corner window in the top right with a column of windows on the left and a row on the bottom.
---| "corner_bottom_left" # One main corner window in the bottom left with a column of windows on the right and a row on the top.
---| "corner_bottom_right" # One main corner window in the bottom right with a column of windows on the left and a row on the top.
---Set this tag's layout.
---
---If this is the first active tag on its output, its layout will be used to tile windows.
---
---### Example
---```lua
--- -- Assume the focused output has tag "Tag"
---Tag.get("Tag"):set_layout("dwindle")
---```
---
---@param layout LayoutOld
function TagHandle:set_layout(layout)
---@diagnostic disable-next-line: redefined-local
local layout = layout_name_to_code[layout]
client.unary_request(build_grpc_request_params("SetLayout", {
tag_id = self.id,
layout = layout,
}))
end
---Activate this tag and deactivate all other ones on the same output.
---
---### Example

View file

@ -1,4 +0,0 @@
---@class LayoutModule
local layout = {}
return layout

View file

@ -26,21 +26,6 @@ message RemoveRequest {
repeated uint32 tag_ids = 1;
}
message SetLayoutRequest {
optional uint32 tag_id = 1;
enum Layout {
LAYOUT_UNSPECIFIED = 0;
LAYOUT_MASTER_STACK = 1;
LAYOUT_DWINDLE = 2;
LAYOUT_SPIRAL = 3;
LAYOUT_CORNER_TOP_LEFT = 4;
LAYOUT_CORNER_TOP_RIGHT = 5;
LAYOUT_CORNER_BOTTOM_LEFT = 6;
LAYOUT_CORNER_BOTTOM_RIGHT = 7;
}
optional Layout layout = 2;
}
message GetRequest {}
message GetResponse {
repeated uint32 tag_ids = 1;
@ -60,7 +45,6 @@ service TagService {
rpc SwitchTo(SwitchToRequest) returns (google.protobuf.Empty);
rpc Add(AddRequest) returns (AddResponse);
rpc Remove(RemoveRequest) returns (google.protobuf.Empty);
rpc SetLayout(SetLayoutRequest) returns (google.protobuf.Empty);
rpc Get(GetRequest) returns (GetResponse);
rpc GetProperties(GetPropertiesRequest) returns (GetPropertiesResponse);
}

View file

@ -1,11 +1,10 @@
use pinnacle_api::layout::{
CornerLayout, DwindleLayout, FairLayout, LayoutManager, MasterStackLayout, SpiralLayout,
CornerLayout, DwindleLayout, FairLayout, MasterStackLayout, SpiralLayout,
};
use pinnacle_api::signal::WindowSignal;
use pinnacle_api::xkbcommon::xkb::Keysym;
use pinnacle_api::{
input::{Mod, MouseButton, MouseEdge},
tag::{Layout, LayoutCycler},
ApiModules,
};

View file

@ -29,11 +29,6 @@
//!
//! These [`TagHandle`]s allow you to manipulate individual tags and get their properties.
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use futures::FutureExt;
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::{
@ -41,7 +36,7 @@ use pinnacle_api_defs::pinnacle::{
self,
v0alpha1::{
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
SetLayoutRequest, SwitchToRequest,
SwitchToRequest,
},
},
v0alpha1::SetOrToggle,
@ -225,126 +220,6 @@ impl Tag {
block_on_tokio(client.remove(RemoveRequest { tag_ids })).unwrap();
}
/// Create a [`LayoutCycler`] to cycle layouts on outputs.
///
/// This will create a `LayoutCycler` with two functions: one to cycle forward the layout for
/// the first active tag on the specified output, and one to cycle backward.
///
/// If you do not specify an output for `LayoutCycler` functions, it will default to the
/// focused output.
///
/// # Examples
///
/// ```
/// use pinnacle_api::tag::{Layout, LayoutCycler};
/// use pinnacle_api::xkbcommon::xkb::Keysym;
/// use pinnacle_api::input::Mod;
///
/// // Create a layout cycler that cycles through the listed layouts
/// let LayoutCycler {
/// prev: layout_prev,
/// next: layout_next,
/// } = tag.new_layout_cycler([
/// Layout::MasterStack,
/// Layout::Dwindle,
/// Layout::Spiral,
/// Layout::CornerTopLeft,
/// Layout::CornerTopRight,
/// Layout::CornerBottomLeft,
/// Layout::CornerBottomRight,
/// ]);
///
/// // Cycle layouts forward on the focused output
/// layout_next(None);
///
/// // Cycle layouts backward on the focused output
/// layout_prev(None);
///
/// // Cycle layouts forward on "eDP-1"
/// layout_next(output.get_by_name("eDP-1")?);
/// ```
pub fn new_layout_cycler(&self, layouts: impl IntoIterator<Item = Layout>) -> LayoutCycler {
let indices = Arc::new(Mutex::new(HashMap::<u32, usize>::new()));
let indices_clone = indices.clone();
let layouts = layouts.into_iter().collect::<Vec<_>>();
let layouts_clone = layouts.clone();
let len = layouts.len();
let output_module = OUTPUT.get().expect("OUTPUT doesn't exist");
let output_module_clone = output_module.clone();
let next = move |output: Option<&OutputHandle>| {
let Some(output) = output.cloned().or_else(|| output_module.get_focused()) else {
return;
};
let Some(first_tag) = output
.props()
.tags
.into_iter()
.find(|tag| tag.active() == Some(true))
else {
return;
};
let mut indices = indices.lock().expect("layout next mutex lock failed");
let index = indices.entry(first_tag.id).or_insert(0);
if *index + 1 >= len {
*index = 0;
} else {
*index += 1;
}
first_tag.set_layout(layouts[*index]);
};
let prev = move |output: Option<&OutputHandle>| {
let Some(output) = output
.cloned()
.or_else(|| output_module_clone.get_focused())
else {
return;
};
let Some(first_tag) = output
.props()
.tags
.into_iter()
.find(|tag| tag.active() == Some(true))
else {
return;
};
let mut indices = indices_clone.lock().expect("layout next mutex lock failed");
let index = indices.entry(first_tag.id).or_insert(0);
if index.checked_sub(1).is_none() {
*index = len - 1;
} else {
*index -= 1;
}
first_tag.set_layout(layouts_clone[*index]);
};
LayoutCycler {
prev: Box::new(prev),
next: Box::new(next),
}
}
}
/// A layout cycler that keeps track of tags and their layouts and provides functions to cycle
/// layouts on them.
#[allow(clippy::type_complexity)]
pub struct LayoutCycler {
/// Cycle to the next layout on the given output, or the focused output if `None`.
pub prev: Box<dyn Fn(Option<&OutputHandle>) + Send + Sync + 'static>,
/// Cycle to the previous layout on the given output, or the focused output if `None`.
pub next: Box<dyn Fn(Option<&OutputHandle>) + Send + Sync + 'static>,
}
/// A handle to a tag.
@ -492,31 +367,6 @@ impl TagHandle {
.unwrap();
}
/// Set this tag's layout.
///
/// Layouting only applies to tiled windows (windows that are not floating, maximized, or
/// fullscreen). If multiple tags are active on an output, the first active tag's layout will
/// determine the layout strategy.
///
/// See [`Layout`] for the different static layouts Pinnacle currently has to offer.
///
/// # Examples
///
/// ```
/// use pinnacle_api::tag::Layout;
///
/// // Set the layout of tag "1" on the focused output to "corner top left".
/// tag.get("1", None)?.set_layout(Layout::CornerTopLeft);
/// ```
pub fn set_layout(&self, layout: Layout) {
let mut client = self.tag_client.clone();
block_on_tokio(client.set_layout(SetLayoutRequest {
tag_id: Some(self.id),
layout: Some(layout as i32),
}))
.unwrap();
}
/// Get all properties of this tag.
///
/// # Examples

View file

@ -21,7 +21,7 @@ use pinnacle_api_defs::pinnacle::{
self,
v0alpha1::{
tag_service_server, AddRequest, AddResponse, RemoveRequest, SetActiveRequest,
SetLayoutRequest, SwitchToRequest,
SwitchToRequest,
},
},
v0alpha1::{pinnacle_service_server, QuitRequest, SetOrToggle},
@ -831,15 +831,6 @@ impl tag_service_server::TagService for TagService {
.await
}
async fn set_layout(
&self,
_request: Request<SetLayoutRequest>,
) -> Result<Response<()>, Status> {
warn!("Tag.set_layout has been deprecated");
run_unary_no_response(&self.sender, move |_state| {}).await
}
async fn get(
&self,
_request: Request<tag::v0alpha1::GetRequest>,