Release 0.9.0-beta.0: workspace creation support

All menu switching commands (`switch-window`, `switch-workspace`, and
`switch-workspace-or-window`) can now create and switch to not yet existing
workspaces.  Just type a digit, a name, or `<digit>:<name>`, confirm your
input, and it'll do.  The `<digit>:<name>` format is explained in `man
sway(5)`.  If that format is given, `swayr` will create the workspace using
`workspace number <digit>:<name>`.  If just a digit or name is given, the
`number` argument is not used.
timeout_old
Tassilo Horn 3 years ago
parent 2191c5d8f7
commit c71698d3ff
  1. 2
      Cargo.lock
  2. 2
      Cargo.toml
  3. 11
      NEWS.md
  4. 57
      src/cmds.rs
  5. 21
      src/con.rs
  6. 6
      src/util.rs

2
Cargo.lock generated

@ -360,7 +360,7 @@ dependencies = [
[[package]] [[package]]
name = "swayr" name = "swayr"
version = "0.8.0" version = "0.9.0-beta.0"
dependencies = [ dependencies = [
"clap", "clap",
"directories", "directories",

@ -1,6 +1,6 @@
[package] [package]
name = "swayr" name = "swayr"
version = "0.8.0" version = "0.9.0-beta.0"
description = "A LRU window-switcher (and more) for the sway window manager" description = "A LRU window-switcher (and more) for the sway window manager"
homepage = "https://sr.ht/~tsdh/swayr/" homepage = "https://sr.ht/~tsdh/swayr/"
repository = "https://git.sr.ht/~tsdh/swayr" repository = "https://git.sr.ht/~tsdh/swayr"

@ -1,3 +1,14 @@
swayr v0.9.0
============
- All menu switching commands (`switch-window`, `switch-workspace`, and
`switch-workspace-or-window`) can now create and switch to not yet existing
workspaces. Just type a digit, a name, or `<digit>:<name>`, confirm your
input, and it'll do. The `<digit>:<name>` format is explained in `man
sway(5)`. If that format is given, `swayr` will create the workspace using
`workspace number <digit>:<name>`. If just a digit or name is given, the
`number` argument is not used.
swayr v0.8.0 swayr v0.8.0
============ ============

@ -21,6 +21,7 @@ use crate::config as cfg;
use crate::layout; use crate::layout;
use crate::util; use crate::util;
use crate::util::DisplayFormat; use crate::util::DisplayFormat;
use lazy_static::lazy_static;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
@ -349,8 +350,7 @@ pub fn exec_swayr_cmd(args: ExecSwayrCmdArgs) {
}) })
} }
if let Some(c) = if let Ok(c) = util::select_from_menu("Select swayr command", &cmds)
util::select_from_menu("Select swayr command", &cmds)
{ {
exec_swayr_cmd(ExecSwayrCmdArgs { exec_swayr_cmd(ExecSwayrCmdArgs {
cmd: c, cmd: c,
@ -393,12 +393,26 @@ pub fn switch_to_urgent_or_lru_window(
} }
} }
lazy_static! {
static ref DIGIT_AND_NAME: regex::Regex =
regex::Regex::new(r"(\d):(.*)").unwrap();
}
pub fn create_workspace(ws_name: &str) {
if DIGIT_AND_NAME.is_match(ws_name) {
run_sway_command(&["workspace", "number", ws_name]);
} else {
run_sway_command(&["workspace", ws_name]);
}
}
pub fn switch_window(extra_props: &HashMap<i64, con::ExtraProps>) { pub fn switch_window(extra_props: &HashMap<i64, con::ExtraProps>) {
let root = get_tree(); let root = get_tree();
let windows = con::get_windows(&root, true, extra_props); let windows = con::get_windows(&root, true, extra_props);
if let Some(window) = con::select_window("Switch to window", &windows) { match util::select_from_menu("Switch to window", &windows) {
focus_window_by_id(window.get_id()) Ok(window) => focus_window_by_id(window.get_id()),
Err(ws_name) => create_workspace(&ws_name),
} }
} }
@ -507,10 +521,9 @@ pub fn switch_workspace(extra_props: &HashMap<i64, con::ExtraProps>) {
let root = get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, false, extra_props); let workspaces = con::get_workspaces(&root, false, extra_props);
if let Some(workspace) = match util::select_from_menu("Switch to workspace", &workspaces) {
con::select_workspace("Switch to workspace", &workspaces) Ok(workspace) => run_sway_command(&["workspace", workspace.get_name()]),
{ Err(ws_name) => create_workspace(&ws_name),
run_sway_command(&["workspace", "number", workspace.get_name()]);
} }
} }
@ -518,16 +531,14 @@ pub fn switch_workspace_or_window(extra_props: &HashMap<i64, con::ExtraProps>) {
let root = get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, true, extra_props); let workspaces = con::get_workspaces(&root, true, extra_props);
let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces);
if let Some(ws_or_win) = con::select_workspace_or_window( match util::select_from_menu("Select workspace or window", &ws_or_wins) {
"Select workspace or window", Ok(ws_or_win) => match ws_or_win {
&ws_or_wins,
) {
match ws_or_win {
con::WsOrWin::Ws { ws } => { con::WsOrWin::Ws { ws } => {
run_sway_command(&["workspace", "number", ws.get_name()]); run_sway_command(&["workspace", ws.get_name()]);
} }
con::WsOrWin::Win { win } => focus_window_by_id(win.get_id()), con::WsOrWin::Win { win } => focus_window_by_id(win.get_id()),
} },
Err(ws_name) => create_workspace(&ws_name),
} }
} }
@ -535,7 +546,7 @@ pub fn quit_window(extra_props: &HashMap<i64, con::ExtraProps>) {
let root = get_tree(); let root = get_tree();
let windows = con::get_windows(&root, true, extra_props); let windows = con::get_windows(&root, true, extra_props);
if let Some(window) = con::select_window("Quit window", &windows) { if let Ok(window) = util::select_from_menu("Quit window", &windows) {
quit_window_by_id(window.get_id()) quit_window_by_id(window.get_id())
} }
} }
@ -544,8 +555,8 @@ pub fn quit_workspace_or_window(extra_props: &HashMap<i64, con::ExtraProps>) {
let root = get_tree(); let root = get_tree();
let workspaces = con::get_workspaces(&root, true, extra_props); let workspaces = con::get_workspaces(&root, true, extra_props);
let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces);
if let Some(ws_or_win) = if let Ok(ws_or_win) =
con::select_workspace_or_window("Quit workspace or window", &ws_or_wins) util::select_from_menu("Quit workspace or window", &ws_or_wins)
{ {
match ws_or_win { match ws_or_win {
con::WsOrWin::Ws { ws } => { con::WsOrWin::Ws { ws } => {
@ -758,8 +769,14 @@ impl DisplayFormat for SwaymsgCmd<'_> {
pub fn exec_swaymsg_command() { pub fn exec_swaymsg_command() {
let cmds = get_swaymsg_commands(); let cmds = get_swaymsg_commands();
let cmd = util::select_from_menu("Execute swaymsg command", &cmds); let cmd = util::select_from_menu("Execute swaymsg command", &cmds);
if let Some(cmd) = cmd { match cmd {
run_sway_command(&cmd.cmd); Ok(cmd) => run_sway_command(&cmd.cmd),
Err(cmd) if !cmd.is_empty() => {
run_sway_command(
&cmd.split_ascii_whitespace().collect::<Vec<&str>>(),
);
}
Err(_) => (),
} }
} }

@ -397,20 +397,6 @@ pub fn get_workspaces<'a>(
workspaces workspaces
} }
pub fn select_window<'a>(
prompt: &str,
windows: &'a [Window],
) -> Option<&'a Window<'a>> {
util::select_from_menu(prompt, windows)
}
pub fn select_workspace<'a>(
prompt: &str,
workspaces: &'a [Workspace],
) -> Option<&'a Workspace<'a>> {
util::select_from_menu(prompt, workspaces)
}
pub enum WsOrWin<'a> { pub enum WsOrWin<'a> {
Ws { ws: &'a Workspace<'a> }, Ws { ws: &'a Workspace<'a> },
Win { win: &'a Window<'a> }, Win { win: &'a Window<'a> },
@ -440,13 +426,6 @@ impl WsOrWin<'_> {
} }
} }
pub fn select_workspace_or_window<'a>(
prompt: &'a str,
ws_or_wins: &'a [WsOrWin<'a>],
) -> Option<&'a WsOrWin<'a>> {
util::select_from_menu(prompt, ws_or_wins)
}
pub struct Workspace<'a> { pub struct Workspace<'a> {
pub node: &'a s::Node, pub node: &'a s::Node,
extra_props: Option<ExtraProps>, extra_props: Option<ExtraProps>,

@ -122,7 +122,7 @@ fn find_icon(icon_name: &str, icon_dirs: &[String]) -> Option<Box<p::Path>> {
lazy_static! { lazy_static! {
static ref WM_CLASS_OR_ICON_RX: regex::Regex = static ref WM_CLASS_OR_ICON_RX: regex::Regex =
regex::Regex::new("(StartupWMClass|Icon)=(.+)").unwrap(); regex::Regex::new(r"(StartupWMClass|Icon)=(.+)").unwrap();
static ref REV_DOMAIN_NAME_RX: regex::Regex = static ref REV_DOMAIN_NAME_RX: regex::Regex =
regex::Regex::new(r"^(?:[a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$").unwrap(); regex::Regex::new(r"^(?:[a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$").unwrap();
} }
@ -234,7 +234,7 @@ pub trait DisplayFormat {
pub fn select_from_menu<'a, 'b, TS>( pub fn select_from_menu<'a, 'b, TS>(
prompt: &'a str, prompt: &'a str,
choices: &'b [TS], choices: &'b [TS],
) -> Option<&'b TS> ) -> Result<&'b TS, String>
where where
TS: DisplayFormat + Sized, TS: DisplayFormat + Sized,
{ {
@ -287,5 +287,5 @@ where
let choice = String::from_utf8_lossy(&output.stdout); let choice = String::from_utf8_lossy(&output.stdout);
let mut choice = String::from(choice); let mut choice = String::from(choice);
choice.pop(); // Remove trailing \n from choice. choice.pop(); // Remove trailing \n from choice.
map.get(&choice).copied() map.get(&choice).copied().ok_or(choice)
} }

Loading…
Cancel
Save