Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Open **Settings** from Wisp's tray icon, or edit `%APPDATA%\wisp\config.toml`. T
| `hotkey` | `"Alt+Space"` | Shows or hides the launcher |
| `dock` | `true` | Shows the dock |
| `dock_autohide` | `true` | Hides the dock until the pointer touches the bottom edge. `false` keeps it visible. |
| `window_list` | `"multiple"` | Shows the app's window list for multiple windows, always, or never |
| `hide_taskbar` | `true` | Hides the Windows taskbar while Wisp runs |
| `position` | `"bottom"` | `"bottom"` grows the launcher from the dock; `"center"` opens it in the middle |
| `theme` | `"system"` | `"system"`, `"dark"` or `"light"` |
Expand Down
Binary file added assets/dock-window-list-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions settings/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct Config {
pub dock: bool,
pub dock_autohide: bool,
pub hide_taskbar: bool,
pub window_list: String,
}

impl Default for Config {
Expand Down Expand Up @@ -54,6 +55,8 @@ impl Config {
choice(&backdrop, &["acrylic", "mica", "solid"], "backdrop")?;
let position = string("position")?;
choice(&position, &["center", "bottom"], "position")?;
let window_list = string("window_list")?;
choice(&window_list, &["always", "multiple", "off"], "window_list")?;
let max_results = get("max_results")
.as_integer()
.filter(|value| *value >= 0)
Expand Down Expand Up @@ -83,6 +86,7 @@ impl Config {
dock: boolean("dock")?,
dock_autohide: boolean("dock_autohide")?,
hide_taskbar: boolean("hide_taskbar")?,
window_list,
})
}
}
Expand Down Expand Up @@ -205,6 +209,10 @@ fn validate_edit(key: &str, value: &Value) -> Result<(), String> {
| "dock"
| "dock_autohide"
| "hide_taskbar" => {}
"window_list" if !matches!(value.as_str(), Some("always" | "multiple" | "off")) => {
return Err("window_list must be always, multiple or off.".into());
}
"window_list" => {}
_ => return Err(format!("Unknown setting: {key}")),
}
Ok(())
Expand All @@ -230,6 +238,8 @@ mod tests {
assert!(!old.dock && !old.hide_taskbar);
assert_eq!(old.position, "center");
assert_eq!(old.max_results, config.max_results);
assert_eq!(config.window_list, "multiple");
assert_eq!(Config::from_document(&DEFAULT_CONFIG.parse().unwrap()).unwrap(), config);
}

#[test]
Expand Down Expand Up @@ -312,6 +322,7 @@ mod tests {
("max_results", 0.into()),
("max_results", 21.into()),
("theme", "blue".into()),
("window_list", "sometimes".into()),
("unknown", true.into()),
] {
assert!(store.set(key, value).is_err(), "{key}");
Expand Down
35 changes: 35 additions & 0 deletions settings/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const ICON_SVG: &[u8] = include_bytes!("../../assets/icon.svg");
/// Config values and the labels the dropdowns show for them.
const THEMES: [(&str, &str); 3] = [("system", "System"), ("dark", "Dark"), ("light", "Light")];
const BACKDROPS: [(&str, &str); 3] = [("acrylic", "Acrylic"), ("mica", "Mica"), ("solid", "Solid")];
const WINDOW_LISTS: [(&str, &str); 3] = [
("multiple", "Only for multiple windows"),
("always", "Always"),
("off", "Off"),
];
const ONBOARDING_STEPS: usize = 3;
type Choices = SelectState<SearchableVec<&'static str>>;

Expand Down Expand Up @@ -84,6 +89,7 @@ pub struct Settings {
folder: Entity<InputState>,
theme: Entity<Choices>,
backdrop: Entity<Choices>,
window_list: Entity<Choices>,
results: Entity<SliderState>,
autostart: bool,
powertoys: Result<bool, String>,
Expand Down Expand Up @@ -141,6 +147,14 @@ impl Settings {
cx,
)
});
let window_list = cx.new(|cx| {
SelectState::new(
SearchableVec::new(labels(&WINDOW_LISTS)),
index_of(&WINDOW_LISTS, &config.window_list),
window,
cx,
)
});
let results = cx.new(|_| {
SliderState::new()
.min(1.)
Expand Down Expand Up @@ -193,6 +207,14 @@ impl Settings {
}
})
.detach();
cx.subscribe_in(&window_list, window, |this, _, event, window, cx| {
if let SelectEvent::Confirm(Some(label)) = event
&& let Some(value) = value_of(&WINDOW_LISTS, label)
{
this.save("window_list", value.into(), window, cx);
}
})
.detach();
cx.subscribe_in(&results, window, |this, _, event, window, cx| {
if let SliderEvent::Change(value) = event {
let value = (value.start().round() as i64).clamp(1, 20);
Expand Down Expand Up @@ -227,6 +249,7 @@ impl Settings {
folder,
theme,
backdrop,
window_list,
results,
autostart: system::autostart_enabled(),
powertoys: system::powertoys_run_enabled(),
Expand Down Expand Up @@ -310,6 +333,9 @@ impl Settings {
let backdrop = index_of(&BACKDROPS, &self.config.backdrop);
self.backdrop
.update(cx, |state, cx| state.set_selected_index(backdrop, window, cx));
let window_list = index_of(&WINDOW_LISTS, &self.config.window_list);
self.window_list
.update(cx, |state, cx| state.set_selected_index(window_list, window, cx));
let results = self.config.max_results as f32;
self.results
.update(cx, |state, cx| state.set_value(results, window, cx));
Expand Down Expand Up @@ -641,6 +667,15 @@ impl Settings {
p,
))
.child(divider(p))
.child(row(
"App window list",
"Show a compact window list when hovering a running app icon.",
Select::new(&self.window_list)
.accessibility_label("App window list")
.w(px(190.)),
p,
))
.child(divider(p))
.child(row(
"Hide the taskbar",
"Hide the Windows taskbar while Wisp runs. The taskbar comes back when Wisp quits.",
Expand Down
1 change: 1 addition & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ impl Launcher {
if let Some(dock) = &mut self.dock {
dock.pace_frames();
dock.set_apps(&self.apps, &self.icons);
dock.set_window_list_mode(self.config.window_list);
// Notification icons need a home while the taskbar is hidden.
dock.set_tray(self.taskbar.as_ref().is_some_and(TaskbarHider::is_active));
dock.set_autohide(self.config.dock_autohide);
Expand Down
23 changes: 23 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ pub enum Position {
/// Keys missing from the user's file take their value from `config-0.3.toml`,
/// then from the default config, as in wisp-settings, so both apps agree on
/// what an old config means.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum WindowListMode {
#[default]
Multiple,
Always,
Off,
}

#[derive(Debug, Deserialize)]
pub struct Config {
pub hide_taskbar: bool,
Expand All @@ -54,6 +63,7 @@ pub struct Config {
pub search_url: String,
pub file_search: bool,
pub file_search_exclude: Vec<String>,
pub window_list: WindowListMode,
}

impl Default for Config {
Expand Down Expand Up @@ -111,6 +121,19 @@ mod tests {
fn legacy_pins_are_ignored() {
let config = Config::parse("dock = true\ndock_apps = ['old.app']").unwrap();
assert!(config.dock);
assert_eq!(config.window_list, super::WindowListMode::Multiple);
}

#[test]
fn window_list_modes_deserialize_and_older_config_defaults_to_multiple() {
for (value, expected) in [
("multiple", super::WindowListMode::Multiple),
("always", super::WindowListMode::Always),
("off", super::WindowListMode::Off),
] {
let config = Config::parse(&format!("window_list = '{value}'")).unwrap();
assert_eq!(config.window_list, expected);
}
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions src/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ dock = true
# false, the dock stays on the main screen and windows stop above it while the
# taskbar is hidden.
dock_autohide = true
# App window list shown when hovering a running app icon:
# "multiple" (only when the app has multiple windows), "always", or "off".
window_list = "multiple"
Loading
Loading