feat(cli): add --toggle to open-many, allow closing multiple windows at once

This commit is contained in:
elkowar 2021-08-19 15:45:07 +02:00
parent 7ffebf6903
commit 9ce219a420
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
3 changed files with 69 additions and 43 deletions

View file

@ -25,6 +25,7 @@ pub enum DaemonCommand {
UpdateCss(String),
OpenMany {
windows: Vec<String>,
should_toggle: bool,
sender: DaemonResponseSender,
},
OpenWindow {
@ -36,8 +37,8 @@ pub enum DaemonCommand {
should_toggle: bool,
sender: DaemonResponseSender,
},
CloseWindow {
window_name: String,
CloseWindows {
windows: Vec<String>,
sender: DaemonResponseSender,
},
KillServer,
@ -97,23 +98,15 @@ impl App {
let mut errors = Vec::new();
let config_result = config::read_from_file(&self.paths.get_yuck_path());
match config_result.and_then(|new_config| self.load_config(new_config)) {
Ok(()) => {}
Err(e) => errors.push(e),
if let Err(e) = config_result.and_then(|new_config| self.load_config(new_config)) {
errors.push(e)
}
let css_result = crate::util::parse_scss_from_file(&self.paths.get_eww_scss_path());
match css_result.and_then(|css| self.load_css(&css)) {
Ok(()) => {}
Err(e) => errors.push(e),
if let Err(e) = css_result.and_then(|css| self.load_css(&css)) {
errors.push(e)
}
let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n");
if errors.is_empty() {
sender.send_success(String::new())?;
} else {
sender.send_failure(errors)?;
}
sender.respond_with_error_list(errors)?;
}
DaemonCommand::UpdateConfig(config) => {
self.load_config(config)?;
@ -132,9 +125,18 @@ impl App {
self.close_window(&window_name)?;
}
}
DaemonCommand::OpenMany { windows, sender } => {
let result = windows.iter().try_for_each(|w| self.open_window(w, None, None, None, None));
respond_with_result(sender, result)?;
DaemonCommand::OpenMany { windows, should_toggle, sender } => {
let errors = windows
.iter()
.map(|w| {
if should_toggle && self.open_windows.contains_key(w) {
self.close_window(w)
} else {
self.open_window(w, None, None, None, None)
}
})
.filter_map(Result::err);
sender.respond_with_error_list(errors)?;
}
DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => {
let result = if should_toggle && self.open_windows.contains_key(&window_name) {
@ -142,11 +144,11 @@ impl App {
} else {
self.open_window(&window_name, pos, size, monitor, anchor)
};
respond_with_result(sender, result)?;
sender.respond_with_result(result)?;
}
DaemonCommand::CloseWindow { window_name, sender } => {
let result = self.close_window(&window_name);
respond_with_result(sender, result)?;
DaemonCommand::CloseWindows { windows, sender } => {
let errors = windows.iter().map(|window| self.close_window(&window)).filter_map(Result::err);
sender.respond_with_error_list(errors)?;
}
DaemonCommand::PrintState { all, sender } => {
let vars = self.eww_state.get_variables().iter();
@ -269,7 +271,8 @@ impl App {
self.eww_config = config;
self.eww_state.clear_all_window_states();
let window_names: Vec<String> = self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
let window_names: Vec<String> =
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
for window_name in &window_names {
self.open_window(&window_name, None, None, None, None)?;
}
@ -381,19 +384,6 @@ fn get_monitor_geometry(n: Option<i32>) -> Result<gdk::Rectangle> {
Ok(monitor.get_geometry())
}
/// In case of an Err, send the error message to a sender.
fn respond_with_result<T>(sender: DaemonResponseSender, result: Result<T>) -> Result<()> {
match result {
Ok(_) => sender.send_success(String::new()),
Err(e) => {
let formatted = error_handling_ctx::format_error(&e);
println!("Action failed with error: {}", formatted);
sender.send_failure(formatted)
},
}
.context("sending response from main thread")
}
pub fn get_window_rectangle(geometry: WindowGeometry, screen_rect: gdk::Rectangle) -> gdk::Rectangle {
let (offset_x, offset_y) = geometry.offset.relative_to(screen_rect.width, screen_rect.height);
let (width, height) = geometry.size.relative_to(screen_rect.width, screen_rect.height);

View file

@ -1,4 +1,7 @@
use anyhow::*;
use itertools::Itertools;
use crate::error_handling_ctx;
/// Response that the app may send as a response to a event.
/// This is used in `DaemonCommand`s that contain a response sender.
@ -34,6 +37,33 @@ impl DaemonResponseSender {
pub fn send_failure(&self, s: String) -> Result<()> {
self.0.send(DaemonResponse::Failure(s)).context("Failed to send failure response from application thread")
}
/// Given a list of errors, respond with an error value if there are any errors, and respond with success otherwise.
pub fn respond_with_error_list(&self, errors: impl IntoIterator<Item = anyhow::Error>) -> Result<()> {
let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n");
if errors.is_empty() {
self.send_success(String::new())
} else {
self.respond_with_error_msg(errors)
}
}
/// In case of an Err, send the error message to a sender.
pub fn respond_with_result<T>(&self, result: Result<T>) -> Result<()> {
match result {
Ok(_) => self.send_success(String::new()),
Err(e) => {
let formatted = error_handling_ctx::format_error(&e);
self.respond_with_error_msg(formatted)
}
}
.context("sending response from main thread")
}
fn respond_with_error_msg(&self, msg: String) -> Result<()> {
println!("Action failed with error: {}", msg);
self.send_failure(msg)
}
}
pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>;

View file

@ -106,11 +106,17 @@ pub enum ActionWithServer {
/// Open multiple windows at once.
/// NOTE: This will in the future be part of eww open, and will then be removed.
#[structopt(name = "open-many")]
OpenMany { windows: Vec<String> },
OpenMany {
windows: Vec<String>,
/// Close the window with the given name
/// If a window is already open, close it instead
#[structopt(long = "toggle")]
should_toggle: bool,
},
/// Close the given windows
#[structopt(name = "close", alias = "c")]
CloseWindow { window_name: String },
CloseWindows { windows: Vec<String> },
/// Reload the configuration
#[structopt(name = "reload", alias = "r")]
@ -184,8 +190,8 @@ impl ActionWithServer {
let _ = send.send(DaemonResponse::Success("pong".to_owned()));
return (app::DaemonCommand::NoOp, Some(recv));
}
ActionWithServer::OpenMany { windows } => {
return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, sender });
ActionWithServer::OpenMany { windows, should_toggle } => {
return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
}
ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle } => {
return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
@ -198,8 +204,8 @@ impl ActionWithServer {
sender,
})
}
ActionWithServer::CloseWindow { window_name } => {
return with_response_channel(|sender| app::DaemonCommand::CloseWindow { window_name, sender });
ActionWithServer::CloseWindows { windows } => {
return with_response_channel(|sender| app::DaemonCommand::CloseWindows { windows, sender });
}
ActionWithServer::Reload => return with_response_channel(app::DaemonCommand::ReloadConfigAndCss),
ActionWithServer::ShowWindows => return with_response_channel(app::DaemonCommand::PrintWindows),