Skip to content

Commit

Permalink
adding default workspaces hook
Browse files Browse the repository at this point in the history
  • Loading branch information
sminez committed Nov 23, 2023
1 parent 3e2a637 commit ba6adec
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/core/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
//!
//! ### Layout Hooks
//!
//! Finally we have [`LayoutHook`]s which operate a little differently, in that they have
//! Next we have [`LayoutHook`]s which operate a little differently, in that they have
//! two methods to implement. Layout hooks are run _around_ whatever [Layout][1] is active
//! for the focused workspace, allowing you to modify the screen dimensions available for the
//! layout algorithm before it runs and editing the list of window positions it generates
Expand Down
58 changes: 58 additions & 0 deletions src/extensions/hooks/default_workspaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Configure workspaces to auto-spawn a set of windows if they are empty when they gain focus
use crate::{
core::{hooks::StateHook, State},
util::spawn,
x::XConn,
Result,
};

/// Specify a workspace by `tag` and use a named layout to spawn a set of default programs
/// on it if it gains focus while currently empty.
///
/// The programs are spawned in the order they are specified in [DefaultWorkspace::boxed] meaning
/// that the final program `progs` will have focus.
#[derive(Debug, Clone)]
pub struct DefaultWorkspace {
tag: String,
layout_name: String,
progs: Vec<String>,
}

impl DefaultWorkspace {
/// Create a new boxed `DefaultWorkspace` that can be added to your Config as a refresh hook.
pub fn boxed<X>(
tag: impl Into<String>,
layout_name: impl Into<String>,
progs: Vec<impl Into<String>>,
) -> Box<dyn StateHook<X>>
where
X: XConn,
{
Box::new(Self {
tag: tag.into(),
layout_name: layout_name.into(),
progs: progs.into_iter().map(|p| p.into()).collect(),
})
}
}

impl<X> StateHook<X> for DefaultWorkspace
where
X: XConn,
{
fn call(&mut self, state: &mut State<X>, _x: &X) -> Result<()> {
let on_screen_and_empty = matches!(state.diff.after.visible.iter().find(|s| s.tag == self.tag), Some(s) if s.clients.is_empty());

if on_screen_and_empty
&& !state
.diff
.previous_visible_tags()
.contains(&self.tag.as_str())
{
state.client_set.set_layout_by_name(&self.layout_name);
self.progs.iter().try_for_each(spawn)?;
}

Ok(())
}
}
1 change: 1 addition & 0 deletions src/extensions/hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Hook implementations and helpers for adding to your Penrose window manager
pub mod default_workspaces;
pub mod ewmh;
pub mod manage;
pub mod named_scratchpads;
Expand Down
11 changes: 11 additions & 0 deletions src/pure/stack_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,17 @@ where
self.screens.focus.workspace.previous_layout()
}

/// Attempt to set the current [Layout][crate::core::layout::Layout] by name.
///
/// This is a no-op if the requested layout is already active or if no layout with the given name
/// is available for the active workspace.
pub fn set_layout_by_name(&mut self, layout: impl AsRef<str>) {
self.screens
.focus
.workspace
.set_layout_by_name(layout.as_ref())
}

/// Move focus to the next [Screen]
pub fn next_screen(&mut self) {
if self.screens.len() == 1 {
Expand Down

0 comments on commit ba6adec

Please sign in to comment.