From f1c9d197087dbc11fbcd29b94278719b71cd089d Mon Sep 17 00:00:00 2001 From: Ottatop Date: Fri, 20 Oct 2023 19:32:08 -0500 Subject: [PATCH] Fix layout cycler --- api/rust/examples/example_config.rs | 58 +++++++++++++++----- api/rust/src/input.rs | 4 +- api/rust/src/tag.rs | 82 ++++++++++++++--------------- 3 files changed, 88 insertions(+), 56 deletions(-) diff --git a/api/rust/examples/example_config.rs b/api/rust/examples/example_config.rs index 53ec9cb..4385026 100644 --- a/api/rust/examples/example_config.rs +++ b/api/rust/examples/example_config.rs @@ -1,7 +1,10 @@ +// You should glob import these to prevent your config from getting cluttered. use pinnacle_api::prelude::*; use pinnacle_api::*; fn main() { + // Connect to the Pinnacle server. + // This needs to be called before you start calling any config functions. pinnacle_api::connect().unwrap(); let mod_key = Modifier::Ctrl; @@ -10,8 +13,17 @@ fn main() { process::set_env("MOZ_ENABLE_WAYLAND", "1"); + // You must create a callback_vec to hold your callbacks. + // Rust is not Lua, so it takes a bit more work to get captures working. + // + // Anything that requires a callback will also require a mut reference to this struct. + // + // Additionally, all callbacks also take in `&mut CallbackVec`. + // This allows you to call functions that need callbacks within other callbacks. let mut callback_vec = CallbackVec::new(); + // Keybinds. + input::mousebind( &[mod_key], MouseButton::Left, @@ -102,19 +114,35 @@ fn main() { &mut callback_vec, ); - // let layout_cycler = tag.layout_cycler(&[ - // Layout::MasterStack, - // Layout::Dwindle, - // Layout::Spiral, - // Layout::CornerTopLeft, - // Layout::CornerTopRight, - // Layout::CornerBottomLeft, - // Layout::CornerBottomRight, - // ]); - // - // input.keybind(&[mod_key], xkbcommon::xkb::keysyms::KEY_space, move || { - // layout_cycler.next(None); - // }); + let mut layout_cycler = tag::layout_cycler(&[ + Layout::MasterStack, + Layout::Dwindle, + Layout::Spiral, + Layout::CornerTopLeft, + Layout::CornerTopRight, + Layout::CornerBottomLeft, + Layout::CornerBottomRight, + ]); + + input::keybind( + &[mod_key], + xkbcommon::xkb::keysyms::KEY_space, + move |_| { + (layout_cycler.next)(None); + }, + &mut callback_vec, + ); + + input::keybind( + &[mod_key, Modifier::Shift], + xkbcommon::xkb::keysyms::KEY_space, + move |_| { + (layout_cycler.prev)(None); + }, + &mut callback_vec, + ); + + // Keybinds for tags for tag_name in tags.iter().map(|t| t.to_string()) { let t = tag_name.clone(); @@ -159,5 +187,9 @@ fn main() { ); } + // At the very end of your config, you will need to start listening to Pinnacle in order for + // your callbacks to be correctly called. + // + // This will not return unless an error occurs. pinnacle_api::listen(callback_vec); } diff --git a/api/rust/src/input.rs b/api/rust/src/input.rs index bf636b2..e80fba1 100644 --- a/api/rust/src/input.rs +++ b/api/rust/src/input.rs @@ -11,13 +11,15 @@ use crate::{ /// Set a keybind. /// -/// This function takes in three parameters: +/// This function takes in four parameters: /// - `modifiers`: A slice of the modifiers you want held for the keybind to trigger. /// - `key`: The key that needs to be pressed. This takes `impl Into` and can /// take the following three types: /// - [`char`]: A character of the key you want. This can be `a`, `~`, `@`, and so on. /// - [`u32`]: The key in numeric form. You can use the keys defined in [`xkbcommon::xkb::keysyms`] for this. /// - [`Keysym`]: The key in `Keysym` form, from [xkbcommon::xkb::Keysym]. +/// - `action`: What you want to run. +/// - `callback_vec`: Your [`CallbackVec`] to insert `action` into. /// /// `action` takes in a `&mut `[`CallbackVec`] for use in the closure. pub fn keybind<'a, F>( diff --git a/api/rust/src/tag.rs b/api/rust/src/tag.rs index 2cb4472..1aa1546 100644 --- a/api/rust/src/tag.rs +++ b/api/rust/src/tag.rs @@ -53,10 +53,12 @@ pub fn add(output: &OutputHandle, names: &[&str]) { /// todo!() /// ``` pub fn layout_cycler(layouts: &[Layout]) -> LayoutCycler { - let mut indices = HashMap::::new(); + let indices = std::rc::Rc::new(std::cell::RefCell::new(HashMap::::new())); + let indices_clone = indices.clone(); let layouts = layouts.to_vec(); + let layouts_clone = layouts.clone(); let len = layouts.len(); - let cycle = move |cycle: Cycle, output: Option<&OutputHandle>| { + let next = move |output: Option<&OutputHandle>| { let Some(output) = output.cloned().or_else(crate::output::get_focused) else { return; }; @@ -70,61 +72,57 @@ pub fn layout_cycler(layouts: &[Layout]) -> LayoutCycler { return; }; + let mut indices = indices.borrow_mut(); let index = indices.entry(tag.0).or_insert(0); - match cycle { - Cycle::Forward => { - if *index + 1 >= len { - *index = 0; - } else { - *index += 1; - } - } - Cycle::Backward => { - if index.wrapping_sub(1) == usize::MAX { - *index = len - 1; - } else { - *index -= 1; - } - } + if *index + 1 >= len { + *index = 0; + } else { + *index += 1; } tag.set_layout(layouts[*index]); }; + let prev = move |output: Option<&OutputHandle>| { + let Some(output) = output.cloned().or_else(crate::output::get_focused) else { + return; + }; + + let Some(tag) = output + .properties() + .tags + .into_iter() + .find(|tag| tag.properties().active == Some(true)) + else { + return; + }; + + let mut indices = indices_clone.borrow_mut(); + let index = indices.entry(tag.0).or_insert(0); + + if index.wrapping_sub(1) == usize::MAX { + *index = len - 1; + } else { + *index -= 1; + } + + tag.set_layout(layouts_clone[*index]); + }; LayoutCycler { - cycle: Box::new(cycle), + next: Box::new(next), + prev: Box::new(prev), } } -/// Which direction to cycle layouts. -#[derive(Debug, Clone, Copy)] -enum Cycle { - /// Cycle layouts forward. - Forward, - /// Cycle layouts backward. - Backward, -} - /// A layout cycler that keeps track of tags and their layouts and provides methods to cycle /// layouts on them. #[allow(clippy::type_complexity)] pub struct LayoutCycler { - cycle: Box)>, -} - -impl LayoutCycler { - /// Cycle to the next layout for the first active tag on `output`. - /// If `output` is `None`, the currently focused output is used. - pub fn next(&mut self, output: Option<&OutputHandle>) { - (self.cycle)(Cycle::Forward, output); - } - - /// Cycle to the previous layout for the first active tag on `output`. - /// If `output` is `None`, the currently focused output is used. - pub fn prev(&mut self, output: Option<&OutputHandle>) { - (self.cycle)(Cycle::Backward, output); - } + /// Cycle to the next layout on the given output, or the focused output if `None`. + pub next: Box)>, + /// Cycle to the previous layout on the given output, or the focused output if `None`. + pub prev: Box)>, } #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]