Support indentation and {layout} in formats

timeout_old
Tassilo Horn 3 years ago
parent 1c1b155e95
commit 69aa2597c8
  1. 15
      NEWS.md
  2. 12
      src/cmds.rs
  3. 22
      src/config.rs
  4. 83
      src/tree.rs
  5. 1
      src/util.rs

@ -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
============

@ -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() {

@ -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<String>,
workspace_format: Option<String>,
//container_format: Option<String>,
indent: Option<String>,
urgency_start: Option<String>,
urgency_end: Option<String>,
html_escape: Option<bool>,
@ -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}<b>{title}</b>{urgency_end} \
<i>{app_name}</i> on workspace {workspace_name} \
"img:{app_icon}:text:{indent}<i>{app_name}</i> \
{urgency_start}<b>{title}</b>{urgency_end} {layout} \
on workspace {workspace_name} {marks} \
<span alpha=\"20000\">({id})</span>"
.to_string(),
),
workspace_format: Some(
"<b>Workspace {name}</b> \
"{indent}<b>Workspace {name}</b> {layout} \
<span alpha=\"20000\">({id})</span>"
.to_string(),
),
@ -221,6 +232,7 @@ impl Default for Format {
// <span alpha=\"20000\">({id})</span>"
// .to_string(),
// ),
indent: Some(" ".to_string()),
html_escape: Some(true),
urgency_start: Some(
"<span background=\"darkred\" foreground=\"yellow\">"
@ -229,7 +241,10 @@ impl Default for Format {
urgency_end: Some("</span>".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],

@ -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<i64, ExtraProps>,
}
#[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<DisplayNode> {
fn as_display_nodes(
&self,
v: Vec<&'a s::Node>,
indent_level: IndentLevel,
) -> Vec<DisplayNode> {
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<DisplayNode> {
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<DisplayNode> {
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<DisplayNode> {
@ -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<Box<std::path::Path>> =
@ -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
}
}
}
}

@ -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>(

Loading…
Cancel
Save