mirror of
https://github.com/elkowar/eww.git
synced 2024-10-05 15:40:12 +03:00
systray: handle mouse click events
This commit is contained in:
parent
f80e093223
commit
361b8d1b66
@ -7,6 +7,7 @@ use crate::{
|
||||
paths::EwwPaths,
|
||||
script_var_handler::ScriptVarHandlerHandle,
|
||||
state::scope_graph::{ScopeGraph, ScopeIndex},
|
||||
widgets::window::Window,
|
||||
window_arguments::WindowArguments,
|
||||
window_initiator::WindowInitiator,
|
||||
*,
|
||||
@ -92,7 +93,7 @@ pub struct EwwWindow {
|
||||
pub instance_id: String,
|
||||
pub name: String,
|
||||
pub scope_index: ScopeIndex,
|
||||
pub gtk_window: gtk::Window,
|
||||
pub gtk_window: Window,
|
||||
pub destroy_event_handler_id: Option<glib::SignalHandlerId>,
|
||||
}
|
||||
|
||||
@ -524,15 +525,21 @@ fn initialize_window<B: DisplayBackend>(
|
||||
window_scope: ScopeIndex,
|
||||
) -> Result<EwwWindow> {
|
||||
let monitor_geometry = monitor.geometry();
|
||||
let window = B::initialize_window(window_init, monitor_geometry)
|
||||
let (actual_window_rect, x, y) = match window_init.geometry {
|
||||
Some(geometry) => {
|
||||
let rect = get_window_rectangle(geometry, monitor_geometry);
|
||||
(Some(rect), rect.x(), rect.y())
|
||||
}
|
||||
_ => (None, 0, 0),
|
||||
};
|
||||
let window = B::initialize_window(window_init, monitor_geometry, x, y)
|
||||
.with_context(|| format!("monitor {} is unavailable", window_init.monitor.clone().unwrap()))?;
|
||||
|
||||
window.set_title(&format!("Eww - {}", window_init.name));
|
||||
window.set_position(gtk::WindowPosition::None);
|
||||
window.set_gravity(gdk::Gravity::Center);
|
||||
|
||||
if let Some(geometry) = window_init.geometry {
|
||||
let actual_window_rect = get_window_rectangle(geometry, monitor_geometry);
|
||||
if let Some(actual_window_rect) = actual_window_rect {
|
||||
window.set_size_request(actual_window_rect.width(), actual_window_rect.height());
|
||||
window.set_default_size(actual_window_rect.width(), actual_window_rect.height());
|
||||
}
|
||||
@ -575,11 +582,7 @@ fn initialize_window<B: DisplayBackend>(
|
||||
|
||||
/// Apply the provided window-positioning rules to the window.
|
||||
#[cfg(feature = "x11")]
|
||||
fn apply_window_position(
|
||||
mut window_geometry: WindowGeometry,
|
||||
monitor_geometry: gdk::Rectangle,
|
||||
window: >k::Window,
|
||||
) -> Result<()> {
|
||||
fn apply_window_position(mut window_geometry: WindowGeometry, monitor_geometry: gdk::Rectangle, window: &Window) -> Result<()> {
|
||||
let gdk_window = window.window().context("Failed to get gdk window from gtk window")?;
|
||||
window_geometry.size = Coords::from_pixels(window.size());
|
||||
let actual_window_rect = get_window_rectangle(window_geometry, monitor_geometry);
|
||||
@ -593,7 +596,7 @@ fn apply_window_position(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) {
|
||||
fn on_screen_changed(window: &Window, _old_screen: Option<&gdk::Screen>) {
|
||||
let visual = gtk::prelude::GtkWindowExt::screen(window)
|
||||
.and_then(|screen| screen.rgba_visual().filter(|_| screen.is_composited()).or_else(|| screen.system_visual()));
|
||||
window.set_visual(visual.as_ref());
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::window_initiator::WindowInitiator;
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub use platform_wayland::WaylandBackend;
|
||||
@ -9,7 +9,7 @@ pub use platform_x11::{set_xprops, X11Backend};
|
||||
pub trait DisplayBackend: Send + Sync + 'static {
|
||||
const IS_X11: bool;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle) -> Option<gtk::Window>;
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window>;
|
||||
}
|
||||
|
||||
pub struct NoBackend;
|
||||
@ -17,14 +17,14 @@ pub struct NoBackend;
|
||||
impl DisplayBackend for NoBackend {
|
||||
const IS_X11: bool = false;
|
||||
|
||||
fn initialize_window(_window_init: &WindowInitiator, _monitor: gdk::Rectangle) -> Option<gtk::Window> {
|
||||
Some(gtk::Window::new(gtk::WindowType::Toplevel))
|
||||
fn initialize_window(_window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
Some(Window::new(gtk::WindowType::Toplevel, x, y))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
mod platform_wayland {
|
||||
use crate::window_initiator::WindowInitiator;
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
use gtk::prelude::*;
|
||||
use yuck::config::{window_definition::WindowStacking, window_geometry::AnchorAlignment};
|
||||
|
||||
@ -35,8 +35,8 @@ mod platform_wayland {
|
||||
impl DisplayBackend for WaylandBackend {
|
||||
const IS_X11: bool = false;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle) -> Option<gtk::Window> {
|
||||
let window = gtk::Window::new(gtk::WindowType::Toplevel);
|
||||
fn initialize_window(window_init: &WindowInitiator, monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
let window = Window::new(gtk::WindowType::Toplevel, x, y);
|
||||
// Initialising a layer shell surface
|
||||
gtk_layer_shell::init_for_window(&window);
|
||||
// Sets the monitor where the surface is shown
|
||||
@ -112,7 +112,7 @@ mod platform_wayland {
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
mod platform_x11 {
|
||||
use crate::window_initiator::WindowInitiator;
|
||||
use crate::{widgets::window::Window, window_initiator::WindowInitiator};
|
||||
use anyhow::{Context, Result};
|
||||
use gdk::Monitor;
|
||||
use gtk::{self, prelude::*};
|
||||
@ -135,10 +135,10 @@ mod platform_x11 {
|
||||
impl DisplayBackend for X11Backend {
|
||||
const IS_X11: bool = true;
|
||||
|
||||
fn initialize_window(window_init: &WindowInitiator, _monitor: gdk::Rectangle) -> Option<gtk::Window> {
|
||||
fn initialize_window(window_init: &WindowInitiator, _monitor: gdk::Rectangle, x: i32, y: i32) -> Option<Window> {
|
||||
let window_type =
|
||||
if window_init.backend_options.x11.wm_ignore { gtk::WindowType::Popup } else { gtk::WindowType::Toplevel };
|
||||
let window = gtk::Window::new(window_type);
|
||||
let window = Window::new(window_type, x, y);
|
||||
window.set_resizable(window_init.resizable);
|
||||
window.set_keep_above(window_init.stacking == WindowStacking::Foreground);
|
||||
window.set_keep_below(window_init.stacking == WindowStacking::Background);
|
||||
@ -151,7 +151,7 @@ mod platform_x11 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_xprops(window: >k::Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
pub fn set_xprops(window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
let backend = X11BackendConnection::new()?;
|
||||
backend.set_xprops_for(window, monitor, window_init)?;
|
||||
Ok(())
|
||||
@ -171,7 +171,7 @@ mod platform_x11 {
|
||||
Ok(X11BackendConnection { conn, root_window: screen.root, atoms })
|
||||
}
|
||||
|
||||
fn set_xprops_for(&self, window: >k::Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
fn set_xprops_for(&self, window: &Window, monitor: Monitor, window_init: &WindowInitiator) -> Result<()> {
|
||||
let monitor_rect = monitor.geometry();
|
||||
let scale_factor = monitor.scale_factor() as u32;
|
||||
let gdk_window = window.window().context("Couldn't get gdk window from gtk window")?;
|
||||
|
@ -7,6 +7,7 @@ pub mod graph;
|
||||
mod systray;
|
||||
pub mod transform;
|
||||
pub mod widget_definitions;
|
||||
pub mod window;
|
||||
|
||||
/// Run a command that was provided as an attribute.
|
||||
/// This command may use placeholders which will be replaced by the values of the arguments given.
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::widgets::window::Window;
|
||||
use futures::StreamExt;
|
||||
use gtk::{cairo::Surface, gdk::ffi::gdk_cairo_surface_create_from_pixbuf, prelude::*};
|
||||
use notifier_host;
|
||||
use std::{future::Future, rc::Rc};
|
||||
|
||||
// DBus state shared between systray instances, to avoid creating too many connections etc.
|
||||
struct DBusSession {
|
||||
@ -23,6 +25,11 @@ async fn dbus_session() -> zbus::Result<&'static DBusSession> {
|
||||
.await
|
||||
}
|
||||
|
||||
fn run_async_task<F: Future>(f: F) -> F::Output {
|
||||
let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().expect("Failed to initialize tokio runtime");
|
||||
rt.block_on(f)
|
||||
}
|
||||
|
||||
pub struct Props {
|
||||
icon_size_tx: tokio::sync::watch::Sender<i32>,
|
||||
}
|
||||
@ -156,6 +163,46 @@ impl Item {
|
||||
let scale = icon.scale_factor();
|
||||
load_icon_for_item(&icon, &item, *icon_size.borrow_and_update(), scale).await;
|
||||
|
||||
let item = Rc::new(item);
|
||||
let window =
|
||||
widget.toplevel().expect("Failed to obtain toplevel window").downcast::<Window>().expect("Failed to downcast window");
|
||||
widget.add_events(gdk::EventMask::BUTTON_PRESS_MASK);
|
||||
widget.connect_button_press_event(glib::clone!(@strong item => move |_, evt| {
|
||||
let (x, y) = (evt.root().0 as i32 + window.x(), evt.root().1 as i32 + window.y());
|
||||
let item_is_menu = run_async_task(async { item.sni.item_is_menu().await });
|
||||
let have_item_is_menu = item_is_menu.is_ok();
|
||||
let item_is_menu = item_is_menu.unwrap_or(false);
|
||||
log::debug!(
|
||||
"mouse click button={}, x={}, y={}, have_item_is_menu={}, item_is_menu={}",
|
||||
evt.button(),
|
||||
x,
|
||||
y,
|
||||
have_item_is_menu,
|
||||
item_is_menu
|
||||
);
|
||||
|
||||
match (evt.button(), item_is_menu) {
|
||||
(gdk::BUTTON_PRIMARY, false) => {
|
||||
if let Err(e) = run_async_task(async { item.sni.activate(x, y).await }) {
|
||||
log::error!("failed to send activate event: {}", e);
|
||||
if !have_item_is_menu {
|
||||
// Some applications are in fact menu-only (don't have Activate method)
|
||||
// but don't report so through ItemIsMenu property. Fallback to menu if
|
||||
// activate failed in this case.
|
||||
return gtk::Inhibit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
(gdk::BUTTON_MIDDLE, _) => {
|
||||
if let Err(e) = run_async_task(async { item.sni.secondary_activate(x, y).await }) {
|
||||
log::error!("failed to send secondary activate event: {}", e);
|
||||
}
|
||||
}
|
||||
_ => return gtk::Inhibit(false),
|
||||
}
|
||||
gtk::Inhibit(true)
|
||||
}));
|
||||
|
||||
// updates
|
||||
let mut status_updates = item.sni.receive_new_status().await?;
|
||||
let mut title_updates = item.sni.receive_new_title().await?;
|
||||
|
64
crates/eww/src/widgets/window.rs
Normal file
64
crates/eww/src/widgets/window.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use glib::{object_subclass, wrapper};
|
||||
use glib_macros::Properties;
|
||||
use gtk::{prelude::*, subclass::prelude::*};
|
||||
use std::cell::RefCell;
|
||||
|
||||
wrapper! {
|
||||
pub struct Window(ObjectSubclass<WindowPriv>)
|
||||
@extends gtk::Window, gtk::Bin, gtk::Container, gtk::Widget, @implements gtk::Buildable;
|
||||
}
|
||||
|
||||
#[derive(Properties)]
|
||||
#[properties(wrapper_type = Window)]
|
||||
pub struct WindowPriv {
|
||||
#[property(get, name = "x", nick = "X", blurb = "Global x coordinate", default = 0)]
|
||||
x: RefCell<i32>,
|
||||
|
||||
#[property(get, name = "y", nick = "Y", blurb = "Global y coordinate", default = 0)]
|
||||
y: RefCell<i32>,
|
||||
}
|
||||
|
||||
// This should match the default values from the ParamSpecs
|
||||
impl Default for WindowPriv {
|
||||
fn default() -> Self {
|
||||
WindowPriv { x: RefCell::new(0), y: RefCell::new(0) }
|
||||
}
|
||||
}
|
||||
|
||||
#[object_subclass]
|
||||
impl ObjectSubclass for WindowPriv {
|
||||
type ParentType = gtk::Window;
|
||||
type Type = Window;
|
||||
|
||||
const NAME: &'static str = "WindowEww";
|
||||
}
|
||||
|
||||
impl Default for Window {
|
||||
fn default() -> Self {
|
||||
glib::Object::new::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(type_: gtk::WindowType, x_: i32, y_: i32) -> Self {
|
||||
let w: Self = glib::Object::builder().property("type", type_).build();
|
||||
let priv_ = w.imp();
|
||||
priv_.x.replace(x_);
|
||||
priv_.y.replace(y_);
|
||||
w
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for WindowPriv {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
impl WindowImpl for WindowPriv {}
|
||||
impl BinImpl for WindowPriv {}
|
||||
impl ContainerImpl for WindowPriv {}
|
||||
impl WidgetImpl for WindowPriv {}
|
Loading…
Reference in New Issue
Block a user