Skip to content

Commit

Permalink
Allow window definitions to have parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
WilfSilver committed Jul 1, 2023
1 parent 25e50ed commit 8321d07
Show file tree
Hide file tree
Showing 14 changed files with 542 additions and 87 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ All notable changes to eww will be listed here, starting at changes since versio
- Add `:onaccept` to input field, add `:onclick` to eventbox
- Add `EWW_CMD`, `EWW_CONFIG_DIR`, `EWW_EXECUTABLE` magic variables
- Add `overlay` widget (By: viandoxdev)
- Add arguments option to `defwindow` (By: WilfSilver)

### Notable Internal changes
- Rework state management completely, now making local state and dynamic widget hierarchy changes possible.
Expand Down
151 changes: 103 additions & 48 deletions crates/eww/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use crate::{
paths::EwwPaths,
script_var_handler::ScriptVarHandlerHandle,
state::scope_graph::{ScopeGraph, ScopeIndex},
*,
window_arguments::WindowArguments,
*, window_initiator::WindowInitiator,
};
use anyhow::anyhow;
use codespan_reporting::files::Files;
Expand All @@ -26,7 +27,6 @@ use yuck::{
config::{
monitor::MonitorIdentifier,
script_var_definition::ScriptVarDefinition,
window_definition::WindowDefinition,
window_geometry::{AnchorPoint, WindowGeometry},
},
error::DiagError,
Expand All @@ -44,18 +44,21 @@ pub enum DaemonCommand {
ReloadConfigAndCss(DaemonResponseSender),
OpenInspector,
OpenMany {
windows: Vec<String>,
windows: Vec<(String, String)>,
args: Vec<(String, VarName, DynVal)>,
should_toggle: bool,
sender: DaemonResponseSender,
},
OpenWindow {
window_name: String,
instance_id: Option<String>,
pos: Option<Coords>,
size: Option<Coords>,
anchor: Option<AnchorPoint>,
screen: Option<MonitorIdentifier>,
should_toggle: bool,
sender: DaemonResponseSender,
args: Option<Vec<(VarName, DynVal)>>,
},
CloseWindows {
windows: Vec<String>,
Expand All @@ -79,6 +82,7 @@ pub enum DaemonCommand {
/// An opened window.
#[derive(Debug)]
pub struct EwwWindow {
pub instance_id: String,
pub name: String,
pub scope_index: ScopeIndex,
pub gtk_window: gtk::Window,
Expand All @@ -105,6 +109,7 @@ pub struct App<B> {
pub eww_config: config::EwwConfig,
/// Map of all currently open windows
pub open_windows: HashMap<String, EwwWindow>,
pub window_argumentss: HashMap<String, WindowArguments>,
/// Window names that are supposed to be open, but failed.
/// When reloading the config, these should be opened again.
pub failed_windows: HashSet<String>,
Expand All @@ -124,6 +129,7 @@ impl<B> std::fmt::Debug for App<B> {
.field("eww_config", &self.eww_config)
.field("open_windows", &self.open_windows)
.field("failed_windows", &self.failed_windows)
.field("window_argumentss", &self.window_argumentss)
.field("paths", &self.paths)
.finish()
}
Expand Down Expand Up @@ -174,25 +180,52 @@ impl<B: DisplayBackend> App<B> {
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, None, None, None, None)
log::debug!("Config: {}, id: {}", config_name, id);
let window_args: Vec<(VarName, DynVal)> =
args.iter()
.filter_map(|(win_id, n, v)| {
if win_id.is_empty() || win_id == id {
Some((n.clone(), v.clone()))
} else {
None
}
})
.collect();
self.open_window(&WindowArguments::new_from_args(id.to_string(), config_name.clone(), window_args)?)
}
})
.filter_map(Result::err);
sender.respond_with_error_list(errors)?;
}
DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => {
let is_open = self.open_windows.contains_key(&window_name);
DaemonCommand::OpenWindow {
window_name,
instance_id,
pos,
size,
anchor,
screen: monitor,
should_toggle,
sender,
args,
} => {
let id = instance_id.unwrap_or_else(|| window_name.clone());

let is_open = self.open_windows.contains_key(&id);

let result = if !is_open {
self.open_window(&window_name, pos, size, monitor, anchor)
self.open_window(
&WindowArguments::new(id, window_name, pos, size, monitor, anchor, args.unwrap_or_default()),
)
} else if should_toggle {
self.close_window(&window_name)
self.close_window(&id)
} else {
Ok(())
};
Expand Down Expand Up @@ -293,11 +326,11 @@ impl<B: DisplayBackend> App<B> {
}

/// Close a window and do all the required cleanups in the scope_graph and script_var_handler
fn close_window(&mut self, window_name: &str) -> Result<()> {
fn close_window(&mut self, instance_id: &str) -> Result<()> {
let eww_window = self
.open_windows
.remove(window_name)
.with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))?;
.remove(instance_id)
.with_context(|| format!("Tried to close window named '{}', but no such window was open", instance_id))?;

let scope_index = eww_window.scope_index;
eww_window.close();
Expand All @@ -310,50 +343,52 @@ impl<B: DisplayBackend> App<B> {
self.script_var_handler.stop_for_variable(unused_var.clone());
}

self.window_argumentss.remove(instance_id);

Ok(())
}

fn open_window(
&mut self,
window_name: &str,
pos: Option<Coords>,
size: Option<Coords>,
monitor: Option<MonitorIdentifier>,
anchor: Option<AnchorPoint>,
) -> Result<()> {
self.failed_windows.remove(window_name);
log::info!("Opening window {}", window_name);
fn open_window(&mut self, window_args: &WindowArguments) -> Result<()> {
let instance_id = &window_args.id;
self.failed_windows.remove(instance_id);
log::info!("Opening window {} as '{}'", window_args.config_name, instance_id);

// if an instance of this is already running, close it
if self.open_windows.contains_key(window_name) {
self.close_window(window_name)?;
if self.open_windows.contains_key(instance_id) {
self.close_window(instance_id)?;
}

self.window_argumentss.insert(instance_id.to_string(), window_args.clone());

let open_result: Result<_> = try {
let mut window_def = self.eww_config.get_window(window_name)?.clone();
let window_name: &str = &window_args.config_name;

let window_def = self.eww_config.get_window(window_name)?.clone();
assert_eq!(window_def.name, window_name, "window definition name did not equal the called window");
window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size));

let initiator = WindowInitiator::new(&window_def, window_args)?;

let root_index = self.scope_graph.borrow().root_index;

let window_scope = self.scope_graph.borrow_mut().register_new_scope(
window_name.to_string(),
Some(root_index),
root_index,
HashMap::new(),
initiator.get_scoped_vars(),
)?;

let root_widget = crate::widgets::build_widget::build_gtk_widget(
&mut self.scope_graph.borrow_mut(),
Rc::new(self.eww_config.get_widget_definitions().clone()),
window_scope,
window_def.widget.clone(),
window_def.widget,
None,
)?;

let monitor_geometry = get_monitor_geometry(monitor.or_else(|| window_def.monitor.clone()))?;
root_widget.style_context().add_class(window_name);

let mut eww_window = initialize_window::<B>(monitor_geometry, root_widget, window_def, window_scope)?;
let monitor_geometry = get_monitor_geometry(initiator.monitor.clone())?;
let mut eww_window = initialize_window::<B>(&initiator, monitor_geometry, root_widget, window_scope)?;
eww_window.gtk_window.style_context().add_class(window_name);

// initialize script var handlers for variables. As starting a scriptvar with the script_var_handler is idempodent,
Expand All @@ -380,12 +415,12 @@ impl<B: DisplayBackend> App<B> {
}
}));

self.open_windows.insert(window_name.to_string(), eww_window);
self.open_windows.insert(instance_id.to_string(), eww_window);
};

if let Err(err) = open_result {
self.failed_windows.insert(window_name.to_string());
Err(err).with_context(|| format!("failed to open window `{}`", window_name))
self.failed_windows.insert(instance_id.to_string());
Err(err).with_context(|| format!("failed to open window `{}`", instance_id))
} else {
Ok(())
}
Expand All @@ -404,10 +439,19 @@ impl<B: DisplayBackend> App<B> {
self.eww_config = config;
self.scope_graph.borrow_mut().clear(self.eww_config.generate_initial_state()?);

let window_names: Vec<String> =
let instances: 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)?;
let initiators = self.window_argumentss.clone();
for instance_id in &instances {
let window_arguments;
match initiators.get(instance_id) {
Some(x) => window_arguments = x,
None => {
return Err(anyhow!("Cannot reopen window, initial parameters were not saved correctly for {}", instance_id))
}
};

self.open_window(window_arguments)?;
}
Ok(())
}
Expand Down Expand Up @@ -436,19 +480,24 @@ impl<B: DisplayBackend> App<B> {
}

fn initialize_window<B: DisplayBackend>(
window_init: &WindowInitiator,
monitor_geometry: gdk::Rectangle,
root_widget: gtk::Widget,
window_def: WindowDefinition,
window_scope: ScopeIndex,
) -> Result<EwwWindow> {
let window = B::initialize_window(&window_def, monitor_geometry)
.with_context(|| format!("monitor {} is unavailable", window_def.monitor.clone().unwrap()))?;

window.set_title(&format!("Eww - {}", window_def.name));
let window = B::initialize_window(window_init, monitor_geometry)
.with_context(
|| format!(
"monitor {} is unavailable",
window_init.monitor.clone().unwrap()
)
)?;

window.set_title(&format!("Eww - {}", window_init.name));
window.set_position(gtk::WindowPosition::None);
window.set_gravity(gdk::Gravity::Center);

if let Some(geometry) = window_def.geometry {
if let Some(geometry) = window_init.geometry {
let actual_window_rect = get_window_rectangle(geometry, monitor_geometry);
window.set_size_request(actual_window_rect.width(), actual_window_rect.height());
window.set_default_size(actual_window_rect.width(), actual_window_rect.height());
Expand All @@ -467,21 +516,27 @@ fn initialize_window<B: DisplayBackend>(

#[cfg(feature = "x11")]
{
if let Some(geometry) = window_def.geometry {
if let Some(geometry) = window_init.geometry {
let _ = apply_window_position(geometry, monitor_geometry, &window);
if window_def.backend_options.x11.window_type != yuck::config::backend_window_options::X11WindowType::Normal {
if window_init.backend_options.x11.window_type != yuck::config::backend_window_options::X11WindowType::Normal {
window.connect_configure_event(move |window, _| {
let _ = apply_window_position(geometry, monitor_geometry, window);
false
});
}
}
display_backend::set_xprops(&window, monitor_geometry, &window_def)?;
display_backend::set_xprops(&window, monitor_geometry, window_init)?;
}

window.show_all();

Ok(EwwWindow { name: window_def.name, gtk_window: window, scope_index: window_scope, destroy_event_handler_id: None })
Ok(EwwWindow {
instance_id: window_init.id.clone(),
name: window_init.name.clone(),
gtk_window: window,
scope_index: window_scope,
destroy_event_handler_id: None,
})
}

/// Apply the provided window-positioning rules to the window.
Expand Down
Loading

0 comments on commit 8321d07

Please sign in to comment.