Skip to content

Commit 3d52475

Browse files
authored
feat: hide_empty_workspaces (#28)
1 parent b126919 commit 3d52475

7 files changed

Lines changed: 124 additions & 36 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66

77
- Added new config at `~/.config/komorebi-switcher.toml`
88
- Added new "Settings" menu item in Right click menu and tray icon.
9-
- Added button to show active workspace layout and click to cycle through available layouts (Enable it through Settings or config).
9+
- Added `show_layout_button` option to show a button for active workspace layout, click to cycle through available layouts.
10+
- Added `hide_empty_workspaces` option to hide empty workspaces from the switcher.
1011

1112
### Changed
1213

13-
- Migrated config options stored in Windows Registry to the new TOML config.
14-
- Active workspace indicator will no longer appear on top of the switcher button and will always be at the bottom of it.
14+
- **Windows** Migrated config options stored in Windows Registry to the new TOML config.
15+
- **Windows** Active workspace indicator will no longer appear on top of the switcher button and will always be at the bottom of it.
1516

1617
### Removed
1718

18-
- Removed "Move and Resize" menu items from Right click menu and tray icon.
19+
- **Windows** Removed "Move and Resize" menu items from Right click menu and tray icon. Use the new "Settings" menu item.
1920

2021
## [0.9.2] - 2026-01-06
2122

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ or use the settings window accessible from the context menu.
3737
```toml
3838
# Global settings
3939
show_layout_button = false
40+
hide_empty_workspaces = false
4041

4142
# Settings for each monitor (Windows only for now)
4243
# Syntax is [monitors.<id>] where <id> is one of:
@@ -53,6 +54,7 @@ height = 65
5354
auto_width = true
5455
auto_height = true
5556
show_layout_button = false # Can be removed to use the global setting
57+
hide_empty_workspaces = false # Can be removed to use the global setting
5658
```
5759

5860
## Development

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub struct WindowConfig {
1414
pub auto_height: bool,
1515
#[serde(default)]
1616
pub show_layout_button: Option<bool>,
17+
#[serde(default)]
18+
pub hide_empty_workspaces: Option<bool>,
1719
}
1820

1921
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -23,6 +25,9 @@ pub struct Config {
2325

2426
#[serde(default)]
2527
pub show_layout_button: bool,
28+
29+
#[serde(default)]
30+
pub hide_empty_workspaces: bool,
2631
}
2732

2833
impl Config {
@@ -139,6 +144,7 @@ impl Config {
139144
auto_width,
140145
auto_height,
141146
show_layout_button: None,
147+
hide_empty_workspaces: None,
142148
},
143149
);
144150
}

src/macos/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ impl AppDelegate {
139139
};
140140

141141
for workspace in &monitor.workspaces {
142+
if config.hide_empty_workspaces && workspace.is_empty && !workspace.focused {
143+
continue;
144+
}
145+
142146
let workspace_button = WorkspaceButton::new(mtm, workspace);
143147
stack_view.addArrangedSubview(&workspace_button);
144148
views.push(workspace_button.downcast().unwrap());

src/macos/windows/settings/view_controller.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ define_class!(
3939
pub struct SettingsViewControllerIvars {
4040
config: RefCell<crate::config::Config>,
4141
show_layout_button_checkbox: RefCell<Option<Retained<NSButton>>>,
42+
hide_empty_workspaces_checkbox: RefCell<Option<Retained<NSButton>>>,
4243
}
4344

4445
impl SettingsViewControllerIvars {
4546
fn new(config: crate::config::Config) -> Self {
4647
Self {
4748
config: RefCell::new(config),
4849
show_layout_button_checkbox: RefCell::new(None),
50+
hide_empty_workspaces_checkbox: RefCell::new(None),
4951
}
5052
}
5153
}
@@ -112,25 +114,34 @@ impl SettingsViewController {
112114
header
113115
}
114116

115-
fn create_global_settings_ui(&self) -> Retained<NSStackView> {
117+
fn create_checkbox(&self, title: &str, initial_state: bool) -> Retained<NSButton> {
116118
let mtm = self.mtm();
117119

118-
let vstack = self.create_vstack();
119-
120120
let checkbox = NSButton::new(mtm);
121121
checkbox.setButtonType(NSButtonType::Switch);
122-
checkbox.setTitle(&NSString::from_str("Show layout button"));
123-
124-
let config = self.ivars().config.borrow();
125-
checkbox.setState(if config.show_layout_button {
122+
checkbox.setTitle(&NSString::from_str(title));
123+
checkbox.setState(if initial_state {
126124
NSControlStateValueOn
127125
} else {
128126
NSControlStateValueOff
129127
});
130128

131-
vstack.addArrangedSubview(&checkbox);
129+
checkbox
130+
}
132131

133-
*self.ivars().show_layout_button_checkbox.borrow_mut() = Some(checkbox);
132+
fn create_global_settings_ui(&self) -> Retained<NSStackView> {
133+
let config = self.ivars().config.borrow();
134+
135+
let vstack = self.create_vstack();
136+
137+
let layout = self.create_checkbox("Show layout button", config.show_layout_button);
138+
vstack.addArrangedSubview(&layout);
139+
*self.ivars().show_layout_button_checkbox.borrow_mut() = Some(layout);
140+
141+
let label = "Hide empty workspaces";
142+
let empty_workspace = self.create_checkbox(label, config.hide_empty_workspaces);
143+
vstack.addArrangedSubview(&empty_workspace);
144+
*self.ivars().hide_empty_workspaces_checkbox.borrow_mut() = Some(empty_workspace);
134145

135146
vstack
136147
}
@@ -163,6 +174,14 @@ impl SettingsViewController {
163174
if let Some(checkbox) = self.ivars().show_layout_button_checkbox.borrow().as_ref() {
164175
config.show_layout_button = checkbox.state() == NSControlStateValueOn;
165176
}
177+
if let Some(checkbox) = self
178+
.ivars()
179+
.hide_empty_workspaces_checkbox
180+
.borrow()
181+
.as_ref()
182+
{
183+
config.hide_empty_workspaces = checkbox.state() == NSControlStateValueOn;
184+
}
166185
config.save()?;
167186
Ok(())
168187
}

src/windows/windows/settings.rs

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,46 +85,62 @@ impl SettingsWindowView {
8585
&mut self.config.show_layout_button,
8686
"Show layout button",
8787
));
88+
89+
ui.add(egui::Checkbox::new(
90+
&mut self.config.hide_empty_workspaces,
91+
"Hide empty workspaces",
92+
));
8893
}
8994

90-
fn layout_button_ui(&mut self, ui: &mut egui::Ui, monitor_id: &str) {
95+
fn show_layout_button_ui(&mut self, ui: &mut egui::Ui, monitor_id: &str) {
9196
let monitor_config = self.config.get_monitor_or_default(monitor_id);
9297

9398
ui.label("Show layout button");
9499

95-
#[derive(Copy, Clone, PartialEq, strum::Display)]
96-
enum ShowLayoutButton {
97-
Inherit,
98-
Show,
99-
Hide,
100+
let mut selected: ActivationOption = monitor_config.show_layout_button.into();
101+
102+
let before = selected;
103+
104+
egui::ComboBox::new("show_layout_button", "")
105+
.selected_text(format!("{}", selected))
106+
.show_ui(ui, |ui| {
107+
for option in [
108+
ActivationOption::Inherit,
109+
ActivationOption::Enable,
110+
ActivationOption::Disable,
111+
] {
112+
ui.selectable_value(&mut selected, option, format!("{}", option));
113+
}
114+
});
115+
116+
if before != selected {
117+
monitor_config.show_layout_button = selected.into();
100118
}
119+
}
101120

102-
let mut selected = match monitor_config.show_layout_button {
103-
None => ShowLayoutButton::Inherit,
104-
Some(true) => ShowLayoutButton::Show,
105-
Some(false) => ShowLayoutButton::Hide,
106-
};
121+
fn hide_empty_workspaces_ui(&mut self, ui: &mut egui::Ui, monitor_id: &str) {
122+
let monitor_config = self.config.get_monitor_or_default(monitor_id);
123+
124+
ui.label("Hide empty workspaces");
125+
126+
let mut selected: ActivationOption = monitor_config.hide_empty_workspaces.into();
107127

108128
let before = selected;
109129

110-
egui::ComboBox::from_label("")
130+
egui::ComboBox::new("hide_empty_workspaces", "")
111131
.selected_text(format!("{}", selected))
112132
.show_ui(ui, |ui| {
113133
for option in [
114-
ShowLayoutButton::Inherit,
115-
ShowLayoutButton::Show,
116-
ShowLayoutButton::Hide,
134+
ActivationOption::Inherit,
135+
ActivationOption::Enable,
136+
ActivationOption::Disable,
117137
] {
118138
ui.selectable_value(&mut selected, option, format!("{}", option));
119139
}
120140
});
121141

122142
if before != selected {
123-
monitor_config.show_layout_button = match selected {
124-
ShowLayoutButton::Inherit => None,
125-
ShowLayoutButton::Show => Some(true),
126-
ShowLayoutButton::Hide => Some(false),
127-
};
143+
monitor_config.hide_empty_workspaces = selected.into();
128144
}
129145
}
130146

@@ -159,7 +175,10 @@ impl SettingsWindowView {
159175
});
160176
ui.end_row();
161177

162-
self.layout_button_ui(ui, monitor_id);
178+
self.show_layout_button_ui(ui, monitor_id);
179+
ui.end_row();
180+
181+
self.hide_empty_workspaces_ui(ui, monitor_id);
163182
ui.end_row();
164183
}
165184

@@ -227,3 +246,31 @@ impl EguiView for SettingsWindowView {
227246
egui::CentralPanel::default().show(ctx, |ui| self.ui(ui));
228247
}
229248
}
249+
250+
/// Represents an activation option for a setting: Inherit, Enable, or Disable.
251+
#[derive(Copy, Clone, PartialEq, strum::Display)]
252+
enum ActivationOption {
253+
Inherit,
254+
Enable,
255+
Disable,
256+
}
257+
258+
impl From<ActivationOption> for Option<bool> {
259+
fn from(option: ActivationOption) -> Self {
260+
match option {
261+
ActivationOption::Inherit => None,
262+
ActivationOption::Enable => Some(false),
263+
ActivationOption::Disable => Some(true),
264+
}
265+
}
266+
}
267+
268+
impl From<Option<bool>> for ActivationOption {
269+
fn from(option: Option<bool>) -> Self {
270+
match option {
271+
None => ActivationOption::Inherit,
272+
Some(true) => ActivationOption::Disable,
273+
Some(false) => ActivationOption::Enable,
274+
}
275+
}
276+
}

src/windows/windows/switcher/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,19 @@ impl SwitcherWindowView {
224224
ui.scope(|ui| {
225225
ui.style_mut().spacing.item_spacing = egui::vec2(4., 4.);
226226

227+
let config = self.effective_config();
228+
let monitor_config = config.get_monitor(&self.monitor_state.id);
229+
let hide_empty_workspaces = match monitor_config.hide_empty_workspaces {
230+
Some(hide) => hide,
231+
None => config.hide_empty_workspaces,
232+
};
233+
227234
// show workspace buttons
228235
for workspace in self.monitor_state.workspaces.iter() {
236+
if hide_empty_workspaces && workspace.is_empty && !workspace.focused {
237+
continue;
238+
}
239+
229240
let btn = WorkspaceButton::new(workspace)
230241
.dark_mode(Some(self.is_system_dark_mode()))
231242
.line_focused_color_opt(self.line_focused_color())
@@ -240,8 +251,6 @@ impl SwitcherWindowView {
240251
}
241252

242253
// show layout button for focused workspace
243-
let config = self.effective_config();
244-
let monitor_config = config.get_monitor(&self.monitor_state.id);
245254
let show_layout_button = match monitor_config.show_layout_button {
246255
Some(show) => show,
247256
None => config.show_layout_button,

0 commit comments

Comments
 (0)