Skip to content

Commit

Permalink
Add args option to open-many command
Browse files Browse the repository at this point in the history
  • Loading branch information
WilfSilver authored and HugoWhittome committed Oct 27, 2022
1 parent ccd8d65 commit e78c615
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 12 deletions.
59 changes: 50 additions & 9 deletions crates/eww/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
str::FromStr
};
use tokio::sync::mpsc::UnboundedSender;
use yuck::{
Expand All @@ -43,7 +44,8 @@ pub enum DaemonCommand {
ReloadConfigAndCss(DaemonResponseSender),
OpenInspector,
OpenMany {
windows: Vec<String>,
windows: Vec<(String, String)>,
args: Vec<(String, VarName, DynVal)>,
should_toggle: bool,
sender: DaemonResponseSender,
},
Expand Down Expand Up @@ -99,11 +101,42 @@ impl WindowInitiator {
WindowInitiator { config_name, pos, size, monitor, anchor, args }
}

pub fn new_from_args(config_name: String, mut args: Vec<(VarName, DynVal)>) -> Result<Self> {
let initiator = WindowInitiator {
config_name,
pos: WindowInitiator::extract_value_from_args::<Coords>("pos", &mut args)?,
size: WindowInitiator::extract_value_from_args::<Coords>("size", &mut args)?,
monitor: WindowInitiator::extract_value_from_args::<i32>("screen", &mut args)?,
anchor: WindowInitiator::extract_value_from_args::<AnchorPoint>("anchor", &mut args)?,
args,
};

Ok(initiator)
}

pub fn extract_value_from_args<T: FromStr>(name: &str, args: &mut Vec<(VarName, DynVal)>) -> Result<Option<T>, T::Err> {
let var_name = name.to_string();
let pos = args.iter()
.position(|(n,_)| n.0 == var_name);

if pos.is_some() {
let (_, val) = args.remove(pos.unwrap());
let converted_val = T::from_str(&val.0)?;
Ok(Some(converted_val))
} else {
Ok(None)
}
}

pub fn get_local_window_variables(&self, id: &str, window_def: &WindowDefinition) -> Result<HashMap<VarName, DynVal>> {
let expected_args: HashSet<&String> = window_def.expected_args.iter().map(|x| &x.name.0).collect();
let mut local_variables: HashMap<VarName, DynVal> = HashMap::new();

local_variables.insert(VarName::from("id"), DynVal::from(id));
if self.monitor.is_some() {
// Inserts these first so they can be overridden
if expected_args.contains(&"id".to_string()) {
local_variables.insert(VarName::from("id"), DynVal::from(id));
}
if self.monitor.is_some() && expected_args.contains(&"screen".to_string()) {
local_variables.insert(VarName::from("screen"), DynVal::from(self.monitor.unwrap()));
}

Expand All @@ -121,12 +154,15 @@ impl WindowInitiator {
}

if local_variables.len() != window_def.expected_args.len() {
let expected_args: HashSet<&String> = window_def.expected_args.iter().map(|x| &x.name.0).collect();
let unexpected_vars: Vec<VarName> = local_variables
.iter()
.filter_map(|(n,_)| if !expected_args.contains(&n.0) { Some(n.clone()) } else { None })
.collect();
return Err(anyhow!("{} were unexpectedly defined when creating window {}", unexpected_vars.join(","), self.config_name));
return Err(anyhow!("'{}' {} unexpectedly defined when creating window {}",
unexpected_vars.join(","),
if unexpected_vars.len() == 1 { "was" } else { "were"},
self.config_name
));
}

Ok(local_variables)
Expand Down Expand Up @@ -233,14 +269,19 @@ impl App {
self.close_window(&window_name)?;
}
}
DaemonCommand::OpenMany { windows, should_toggle, sender } => {
DaemonCommand::OpenMany { windows, args, should_toggle, sender } => {
let errors = windows
.iter()
.map(|w| {
if should_toggle && self.open_windows.contains_key(w) {
self.close_window(w)
let (config_name, id) = w;
if should_toggle && self.open_windows.contains_key(id) {
self.close_window(id)
} else {
self.open_window(w, &WindowInitiator::new(w.clone(), None, None, None, None, Vec::new()))
log::debug!("Config: {}, id: {}", config_name, id);
let window_args: Vec<(VarName, DynVal)> = args.iter()
.filter_map(|(win_id,n,v)| if win_id == "" || win_id == id { Some((n.clone(), v.clone())) } else { None })
.collect();
self.open_window(id, &WindowInitiator::new_from_args(config_name.clone(), window_args)?)
}
})
.filter_map(Result::err);
Expand Down
29 changes: 26 additions & 3 deletions crates/eww/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ pub enum ActionWithServer {
/// NOTE: This will in the future be part of eww open, and will then be removed.
#[clap(name = "open-many")]
OpenMany {
windows: Vec<String>,
#[structopt(parse(try_from_str = parse_window_config_and_id))]
windows: Vec<(String, String)>,

#[structopt(long, parse(try_from_str = parse_window_id_args))]
args: Vec<(String, VarName, DynVal)>,

/// If a window is already open, close it instead
#[clap(long = "toggle")]
Expand Down Expand Up @@ -193,6 +197,25 @@ impl From<RawOpt> for Opt {
}
}

fn parse_window_config_and_id(s: &str) -> Result<(String, String)> {
let (name, id) = s
.split_once(':')
.unwrap_or((s, s));

Ok((name.to_string(), id.to_string()))
}

fn parse_window_id_args(s: &str) -> Result<(String, VarName, DynVal)> {
// Parse the = first so we know if an id has not been given
let (name, value) = parse_var_update_arg(s)?;

let (id, var_name) = (&name.0)
.split_once(':')
.unwrap_or((&"", &name.0));

Ok((id.to_string(), var_name.into(), value))
}

fn parse_var_update_arg(s: &str) -> Result<(VarName, DynVal)> {
let (name, value) = s
.split_once('=')
Expand All @@ -217,8 +240,8 @@ impl ActionWithServer {
let _ = send.send(DaemonResponse::Success("pong".to_owned()));
return (app::DaemonCommand::NoOp, Some(recv));
}
ActionWithServer::OpenMany { windows, should_toggle } => {
return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
ActionWithServer::OpenMany { windows, args, should_toggle } => {
return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, args, should_toggle, sender });
}
ActionWithServer::OpenWindow { window_name, id, pos, size, screen, anchor, should_toggle, args } => {
return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
Expand Down
33 changes: 33 additions & 0 deletions docs/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,14 @@ eww open my_bar --screen 0 --id primary
eww open my_bar --screen 1 --id secondary
```

When using `open-many` you can follow the structure below. Again if no id is given, the id will default to the name of the window configuration.

```bash
eww open-many my_config:primary my_config:secondary
```

You may notice with this we didn't set `screen`, this is set through the `--args` system, please see below for more information.

However you may want to have slight changes for each of these bars, e.g. spawning other windows on the same monitor. This is where the arguments come in.

Defining arguments in a window is the exact same as in a widget so you can have:
Expand All @@ -285,13 +293,38 @@ Once we have these parameters, when opening a new window, we must specify them (
eww open my_bar --id primary --args arg1=some_value arg2=another_value
```

With the `open-many` it looks like this:

```bash
# Please note that `--args` option must be given after all the windows names
eww open-many my_bar:primary --args primary:arg1=some_value primary:arg2=another_value
```

Using this method you can define `screen`, `anchor`, `pos`, `size` inside the args for each window and it will act like giving `--screen`, `--anchor` etc. in the `open` command.

You may notice that this is the same layout to set values with `update` and you'd be correct.

So, now you know the basics, I shall introduce you to some of these "special" parameters, which are set slightly differently. However these can all be overridden by the `--args` option.

- `id` - If `id` is included in the argument list, it will be set to the id specified by `--id` or will be set to the name of the config. This can be used when closing the current window through eww commands.
- `screen` - If `screen` is specified it will be set to the value given by `--screen`, so you can use this in other widgets to access screen specific information.

### Further insight into args in `open-many`

Now due to the system behind processing the `open-many` `--args` option you don't have to specify an id for each argument. If you do not, that argument will be applied across all windows e.g.

```bash
eww open-many -c "~/.config/eww/bars" my_bar:primary my_bar:secondary --args config="~/.config/eww/bars"
```

This will mean the config is the same throughout the bars.

Furthermore if you didn't specify an id for the window, you can still set args specifically for that window - following the idea that the id will be set to the window configuration if not given - by just using the name of the window configuration e.g.

```bash
eww open-many my_primary_bar --args my_primary_bar:screen=0
```

## Generating a list of widgets from JSON using `for`

If you want to display a list of values, you can use the `for`-Element to fill a container with a list of elements generated from a JSON-array.
Expand Down

0 comments on commit e78c615

Please sign in to comment.