From 69aa2597c87f04f3cc2f83c020b9454712249759 Mon Sep 17 00:00:00 2001 From: Tassilo Horn Date: Sat, 13 Nov 2021 15:51:38 +0100 Subject: [PATCH] Support indentation and {layout} in formats --- NEWS.md | 15 ++++++++++ src/cmds.rs | 12 +++++++- src/config.rs | 22 ++++++++++++-- src/tree.rs | 83 ++++++++++++++++++++++++++++++++++++++------------- src/util.rs | 1 + 5 files changed, 109 insertions(+), 24 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7ae1a26..3039cc1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,18 @@ +swayr v0.10.0 +============= + +- The `con` module which enhances the sway IPC container tree structure has + been replaced by `tree` which achieves the same job but is not restricted to + only handle workspaces, and windows. +- Formats such as `format.workspace_format`, and `format.window_format` can now + include a `{indent}` placeholder which will be replaced with N times the new + `format.indent` value. N is the depth in the shown menu input, e.g., with + `swayr switch-workspace-or-window` the indent level for workspaces is 0, and + 1 for windows. +- The `format.workspace_format` may now include a `{layout}` placeholder which + is replaced with the current container's layout. + + swayr v0.9.0 ============ diff --git a/src/cmds.rs b/src/cmds.rs index 837a30b..7c46cbb 100644 --- a/src/cmds.rs +++ b/src/cmds.rs @@ -167,9 +167,15 @@ pub struct ExecSwayrCmdArgs<'a> { impl DisplayFormat for SwayrCommand { fn format_for_display(&self, _: &cfg::Config) -> std::string::String { - // TODO: Add a format to Config + // TODO: It would be very nice if the display format was exactly like + // the swayr invocation in the shell. Can that somehow be retrieved + // from clap? format!("{:?}", self) } + + fn get_indent_level(&self) -> usize { + 0 + } } fn always_true(_x: &t::DisplayNode) -> bool { @@ -867,6 +873,10 @@ impl DisplayFormat for SwaymsgCmd<'_> { fn format_for_display(&self, _: &cfg::Config) -> std::string::String { self.cmd.join(" ") } + + fn get_indent_level(&self) -> usize { + 0 + } } pub fn exec_swaymsg_command() { diff --git a/src/config.rs b/src/config.rs index 34834e8..f1ab53a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,6 +87,14 @@ impl Config { // .expect("No format.container_format defined.") // } + pub fn get_format_indent(&self) -> String { + self.format + .as_ref() + .and_then(|f| f.indent.clone()) + .or_else(|| Format::default().indent) + .expect("No format.indent defined.") + } + pub fn get_format_urgency_start(&self) -> String { self.format .as_ref() @@ -156,6 +164,7 @@ pub struct Format { window_format: Option, workspace_format: Option, //container_format: Option, + indent: Option, urgency_start: Option, urgency_end: Option, html_escape: Option, @@ -196,6 +205,7 @@ impl Default for Menu { "--insensitive".to_string(), "--cache-file=/dev/null".to_string(), "--parse-search".to_string(), + "--height=40%".to_string(), "--prompt={prompt}".to_string(), ]), } @@ -206,13 +216,14 @@ impl Default for Format { fn default() -> Self { Format { window_format: Some( - "{urgency_start}“{title}”{urgency_end} \ - — {app_name} on workspace {workspace_name} \ + "img:{app_icon}:text:{indent}{app_name} — \ + {urgency_start}“{title}”{urgency_end} {layout} \ + on workspace {workspace_name} {marks} \ ({id})" .to_string(), ), workspace_format: Some( - "Workspace {name} \ + "{indent}Workspace {name} {layout} \ ({id})" .to_string(), ), @@ -221,6 +232,7 @@ impl Default for Format { // ({id})" // .to_string(), // ), + indent: Some(" ".to_string()), html_escape: Some(true), urgency_start: Some( "" @@ -229,7 +241,10 @@ impl Default for Format { urgency_end: Some("".to_string()), icon_dirs: Some(vec![ "/usr/share/icons/hicolor/scalable/apps".to_string(), + "/usr/share/icons/hicolor/64x64/apps".to_string(), "/usr/share/icons/hicolor/48x48/apps".to_string(), + "/usr/share/icons/Adwaita/64x64/apps".to_string(), + "/usr/share/icons/Adwaita/48x48/apps".to_string(), "/usr/share/pixmaps".to_string(), ]), fallback_icon: None, @@ -240,6 +255,7 @@ impl Default for Format { impl Default for Layout { fn default() -> Layout { let resolution_min_width_vec = vec![ + [800, 400], [1024, 500], [1280, 600], [1400, 680], diff --git a/src/tree.rs b/src/tree.rs index 65cda15..285f562 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -151,7 +151,7 @@ impl NodeMethods for s::Node { } /// Extra properties gathered by swayrd for windows and workspaces. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub struct ExtraProps { /// Milliseconds since UNIX epoch. pub last_focus_time: u128, @@ -165,9 +165,17 @@ pub struct Tree<'a> { extra_props: &'a HashMap, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum IndentLevel { + Fixed(u32), + WorkspacesZeroWindowsOne, + TreeDepth(u8), +} + pub struct DisplayNode<'a> { pub node: &'a s::Node, pub tree: &'a Tree<'a>, + indent_level: IndentLevel, } impl<'a> Tree<'a> { @@ -226,11 +234,16 @@ impl<'a> Tree<'a> { self.sorted_nodes_of_type_1(self.root, t) } - fn as_display_nodes(&self, v: Vec<&'a s::Node>) -> Vec { + fn as_display_nodes( + &self, + v: Vec<&'a s::Node>, + indent_level: IndentLevel, + ) -> Vec { v.iter() - .map(|n| DisplayNode { - node: n, + .map(|node| DisplayNode { + node, tree: self, + indent_level, }) .collect() } @@ -245,13 +258,13 @@ impl<'a> Tree<'a> { pub fn get_workspaces(&self) -> Vec { let mut v = self.sorted_nodes_of_type(Type::Workspace); v.rotate_left(1); - self.as_display_nodes(v) + self.as_display_nodes(v, IndentLevel::Fixed(0)) } pub fn get_windows(&self) -> Vec { let mut v = self.sorted_nodes_of_type(Type::Window); v.rotate_left(1); - self.as_display_nodes(v) + self.as_display_nodes(v, IndentLevel::Fixed(0)) } pub fn get_workspaces_and_windows(&self) -> Vec { @@ -274,7 +287,7 @@ impl<'a> Tree<'a> { v.rotate_left(1); } - self.as_display_nodes(v) + self.as_display_nodes(v, IndentLevel::WorkspacesZeroWindowsOne) } pub fn is_child_of_tiled_container(&self, id: i64) -> bool { @@ -351,27 +364,27 @@ fn maybe_html_escape(do_it: bool, text: &str) -> String { impl DisplayFormat for DisplayNode<'_> { fn format_for_display(&self, cfg: &config::Config) -> String { + let indent = cfg.get_format_indent(); + let html_escape = cfg.get_format_html_escape(); + match self.node.get_type() { Type::Root => String::from("Cannot format Root"), Type::Output => String::from("Cannot format Output"), - Type::Workspace => { - let fmt = cfg.get_format_workspace_format(); - let html_escape = cfg.get_format_html_escape(); - - fmt.replace("{id}", format!("{}", self.node.id).as_str()) - .replace( - "{name}", - &maybe_html_escape(html_escape, self.node.get_name()), - ) - } + Type::Workspace => cfg + .get_format_workspace_format() + .replace("{indent}", &indent.repeat(self.get_indent_level())) + .replace("{layout}", format!("{:?}", self.node.layout).as_str()) + .replace("{id}", format!("{}", self.node.id).as_str()) + .replace( + "{name}", + &maybe_html_escape(html_escape, self.node.get_name()), + ), Type::Container => { todo!("DisplayFormat for Container not yet implemented") } Type::Window => { - let window_format = cfg.get_format_window_format(); let urgency_start = cfg.get_format_urgency_start(); let urgency_end = cfg.get_format_urgency_end(); - let html_escape = cfg.get_format_html_escape(); let icon_dirs = cfg.get_format_icon_dirs(); // fallback_icon has no default value. let fallback_icon: Option> = @@ -386,7 +399,15 @@ impl DisplayFormat for DisplayNode<'_> { let app_name_no_version = APP_NAME_AND_VERSION_RX.replace(app_name, "$1"); - window_format + cfg.get_format_window_format() + .replace( + "{indent}", + &indent.repeat(self.get_indent_level()), + ) + .replace( + "{layout}", + format!("{:?}", self.node.layout).as_str(), + ) .replace("{id}", format!("{}", self.node.id).as_str()) .replace( "{urgency_start}", @@ -454,4 +475,26 @@ impl DisplayFormat for DisplayNode<'_> { } } } + + fn get_indent_level(&self) -> usize { + match self.indent_level { + IndentLevel::Fixed(level) => level as usize, + IndentLevel::WorkspacesZeroWindowsOne => { + match self.node.get_type(){ + Type::Workspace => 0, + Type::Window => 1, + _ => panic!("Only Workspaces and Windows expected. File a bug report!") + } + } + IndentLevel::TreeDepth(offset) => { + let mut depth: usize = 0; + let mut node = self.node; + while let Some(p) = self.tree.get_parent_node(node.id) { + depth += 1; + node = p; + } + depth - offset as usize + } + } + } } diff --git a/src/util.rs b/src/util.rs index a450e4d..64c19bd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -229,6 +229,7 @@ fn test_icon_stuff() { pub trait DisplayFormat { fn format_for_display(&self, config: &cfg::Config) -> String; + fn get_indent_level(&self) -> usize; } pub fn select_from_menu<'a, 'b, TS>(