Skip to content

Commit

Permalink
feat: Ability to use icons for empty workspace.
Browse files Browse the repository at this point in the history
This allows users of ex. `waybar` to use dynamic icons,
while still giving empty workspaces an icon instead of a number.
This is implemented by renaming new workspaces right when they
are created.
This is all behind a config option; defaults remain unchanged.

Implementing this is also an optimization for the rest of the program,
which no longer recalculates workspace naming on _every_ `Event::Window`.
Instead, only specific `Window` (and now also `Workspace`) events
will do so.

**Caveats**:
- There is a small visual artifact when opening a new workspace,
when the number gets renamed to the desired icon.. Unfortunately,
being a program reacting to sway events, I don't see a way to fix this.

**Future Possibilities**:
- Allowing specific icons for specific numbered (empty) workspaces
wouldn't be difficult to add (which would close #33).
- Which `*.change` types precisely should trigger workspace renaming
was determined with common sense, and could do with a one-over.
The relevant function is `should_rename_after_event`.

Closes #39.
  • Loading branch information
so-rose authored and pierrechevalier83 committed Apr 15, 2023
1 parent 8bde72d commit 8906afc
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "workstyle"
version = "0.8.2"
version = "0.9.0"
authors = ["Pierre Chevalier <[email protected]>"]
edition = "2021"
license = "MIT"
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ If you prefer not to have multiple copies of the same icon when there are multip
deduplicate_icons = true
```

If you prefer that empty workspaces be named with an icon,
instead of with a number, you can also specify:

```toml
[other]
use_empty_icon = true
empty_icon = ""
```

Note that the crate [`find_unicode`](https://github.com/pierrechevalier83/find_unicode/) can help find a unicode character directly from the command line. It now supports all of nerdfonts unicode space.

Minimal waybar configuration so the workspace names are showed
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct Other {
pub fallback_icon: Option<String>,
pub separator: Option<String>,
pub deduplicate_icons: bool,
pub empty_icon: Option<String>,
}

impl Config {
Expand Down Expand Up @@ -70,6 +71,9 @@ impl Config {
DEFAULT_SEPARATOR
}
}
pub fn empty_icon(&self) -> Option<&str> {
self.other.empty_icon.as_deref()
}

pub fn path() -> Result<PathBuf> {
let mut user_path = dirs::config_dir().context("Could not find the configuration path")?;
Expand Down
18 changes: 16 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ use window_manager::{Window, WindowManager, WM};
///
/// [other]
/// deduplicate_icons = true
///
/// If you prefer that empty workspaces be named with an icon,
/// instead of with a number, you can also specify:
///
/// [other]
/// empty_icon = ""
#[derive(Parser, Debug)]
#[clap(version, about, long_about)]
struct Args {
Expand Down Expand Up @@ -139,12 +146,19 @@ fn run() -> Result<()> {
.next()
.context("Unexpected workspace name")?;
if new_name.is_empty() {
wm.rename_workspace(&name, num)?;
let empty_name = if let Some(icon) = config.empty_icon() {
icon
} else {
num
}
.to_string();

// Extra space matches other workspace icons.
wm.rename_workspace(&name, &format!("{num}{sep}{empty_name} "))?;
} else {
wm.rename_workspace(&name, &format!("{num}{sep}{new_name}"))?;
}
}

wm.wait_for_event()?;
}
}
Expand Down
63 changes: 55 additions & 8 deletions src/window_manager.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::EnforceWindowManager;
use log::{debug, info};

use anyhow::{anyhow, bail, Context, Result};
use hyprland::data::{Clients, Version, Workspaces};
use hyprland::dispatch::{Dispatch, DispatchType};
Expand All @@ -8,7 +10,9 @@ use itertools::Itertools;
use std::collections::BTreeMap;
use std::sync::{mpsc, mpsc::Receiver};
use std::thread;
use swayipc::{Connection, EventStream, EventType, Node, NodeType};
use swayipc::{
Connection, Event, EventStream, EventType, Node, NodeType, WindowChange, WorkspaceChange,
};

trait NodeExt {
fn is_workspace(&self) -> bool;
Expand Down Expand Up @@ -287,13 +291,12 @@ impl WM for SwayOrI3 {
connection: Connection::new().context("Couldn't connect to WM")?,
events: Connection::new()
.context("Couldn't connect to WM")?
.subscribe([EventType::Window])
.context("Couldn't subscribe to events of type Window")?,
.subscribe([EventType::Window, EventType::Workspace])
.context("Couldn't subscribe to events of type Window and Workspace")?,
})),
_ => bail!("Not connecting to Sway or i3 as we've explicitly been asked not to"),
}
}

fn get_windows_in_each_workspace(&mut self) -> Result<BTreeMap<String, Vec<Window>>> {
self.connection
.get_tree()
Expand All @@ -313,10 +316,54 @@ impl WM for SwayOrI3 {
}

fn wait_for_event(&mut self) -> Result<()> {
match self.events.next() {
Some(Err(e)) => Err(anyhow!(e).context("Failed to receive next event")),
None => bail!("Event stream ended"),
_ => Ok(()),
let next_relevant_event = self.events.find_map(move |event| {
if let Ok(event) = event {
if Self::event_is_relevant(&event) {
Some(Ok(()))
} else {
None
}
} else {
Some(Err(()))
}
});
if next_relevant_event.is_none() {
bail!("Event stream ended")
} else {
Ok(())
}
}
}

impl SwayOrI3 {
fn event_is_relevant(event: &Event) -> bool {
match event {
Event::Workspace(boxed_workspace_event) => match (*boxed_workspace_event).change {
ch @ WorkspaceChange::Init => {
info!("Renaming on WorkspaceEvent: {:#?}", ch);
true
}
ch => {
debug!("Rejected Rename for WorkspaceEvent: {:#?}", ch);
false
}
},
Event::Window(boxed_window_event) => match (*boxed_window_event).change {
ch @ (WindowChange::New
| WindowChange::Close
| WindowChange::Title
| WindowChange::Move
| WindowChange::Urgent
| WindowChange::Mark) => {
info!("Renaming on WindowEvent: {:#?}", ch);
true
}
ch => {
debug!("Rejected Rename for WindowEvent: {:#?}", ch);
false
}
},
_ => false,
}
}
}

0 comments on commit 8906afc

Please sign in to comment.