feat: reduced max_width to 100 and ran cargo fmt
This commit is contained in:
@@ -3,11 +3,16 @@ use crate::{
|
|||||||
daemon_response::DaemonResponseSender,
|
daemon_response::DaemonResponseSender,
|
||||||
display_backend::DisplayBackend,
|
display_backend::DisplayBackend,
|
||||||
error_handling_ctx,
|
error_handling_ctx,
|
||||||
gtk::prelude::{ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt},
|
gtk::prelude::{
|
||||||
|
ContainerExt, CssProviderExt, GtkWindowExt, MonitorExt, StyleContextExt, WidgetExt,
|
||||||
|
},
|
||||||
paths::EwwPaths,
|
paths::EwwPaths,
|
||||||
widgets::window::Window,
|
widgets::window::Window,
|
||||||
// dynval::DynVal,
|
// 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::{
|
window::{
|
||||||
coords::Coords,
|
coords::Coords,
|
||||||
monitor::MonitorIdentifier,
|
monitor::MonitorIdentifier,
|
||||||
@@ -139,8 +144,8 @@ async fn wait_for_monitor_model() {
|
|||||||
let display = gdk::Display::default().expect("could not get default display");
|
let display = gdk::Display::default().expect("could not get default display");
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
loop {
|
loop {
|
||||||
let all_monitors_set =
|
let all_monitors_set = (0..display.n_monitors())
|
||||||
(0..display.n_monitors()).all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some());
|
.all(|i| display.monitor(i).and_then(|monitor| monitor.model()).is_some());
|
||||||
if all_monitors_set {
|
if all_monitors_set {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -241,13 +246,24 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
let result = if should_toggle && is_open {
|
let result = if should_toggle && is_open {
|
||||||
self.close_window(&instance_id, false)
|
self.close_window(&instance_id, false)
|
||||||
} else {
|
} 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)?;
|
sender.respond_with_result(result)?;
|
||||||
}
|
}
|
||||||
DaemonCommand::CloseWindows { windows, auto_reopen, sender } => {
|
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
|
// Ignore sending errors, as the channel might already be closed
|
||||||
let _ = sender.respond_with_error_list(errors);
|
let _ = sender.respond_with_error_list(errors);
|
||||||
}
|
}
|
||||||
@@ -256,7 +272,11 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
sender.send_success(output)?
|
sender.send_success(output)?
|
||||||
}
|
}
|
||||||
DaemonCommand::ListActiveWindows(sender) => {
|
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)?
|
sender.send_success(output)?
|
||||||
}
|
}
|
||||||
DaemonCommand::PrintDebug(sender) => {
|
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) {
|
if let Some(old_abort_send) = self.window_close_timer_abort_senders.remove(instance_id) {
|
||||||
_ = old_abort_send.send(());
|
_ = old_abort_send.send(());
|
||||||
}
|
}
|
||||||
let ewwii_window = self
|
let ewwii_window = self.open_windows.remove(instance_id).with_context(|| {
|
||||||
.open_windows
|
format!("Tried to close window with id '{instance_id}', but no such window was open")
|
||||||
.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;
|
// let scope_index = ewwii_window.scope_index;
|
||||||
ewwii_window.close();
|
ewwii_window.close();
|
||||||
@@ -324,7 +343,10 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
let window_name: &str = &window_args.window_name;
|
let window_name: &str = &window_args.window_name;
|
||||||
|
|
||||||
let window_def = self.ewwii_config.get_window(window_name)?.clone();
|
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)?;
|
let initiator = WindowInitiator::new(&window_def, window_args)?;
|
||||||
|
|
||||||
@@ -332,7 +354,8 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
// It is critical for supporting dynamic updates
|
// It is critical for supporting dynamic updates
|
||||||
let mut widget_reg_store = WidgetRegistry::new(Some(&window_def.root_widget));
|
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);
|
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 (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||||
let config_path = self.paths.get_rhai_path();
|
let config_path = self.paths.get_rhai_path();
|
||||||
let compiled_ast = self.ewwii_config.get_owned_compiled_ast();
|
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 {
|
glib::MainContext::default().spawn_local(async move {
|
||||||
while let Some(var_name) = rx.recv().await {
|
while let Some(var_name) = rx.recv().await {
|
||||||
log::debug!("Received update for var: {}", var_name);
|
log::debug!("Received update for var: {}", var_name);
|
||||||
let vars = store.read().unwrap().clone();
|
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) => {
|
Ok(new_widget) => {
|
||||||
let _ = widget_reg_store.update_widget_tree(new_widget);
|
let _ = widget_reg_store.update_widget_tree(new_widget);
|
||||||
}
|
}
|
||||||
@@ -375,8 +400,11 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
// becomes available again
|
// becomes available again
|
||||||
move |auto_reopen| {
|
move |auto_reopen| {
|
||||||
let (response_sender, _) = daemon_response::create_pair();
|
let (response_sender, _) = daemon_response::create_pair();
|
||||||
let command =
|
let command = DaemonCommand::CloseWindows {
|
||||||
DaemonCommand::CloseWindows { windows: vec![instance_id.clone()], auto_reopen, sender: response_sender };
|
windows: vec![instance_id.clone()],
|
||||||
|
auto_reopen,
|
||||||
|
sender: response_sender,
|
||||||
|
};
|
||||||
if let Err(err) = app_evt_sender.send(command) {
|
if let Err(err) = app_evt_sender.send(command) {
|
||||||
log::error!("Error sending close window command: {}", err);
|
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));
|
let closed_by_user = Rc::new(Cell::new(false));
|
||||||
|
|
||||||
// handling users close request
|
// handling users close request
|
||||||
ewwii_window.delete_event_handler_id = Some(ewwii_window.gtk_window.connect_delete_event({
|
ewwii_window.delete_event_handler_id =
|
||||||
let handler = gtk_close_handler.clone();
|
Some(ewwii_window.gtk_window.connect_delete_event({
|
||||||
let closed_by_user = closed_by_user.clone();
|
let handler = gtk_close_handler.clone();
|
||||||
move |_, _| {
|
let closed_by_user = closed_by_user.clone();
|
||||||
handler(false); // -- false: don't reopen window to respect users intent
|
move |_, _| {
|
||||||
closed_by_user.set(true);
|
handler(false); // -- false: don't reopen window to respect users intent
|
||||||
glib::Propagation::Proceed
|
closed_by_user.set(true);
|
||||||
}
|
glib::Propagation::Proceed
|
||||||
}));
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
// handling destory request
|
// handling destory request
|
||||||
ewwii_window.destroy_event_handler_id = Some(ewwii_window.gtk_window.connect_destroy({
|
ewwii_window.destroy_event_handler_id =
|
||||||
let handler = gtk_close_handler.clone();
|
Some(ewwii_window.gtk_window.connect_destroy({
|
||||||
let closed_by_user = closed_by_user.clone();
|
let handler = gtk_close_handler.clone();
|
||||||
move |_| {
|
let closed_by_user = closed_by_user.clone();
|
||||||
if !closed_by_user.get() {
|
move |_| {
|
||||||
handler(true);
|
if !closed_by_user.get() {
|
||||||
|
handler(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
let duration = window_args.duration;
|
let duration = window_args.duration;
|
||||||
if let Some(duration) = 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(());
|
_ = old_abort_send.send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,8 +487,13 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
|
|
||||||
self.ewwii_config = config;
|
self.ewwii_config = config;
|
||||||
|
|
||||||
let open_window_ids: Vec<String> =
|
let open_window_ids: Vec<String> = self
|
||||||
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
|
.open_windows
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.chain(self.failed_windows.iter().cloned())
|
||||||
|
.dedup()
|
||||||
|
.collect();
|
||||||
for instance_id in &open_window_ids {
|
for instance_id in &open_window_ids {
|
||||||
let window_arguments = self.instance_id_to_args.get(instance_id).with_context(|| {
|
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}")
|
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
|
/// 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<()> {
|
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()) {
|
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 nice_error_option: Option<_> = (|| {
|
||||||
let captures = PATTERN.captures(err.message())?;
|
let captures = PATTERN.captures(err.message())?;
|
||||||
let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?;
|
let line = captures.get(1).unwrap().as_str().parse::<usize>().ok()?;
|
||||||
@@ -501,8 +540,9 @@ fn initialize_window<B: DisplayBackend>(
|
|||||||
}
|
}
|
||||||
_ => (None, 0, 0),
|
_ => (None, 0, 0),
|
||||||
};
|
};
|
||||||
let window = B::initialize_window(window_init, monitor_geometry, x, y)
|
let window = B::initialize_window(window_init, monitor_geometry, x, y).with_context(|| {
|
||||||
.with_context(|| format!("monitor {} is unavailable", window_init.monitor.clone().unwrap()))?;
|
format!("monitor {} is unavailable", window_init.monitor.clone().unwrap())
|
||||||
|
})?;
|
||||||
|
|
||||||
window.set_title(&format!("Ewwii - {}", window_init.name));
|
window.set_title(&format!("Ewwii - {}", window_init.name));
|
||||||
window.set_position(gtk::WindowPosition::None);
|
window.set_position(gtk::WindowPosition::None);
|
||||||
@@ -528,7 +568,9 @@ fn initialize_window<B: DisplayBackend>(
|
|||||||
if B::IS_X11 {
|
if B::IS_X11 {
|
||||||
if let Some(geometry) = window_init.geometry {
|
if let Some(geometry) = window_init.geometry {
|
||||||
let _ = apply_window_position(geometry, monitor_geometry, &window);
|
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));
|
let last_pos = Rc::new(RefCell::new(None));
|
||||||
window.connect_configure_event({
|
window.connect_configure_event({
|
||||||
let last_pos = last_pos.clone();
|
let last_pos = last_pos.clone();
|
||||||
@@ -583,7 +625,11 @@ async fn generate_new_widgetnode(
|
|||||||
|
|
||||||
/// Apply the provided window-positioning rules to the window.
|
/// Apply the provided window-positioning rules to the window.
|
||||||
#[cfg(feature = "x11")]
|
#[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")?;
|
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());
|
window_geometry.size = crate::window::window_geometry::Coords::from_pixels(window.size());
|
||||||
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
|
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>) {
|
fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) {
|
||||||
let visual = gtk::prelude::GtkWindowExt::screen(window)
|
let visual = gtk::prelude::GtkWindowExt::screen(window).and_then(|screen| {
|
||||||
.and_then(|screen| screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual()));
|
screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual())
|
||||||
|
});
|
||||||
window.set_visual(visual.as_ref());
|
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> {
|
fn get_monitor_plug_name(display: &gdk::Display, monitor_num: i32) -> Option<&str> {
|
||||||
unsafe {
|
unsafe {
|
||||||
use glib::translate::ToGlibPtr;
|
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;
|
use std::ffi::CStr;
|
||||||
CStr::from_ptr(plug_name_pointer).to_str().ok()
|
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.
|
/// Returns the [Monitor][gdk::Monitor] structure corresponding to the identifer.
|
||||||
/// Outside of x11, only [MonitorIdentifier::Numeric] is supported
|
/// 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 {
|
match identifier {
|
||||||
MonitorIdentifier::List(list) => {
|
MonitorIdentifier::List(list) => {
|
||||||
for ident in 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 {
|
pub fn get_window_rectangle(
|
||||||
let (offset_x, offset_y) = geometry.offset.relative_to(screen_rect.width(), screen_rect.height());
|
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 (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 x = screen_rect.x()
|
||||||
let y = screen_rect.y() + offset_y + geometry.anchor_point.y.alignment_to_coordinate(height, screen_rect.height());
|
+ 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)
|
gdk::Rectangle::new(x, y, width, height)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use anyhow::{Context, Result};
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::sync::broadcast;
|
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.
|
/// Notify all listening tasks of the termination of the eww application process.
|
||||||
pub fn send_exit() -> Result<()> {
|
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.
|
/// 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.
|
/// 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");
|
log::debug!("Forwarding options to server");
|
||||||
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
stream.set_nonblocking(false).context("Failed to set stream to non-blocking")?;
|
||||||
|
|
||||||
let message_bytes = bincode::serialize(&action)?;
|
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")?;
|
stream.write_all(&message_bytes).context("Failed to write command to IPC stream")?;
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
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")?;
|
stream.read_to_end(&mut buf).context("Error reading response from server")?;
|
||||||
|
|
||||||
Ok(if buf.is_empty() {
|
Ok(if buf.is_empty() {
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ impl EwwiiConfig {
|
|||||||
// get the iirhai widget tree
|
// get the iirhai widget tree
|
||||||
let compiled_ast = config_parser.compile_code(&rhai_code)?;
|
let compiled_ast = config_parser.compile_code(&rhai_code)?;
|
||||||
let poll_listen_scope = ParseConfig::initial_poll_listen_scope(&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();
|
let mut window_definitions = HashMap::new();
|
||||||
|
|
||||||
@@ -76,7 +80,11 @@ impl EwwiiConfig {
|
|||||||
bail!("Expected root node to be `Enter`, but got something else.");
|
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> {
|
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 css_file = path.join("ewwii.css");
|
||||||
let scss_file = path.join("ewwii.scss");
|
let scss_file = path.join("ewwii.scss");
|
||||||
if css_file.exists() && scss_file.exists() {
|
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() {
|
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);
|
let css = replace_env_var_references(css_file_content);
|
||||||
(css_file, css)
|
(css_file, css)
|
||||||
} else {
|
} else {
|
||||||
let scss_file_content =
|
let scss_file_content = std::fs::read_to_string(&scss_file)
|
||||||
std::fs::read_to_string(&scss_file).with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?;
|
.with_context(|| format!("Given SCSS file doesn't exist! {}", path.display()))?;
|
||||||
let file_content = replace_env_var_references(scss_file_content);
|
let file_content = replace_env_var_references(scss_file_content);
|
||||||
let grass_config = grass::Options::default().load_path(path);
|
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)
|
(scss_file, css)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,15 +28,22 @@ pub fn create_pair() -> (DaemonResponseSender, mpsc::UnboundedReceiver<DaemonRes
|
|||||||
|
|
||||||
impl DaemonResponseSender {
|
impl DaemonResponseSender {
|
||||||
pub fn send_success(&self, s: String) -> Result<()> {
|
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<()> {
|
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.
|
/// 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");
|
let errors = errors.into_iter().map(|e| error_handling_ctx::format_error(&e)).join("\n");
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
self.send_success(String::new())
|
self.send_success(String::new())
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ pub trait DisplayBackend: Send + Sync + 'static {
|
|||||||
const IS_X11: bool;
|
const IS_X11: bool;
|
||||||
const IS_WAYLAND: 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;
|
pub struct NoBackend;
|
||||||
@@ -21,7 +26,12 @@ impl DisplayBackend for NoBackend {
|
|||||||
const IS_X11: bool = false;
|
const IS_X11: bool = false;
|
||||||
const IS_WAYLAND: 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))
|
Some(Window::new(gtk::WindowType::Toplevel, x, y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +53,12 @@ mod platform_wayland {
|
|||||||
const IS_X11: bool = false;
|
const IS_X11: bool = false;
|
||||||
const IS_WAYLAND: bool = true;
|
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);
|
let window = Window::new(gtk::WindowType::Toplevel, x, y);
|
||||||
// Initialising a layer shell surface
|
// Initialising a layer shell surface
|
||||||
window.init_layer_shell();
|
window.init_layer_shell();
|
||||||
@@ -155,9 +170,17 @@ mod platform_x11 {
|
|||||||
const IS_X11: bool = true;
|
const IS_X11: bool = true;
|
||||||
const IS_WAYLAND: 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(
|
||||||
let window_type =
|
window_init: &WindowInitiator,
|
||||||
if window_init.backend_options.x11.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel };
|
_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);
|
let window = Window::new(window_type, x, y);
|
||||||
window.set_resizable(window_init.resizable);
|
window.set_resizable(window_init.resizable);
|
||||||
window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
|
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()?;
|
let backend = X11BackendConnection::new()?;
|
||||||
backend.set_xprops_for(window, monitor, window_init)?;
|
backend.set_xprops_for(window, monitor, window_init)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -191,12 +218,19 @@ mod platform_x11 {
|
|||||||
Ok(X11BackendConnection { conn, root_window: screen.root, atoms })
|
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 monitor_rect = monitor.geometry();
|
||||||
let scale_factor = monitor.scale_factor() as u32;
|
let scale_factor = monitor.scale_factor() as u32;
|
||||||
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
|
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
|
||||||
let win_id =
|
let win_id = gdk_window
|
||||||
gdk_window.downcast_ref::<gdkx11::X11Window>().context("Failed to get x11 window for gtk window")?.xid() as u32;
|
.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 strut_def = window_init.backend_options.x11.struts;
|
||||||
let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;
|
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 mon_end_y = scale_factor * (monitor_rect.y() + monitor_rect.height()) as u32 - 1u32;
|
||||||
|
|
||||||
let dist = match strut_def.side {
|
let dist = match strut_def.side {
|
||||||
Side::Left | Side::Right => strut_def.distance.pixels_relative_to(monitor_rect.width()) as u32,
|
Side::Left | Side::Right => {
|
||||||
Side::Top | Side::Bottom => strut_def.distance.pixels_relative_to(monitor_rect.height()) as u32,
|
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,.....
|
// don't question it,.....
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ pub struct ConversionError {
|
|||||||
pub struct DurationParseError;
|
pub struct DurationParseError;
|
||||||
|
|
||||||
impl ConversionError {
|
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)) }
|
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 {
|
impl From<Vec<DynVal>> for DynVal {
|
||||||
fn from(v: Vec<DynVal>) -> Self {
|
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<_>>();
|
let elements = v.into_iter().map(|x| x.as_string().unwrap()).collect::<Vec<_>>();
|
||||||
DynVal(serde_json::to_string(&elements).unwrap(), span)
|
DynVal(serde_json::to_string(&elements).unwrap(), span)
|
||||||
}
|
}
|
||||||
@@ -174,10 +182,15 @@ impl DynVal {
|
|||||||
let s = &self.0;
|
let s = &self.0;
|
||||||
if s.ends_with("ms") {
|
if s.ends_with("ms") {
|
||||||
Ok(Duration::from_millis(
|
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') {
|
} 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))
|
Ok(Duration::from_millis(f64::floor(secs * 1000f64) as u64))
|
||||||
} else if s.ends_with('m') || s.ends_with("min") {
|
} else if s.ends_with('m') || s.ends_with("min") {
|
||||||
let minutes = s
|
let minutes = s
|
||||||
@@ -187,12 +200,19 @@ impl DynVal {
|
|||||||
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
.map_err(|e| ConversionError::new(self.clone(), "number", e))?;
|
||||||
Ok(Duration::from_secs(f64::floor(minutes * 60f64) as u64))
|
Ok(Duration::from_secs(f64::floor(minutes * 60f64) as u64))
|
||||||
} else if s.ends_with('h') {
|
} 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))
|
Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64))
|
||||||
} else if let Ok(millis) = s.parse() {
|
} else if let Ok(millis) = s.parse() {
|
||||||
Ok(Duration::from_millis(millis))
|
Ok(Duration::from_millis(millis))
|
||||||
} else {
|
} 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 ewwii_shared_util::Span;
|
||||||
use once_cell::sync::Lazy;
|
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() {
|
// pub fn clear_files() {
|
||||||
// *FILE_DATABASE.write().unwrap() = FileDatabase::new();
|
// *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 {
|
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
|
// * 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> {
|
pub fn stringify_diagnostic(
|
||||||
diagnostic.labels.retain(|label| !Span(label.range.start, label.range.end, label.file_id).is_dummy());
|
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 config = term::Config::default();
|
||||||
let mut chars = Chars::box_drawing();
|
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> {
|
pub fn insert_string(&mut self, name: String, content: String) -> Result<usize, DiagError> {
|
||||||
let line_starts = codespan_reporting::files::line_starts(&content).collect();
|
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);
|
let file_id = self.insert_code_file(code_file);
|
||||||
Ok(file_id)
|
Ok(file_id)
|
||||||
}
|
}
|
||||||
@@ -55,12 +60,23 @@ impl<'a> Files<'a> for FileDatabase {
|
|||||||
Ok(&self.get_file(id)?.name)
|
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)
|
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> {
|
fn line_index(
|
||||||
Ok(self.get_file(id)?.line_starts.binary_search(&byte_index).unwrap_or_else(|next_line| next_line - 1))
|
&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(
|
fn line_range(
|
||||||
@@ -90,11 +106,16 @@ impl CodeFile {
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
match line_index.cmp(&self.line_starts.len()) {
|
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::Equal => Ok(self.source_len_bytes),
|
||||||
Ordering::Greater => {
|
Ordering::Greater => Err(codespan_reporting::files::Error::LineTooLarge {
|
||||||
Err(codespan_reporting::files::Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1 })
|
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.
|
/// 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 (mut stream_read, mut stream_write) = stream.split();
|
||||||
|
|
||||||
let action: opts::ActionWithServer = read_ewwii_action_from_stream(&mut stream_read).await?;
|
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 {
|
if let Some(mut response_recv) = maybe_response_recv {
|
||||||
log::debug!("Waiting for response for IPC client");
|
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 response = bincode::serialize(&response)?;
|
||||||
let result = &stream_write.write_all(&response).await;
|
let result = &stream_write.write_all(&response).await;
|
||||||
crate::print_result_err!("sending text response to ipc client", &result);
|
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`
|
/// 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.
|
/// 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];
|
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 message_byte_length = u32::from_be_bytes(message_byte_length);
|
||||||
let mut raw_message = Vec::<u8>::with_capacity(message_byte_length as usize);
|
let mut raw_message = Vec::<u8>::with_capacity(message_byte_length as usize);
|
||||||
while raw_message.len() < 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")
|
bincode::deserialize(&raw_message).context("Failed to parse client message")
|
||||||
|
|||||||
@@ -83,7 +83,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let opts::Action::ShellCompletions { shell } = opts.action {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,10 +97,18 @@ fn main() {
|
|||||||
let use_wayland = opts.force_wayland || detected_wayland;
|
let use_wayland = opts.force_wayland || detected_wayland;
|
||||||
#[cfg(all(feature = "wayland", feature = "x11"))]
|
#[cfg(all(feature = "wayland", feature = "x11"))]
|
||||||
let result = if use_wayland {
|
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)
|
run::<display_backend::WaylandBackend>(opts, eww_binary_name)
|
||||||
} else {
|
} 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)
|
run::<display_backend::X11Backend>(opts, eww_binary_name)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +136,8 @@ fn main() {
|
|||||||
fn detect_wayland() -> bool {
|
fn detect_wayland() -> bool {
|
||||||
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
|
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
|
||||||
let wayland_display = std::env::var("WAYLAND_DISPLAY").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<()> {
|
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());
|
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||||
|
|
||||||
if !opts.show_logs {
|
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.no_daemonize || fork_result == ForkResult::Parent
|
||||||
}
|
}
|
||||||
|
|
||||||
opts::Action::WithServer(ActionWithServer::KillServer) => {
|
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);
|
handle_daemon_response(response);
|
||||||
}
|
}
|
||||||
false
|
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 => {
|
Err(err) if action.can_start_daemon() && !opts.no_daemonize => {
|
||||||
// connecting to the daemon failed. Thus, start the daemon here!
|
// connecting to the daemon failed. Thus, start the daemon here!
|
||||||
log::warn!("Failed to connect to daemon: {}", err);
|
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());
|
let _ = std::fs::remove_file(paths.get_ipc_socket_file());
|
||||||
if !opts.show_logs {
|
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();
|
let (command, response_recv) = action.into_daemon_command();
|
||||||
// start the daemon and give it the 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;
|
let is_parent = fork_result == ForkResult::Parent;
|
||||||
if let (Some(recv), true) = (response_recv, is_parent) {
|
if let (Some(recv), true) = (response_recv, is_parent) {
|
||||||
listen_for_daemon_response(recv);
|
listen_for_daemon_response(recv);
|
||||||
@@ -224,16 +250,26 @@ fn listen_for_daemon_response(mut recv: DaemonResponseReceiver) {
|
|||||||
.build()
|
.build()
|
||||||
.expect("Failed to initialize tokio runtime");
|
.expect("Failed to initialize tokio runtime");
|
||||||
rt.block_on(async {
|
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);
|
println!("{}", response);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// attempt to send a command to the daemon and send it the given action repeatedly.
|
/// 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>> {
|
fn handle_server_command(
|
||||||
log::debug!("Trying to find server process at socket {}", paths.get_ipc_socket_file().display());
|
paths: &EwwPaths,
|
||||||
let mut stream = attempt_connect(paths.get_ipc_socket_file(), connect_attempts).context("Failed to connect to daemon")?;
|
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());
|
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")
|
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.
|
/// 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 {
|
fn check_server_running(socket_path: impl AsRef<Path>) -> bool {
|
||||||
let response = net::UnixStream::connect(socket_path)
|
let response = net::UnixStream::connect(socket_path).ok().and_then(|mut stream| {
|
||||||
.ok()
|
client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok()
|
||||||
.and_then(|mut stream| client::do_server_call(&mut stream, &opts::ActionWithServer::Ping).ok());
|
});
|
||||||
response.is_some()
|
response.is_some()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,8 +197,17 @@ impl Opt {
|
|||||||
|
|
||||||
impl From<RawOpt> for Opt {
|
impl From<RawOpt> for Opt {
|
||||||
fn from(other: RawOpt) -> Self {
|
fn from(other: RawOpt) -> Self {
|
||||||
let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } = other;
|
let RawOpt { log_debug, force_wayland, config, show_logs, no_daemonize, restart, action } =
|
||||||
Opt { log_debug, force_wayland, show_logs, restart, config_path: config, action, no_daemonize }
|
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 { .. })
|
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 {
|
let command = match self {
|
||||||
ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector,
|
ActionWithServer::OpenInspector => app::DaemonCommand::OpenInspector,
|
||||||
|
|
||||||
@@ -248,7 +259,16 @@ impl ActionWithServer {
|
|||||||
// ActionWithServer::OpenMany { windows, should_toggle } => {
|
// ActionWithServer::OpenMany { windows, should_toggle } => {
|
||||||
// return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender });
|
// 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 {
|
return with_response_channel(|sender| app::DaemonCommand::OpenWindow {
|
||||||
window_name,
|
window_name,
|
||||||
instance_id: id,
|
instance_id: id,
|
||||||
@@ -263,18 +283,32 @@ impl ActionWithServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ActionWithServer::CloseWindows { windows } => {
|
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)
|
(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
|
where
|
||||||
F: FnOnce(DaemonResponseSender) -> O,
|
F: FnOnce(DaemonResponseSender) -> O,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,7 +55,12 @@ impl EwwPaths {
|
|||||||
std::fs::create_dir_all(&log_dir)?;
|
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> {
|
pub fn default() -> Result<Self> {
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ pub fn initialize_server<B: DisplayBackend>(
|
|||||||
) -> Result<ForkResult> {
|
) -> Result<ForkResult> {
|
||||||
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
|
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
|
||||||
std::env::set_current_dir(paths.get_config_dir())
|
std::env::set_current_dir(paths.get_config_dir()).with_context(|| {
|
||||||
.with_context(|| format!("Failed to change working directory to {}", paths.get_config_dir().display()))?;
|
format!("Failed to change working directory to {}", paths.get_config_dir().display())
|
||||||
|
})?;
|
||||||
|
|
||||||
log::info!("Loading paths: {}", &paths);
|
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 |_| {
|
simple_signal::set_handler(
|
||||||
log::info!("Shutting down ewwii daemon...");
|
&[simple_signal::Signal::Int, simple_signal::Signal::Term],
|
||||||
if let Err(e) = crate::application_lifecycle::send_exit() {
|
move |_| {
|
||||||
log::error!("Failed to send application shutdown event to workers: {:?}", e);
|
log::info!("Shutting down ewwii daemon...");
|
||||||
std::process::exit(1);
|
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 {
|
if B::IS_WAYLAND {
|
||||||
std::env::set_var("GDK_BACKEND", "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() {
|
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()) {
|
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(())
|
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()
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||||
.thread_name("main-async-runtime")
|
.thread_name("main-async-runtime")
|
||||||
.enable_all()
|
.enable_all()
|
||||||
@@ -176,7 +187,9 @@ fn init_async_part(paths: EwwPaths, ui_send: UnboundedSender<app::DaemonCommand>
|
|||||||
|
|
||||||
let ipc_server_join_handle = {
|
let ipc_server_join_handle = {
|
||||||
let ui_send = ui_send.clone();
|
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 = {
|
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 {
|
if let Err(e) = result {
|
||||||
log::error!("Ewwii exiting with error: {:?}", e);
|
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.
|
/// 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};
|
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||||
let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
|
let mut watcher: RecommendedWatcher =
|
||||||
Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => {
|
notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
|
||||||
let relevant_files_changed = paths.iter().any(|path| {
|
Ok(notify::Event { kind: notify::EventKind::Modify(_), paths, .. }) => {
|
||||||
let ext = path.extension().unwrap_or_default();
|
let relevant_files_changed = paths.iter().any(|path| {
|
||||||
ext == "rhai" || ext == "scss" || ext == "css"
|
let ext = path.extension().unwrap_or_default();
|
||||||
});
|
ext == "rhai" || ext == "scss" || ext == "css"
|
||||||
if relevant_files_changed {
|
});
|
||||||
if let Err(err) = tx.send(()) {
|
if relevant_files_changed {
|
||||||
log::warn!("Error forwarding file update event: {:?}", err);
|
if let Err(err) = tx.send(()) {
|
||||||
|
log::warn!("Error forwarding file update event: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Ok(_) => {}
|
||||||
Ok(_) => {}
|
Err(e) => log::error!("Encountered Error While Watching Files: {}", e),
|
||||||
Err(e) => log::error!("Encountered Error While Watching Files: {}", e),
|
})?;
|
||||||
})?;
|
|
||||||
watcher.watch(config_dir.as_ref(), RecursiveMode::Recursive)?;
|
watcher.watch(config_dir.as_ref(), RecursiveMode::Recursive)?;
|
||||||
|
|
||||||
// make sure to not trigger reloads too much by only accepting one reload every 500ms.
|
// 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()
|
let file =
|
||||||
.create(true)
|
std::fs::OpenOptions::new().create(true).append(true).open(&log_file_path).unwrap_or_else(
|
||||||
.append(true)
|
|_| {
|
||||||
.open(&log_file_path)
|
panic!(
|
||||||
.unwrap_or_else(|_| panic!("Error opening log file ({}), for writing", log_file_path.as_ref().to_string_lossy()));
|
"Error opening log file ({}), for writing",
|
||||||
|
log_file_path.as_ref().to_string_lossy()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
let fd = file.as_raw_fd();
|
let fd = file.as_raw_fd();
|
||||||
|
|
||||||
if nix::unistd::isatty(1)? {
|
if nix::unistd::isatty(1)? {
|
||||||
@@ -297,7 +322,9 @@ fn cleanup_log_dir(log_dir: impl AsRef<Path>) -> Result<()> {
|
|||||||
let entry = entry.ok()?;
|
let entry = entry.ok()?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if let Some(file_name) = path.file_name() {
|
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)
|
Some(path)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ impl<T: AsRef<str>> T {
|
|||||||
/// reference with an empty string.
|
/// reference with an empty string.
|
||||||
pub fn replace_env_var_references(input: String) -> String {
|
pub fn replace_env_var_references(input: String) -> String {
|
||||||
regex!(r"\$\{([^\s]*)\}")
|
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()
|
.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ pub enum WidgetInput {
|
|||||||
Window(WindowDefinition),
|
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 {
|
let node = match input {
|
||||||
WidgetInput::Node(n) => n,
|
WidgetInput::Node(n) => n,
|
||||||
WidgetInput::Window(w) => w.root_widget,
|
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
|
// 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,
|
When a a new widget is added to the build process,
|
||||||
make sure to update get_id_to_props_map() found in
|
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 {
|
let gtk_widget = match root_node {
|
||||||
WidgetNode::Box { props, children } => build_gtk_box(props, children, widget_reg)?.upcast(),
|
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::CenterBox { props, children } => {
|
||||||
WidgetNode::EventBox { props, children } => build_gtk_event_box(props, children, widget_reg)?.upcast(),
|
build_center_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::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::Graph { props } => build_graph(props, widget_reg)?.upcast(),
|
||||||
WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
|
WidgetNode::Transform { props } => build_transform(props, widget_reg)?.upcast(),
|
||||||
WidgetNode::Slider { props } => build_gtk_scale(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::Input { props } => build_gtk_input(props, widget_reg)?.upcast(),
|
||||||
WidgetNode::Calendar { props } => build_gtk_calendar(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::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::ColorChooser { props } => build_gtk_color_chooser(props, widget_reg)?.upcast(),
|
||||||
WidgetNode::ComboBoxText { props } => build_gtk_combo_box_text(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::Checkbox { props } => build_gtk_checkbox(props, widget_reg)?.upcast(),
|
||||||
WidgetNode::Revealer { props, children } => build_gtk_revealer(props, children, widget_reg)?.upcast(),
|
WidgetNode::Revealer { props, children } => {
|
||||||
WidgetNode::Scroll { props, children } => build_gtk_scrolledwindow(props, children, widget_reg)?.upcast(),
|
build_gtk_revealer(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::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(),
|
// WIDGET_NAME_SYSTRAY => build_systray(node)?.upcast(),
|
||||||
unknown => {
|
unknown => {
|
||||||
return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown));
|
return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown));
|
||||||
|
|||||||
@@ -13,13 +13,37 @@ wrapper! {
|
|||||||
#[derive(Properties)]
|
#[derive(Properties)]
|
||||||
#[properties(wrapper_type = CircProg)]
|
#[properties(wrapper_type = CircProg)]
|
||||||
pub struct CircProgPriv {
|
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>,
|
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>,
|
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>,
|
thickness: RefCell<f64>,
|
||||||
|
|
||||||
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
|
#[property(get, set, nick = "Clockwise", blurb = "Clockwise", default = true)]
|
||||||
@@ -98,7 +122,9 @@ impl ContainerImpl for CircProgPriv {
|
|||||||
fn add(&self, widget: >k::Widget) {
|
fn add(&self, widget: >k::Widget) {
|
||||||
if let Some(content) = &*self.content.borrow() {
|
if let Some(content) = &*self.content.borrow() {
|
||||||
// TODO: Handle this error when populating children widgets instead
|
// 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_remove(content);
|
||||||
}
|
}
|
||||||
self.parent_add(widget);
|
self.parent_add(widget);
|
||||||
@@ -125,9 +151,13 @@ impl WidgetImpl for CircProgPriv {
|
|||||||
|
|
||||||
if let Some(child) = &*self.content.borrow() {
|
if let Some(child) = &*self.content.borrow() {
|
||||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
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 {
|
} 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)
|
(empty_width, empty_width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,9 +172,13 @@ impl WidgetImpl for CircProgPriv {
|
|||||||
|
|
||||||
if let Some(child) = &*self.content.borrow() {
|
if let Some(child) = &*self.content.borrow() {
|
||||||
let (min_child, natural_child) = calc_widget_lowest_preferred_dimension(child);
|
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 {
|
} 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)
|
(empty_height, empty_height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,9 +198,14 @@ impl WidgetImpl for CircProgPriv {
|
|||||||
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
let margin = styles.margin(gtk::StateFlags::NORMAL);
|
||||||
// Padding is not supported yet
|
// Padding is not supported yet
|
||||||
let fg_color: gdk::RGBA = styles.color(gtk::StateFlags::NORMAL);
|
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 bg_color: gdk::RGBA = styles
|
||||||
let (start_angle, end_angle) =
|
.style_property_for_state("background-color", gtk::StateFlags::NORMAL)
|
||||||
if clockwise { (0.0, perc_to_rad(value)) } else { (perc_to_rad(100.0 - value), 2f64 * std::f64::consts::PI) };
|
.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_width = self.obj().allocated_width() as f64;
|
||||||
let total_height = self.obj().allocated_height() as f64;
|
let total_height = self.obj().allocated_height() as f64;
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ impl GraphPriv {
|
|||||||
*last_updated_at = std::time::Instant::now();
|
*last_updated_at = std::time::Instant::now();
|
||||||
|
|
||||||
while let Some(entry) = history.front() {
|
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();
|
*last_value = history.pop_front();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -243,14 +245,24 @@ impl WidgetImpl for GraphPriv {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(instant, value)| {
|
.map(|(instant, value)| {
|
||||||
let t = last_updated_at.duration_since(*instant).as_millis() as f64;
|
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)>>();
|
.collect::<VecDeque<(f64, f64)>>();
|
||||||
|
|
||||||
// Aad an extra point outside of the graph to extend the line to the left
|
// Aad an extra point outside of the graph to extend the line to the left
|
||||||
if let Some((instant, value)) = extra_point {
|
if let Some((instant, value)) = extra_point {
|
||||||
let t = last_updated_at.duration_since(instant).as_millis() as f64;
|
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.push_front(if *self.vertical.borrow() { (x, -y) } else { (-x, y) });
|
||||||
}
|
}
|
||||||
points
|
points
|
||||||
@@ -263,7 +275,9 @@ impl WidgetImpl for GraphPriv {
|
|||||||
cr.clip();
|
cr.clip();
|
||||||
|
|
||||||
// Draw Background
|
// 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 bg_color.alpha() > 0.0 {
|
||||||
if let Some(first_point) = points.front() {
|
if let Some(first_point) = points.front() {
|
||||||
cr.line_to(first_point.0, height + margin_bottom);
|
cr.line_to(first_point.0, height + margin_bottom);
|
||||||
@@ -273,7 +287,12 @@ impl WidgetImpl for GraphPriv {
|
|||||||
}
|
}
|
||||||
cr.line_to(width, height);
|
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()?;
|
cr.fill()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +307,12 @@ impl WidgetImpl for GraphPriv {
|
|||||||
let line_style = &*self.line_style.borrow();
|
let line_style = &*self.line_style.borrow();
|
||||||
apply_line_style(line_style.as_str(), cr)?;
|
apply_line_style(line_style.as_str(), cr)?;
|
||||||
cr.set_line_width(thickness);
|
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()?;
|
cr.stroke()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ impl ContainerImpl for TransformPriv {
|
|||||||
fn add(&self, widget: >k::Widget) {
|
fn add(&self, widget: >k::Widget) {
|
||||||
if let Some(content) = &*self.content.borrow() {
|
if let Some(content) = &*self.content.borrow() {
|
||||||
// TODO: Handle this error when populating children widgets instead
|
// 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_remove(content);
|
||||||
}
|
}
|
||||||
self.parent_add(widget);
|
self.parent_add(widget);
|
||||||
@@ -145,31 +147,43 @@ impl WidgetImpl for TransformPriv {
|
|||||||
cr.save()?;
|
cr.save()?;
|
||||||
|
|
||||||
let transform_origin_x = match &*self.transform_origin_x.borrow() {
|
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,
|
None => 0.0,
|
||||||
};
|
};
|
||||||
let transform_origin_y = match &*self.transform_origin_y.borrow() {
|
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,
|
None => 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let translate_x = match &*self.translate_x.borrow() {
|
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,
|
None => 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let translate_y = match &*self.translate_y.borrow() {
|
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,
|
None => 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale_x = match &*self.scale_x.borrow() {
|
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,
|
None => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let scale_y = match &*self.scale_y.borrow() {
|
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,
|
None => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -80,12 +80,15 @@ impl WidgetRegistry {
|
|||||||
for patch_req in patches {
|
for patch_req in patches {
|
||||||
match patch_req {
|
match patch_req {
|
||||||
PatchGtkWidget::Create(wdgt_node, wdgt_id, parent_id) => {
|
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) => {
|
PatchGtkWidget::Update(widget_id, new_props) => {
|
||||||
self.update_props(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
|
// Removals
|
||||||
for (id, new_info) in &old_map {
|
for (id, new_info) in &old_map {
|
||||||
if !new_map.contains_key(id) {
|
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
|
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);
|
log::trace!("Creating '{}'", widget_id);
|
||||||
if let Some(parent) = self.widgets.get(&parent_id) {
|
if let Some(parent) = self.widgets.get(&parent_id) {
|
||||||
let parent_widget = parent.widget.clone();
|
let parent_widget = parent.widget.clone();
|
||||||
|
|
||||||
if let Some(container) = parent_widget.dynamic_cast::<gtk::Container>().ok() {
|
if let Some(container) = parent_widget.dynamic_cast::<gtk::Container>().ok() {
|
||||||
// check if the widget already exists
|
// check if the widget already exists
|
||||||
let position = self
|
let position = self.widgets.get(&widget_id).and_then(|old_entry| {
|
||||||
.widgets
|
container.children().iter().position(|w| w == &old_entry.widget)
|
||||||
.get(&widget_id)
|
});
|
||||||
.and_then(|old_entry| container.children().iter().position(|w| w == &old_entry.widget));
|
|
||||||
|
|
||||||
// obliterate that widget....
|
// obliterate that widget....
|
||||||
// how dare it try to create duplication...
|
// 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:
|
// Parse initial props to create the widget:
|
||||||
let orientation = props
|
let orientation = props
|
||||||
.get("orientation")
|
.get("orientation")
|
||||||
@@ -209,7 +223,8 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(gtk::Orientation::Horizontal);
|
.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))?;
|
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 gtk_widget_clone = gtk_widget.clone();
|
||||||
|
|
||||||
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
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) {
|
if let Ok(orientation) = parse_orientation(&orientation_str) {
|
||||||
gtk_widget_clone.set_orientation(orientation);
|
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
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Box");
|
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)?;
|
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");
|
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
|
// we have more than one child, we can unwrap
|
||||||
let first = children.next().unwrap()?;
|
let first = children.next().unwrap()?;
|
||||||
@@ -284,7 +307,11 @@ pub(super) fn build_gtk_overlay(
|
|||||||
Ok(gtk_widget)
|
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);
|
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||||
gtk_widget.set_has_tooltip(true);
|
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);
|
gtk_widget.add(&content_widget);
|
||||||
|
|
||||||
let tooltip_node = Rc::new(tooltip_node);
|
let tooltip_node = Rc::new(tooltip_node);
|
||||||
let tooltip_widget = build_gtk_widget(WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()), widget_registry)
|
let tooltip_widget = build_gtk_widget(
|
||||||
.expect("Failed to build tooltip 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| {
|
gtk_widget.connect_query_tooltip(move |_widget, _x, _y, _keyboard_mode, tooltip| {
|
||||||
tooltip.set_custom(Some(&tooltip_widget));
|
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)
|
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
|
let orientation = props
|
||||||
.get("orientation")
|
.get("orientation")
|
||||||
.and_then(|v| v.clone().try_cast::<String>())
|
.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);
|
gtk_widget_clone.set_orientation(orientation);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "CenterBox");
|
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)?;
|
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;
|
let delta = evt.delta().1;
|
||||||
if delta != 0f64 {
|
if delta != 0f64 {
|
||||||
// Ignore the first event https://bugzilla.gnome.org/show_bug.cgi?id=675959
|
// 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
|
glib::Propagation::Proceed
|
||||||
})
|
})
|
||||||
@@ -482,7 +524,8 @@ pub(super) fn build_gtk_event_box(
|
|||||||
let display = gdk::Display::default();
|
let display = gdk::Display::default();
|
||||||
let gdk_window = widget.window();
|
let gdk_window = widget.window();
|
||||||
if let (Some(display), Some(gdk_window)) = (display, gdk_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
|
glib::Propagation::Proceed
|
||||||
@@ -507,20 +550,38 @@ pub(super) fn build_gtk_event_box(
|
|||||||
widget.drag_dest_set(
|
widget.drag_dest_set(
|
||||||
DestDefaults::ALL,
|
DestDefaults::ALL,
|
||||||
&[
|
&[
|
||||||
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0),
|
TargetEntry::new(
|
||||||
TargetEntry::new("text/plain", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0),
|
"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,
|
gdk::DragAction::COPY,
|
||||||
);
|
);
|
||||||
connect_signal_handler!(
|
connect_signal_handler!(
|
||||||
widget,
|
widget,
|
||||||
widget.connect_drag_data_received(move |_, _, _x, _y, selection_data, _target_type, _timestamp| {
|
widget.connect_drag_data_received(
|
||||||
if let Some(data) = selection_data.uris().first() {
|
move |_, _, _x, _y, selection_data, _target_type, _timestamp| {
|
||||||
run_command(timeout, &ondropped, &[data.to_string(), "file".to_string()]);
|
if let Some(data) = selection_data.uris().first() {
|
||||||
} else if let Some(data) = selection_data.text() {
|
run_command(
|
||||||
run_command(timeout, &ondropped, &[data.to_string(), "text".to_string()]);
|
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();
|
widget.drag_source_unset();
|
||||||
} else {
|
} else {
|
||||||
let target_entry = match dragtype {
|
let target_entry = match dragtype {
|
||||||
DragEntryType::File => {
|
DragEntryType::File => TargetEntry::new(
|
||||||
TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET, 0)
|
"text/uri-list",
|
||||||
}
|
gtk::TargetFlags::OTHER_APP | gtk::TargetFlags::OTHER_WIDGET,
|
||||||
DragEntryType::Text => {
|
0,
|
||||||
TargetEntry::new("text/plain", 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(
|
widget.drag_source_set(
|
||||||
ModifierType::BUTTON1_MASK,
|
ModifierType::BUTTON1_MASK,
|
||||||
@@ -588,7 +653,9 @@ pub(super) fn build_gtk_event_box(
|
|||||||
let _ = apply_props(props, >k_widget_clone);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
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");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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();
|
let gtk_widget = gtk::Stack::new();
|
||||||
|
|
||||||
if children.is_empty() {
|
if children.is_empty() {
|
||||||
return Err(anyhow!("stack must contain at least one element"));
|
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() {
|
for (i, child) in children.enumerate() {
|
||||||
let child = child?;
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Stack");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 widget = Transform::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: &Transform| -> Result<()> {
|
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);
|
let _ = apply_props(props, &widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -729,7 +813,10 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
|
|||||||
Ok(widget)
|
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 widget = CircProg::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: &CircProg| -> Result<()> {
|
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);
|
let _ = apply_props(props, &widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
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)
|
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 widget = super::graph::Graph::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: &super::graph::Graph| -> Result<()> {
|
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);
|
let _ = apply_props(props, &widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
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)
|
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 gtk_widget = gtk::ProgressBar::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::ProgressBar| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Progress");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::Image::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::Image| -> Result<()> {
|
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") {
|
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);
|
widget.set_from_animation(&pixbuf_animation);
|
||||||
} else {
|
} else {
|
||||||
let pixbuf;
|
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")?;
|
let reg = regex::Regex::new(r"<svg")?;
|
||||||
reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_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(
|
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale(
|
||||||
&stream,
|
&stream,
|
||||||
image_width,
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Image");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::Button::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::Button| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Button");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::Label::new(None);
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::Label| -> Result<()> {
|
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 {
|
} else {
|
||||||
widget.set_max_width_chars(limit_width);
|
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
|
text
|
||||||
} else {
|
} else {
|
||||||
widget.set_ellipsize(pango::EllipsizeMode::None);
|
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 };
|
let final_text = if unindent { util::unindent(&unescaped) } else { unescaped };
|
||||||
widget.set_text(&final_text);
|
widget.set_text(&final_text);
|
||||||
} else if has_markup {
|
} 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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Label");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::Entry::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::Entry| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Input");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::Calendar::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::Calendar| -> Result<()> {
|
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) {
|
if let Ok(onclick) = get_string_prop(&props, "onclick", None) {
|
||||||
connect_signal_handler!(
|
connect_signal_handler!(
|
||||||
widget,
|
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(())
|
Ok(())
|
||||||
@@ -1284,21 +1428,28 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
|
|||||||
let _ = apply_props(props, >k_widget_clone);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Calendar");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::ComboBoxText::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::ComboBoxText| -> Result<()> {
|
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!(
|
connect_signal_handler!(
|
||||||
widget,
|
widget,
|
||||||
widget.connect_changed(move |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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "ComboBoxText");
|
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)?;
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Expander");
|
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)?;
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1433,7 +1598,8 @@ pub(super) fn build_gtk_revealer(
|
|||||||
match children.len() {
|
match children.len() {
|
||||||
0 => { /* maybe warn? */ }
|
0 => { /* maybe warn? */ }
|
||||||
1 => {
|
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));
|
gtk_widget.set_child(Some(&child_widget));
|
||||||
}
|
}
|
||||||
n => {
|
n => {
|
||||||
@@ -1443,14 +1609,19 @@ pub(super) fn build_gtk_revealer(
|
|||||||
|
|
||||||
let id = hash_props_and_type(&props, "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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::CheckButton::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::CheckButton| -> Result<()> {
|
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!(
|
connect_signal_handler!(
|
||||||
widget,
|
widget,
|
||||||
widget.connect_toggled(move |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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Checkbox");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::ColorButton::builder().build();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::ColorButton| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "ColorButton");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
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 gtk_widget = gtk::ColorChooserWidget::new();
|
||||||
|
|
||||||
let apply_props = |props: &Map, widget: >k::ColorChooserWidget| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "ColorChooser");
|
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)?;
|
resolve_rhai_widget_attrs(>k_widget.clone().upcast::<gtk::Widget>(), &props)?;
|
||||||
|
|
||||||
Ok(gtk_widget)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Scale> {
|
pub(super) fn build_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)));
|
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
|
// Reusable closure for applying props
|
||||||
let apply_props = |props: &Map, widget: >k::Scale| -> Result<()> {
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Slider");
|
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)?;
|
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);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
// now re-apply generic widget attrs
|
// 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);
|
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");
|
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)?;
|
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) {
|
if let Ok(style_str) = get_string_prop(&props, "style", None) {
|
||||||
let css_provider = gtk::CssProvider::new();
|
let css_provider = gtk::CssProvider::new();
|
||||||
let scss = format!("* {{ {} }}", style_str);
|
let scss = format!("* {{ {} }}", style_str);
|
||||||
css_provider.load_from_data(grass::from_string(scss, &grass::Options::default())?.as_bytes())?;
|
css_provider
|
||||||
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
.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) {
|
if let Ok(css_str) = get_string_prop(&props, "css", None) {
|
||||||
let css_provider = gtk::CssProvider::new();
|
let css_provider = gtk::CssProvider::new();
|
||||||
css_provider.load_from_data(grass::from_string(css_str, &grass::Options::default())?.as_bytes())?;
|
css_provider
|
||||||
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
.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) {
|
if let Ok(valign) = get_string_prop(&props, "valign", None) {
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ where
|
|||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("command-execution-thread".to_string())
|
.name("command-execution-thread".to_string())
|
||||||
.spawn(move || {
|
.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();
|
let child = Command::new("/bin/sh").arg("-c").arg(&cmd).spawn();
|
||||||
match child {
|
match child {
|
||||||
Ok(mut child) => match child.wait_timeout(timeout) {
|
Ok(mut child) => match child.wait_timeout(timeout) {
|
||||||
@@ -89,10 +93,20 @@ pub(super) fn parse_orientation(ori: &str) -> Result<gtk::Orientation> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gtk Label
|
/// 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 {
|
if (truncate || limit_width != i32::MAX) && show {
|
||||||
label.set_max_width_chars(if limit_width == i32::MAX { -1 } else { limit_width });
|
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 {
|
} else {
|
||||||
label.set_ellipsize(pango::EllipsizeMode::None);
|
label.set_ellipsize(pango::EllipsizeMode::None);
|
||||||
}
|
}
|
||||||
@@ -146,7 +160,9 @@ where
|
|||||||
{
|
{
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
let cmd = cmd.replace("{}", &format!("{}", args[0]));
|
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 {
|
} else {
|
||||||
cmd.to_string()
|
cmd.to_string()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ pub struct BackendWindowOptionsDef {
|
|||||||
|
|
||||||
impl BackendWindowOptionsDef {
|
impl BackendWindowOptionsDef {
|
||||||
pub fn eval(&self, properties: Map) -> Result<BackendWindowOptions, Error> {
|
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> {
|
// pub fn from_attrs(attrs: &mut Attributes) -> DiagResult<Self> {
|
||||||
@@ -119,13 +122,22 @@ impl X11BackendWindowOptionsDef {
|
|||||||
|
|
||||||
struts: match properties.get("reserve") {
|
struts: match properties.get("reserve") {
|
||||||
Some(dynval) => {
|
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 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()) }
|
X11StrutDefinition { distance, side: side.unwrap_or(Side::default()) }
|
||||||
}
|
}
|
||||||
@@ -142,7 +154,9 @@ impl X11BackendWindowOptionsDef {
|
|||||||
|
|
||||||
wm_ignore: {
|
wm_ignore: {
|
||||||
let wm_ignore = properties.get("wm_ignore").map(|d| d.clone_cast::<bool>());
|
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;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
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 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() {
|
match captures.get(2).unwrap().as_str() {
|
||||||
"px" | "" => Ok(NumWithUnit::Pixels(value.floor() as i32)),
|
"px" | "" => Ok(NumWithUnit::Pixels(value.floor() as i32)),
|
||||||
"%" => Ok(NumWithUnit::Percent(value)),
|
"%" => Ok(NumWithUnit::Percent(value)),
|
||||||
@@ -109,7 +115,10 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_coords() {
|
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());
|
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 {
|
pub enum WindowStacking {
|
||||||
#[default]
|
#[default]
|
||||||
Foreground,
|
Foreground,
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ impl FromStr for Coords {
|
|||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
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()? })
|
Ok(Coords { x: sx.parse()?, y: sy.parse()? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,9 +114,10 @@ impl std::str::FromStr for AnchorPoint {
|
|||||||
type Err = EnumParseError;
|
type Err = EnumParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let (x_str, y_str) = s
|
let (x_str, y_str) = s.split_once(' ').ok_or_else(|| EnumParseError {
|
||||||
.split_once(' ')
|
input: s.to_string(),
|
||||||
.ok_or_else(|| EnumParseError { input: s.to_string(), expected: vec!["<horizontal> <vertical>"] })?;
|
expected: vec!["<horizontal> <vertical>"],
|
||||||
|
})?;
|
||||||
|
|
||||||
let x = AnchorAlignment::from_x_alignment(x_str)?;
|
let x = AnchorAlignment::from_x_alignment(x_str)?;
|
||||||
let y = AnchorAlignment::from_y_alignment(y_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
|
/// This stores the arguments given in the command line to create a window
|
||||||
/// While creating a window, we combine this with information from the
|
/// 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)),
|
// Some(geo) => Some(geo.eval(&vars)?.override_if_given(args.anchor, args.pos, args.size)),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
let monitor = args
|
let monitor = args.monitor.clone().or_else(|| {
|
||||||
.monitor
|
properties
|
||||||
.clone()
|
.get("monitor")?
|
||||||
.or_else(|| properties.get("monitor")?.clone().try_cast::<i64>().map(|n| MonitorIdentifier::Numeric(n as i32)));
|
.clone()
|
||||||
|
.try_cast::<i64>()
|
||||||
|
.map(|n| MonitorIdentifier::Numeric(n as i32))
|
||||||
|
});
|
||||||
Ok(WindowInitiator {
|
Ok(WindowInitiator {
|
||||||
backend_options: window_def.backend_options.eval(properties.clone())?,
|
backend_options: window_def.backend_options.eval(properties.clone())?,
|
||||||
geometry,
|
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 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 {
|
let mut geom = WindowGeometry {
|
||||||
offset: get_coords_from_map(&map, "x", "y")?,
|
offset: get_coords_from_map(&map, "x", "y")?,
|
||||||
size: get_coords_from_map(&map, "width", "height")?,
|
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 {
|
if override_geom {
|
||||||
@@ -111,7 +122,8 @@ fn anchor_point_from_str(s: &str) -> Result<AnchorPoint> {
|
|||||||
match parts.as_slice() {
|
match parts.as_slice() {
|
||||||
[single] => {
|
[single] => {
|
||||||
// Apply to both x and y
|
// 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 })
|
Ok(AnchorPoint { x: alignment, y: alignment })
|
||||||
}
|
}
|
||||||
[y_part, x_part] => {
|
[y_part, x_part] => {
|
||||||
|
|||||||
@@ -5,15 +5,23 @@ use std::time::Duration;
|
|||||||
/// General purpose helpers
|
/// General purpose helpers
|
||||||
pub fn get_string_prop(props: &Map, key: &str, default: Option<&str>) -> Result<String> {
|
pub fn get_string_prop(props: &Map, key: &str, default: Option<&str>) -> Result<String> {
|
||||||
if let Some(value) = props.get(key) {
|
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 {
|
} 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> {
|
pub fn get_bool_prop(props: &Map, key: &str, default: Option<bool>) -> Result<bool> {
|
||||||
if let Some(value) = props.get(key) {
|
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 {
|
} else {
|
||||||
default.map(|s| s).ok_or_else(|| anyhow!("Missing required bool property `{}`", key))
|
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>() {
|
if let Some(v) = value.clone().try_cast::<i64>() {
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
} 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 {
|
} else {
|
||||||
Err(anyhow!("Expected property `{}` to be an i64 or numeric string", key))
|
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>() {
|
} else if let Some(v) = value.clone().try_cast::<i64>() {
|
||||||
Ok(v as f64)
|
Ok(v as f64)
|
||||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
} 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 {
|
} else {
|
||||||
Err(anyhow!("Expected property `{}` to be an f64, i64, or numeric string", key))
|
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>() {
|
if let Some(v) = value.clone().try_cast::<i32>() {
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else if let Some(s) = value.clone().try_cast::<String>() {
|
} 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 {
|
} else {
|
||||||
Err(anyhow!("Expected property `{}` to be an i32 or numeric string", key))
|
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) {
|
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
|
array
|
||||||
.into_iter()
|
.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()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
default.ok_or_else(|| anyhow!("Missing required vec property `{}`", key))
|
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))
|
Ok(Duration::from_secs(s))
|
||||||
} else if key_str.ends_with("min") {
|
} else if key_str.ends_with("min") {
|
||||||
let num = &key_str[..key_str.len() - 3];
|
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))
|
Ok(Duration::from_secs(mins * 60))
|
||||||
} else if key_str.ends_with("h") {
|
} else if key_str.ends_with("h") {
|
||||||
let num = &key_str[..key_str.len() - 1];
|
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.
|
/// 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.
|
/// If the environment variable is not defined or is malformed use the POSIX locale.
|
||||||
pub fn get_locale() -> Locale {
|
pub fn get_locale() -> Locale {
|
||||||
var("LC_ALL")
|
var("LC_ALL").or_else(|_| var("LC_TIME")).or_else(|_| var("LANG")).map_or(Locale::POSIX, |v| {
|
||||||
.or_else(|_| var("LC_TIME"))
|
v.split('.').next().and_then(|x| x.try_into().ok()).unwrap_or_default()
|
||||||
.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
|
/// The name of a variable
|
||||||
#[repr(transparent)]
|
#[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)]
|
#[debug("VarName({})", _0)]
|
||||||
pub struct VarName(pub String);
|
pub struct VarName(pub String);
|
||||||
|
|
||||||
@@ -34,7 +47,20 @@ impl From<AttrName> for VarName {
|
|||||||
|
|
||||||
/// The name of an attribute
|
/// The name of an attribute
|
||||||
#[repr(transparent)]
|
#[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)]
|
#[debug("AttrName({})", _0)]
|
||||||
pub struct AttrName(pub String);
|
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 {
|
engine.register_fn("defwindow", |name: &str, props: Map, node: WidgetNode| {
|
||||||
name: name.to_string(),
|
WidgetNode::DefWindow { name: name.to_string(), props, node: Box::new(node) }
|
||||||
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
|
// bar seperator
|
||||||
out.push_str(&format!(" {bar}\n", bar = "|".dimmed()));
|
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
|
// The caret line, pointing at the column
|
||||||
let caret_padding = " ".repeat(self.column.saturating_sub(1));
|
let caret_padding = " ".repeat(self.column.saturating_sub(1));
|
||||||
@@ -72,7 +78,12 @@ impl<'a> Diagnostic<'a> {
|
|||||||
));
|
));
|
||||||
|
|
||||||
for line in note_lines {
|
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!(
|
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 {
|
let (help, hint) = match root_err {
|
||||||
EvalAltResult::ErrorParsing(..) => (
|
EvalAltResult::ErrorParsing(..) => (
|
||||||
"Syntax error encountered while parsing.".into(),
|
"Syntax error encountered while parsing.".into(),
|
||||||
"Check for unmatched tokens, invalid constructs, or misplaced punctuation.".into(),
|
"Check for unmatched tokens, invalid constructs, or misplaced punctuation.".into(),
|
||||||
),
|
),
|
||||||
EvalAltResult::ErrorVariableExists(name, ..) => {
|
EvalAltResult::ErrorVariableExists(name, ..) => (
|
||||||
(format!("Variable '{}' is already defined.", name), "Remove or rename the duplicate declaration.".into())
|
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::ErrorForbiddenVariable(name, ..) => (
|
||||||
}
|
format!("Usage of forbidden variable '{}'.", name),
|
||||||
EvalAltResult::ErrorVariableNotFound(name, ..) => {
|
"Avoid using reserved or protected variable names.".into(),
|
||||||
(format!("Unknown variable '{}'.", name), "Check for typos or ensure the variable is initialized before use.".into())
|
),
|
||||||
}
|
EvalAltResult::ErrorVariableNotFound(name, ..) => (
|
||||||
|
format!("Unknown variable '{}'.", name),
|
||||||
|
"Check for typos or ensure the variable is initialized before use.".into(),
|
||||||
|
),
|
||||||
EvalAltResult::ErrorPropertyNotFound(name, ..) => (
|
EvalAltResult::ErrorPropertyNotFound(name, ..) => (
|
||||||
format!("Property '{}' not found on this object.", name),
|
format!("Property '{}' not found on this object.", name),
|
||||||
"Verify the property name and the object’s available fields.".into(),
|
"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 ")),
|
format!("Did you mean one of:\n {}", candidates.join("\n ")),
|
||||||
)
|
)
|
||||||
} else {
|
} 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, ..) => (
|
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),
|
format!("Error inside function '{}': {}", fn_name, msg),
|
||||||
"Inspect the function implementation and arguments passed.".into(),
|
"Inspect the function implementation and arguments passed.".into(),
|
||||||
),
|
),
|
||||||
EvalAltResult::ErrorInModule(name, ..) => {
|
EvalAltResult::ErrorInModule(name, ..) => (
|
||||||
(format!("Error while loading module '{}'.", name), "Check the module code for syntax or runtime errors.".into())
|
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::ErrorUnboundThis(..) => (
|
||||||
}
|
"`this` is unbound in this context.".into(),
|
||||||
|
"Only use `this` inside methods or bound closures.".into(),
|
||||||
|
),
|
||||||
EvalAltResult::ErrorMismatchDataType(found, expected, ..) => (
|
EvalAltResult::ErrorMismatchDataType(found, expected, ..) => (
|
||||||
format!("Data type mismatch: found '{}', expected '{}'.", found, expected),
|
format!("Data type mismatch: found '{}', expected '{}'.", found, expected),
|
||||||
"Convert or cast values to the required type.".into(),
|
"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),
|
format!("Cannot index into value of type '{}'.", typ),
|
||||||
"Only arrays, maps, bitfields, or strings support indexing.".into(),
|
"Only arrays, maps, bitfields, or strings support indexing.".into(),
|
||||||
),
|
),
|
||||||
EvalAltResult::ErrorArrayBounds(len, idx, ..) => {
|
EvalAltResult::ErrorArrayBounds(len, idx, ..) => (
|
||||||
(format!("Array index {} out of bounds (0..{}).", idx, len), "Use a valid index within the array’s range.".into())
|
format!("Array index {} out of bounds (0..{}).", idx, len),
|
||||||
}
|
"Use a valid index within the array’s range.".into(),
|
||||||
|
),
|
||||||
EvalAltResult::ErrorStringBounds(len, idx, ..) => (
|
EvalAltResult::ErrorStringBounds(len, idx, ..) => (
|
||||||
format!("String index {} out of bounds (0..{}).", idx, len),
|
format!("String index {} out of bounds (0..{}).", idx, len),
|
||||||
"Ensure you index only valid character positions.".into(),
|
"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),
|
format!("Bitfield index {} out of bounds (0..{}).", idx, len),
|
||||||
"Use a valid bit position within the bitfield’s size.".into(),
|
"Use a valid bit position within the bitfield’s size.".into(),
|
||||||
),
|
),
|
||||||
EvalAltResult::ErrorFor(..) => {
|
EvalAltResult::ErrorFor(..) => (
|
||||||
("`for` loop value is not iterable.".into(), "Iterate only over arrays, strings, ranges, or iterators.".into())
|
"`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(..) => (
|
EvalAltResult::ErrorTooManyOperations(..) => (
|
||||||
"Script exceeded the maximum number of operations.".into(),
|
"Script exceeded the maximum number of operations.".into(),
|
||||||
"Break complex expressions into smaller steps or increase the limit.".into(),
|
"Break complex expressions into smaller steps or increase the limit.".into(),
|
||||||
),
|
),
|
||||||
EvalAltResult::ErrorTooManyModules(..) => {
|
EvalAltResult::ErrorTooManyModules(..) => (
|
||||||
("Too many modules have been loaded.".into(), "Use fewer modules or increase the module limit.".into())
|
"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::ErrorStackOverflow(..) => (
|
||||||
}
|
"Call stack overflow detected.".into(),
|
||||||
EvalAltResult::ErrorDataTooLarge(name, ..) => {
|
"Check for infinite recursion or deeply nested calls.".into(),
|
||||||
(format!("Data '{}' is too large to handle.", name), "Use smaller data sizes or adjust engine limits.".into())
|
),
|
||||||
}
|
EvalAltResult::ErrorDataTooLarge(name, ..) => (
|
||||||
EvalAltResult::ErrorTerminated(..) => {
|
format!("Data '{}' is too large to handle.", name),
|
||||||
("Script execution was terminated.".into(), "This occurs when a `stop` or external termination is triggered.".into())
|
"Use smaller data sizes or adjust engine limits.".into(),
|
||||||
}
|
),
|
||||||
EvalAltResult::ErrorCustomSyntax(msg, options, ..) => {
|
EvalAltResult::ErrorTerminated(..) => (
|
||||||
(format!("Custom syntax error: {}.", msg), format!("Expected one of: {}.", options.join(", ")))
|
"Script execution was terminated.".into(),
|
||||||
}
|
"This occurs when a `stop` or external termination is triggered.".into(),
|
||||||
EvalAltResult::ErrorRuntime(..) => {
|
),
|
||||||
("Runtime error encountered.".into(), "Inspect the error message and script logic for issues.".into())
|
EvalAltResult::ErrorCustomSyntax(msg, options, ..) => (
|
||||||
}
|
format!("Custom syntax error: {}.", msg),
|
||||||
EvalAltResult::LoopBreak(..) => {
|
format!("Expected one of: {}.", options.join(", ")),
|
||||||
("`break` used outside of a loop.".into(), "Only use `break` inside `for` or `while` loops.".into())
|
),
|
||||||
}
|
EvalAltResult::ErrorRuntime(..) => (
|
||||||
EvalAltResult::Return(..) => {
|
"Runtime error encountered.".into(),
|
||||||
("`return` statement encountered.".into(), "Script terminated with an explicit return value.".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()),
|
_ => ("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) {
|
fn register_temp_poll_listen(engine: &mut rhai::Engine) {
|
||||||
engine.register_type::<TempSignal>();
|
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 {
|
let base_dir = if let Some(src) = source_path {
|
||||||
PathBuf::from(src).parent().map(|p| p.to_path_buf()).unwrap_or(
|
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 {
|
} 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() {
|
if !file_path.is_absolute() {
|
||||||
file_path = base_dir.join(file_path);
|
file_path = base_dir.join(file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
let full_path =
|
let full_path = file_path
|
||||||
file_path.canonicalize().map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?;
|
.canonicalize()
|
||||||
|
.map_err(|e| EvalAltResult::ErrorSystem(format!("resolving path: {path}"), e.into()))?;
|
||||||
|
|
||||||
let script = fs::read_to_string(&full_path)
|
let script = fs::read_to_string(&full_path).map_err(|e| {
|
||||||
.map_err(|e| EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into()))?;
|
EvalAltResult::ErrorSystem(format!("reading file: {full_path:?}"), e.into())
|
||||||
|
})?;
|
||||||
|
|
||||||
let ast: AST = engine.compile(&script)?;
|
let ast: AST = engine.compile(&script)?;
|
||||||
let scope = Scope::new();
|
let scope = Scope::new();
|
||||||
@@ -59,7 +64,9 @@ impl<R1: ModuleResolver, R2: ModuleResolver> ModuleResolver for ChainedResolver<
|
|||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Rc<Module>, Box<EvalAltResult>> {
|
) -> 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)?)
|
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 {
|
let mut scope = match rhai_scope {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => Scope::new(),
|
None => Scope::new(),
|
||||||
@@ -55,7 +60,8 @@ impl ParseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn code_from_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<String> {
|
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> {
|
pub fn initial_poll_listen_scope(code: &str) -> Result<Scope> {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ pub mod wifi {
|
|||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
.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();
|
let mut result = Array::new();
|
||||||
for line in stdout.lines() {
|
for line in stdout.lines() {
|
||||||
@@ -40,7 +41,8 @@ pub mod wifi {
|
|||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to run airport: {e}"))?;
|
.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();
|
let mut result = Array::new();
|
||||||
for line in stdout.lines().skip(1) {
|
for line in stdout.lines().skip(1) {
|
||||||
@@ -100,7 +102,8 @@ pub mod wifi {
|
|||||||
.args(&["-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"])
|
.args(&["-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
.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();
|
let mut map = Map::new();
|
||||||
if let Some(line) = stdout.lines().find(|l| l.starts_with("yes:")) {
|
if let Some(line) = stdout.lines().find(|l| l.starts_with("yes:")) {
|
||||||
let parts: Vec<&str> = line.split(':').collect();
|
let parts: Vec<&str> = line.split(':').collect();
|
||||||
@@ -144,7 +147,10 @@ pub mod wifi {
|
|||||||
args.push("password");
|
args.push("password");
|
||||||
args.push(pw);
|
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() {
|
if status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@@ -196,7 +202,8 @@ pub mod wifi {
|
|||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
.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
|
let ssid = stdout
|
||||||
.lines()
|
.lines()
|
||||||
@@ -243,8 +250,10 @@ pub mod wifi {
|
|||||||
pub fn disable_adapter() -> Result<(), Box<EvalAltResult>> {
|
pub fn disable_adapter() -> Result<(), Box<EvalAltResult>> {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
let status =
|
let status = Command::new("nmcli")
|
||||||
Command::new("nmcli").args(&["networking", "off"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
.args(&["networking", "off"])
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@@ -275,8 +284,10 @@ pub mod wifi {
|
|||||||
pub fn enable_adapter() -> Result<(), Box<EvalAltResult>> {
|
pub fn enable_adapter() -> Result<(), Box<EvalAltResult>> {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
let status =
|
let status = Command::new("nmcli")
|
||||||
Command::new("nmcli").args(&["networking", "on"]).status().map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
.args(&["networking", "on"])
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("Failed to run nmcli: {e}"))?;
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ pub mod env {
|
|||||||
pub fn get_current_dir() -> Result<String, Box<EvalAltResult>> {
|
pub fn get_current_dir() -> Result<String, Box<EvalAltResult>> {
|
||||||
std::env::current_dir()
|
std::env::current_dir()
|
||||||
.map_err(|e| format!("Failed to get CURRENT DIRECTORY: {e}").into())
|
.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)]
|
#[rhai_fn(return_raw)]
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ pub mod json {
|
|||||||
// parse a JSON string into a Dynamic representing serde_json::Value
|
// parse a JSON string into a Dynamic representing serde_json::Value
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn parse_json(json_str: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
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
|
// Turn a dyn JSON val back to a string
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use rhai::exported_module;
|
|||||||
use rhai::module_resolvers::StaticModuleResolver;
|
use rhai::module_resolvers::StaticModuleResolver;
|
||||||
|
|
||||||
pub fn register_stdlib(resolver: &mut 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
|
// adding modules
|
||||||
let text_mod = exported_module!(text);
|
let text_mod = exported_module!(text);
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ pub mod monitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_resolutions_str() -> String {
|
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) {
|
pub fn dimensions(index: i64) -> (i64, i64, i64, i64) {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ pub mod text {
|
|||||||
pub fn to_slug(text: &str) -> String {
|
pub fn to_slug(text: &str) -> String {
|
||||||
let lower = text.to_lowercase();
|
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 words = sanitized.split_whitespace();
|
||||||
let slug = words.collect::<Vec<_>>().join("-");
|
let slug = words.collect::<Vec<_>>().join("-");
|
||||||
@@ -14,7 +17,10 @@ pub mod text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_camel_case(text: &str) -> String {
|
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();
|
let words = cleaned.split_whitespace();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ use tokio::io::BufReader;
|
|||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::watch;
|
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("")) {
|
let cmd = match get_string_prop(&props, "cmd", Some("")) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -27,9 +27,13 @@ use tokio::sync::mpsc::UnboundedSender;
|
|||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|
||||||
pub type ReactiveVarStore = Arc<RwLock<HashMap<String, String>>>;
|
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()
|
// Enter node is the WidgetNode of Enter()
|
||||||
// it is the very root of every config.
|
// it is the very root of every config.
|
||||||
let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new()));
|
let store: ReactiveVarStore = Arc::new(RwLock::new(HashMap::new()));
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ use tokio::process::Command;
|
|||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tokio::time::sleep;
|
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
|
// Parse polling interval
|
||||||
let interval = get_duration_prop(&props, "interval", Some(Duration::from_secs(1)));
|
let interval = get_duration_prop(&props, "interval", Some(Duration::from_secs(1)));
|
||||||
let interval = interval.expect("Error parsing interval property of poll");
|
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>,
|
id_to_info: &mut HashMap<u64, WidgetInfo>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let id = hash_props_and_type(props, widget_type);
|
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);
|
id_to_info.insert(id, info);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ pub trait Host {
|
|||||||
/// removed items.
|
/// removed items.
|
||||||
pub async fn register_as_host(
|
pub async fn register_as_host(
|
||||||
con: &zbus::Connection,
|
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?;
|
let snw = proxy::StatusNotifierWatcherProxy::new(con).await?;
|
||||||
|
|
||||||
// get a well-known name
|
// get a well-known name
|
||||||
@@ -39,14 +40,17 @@ pub async fn register_as_host(
|
|||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
let wellknown = format!("org.freedesktop.StatusNotifierHost-{}-{}", pid, i);
|
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];
|
let flags = [zbus::fdo::RequestNameFlags::DoNotQueue];
|
||||||
match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? {
|
match con.request_name_with_flags(&wellknown, flags.into_iter().collect()).await? {
|
||||||
PrimaryOwner => break wellknown,
|
PrimaryOwner => break wellknown,
|
||||||
Exists => {}
|
Exists => {}
|
||||||
AlreadyOwner => {}
|
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
|
/// 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
|
/// recommended to call this via something like `tokio::spawn` that runs this in the
|
||||||
/// background.
|
/// 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.
|
// Replacement for ? operator since we're not returning a Result.
|
||||||
macro_rules! try_ {
|
macro_rules! try_ {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
@@ -116,7 +123,11 @@ pub async fn run_host(host: &mut dyn Host, snw: &proxy::StatusNotifierWatcherPro
|
|||||||
host.add_item(svc, item);
|
host.add_item(svc, item);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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.
|
/// 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> {
|
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");
|
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,
|
Ok(pb) => pb,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("failed to load \"image-missing\" from default theme: {}", 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.
|
/// 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.
|
/// 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
|
pixmaps
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.max_by(|(w1, h1, _), (w2, h2, _)| {
|
.max_by(|(w1, h1, _), (w2, h2, _)| {
|
||||||
@@ -190,7 +194,9 @@ pub async fn load_icon_from_sni(
|
|||||||
},
|
},
|
||||||
Err(zbus::Error::FDO(e)) => match *e {
|
Err(zbus::Error::FDO(e)) => match *e {
|
||||||
// property not existing is an expected error
|
// 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))),
|
_ => Err(IconError::DBusPixmap(zbus::Error::FDO(e))),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -65,16 +65,24 @@ impl Item {
|
|||||||
} else if service.starts_with(':') {
|
} else if service.starts_with(':') {
|
||||||
(
|
(
|
||||||
service.to_owned(),
|
service.to_owned(),
|
||||||
resolve_pathless_address(con, service, "/".to_owned())
|
resolve_pathless_address(con, service, "/".to_owned()).await?.ok_or_else(
|
||||||
.await?
|
|| {
|
||||||
.ok_or_else(|| zbus::Error::Failure(format!("no StatusNotifierItem found for {service}")))?,
|
zbus::Error::Failure(format!(
|
||||||
|
"no StatusNotifierItem found for {service}"
|
||||||
|
))
|
||||||
|
},
|
||||||
|
)?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return Err(zbus::Error::Address(service.to_owned()));
|
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 })
|
Ok(Self { sni, gtk_menu: None })
|
||||||
}
|
}
|
||||||
@@ -95,7 +103,12 @@ impl Item {
|
|||||||
Ok(())
|
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 {
|
if let Some(menu) = &self.gtk_menu {
|
||||||
menu.popup_at_pointer(event.downcast_ref::<gtk::gdk::Event>());
|
menu.popup_at_pointer(event.downcast_ref::<gtk::gdk::Event>());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -131,12 +144,21 @@ struct DBusInterface {
|
|||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_pathless_address(con: &zbus::Connection, service: &str, path: String) -> zbus::Result<Option<String>> {
|
async fn resolve_pathless_address(
|
||||||
let introspection_xml =
|
con: &zbus::Connection,
|
||||||
IntrospectableProxy::builder(con).destination(service)?.path(path.as_str())?.build().await?.introspect().await?;
|
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 =
|
let dbus_node = quick_xml::de::from_str::<DBusNode>(&introspection_xml)
|
||||||
quick_xml::de::from_str::<DBusNode>(&introspection_xml).map_err(|err| zbus::Error::Failure(err.to_string()))?;
|
.map_err(|err| zbus::Error::Failure(err.to_string()))?;
|
||||||
|
|
||||||
if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") {
|
if dbus_node.interface.iter().any(|interface| interface.name == "org.kde.StatusNotifierItem") {
|
||||||
// This item implements the desired interface, so bubble it back up
|
// 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)));
|
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() {
|
if path.is_some() {
|
||||||
// Return the first item found from a child
|
// Return the first item found from a child
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ impl Watcher {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if removed_last {
|
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);
|
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 {
|
if let Err(e) = Watcher::registered_status_notifier_items_refresh(&ctxt).await {
|
||||||
log::error!("failed to signal Watcher: {}", e);
|
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);
|
log::error!("failed to signal Watcher: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,11 +163,17 @@ impl Watcher {
|
|||||||
|
|
||||||
/// StatusNotifierItemRegistered signal
|
/// StatusNotifierItemRegistered signal
|
||||||
#[dbus_interface(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
|
/// StatusNotifierItemUnregistered signal
|
||||||
#[dbus_interface(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
|
/// RegisteredStatusNotifierItems property
|
||||||
#[dbus_interface(property)]
|
#[dbus_interface(property)]
|
||||||
@@ -208,7 +217,9 @@ impl Watcher {
|
|||||||
|
|
||||||
/// Equivalent to `is_status_notifier_host_registered_invalidate`, but without requiring
|
/// Equivalent to `is_status_notifier_host_registered_invalidate`, but without requiring
|
||||||
/// `self`.
|
/// `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(
|
zbus::fdo::Properties::properties_changed(
|
||||||
ctxt,
|
ctxt,
|
||||||
Self::name(),
|
Self::name(),
|
||||||
@@ -219,7 +230,9 @@ impl Watcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to `registered_status_notifier_items_invalidate`, but without requiring `self`.
|
/// 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(
|
zbus::fdo::Properties::properties_changed(
|
||||||
ctxt,
|
ctxt,
|
||||||
Self::name(),
|
Self::name(),
|
||||||
@@ -279,7 +292,10 @@ async fn parse_service<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for a DBus service to disappear
|
/// 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 dbus = zbus::fdo::DBusProxy::new(con).await?;
|
||||||
let mut owner_changes = dbus.receive_name_owner_changed_with_args(&[(0, &service)]).await?;
|
let mut owner_changes = dbus.receive_name_owner_changed_with_args(&[(0, &service)]).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
max_width = 130
|
max_width = 100
|
||||||
use_field_init_shorthand = true
|
use_field_init_shorthand = true
|
||||||
|
|
||||||
# these where set when we where still on nightly
|
# these where set when we where still on nightly
|
||||||
|
|||||||
Reference in New Issue
Block a user