status icon: Add support for scroll events
ref:
https://github.com/linuxmint/cinnamon/issues/9152
Michael Webster
2 years ago
136 | 136 | gir = gnome.generate_gir(libxapp, |
137 | 137 | namespace: 'XApp', |
138 | 138 | nsversion: '1.0', |
139 | sources: xapp_headers + xapp_sources + dbus_headers, | |
139 | sources: xapp_headers + xapp_sources + dbus_headers + xapp_enums, | |
140 | 140 | identifier_prefix: 'XApp', |
141 | 141 | symbol_prefix: 'xapp_', |
142 | 142 | includes: ['GObject-2.0', 'Gtk-3.0'], |
16 | 16 | <arg name='time' direction='in' type='u'/> |
17 | 17 | <arg name='panel_position' direction='in' type='i'/> |
18 | 18 | </method> |
19 | <method name='Scroll'> | |
20 | <arg name='delta' direction='in' type='i'/> | |
21 | <arg name='orientation' direction='in' type='i'/> | |
22 | <arg name='time' direction='in' type='u'/> | |
23 | </method> | |
19 | 24 | <property type='s' name='Name' access='read'/> |
20 | 25 | <property type='s' name='IconName' access='read'/> |
21 | 26 | <property type='s' name='TooltipText' access='read'/> |
36 | 36 | BUTTON_RELEASE, |
37 | 37 | ACTIVATE, |
38 | 38 | STATE_CHANGED, |
39 | SCROLL, | |
39 | 40 | LAST_SIGNAL |
40 | 41 | }; |
41 | 42 | |
157 | 158 | return "Fallback"; |
158 | 159 | case XAPP_STATUS_ICON_STATE_NO_SUPPORT: |
159 | 160 | return "NoSupport"; |
161 | default: | |
162 | return "Unknown"; | |
163 | } | |
164 | } | |
165 | ||
166 | static const gchar * | |
167 | direction_to_str (XAppScrollDirection direction) | |
168 | { | |
169 | switch (direction) | |
170 | { | |
171 | case XAPP_SCROLL_UP: | |
172 | return "Up"; | |
173 | case XAPP_SCROLL_DOWN: | |
174 | return "Down"; | |
175 | case XAPP_SCROLL_LEFT: | |
176 | return "Left"; | |
177 | case XAPP_SCROLL_RIGHT: | |
178 | return "Right"; | |
160 | 179 | default: |
161 | 180 | return "Unknown"; |
162 | 181 | } |
437 | 456 | return TRUE; |
438 | 457 | } |
439 | 458 | |
459 | static gboolean | |
460 | handle_scroll_method (XAppStatusIconInterface *skeleton, | |
461 | GDBusMethodInvocation *invocation, | |
462 | gint delta, | |
463 | XAppScrollDirection direction, | |
464 | guint _time, | |
465 | XAppStatusIcon *icon) | |
466 | { | |
467 | g_debug ("XAppStatusIcon: received Scroll from monitor %s: " | |
468 | "delta: %d , direction: %s , time: %u", | |
469 | g_dbus_method_invocation_get_sender (invocation), | |
470 | delta, direction_to_str (direction), _time); | |
471 | ||
472 | g_signal_emit(icon, signals[SCROLL], 0, | |
473 | delta, | |
474 | direction, | |
475 | _time); | |
476 | ||
477 | xapp_status_icon_interface_complete_scroll (skeleton, | |
478 | invocation); | |
479 | ||
480 | return TRUE; | |
481 | } | |
482 | ||
440 | 483 | static void |
441 | 484 | popup_gtk_status_icon_with_menu (XAppStatusIcon *icon, |
442 | 485 | GtkMenu *menu, |
723 | 766 | static SkeletonSignal skeleton_signals[] = { |
724 | 767 | // signal name callback |
725 | 768 | { "handle-button-press", handle_click_method }, |
726 | { "handle-button-release", handle_click_method } | |
769 | { "handle-button-release", handle_click_method }, | |
770 | { "handle-scroll", handle_scroll_method } | |
727 | 771 | }; |
728 | 772 | |
729 | 773 | static gboolean |
1343 | 1387 | 0, |
1344 | 1388 | NULL, NULL, NULL, |
1345 | 1389 | G_TYPE_NONE, 1, XAPP_TYPE_STATUS_ICON_STATE); |
1390 | ||
1391 | /** | |
1392 | * XAppStatusIcon::scroll-event: | |
1393 | * @icon: The #XAppStatusIcon | |
1394 | * @amount: The amount of movement for the scroll event | |
1395 | * @direction: the #XAppScrollDirection of the scroll event | |
1396 | * @time: The time supplied by the event, or 0 | |
1397 | * | |
1398 | * Gets emitted when the user uses the mouse scroll wheel over the status icon. | |
1399 | * For the most part, amounts will always be 1, unless an applet supports smooth | |
1400 | * scrolling. Generally the direction value is most important. | |
1401 | */ | |
1402 | signals [SCROLL] = | |
1403 | g_signal_new ("scroll-event", | |
1404 | XAPP_TYPE_STATUS_ICON, | |
1405 | G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, | |
1406 | 0, | |
1407 | NULL, NULL, NULL, | |
1408 | G_TYPE_NONE, 3, G_TYPE_INT, XAPP_TYPE_SCROLL_DIRECTION, G_TYPE_UINT); | |
1346 | 1409 | } |
1347 | 1410 | |
1348 | 1411 | /** |
27 | 27 | XAPP_STATUS_ICON_STATE_NO_SUPPORT |
28 | 28 | } XAppStatusIconState; |
29 | 29 | |
30 | /** | |
31 | * XAppScrollDirection: | |
32 | * @XAPP_SCROLL_UP: Scroll theoretical content up. | |
33 | * @XAPP_SCROLL_DOWN: Scroll theoretical content down. | |
34 | * @XAPP_SCROLL_LEFT: Scroll theoretical content left. | |
35 | * @XAPP_SCROLL_RIGHT: Scroll theoretical content right. | |
36 | * | |
37 | * Represents the direction of icon scroll events. | |
38 | */ | |
39 | typedef enum | |
40 | { | |
41 | XAPP_SCROLL_UP, | |
42 | XAPP_SCROLL_DOWN, | |
43 | XAPP_SCROLL_LEFT, | |
44 | XAPP_SCROLL_RIGHT | |
45 | } XAppScrollDirection; | |
46 | ||
47 | ||
30 | 48 | XAppStatusIcon *xapp_status_icon_new (void); |
31 | 49 | void xapp_status_icon_set_name (XAppStatusIcon *icon, const gchar *name); |
32 | 50 | void xapp_status_icon_set_icon_name (XAppStatusIcon *icon, const gchar *icon_name); |
66 | 66 | # this is the bus owned name |
67 | 67 | self.name = self.proxy.get_name() |
68 | 68 | |
69 | self.add_events(Gdk.EventMask.SCROLL_MASK) | |
70 | ||
69 | 71 | # this is (usually) the name of the remote process |
70 | 72 | self.proc_name = self.proxy.props.name |
71 | 73 | |
98 | 100 | |
99 | 101 | self.connect("button-press-event", self.on_button_press) |
100 | 102 | self.connect("button-release-event", self.on_button_release) |
103 | self.connect("scroll-event", self.on_scroll) | |
101 | 104 | self.connect("enter-notify-event", self.on_enter_notify) |
102 | 105 | self.connect("leave-notify-event", self.on_leave_notify) |
103 | 106 | |
217 | 220 | GObject.timeout_add(200, self.after_release_idle) |
218 | 221 | |
219 | 222 | return Gdk.EVENT_PROPAGATE |
223 | ||
224 | def on_scroll(self, widget, event): | |
225 | has, direction = event.get_scroll_direction() | |
226 | ||
227 | x_dir = XApp.ScrollDirection.UP | |
228 | delta = 0 | |
229 | ||
230 | if direction != Gdk.ScrollDirection.SMOOTH: | |
231 | x_dir = XApp.ScrollDirection(int(direction)) | |
232 | ||
233 | if direction == Gdk.ScrollDirection.UP: | |
234 | delta = -1 | |
235 | elif direction == Gdk.ScrollDirection.DOWN: | |
236 | delta = 1 | |
237 | elif direction == Gdk.ScrollDirection.LEFT: | |
238 | delta = -1 | |
239 | elif direction == Gdk.ScrollDirection.RIGHT: | |
240 | delta = 1 | |
241 | ||
242 | self.proxy.call_scroll_sync(delta, x_dir, event.time, None) | |
220 | 243 | |
221 | 244 | def calc_menu_origin(self, widget, orientation): |
222 | 245 | alloc = widget.get_allocation() |
1 | 1 | import gi |
2 | 2 | gi.require_version('Gtk', '3.0') |
3 | 3 | gi.require_version('XApp', '1.0') |
4 | from gi.repository import Gio, GLib, GObject, Gtk, XApp | |
4 | from gi.repository import Gio, GLib, GObject, Gtk, XApp, Gdk | |
5 | 5 | import os |
6 | 6 | import sys |
7 | 7 | |
15 | 15 | |
16 | 16 | self.proxy = icon |
17 | 17 | self.name = self.proxy.get_name() |
18 | self.add_events(Gdk.EventMask.SCROLL_MASK) | |
18 | 19 | |
19 | 20 | box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) |
20 | 21 | |
39 | 40 | |
40 | 41 | self.connect("button-press-event", self.on_button_press) |
41 | 42 | self.connect("button-release-event", self.on_button_release) |
43 | self.connect("scroll-event", self.on_scroll) | |
42 | 44 | |
43 | 45 | def on_icon_name_changed(self, proxy, gparamspec, data=None): |
44 | 46 | string = self.proxy.props.icon_name |
75 | 77 | time = event.time |
76 | 78 | print ("Button release : %d:%d" % (x, y)) |
77 | 79 | self.proxy.call_button_release_sync(x, y, event.button, event.time, Gtk.PositionType.TOP, None) |
80 | ||
81 | def on_scroll(self, widget, event): | |
82 | has, direction = event.get_scroll_direction() | |
83 | ||
84 | x_dir = XApp.ScrollDirection.UP | |
85 | delta = 0 | |
86 | ||
87 | if direction != Gdk.ScrollDirection.SMOOTH: | |
88 | x_dir = XApp.ScrollDirection(int(direction)) | |
89 | ||
90 | if direction == Gdk.ScrollDirection.UP: | |
91 | delta = -1 | |
92 | elif direction == Gdk.ScrollDirection.DOWN: | |
93 | delta = 1 | |
94 | elif direction == Gdk.ScrollDirection.LEFT: | |
95 | delta = -1 | |
96 | elif direction == Gdk.ScrollDirection.RIGHT: | |
97 | delta = 1 | |
98 | ||
99 | print ("Scroll : delta: %d, orientation: %d" % (delta, x_dir)) | |
100 | self.proxy.call_scroll_sync(delta, x_dir, event.time, None) | |
78 | 101 | |
79 | 102 | class StatusApplet(GObject.Object): |
80 | 103 |
21 | 21 | self.status_icon.set_tooltip_text("Testing primary activate and secondary menu") |
22 | 22 | self.status_icon.set_label("label 1") |
23 | 23 | self.status_icon.set_visible(True) |
24 | self.status_icon.connect("scroll-event", self.handle_scroll_event) | |
25 | ||
26 | self.label = None | |
27 | self.window = None | |
24 | 28 | |
25 | 29 | self.counter = 1 |
26 | 30 | |
47 | 51 | print("Activated via button %d" % button) |
48 | 52 | self.counter = 0 |
49 | 53 | self.status_icon.set_label("label %d" % self.counter) |
54 | self.make_window() | |
55 | ||
56 | def make_window(self): | |
57 | w = Gtk.Window(default_width=300, default_height=130) | |
58 | b = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) | |
59 | w.add(b) | |
60 | self.label = Gtk.Label("How can I help you?") | |
61 | b.pack_start(self.label, True, True, 0) | |
62 | ||
63 | self.window = w | |
64 | w.show_all() | |
65 | ||
66 | def handle_scroll_event(self, icon, amount, orientation, time, data=None): | |
67 | if self.window == None: | |
68 | self.make_window() | |
69 | ||
70 | if orientation == XApp.ScrollDirection.UP: | |
71 | self.label.set_text("Scrolled Up !") | |
72 | elif orientation == XApp.ScrollDirection.DOWN: | |
73 | self.label.set_text("Scrolled Down!") | |
74 | elif orientation == XApp.ScrollDirection.LEFT: | |
75 | self.label.set_text("Scrolled Left!") | |
76 | else: | |
77 | self.label.set_text("Scrolled Right!") | |
78 | ||
50 | 79 | |
51 | 80 | if __name__ == '__main__': |
52 | 81 | GLib.setenv ("G_MESSAGES_DEBUG", "all", True) |
16 | 16 | class App(GObject.Object): |
17 | 17 | def __init__(self): |
18 | 18 | super(App, self).__init__() |
19 | self.window = None | |
20 | ||
19 | 21 | self.indicator = AppIndicator3.Indicator.new("xapp-status-icon-via-libappindicator", |
20 | 22 | "info", |
21 | 23 | AppIndicator3.IndicatorCategory.SYSTEM_SERVICES) |
38 | 40 | |
39 | 41 | self.indicator.set_menu(self.menu) |
40 | 42 | |
43 | self.indicator.connect("scroll-event", self.handle_scroll_event) | |
41 | 44 | GLib.timeout_add_seconds(2, self.on_timeout_cb) |
42 | 45 | |
43 | 46 | def activate_window(self, item, data=None): |
44 | 47 | w = Gtk.Window(default_width=300, default_height=130) |
45 | 48 | b = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) |
46 | 49 | w.add(b) |
47 | b.pack_start(Gtk.Label("How can I help you?"), True, True, 0) | |
50 | self.label = Gtk.Label("How can I help you?") | |
51 | b.pack_start(self.label, True, True, 0) | |
52 | ||
53 | self.window = w | |
48 | 54 | w.show_all() |
55 | ||
56 | def handle_scroll_event(self, icon, amount, direction, data=None): | |
57 | if self.window == None: | |
58 | self.activate_window(None) | |
59 | ||
60 | if direction == Gdk.ScrollDirection.UP: | |
61 | self.label.set_text("Scrolled Up!") | |
62 | elif direction == Gdk.ScrollDirection.DOWN: | |
63 | self.label.set_text("Scrolled Down!") | |
64 | elif direction == Gdk.ScrollDirection.LEFT: | |
65 | self.label.set_text("Scrolled Left!") | |
66 | else: | |
67 | self.label.set_text("Scrolled Right!") | |
49 | 68 | |
50 | 69 | def on_timeout_cb(self): |
51 | 70 | self.counter += 1 |
21 | 21 | self.status_icon.set_tooltip_text("Testing primary and secondary menus") |
22 | 22 | self.status_icon.set_label("label 1") |
23 | 23 | self.status_icon.set_visible(True) |
24 | self.status_icon.connect("scroll-event", self.handle_scroll_event) | |
24 | 25 | |
25 | 26 | self.counter = 1 |
27 | ||
28 | self.label = None | |
29 | self.window = None | |
26 | 30 | |
27 | 31 | menu = Gtk.Menu() |
28 | 32 | menu.append(Gtk.MenuItem.new_with_label("Engage the hyperdrive")) |
50 | 54 | self.status_icon.set_label("label %d" % self.counter) |
51 | 55 | return True |
52 | 56 | |
57 | def make_window(self): | |
58 | w = Gtk.Window(default_width=300, default_height=130) | |
59 | b = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) | |
60 | w.add(b) | |
61 | self.label = Gtk.Label("How can I help you?") | |
62 | b.pack_start(self.label, True, True, 0) | |
63 | ||
64 | self.window = w | |
65 | w.show_all() | |
66 | ||
67 | def handle_scroll_event(self, icon, amount, orientation, time, data=None): | |
68 | if self.window == None: | |
69 | self.make_window() | |
70 | ||
71 | if orientation == XApp.ScrollDirection.UP: | |
72 | self.label.set_text("Scrolled Up !") | |
73 | elif orientation == XApp.ScrollDirection.DOWN: | |
74 | self.label.set_text("Scrolled Down!") | |
75 | elif orientation == XApp.ScrollDirection.LEFT: | |
76 | self.label.set_text("Scrolled Left!") | |
77 | else: | |
78 | self.label.set_text("Scrolled Right!") | |
79 | ||
53 | 80 | if __name__ == '__main__': |
54 | 81 | GLib.setenv ("G_MESSAGES_DEBUG", "all", True) |
55 | 82 | app = App() |
22 | 22 | self.status_icon.set_label("label 1") |
23 | 23 | self.status_icon.set_visible(True) |
24 | 24 | |
25 | self.label = None | |
26 | self.window = None | |
27 | ||
25 | 28 | self.counter = 1 |
26 | 29 | |
27 | 30 | self.status_icon.connect("activate", self.on_status_icon_activate) |
31 | self.status_icon.connect("scroll-event", self.handle_scroll_event) | |
28 | 32 | |
29 | 33 | GLib.timeout_add_seconds(2, self.on_timeout_cb) |
30 | 34 | |
40 | 44 | print("Activated via button %d" % button) |
41 | 45 | self.counter = 0 |
42 | 46 | self.status_icon.set_label("label %d" % self.counter) |
47 | self.make_window() | |
48 | ||
49 | def make_window(self): | |
50 | w = Gtk.Window(default_width=300, default_height=130) | |
51 | b = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) | |
52 | w.add(b) | |
53 | self.label = Gtk.Label("How can I help you?") | |
54 | b.pack_start(self.label, True, True, 0) | |
55 | ||
56 | self.window = w | |
57 | w.show_all() | |
58 | ||
59 | def handle_scroll_event(self, icon, amount, orientation, time, data=None): | |
60 | if self.window == None: | |
61 | self.make_window() | |
62 | ||
63 | if orientation == XApp.ScrollDirection.UP: | |
64 | self.label.set_text("Scrolled Up !") | |
65 | elif orientation == XApp.ScrollDirection.DOWN: | |
66 | self.label.set_text("Scrolled Down!") | |
67 | elif orientation == XApp.ScrollDirection.LEFT: | |
68 | self.label.set_text("Scrolled Left!") | |
69 | else: | |
70 | self.label.set_text("Scrolled Right!") | |
43 | 71 | |
44 | 72 | if __name__ == '__main__': |
45 | 73 | GLib.setenv ("G_MESSAGES_DEBUG", "all", True) |
45 | 45 | self.xapp_icon.connect("activate", self.on_xapp_icon_activated) |
46 | 46 | self.xapp_icon.connect("button-press-event", self.on_xapp_button_pressed) |
47 | 47 | self.xapp_icon.connect("button-release-event", self.on_xapp_button_released) |
48 | self.xapp_icon.connect("scroll-event", self.on_xapp_scroll_event) | |
48 | 49 | self.xapp_icon.connect("state-changed", self.xapp_icon_state_changed); |
49 | 50 | |
50 | 51 | def xapp_icon_state_changed(self, state, data=None): |
82 | 83 | def on_xapp_button_released(self, icon, x, y, button, _time, panel_position): |
83 | 84 | if not self.gtk_menu: |
84 | 85 | self.sn_item.show_context_menu(button, x, y) |
86 | ||
87 | def on_xapp_scroll_event(self, icon, delta, direction, _time): | |
88 | o_str = "horizontal" if direction in (XApp.ScrollDirection.LEFT, XApp.ScrollDirection.RIGHT) else "vertical" | |
89 | ||
90 | self.sn_item.scroll(delta, o_str) | |
85 | 91 | |
86 | 92 | def update_menu(self): |
87 | 93 | # print("ItemIsMenu: ", self.sn_item.item_is_menu()) |
169 | 169 | if button == Gdk.BUTTON_SECONDARY: |
170 | 170 | self.sn_item_proxy.call_context_menu(x, y, None, None) |
171 | 171 | |
172 | def scroll(self, delta, o_str): | |
173 | self.sn_item_proxy.call_scroll(delta, o_str, None, None) | |
174 | ||
172 | 175 | def destroy(self): |
173 | 176 | try: |
174 | 177 | self.sn_item_proxy.disconnect_by_func(self.signal_received) |