feat: reduced max_width to 100 and ran cargo fmt
This commit is contained in:
@@ -3,11 +3,16 @@ use crate::{
|
||||
daemon_response::DaemonResponseSender,
|
||||
display_backend::DisplayBackend,
|
||||
error_handling_ctx,
|
||||
gtk::prelude::{ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt},
|
||||
gtk::prelude::{
|
||||
ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt,
|
||||
},
|
||||
paths::EwwPaths,
|
||||
widgets::window::Window,
|
||||
// dynval::DynVal,
|
||||
widgets::{build_widget::build_gtk_widget, build_widget::WidgetInput, widget_definitions::WidgetRegistry},
|
||||
widgets::{
|
||||
build_widget::build_gtk_widget, build_widget::WidgetInput,
|
||||
widget_definitions::WidgetRegistry,
|
||||
},
|
||||
window::{
|
||||
coords::Coords,
|
||||
monitor::MonitorIdentifier,
|
||||
@@ -139,8 +144,8 @@ async fn wait_for_monitor_model() {
|
||||
let display = gdk::Display::default().expect("could not get default display");
|
||||
let start = std::time::Instant::now();
|
||||
loop {
|
||||
let all_monitors_set =
|
||||
(0..display.n_monitors()).all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some());
|
||||
let all_monitors_set = (0..display.n_monitors())
|
||||
.all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some());
|
||||
if all_monitors_set {
|
||||
break;
|
||||
}
|
||||
@@ -241,13 +246,24 @@ impl<B: DisplayBackend> App<B> {
|
||||
let result = if should_toggle && is_open {
|
||||
self.close_window(&instance_id, false)
|
||||
} else {
|
||||
self.open_window(&WindowArguments { instance_id, window_name, pos, size, monitor, anchor, duration })
|
||||
self.open_window(&WindowArguments {
|
||||
instance_id,
|
||||
window_name,
|
||||
pos,
|
||||
size,
|
||||
monitor,
|
||||
anchor,
|
||||
duration,
|
||||
})
|
||||
};
|
||||
|
||||
sender.respond_with_result(result)?;
|
||||
}
|
||||
DaemonCommand::CloseWindows { windows, auto_reopen, sender } => {
|
||||
let errors = windows.iter().map(|window| self.close_window(window, auto_reopen)).filter_map(Result::err);
|
||||
let errors = windows
|
||||
.iter()
|
||||
.map(|window| self.close_window(window, auto_reopen))
|
||||
.filter_map(Result::err);
|
||||
// Ignore sending errors, as the channel might already be closed
|
||||
let _ = sender.respond_with_error_list(errors);
|
||||
}
|
||||
@@ -256,7 +272,11 @@ impl<B: DisplayBackend> App<B> {
|
||||
sender.send_success(output)?
|
||||
}
|
||||
DaemonCommand::ListActiveWindows(sender) => {
|
||||
let output = self.open_windows.iter().map(|(id, window)| format!("{id}: {}", window.name)).join("\n");
|
||||
let output = self
|
||||
.open_windows
|
||||
.iter()
|
||||
.map(|(id, window)| format!("{id}: {}", window.name))
|
||||
.join("\n");
|
||||
sender.send_success(output)?
|
||||
}
|
||||
DaemonCommand::PrintDebug(sender) => {
|
||||
@@ -282,10 +302,9 @@ impl<B: DisplayBackend> App<B> {
|
||||
if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(instance_id) {
|
||||
_ = old_abort_send.send(());
|
||||
}
|
||||
let ewwii_window = self
|
||||
.open_windows
|
||||
.remove(instance_id)
|
||||
.with_context(|| format!("Tried to close window with id '{instance_id}', but no such window was open"))?;
|
||||
let ewwii_window = self.open_windows.remove(instance_id).with_context(|| {
|
||||
format!("Tried to close window with id '{instance_id}', but no such window was open")
|
||||
})?;
|
||||
|
||||
// let scope_index = ewwii_window.scope_index;
|
||||
ewwii_window.close();
|
||||
@@ -324,7 +343,10 @@ impl<B: DisplayBackend> App<B> {
|
||||
let window_name: &str = &window_args.window_name;
|
||||
|
||||
let window_def = self.ewwii_config.get_window(window_name)?.clone();
|
||||
assert_eq!(window_def.name, window_name, "window definition name did not equal the called window");
|
||||
assert_eq!(
|
||||
window_def.name, window_name,
|
||||
"window definition name did not equal the called window"
|
||||
);
|
||||
|
||||
let initiator = WindowInitiator::new(&window_def, window_args)?;
|
||||
|
||||
@@ -332,7 +354,8 @@ impl<B: DisplayBackend> App<B> {
|
||||
// It is critical for supporting dynamic updates
|
||||
let mut widget_reg_store = WidgetRegistry::new(Some(&window_def.root_widget));
|
||||
|
||||
let root_widget = build_gtk_widget(WidgetInput::Window(window_def), &mut widget_reg_store)?;
|
||||
let root_widget =
|
||||
build_gtk_widget(WidgetInput::Window(window_def), &mut widget_reg_store)?;
|
||||
|
||||
root_widget.style_context().add_class(window_name);
|
||||
|
||||
@@ -344,14 +367,16 @@ impl<B: DisplayBackend> App<B> {
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||
let config_path = self.paths.get_rhai_path();
|
||||
let compiled_ast = self.ewwii_config.get_owned_compiled_ast();
|
||||
let store = iirhai::updates::handle_state_changes(self.ewwii_config.get_root_node()?, tx);
|
||||
let store =
|
||||
iirhai::updates::handle_state_changes(self.ewwii_config.get_root_node()?, tx);
|
||||
|
||||
glib::MainContext::default().spawn_local(async move {
|
||||
while let Some(var_name) = rx.recv().await {
|
||||
log::debug!("Received update for var: {}", var_name);
|
||||
let vars = store.read().unwrap().clone();
|
||||
|
||||
match generate_new_widgetnode(&vars, &config_path, compiled_ast.as_ref()).await {
|
||||
match generate_new_widgetnode(&vars, &config_path, compiled_ast.as_ref()).await
|
||||
{
|
||||
Ok(new_widget) => {
|
||||
let _ = widget_reg_store.update_widget_tree(new_widget);
|
||||
}
|
||||
@@ -375,8 +400,11 @@ impl<B: DisplayBackend> App<B> {
|
||||
// becomes available again
|
||||
move |auto_reopen| {
|
||||
let (response_sender, _) = daemon_response::create_pair();
|
||||
let command =
|
||||
DaemonCommand::CloseWindows { windows: vec![instance_id.clone()], auto_reopen, sender: response_sender };
|
||||
let command = DaemonCommand::CloseWindows {
|
||||
windows: vec![instance_id.clone()],
|
||||
auto_reopen,
|
||||
sender: response_sender,
|
||||
};
|
||||
if let Err(err) = app_evt_sender.send(command) {
|
||||
log::error!("Error sending close window command: {}", err);
|
||||
}
|
||||
@@ -386,26 +414,28 @@ impl<B: DisplayBackend> App<B> {
|
||||
let closed_by_user = Rc::new(Cell::new(false));
|
||||
|
||||
// handling users close request
|
||||
ewwii_window.delete_event_handler_id = Some(ewwii_window.gtk_window.connect_delete_event({
|
||||
let handler = gtk_close_handler.clone();
|
||||
let closed_by_user = closed_by_user.clone();
|
||||
move |_, _| {
|
||||
handler(false); // -- false: don't reopen window to respect users intent
|
||||
closed_by_user.set(true);
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
}));
|
||||
ewwii_window.delete_event_handler_id =
|
||||
Some(ewwii_window.gtk_window.connect_delete_event({
|
||||
let handler = gtk_close_handler.clone();
|
||||
let closed_by_user = closed_by_user.clone();
|
||||
move |_, _| {
|
||||
handler(false); // -- false: don't reopen window to respect users intent
|
||||
closed_by_user.set(true);
|
||||
glib::Propagation::Proceed
|
||||
}
|
||||
}));
|
||||
|
||||
// handling destory request
|
||||
ewwii_window.destroy_event_handler_id = Some(ewwii_window.gtk_window.connect_destroy({
|
||||
let handler = gtk_close_handler.clone();
|
||||
let closed_by_user = closed_by_user.clone();
|
||||
move |_| {
|
||||
if !closed_by_user.get() {
|
||||
handler(true);
|
||||
ewwii_window.destroy_event_handler_id =
|
||||
Some(ewwii_window.gtk_window.connect_destroy({
|
||||
let handler = gtk_close_handler.clone();
|
||||
let closed_by_user = closed_by_user.clone();
|
||||
move |_| {
|
||||
if !closed_by_user.get() {
|
||||
handler(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}));
|
||||
|
||||
let duration = window_args.duration;
|
||||
if let Some(duration) = duration {
|
||||
@@ -430,7 +460,10 @@ impl<B: DisplayBackend> App<B> {
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(old_abort_send) = self.window_close_timer_abort_senders.insert(instance_id.to_string(), abort_send) {
|
||||
if let Some(old_abort_send) = self
|
||||
.window_close_timer_abort_senders
|
||||
.insert(instance_id.to_string(), abort_send)
|
||||
{
|
||||
_ = old_abort_send.send(());
|
||||
}
|
||||
}
|
||||
@@ -454,8 +487,13 @@ impl<B: DisplayBackend> App<B> {
|
||||
|
||||
self.ewwii_config = config;
|
||||
|
||||
let open_window_ids: Vec<String> =
|
||||
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
|
||||
let open_window_ids: Vec<String> = self
|
||||
.open_windows
|
||||
.keys()
|
||||
.cloned()
|
||||
.chain(self.failed_windows.iter().cloned())
|
||||
.dedup()
|
||||
.collect();
|
||||
for instance_id in &open_window_ids {
|
||||
let window_arguments = self.instance_id_to_args.get(instance_id).with_context(|| {
|
||||
format!("Cannot reopen window, initial parameters were not saved correctly for {instance_id}")
|
||||
@@ -468,7 +506,8 @@ impl<B: DisplayBackend> App<B> {
|
||||
/// Load a given CSS string into the gtk css provider, returning a nicely formatted [`DiagError`] when GTK errors out
|
||||
pub fn load_css(&mut self, file_id: usize, css: &str) -> Result<()> {
|
||||
if let Err(err) = self.css_provider.load_from_data(css.as_bytes()) {
|
||||
static PATTERN: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap());
|
||||
static PATTERN: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new(r"[^:]*:(\d+):(\d+)(.*)$").unwrap());
|
||||
let nice_error_option: Option<_> = (|| {
|
||||
let captures = PATTERN.captures(err.message())?;
|
||||
let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?;
|
||||
@@ -501,8 +540,9 @@ fn initialize_window<B: DisplayBackend>(
|
||||
}
|
||||
_ => (None, 0, 0),
|
||||
};
|
||||
let window = B::initialize_window(window_init, monitor_geometry, x, y)
|
||||
.with_context(|| format!("monitor {} is unavailable", window_init.monitor.clone().unwrap()))?;
|
||||
let window = B::initialize_window(window_init, monitor_geometry, x, y).with_context(|| {
|
||||
format!("monitor {} is unavailable", window_init.monitor.clone().unwrap())
|
||||
})?;
|
||||
|
||||
window.set_title(&format!("Ewwii - {}", window_init.name));
|
||||
window.set_position(gtk::WindowPosition::None);
|
||||
@@ -528,7 +568,9 @@ fn initialize_window<B: DisplayBackend>(
|
||||
if B::IS_X11 {
|
||||
if let Some(geometry) = window_init.geometry {
|
||||
let _ = apply_window_position(geometry, monitor_geometry, &window);
|
||||
if window_init.backend_options.x11.window_type != crate::window::backend_window_options::X11WindowType::Normal {
|
||||
if window_init.backend_options.x11.window_type
|
||||
!= crate::window::backend_window_options::X11WindowType::Normal
|
||||
{
|
||||
let last_pos = Rc::new(RefCell::new(None));
|
||||
window.connect_configure_event({
|
||||
let last_pos = last_pos.clone();
|
||||
@@ -583,7 +625,11 @@ async fn generate_new_widgetnode(
|
||||
|
||||
/// Apply the provided window-positioning rules to the window.
|
||||
#[cfg(feature = "x11")]
|
||||
fn apply_window_position(mut window_geometry: WindowGeometry, monitor_geometry: gdk::Rectangle, window: &Window) -> Result<()> {
|
||||
fn apply_window_position(
|
||||
mut window_geometry: WindowGeometry,
|
||||
monitor_geometry: gdk::Rectangle,
|
||||
window: &Window,
|
||||
) -> Result<()> {
|
||||
let gdk_window = window.window().context("Failed to get gdk window from gtk window")?;
|
||||
window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.size());
|
||||
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
|
||||
@@ -598,8 +644,9 @@ fn apply_window_position(mut window_geometry: WindowGeometry, monitor_geometry:
|
||||
}
|
||||
|
||||
fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) {
|
||||
let visual = gtk::prelude::GtkWindowExt::screen(window)
|
||||
.and_then(|screen| screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual()));
|
||||
let visual = gtk::prelude::GtkWindowExt::screen(window).and_then(|screen| {
|
||||
screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual())
|
||||
});
|
||||
window.set_visual(visual.as_ref());
|
||||
}
|
||||
|
||||
@@ -633,7 +680,10 @@ fn get_gdk_monitor(identifier: Option<MonitorIdentifier>) -> Result<Monitor> {
|
||||
fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&str> {
|
||||
unsafe {
|
||||
use glib::translate::ToGlibPtr;
|
||||
let plug_name_pointer = gdk_sys::gdk_screen_get_monitor_plug_name(display.default_screen().to_glib_none().0, monitor_num);
|
||||
let plug_name_pointer = gdk_sys::gdk_screen_get_monitor_plug_name(
|
||||
display.default_screen().to_glib_none().0,
|
||||
monitor_num,
|
||||
);
|
||||
use std::ffi::CStr;
|
||||
CStr::from_ptr(plug_name_pointer).to_str().ok()
|
||||
}
|
||||
@@ -641,7 +691,10 @@ fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&st
|
||||
|
||||
/// Returns the [Monitor][gdk::Monitor] structure corresponding to the identifer.
|
||||
/// Outside of x11, only [MonitorIdentifier::Numeric] is supported
|
||||
pub fn get_monitor_from_display(display: &gdk::Display, identifier: &MonitorIdentifier) -> Option<gdk::Monitor> {
|
||||
pub fn get_monitor_from_display(
|
||||
display: &gdk::Display,
|
||||
identifier: &MonitorIdentifier,
|
||||
) -> Option<gdk::Monitor> {
|
||||
match identifier {
|
||||
MonitorIdentifier::List(list) => {
|
||||
for ident in list {
|
||||
@@ -666,10 +719,18 @@ pub fn get_monitor_from_display(display: &gdk::Display, identifier: &MonitorIden
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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());
|
||||
let x = screen_rect.x() + offset_x + geometry.anchor_point.x.alignment_to_coordinate(width, screen_rect.width());
|
||||
let y = screen_rect.y() + offset_y + geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height());
|
||||
let x = screen_rect.x()
|
||||
+ offset_x
|
||||
+ geometry.anchor_point.x.alignment_to_coordinate(width, screen_rect.width());
|
||||
let y = screen_rect.y()
|
||||
+ offset_y
|
||||
+ geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height());
|
||||
gdk::Rectangle::new(x, y, width, height)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ use anyhow::{Context, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
pub static APPLICATION_EXIT_SENDER: Lazy<broadcast::Sender<()>> = Lazy::new(|| broadcast::channel(2).0);
|
||||
pub static APPLICATION_EXIT_SENDER: Lazy<broadcast::Sender<()>> =
|
||||
Lazy::new(|| broadcast::channel(2).0);
|
||||
|
||||
/// Notify all listening tasks of the termination of the eww application process.
|
||||
pub fn send_exit() -> Result<()> {
|
||||
|
||||
@@ -26,18 +26,25 @@ pub fn handle_client_only_action(paths: &EwwPaths, action: ActionClientOnly) ->
|
||||
|
||||
/// Connect to the daemon and send the given request.
|
||||
/// Returns the response from the daemon, or None if the daemon did not provide any useful response. An Ok(None) response does _not_ indicate failure.
|
||||
pub fn do_server_call(stream: &mut UnixStream, action: &opts::ActionWithServer) -> Result<Option<DaemonResponse>> {
|
||||
pub fn do_server_call(
|
||||
stream: &mut UnixStream,
|
||||
action: &opts::ActionWithServer,
|
||||
) -> Result<Option<DaemonResponse>> {
|
||||
log::debug!("Forwarding options to server");
|
||||
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
||||
|
||||
let message_bytes = bincode::serialize(&action)?;
|
||||
|
||||
stream.write(&(message_bytes.len() as u32).to_be_bytes()).context("Failed to send command size header to IPC stream")?;
|
||||
stream
|
||||
.write(&(message_bytes.len() as u32).to_be_bytes())
|
||||
.context("Failed to send command size header to IPC stream")?;
|
||||
|
||||
stream.write_all(&message_bytes).context("Failed to write command to IPC stream")?;
|
||||
|
||||
let mut buf = Vec::new();
|
||||
stream.set_read_timeout(Some(std::time::Duration::from_millis(100))).context("Failed to set read timeout")?;
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::from_millis(100)))
|
||||
.context("Failed to set read timeout")?;
|
||||
stream.read_to_end(&mut buf).context("Error reading response from server")?;
|
||||
|
||||
Ok(if buf.is_empty() {
|
||||
|
||||
@@ -56,7 +56,11 @@ impl EwwiiConfig {
|
||||
// get the iirhai widget tree
|
||||
let compiled_ast = config_parser.compile_code(&rhai_code)?;
|
||||
let poll_listen_scope = ParseConfig::initial_poll_listen_scope(&rhai_code)?;
|
||||
let config_tree = config_parser.eval_code_with(&rhai_code, Some(poll_listen_scope), Some(&compiled_ast))?;
|
||||
let config_tree = config_parser.eval_code_with(
|
||||
&rhai_code,
|
||||
Some(poll_listen_scope),
|
||||
Some(&compiled_ast),
|
||||
)?;
|
||||
|
||||
let mut window_definitions = HashMap::new();
|
||||
|
||||
@@ -76,7 +80,11 @@ impl EwwiiConfig {
|
||||
bail!("Expected root node to be `Enter`, but got something else.");
|
||||
}
|
||||
|
||||
Ok(EwwiiConfig { windows: window_definitions, root_node: Some(config_tree), compiled_ast: Some(compiled_ast) })
|
||||
Ok(EwwiiConfig {
|
||||
windows: window_definitions,
|
||||
root_node: Some(config_tree),
|
||||
compiled_ast: Some(compiled_ast),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_windows(&self) -> &HashMap<String, WindowDefinition> {
|
||||
|
||||
@@ -11,7 +11,9 @@ pub fn parse_scss_from_config(path: &Path) -> anyhow::Result<(usize, String)> {
|
||||
let css_file = path.join("ewwii.css");
|
||||
let scss_file = path.join("ewwii.scss");
|
||||
if css_file.exists() && scss_file.exists() {
|
||||
return Err(anyhow!("Encountered both an SCSS and CSS file. Only one of these may exist at a time"));
|
||||
return Err(anyhow!(
|
||||
"Encountered both an SCSS and CSS file. Only one of these may exist at a time"
|
||||
));
|
||||
}
|
||||
|
||||
let (s_css_path, css) = if css_file.exists() {
|
||||
@@ -20,11 +22,12 @@ pub fn parse_scss_from_config(path: &Path) -> anyhow::Result<(usize, String)> {
|
||||
let css = replace_env_var_references(css_file_content);
|
||||
(css_file, css)
|
||||
} else {
|
||||
let scss_file_content =
|
||||
std::fs::read_to_string(&scss_file).with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?;
|
||||
let scss_file_content = std::fs::read_to_string(&scss_file)
|
||||
.with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?;
|
||||
let file_content = replace_env_var_references(scss_file_content);
|
||||
let grass_config = grass::Options::default().load_path(path);
|
||||
let css = grass::from_string(file_content, &grass_config).map_err(|err| anyhow!("SCSS parsing error: {}", err))?;
|
||||
let css = grass::from_string(file_content, &grass_config)
|
||||
.map_err(|err| anyhow!("SCSS parsing error: {}", err))?;
|
||||
(scss_file, css)
|
||||
};
|
||||
|
||||
|
||||
@@ -28,15 +28,22 @@ pub fn create_pair() -> (DaemonResponseSender, mpsc::UnboundedReceiver<DaemonRes
|
||||
|
||||
impl DaemonResponseSender {
|
||||
pub fn send_success(&self, s: String) -> Result<()> {
|
||||
self.0.send(DaemonResponse::Success(s)).context("Failed to send success response from application thread")
|
||||
self.0
|
||||
.send(DaemonResponse::Success(s))
|
||||
.context("Failed to send success response from application thread")
|
||||
}
|
||||
|
||||
pub fn send_failure(&self, s: String) -> Result<()> {
|
||||
self.0.send(DaemonResponse::Failure(s)).context("Failed to send failure response from application thread")
|
||||
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<()> {
|
||||
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())
|
||||
|
||||
@@ -12,7 +12,12 @@ pub trait DisplayBackend: Send + Sync + 'static {
|
||||
const IS_X11: bool;
|
||||
const IS_WAYLAND: bool;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window>;
|
||||
fn initialize_window(
|
||||
window_init: &WindowInitiator,
|
||||
monitor: gdk::Rectangle,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window>;
|
||||
}
|
||||
|
||||
pub struct NoBackend;
|
||||
@@ -21,7 +26,12 @@ impl DisplayBackend for NoBackend {
|
||||
const IS_X11: bool = false;
|
||||
const IS_WAYLAND: bool = false;
|
||||
|
||||
fn initialize_window(_window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
fn initialize_window(
|
||||
_window_init: &WindowInitiator,
|
||||
_monitor: gdk::Rectangle,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
Some(Window::new(gtk::WindowType::Toplevel, x, y))
|
||||
}
|
||||
}
|
||||
@@ -43,7 +53,12 @@ mod platform_wayland {
|
||||
const IS_X11: bool = false;
|
||||
const IS_WAYLAND: bool = true;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
fn initialize_window(
|
||||
window_init: &WindowInitiator,
|
||||
monitor: gdk::Rectangle,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
let window = Window::new(gtk::WindowType::Toplevel, x, y);
|
||||
// Initialising a layer shell surface
|
||||
window.init_layer_shell();
|
||||
@@ -155,9 +170,17 @@ mod platform_x11 {
|
||||
const IS_X11: bool = true;
|
||||
const IS_WAYLAND: bool = false;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
let window_type =
|
||||
if window_init.backend_options.x11.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel };
|
||||
fn initialize_window(
|
||||
window_init: &WindowInitiator,
|
||||
_monitor: gdk::Rectangle,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> Option<Window> {
|
||||
let window_type = if window_init.backend_options.x11.wm_ignore {
|
||||
gtk::WindowType::Popup
|
||||
} else {
|
||||
gtk::WindowType::Toplevel
|
||||
};
|
||||
let window = Window::new(window_type, x, y);
|
||||
window.set_resizable(window_init.resizable);
|
||||
window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
|
||||
@@ -171,7 +194,11 @@ mod platform_x11 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_xprops(window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
pub fn set_xprops(
|
||||
window: &Window,
|
||||
monitor: Monitor,
|
||||
window_init: &WindowInitiator,
|
||||
) -> Result<()> {
|
||||
let backend = X11BackendConnection::new()?;
|
||||
backend.set_xprops_for(window, monitor, window_init)?;
|
||||
Ok(())
|
||||
@@ -191,12 +218,19 @@ mod platform_x11 {
|
||||
Ok(X11BackendConnection { conn, root_window: screen.root, atoms })
|
||||
}
|
||||
|
||||
fn set_xprops_for(&self, window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
fn set_xprops_for(
|
||||
&self,
|
||||
window: &Window,
|
||||
monitor: Monitor,
|
||||
window_init: &WindowInitiator,
|
||||
) -> Result<()> {
|
||||
let monitor_rect = monitor.geometry();
|
||||
let scale_factor = monitor.scale_factor() as u32;
|
||||
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
|
||||
let win_id =
|
||||
gdk_window.downcast_ref::<gdkx11::X11Window>().context("Failed to get x11 window for gtk window")?.xid() as u32;
|
||||
let win_id = gdk_window
|
||||
.downcast_ref::<gdkx11::X11Window>()
|
||||
.context("Failed to get x11 window for gtk window")?
|
||||
.xid() as u32;
|
||||
let strut_def = window_init.backend_options.x11.struts;
|
||||
let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;
|
||||
|
||||
@@ -206,8 +240,12 @@ mod platform_x11 {
|
||||
let mon_end_y = scale_factor * (monitor_rect.y() + monitor_rect.height()) as u32 - 1u32;
|
||||
|
||||
let dist = match strut_def.side {
|
||||
Side::Left | Side::Right => strut_def.distance.pixels_relative_to(monitor_rect.width()) as u32,
|
||||
Side::Top | Side::Bottom => strut_def.distance.pixels_relative_to(monitor_rect.height()) as u32,
|
||||
Side::Left | Side::Right => {
|
||||
strut_def.distance.pixels_relative_to(monitor_rect.width()) as u32
|
||||
}
|
||||
Side::Top | Side::Bottom => {
|
||||
strut_def.distance.pixels_relative_to(monitor_rect.height()) as u32
|
||||
}
|
||||
};
|
||||
|
||||
// don't question it,.....
|
||||
|
||||
@@ -18,7 +18,11 @@ pub struct ConversionError {
|
||||
pub struct DurationParseError;
|
||||
|
||||
impl ConversionError {
|
||||
pub fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self {
|
||||
pub fn new(
|
||||
value: DynVal,
|
||||
target_type: &'static str,
|
||||
source: impl std::error::Error + 'static + Sync + Send,
|
||||
) -> Self {
|
||||
ConversionError { value, target_type, source: Some(Box::new(source)) }
|
||||
}
|
||||
}
|
||||
@@ -113,7 +117,11 @@ impl TryFrom<serde_json::Value> for DynVal {
|
||||
|
||||
impl From<Vec<DynVal>> for DynVal {
|
||||
fn from(v: Vec<DynVal>) -> Self {
|
||||
let span = if let (Some(first), Some(last)) = (v.first(), v.last()) { first.span().to(last.span()) } else { Span::DUMMY };
|
||||
let span = if let (Some(first), Some(last)) = (v.first(), v.last()) {
|
||||
first.span().to(last.span())
|
||||
} else {
|
||||
Span::DUMMY
|
||||
};
|
||||
let elements = v.into_iter().map(|x| x.as_string().unwrap()).collect::<Vec<_>>();
|
||||
DynVal(serde_json::to_string(&elements).unwrap(), span)
|
||||
}
|
||||
@@ -174,10 +182,15 @@ impl DynVal {
|
||||
let s = &self.0;
|
||||
if s.ends_with("ms") {
|
||||
Ok(Duration::from_millis(
|
||||
s.trim_end_matches("ms").parse().map_err(|e| ConversionError::new(self.clone(), "integer", e))?,
|
||||
s.trim_end_matches("ms")
|
||||
.parse()
|
||||
.map_err(|e| ConversionError::new(self.clone(), "integer", e))?,
|
||||
))
|
||||
} else if s.ends_with('s') {
|
||||
let secs = s.trim_end_matches('s').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||
let secs = s
|
||||
.trim_end_matches('s')
|
||||
.parse::<f64>()
|
||||
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||
Ok(Duration::from_millis(f64::floor(secs * 1000f64) as u64))
|
||||
} else if s.ends_with('m') || s.ends_with("min") {
|
||||
let minutes = s
|
||||
@@ -187,12 +200,19 @@ impl DynVal {
|
||||
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||
Ok(Duration::from_secs(f64::floor(minutes * 60f64) as u64))
|
||||
} else if s.ends_with('h') {
|
||||
let hours = s.trim_end_matches('h').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||
let hours = s
|
||||
.trim_end_matches('h')
|
||||
.parse::<f64>()
|
||||
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||
Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64))
|
||||
} else if let Ok(millis) = s.parse() {
|
||||
Ok(Duration::from_millis(millis))
|
||||
} else {
|
||||
Err(ConversionError { value: self.clone(), target_type: "duration", source: Some(Box::new(DurationParseError)) })
|
||||
Err(ConversionError {
|
||||
value: self.clone(),
|
||||
target_type: "duration",
|
||||
source: Some(Box::new(DurationParseError)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ use codespan_reporting::{
|
||||
use ewwii_shared_util::Span;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub static FILE_DATABASE: Lazy<Arc<RwLock<FileDatabase>>> = Lazy::new(|| Arc::new(RwLock::new(FileDatabase::new())));
|
||||
pub static FILE_DATABASE: Lazy<Arc<RwLock<FileDatabase>>> =
|
||||
Lazy::new(|| Arc::new(RwLock::new(FileDatabase::new())));
|
||||
|
||||
// pub fn clear_files() {
|
||||
// *FILE_DATABASE.write().unwrap() = FileDatabase::new();
|
||||
@@ -30,7 +31,9 @@ pub fn print_error(err: anyhow::Error) {
|
||||
}
|
||||
|
||||
pub fn format_error(err: &anyhow::Error) -> String {
|
||||
anyhow_err_to_diagnostic(err).and_then(|diag| stringify_diagnostic(diag).ok()).unwrap_or_else(|| format!("{:?}", err))
|
||||
anyhow_err_to_diagnostic(err)
|
||||
.and_then(|diag| stringify_diagnostic(diag).ok())
|
||||
.unwrap_or_else(|| format!("{:?}", err))
|
||||
}
|
||||
|
||||
// * OLD
|
||||
@@ -63,8 +66,12 @@ pub fn anyhow_err_to_diagnostic(err: &anyhow::Error) -> Option<Diagnostic<usize>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stringify_diagnostic(mut diagnostic: codespan_reporting::diagnostic::Diagnostic<usize>) -> anyhow::Result<String> {
|
||||
diagnostic.labels.retain(|label| !Span(label.range.start, label.range.end, label.file_id).is_dummy());
|
||||
pub fn stringify_diagnostic(
|
||||
mut diagnostic: codespan_reporting::diagnostic::Diagnostic<usize>,
|
||||
) -> anyhow::Result<String> {
|
||||
diagnostic
|
||||
.labels
|
||||
.retain(|label| !Span(label.range.start, label.range.end, label.file_id).is_dummy());
|
||||
|
||||
let mut config = term::Config::default();
|
||||
let mut chars = Chars::box_drawing();
|
||||
|
||||
@@ -40,7 +40,12 @@ impl FileDatabase {
|
||||
|
||||
pub fn insert_string(&mut self, name: String, content: String) -> Result<usize, DiagError> {
|
||||
let line_starts = codespan_reporting::files::line_starts(&content).collect();
|
||||
let code_file = CodeFile { name, line_starts, source_len_bytes: content.len(), source: CodeSource::Literal(content) };
|
||||
let code_file = CodeFile {
|
||||
name,
|
||||
line_starts,
|
||||
source_len_bytes: content.len(),
|
||||
source: CodeSource::Literal(content),
|
||||
};
|
||||
let file_id = self.insert_code_file(code_file);
|
||||
Ok(file_id)
|
||||
}
|
||||
@@ -55,12 +60,23 @@ impl<'a> Files<'a> for FileDatabase {
|
||||
Ok(&self.get_file(id)?.name)
|
||||
}
|
||||
|
||||
fn source(&'a self, id: Self::FileId) -> Result<Self::Source, codespan_reporting::files::Error> {
|
||||
fn source(
|
||||
&'a self,
|
||||
id: Self::FileId,
|
||||
) -> Result<Self::Source, codespan_reporting::files::Error> {
|
||||
self.get_file(id)?.source.read_content().map_err(codespan_reporting::files::Error::Io)
|
||||
}
|
||||
|
||||
fn line_index(&self, id: Self::FileId, byte_index: usize) -> Result<usize, codespan_reporting::files::Error> {
|
||||
Ok(self.get_file(id)?.line_starts.binary_search(&byte_index).unwrap_or_else(|next_line| next_line - 1))
|
||||
fn line_index(
|
||||
&self,
|
||||
id: Self::FileId,
|
||||
byte_index: usize,
|
||||
) -> Result<usize, codespan_reporting::files::Error> {
|
||||
Ok(self
|
||||
.get_file(id)?
|
||||
.line_starts
|
||||
.binary_search(&byte_index)
|
||||
.unwrap_or_else(|next_line| next_line - 1))
|
||||
}
|
||||
|
||||
fn line_range(
|
||||
@@ -90,11 +106,16 @@ impl CodeFile {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match line_index.cmp(&self.line_starts.len()) {
|
||||
Ordering::Less => Ok(self.line_starts.get(line_index).cloned().expect("failed despite previous check")),
|
||||
Ordering::Less => Ok(self
|
||||
.line_starts
|
||||
.get(line_index)
|
||||
.cloned()
|
||||
.expect("failed despite previous check")),
|
||||
Ordering::Equal => Ok(self.source_len_bytes),
|
||||
Ordering::Greater => {
|
||||
Err(codespan_reporting::files::Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1 })
|
||||
}
|
||||
Ordering::Greater => Err(codespan_reporting::files::Error::LineTooLarge {
|
||||
given: line_index,
|
||||
max: self.line_starts.len() - 1,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,10 @@ pub async fn run_ewwii_server<P: AsRef<std::path::Path>>(
|
||||
}
|
||||
|
||||
/// Handle a single IPC connection from start to end.
|
||||
async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: UnboundedSender<app::DaemonCommand>) -> Result<()> {
|
||||
async fn handle_connection(
|
||||
mut stream: tokio::net::UnixStream,
|
||||
evt_send: UnboundedSender<app::DaemonCommand>,
|
||||
) -> Result<()> {
|
||||
let (mut stream_read, mut stream_write) = stream.split();
|
||||
|
||||
let action: opts::ActionWithServer = read_ewwii_action_from_stream(&mut stream_read).await?;
|
||||
@@ -45,7 +48,9 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound
|
||||
|
||||
if let Some(mut response_recv) = maybe_response_recv {
|
||||
log::debug!("Waiting for response for IPC client");
|
||||
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await {
|
||||
if let Ok(Some(response)) =
|
||||
tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await
|
||||
{
|
||||
let response = bincode::serialize(&response)?;
|
||||
let result = &stream_write.write_all(&response).await;
|
||||
crate::print_result_err!("sending text response to ipc client", &result);
|
||||
@@ -57,13 +62,21 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound
|
||||
|
||||
/// Read a single message from a unix stream, and parses it into a `ActionWithServer`
|
||||
/// The format here requires the first 4 bytes to be the size of the rest of the message (in big-endian), followed by the rest of the message.
|
||||
async fn read_ewwii_action_from_stream(stream_read: &'_ mut tokio::net::unix::ReadHalf<'_>) -> Result<opts::ActionWithServer> {
|
||||
async fn read_ewwii_action_from_stream(
|
||||
stream_read: &'_ mut tokio::net::unix::ReadHalf<'_>,
|
||||
) -> Result<opts::ActionWithServer> {
|
||||
let mut message_byte_length = [0u8; 4];
|
||||
stream_read.read_exact(&mut message_byte_length).await.context("Failed to read message size header in IPC message")?;
|
||||
stream_read
|
||||
.read_exact(&mut message_byte_length)
|
||||
.await
|
||||
.context("Failed to read message size header in IPC message")?;
|
||||
let message_byte_length = u32::from_be_bytes(message_byte_length);
|
||||
let mut raw_message = Vec::<u8>::with_capacity(message_byte_length as usize);
|
||||
while raw_message.len() < message_byte_length as usize {
|
||||
stream_read.read_buf(&mut raw_message).await.context("Failed to read actual IPC message")?;
|
||||
stream_read
|
||||
.read_buf(&mut raw_message)
|
||||
.await
|
||||
.context("Failed to read actual IPC message")?;
|
||||
}
|
||||
|
||||
bincode::deserialize(&raw_message).context("Failed to parse client message")
|
||||
|
||||
@@ -83,7 +83,12 @@ fn main() {
|
||||
}
|
||||
|
||||
if let opts::Action::ShellCompletions { shell } = opts.action {
|
||||
clap_complete::generate(shell, &mut opts::RawOpt::command(), "ewwii", &mut std::io::stdout());
|
||||
clap_complete::generate(
|
||||
shell,
|
||||
&mut opts::RawOpt::command(),
|
||||
"ewwii",
|
||||
&mut std::io::stdout(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,10 +97,18 @@ fn main() {
|
||||
let use_wayland = opts.force_wayland || detected_wayland;
|
||||
#[cfg(all(feature = "wayland", feature = "x11"))]
|
||||
let result = if use_wayland {
|
||||
log::debug!("Running on wayland. force_wayland={}, detected_wayland={}", opts.force_wayland, detected_wayland);
|
||||
log::debug!(
|
||||
"Running on wayland. force_wayland={}, detected_wayland={}",
|
||||
opts.force_wayland,
|
||||
detected_wayland
|
||||
);
|
||||
run::<display_backend::WaylandBackend>(opts, eww_binary_name)
|
||||
} else {
|
||||
log::debug!("Running on X11. force_wayland={}, detected_wayland={}", opts.force_wayland, detected_wayland);
|
||||
log::debug!(
|
||||
"Running on X11. force_wayland={}, detected_wayland={}",
|
||||
opts.force_wayland,
|
||||
detected_wayland
|
||||
);
|
||||
run::<display_backend::X11Backend>(opts, eww_binary_name)
|
||||
};
|
||||
|
||||
@@ -123,7 +136,8 @@ fn main() {
|
||||
fn detect_wayland() -> bool {
|
||||
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
|
||||
let wayland_display = std::env::var("WAYLAND_DISPLAY").unwrap_or_default();
|
||||
session_type.contains("wayland") || (!wayland_display.is_empty() && !session_type.contains("x11"))
|
||||
session_type.contains("wayland")
|
||||
|| (!wayland_display.is_empty() && !session_type.contains("x11"))
|
||||
}
|
||||
|
||||
fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()> {
|
||||
@@ -165,14 +179,19 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
|
||||
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||
|
||||
if !opts.show_logs {
|
||||
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name);
|
||||
println!(
|
||||
"Run `{} logs` to see any errors while editing your configuration.",
|
||||
eww_binary_name
|
||||
);
|
||||
}
|
||||
let fork_result = server::initialize_server::<B>(paths.clone(), None, !opts.no_daemonize)?;
|
||||
let fork_result =
|
||||
server::initialize_server::<B>(paths.clone(), None, !opts.no_daemonize)?;
|
||||
opts.no_daemonize || fork_result == ForkResult::Parent
|
||||
}
|
||||
|
||||
opts::Action::WithServer(ActionWithServer::KillServer) => {
|
||||
if let Some(response) = handle_server_command(&paths, &ActionWithServer::KillServer, 1)? {
|
||||
if let Some(response) = handle_server_command(&paths, &ActionWithServer::KillServer, 1)?
|
||||
{
|
||||
handle_daemon_response(response);
|
||||
}
|
||||
false
|
||||
@@ -191,15 +210,22 @@ fn run<B: DisplayBackend>(opts: opts::Opt, eww_binary_name: String) -> Result<()
|
||||
Err(err) if action.can_start_daemon() && !opts.no_daemonize => {
|
||||
// connecting to the daemon failed. Thus, start the daemon here!
|
||||
log::warn!("Failed to connect to daemon: {}", err);
|
||||
log::info!("Initializing ewwii server. ({})", paths.get_ipc_socket_file().display());
|
||||
log::info!(
|
||||
"Initializing ewwii server. ({})",
|
||||
paths.get_ipc_socket_file().display()
|
||||
);
|
||||
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||
if !opts.show_logs {
|
||||
println!("Run `{} logs` to see any errors while editing your configuration.", eww_binary_name);
|
||||
println!(
|
||||
"Run `{} logs` to see any errors while editing your configuration.",
|
||||
eww_binary_name
|
||||
);
|
||||
}
|
||||
|
||||
let (command, response_recv) = action.into_daemon_command();
|
||||
// start the daemon and give it the command
|
||||
let fork_result = server::initialize_server::<B>(paths.clone(), Some(command), true)?;
|
||||
let fork_result =
|
||||
server::initialize_server::<B>(paths.clone(), Some(command), true)?;
|
||||
let is_parent = fork_result == ForkResult::Parent;
|
||||
if let (Some(recv), true) = (response_recv, is_parent) {
|
||||
listen_for_daemon_response(recv);
|
||||
@@ -224,16 +250,26 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) {
|
||||
.build()
|
||||
.expect("Failed to initialize tokio runtime");
|
||||
rt.block_on(async {
|
||||
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), recv.recv()).await {
|
||||
if let Ok(Some(response)) =
|
||||
tokio::time::timeout(Duration::from_millis(100), recv.recv()).await
|
||||
{
|
||||
println!("{}", response);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// attempt to send a command to the daemon and send it the given action repeatedly.
|
||||
fn handle_server_command(paths: &EwwPaths, action: &ActionWithServer, connect_attempts: usize) -> Result<Option<DaemonResponse>> {
|
||||
log::debug!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display());
|
||||
let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?;
|
||||
fn handle_server_command(
|
||||
paths: &EwwPaths,
|
||||
action: &ActionWithServer,
|
||||
connect_attempts: usize,
|
||||
) -> Result<Option<DaemonResponse>> {
|
||||
log::debug!(
|
||||
"Trying to find server process at socket {}",
|
||||
paths.get_ipc_socket_file().display()
|
||||
);
|
||||
let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts)
|
||||
.context("Failed to connect to daemon")?;
|
||||
log::debug!("Connected to Ewwii server ({}).", &paths.get_ipc_socket_file().display());
|
||||
client::do_server_call(&mut stream, action).context("Error while forwarding command to server")
|
||||
}
|
||||
@@ -262,8 +298,8 @@ fn attempt_connect(socket_path: impl AsRef<Path>, attempts: usize) -> Option<net
|
||||
|
||||
/// Check if a eww server is currently running by trying to send a ping message to it.
|
||||
fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
|
||||
let response = net::UnixStream::connect(socket_path)
|
||||
.ok()
|
||||
.and_then(|mut stream| client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok());
|
||||
let response = net::UnixStream::connect(socket_path).ok().and_then(|mut stream| {
|
||||
client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()
|
||||
});
|
||||
response.is_some()
|
||||
}
|
||||
|
||||
@@ -197,8 +197,17 @@ impl Opt {
|
||||
|
||||
impl From<RawOpt> for Opt {
|
||||
fn from(other: RawOpt) -> Self {
|
||||
let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } = other;
|
||||
Opt { log_debug, force_wayland, show_logs, restart, config_path: config, action, no_daemonize }
|
||||
let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } =
|
||||
other;
|
||||
Opt {
|
||||
log_debug,
|
||||
force_wayland,
|
||||
show_logs,
|
||||
restart,
|
||||
config_path: config,
|
||||
action,
|
||||
no_daemonize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +243,9 @@ impl ActionWithServer {
|
||||
matches!(self, ActionWithServer::OpenWindow { .. })
|
||||
}
|
||||
|
||||
pub fn into_daemon_command(self) -> (app::DaemonCommand, Option<daemon_response::DaemonResponseReceiver>) {
|
||||
pub fn into_daemon_command(
|
||||
self,
|
||||
) -> (app::DaemonCommand, Option<daemon_response::DaemonResponseReceiver>) {
|
||||
let command = match self {
|
||||
ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector,
|
||||
|
||||
@@ -248,7 +259,16 @@ impl ActionWithServer {
|
||||
// ActionWithServer::OpenMany { windows, should_toggle } => {
|
||||
// return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
|
||||
// }
|
||||
ActionWithServer::OpenWindow { window_name, id, pos, size, screen, anchor, should_toggle, duration } => {
|
||||
ActionWithServer::OpenWindow {
|
||||
window_name,
|
||||
id,
|
||||
pos,
|
||||
size,
|
||||
screen,
|
||||
anchor,
|
||||
should_toggle,
|
||||
duration,
|
||||
} => {
|
||||
return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
|
||||
window_name,
|
||||
instance_id: id,
|
||||
@@ -263,18 +283,32 @@ impl ActionWithServer {
|
||||
});
|
||||
}
|
||||
ActionWithServer::CloseWindows { windows } => {
|
||||
return with_response_channel(|sender| app::DaemonCommand::CloseWindows { windows, auto_reopen: false, sender });
|
||||
return with_response_channel(|sender| app::DaemonCommand::CloseWindows {
|
||||
windows,
|
||||
auto_reopen: false,
|
||||
sender,
|
||||
});
|
||||
}
|
||||
ActionWithServer::Reload => {
|
||||
return with_response_channel(app::DaemonCommand::ReloadConfigAndCss)
|
||||
}
|
||||
ActionWithServer::ListWindows => {
|
||||
return with_response_channel(app::DaemonCommand::ListWindows)
|
||||
}
|
||||
ActionWithServer::ListActiveWindows => {
|
||||
return with_response_channel(app::DaemonCommand::ListActiveWindows)
|
||||
}
|
||||
ActionWithServer::ShowDebug => {
|
||||
return with_response_channel(app::DaemonCommand::PrintDebug)
|
||||
}
|
||||
ActionWithServer::Reload => return with_response_channel(app::DaemonCommand::ReloadConfigAndCss),
|
||||
ActionWithServer::ListWindows => return with_response_channel(app::DaemonCommand::ListWindows),
|
||||
ActionWithServer::ListActiveWindows => return with_response_channel(app::DaemonCommand::ListActiveWindows),
|
||||
ActionWithServer::ShowDebug => return with_response_channel(app::DaemonCommand::PrintDebug),
|
||||
};
|
||||
(command, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn with_response_channel<O, F>(f: F) -> (O, Option<tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>>)
|
||||
fn with_response_channel<O, F>(
|
||||
f: F,
|
||||
) -> (O, Option<tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>>)
|
||||
where
|
||||
F: FnOnce(DaemonResponseSender) -> O,
|
||||
{
|
||||
|
||||
@@ -55,7 +55,12 @@ impl EwwPaths {
|
||||
std::fs::create_dir_all(&log_dir)?;
|
||||
}
|
||||
|
||||
Ok(EwwPaths { config_dir, log_file: log_dir.join(format!("eww_{}.log", daemon_id)), log_dir, ipc_socket_file })
|
||||
Ok(EwwPaths {
|
||||
config_dir,
|
||||
log_file: log_dir.join(format!("eww_{}.log", daemon_id)),
|
||||
log_dir,
|
||||
ipc_socket_file,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn default() -> Result<Self> {
|
||||
|
||||
@@ -25,8 +25,9 @@ pub fn initialize_server<B: DisplayBackend>(
|
||||
) -> Result<ForkResult> {
|
||||
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
std::env::set_current_dir(paths.get_config_dir())
|
||||
.with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?;
|
||||
std::env::set_current_dir(paths.get_config_dir()).with_context(|| {
|
||||
format!("Failed to change working directory to {}", paths.get_config_dir().display())
|
||||
})?;
|
||||
|
||||
log::info!("Loading paths: {}", &paths);
|
||||
|
||||
@@ -63,13 +64,16 @@ pub fn initialize_server<B: DisplayBackend>(
|
||||
"#
|
||||
);
|
||||
|
||||
simple_signal::set_handler(&[simple_signal::Signal::Int, simple_signal::Signal::Term], move |_| {
|
||||
log::info!("Shutting down ewwii daemon...");
|
||||
if let Err(e) = crate::application_lifecycle::send_exit() {
|
||||
log::error!("Failed to send application shutdown event to workers: {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
simple_signal::set_handler(
|
||||
&[simple_signal::Signal::Int, simple_signal::Signal::Term],
|
||||
move |_| {
|
||||
log::info!("Shutting down ewwii daemon...");
|
||||
if let Err(e) = crate::application_lifecycle::send_exit() {
|
||||
log::error!("Failed to send application shutdown event to workers: {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if B::IS_WAYLAND {
|
||||
std::env::set_var("GDK_BACKEND", "wayland")
|
||||
@@ -89,7 +93,11 @@ pub fn initialize_server<B: DisplayBackend>(
|
||||
};
|
||||
|
||||
if let Some(screen) = gtk::gdk::Screen::default() {
|
||||
gtk::StyleContext::add_provider_for_screen(&screen, &app.css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
gtk::StyleContext::add_provider_for_screen(
|
||||
&screen,
|
||||
&app.css_provider,
|
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok((file_id, css)) = config::scss::parse_scss_from_config(app.paths.get_config_dir()) {
|
||||
@@ -156,7 +164,10 @@ fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>) -> tokio::runtime::Handle {
|
||||
fn init_async_part(
|
||||
paths: EwwPaths,
|
||||
ui_send: UnboundedSender<app::DaemonCommand>,
|
||||
) -> tokio::runtime::Handle {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.thread_name("main-async-runtime")
|
||||
.enable_all()
|
||||
@@ -176,7 +187,9 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
|
||||
|
||||
let ipc_server_join_handle = {
|
||||
let ui_send = ui_send.clone();
|
||||
tokio::spawn(async move { ipc_server::run_ewwii_server(ui_send, paths.get_ipc_socket_file()).await })
|
||||
tokio::spawn(async move {
|
||||
ipc_server::run_ewwii_server(ui_send, paths.get_ipc_socket_file()).await
|
||||
})
|
||||
};
|
||||
|
||||
let forward_exit_to_app_handle = {
|
||||
@@ -190,7 +203,11 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
|
||||
})
|
||||
};
|
||||
|
||||
let result = tokio::try_join!(filewatch_join_handle, ipc_server_join_handle, forward_exit_to_app_handle);
|
||||
let result = tokio::try_join!(
|
||||
filewatch_join_handle,
|
||||
ipc_server_join_handle,
|
||||
forward_exit_to_app_handle
|
||||
);
|
||||
|
||||
if let Err(e) = result {
|
||||
log::error!("Ewwii exiting with error: {:?}", e);
|
||||
@@ -203,25 +220,29 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
|
||||
}
|
||||
|
||||
/// Watch configuration files for changes, sending reload events to the eww app when the files change.
|
||||
async fn run_filewatch<P: AsRef<Path>>(config_dir: P, evt_send: UnboundedSender<app::DaemonCommand>) -> Result<()> {
|
||||
async fn run_filewatch<P: AsRef<Path>>(
|
||||
config_dir: P,
|
||||
evt_send: UnboundedSender<app::DaemonCommand>,
|
||||
) -> Result<()> {
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
|
||||
Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => {
|
||||
let relevant_files_changed = paths.iter().any(|path| {
|
||||
let ext = path.extension().unwrap_or_default();
|
||||
ext == "rhai" || ext == "scss" || ext == "css"
|
||||
});
|
||||
if relevant_files_changed {
|
||||
if let Err(err) = tx.send(()) {
|
||||
log::warn!("Error forwarding file update event: {:?}", err);
|
||||
let mut watcher: RecommendedWatcher =
|
||||
notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
|
||||
Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => {
|
||||
let relevant_files_changed = paths.iter().any(|path| {
|
||||
let ext = path.extension().unwrap_or_default();
|
||||
ext == "rhai" || ext == "scss" || ext == "css"
|
||||
});
|
||||
if relevant_files_changed {
|
||||
if let Err(err) = tx.send(()) {
|
||||
log::warn!("Error forwarding file update event: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => log::error!("Encountered Error While Watching Files: {}", e),
|
||||
})?;
|
||||
Ok(_) => {}
|
||||
Err(e) => log::error!("Encountered Error While Watching Files: {}", e),
|
||||
})?;
|
||||
watcher.watch(config_dir.as_ref(), RecursiveMode::Recursive)?;
|
||||
|
||||
// make sure to not trigger reloads too much by only accepting one reload every 500ms.
|
||||
@@ -271,11 +292,15 @@ fn do_detach(log_file_path: impl AsRef<Path>) -> Result<ForkResult> {
|
||||
}
|
||||
}
|
||||
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&log_file_path)
|
||||
.unwrap_or_else(|_| panic!("Error opening log file ({}), for writing", log_file_path.as_ref().to_string_lossy()));
|
||||
let file =
|
||||
std::fs::OpenOptions::new().create(true).append(true).open(&log_file_path).unwrap_or_else(
|
||||
|_| {
|
||||
panic!(
|
||||
"Error opening log file ({}), for writing",
|
||||
log_file_path.as_ref().to_string_lossy()
|
||||
)
|
||||
},
|
||||
);
|
||||
let fd = file.as_raw_fd();
|
||||
|
||||
if nix::unistd::isatty(1)? {
|
||||
@@ -297,7 +322,9 @@ fn cleanup_log_dir(log_dir: impl AsRef<Path>) -> Result<()> {
|
||||
let entry = entry.ok()?;
|
||||
let path = entry.path();
|
||||
if let Some(file_name) = path.file_name() {
|
||||
if file_name.to_string_lossy().starts_with("ewwii_") && file_name.to_string_lossy().ends_with(".log") {
|
||||
if file_name.to_string_lossy().starts_with("ewwii_")
|
||||
&& file_name.to_string_lossy().ends_with(".log")
|
||||
{
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -108,7 +108,9 @@ impl<T: AsRef<str>> T {
|
||||
/// reference with an empty string.
|
||||
pub fn replace_env_var_references(input: String) -> String {
|
||||
regex!(r"\$\{([^\s]*)\}")
|
||||
.replace_all(&input, |var_name: ®ex::Captures| std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default())
|
||||
.replace_all(&input, |var_name: ®ex::Captures| {
|
||||
std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default()
|
||||
})
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ pub enum WidgetInput {
|
||||
Window(WindowDefinition),
|
||||
}
|
||||
|
||||
pub fn build_gtk_widget(input: WidgetInput, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> {
|
||||
pub fn build_gtk_widget(
|
||||
input: WidgetInput,
|
||||
widget_reg: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Widget> {
|
||||
let node = match input {
|
||||
WidgetInput::Node(n) => n,
|
||||
WidgetInput::Window(w) => w.root_widget,
|
||||
@@ -22,7 +25,10 @@ pub fn build_gtk_widget(input: WidgetInput, widget_reg: &mut WidgetRegistry) ->
|
||||
}
|
||||
|
||||
// TODO: implement the commented lines
|
||||
fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> {
|
||||
fn build_gtk_widget_from_node(
|
||||
root_node: WidgetNode,
|
||||
widget_reg: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Widget> {
|
||||
/*
|
||||
When a a new widget is added to the build process,
|
||||
make sure to update get_id_to_props_map() found in
|
||||
@@ -32,10 +38,18 @@ fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegi
|
||||
|
||||
let gtk_widget = match root_node {
|
||||
WidgetNode::Box { props, children } => build_gtk_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::CenterBox { props, children } => build_center_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::EventBox { props, children } => build_gtk_event_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::ToolTip { props, children } => build_tooltip(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::CircularProgress { props } => build_circular_progress_bar(props, widget_reg)?.upcast(),
|
||||
WidgetNode::CenterBox { props, children } => {
|
||||
build_center_box(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::EventBox { props, children } => {
|
||||
build_gtk_event_box(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::ToolTip { props, children } => {
|
||||
build_tooltip(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::CircularProgress { props } => {
|
||||
build_circular_progress_bar(props, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::Graph { props } => build_graph(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Slider { props } => build_gtk_scale(props, widget_reg)?.upcast(),
|
||||
@@ -47,14 +61,24 @@ fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegi
|
||||
WidgetNode::Input { props } => build_gtk_input(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Calendar { props } => build_gtk_calendar(props, widget_reg)?.upcast(),
|
||||
WidgetNode::ColorButton { props } => build_gtk_color_button(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Expander { props, children } => build_gtk_expander(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::Expander { props, children } => {
|
||||
build_gtk_expander(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::ColorChooser { props } => build_gtk_color_chooser(props, widget_reg)?.upcast(),
|
||||
WidgetNode::ComboBoxText { props } => build_gtk_combo_box_text(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Checkbox { props } => build_gtk_checkbox(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Revealer { props, children } => build_gtk_revealer(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::Scroll { props, children } => build_gtk_scrolledwindow(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::OverLay { props, children } => build_gtk_overlay(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::Stack { props, children } => build_gtk_stack(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::Revealer { props, children } => {
|
||||
build_gtk_revealer(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::Scroll { props, children } => {
|
||||
build_gtk_scrolledwindow(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::OverLay { props, children } => {
|
||||
build_gtk_overlay(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
WidgetNode::Stack { props, children } => {
|
||||
build_gtk_stack(props, children, widget_reg)?.upcast()
|
||||
}
|
||||
// WIDGET_NAME_SYSTRAY => build_systray(node)?.upcast(),
|
||||
unknown => {
|
||||
return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown));
|
||||
|
||||
@@ -13,13 +13,37 @@ wrapper! {
|
||||
#[derive(Properties)]
|
||||
#[properties(wrapper_type = CircProg)]
|
||||
pub struct CircProgPriv {
|
||||
#[property(get, set, nick = "Starting at", blurb = "Starting at", minimum = 0f64, maximum = 100f64, default = 0f64)]
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Starting at",
|
||||
blurb = "Starting at",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 0f64
|
||||
)]
|
||||
start_at: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Value", blurb = "The value", minimum = 0f64, maximum = 100f64, default = 0f64)]
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Value",
|
||||
blurb = "The value",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 0f64
|
||||
)]
|
||||
value: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Thickness", blurb = "Thickness", minimum = 0f64, maximum = 100f64, default = 1f64)]
|
||||
#[property(
|
||||
get,
|
||||
set,
|
||||
nick = "Thickness",
|
||||
blurb = "Thickness",
|
||||
minimum = 0f64,
|
||||
maximum = 100f64,
|
||||
default = 1f64
|
||||
)]
|
||||
thickness: RefCell<f64>,
|
||||
|
||||
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
|
||||
@@ -98,7 +122,9 @@ impl ContainerImpl for CircProgPriv {
|
||||
fn add(&self, widget: >k::Widget) {
|
||||
if let Some(content) = &*self.content.borrow() {
|
||||
// TODO: Handle this error when populating children widgets instead
|
||||
error_handling_ctx::print_error(anyhow!("Error, trying to add multiple children to a circular-progress widget"));
|
||||
error_handling_ctx::print_error(anyhow!(
|
||||
"Error, trying to add multiple children to a circular-progress widget"
|
||||
));
|
||||
self.parent_remove(content);
|
||||
}
|
||||
self.parent_add(widget);
|
||||
@@ -125,9 +151,13 @@ impl WidgetImpl for CircProgPriv {
|
||||
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
(min_child + margin.right as i32 + margin.left as i32, natural_child + margin.right as i32 + margin.left as i32)
|
||||
(
|
||||
min_child + margin.right as i32 + margin.left as i32,
|
||||
natural_child + margin.right as i32 + margin.left as i32,
|
||||
)
|
||||
} else {
|
||||
let empty_width = (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
let empty_width =
|
||||
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
(empty_width, empty_width)
|
||||
}
|
||||
}
|
||||
@@ -142,9 +172,13 @@ impl WidgetImpl for CircProgPriv {
|
||||
|
||||
if let Some(child) = &*self.content.borrow() {
|
||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
||||
(min_child + margin.bottom as i32 + margin.top as i32, natural_child + margin.bottom as i32 + margin.top as i32)
|
||||
(
|
||||
min_child + margin.bottom as i32 + margin.top as i32,
|
||||
natural_child + margin.bottom as i32 + margin.top as i32,
|
||||
)
|
||||
} else {
|
||||
let empty_height = (2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
let empty_height =
|
||||
(2 * *self.thickness.borrow() as i32) + margin.right as i32 + margin.left as i32;
|
||||
(empty_height, empty_height)
|
||||
}
|
||||
}
|
||||
@@ -164,9 +198,14 @@ impl WidgetImpl for CircProgPriv {
|
||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||
// Padding is not supported yet
|
||||
let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
|
||||
let bg_color: gdk::RGBA = styles.style_property_for_state("background-color", gtk::StateFlags::NORMAL).get()?;
|
||||
let (start_angle, end_angle) =
|
||||
if clockwise { (0.0, perc_to_rad(value)) } else { (perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI) };
|
||||
let bg_color: gdk::RGBA = styles
|
||||
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
|
||||
.get()?;
|
||||
let (start_angle, end_angle) = if clockwise {
|
||||
(0.0, perc_to_rad(value))
|
||||
} else {
|
||||
(perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI)
|
||||
};
|
||||
|
||||
let total_width = self.obj().allocated_width() as f64;
|
||||
let total_height = self.obj().allocated_height() as f64;
|
||||
|
||||
@@ -79,7 +79,9 @@ impl GraphPriv {
|
||||
*last_updated_at = std::time::Instant::now();
|
||||
|
||||
while let Some(entry) = history.front() {
|
||||
if last_updated_at.duration_since(entry.0).as_millis() as u64 > *self.time_range.borrow() {
|
||||
if last_updated_at.duration_since(entry.0).as_millis() as u64
|
||||
> *self.time_range.borrow()
|
||||
{
|
||||
*last_value = history.pop_front();
|
||||
} else {
|
||||
break;
|
||||
@@ -243,14 +245,24 @@ impl WidgetImpl for GraphPriv {
|
||||
.iter()
|
||||
.map(|(instant, value)| {
|
||||
let t = last_updated_at.duration_since(*instant).as_millis() as f64;
|
||||
self.value_to_point(width, height, t / time_range, (value - min) / value_range)
|
||||
self.value_to_point(
|
||||
width,
|
||||
height,
|
||||
t / time_range,
|
||||
(value - min) / value_range,
|
||||
)
|
||||
})
|
||||
.collect::<VecDeque<(f64, f64)>>();
|
||||
|
||||
// Aad an extra point outside of the graph to extend the line to the left
|
||||
if let Some((instant, value)) = extra_point {
|
||||
let t = last_updated_at.duration_since(instant).as_millis() as f64;
|
||||
let (x, y) = self.value_to_point(width, height, (t - time_range) / time_range, (value - min) / value_range);
|
||||
let (x, y) = self.value_to_point(
|
||||
width,
|
||||
height,
|
||||
(t - time_range) / time_range,
|
||||
(value - min) / value_range,
|
||||
);
|
||||
points.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
|
||||
}
|
||||
points
|
||||
@@ -263,7 +275,9 @@ impl WidgetImpl for GraphPriv {
|
||||
cr.clip();
|
||||
|
||||
// Draw Background
|
||||
let bg_color: gdk::RGBA = styles.style_property_for_state("background-color", gtk::StateFlags::NORMAL).get()?;
|
||||
let bg_color: gdk::RGBA = styles
|
||||
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
|
||||
.get()?;
|
||||
if bg_color.alpha() > 0.0 {
|
||||
if let Some(first_point) = points.front() {
|
||||
cr.line_to(first_point.0, height + margin_bottom);
|
||||
@@ -273,7 +287,12 @@ impl WidgetImpl for GraphPriv {
|
||||
}
|
||||
cr.line_to(width, height);
|
||||
|
||||
cr.set_source_rgba(bg_color.red(), bg_color.green(), bg_color.blue(), bg_color.alpha());
|
||||
cr.set_source_rgba(
|
||||
bg_color.red(),
|
||||
bg_color.green(),
|
||||
bg_color.blue(),
|
||||
bg_color.alpha(),
|
||||
);
|
||||
cr.fill()?;
|
||||
}
|
||||
|
||||
@@ -288,7 +307,12 @@ impl WidgetImpl for GraphPriv {
|
||||
let line_style = &*self.line_style.borrow();
|
||||
apply_line_style(line_style.as_str(), cr)?;
|
||||
cr.set_line_width(thickness);
|
||||
cr.set_source_rgba(line_color.red(), line_color.green(), line_color.blue(), line_color.alpha());
|
||||
cr.set_source_rgba(
|
||||
line_color.red(),
|
||||
line_color.green(),
|
||||
line_color.blue(),
|
||||
line_color.alpha(),
|
||||
);
|
||||
cr.stroke()?;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,9 @@ impl ContainerImpl for TransformPriv {
|
||||
fn add(&self, widget: >k::Widget) {
|
||||
if let Some(content) = &*self.content.borrow() {
|
||||
// TODO: Handle this error when populating children widgets instead
|
||||
error_handling_ctx::print_error(anyhow!("Error, trying to add multiple children to a circular-progress widget"));
|
||||
error_handling_ctx::print_error(anyhow!(
|
||||
"Error, trying to add multiple children to a circular-progress widget"
|
||||
));
|
||||
self.parent_remove(content);
|
||||
}
|
||||
self.parent_add(widget);
|
||||
@@ -145,31 +147,43 @@ impl WidgetImpl for TransformPriv {
|
||||
cr.save()?;
|
||||
|
||||
let transform_origin_x = match &*self.transform_origin_x.borrow() {
|
||||
Some(rcx) => NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64,
|
||||
Some(rcx) => {
|
||||
NumWithUnit::from_str(rcx)?.pixels_relative_to(total_width as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
let transform_origin_y = match &*self.transform_origin_y.borrow() {
|
||||
Some(rcy) => NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64,
|
||||
Some(rcy) => {
|
||||
NumWithUnit::from_str(rcy)?.pixels_relative_to(total_height as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
|
||||
let translate_x = match &*self.translate_x.borrow() {
|
||||
Some(tx) => NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64,
|
||||
Some(tx) => {
|
||||
NumWithUnit::from_str(tx)?.pixels_relative_to(total_width as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
|
||||
let translate_y = match &*self.translate_y.borrow() {
|
||||
Some(ty) => NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64,
|
||||
Some(ty) => {
|
||||
NumWithUnit::from_str(ty)?.pixels_relative_to(total_height as i32) as f64
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
|
||||
let scale_x = match &*self.scale_x.borrow() {
|
||||
Some(sx) => NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0,
|
||||
Some(sx) => {
|
||||
NumWithUnit::from_str(sx)?.perc_relative_to(total_width as i32) as f64 / 100.0
|
||||
}
|
||||
None => 1.0,
|
||||
};
|
||||
|
||||
let scale_y = match &*self.scale_y.borrow() {
|
||||
Some(sy) => NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0,
|
||||
Some(sy) => {
|
||||
NumWithUnit::from_str(sy)?.perc_relative_to(total_height as i32) as f64 / 100.0
|
||||
}
|
||||
None => 1.0,
|
||||
};
|
||||
|
||||
|
||||
@@ -80,12 +80,15 @@ impl WidgetRegistry {
|
||||
for patch_req in patches {
|
||||
match patch_req {
|
||||
PatchGtkWidget::Create(wdgt_node, wdgt_id, parent_id) => {
|
||||
self.create_widget(wdgt_node, wdgt_id, parent_id).expect("failed to create new gtk widget");
|
||||
self.create_widget(wdgt_node, wdgt_id, parent_id)
|
||||
.expect("failed to create new gtk widget");
|
||||
}
|
||||
PatchGtkWidget::Update(widget_id, new_props) => {
|
||||
self.update_props(widget_id, new_props);
|
||||
}
|
||||
PatchGtkWidget::Remove(widget_id, parent_id) => self.remove_widget(widget_id, parent_id),
|
||||
PatchGtkWidget::Remove(widget_id, parent_id) => {
|
||||
self.remove_widget(widget_id, parent_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,24 +127,31 @@ impl WidgetRegistry {
|
||||
// Removals
|
||||
for (id, new_info) in &old_map {
|
||||
if !new_map.contains_key(id) {
|
||||
patch.push(PatchGtkWidget::Remove(*id, new_info.parent_id.expect("Parent ID must exist")));
|
||||
patch.push(PatchGtkWidget::Remove(
|
||||
*id,
|
||||
new_info.parent_id.expect("Parent ID must exist"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
patch
|
||||
}
|
||||
|
||||
pub fn create_widget(&mut self, widget_node: WidgetNode, widget_id: u64, parent_id: u64) -> Result<()> {
|
||||
pub fn create_widget(
|
||||
&mut self,
|
||||
widget_node: WidgetNode,
|
||||
widget_id: u64,
|
||||
parent_id: u64,
|
||||
) -> Result<()> {
|
||||
log::trace!("Creating '{}'", widget_id);
|
||||
if let Some(parent) = self.widgets.get(&parent_id) {
|
||||
let parent_widget = parent.widget.clone();
|
||||
|
||||
if let Some(container) = parent_widget.dynamic_cast::<gtk::Container>().ok() {
|
||||
// check if the widget already exists
|
||||
let position = self
|
||||
.widgets
|
||||
.get(&widget_id)
|
||||
.and_then(|old_entry| container.children().iter().position(|w| w == &old_entry.widget));
|
||||
let position = self.widgets.get(&widget_id).and_then(|old_entry| {
|
||||
container.children().iter().position(|w| w == &old_entry.widget)
|
||||
});
|
||||
|
||||
// obliterate that widget....
|
||||
// how dare it try to create duplication...
|
||||
@@ -200,7 +210,11 @@ impl WidgetRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
pub(super) fn build_gtk_box(
|
||||
props: Map,
|
||||
children: Vec<WidgetNode>,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Box> {
|
||||
// Parse initial props to create the widget:
|
||||
let orientation = props
|
||||
.get("orientation")
|
||||
@@ -209,7 +223,8 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
|
||||
.transpose()?
|
||||
.unwrap_or(gtk::Orientation::Horizontal);
|
||||
|
||||
let spacing = props.get("spacing").and_then(|v| v.clone().try_cast::<i64>()).unwrap_or(0) as i32;
|
||||
let spacing =
|
||||
props.get("spacing").and_then(|v| v.clone().try_cast::<i64>()).unwrap_or(0) as i32;
|
||||
|
||||
let space_evenly = get_bool_prop(&props, "space_evenly", Some(true))?;
|
||||
|
||||
@@ -224,7 +239,9 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
|
||||
let gtk_widget_clone = gtk_widget.clone();
|
||||
|
||||
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||
if let Some(orientation_str) = props.get("orientation").and_then(|v| v.clone().try_cast::<String>()) {
|
||||
if let Some(orientation_str) =
|
||||
props.get("orientation").and_then(|v| v.clone().try_cast::<String>())
|
||||
{
|
||||
if let Ok(orientation) = parse_orientation(&orientation_str) {
|
||||
gtk_widget_clone.set_orientation(orientation);
|
||||
}
|
||||
@@ -239,14 +256,18 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
|
||||
}
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Box");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { widget: gtk_widget.clone().upcast(), update_fn });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { widget: gtk_widget.clone().upcast(), update_fn });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -266,7 +287,9 @@ pub(super) fn build_gtk_overlay(
|
||||
bail!("overlay must contain at least one element");
|
||||
}
|
||||
|
||||
let mut children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
let mut children = children
|
||||
.into_iter()
|
||||
.map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
|
||||
// we have more than one child, we can unwrap
|
||||
let first = children.next().unwrap()?;
|
||||
@@ -284,7 +307,11 @@ pub(super) fn build_gtk_overlay(
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
pub(super) fn build_tooltip(
|
||||
props: Map,
|
||||
children: Vec<WidgetNode>,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Box> {
|
||||
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
gtk_widget.set_has_tooltip(true);
|
||||
|
||||
@@ -304,8 +331,11 @@ pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_regist
|
||||
gtk_widget.add(&content_widget);
|
||||
|
||||
let tooltip_node = Rc::new(tooltip_node);
|
||||
let tooltip_widget = build_gtk_widget(WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()), widget_registry)
|
||||
.expect("Failed to build tooltip widget");
|
||||
let tooltip_widget = build_gtk_widget(
|
||||
WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()),
|
||||
widget_registry,
|
||||
)
|
||||
.expect("Failed to build tooltip widget");
|
||||
|
||||
gtk_widget.connect_query_tooltip(move |_widget, _x, _y, _keyboard_mode, tooltip| {
|
||||
tooltip.set_custom(Some(&tooltip_widget));
|
||||
@@ -317,7 +347,11 @@ pub(super) fn build_tooltip(props: Map, children: Vec<WidgetNode>, widget_regist
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
pub(super) fn build_center_box(
|
||||
props: Map,
|
||||
children: Vec<WidgetNode>,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Box> {
|
||||
let orientation = props
|
||||
.get("orientation")
|
||||
.and_then(|v| v.clone().try_cast::<String>())
|
||||
@@ -374,14 +408,18 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_reg
|
||||
gtk_widget_clone.set_orientation(orientation);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "CenterBox");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -435,7 +473,11 @@ pub(super) fn build_gtk_event_box(
|
||||
let delta = evt.delta().1;
|
||||
if delta != 0f64 {
|
||||
// Ignore the first event https://bugzilla.gnome.org/show_bug.cgi?id=675959
|
||||
run_command(timeout, &onscroll, &[if delta < 0f64 { "up" } else { "down" }]);
|
||||
run_command(
|
||||
timeout,
|
||||
&onscroll,
|
||||
&[if delta < 0f64 { "up" } else { "down" }],
|
||||
);
|
||||
}
|
||||
glib::Propagation::Proceed
|
||||
})
|
||||
@@ -482,7 +524,8 @@ pub(super) fn build_gtk_event_box(
|
||||
let display = gdk::Display::default();
|
||||
let gdk_window = widget.window();
|
||||
if let (Some(display), Some(gdk_window)) = (display, gdk_window) {
|
||||
gdk_window.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref());
|
||||
gdk_window
|
||||
.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref());
|
||||
}
|
||||
}
|
||||
glib::Propagation::Proceed
|
||||
@@ -507,20 +550,38 @@ pub(super) fn build_gtk_event_box(
|
||||
widget.drag_dest_set(
|
||||
DestDefaults::ALL,
|
||||
&[
|
||||
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0),
|
||||
TargetEntry::new("text/plain", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0),
|
||||
TargetEntry::new(
|
||||
"text/uri-list",
|
||||
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
|
||||
0,
|
||||
),
|
||||
TargetEntry::new(
|
||||
"text/plain",
|
||||
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
|
||||
0,
|
||||
),
|
||||
],
|
||||
gdk::DragAction::COPY,
|
||||
);
|
||||
connect_signal_handler!(
|
||||
widget,
|
||||
widget.connect_drag_data_received(move |_, _, _x, _y, selection_data, _target_type, _timestamp| {
|
||||
if let Some(data) = selection_data.uris().first() {
|
||||
run_command(timeout, &ondropped, &[data.to_string(), "file".to_string()]);
|
||||
} else if let Some(data) = selection_data.text() {
|
||||
run_command(timeout, &ondropped, &[data.to_string(), "text".to_string()]);
|
||||
widget.connect_drag_data_received(
|
||||
move |_, _, _x, _y, selection_data, _target_type, _timestamp| {
|
||||
if let Some(data) = selection_data.uris().first() {
|
||||
run_command(
|
||||
timeout,
|
||||
&ondropped,
|
||||
&[data.to_string(), "file".to_string()],
|
||||
);
|
||||
} else if let Some(data) = selection_data.text() {
|
||||
run_command(
|
||||
timeout,
|
||||
&ondropped,
|
||||
&[data.to_string(), "text".to_string()],
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -534,12 +595,16 @@ pub(super) fn build_gtk_event_box(
|
||||
widget.drag_source_unset();
|
||||
} else {
|
||||
let target_entry = match dragtype {
|
||||
DragEntryType::File => {
|
||||
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0)
|
||||
}
|
||||
DragEntryType::Text => {
|
||||
TargetEntry::new("text/plain", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0)
|
||||
}
|
||||
DragEntryType::File => TargetEntry::new(
|
||||
"text/uri-list",
|
||||
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
|
||||
0,
|
||||
),
|
||||
DragEntryType::Text => TargetEntry::new(
|
||||
"text/plain",
|
||||
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
|
||||
0,
|
||||
),
|
||||
};
|
||||
widget.drag_source_set(
|
||||
ModifierType::BUTTON1_MASK,
|
||||
@@ -588,7 +653,9 @@ pub(super) fn build_gtk_event_box(
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -608,21 +675,29 @@ pub(super) fn build_gtk_event_box(
|
||||
|
||||
let id = hash_props_and_type(&props, "EventBox");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Stack> {
|
||||
pub(super) fn build_gtk_stack(
|
||||
props: Map,
|
||||
children: Vec<WidgetNode>,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Stack> {
|
||||
let gtk_widget = gtk::Stack::new();
|
||||
|
||||
if children.is_empty() {
|
||||
return Err(anyhow!("stack must contain at least one element"));
|
||||
}
|
||||
|
||||
let children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
let children = children
|
||||
.into_iter()
|
||||
.map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
|
||||
for (i, child) in children.enumerate() {
|
||||
let child = child?;
|
||||
@@ -652,21 +727,28 @@ pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_regi
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Stack");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry) -> Result<Transform> {
|
||||
pub(super) fn build_transform(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<Transform> {
|
||||
let widget = Transform::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: &Transform| -> Result<()> {
|
||||
@@ -715,7 +797,9 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, &widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -729,7 +813,10 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<CircProg> {
|
||||
pub(super) fn build_circular_progress_bar(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<CircProg> {
|
||||
let widget = CircProg::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: &CircProg| -> Result<()> {
|
||||
@@ -759,7 +846,9 @@ pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut Widg
|
||||
let _ = apply_props(props, &widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -773,7 +862,10 @@ pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut Widg
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> Result<super::graph::Graph> {
|
||||
pub(super) fn build_graph(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<super::graph::Graph> {
|
||||
let widget = super::graph::Graph::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: &super::graph::Graph| -> Result<()> {
|
||||
@@ -842,7 +934,9 @@ pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> R
|
||||
let _ = apply_props(props, &widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(&widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -856,7 +950,10 @@ pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> R
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ProgressBar> {
|
||||
pub(super) fn build_gtk_progress(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::ProgressBar> {
|
||||
let gtk_widget = gtk::ProgressBar::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::ProgressBar| -> Result<()> {
|
||||
@@ -887,21 +984,28 @@ pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistr
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Progress");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Image> {
|
||||
pub(super) fn build_gtk_image(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Image> {
|
||||
let gtk_widget = gtk::Image::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::Image| -> Result<()> {
|
||||
@@ -916,7 +1020,8 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
}
|
||||
|
||||
if path.ends_with(".gif") {
|
||||
let pixbuf_animation = gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?;
|
||||
let pixbuf_animation =
|
||||
gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?;
|
||||
widget.set_from_animation(&pixbuf_animation);
|
||||
} else {
|
||||
let pixbuf;
|
||||
@@ -931,7 +1036,9 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let reg = regex::Regex::new(r"<svg")?;
|
||||
reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_svg))
|
||||
};
|
||||
let stream = gtk::gio::MemoryInputStream::from_bytes(>k::glib::Bytes::from(svg_data.as_bytes()));
|
||||
let stream = gtk::gio::MemoryInputStream::from_bytes(>k::glib::Bytes::from(
|
||||
svg_data.as_bytes(),
|
||||
));
|
||||
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale(
|
||||
&stream,
|
||||
image_width,
|
||||
@@ -967,21 +1074,28 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Image");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Button> {
|
||||
pub(super) fn build_gtk_button(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Button> {
|
||||
let gtk_widget = gtk::Button::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::Button| -> Result<()> {
|
||||
@@ -1043,21 +1157,28 @@ pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Button");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Label> {
|
||||
pub(super) fn build_gtk_label(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Label> {
|
||||
let gtk_widget = gtk::Label::new(None);
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::Label| -> Result<()> {
|
||||
@@ -1080,7 +1201,13 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
} else {
|
||||
widget.set_max_width_chars(limit_width);
|
||||
}
|
||||
apply_ellipsize_settings(&widget, truncate, limit_width, truncate_left, show_truncated);
|
||||
apply_ellipsize_settings(
|
||||
&widget,
|
||||
truncate,
|
||||
limit_width,
|
||||
truncate_left,
|
||||
show_truncated,
|
||||
);
|
||||
text
|
||||
} else {
|
||||
widget.set_ellipsize(pango::EllipsizeMode::None);
|
||||
@@ -1098,7 +1225,8 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
}
|
||||
};
|
||||
|
||||
let unescaped = unescape::unescape(&t).ok_or_else(|| anyhow!("Failed to unescape..."))?;
|
||||
let unescaped =
|
||||
unescape::unescape(&t).ok_or_else(|| anyhow!("Failed to unescape..."))?;
|
||||
let final_text = if unindent { util::unindent(&unescaped) } else { unescaped };
|
||||
widget.set_text(&final_text);
|
||||
} else if has_markup {
|
||||
@@ -1148,21 +1276,28 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Label");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Entry> {
|
||||
pub(super) fn build_gtk_input(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Entry> {
|
||||
let gtk_widget = gtk::Entry::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::Entry| -> Result<()> {
|
||||
@@ -1203,21 +1338,28 @@ pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Input");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Calendar> {
|
||||
pub(super) fn build_gtk_calendar(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Calendar> {
|
||||
let gtk_widget = gtk::Calendar::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::Calendar| -> Result<()> {
|
||||
@@ -1271,7 +1413,9 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
|
||||
if let Ok(onclick) = get_string_prop(&props, "onclick", None) {
|
||||
connect_signal_handler!(
|
||||
widget,
|
||||
widget.connect_day_selected(move |w| { run_command(timeout, &onclick, &[w.day(), w.month(), w.year()]) })
|
||||
widget.connect_day_selected(move |w| {
|
||||
run_command(timeout, &onclick, &[w.day(), w.month(), w.year()])
|
||||
})
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
@@ -1284,21 +1428,28 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Calendar");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ComboBoxText> {
|
||||
pub(super) fn build_gtk_combo_box_text(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::ComboBoxText> {
|
||||
let gtk_widget = gtk::ComboBoxText::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::ComboBoxText| -> Result<()> {
|
||||
@@ -1315,7 +1466,11 @@ pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetR
|
||||
connect_signal_handler!(
|
||||
widget,
|
||||
widget.connect_changed(move |widget| {
|
||||
run_command(timeout, &onchange, &[widget.active_text().unwrap_or_else(|| "".into())]);
|
||||
run_command(
|
||||
timeout,
|
||||
&onchange,
|
||||
&[widget.active_text().unwrap_or_else(|| "".into())],
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1329,14 +1484,18 @@ pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetR
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "ComboBoxText");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -1382,14 +1541,18 @@ pub(super) fn build_gtk_expander(
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Expander");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -1425,7 +1588,9 @@ pub(super) fn build_gtk_revealer(
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -1433,7 +1598,8 @@ pub(super) fn build_gtk_revealer(
|
||||
match children.len() {
|
||||
0 => { /* maybe warn? */ }
|
||||
1 => {
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(children[0].clone()), widget_registry)?;
|
||||
let child_widget =
|
||||
build_gtk_widget(WidgetInput::Node(children[0].clone()), widget_registry)?;
|
||||
gtk_widget.set_child(Some(&child_widget));
|
||||
}
|
||||
n => {
|
||||
@@ -1443,14 +1609,19 @@ pub(super) fn build_gtk_revealer(
|
||||
|
||||
let id = hash_props_and_type(&props, "Revealer");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::CheckButton> {
|
||||
pub(super) fn build_gtk_checkbox(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::CheckButton> {
|
||||
let gtk_widget = gtk::CheckButton::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::CheckButton| -> Result<()> {
|
||||
@@ -1464,7 +1635,11 @@ pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistr
|
||||
connect_signal_handler!(
|
||||
widget,
|
||||
widget.connect_toggled(move |widget| {
|
||||
run_command(timeout, if widget.is_active() { &onchecked } else { &onunchecked }, &[] as &[&str]);
|
||||
run_command(
|
||||
timeout,
|
||||
if widget.is_active() { &onchecked } else { &onunchecked },
|
||||
&[] as &[&str],
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1478,21 +1653,28 @@ pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistr
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Checkbox");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorButton> {
|
||||
pub(super) fn build_gtk_color_button(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::ColorButton> {
|
||||
let gtk_widget = gtk::ColorButton::builder().build();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::ColorButton| -> Result<()> {
|
||||
@@ -1524,21 +1706,28 @@ pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetReg
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "ColorButton");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorChooserWidget> {
|
||||
pub(super) fn build_gtk_color_chooser(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::ColorChooserWidget> {
|
||||
let gtk_widget = gtk::ColorChooserWidget::new();
|
||||
|
||||
let apply_props = |props: &Map, widget: >k::ColorChooserWidget| -> Result<()> {
|
||||
@@ -1570,22 +1759,32 @@ pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRe
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "ColorChooser");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Scale> {
|
||||
let gtk_widget = gtk::Scale::new(gtk::Orientation::Horizontal, Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)));
|
||||
pub(super) fn build_gtk_scale(
|
||||
props: Map,
|
||||
widget_registry: &mut WidgetRegistry,
|
||||
) -> Result<gtk::Scale> {
|
||||
let gtk_widget = gtk::Scale::new(
|
||||
gtk::Orientation::Horizontal,
|
||||
Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
|
||||
);
|
||||
|
||||
// Reusable closure for applying props
|
||||
let apply_props = |props: &Map, widget: >k::Scale| -> Result<()> {
|
||||
@@ -1617,14 +1816,18 @@ pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry)
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Slider");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -1662,7 +1865,9 @@ pub(super) fn build_gtk_scrolledwindow(
|
||||
let _ = apply_props(props, >k_widget_clone);
|
||||
|
||||
// now re-apply generic widget attrs
|
||||
if let Err(err) = resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props) {
|
||||
if let Err(err) =
|
||||
resolve_rhai_widget_attrs(>k_widget_clone.clone().upcast::<gtk::Widget>(), &props)
|
||||
{
|
||||
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||
}
|
||||
});
|
||||
@@ -1682,7 +1887,9 @@ pub(super) fn build_gtk_scrolledwindow(
|
||||
|
||||
let id = hash_props_and_type(&props, "ScrolledWindow");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
widget_registry
|
||||
.widgets
|
||||
.insert(id, WidgetEntry { update_fn, widget: gtk_widget.clone().upcast() });
|
||||
|
||||
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||
|
||||
@@ -1730,14 +1937,20 @@ pub(super) fn resolve_rhai_widget_attrs(gtk_widget: >k::Widget, props: &Map) -
|
||||
if let Ok(style_str) = get_string_prop(&props, "style", None) {
|
||||
let css_provider = gtk::CssProvider::new();
|
||||
let scss = format!("* {{ {} }}", style_str);
|
||||
css_provider.load_from_data(grass::from_string(scss, &grass::Options::default())?.as_bytes())?;
|
||||
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
css_provider
|
||||
.load_from_data(grass::from_string(scss, &grass::Options::default())?.as_bytes())?;
|
||||
gtk_widget
|
||||
.style_context()
|
||||
.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
}
|
||||
|
||||
if let Ok(css_str) = get_string_prop(&props, "css", None) {
|
||||
let css_provider = gtk::CssProvider::new();
|
||||
css_provider.load_from_data(grass::from_string(css_str, &grass::Options::default())?.as_bytes())?;
|
||||
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
css_provider
|
||||
.load_from_data(grass::from_string(css_str, &grass::Options::default())?.as_bytes())?;
|
||||
gtk_widget
|
||||
.style_context()
|
||||
.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
}
|
||||
|
||||
if let Ok(valign) = get_string_prop(&props, "valign", None) {
|
||||
|
||||
@@ -14,7 +14,11 @@ where
|
||||
std::thread::Builder::new()
|
||||
.name("command-execution-thread".to_string())
|
||||
.spawn(move || {
|
||||
log::debug!("Running command from widget [timeout: {}ms]: {}", timeout.as_millis(), cmd);
|
||||
log::debug!(
|
||||
"Running command from widget [timeout: {}ms]: {}",
|
||||
timeout.as_millis(),
|
||||
cmd
|
||||
);
|
||||
let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn();
|
||||
match child {
|
||||
Ok(mut child) => match child.wait_timeout(timeout) {
|
||||
@@ -89,10 +93,20 @@ pub(super) fn parse_orientation(ori: &str) -> Result<gtk::Orientation> {
|
||||
}
|
||||
|
||||
/// Gtk Label
|
||||
pub(super) fn apply_ellipsize_settings(label: >k::Label, truncate: bool, limit_width: i32, truncate_left: bool, show: bool) {
|
||||
pub(super) fn apply_ellipsize_settings(
|
||||
label: >k::Label,
|
||||
truncate: bool,
|
||||
limit_width: i32,
|
||||
truncate_left: bool,
|
||||
show: bool,
|
||||
) {
|
||||
if (truncate || limit_width != i32::MAX) && show {
|
||||
label.set_max_width_chars(if limit_width == i32::MAX { -1 } else { limit_width });
|
||||
label.set_ellipsize(if truncate_left { pango::EllipsizeMode::Start } else { pango::EllipsizeMode::End });
|
||||
label.set_ellipsize(if truncate_left {
|
||||
pango::EllipsizeMode::Start
|
||||
} else {
|
||||
pango::EllipsizeMode::End
|
||||
});
|
||||
} else {
|
||||
label.set_ellipsize(pango::EllipsizeMode::None);
|
||||
}
|
||||
@@ -146,7 +160,9 @@ where
|
||||
{
|
||||
if !args.is_empty() {
|
||||
let cmd = cmd.replace("{}", &format!("{}", args[0]));
|
||||
args.iter().enumerate().fold(cmd, |acc, (i, arg)| acc.replace(&format!("{{{}}}", i), &format!("{}", arg)))
|
||||
args.iter()
|
||||
.enumerate()
|
||||
.fold(cmd, |acc, (i, arg)| acc.replace(&format!("{{{}}}", i), &format!("{}", arg)))
|
||||
} else {
|
||||
cmd.to_string()
|
||||
}
|
||||
|
||||
@@ -37,7 +37,10 @@ pub struct BackendWindowOptionsDef {
|
||||
|
||||
impl BackendWindowOptionsDef {
|
||||
pub fn eval(&self, properties: Map) -> Result<BackendWindowOptions, Error> {
|
||||
Ok(BackendWindowOptions { wayland: self.wayland.eval(properties.clone())?, x11: self.x11.eval(properties)? })
|
||||
Ok(BackendWindowOptions {
|
||||
wayland: self.wayland.eval(properties.clone())?,
|
||||
x11: self.x11.eval(properties)?,
|
||||
})
|
||||
}
|
||||
|
||||
// pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> {
|
||||
@@ -119,13 +122,22 @@ impl X11BackendWindowOptionsDef {
|
||||
|
||||
struts: match properties.get("reserve") {
|
||||
Some(dynval) => {
|
||||
let obj_map = dynval.read_lock::<Map>().ok_or(Error::EnumParseErrorMessage("Expected map for reserve"))?;
|
||||
let obj_map = dynval
|
||||
.read_lock::<Map>()
|
||||
.ok_or(Error::EnumParseErrorMessage("Expected map for reserve"))?;
|
||||
|
||||
let distance_str = obj_map.get("distance").ok_or(Error::MissingField("distance"))?.clone_cast::<String>();
|
||||
let distance_str = obj_map
|
||||
.get("distance")
|
||||
.ok_or(Error::MissingField("distance"))?
|
||||
.clone_cast::<String>();
|
||||
|
||||
let distance = NumWithUnit::from_str(&distance_str)?;
|
||||
|
||||
let side = obj_map.get("side").map(|s| s.clone_cast::<String>()).map(|s| Side::from_str(&s)).transpose()?;
|
||||
let side = obj_map
|
||||
.get("side")
|
||||
.map(|s| s.clone_cast::<String>())
|
||||
.map(|s| Side::from_str(&s))
|
||||
.transpose()?;
|
||||
|
||||
X11StrutDefinition { distance, side: side.unwrap_or(Side::default()) }
|
||||
}
|
||||
@@ -142,7 +154,9 @@ impl X11BackendWindowOptionsDef {
|
||||
|
||||
wm_ignore: {
|
||||
let wm_ignore = properties.get("wm_ignore").map(|d| d.clone_cast::<bool>());
|
||||
wm_ignore.unwrap_or_else(|| properties.get("windowtype").is_none() && properties.get("reserve").is_none())
|
||||
wm_ignore.unwrap_or_else(|| {
|
||||
properties.get("windowtype").is_none() && properties.get("reserve").is_none()
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,10 +45,16 @@ impl FromStr for NumWithUnit {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static PATTERN: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new("^(-?\\d+(?:.\\d+)?)(.*)$").unwrap());
|
||||
static PATTERN: Lazy<regex::Regex> =
|
||||
Lazy::new(|| regex::Regex::new("^(-?\\d+(?:.\\d+)?)(.*)$").unwrap());
|
||||
|
||||
let captures = PATTERN.captures(s).ok_or_else(|| Error::NumParseFailed(s.to_string()))?;
|
||||
let value = captures.get(1).unwrap().as_str().parse::<f32>().map_err(|_| Error::NumParseFailed(s.to_string()))?;
|
||||
let value = captures
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.parse::<f32>()
|
||||
.map_err(|_| Error::NumParseFailed(s.to_string()))?;
|
||||
match captures.get(2).unwrap().as_str() {
|
||||
"px" | "" => Ok(NumWithUnit::Pixels(value.floor() as i32)),
|
||||
"%" => Ok(NumWithUnit::Percent(value)),
|
||||
@@ -109,7 +115,10 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_parse_coords() {
|
||||
assert_eq!(Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) }, Coords::from_str("50x60").unwrap());
|
||||
assert_eq!(
|
||||
Coords { x: NumWithUnit::Pixels(50), y: NumWithUnit::Pixels(60) },
|
||||
Coords::from_str("50x60").unwrap()
|
||||
);
|
||||
assert!(Coords::from_str("5060").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,16 @@ impl Display for EnumParseError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display, smart_default::SmartDefault, serde::Serialize)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
derive_more::Display,
|
||||
smart_default::SmartDefault,
|
||||
serde::Serialize,
|
||||
)]
|
||||
pub enum WindowStacking {
|
||||
#[default]
|
||||
Foreground,
|
||||
|
||||
@@ -35,7 +35,8 @@ impl FromStr for Coords {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (sx, sy) = s.split_once(|c: char| c == 'x' || c == '*').ok_or(Error::MalformedCoords)?;
|
||||
let (sx, sy) =
|
||||
s.split_once(|c: char| c == 'x' || c == '*').ok_or(Error::MalformedCoords)?;
|
||||
Ok(Coords { x: sx.parse()?, y: sy.parse()? })
|
||||
}
|
||||
}
|
||||
@@ -113,9 +114,10 @@ impl std::str::FromStr for AnchorPoint {
|
||||
type Err = EnumParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (x_str, y_str) = s
|
||||
.split_once(' ')
|
||||
.ok_or_else(|| EnumParseError { input: s.to_string(), expected: vec!["<horizontal> <vertical>"] })?;
|
||||
let (x_str, y_str) = s.split_once(' ').ok_or_else(|| EnumParseError {
|
||||
input: s.to_string(),
|
||||
expected: vec!["<horizontal> <vertical>"],
|
||||
})?;
|
||||
|
||||
let x = AnchorAlignment::from_x_alignment(x_str)?;
|
||||
let y = AnchorAlignment::from_y_alignment(y_str)?;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{window::coords::Coords, window::monitor::MonitorIdentifier, window::window_geometry::AnchorPoint};
|
||||
use crate::{
|
||||
window::coords::Coords, window::monitor::MonitorIdentifier,
|
||||
window::window_geometry::AnchorPoint,
|
||||
};
|
||||
|
||||
/// This stores the arguments given in the command line to create a window
|
||||
/// While creating a window, we combine this with information from the
|
||||
|
||||
@@ -41,10 +41,13 @@ impl WindowInitiator {
|
||||
// Some(geo) => Some(geo.eval(&vars)?.override_if_given(args.anchor, args.pos, args.size)),
|
||||
None => None,
|
||||
};
|
||||
let monitor = args
|
||||
.monitor
|
||||
.clone()
|
||||
.or_else(|| properties.get("monitor")?.clone().try_cast::<i64>().map(|n| MonitorIdentifier::Numeric(n as i32)));
|
||||
let monitor = args.monitor.clone().or_else(|| {
|
||||
properties
|
||||
.get("monitor")?
|
||||
.clone()
|
||||
.try_cast::<i64>()
|
||||
.map(|n| MonitorIdentifier::Numeric(n as i32))
|
||||
});
|
||||
Ok(WindowInitiator {
|
||||
backend_options: window_def.backend_options.eval(properties.clone())?,
|
||||
geometry,
|
||||
@@ -63,15 +66,23 @@ impl WindowInitiator {
|
||||
// }
|
||||
}
|
||||
|
||||
fn parse_geometry(val: &Dynamic, args: &WindowArguments, override_geom: bool) -> Result<WindowGeometry> {
|
||||
fn parse_geometry(
|
||||
val: &Dynamic,
|
||||
args: &WindowArguments,
|
||||
override_geom: bool,
|
||||
) -> Result<WindowGeometry> {
|
||||
let map = val.clone().cast::<rhai::Map>();
|
||||
|
||||
let anchor = map.get("anchor").map(|dyn_value| anchor_point_from_str(&dyn_value.to_string())).transpose()?;
|
||||
let anchor = map
|
||||
.get("anchor")
|
||||
.map(|dyn_value| anchor_point_from_str(&dyn_value.to_string()))
|
||||
.transpose()?;
|
||||
|
||||
let mut geom = WindowGeometry {
|
||||
offset: get_coords_from_map(&map, "x", "y")?,
|
||||
size: get_coords_from_map(&map, "width", "height")?,
|
||||
anchor_point: anchor.unwrap_or(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::START }),
|
||||
anchor_point: anchor
|
||||
.unwrap_or(AnchorPoint { x: AnchorAlignment::CENTER, y: AnchorAlignment::START }),
|
||||
};
|
||||
|
||||
if override_geom {
|
||||
@@ -111,7 +122,8 @@ fn anchor_point_from_str(s: &str) -> Result<AnchorPoint> {
|
||||
match parts.as_slice() {
|
||||
[single] => {
|
||||
// Apply to both x and y
|
||||
let alignment = AnchorAlignment::from_x_alignment(single).or_else(|_| AnchorAlignment::from_y_alignment(single))?;
|
||||
let alignment = AnchorAlignment::from_x_alignment(single)
|
||||
.or_else(|_| AnchorAlignment::from_y_alignment(single))?;
|
||||
Ok(AnchorPoint { x: alignment, y: alignment })
|
||||
}
|
||||
[y_part, x_part] => {
|
||||
|
||||
@@ -5,15 +5,23 @@ use std::time::Duration;
|
||||
/// General purpose helpers
|
||||
pub fn get_string_prop(props: &Map, key: &str, default: Option<&str>) -> Result<String> {
|
||||
if let Some(value) = props.get(key) {
|
||||
value.clone().try_cast::<String>().ok_or_else(|| anyhow!("Expected property `{}` to be a string", key))
|
||||
value
|
||||
.clone()
|
||||
.try_cast::<String>()
|
||||
.ok_or_else(|| anyhow!("Expected property `{}` to be a string", key))
|
||||
} else {
|
||||
default.map(|s| s.to_string()).ok_or_else(|| anyhow!("Missing required string property `{}`", key))
|
||||
default
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| anyhow!("Missing required string property `{}`", key))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_bool_prop(props: &Map, key: &str, default: Option<bool>) -> Result<bool> {
|
||||
if let Some(value) = props.get(key) {
|
||||
value.clone().try_cast::<bool>().ok_or_else(|| anyhow!("Expected property `{}` to be a bool", key))
|
||||
value
|
||||
.clone()
|
||||
.try_cast::<bool>()
|
||||
.ok_or_else(|| anyhow!("Expected property `{}` to be a bool", key))
|
||||
} else {
|
||||
default.map(|s| s).ok_or_else(|| anyhow!("Missing required bool property `{}`", key))
|
||||
}
|
||||
@@ -23,7 +31,8 @@ pub fn get_i64_prop(props: &Map, key: &str, default: Option<i64>) -> Result<i64>
|
||||
if let Some(v) = value.clone().try_cast::<i64>() {
|
||||
Ok(v)
|
||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
||||
s.parse::<i64>().map_err(|_| anyhow!("Expected property `{}` to be an i64 or numeric string", key))
|
||||
s.parse::<i64>()
|
||||
.map_err(|_| anyhow!("Expected property `{}` to be an i64 or numeric string", key))
|
||||
} else {
|
||||
Err(anyhow!("Expected property `{}` to be an i64 or numeric string", key))
|
||||
}
|
||||
@@ -39,7 +48,9 @@ pub fn get_f64_prop(props: &Map, key: &str, default: Option<f64>) -> Result<f64>
|
||||
} else if let Some(v) = value.clone().try_cast::<i64>() {
|
||||
Ok(v as f64)
|
||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
||||
s.parse::<f64>().map_err(|_| anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key))
|
||||
s.parse::<f64>().map_err(|_| {
|
||||
anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key)
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key))
|
||||
}
|
||||
@@ -53,7 +64,8 @@ pub fn get_i32_prop(props: &Map, key: &str, default: Option<i32>) -> Result<i32>
|
||||
if let Some(v) = value.clone().try_cast::<i32>() {
|
||||
Ok(v)
|
||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
||||
s.parse::<i32>().map_err(|_| anyhow!("Expected property `{}` to be an i32 or numeric string", key))
|
||||
s.parse::<i32>()
|
||||
.map_err(|_| anyhow!("Expected property `{}` to be an i32 or numeric string", key))
|
||||
} else {
|
||||
Err(anyhow!("Expected property `{}` to be an i32 or numeric string", key))
|
||||
}
|
||||
@@ -62,13 +74,23 @@ pub fn get_i32_prop(props: &Map, key: &str, default: Option<i32>) -> Result<i32>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vec_string_prop(props: &Map, key: &str, default: Option<Vec<String>>) -> Result<Vec<String>> {
|
||||
pub fn get_vec_string_prop(
|
||||
props: &Map,
|
||||
key: &str,
|
||||
default: Option<Vec<String>>,
|
||||
) -> Result<Vec<String>> {
|
||||
if let Some(value) = props.get(key) {
|
||||
let array = value.clone().try_cast::<Vec<Dynamic>>().ok_or_else(|| anyhow!("Expected property `{}` to be a vec", key))?;
|
||||
let array = value
|
||||
.clone()
|
||||
.try_cast::<Vec<Dynamic>>()
|
||||
.ok_or_else(|| anyhow!("Expected property `{}` to be a vec", key))?;
|
||||
|
||||
array
|
||||
.into_iter()
|
||||
.map(|d| d.try_cast::<String>().ok_or_else(|| anyhow!("Expected all elements of `{}` to be strings", key)))
|
||||
.map(|d| {
|
||||
d.try_cast::<String>()
|
||||
.ok_or_else(|| anyhow!("Expected all elements of `{}` to be strings", key))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
default.ok_or_else(|| anyhow!("Missing required vec property `{}`", key))
|
||||
@@ -88,7 +110,8 @@ pub fn get_duration_prop(props: &Map, key: &str, default: Option<Duration>) -> R
|
||||
Ok(Duration::from_secs(s))
|
||||
} else if key_str.ends_with("min") {
|
||||
let num = &key_str[..key_str.len() - 3];
|
||||
let mins = num.parse::<u64>().map_err(|_| anyhow!("Invalid min value: '{}'", key_str))?;
|
||||
let mins =
|
||||
num.parse::<u64>().map_err(|_| anyhow!("Invalid min value: '{}'", key_str))?;
|
||||
Ok(Duration::from_secs(mins * 60))
|
||||
} else if key_str.ends_with("h") {
|
||||
let num = &key_str[..key_str.len() - 1];
|
||||
|
||||
@@ -5,8 +5,7 @@ use std::env::var;
|
||||
/// that order, which is the precedence order prescribed by Section 8.2 of POSIX.1-2017.
|
||||
/// If the environment variable is not defined or is malformed use the POSIX locale.
|
||||
pub fn get_locale() -> Locale {
|
||||
var("LC_ALL")
|
||||
.or_else(|_| var("LC_TIME"))
|
||||
.or_else(|_| var("LANG"))
|
||||
.map_or(Locale::POSIX, |v| v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default())
|
||||
var("LC_ALL").or_else(|_| var("LC_TIME")).or_else(|_| var("LANG")).map_or(Locale::POSIX, |v| {
|
||||
v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,20 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The name of a variable
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, Debug, RefCast)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
AsRef,
|
||||
From,
|
||||
FromStr,
|
||||
Display,
|
||||
Debug,
|
||||
RefCast,
|
||||
)]
|
||||
#[debug("VarName({})", _0)]
|
||||
pub struct VarName(pub String);
|
||||
|
||||
@@ -34,7 +47,20 @@ impl From<AttrName> for VarName {
|
||||
|
||||
/// The name of an attribute
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Serialize, Deserialize, AsRef, From, FromStr, Display, Debug, RefCast)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
AsRef,
|
||||
From,
|
||||
FromStr,
|
||||
Display,
|
||||
Debug,
|
||||
RefCast,
|
||||
)]
|
||||
#[debug("AttrName({})", _0)]
|
||||
pub struct AttrName(pub String);
|
||||
|
||||
|
||||
@@ -91,15 +91,21 @@ pub fn register_all_widgets(engine: &mut Engine) {
|
||||
// }
|
||||
// });
|
||||
|
||||
engine.register_fn("defwindow", |name: &str, props: Map, node: WidgetNode| WidgetNode::DefWindow {
|
||||
name: name.to_string(),
|
||||
props,
|
||||
node: Box::new(node),
|
||||
engine.register_fn("defwindow", |name: &str, props: Map, node: WidgetNode| {
|
||||
WidgetNode::DefWindow { name: name.to_string(), props, node: Box::new(node) }
|
||||
});
|
||||
|
||||
engine.register_fn("poll", |var: &str, props: Map| WidgetNode::Poll { var: var.to_string(), props });
|
||||
engine.register_fn("poll", |var: &str, props: Map| WidgetNode::Poll {
|
||||
var: var.to_string(),
|
||||
props,
|
||||
});
|
||||
|
||||
engine.register_fn("listen", |var: &str, props: Map| WidgetNode::Listen { var: var.to_string(), props });
|
||||
engine.register_fn("listen", |var: &str, props: Map| WidgetNode::Listen {
|
||||
var: var.to_string(),
|
||||
props,
|
||||
});
|
||||
|
||||
engine.register_fn("enter", |children: Array| WidgetNode::Enter(children.into_iter().map(|v| v.cast()).collect()));
|
||||
engine.register_fn("enter", |children: Array| {
|
||||
WidgetNode::Enter(children.into_iter().map(|v| v.cast()).collect())
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,7 +33,13 @@ impl<'a> Diagnostic<'a> {
|
||||
// bar seperator
|
||||
out.push_str(&format!(" {bar}\n", bar = "|".dimmed()));
|
||||
|
||||
out.push_str(&format!("{:>width$} {sep} {}\n", self.line, self.line_text, width = num_width, sep = "|".dimmed(),));
|
||||
out.push_str(&format!(
|
||||
"{:>width$} {sep} {}\n",
|
||||
self.line,
|
||||
self.line_text,
|
||||
width = num_width,
|
||||
sep = "|".dimmed(),
|
||||
));
|
||||
|
||||
// The caret line, pointing at the column
|
||||
let caret_padding = " ".repeat(self.column.saturating_sub(1));
|
||||
@@ -72,7 +78,12 @@ impl<'a> Diagnostic<'a> {
|
||||
));
|
||||
|
||||
for line in note_lines {
|
||||
out.push_str(&format!("{v} {line:<width$} {v}\n", v = "│".green().bold(), line = line.green(), width = width));
|
||||
out.push_str(&format!(
|
||||
"{v} {line:<width$} {v}\n",
|
||||
v = "│".green().bold(),
|
||||
line = line.green(),
|
||||
width = width
|
||||
));
|
||||
}
|
||||
|
||||
out.push_str(&format!(
|
||||
@@ -127,21 +138,29 @@ fn get_root_cause<'a>(err: &'a EvalAltResult) -> &'a EvalAltResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &Engine, code: &str) -> ErrorHelp {
|
||||
fn get_error_info(
|
||||
root_err: &EvalAltResult,
|
||||
outer_err: &EvalAltResult,
|
||||
engine: &Engine,
|
||||
code: &str,
|
||||
) -> ErrorHelp {
|
||||
let (help, hint) = match root_err {
|
||||
EvalAltResult::ErrorParsing(..) => (
|
||||
"Syntax error encountered while parsing.".into(),
|
||||
"Check for unmatched tokens, invalid constructs, or misplaced punctuation.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorVariableExists(name, ..) => {
|
||||
(format!("Variable '{}' is already defined.", name), "Remove or rename the duplicate declaration.".into())
|
||||
}
|
||||
EvalAltResult::ErrorForbiddenVariable(name, ..) => {
|
||||
(format!("Usage of forbidden variable '{}'.", name), "Avoid using reserved or protected variable names.".into())
|
||||
}
|
||||
EvalAltResult::ErrorVariableNotFound(name, ..) => {
|
||||
(format!("Unknown variable '{}'.", name), "Check for typos or ensure the variable is initialized before use.".into())
|
||||
}
|
||||
EvalAltResult::ErrorVariableExists(name, ..) => (
|
||||
format!("Variable '{}' is already defined.", name),
|
||||
"Remove or rename the duplicate declaration.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorForbiddenVariable(name, ..) => (
|
||||
format!("Usage of forbidden variable '{}'.", name),
|
||||
"Avoid using reserved or protected variable names.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorVariableNotFound(name, ..) => (
|
||||
format!("Unknown variable '{}'.", name),
|
||||
"Check for typos or ensure the variable is initialized before use.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorPropertyNotFound(name, ..) => (
|
||||
format!("Property '{}' not found on this object.", name),
|
||||
"Verify the property name and the object’s available fields.".into(),
|
||||
@@ -177,7 +196,10 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
|
||||
format!("Did you mean one of:\n {}", candidates.join("\n ")),
|
||||
)
|
||||
} else {
|
||||
(format!("Function '{}' is not defined.", fn_sig), "Check spelling, module path, or argument count.".into())
|
||||
(
|
||||
format!("Function '{}' is not defined.", fn_sig),
|
||||
"Check spelling, module path, or argument count.".into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
EvalAltResult::ErrorModuleNotFound(name, ..) => (
|
||||
@@ -188,12 +210,14 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
|
||||
format!("Error inside function '{}': {}", fn_name, msg),
|
||||
"Inspect the function implementation and arguments passed.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorInModule(name, ..) => {
|
||||
(format!("Error while loading module '{}'.", name), "Check the module code for syntax or runtime errors.".into())
|
||||
}
|
||||
EvalAltResult::ErrorUnboundThis(..) => {
|
||||
("`this` is unbound in this context.".into(), "Only use `this` inside methods or bound closures.".into())
|
||||
}
|
||||
EvalAltResult::ErrorInModule(name, ..) => (
|
||||
format!("Error while loading module '{}'.", name),
|
||||
"Check the module code for syntax or runtime errors.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorUnboundThis(..) => (
|
||||
"`this` is unbound in this context.".into(),
|
||||
"Only use `this` inside methods or bound closures.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorMismatchDataType(found, expected, ..) => (
|
||||
format!("Data type mismatch: found '{}', expected '{}'.", found, expected),
|
||||
"Convert or cast values to the required type.".into(),
|
||||
@@ -206,9 +230,10 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
|
||||
format!("Cannot index into value of type '{}'.", typ),
|
||||
"Only arrays, maps, bitfields, or strings support indexing.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorArrayBounds(len, idx, ..) => {
|
||||
(format!("Array index {} out of bounds (0..{}).", idx, len), "Use a valid index within the array’s range.".into())
|
||||
}
|
||||
EvalAltResult::ErrorArrayBounds(len, idx, ..) => (
|
||||
format!("Array index {} out of bounds (0..{}).", idx, len),
|
||||
"Use a valid index within the array’s range.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorStringBounds(len, idx, ..) => (
|
||||
format!("String index {} out of bounds (0..{}).", idx, len),
|
||||
"Ensure you index only valid character positions.".into(),
|
||||
@@ -217,47 +242,61 @@ fn get_error_info(root_err: &EvalAltResult, outer_err: &EvalAltResult, engine: &
|
||||
format!("Bitfield index {} out of bounds (0..{}).", idx, len),
|
||||
"Use a valid bit position within the bitfield’s size.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorFor(..) => {
|
||||
("`for` loop value is not iterable.".into(), "Iterate only over arrays, strings, ranges, or iterators.".into())
|
||||
EvalAltResult::ErrorFor(..) => (
|
||||
"`for` loop value is not iterable.".into(),
|
||||
"Iterate only over arrays, strings, ranges, or iterators.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorDataRace(name, ..) => (
|
||||
format!("Data race detected on '{}'.", name),
|
||||
"Avoid shared mutable data or use synchronization primitives.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorAssignmentToConstant(name, ..) => (
|
||||
format!("Cannot assign to constant '{}'.", name),
|
||||
"Constants cannot be reassigned after declaration.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorDotExpr(field, ..) => (
|
||||
format!("Invalid member access '{}'.", field),
|
||||
"Verify the object has this member or method.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorArithmetic(msg, ..) => {
|
||||
("Arithmetic error encountered.".into(), msg.clone())
|
||||
}
|
||||
EvalAltResult::ErrorDataRace(name, ..) => {
|
||||
(format!("Data race detected on '{}'.", name), "Avoid shared mutable data or use synchronization primitives.".into())
|
||||
}
|
||||
EvalAltResult::ErrorAssignmentToConstant(name, ..) => {
|
||||
(format!("Cannot assign to constant '{}'.", name), "Constants cannot be reassigned after declaration.".into())
|
||||
}
|
||||
EvalAltResult::ErrorDotExpr(field, ..) => {
|
||||
(format!("Invalid member access '{}'.", field), "Verify the object has this member or method.".into())
|
||||
}
|
||||
EvalAltResult::ErrorArithmetic(msg, ..) => ("Arithmetic error encountered.".into(), msg.clone()),
|
||||
EvalAltResult::ErrorTooManyOperations(..) => (
|
||||
"Script exceeded the maximum number of operations.".into(),
|
||||
"Break complex expressions into smaller steps or increase the limit.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorTooManyModules(..) => {
|
||||
("Too many modules have been loaded.".into(), "Use fewer modules or increase the module limit.".into())
|
||||
}
|
||||
EvalAltResult::ErrorStackOverflow(..) => {
|
||||
("Call stack overflow detected.".into(), "Check for infinite recursion or deeply nested calls.".into())
|
||||
}
|
||||
EvalAltResult::ErrorDataTooLarge(name, ..) => {
|
||||
(format!("Data '{}' is too large to handle.", name), "Use smaller data sizes or adjust engine limits.".into())
|
||||
}
|
||||
EvalAltResult::ErrorTerminated(..) => {
|
||||
("Script execution was terminated.".into(), "This occurs when a `stop` or external termination is triggered.".into())
|
||||
}
|
||||
EvalAltResult::ErrorCustomSyntax(msg, options, ..) => {
|
||||
(format!("Custom syntax error: {}.", msg), format!("Expected one of: {}.", options.join(", ")))
|
||||
}
|
||||
EvalAltResult::ErrorRuntime(..) => {
|
||||
("Runtime error encountered.".into(), "Inspect the error message and script logic for issues.".into())
|
||||
}
|
||||
EvalAltResult::LoopBreak(..) => {
|
||||
("`break` used outside of a loop.".into(), "Only use `break` inside `for` or `while` loops.".into())
|
||||
}
|
||||
EvalAltResult::Return(..) => {
|
||||
("`return` statement encountered.".into(), "Script terminated with an explicit return value.".into())
|
||||
}
|
||||
EvalAltResult::ErrorTooManyModules(..) => (
|
||||
"Too many modules have been loaded.".into(),
|
||||
"Use fewer modules or increase the module limit.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorStackOverflow(..) => (
|
||||
"Call stack overflow detected.".into(),
|
||||
"Check for infinite recursion or deeply nested calls.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorDataTooLarge(name, ..) => (
|
||||
format!("Data '{}' is too large to handle.", name),
|
||||
"Use smaller data sizes or adjust engine limits.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorTerminated(..) => (
|
||||
"Script execution was terminated.".into(),
|
||||
"This occurs when a `stop` or external termination is triggered.".into(),
|
||||
),
|
||||
EvalAltResult::ErrorCustomSyntax(msg, options, ..) => (
|
||||
format!("Custom syntax error: {}.", msg),
|
||||
format!("Expected one of: {}.", options.join(", ")),
|
||||
),
|
||||
EvalAltResult::ErrorRuntime(..) => (
|
||||
"Runtime error encountered.".into(),
|
||||
"Inspect the error message and script logic for issues.".into(),
|
||||
),
|
||||
EvalAltResult::LoopBreak(..) => (
|
||||
"`break` used outside of a loop.".into(),
|
||||
"Only use `break` inside `for` or `while` loops.".into(),
|
||||
),
|
||||
EvalAltResult::Return(..) => (
|
||||
"`return` statement encountered.".into(),
|
||||
"Script terminated with an explicit return value.".into(),
|
||||
),
|
||||
_ => ("Unknown error".into(), "No additional information available for this error.".into()),
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +73,13 @@ struct TempSignal {
|
||||
fn register_temp_poll_listen(engine: &mut rhai::Engine) {
|
||||
engine.register_type::<TempSignal>();
|
||||
|
||||
engine.register_fn("poll", |var: &str, props: rhai::Map| TempSignal { var: var.to_string(), props });
|
||||
engine.register_fn("poll", |var: &str, props: rhai::Map| TempSignal {
|
||||
var: var.to_string(),
|
||||
props,
|
||||
});
|
||||
|
||||
engine.register_fn("listen", |var: &str, props: rhai::Map| TempSignal { var: var.to_string(), props });
|
||||
engine.register_fn("listen", |var: &str, props: rhai::Map| TempSignal {
|
||||
var: var.to_string(),
|
||||
props,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,21 +21,26 @@ impl ModuleResolver for SimpleFileResolver {
|
||||
|
||||
let base_dir = if let Some(src) = source_path {
|
||||
PathBuf::from(src).parent().map(|p| p.to_path_buf()).unwrap_or(
|
||||
std::env::current_dir().map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))?,
|
||||
std::env::current_dir().map_err(|e| {
|
||||
EvalAltResult::ErrorSystem("getting current_dir".into(), e.into())
|
||||
})?,
|
||||
)
|
||||
} else {
|
||||
std::env::current_dir().map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))?
|
||||
std::env::current_dir()
|
||||
.map_err(|e| EvalAltResult::ErrorSystem("getting current_dir".into(), e.into()))?
|
||||
};
|
||||
|
||||
if !file_path.is_absolute() {
|
||||
file_path = base_dir.join(file_path);
|
||||
}
|
||||
|
||||
let full_path =
|
||||
file_path.canonicalize().map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?;
|
||||
let full_path = file_path
|
||||
.canonicalize()
|
||||
.map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?;
|
||||
|
||||
let script = fs::read_to_string(&full_path)
|
||||
.map_err(|e| EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into()))?;
|
||||
let script = fs::read_to_string(&full_path).map_err(|e| {
|
||||
EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into())
|
||||
})?;
|
||||
|
||||
let ast: AST = engine.compile(&script)?;
|
||||
let scope = Scope::new();
|
||||
@@ -59,7 +64,9 @@ impl<R1: ModuleResolver, R2: ModuleResolver> ModuleResolver for ChainedResolver<
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Rc<Module>, Box<EvalAltResult>> {
|
||||
self.first.resolve(engine, source_path, path, pos).or_else(|_| self.second.resolve(engine, source_path, path, pos))
|
||||
self.first
|
||||
.resolve(engine, source_path, path, pos)
|
||||
.or_else(|_| self.second.resolve(engine, source_path, path, pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,12 @@ impl ParseConfig {
|
||||
Ok(self.engine.compile(code)?)
|
||||
}
|
||||
|
||||
pub fn eval_code_with(&mut self, code: &str, rhai_scope: Option<Scope>, compiled_ast: Option<&AST>) -> Result<WidgetNode> {
|
||||
pub fn eval_code_with(
|
||||
&mut self,
|
||||
code: &str,
|
||||
rhai_scope: Option<Scope>,
|
||||
compiled_ast: Option<&AST>,
|
||||
) -> Result<WidgetNode> {
|
||||
let mut scope = match rhai_scope {
|
||||
Some(s) => s,
|
||||
None => Scope::new(),
|
||||
@@ -55,7 +60,8 @@ impl ParseConfig {
|
||||
}
|
||||
|
||||
pub fn code_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<String> {
|
||||
Ok(fs::read_to_string(&file_path).map_err(|e| anyhow!("Failed to read {:?}: {}", file_path.as_ref(), e))?)
|
||||
Ok(fs::read_to_string(&file_path)
|
||||
.map_err(|e| anyhow!("Failed to read {:?}: {}", file_path.as_ref(), e))?)
|
||||
}
|
||||
|
||||
pub fn initial_poll_listen_scope(code: &str) -> Result<Scope> {
|
||||
|
||||
@@ -15,7 +15,8 @@ pub mod wifi {
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output from nmcli: {e}"))?;
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Invalid UTF-8 output from nmcli: {e}"))?;
|
||||
|
||||
let mut result = Array::new();
|
||||
for line in stdout.lines() {
|
||||
@@ -40,7 +41,8 @@ pub mod wifi {
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run airport: {e}"))?;
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output from airport: {e}"))?;
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Invalid UTF-8 output from airport: {e}"))?;
|
||||
|
||||
let mut result = Array::new();
|
||||
for line in stdout.lines().skip(1) {
|
||||
@@ -100,7 +102,8 @@ pub mod wifi {
|
||||
.args(&["-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"])
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {}", e))?;
|
||||
let stdout =
|
||||
String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 output: {}", e))?;
|
||||
let mut map = Map::new();
|
||||
if let Some(line) = stdout.lines().find(|l| l.starts_with("yes:")) {
|
||||
let parts: Vec<&str> = line.split(':').collect();
|
||||
@@ -144,7 +147,10 @@ pub mod wifi {
|
||||
args.push("password");
|
||||
args.push(pw);
|
||||
}
|
||||
let status = Command::new("nmcli").args(&args).status().map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
let status = Command::new("nmcli")
|
||||
.args(&args)
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -196,7 +202,8 @@ pub mod wifi {
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).map_err(|e| format!("Invalid UTF-8 from nmcli: {e}"))?;
|
||||
let stdout = String::from_utf8(output.stdout)
|
||||
.map_err(|e| format!("Invalid UTF-8 from nmcli: {e}"))?;
|
||||
|
||||
let ssid = stdout
|
||||
.lines()
|
||||
@@ -243,8 +250,10 @@ pub mod wifi {
|
||||
pub fn disable_adapter() -> Result<(), Box<EvalAltResult>> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let status =
|
||||
Command::new("nmcli").args(&["networking", "off"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
let status = Command::new("nmcli")
|
||||
.args(&["networking", "off"])
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -275,8 +284,10 @@ pub mod wifi {
|
||||
pub fn enable_adapter() -> Result<(), Box<EvalAltResult>> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let status =
|
||||
Command::new("nmcli").args(&["networking", "on"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
let status = Command::new("nmcli")
|
||||
.args(&["networking", "on"])
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,9 @@ pub mod env {
|
||||
pub fn get_current_dir() -> Result<String, Box<EvalAltResult>> {
|
||||
std::env::current_dir()
|
||||
.map_err(|e| format!("Failed to get CURRENT DIRECTORY: {e}").into())
|
||||
.and_then(|p| p.into_os_string().into_string().map_err(|_| "Invalid path encoding".into()))
|
||||
.and_then(|p| {
|
||||
p.into_os_string().into_string().map_err(|_| "Invalid path encoding".into())
|
||||
})
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
|
||||
@@ -8,7 +8,9 @@ pub mod json {
|
||||
// parse a JSON string into a Dynamic representing serde_json::Value
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_json(json_str: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
serde_json::from_str::<Value>(json_str).map(Dynamic::from).map_err(|e| format!("Failed to parse JSON: {e}").into())
|
||||
serde_json::from_str::<Value>(json_str)
|
||||
.map(Dynamic::from)
|
||||
.map_err(|e| format!("Failed to parse JSON: {e}").into())
|
||||
}
|
||||
|
||||
// Turn a dyn JSON val back to a string
|
||||
|
||||
@@ -8,7 +8,9 @@ use rhai::exported_module;
|
||||
use rhai::module_resolvers::StaticModuleResolver;
|
||||
|
||||
pub fn register_stdlib(resolver: &mut StaticModuleResolver) {
|
||||
use crate::providers::stdlib::{env::env, json::json, math::math, monitor::monitor, text::text};
|
||||
use crate::providers::stdlib::{
|
||||
env::env, json::json, math::math, monitor::monitor, text::text,
|
||||
};
|
||||
|
||||
// adding modules
|
||||
let text_mod = exported_module!(text);
|
||||
|
||||
@@ -22,7 +22,11 @@ pub mod monitor {
|
||||
}
|
||||
|
||||
pub fn all_resolutions_str() -> String {
|
||||
get_all_monitor_resolutions().into_iter().map(|(w, h)| format!("{w}x{h}")).collect::<Vec<_>>().join(", ")
|
||||
get_all_monitor_resolutions()
|
||||
.into_iter()
|
||||
.map(|(w, h)| format!("{w}x{h}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
pub fn dimensions(index: i64) -> (i64, i64, i64, i64) {
|
||||
|
||||
@@ -5,7 +5,10 @@ pub mod text {
|
||||
pub fn to_slug(text: &str) -> String {
|
||||
let lower = text.to_lowercase();
|
||||
|
||||
let sanitized: String = lower.chars().map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' }).collect();
|
||||
let sanitized: String = lower
|
||||
.chars()
|
||||
.map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' })
|
||||
.collect();
|
||||
|
||||
let words = sanitized.split_whitespace();
|
||||
let slug = words.collect::<Vec<_>>().join("-");
|
||||
@@ -14,7 +17,10 @@ pub mod text {
|
||||
}
|
||||
|
||||
pub fn to_camel_case(text: &str) -> String {
|
||||
let cleaned: String = text.chars().map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' }).collect();
|
||||
let cleaned: String = text
|
||||
.chars()
|
||||
.map(|c| if c.is_alphanumeric() || c.is_whitespace() { c } else { ' ' })
|
||||
.collect();
|
||||
|
||||
let words = cleaned.split_whitespace();
|
||||
|
||||
|
||||
@@ -22,7 +22,12 @@ use tokio::io::BufReader;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::watch;
|
||||
|
||||
pub fn handle_listen(var_name: String, props: Map, store: ReactiveVarStore, tx: tokio::sync::mpsc::UnboundedSender<String>) {
|
||||
pub fn handle_listen(
|
||||
var_name: String,
|
||||
props: Map,
|
||||
store: ReactiveVarStore,
|
||||
tx: tokio::sync::mpsc::UnboundedSender<String>,
|
||||
) {
|
||||
let cmd = match get_string_prop(&props, "cmd", Some("")) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
|
||||
@@ -27,9 +27,13 @@ use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio::sync::watch;
|
||||
|
||||
pub type ReactiveVarStore = Arc<RwLock<HashMap<String, String>>>;
|
||||
pub static SHUTDOWN_REGISTRY: Lazy<Mutex<Vec<watch::Sender<bool>>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||
pub static SHUTDOWN_REGISTRY: Lazy<Mutex<Vec<watch::Sender<bool>>>> =
|
||||
Lazy::new(|| Mutex::new(Vec::new()));
|
||||
|
||||
pub fn handle_state_changes(enter_node: WidgetNode, tx: UnboundedSender<String>) -> ReactiveVarStore {
|
||||
pub fn handle_state_changes(
|
||||
enter_node: WidgetNode,
|
||||
tx: UnboundedSender<String>,
|
||||
) -> ReactiveVarStore {
|
||||
// Enter node is the WidgetNode of Enter()
|
||||
// it is the very root of every config.
|
||||
let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new()));
|
||||
|
||||
@@ -22,7 +22,12 @@ use tokio::process::Command;
|
||||
use tokio::sync::watch;
|
||||
use tokio::time::sleep;
|
||||
|
||||
pub fn handle_poll(var_name: String, props: Map, store: ReactiveVarStore, tx: tokio::sync::mpsc::UnboundedSender<String>) {
|
||||
pub fn handle_poll(
|
||||
var_name: String,
|
||||
props: Map,
|
||||
store: ReactiveVarStore,
|
||||
tx: tokio::sync::mpsc::UnboundedSender<String>,
|
||||
) {
|
||||
// Parse polling interval
|
||||
let interval = get_duration_prop(&props, "interval", Some(Duration::from_secs(1)));
|
||||
let interval = interval.expect("Error parsing interval property of poll");
|
||||
|
||||
@@ -188,7 +188,13 @@ fn insert_wdgt_info(
|
||||
id_to_info: &mut HashMap<u64, WidgetInfo>,
|
||||
) -> Result<()> {
|
||||
let id = hash_props_and_type(props, widget_type);
|
||||
let info = WidgetInfo { node: node.clone(), props: props.clone(), widget_type: widget_type.to_string(), children, parent_id };
|
||||
let info = WidgetInfo {
|
||||
node: node.clone(),
|
||||
props: props.clone(),
|
||||
widget_type: widget_type.to_string(),
|
||||
children,
|
||||
parent_id,
|
||||
};
|
||||
id_to_info.insert(id, info);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ pub trait Host {
|
||||
/// removed items.
|
||||
pub async fn register_as_host(
|
||||
con: &zbus::Connection,
|
||||
) -> zbus::Result<(zbus::names::WellKnownName<'static>, proxy::StatusNotifierWatcherProxy<'static>)> {
|
||||
) -> zbus::Result<(zbus::names::WellKnownName<'static>, proxy::StatusNotifierWatcherProxy<'static>)>
|
||||
{
|
||||
let snw = proxy::StatusNotifierWatcherProxy::new(con).await?;
|
||||
|
||||
// get a well-known name
|
||||
@@ -39,14 +40,17 @@ pub async fn register_as_host(
|
||||
|
||||
i += 1;
|
||||
let wellknown = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i);
|
||||
let wellknown: zbus::names::WellKnownName = wellknown.try_into().expect("generated well-known name is invalid");
|
||||
let wellknown: zbus::names::WellKnownName =
|
||||
wellknown.try_into().expect("generated well-known name is invalid");
|
||||
|
||||
let flags = [zbus::fdo::RequestNameFlags::DoNotQueue];
|
||||
match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? {
|
||||
PrimaryOwner => break wellknown,
|
||||
Exists => {}
|
||||
AlreadyOwner => {}
|
||||
InQueue => unreachable!("request_name_with_flags returned InQueue even though we specified DoNotQueue"),
|
||||
InQueue => unreachable!(
|
||||
"request_name_with_flags returned InQueue even though we specified DoNotQueue"
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -64,7 +68,10 @@ pub async fn register_as_host(
|
||||
/// This async function runs forever, and only returns if it gets an error! As such, it is
|
||||
/// recommended to call this via something like `tokio::spawn` that runs this in the
|
||||
/// background.
|
||||
pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherProxy<'static>) -> zbus::Error {
|
||||
pub async fn run_host(
|
||||
host: &mut dyn Host,
|
||||
snw: &proxy::StatusNotifierWatcherProxy<'static>,
|
||||
) -> zbus::Error {
|
||||
// Replacement for ? operator since we're not returning a Result.
|
||||
macro_rules! try_ {
|
||||
($e:expr) => {
|
||||
@@ -116,7 +123,11 @@ pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherPro
|
||||
host.add_item(svc, item);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Could not create StatusNotifierItem from address {:?}: {:?}", svc, e);
|
||||
log::warn!(
|
||||
"Could not create StatusNotifierItem from address {:?}: {:?}",
|
||||
svc,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ enum IconError {
|
||||
/// Get the fallback GTK icon, as a final fallback if the tray item has no icon.
|
||||
async fn fallback_icon(size: i32, scale: i32) -> Option<gtk::gdk_pixbuf::Pixbuf> {
|
||||
let theme = gtk::IconTheme::default().expect("Could not get default gtk theme");
|
||||
match theme.load_icon_for_scale("image-missing", size, scale, gtk::IconLookupFlags::FORCE_SIZE) {
|
||||
match theme.load_icon_for_scale("image-missing", size, scale, gtk::IconLookupFlags::FORCE_SIZE)
|
||||
{
|
||||
Ok(pb) => pb,
|
||||
Err(e) => {
|
||||
log::error!("failed to load \"image-missing\" from default theme: {}", e);
|
||||
@@ -70,7 +71,10 @@ fn icon_from_pixmap(width: i32, height: i32, mut data: Vec<u8>) -> gtk::gdk_pixb
|
||||
/// From a list of pixmaps, create an icon from the most appropriately sized one.
|
||||
///
|
||||
/// This function returns None if and only if no pixmaps are provided.
|
||||
fn icon_from_pixmaps(pixmaps: Vec<(i32, i32, Vec<u8>)>, size: i32) -> Option<gtk::gdk_pixbuf::Pixbuf> {
|
||||
fn icon_from_pixmaps(
|
||||
pixmaps: Vec<(i32, i32, Vec<u8>)>,
|
||||
size: i32,
|
||||
) -> Option<gtk::gdk_pixbuf::Pixbuf> {
|
||||
pixmaps
|
||||
.into_iter()
|
||||
.max_by(|(w1, h1, _), (w2, h2, _)| {
|
||||
@@ -190,7 +194,9 @@ pub async fn load_icon_from_sni(
|
||||
},
|
||||
Err(zbus::Error::FDO(e)) => match *e {
|
||||
// property not existing is an expected error
|
||||
zbus::fdo::Error::UnknownProperty(_) | zbus::fdo::Error::InvalidArgs(_) => Err(IconError::NotAvailable),
|
||||
zbus::fdo::Error::UnknownProperty(_) | zbus::fdo::Error::InvalidArgs(_) => {
|
||||
Err(IconError::NotAvailable)
|
||||
}
|
||||
|
||||
_ => Err(IconError::DBusPixmap(zbus::Error::FDO(e))),
|
||||
},
|
||||
|
||||
@@ -65,16 +65,24 @@ impl Item {
|
||||
} else if service.starts_with(':') {
|
||||
(
|
||||
service.to_owned(),
|
||||
resolve_pathless_address(con, service, "/".to_owned())
|
||||
.await?
|
||||
.ok_or_else(|| zbus::Error::Failure(format!("no StatusNotifierItem found for {service}")))?,
|
||||
resolve_pathless_address(con, service, "/".to_owned()).await?.ok_or_else(
|
||||
|| {
|
||||
zbus::Error::Failure(format!(
|
||||
"no StatusNotifierItem found for {service}"
|
||||
))
|
||||
},
|
||||
)?,
|
||||
)
|
||||
} else {
|
||||
return Err(zbus::Error::Address(service.to_owned()));
|
||||
}
|
||||
};
|
||||
|
||||
let sni = proxy::StatusNotifierItemProxy::builder(con).destination(addr)?.path(path)?.build().await?;
|
||||
let sni = proxy::StatusNotifierItemProxy::builder(con)
|
||||
.destination(addr)?
|
||||
.path(path)?
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
Ok(Self { sni, gtk_menu: None })
|
||||
}
|
||||
@@ -95,7 +103,12 @@ impl Item {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn popup_menu(&self, event: >k::gdk::EventButton, x: i32, y: i32) -> zbus::Result<()> {
|
||||
pub async fn popup_menu(
|
||||
&self,
|
||||
event: >k::gdk::EventButton,
|
||||
x: i32,
|
||||
y: i32,
|
||||
) -> zbus::Result<()> {
|
||||
if let Some(menu) = &self.gtk_menu {
|
||||
menu.popup_at_pointer(event.downcast_ref::<gtk::gdk::Event>());
|
||||
Ok(())
|
||||
@@ -131,12 +144,21 @@ struct DBusInterface {
|
||||
name: String,
|
||||
}
|
||||
|
||||
async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: String) -> zbus::Result<Option<String>> {
|
||||
let introspection_xml =
|
||||
IntrospectableProxy::builder(con).destination(service)?.path(path.as_str())?.build().await?.introspect().await?;
|
||||
async fn resolve_pathless_address(
|
||||
con: &zbus::Connection,
|
||||
service: &str,
|
||||
path: String,
|
||||
) -> zbus::Result<Option<String>> {
|
||||
let introspection_xml = IntrospectableProxy::builder(con)
|
||||
.destination(service)?
|
||||
.path(path.as_str())?
|
||||
.build()
|
||||
.await?
|
||||
.introspect()
|
||||
.await?;
|
||||
|
||||
let dbus_node =
|
||||
quick_xml::de::from_str::<DBusNode>(&introspection_xml).map_err(|err| zbus::Error::Failure(err.to_string()))?;
|
||||
let dbus_node = quick_xml::de::from_str::<DBusNode>(&introspection_xml)
|
||||
.map_err(|err| zbus::Error::Failure(err.to_string()))?;
|
||||
|
||||
if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") {
|
||||
// This item implements the desired interface, so bubble it back up
|
||||
@@ -150,7 +172,9 @@ async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: S
|
||||
return Ok(Some(join_to_path(&path, name)));
|
||||
}
|
||||
|
||||
let path = Box::pin(resolve_pathless_address(con, service, join_to_path(&path, name))).await?;
|
||||
let path =
|
||||
Box::pin(resolve_pathless_address(con, service, join_to_path(&path, name)))
|
||||
.await?;
|
||||
|
||||
if path.is_some() {
|
||||
// Return the first item found from a child
|
||||
|
||||
@@ -75,7 +75,8 @@ impl Watcher {
|
||||
};
|
||||
|
||||
if removed_last {
|
||||
if let Err(e) = Watcher::is_status_notifier_host_registered_refresh(&ctxt).await {
|
||||
if let Err(e) = Watcher::is_status_notifier_host_registered_refresh(&ctxt).await
|
||||
{
|
||||
log::error!("failed to signal Watcher: {}", e);
|
||||
}
|
||||
}
|
||||
@@ -149,7 +150,9 @@ impl Watcher {
|
||||
if let Err(e) = Watcher::registered_status_notifier_items_refresh(&ctxt).await {
|
||||
log::error!("failed to signal Watcher: {}", e);
|
||||
}
|
||||
if let Err(e) = Watcher::status_notifier_item_unregistered(&ctxt, item.as_ref()).await {
|
||||
if let Err(e) =
|
||||
Watcher::status_notifier_item_unregistered(&ctxt, item.as_ref()).await
|
||||
{
|
||||
log::error!("failed to signal Watcher: {}", e);
|
||||
}
|
||||
}
|
||||
@@ -160,11 +163,17 @@ impl Watcher {
|
||||
|
||||
/// StatusNotifierItemRegistered signal
|
||||
#[dbus_interface(signal)]
|
||||
async fn status_notifier_item_registered(ctxt: &zbus::SignalContext<'_>, service: &str) -> zbus::Result<()>;
|
||||
async fn status_notifier_item_registered(
|
||||
ctxt: &zbus::SignalContext<'_>,
|
||||
service: &str,
|
||||
) -> zbus::Result<()>;
|
||||
|
||||
/// StatusNotifierItemUnregistered signal
|
||||
#[dbus_interface(signal)]
|
||||
async fn status_notifier_item_unregistered(ctxt: &zbus::SignalContext<'_>, service: &str) -> zbus::Result<()>;
|
||||
async fn status_notifier_item_unregistered(
|
||||
ctxt: &zbus::SignalContext<'_>,
|
||||
service: &str,
|
||||
) -> zbus::Result<()>;
|
||||
|
||||
/// RegisteredStatusNotifierItems property
|
||||
#[dbus_interface(property)]
|
||||
@@ -208,7 +217,9 @@ impl Watcher {
|
||||
|
||||
/// Equivalent to `is_status_notifier_host_registered_invalidate`, but without requiring
|
||||
/// `self`.
|
||||
async fn is_status_notifier_host_registered_refresh(ctxt: &zbus::SignalContext<'_>) -> zbus::Result<()> {
|
||||
async fn is_status_notifier_host_registered_refresh(
|
||||
ctxt: &zbus::SignalContext<'_>,
|
||||
) -> zbus::Result<()> {
|
||||
zbus::fdo::Properties::properties_changed(
|
||||
ctxt,
|
||||
Self::name(),
|
||||
@@ -219,7 +230,9 @@ impl Watcher {
|
||||
}
|
||||
|
||||
/// Equivalent to `registered_status_notifier_items_invalidate`, but without requiring `self`.
|
||||
async fn registered_status_notifier_items_refresh(ctxt: &zbus::SignalContext<'_>) -> zbus::Result<()> {
|
||||
async fn registered_status_notifier_items_refresh(
|
||||
ctxt: &zbus::SignalContext<'_>,
|
||||
) -> zbus::Result<()> {
|
||||
zbus::fdo::Properties::properties_changed(
|
||||
ctxt,
|
||||
Self::name(),
|
||||
@@ -279,7 +292,10 @@ async fn parse_service<'a>(
|
||||
}
|
||||
|
||||
/// Wait for a DBus service to disappear
|
||||
async fn wait_for_service_exit(con: &zbus::Connection, service: zbus::names::BusName<'_>) -> zbus::fdo::Result<()> {
|
||||
async fn wait_for_service_exit(
|
||||
con: &zbus::Connection,
|
||||
service: zbus::names::BusName<'_>,
|
||||
) -> zbus::fdo::Result<()> {
|
||||
let dbus = zbus::fdo::DBusProxy::new(con).await?;
|
||||
let mut owner_changes = dbus.receive_name_owner_changed_with_args(&[(0, &service)]).await?;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use_small_heuristics = "Max"
|
||||
max_width = 130
|
||||
max_width = 100
|
||||
use_field_init_shorthand = true
|
||||
|
||||
# these where set when we where still on nightly
|
||||
|
||||
Reference in New Issue
Block a user