Adds :onhoverlost and separates out event based actions on eventbox widget (See #251 for more info). (#256)

* Adds :onhoverlost and fixes all the masks mistyped

* Adds eventbox widget and added deprecation error level warning on top-level widgets

* Run cargo fmt

* Fixes broken old link

* Fix nested element glitch by ignoring inferior events, improve deprecation arg processing into single operation

* Extract large deprecation msg to note
This commit is contained in:
Animesh Sahu 2021-09-12 15:31:56 +05:30 committed by GitHub
parent e1558965ff
commit 4a56b74b9a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 69 deletions

View file

@ -1,11 +1,14 @@
use crate::{
config, daemon_response::DaemonResponseSender, display_backend, error_handling_ctx, eww_state, script_var_handler::*,
config,
daemon_response::DaemonResponseSender,
display_backend, error_handling_ctx, eww_state,
gtk::prelude::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt},
script_var_handler::*,
EwwPaths,
};
use anyhow::*;
use debug_stub_derive::*;
use eww_shared_util::VarName;
use crate::gtk::prelude::{GtkWindowExt, WidgetExt, ContainerExt, CssProviderExt, StyleContextExt};
use itertools::Itertools;
use simplexpr::dynval::DynVal;
use std::collections::{HashMap, HashSet};
@ -383,9 +386,8 @@ fn initialize_window(
fn apply_window_position(
mut window_geometry: WindowGeometry,
monitor_geometry: gdk::Rectangle,
window: &gtk::Window
window: &gtk::Window,
) -> Result<()> {
let gdk_window = window.window().context("Failed to get gdk window from gtk window")?;
window_geometry.size = Coords::from_pixels(window.size());
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);

View file

@ -152,13 +152,9 @@ mod platform {
monitor_rect: gdk::Rectangle,
window_def: &EwwWindowDefinition,
) -> Result<()> {
let gdk_window = window
.window()
.context("Couldn't get gdk window from gtk window")?;
let win_id = gdk_window
.downcast_ref::<gdkx11::X11Window>()
.context("Failed to get x11 window for gtk window")?
.xid() as u32;
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
let win_id =
gdk_window.downcast_ref::<gdkx11::X11Window>().context("Failed to get x11 window for gtk window")?.xid() as u32;
let strut_def = window_def.backend_options.struts;
let root_window_geometry = self.conn.get_geometry(self.root_window)?.reply()?;

View file

@ -4,10 +4,19 @@ use crate::{
enum_parse, error::DiagError, error_handling_ctx, eww_state, resolve_block, util::list_difference, widgets::widget_node,
};
use anyhow::*;
use codespan_reporting::diagnostic::Severity;
use gdk::NotifyType;
use glib;
use gtk::{self, prelude::*};
use itertools::Itertools;
use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, time::Duration};
use once_cell::sync::Lazy;
use std::{
cell::RefCell,
cmp::Ordering,
collections::{HashMap, HashSet},
rc::Rc,
time::Duration,
};
use yuck::{
config::validate::ValidationError,
error::{AstError, AstResult, AstResultExt},
@ -26,6 +35,7 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<gtk::Widge
let gtk_widget = match bargs.widget.name.as_str() {
"box" => build_gtk_box(bargs)?.upcast(),
"centerbox" => build_center_box(bargs)?.upcast(),
"eventbox" => build_gtk_event_box(bargs)?.upcast(),
"scale" => build_gtk_scale(bargs)?.upcast(),
"progress" => build_gtk_progress(bargs)?.upcast(),
"image" => build_gtk_image(bargs)?.upcast(),
@ -52,10 +62,26 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<gtk::Widge
Ok(gtk_widget)
}
/// Deprecated attributes from top of widget hierarchy
static DEPRECATED_ATTRS: Lazy<HashSet<&str>> =
Lazy::new(|| ["timeout", "onscroll", "onhover", "cursor"].iter().cloned().collect());
/// attributes that apply to all widgets
/// @widget widget
/// @desc these properties apply to _all_ widgets, and can be used anywhere!
pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Widget) {
let deprecated: HashSet<_> = DEPRECATED_ATTRS.to_owned();
let contained_deprecated: Vec<_> = bargs.unhandled_attrs.drain_filter(|a| deprecated.contains(&a.0 as &str)).collect();
if !contained_deprecated.is_empty() {
let diag = error_handling_ctx::stringify_diagnostic(gen_diagnostic! {
kind = Severity::Error,
msg = "Unsupported attributes provided",
label = bargs.widget.span => "Found in here",
note = format!("The attribute(s) ({}) has/have been removed, as GTK does not support it consistently. Instead, use eventbox to wrap this widget and set the attribute there. See #251 (https://github.com/elkowar/eww/issues/251) for more details.", contained_deprecated.iter().join(", ")),
}).unwrap();
eprintln!("{}", diag);
}
let css_provider = gtk::CssProvider::new();
if let Ok(visible) =
@ -70,11 +96,6 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Wi
})
}
let on_scroll_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let on_hover_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let cursor_hover_enter_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let cursor_hover_leave_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, {
// @prop class - css class name
prop(class: as_string) {
@ -118,56 +139,6 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Wi
css_provider.load_from_data(format!("* {{ {} }}", style).as_bytes())?;
gtk_widget.style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION)
},
// @prop timeout - timeout of the command
// @prop onscroll - event to execute when the user scrolls with the mouse over the widget. The placeholder `{}` used in the command will be replaced with either `up` or `down`.
prop(timeout: as_duration = Duration::from_millis(200), onscroll: as_string) {
gtk_widget.add_events(gdk::EventMask::SCROLL_MASK);
gtk_widget.add_events(gdk::EventMask::SMOOTH_SCROLL_MASK);
let old_id = on_scroll_handler_id.replace(Some(
gtk_widget.connect_scroll_event(move |_, evt| {
run_command(timeout, &onscroll, if evt.delta().1 < 0f64 { "up" } else { "down" });
gtk::Inhibit(false)
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
// @prop timeout - timeout of the command
// @prop onhover - event to execute when the user hovers over the widget
prop(timeout: as_duration = Duration::from_millis(200),onhover: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
let old_id = on_hover_handler_id.replace(Some(
gtk_widget.connect_enter_notify_event(move |_, evt| {
run_command(timeout, &onhover, format!("{} {}", evt.position().0, evt.position().1));
gtk::Inhibit(false)
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
// @prop cursor - Cursor to show while hovering (see [gtk3-cursors](https://developer.gnome.org/gdk3/stable/gdk3-Cursors.html) for possible names)
prop(cursor: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
cursor_hover_enter_handler_id.replace(Some(
gtk_widget.connect_enter_notify_event(move |widget, _evt| {
let display = gdk::Display::default();
let gdk_window = widget.window();
if let (Some(display), Some(gdk_window)) = (display, gdk_window) {
gdk_window.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref());
}
gtk::Inhibit(false)
})
)).map(|id| gtk_widget.disconnect(id));
cursor_hover_leave_handler_id.replace(Some(
gtk_widget.connect_leave_notify_event(move |widget, _evt| {
let gdk_window = widget.window();
if let Some(gdk_window) = gdk_window {
gdk_window.set_cursor(None);
}
gtk::Inhibit(false)
})
)).map(|id| gtk_widget.disconnect(id));
},
});
}
@ -207,7 +178,7 @@ pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Ran
// @prop onchange - command executed once the value is changes. The placeholder `{}`, used in the command will be replaced by the new value.
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
gtk_widget.set_sensitive(true);
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
gtk_widget.add_events(gdk::EventMask::PROPERTY_CHANGE_MASK);
let old_id = on_change_handler_id.replace(Some(
gtk_widget.connect_value_changed(move |gtk_widget| {
run_command(timeout, &onchange, gtk_widget.value());
@ -449,7 +420,7 @@ fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
onmiddleclick: as_string = "",
onrightclick: as_string = ""
) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
gtk_widget.add_events(gdk::EventMask::BUTTON_PRESS_MASK);
let old_id = on_click_handler_id.replace(Some(
gtk_widget.connect_button_press_event(move |_, evt| {
match evt.button() {
@ -548,6 +519,93 @@ fn build_center_box(bargs: &mut BuilderArgs) -> Result<gtk::Box> {
}
}
/// @widget eventbox extends container
/// @desc a container which can receive events and must contain exactly one child
fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
let gtk_widget = gtk::EventBox::new();
let on_scroll_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let on_hover_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let on_hover_leave_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let cursor_hover_enter_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
let cursor_hover_leave_handler_id: EventHandlerId = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, {
// @prop timeout - timeout of the command
// @prop onscroll - event to execute when the user scrolls with the mouse over the widget. The placeholder `{}` used in the command will be replaced with either `up` or `down`.
prop(timeout: as_duration = Duration::from_millis(200), onscroll: as_string) {
gtk_widget.add_events(gdk::EventMask::SCROLL_MASK);
gtk_widget.add_events(gdk::EventMask::SMOOTH_SCROLL_MASK);
let old_id = on_scroll_handler_id.replace(Some(
gtk_widget.connect_scroll_event(move |_, evt| {
run_command(timeout, &onscroll, if evt.delta().1 < 0f64 { "up" } else { "down" });
gtk::Inhibit(false)
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
// @prop timeout - timeout of the command
// @prop onhover - event to execute when the user hovers over the widget
prop(timeout: as_duration = Duration::from_millis(200), onhover: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
let old_id = on_hover_handler_id.replace(Some(
gtk_widget.connect_enter_notify_event(move |_, evt| {
if evt.detail() != NotifyType::Inferior {
run_command(timeout, &onhover, format!("{} {}", evt.position().0, evt.position().1));
}
gtk::Inhibit(false)
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
// @prop timeout - timeout of the command
// @prop onhoverlost - event to execute when the user losts hovers over the widget
prop(timeout: as_duration = Duration::from_millis(200), onhoverlost: as_string) {
gtk_widget.add_events(gdk::EventMask::LEAVE_NOTIFY_MASK);
let old_id = on_hover_leave_handler_id.replace(Some(
gtk_widget.connect_leave_notify_event(move |_, evt| {
if evt.detail() != NotifyType::Inferior {
run_command(timeout, &onhoverlost, format!("{} {}", evt.position().0, evt.position().1));
}
gtk::Inhibit(false)
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
// @prop cursor - Cursor to show while hovering (see [gtk3-cursors](https://docs.gtk.org/gdk3/ctor.Cursor.new_from_name.html) for possible names)
prop(cursor: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
gtk_widget.add_events(gdk::EventMask::LEAVE_NOTIFY_MASK);
cursor_hover_enter_handler_id.replace(Some(
gtk_widget.connect_enter_notify_event(move |widget, _evt| {
if _evt.detail() != NotifyType::Inferior {
let display = gdk::Display::default();
let gdk_window = widget.window();
if let (Some(display), Some(gdk_window)) = (display, gdk_window) {
gdk_window.set_cursor(gdk::Cursor::from_name(&display, &cursor).as_ref());
}
}
gtk::Inhibit(false)
})
)).map(|id| gtk_widget.disconnect(id));
cursor_hover_leave_handler_id.replace(Some(
gtk_widget.connect_leave_notify_event(move |widget, _evt| {
if _evt.detail() != NotifyType::Inferior {
let gdk_window = widget.window();
if let Some(gdk_window) = gdk_window {
gdk_window.set_cursor(None);
}
}
gtk::Inhibit(false)
})
)).map(|id| gtk_widget.disconnect(id));
},
});
Ok(gtk_widget)
}
/// @widget label
/// @desc A text widget giving you more control over how the text is displayed
fn build_gtk_label(bargs: &mut BuilderArgs) -> Result<gtk::Label> {