Merge pull request #147 from yavko/add-always-workspaces

feat: Add favorite and hidden workspaces
This commit is contained in:
Jake Stanger 2023-08-15 20:25:49 +01:00 committed by GitHub
commit 50741941fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 460 additions and 268 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

579
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -131,7 +131,7 @@ zbus = { version = "3.14.1", optional = true }
# workspaces
swayipc-async = { version = "2.0.1", optional = true }
hyprland = { version = "0.3.8", optional = true }
hyprland = { version = "0.3.8", features = ["silent"], optional = true }
futures-util = { version = "0.3.21", optional = true }
# shared

View File

@ -8,12 +8,14 @@ Shows all current workspaces. Clicking a workspace changes focus to it.
> Type: `workspaces`
| Name | Type | Default | Description |
|----------------|--------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name_map` | `Map<string, string or image>` | `{}` | A map of actual workspace names to their display labels/images. Workspaces use their actual name if not present in the map. See [here](images) for information on images. |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `all_monitors` | `boolean` | `false` | Whether to display workspaces from all monitors. When `false`, only shows workspaces on the current monitor. |
| `sort` | `'added'` or `'alphanumeric'` | `alphanumeric` | The method used for sorting workspaces. `added` always appends to the end, `alphanumeric` sorts by number/name. |
| Name | Type | Default | Description |
|----------------|---------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name_map` | `Map<string, string or image>` | `{}` | A map of actual workspace names to their display labels/images. Workspaces use their actual name if not present in the map. See [here](images) for information on images. |
| `favorites` | `Map<string, string[]>` or `string[]` | `[]` | Workspaces to always show. This can be for all monitors, or a map to set per monitor. |
| `hidden` | `string[]` | `[]` | A list of workspace names to never show |
| `icon_size` | `integer` | `32` | Size to render icon at (image icons only). |
| `all_monitors` | `boolean` | `false` | Whether to display workspaces from all monitors. When `false`, only shows workspaces on the current monitor. |
| `sort` | `'added'` or `'alphanumeric'` | `alphanumeric` | The method used for sorting workspaces. `added` always appends to the end, `alphanumeric` sorts by number/name. |
<details>
<summary>JSON</summary>
@ -28,6 +30,7 @@ Shows all current workspaces. Clicking a workspace changes focus to it.
"2": "",
"3": ""
},
"favorites": ["1", "2", "3"],
"all_monitors": false
}
]
@ -43,6 +46,7 @@ Shows all current workspaces. Clicking a workspace changes focus to it.
[[end]]
type = "workspaces"
all_monitors = false
favorites = ["1", "2", "3"]
[[end.name_map]]
1 = ""
@ -63,6 +67,10 @@ end:
1: ""
2: ""
3: ""
favorites:
- "1"
- "2"
- "3"
all_monitors: false
```
@ -79,6 +87,7 @@ end:
name_map.1 = ""
name_map.2 = ""
name_map.3 = ""
favorites = [ "1" "2" "3" ]
all_monitors = false
}
]
@ -98,4 +107,4 @@ end:
| `.workspaces .item .text-icon` | Workspace button icon (textual only) |
| `.workspaces .item .image` | Workspace button icon (image only) |
For more information on styling, please see the [styling guide](styling-guide).
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -34,7 +34,10 @@
rust-overlay.overlays.default
];
};
mkRustToolchain = pkgs: pkgs.rust-bin.stable.latest.default;
mkRustToolchain = pkgs:
pkgs.rust-bin.stable.latest.default.override {
extensions = ["rust-src"];
};
in {
overlays.default = final: prev: let
rust = mkRustToolchain final;
@ -116,6 +119,14 @@
gtk-layer-shell
pkg-config
openssl
gdk-pixbuf
glib
glib-networking
shared-mime-info
gnome.adwaita-icon-theme
hicolor-icon-theme
gsettings-desktop-schemas
libxkbcommon
];
RUST_SRC_PATH = "${rust}/lib/rustlib/src/rust/library";

View File

@ -1,4 +1,4 @@
use crate::clients::compositor::{Compositor, WorkspaceUpdate};
use crate::clients::compositor::{Compositor, Workspace, WorkspaceUpdate};
use crate::config::CommonConfig;
use crate::image::new_icon_button;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
@ -8,7 +8,7 @@ use gtk::prelude::*;
use gtk::{Button, IconTheme};
use serde::Deserialize;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use tokio::spawn;
use tokio::sync::mpsc::{Receiver, Sender};
use tracing::trace;
@ -29,11 +29,32 @@ impl Default for SortOrder {
}
}
#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
pub enum Favorites {
ByMonitor(HashMap<String, Vec<String>>),
Global(Vec<String>),
}
impl Default for Favorites {
fn default() -> Self {
Self::Global(vec![])
}
}
#[derive(Debug, Deserialize, Clone)]
pub struct WorkspacesModule {
/// Map of actual workspace names to custom names.
name_map: Option<HashMap<String, String>>,
/// Array of always shown workspaces, and what monitor to show on
#[serde(default)]
favorites: Favorites,
/// List of workspace names to never show
#[serde(default)]
hidden: Vec<String>,
/// Whether to display buttons for all monitors.
#[serde(default = "crate::config::default_false")]
all_monitors: bool,
@ -56,6 +77,7 @@ const fn default_icon_size() -> i32 {
fn create_button(
name: &str,
focused: bool,
inactive: bool,
name_map: &HashMap<String, String>,
icon_theme: &IconTheme,
icon_size: i32,
@ -71,6 +93,8 @@ fn create_button(
if focused {
style_context.add_class("focused");
} else if inactive {
style_context.add_class("inactive");
}
{
@ -105,6 +129,13 @@ fn reorder_workspaces(container: &gtk::Box) {
}
}
impl WorkspacesModule {
fn show_workspace_check(&self, output: &String, work: &Workspace) -> bool {
(work.focused || !self.hidden.contains(&work.name))
&& (self.all_monitors || output == &work.monitor)
}
}
impl Module<gtk::Box> for WorkspacesModule {
type SendMessage = WorkspaceUpdate;
type ReceiveMessage = String;
@ -157,7 +188,9 @@ impl Module<gtk::Box> for WorkspacesModule {
) -> Result<ModuleParts<gtk::Box>> {
let container = gtk::Box::new(info.bar_position.get_orientation(), 0);
let name_map = self.name_map.unwrap_or_default();
let name_map = self.name_map.clone().unwrap_or_default();
let favs = self.favorites.clone();
let mut fav_names: Vec<String> = vec![];
let mut button_map: HashMap<String, Button> = HashMap::new();
@ -176,19 +209,49 @@ impl Module<gtk::Box> for WorkspacesModule {
WorkspaceUpdate::Init(workspaces) => {
if !has_initialized {
trace!("Creating workspace buttons");
for workspace in workspaces {
if self.all_monitors || workspace.monitor == output_name {
let item = create_button(
&workspace.name,
workspace.focused,
&name_map,
&icon_theme,
icon_size,
&context.controller_tx,
);
container.add(&item);
button_map.insert(workspace.name, item);
let mut added = HashSet::new();
let mut add_workspace = |name: &str, focused: bool| {
let item = create_button(
name,
focused,
false,
&name_map,
&icon_theme,
icon_size,
&context.controller_tx,
);
container.add(&item);
button_map.insert(name.to_string(), item);
};
// add workspaces from client
for workspace in &workspaces {
if self.show_workspace_check(&output_name, workspace) {
add_workspace(&workspace.name, workspace.focused);
added.insert(workspace.name.to_string());
}
}
let mut add_favourites = |names: &Vec<String>| {
for name in names {
if !added.contains(name) {
add_workspace(name, false);
added.insert(name.to_string());
fav_names.push(name.to_string());
}
}
};
// add workspaces from favourites
match &favs {
Favorites::Global(names) => add_favourites(names),
Favorites::ByMonitor(map) => {
if let Some(to_add) = map.get(&output_name) {
add_favourites(to_add);
}
}
}
@ -212,11 +275,17 @@ impl Module<gtk::Box> for WorkspacesModule {
}
}
WorkspaceUpdate::Add(workspace) => {
if self.all_monitors || workspace.monitor == output_name {
if fav_names.contains(&workspace.name) {
let btn = button_map.get(&workspace.name);
if let Some(btn) = btn {
btn.style_context().remove_class("inactive");
}
} else if self.show_workspace_check(&output_name, &workspace) {
let name = workspace.name;
let item = create_button(
&name,
workspace.focused,
false,
&name_map,
&icon_theme,
icon_size,
@ -236,12 +305,13 @@ impl Module<gtk::Box> for WorkspacesModule {
}
}
WorkspaceUpdate::Move(workspace) => {
if !self.all_monitors {
if !self.hidden.contains(&workspace.name) && !self.all_monitors {
if workspace.monitor == output_name {
let name = workspace.name;
let item = create_button(
&name,
workspace.focused,
false,
&name_map,
&icon_theme,
icon_size,
@ -267,7 +337,11 @@ impl Module<gtk::Box> for WorkspacesModule {
WorkspaceUpdate::Remove(workspace) => {
let button = button_map.get(&workspace);
if let Some(item) = button {
container.remove(item);
if fav_names.contains(&workspace) {
item.style_context().add_class("inactive");
} else {
container.remove(item);
}
}
}
WorkspaceUpdate::Update(_) => {}