Fix layout cycler

This commit is contained in:
Ottatop 2023-10-20 19:32:08 -05:00
parent 1680acc5e9
commit f1c9d19708
3 changed files with 88 additions and 56 deletions

View file

@ -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);
}

View file

@ -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<KeyIntOrString>` 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>(

View file

@ -53,10 +53,12 @@ pub fn add(output: &OutputHandle, names: &[&str]) {
/// todo!()
/// ```
pub fn layout_cycler(layouts: &[Layout]) -> LayoutCycler {
let mut indices = HashMap::<TagId, usize>::new();
let indices = std::rc::Rc::new(std::cell::RefCell::new(HashMap::<TagId, usize>::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 => {
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[*index]);
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<dyn FnMut(Cycle, Option<&OutputHandle>)>,
}
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<dyn FnMut(Option<&OutputHandle>)>,
/// Cycle to the previous layout on the given output, or the focused output if `None`.
pub prev: Box<dyn FnMut(Option<&OutputHandle>)>,
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]