1 /*
2 * This file is part of brisk-menu.
3 *
4 * Copyright © 2017-2020 Brisk Menu Developers
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #define _GNU_SOURCE
13
14 #include "styles.h"
15 #include "util.h"
16
17 BRISK_BEGIN_PEDANTIC
18 #include "category-button.h"
19 #include "dash-entry-button.h"
20 #include "dash-window.h"
21 #include <glib/gi18n.h>
22 BRISK_END_PEDANTIC
23
24 G_DEFINE_TYPE(BriskDashWindow, brisk_dash_window, BRISK_TYPE_MENU_WINDOW)
25
26 static void brisk_dash_window_associate_category(BriskMenuWindow *self, GtkWidget *button);
27 static void brisk_dash_window_on_toggled(BriskMenuWindow *self, GtkWidget *button);
28 static gboolean brisk_dash_window_on_enter(BriskMenuWindow *self, GdkEvent *event,
29 GtkWidget *button);
30 static void brisk_dash_window_load_css(GtkSettings *settings, const gchar *key,
31 BriskDashWindow *self);
32 static void brisk_dash_window_key_activate(BriskDashWindow *self, gpointer v);
33 static void brisk_dash_window_activated(BriskMenuWindow *self, GtkFlowBoxChild *row, gpointer v);
34 static void brisk_dash_window_set_filters_enabled(BriskDashWindow *self, gboolean enabled);
35 static gboolean brisk_dash_window_filter_apps(GtkFlowBoxChild *row, gpointer v);
36 static gint brisk_dash_window_sort(GtkFlowBoxChild *row1, GtkFlowBoxChild *row2, gpointer v);
37
38 /**
39 * brisk_dash_window_dispose:
40 *
41 * Clean up a BriskDashWindow instance
42 */
brisk_dash_window_dispose(GObject * obj)43 static void brisk_dash_window_dispose(GObject *obj)
44 {
45 BriskDashWindow *self = BRISK_DASH_WINDOW(obj);
46 GdkScreen *screen = NULL;
47
48 if (self->css) {
49 screen = gtk_widget_get_screen(GTK_WIDGET(self));
50 gtk_style_context_remove_provider_for_screen(screen, GTK_STYLE_PROVIDER(self->css));
51 g_clear_object(&self->css);
52 }
53
54 G_OBJECT_CLASS(brisk_dash_window_parent_class)->dispose(obj);
55 }
56
brisk_dash_window_get_id(__brisk_unused__ BriskMenuWindow * window)57 static const gchar *brisk_dash_window_get_id(__brisk_unused__ BriskMenuWindow *window)
58 {
59 return "dash";
60 }
61
brisk_dash_window_get_display_name(__brisk_unused__ BriskMenuWindow * window)62 static const gchar *brisk_dash_window_get_display_name(__brisk_unused__ BriskMenuWindow *window)
63 {
64 return _("Dash");
65 }
66
brisk_dash_window_update_screen_position(BriskMenuWindow * self)67 static void brisk_dash_window_update_screen_position(BriskMenuWindow *self)
68 {
69 GdkScreen *screen = NULL;
70 GtkAllocation relative_alloc = { 0 };
71 GdkWindow *window = NULL;
72 GdkRectangle geom = { 0 };
73 gint relative_x, relative_y = 0; /* Real X, Y of the applet, on screen */
74 gint window_width, window_height = 0; /* Window width & height */
75 gint mon = 0; /* Monitor to display on */
76 gint window_x, window_y = 0; /* Target X, Y */
77
78 if (!self->relative_to) {
79 g_warning("Cannot set relative location without relative widget!");
80 return;
81 }
82
83 /* Forcibly realize the applet window */
84 if (!gtk_widget_get_realized(self->relative_to)) {
85 gtk_widget_realize(self->relative_to);
86 }
87
88 /* Forcibly realize ourselves */
89 if (!gtk_widget_get_realized(GTK_WIDGET(self))) {
90 gtk_widget_realize(GTK_WIDGET(self));
91 }
92
93 gtk_widget_get_allocation(self->relative_to, &relative_alloc);
94
95 /* Find out where we are on screen */
96 window = gtk_widget_get_window(self->relative_to);
97 gdk_window_get_origin(window, &relative_x, &relative_y);
98
99 /* Grab the geometry for the monitor we're currently on */
100 screen = gtk_widget_get_screen(self->relative_to);
101 mon = gdk_screen_get_monitor_at_point(screen, relative_x, relative_y);
102 gdk_screen_get_monitor_geometry(screen, mon, &geom);
103
104 switch (self->position) {
105 case GTK_POS_LEFT:
106 window_width = geom.width - relative_alloc.width;
107 window_height = geom.height;
108 window_x = geom.x + relative_alloc.width;
109 window_y = geom.y;
110 break;
111 case GTK_POS_RIGHT:
112 window_width = geom.width - relative_alloc.width;
113 window_height = geom.height;
114 window_x = geom.x;
115 window_y = geom.y;
116 break;
117 case GTK_POS_TOP:
118 /* Top panel, appear below it */
119 window_width = geom.width;
120 window_height = geom.height - relative_alloc.height;
121 window_x = geom.x;
122 window_y = relative_y + relative_alloc.height;
123 break;
124 case GTK_POS_BOTTOM:
125 default:
126 /* Bottom panel, appear above it */
127 window_width = geom.width;
128 window_height = geom.height - relative_alloc.height;
129 window_x = geom.x;
130 window_y = geom.y;
131 break;
132 }
133
134 gtk_window_set_default_size(GTK_WINDOW(self), window_width, window_height);
135 gtk_window_move(GTK_WINDOW(self), window_x, window_y);
136 }
137
138 /**
139 * Backend has new items for us, add to the global store
140 */
brisk_dash_window_add_item(BriskMenuWindow * self,BriskItem * item,__brisk_unused__ BriskBackend * backend)141 static void brisk_dash_window_add_item(BriskMenuWindow *self, BriskItem *item,
142 __brisk_unused__ BriskBackend *backend)
143 {
144 BriskMenuEntryButton *button = NULL;
145 const gchar *item_id = brisk_item_get_id(item);
146
147 button = brisk_dash_entry_button_new(self->launcher, item);
148 g_signal_connect_swapped(button,
149 "show-context-menu",
150 G_CALLBACK(brisk_menu_window_show_context),
151 self);
152 gtk_container_add(GTK_CONTAINER(BRISK_DASH_WINDOW(self)->apps), GTK_WIDGET(button));
153 gtk_widget_show_all(GTK_WIDGET(button));
154
155 g_hash_table_insert(self->item_store, g_strdup(item_id), GTK_WIDGET(button));
156 }
157
158 /**
159 * Backend has a new sidebar section for us
160 */
brisk_dash_window_add_section(BriskMenuWindow * self,BriskSection * section,__brisk_unused__ BriskBackend * backend)161 static void brisk_dash_window_add_section(BriskMenuWindow *self, BriskSection *section,
162 __brisk_unused__ BriskBackend *backend)
163 {
164 GtkWidget *button = NULL;
165 const gchar *section_id = brisk_section_get_id(section);
166 GtkWidget *box_target = NULL;
167
168 /* Skip dupes. Sections are uniquely namespaced */
169 if (g_hash_table_lookup(self->item_store, section_id) != NULL) {
170 return;
171 }
172
173 box_target = brisk_menu_window_get_section_box(self, backend);
174 gtk_orientable_set_orientation(GTK_ORIENTABLE(box_target), GTK_ORIENTATION_HORIZONTAL);
175
176 button = brisk_dash_category_button_new(section);
177 gtk_radio_button_join_group(GTK_RADIO_BUTTON(button),
178 GTK_RADIO_BUTTON(self->section_box_leader));
179 gtk_box_pack_start(GTK_BOX(box_target), button, FALSE, FALSE, 0);
180 brisk_dash_window_associate_category(self, button);
181 gtk_widget_show_all(button);
182
183 /* Avoid new dupes */
184 g_hash_table_insert(self->item_store, g_strdup(section_id), button);
185
186 brisk_menu_window_select_sections(self);
187 }
188
189 /**
190 * A backend needs us to invalidate the filters
191 */
brisk_dash_window_invalidate_filter(BriskMenuWindow * self,__brisk_unused__ BriskBackend * backend)192 static void brisk_dash_window_invalidate_filter(BriskMenuWindow *self,
193 __brisk_unused__ BriskBackend *backend)
194 {
195 gtk_flow_box_invalidate_filter(GTK_FLOW_BOX(BRISK_DASH_WINDOW(self)->apps));
196 gtk_flow_box_invalidate_sort(GTK_FLOW_BOX(BRISK_DASH_WINDOW(self)->apps));
197
198 gtk_flow_box_unselect_all(GTK_FLOW_BOX(BRISK_DASH_WINDOW(self)->apps));
199 }
200
201 /**
202 * A backend needs us to purge any data we have for it
203 */
brisk_dash_window_reset(BriskMenuWindow * self,BriskBackend * backend)204 static void brisk_dash_window_reset(BriskMenuWindow *self, BriskBackend *backend)
205 {
206 GtkWidget *box_target = NULL;
207 GList *kids = NULL, *elem = NULL;
208 const gchar *backend_id = NULL;
209
210 backend_id = brisk_backend_get_id(backend);
211
212 box_target = brisk_menu_window_get_section_box(self, backend);
213 gtk_container_foreach(GTK_CONTAINER(box_target),
214 (GtkCallback)brisk_menu_window_remove_category,
215 self);
216
217 /* Manual work for the items */
218 kids = gtk_container_get_children(GTK_CONTAINER(BRISK_DASH_WINDOW(self)->apps));
219 for (elem = kids; elem; elem = elem->next) {
220 GtkWidget *row = elem->data;
221 GtkWidget *child = NULL;
222 BriskItem *item = NULL;
223 const gchar *local_backend_id = NULL;
224 const gchar *local_id = NULL;
225
226 if (!GTK_IS_BIN(GTK_BIN(row))) {
227 continue;
228 }
229
230 child = gtk_bin_get_child(GTK_BIN(row));
231 if (!BRISK_IS_MENU_ENTRY_BUTTON(child)) {
232 continue;
233 }
234
235 g_object_get(child, "item", &item, NULL);
236 if (!item) {
237 g_warning("missing item for entry in backend '%s'", backend_id);
238 continue;
239 }
240
241 local_backend_id = brisk_item_get_backend_id(item);
242 if (!g_str_equal(backend_id, local_backend_id)) {
243 continue;
244 }
245 local_id = brisk_item_get_id(item);
246 g_hash_table_remove(self->item_store, local_id);
247 gtk_widget_destroy(row);
248 }
249 g_list_free(kids);
250 }
251
252 /**
253 * Override hiding so that we can invalidate all filters
254 */
brisk_dash_window_hide(GtkWidget * widget)255 static void brisk_dash_window_hide(GtkWidget *widget)
256 {
257 BriskDashWindow *self = NULL;
258 GtkAdjustment *adjustment = NULL;
259 autofree(GList) *selected_children = NULL;
260 GtkWidget *selected = NULL;
261
262 /* Have parent deal with it first */
263 GTK_WIDGET_CLASS(brisk_dash_window_parent_class)->hide(widget);
264
265 self = BRISK_DASH_WINDOW(widget);
266
267 /* Remove search filter */
268 gtk_entry_set_text(GTK_ENTRY(BRISK_MENU_WINDOW(self)->search), "");
269
270 brisk_menu_window_select_sections(BRISK_MENU_WINDOW(self));
271
272 /* Reset scrollbars */
273 adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(self->apps_scroll));
274 gtk_adjustment_set_value(adjustment, 0);
275 adjustment =
276 gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(self->categories_scroll));
277 gtk_adjustment_set_value(adjustment, 0);
278
279 /* Unselect any current "apps" */
280 selected_children = gtk_flow_box_get_selected_children(GTK_FLOW_BOX(self->apps));
281 selected = g_list_nth_data(selected_children, 0);
282 if (selected) {
283 gtk_flow_box_unselect_child(GTK_FLOW_BOX(self->apps), GTK_FLOW_BOX_CHILD(selected));
284 }
285 }
286
287 /**
288 * brisk_dash_window_class_init:
289 *
290 * Handle class initialisation
291 */
brisk_dash_window_class_init(BriskDashWindowClass * klazz)292 static void brisk_dash_window_class_init(BriskDashWindowClass *klazz)
293 {
294 GObjectClass *obj_class = G_OBJECT_CLASS(klazz);
295 BriskMenuWindowClass *b_class = BRISK_MENU_WINDOW_CLASS(klazz);
296 GtkWidgetClass *wid_class = GTK_WIDGET_CLASS(klazz);
297
298 /* gobject vtable hookup */
299 obj_class->dispose = brisk_dash_window_dispose;
300
301 /* Backend vtable hookup */
302 b_class->get_id = brisk_dash_window_get_id;
303 b_class->get_display_name = brisk_dash_window_get_display_name;
304 b_class->update_screen_position = brisk_dash_window_update_screen_position;
305 b_class->add_item = brisk_dash_window_add_item;
306 b_class->add_section = brisk_dash_window_add_section;
307 b_class->invalidate_filter = brisk_dash_window_invalidate_filter;
308 b_class->reset = brisk_dash_window_reset;
309
310 wid_class->hide = brisk_dash_window_hide;
311 }
312
313 /**
314 * brisk_dash_window_init:
315 *
316 * Handle construction of the BriskDashWindow
317 */
brisk_dash_window_init(BriskDashWindow * self)318 static void brisk_dash_window_init(BriskDashWindow *self)
319 {
320 GtkWidget *layout = NULL;
321 GtkWidget *widget = NULL;
322 GtkWidget *scroll = NULL;
323 GtkWidget *header = NULL;
324 GtkWidget *content = NULL;
325 GdkScreen *screen = NULL;
326 GtkStyleContext *style = NULL;
327 BriskMenuWindow *base = NULL;
328 GtkSettings *gtk_settings = NULL;
329 autofree(gchar) *txt_holder = NULL;
330
331 base = BRISK_MENU_WINDOW(self);
332
333 brisk_dash_window_load_css(gtk_settings, "gtk-theme-name", self);
334 gtk_settings = gtk_settings_get_default();
335 g_signal_connect(gtk_settings,
336 "notify::gtk-theme-name",
337 G_CALLBACK(brisk_dash_window_load_css),
338 self);
339
340 style = gtk_widget_get_style_context(GTK_WIDGET(self));
341 gtk_style_context_add_class(style, BRISK_STYLE_MAIN);
342 gtk_style_context_add_class(style, BRISK_STYLE_DASH);
343
344 brisk_menu_window_configure_grabs(base);
345
346 layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
347 gtk_container_add(GTK_CONTAINER(self), layout);
348
349 header = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
350 style = gtk_widget_get_style_context(GTK_WIDGET(header));
351 gtk_style_context_add_class(style, "header");
352 gtk_box_pack_start(GTK_BOX(layout), header, FALSE, FALSE, 0);
353
354 /* Create search entry */
355 widget = gtk_entry_new();
356 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget),
357 GTK_ENTRY_ICON_PRIMARY,
358 "edit-find-symbolic");
359 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(widget),
360 GTK_ENTRY_ICON_SECONDARY,
361 "edit-clear-symbolic");
362 gtk_box_pack_start(GTK_BOX(header), widget, FALSE, FALSE, 20);
363 gtk_entry_set_width_chars(GTK_ENTRY(widget), 55);
364 gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
365 gtk_widget_set_halign(widget, GTK_ALIGN_CENTER);
366 /* Translators: This is the text shown in the "search box" with no content */
367 txt_holder = g_strdup_printf("%s\u2026", _("Type to search"));
368 gtk_entry_set_placeholder_text(GTK_ENTRY(widget), txt_holder);
369 base->search = widget;
370 g_signal_connect_swapped(widget, "changed", G_CALLBACK(brisk_menu_window_search), self);
371 g_signal_connect_swapped(widget,
372 "activate",
373 G_CALLBACK(brisk_dash_window_key_activate),
374 self);
375 g_signal_connect(widget, "icon-press", G_CALLBACK(brisk_menu_window_clear_search), self);
376
377 /* Scrollbar for categories */
378 scroll = gtk_scrolled_window_new(NULL, NULL);
379 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_NONE);
380 gtk_box_pack_start(GTK_BOX(header), scroll, FALSE, FALSE, 0);
381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
382 GTK_POLICY_AUTOMATIC,
383 GTK_POLICY_NEVER);
384 gtk_widget_set_can_focus(scroll, TRUE);
385 gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(scroll), TRUE);
386 self->categories_scroll = scroll;
387
388 /* Container for categories */
389 widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
390 gtk_container_add(GTK_CONTAINER(scroll), widget);
391 base->section_box_holder = widget;
392
393 style = gtk_widget_get_style_context(widget);
394 gtk_style_context_add_class(style, "section-box-holder");
395
396 /* Content layout */
397 content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
398 gtk_box_pack_start(GTK_BOX(layout), content, TRUE, TRUE, 0);
399
400 /* Scrollbar for apps */
401 scroll = gtk_scrolled_window_new(NULL, NULL);
402 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_NONE);
403 gtk_box_pack_start(GTK_BOX(content), scroll, TRUE, TRUE, 0);
404 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
405 GTK_POLICY_NEVER,
406 GTK_POLICY_AUTOMATIC);
407 gtk_scrolled_window_set_overlay_scrolling(GTK_SCROLLED_WINDOW(scroll), FALSE);
408 self->apps_scroll = scroll;
409
410 /* Application launcher display */
411 widget = gtk_flow_box_new();
412 gtk_widget_set_margin_top(widget, 50);
413 gtk_widget_set_margin_bottom(widget, 50);
414 gtk_widget_set_margin_start(widget, 150);
415 gtk_widget_set_margin_end(widget, 150);
416 gtk_container_add(GTK_CONTAINER(scroll), widget);
417 self->apps = widget;
418 g_object_set(self->apps, "halign", GTK_ALIGN_FILL, "valign", GTK_ALIGN_START, NULL);
419 gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(widget), 80);
420 gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(widget), 60);
421 gtk_flow_box_set_homogeneous(GTK_FLOW_BOX(widget), TRUE);
422 gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->apps), TRUE);
423 gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->apps), GTK_SELECTION_SINGLE);
424 g_signal_connect_swapped(self->apps,
425 "child-activated",
426 G_CALLBACK(brisk_dash_window_activated),
427 self);
428
429 screen = gtk_widget_get_screen(widget);
430 GdkVisual *vis = gdk_screen_get_rgba_visual(screen);
431 if (vis) {
432 gtk_widget_set_visual(GTK_WIDGET(self), vis);
433 }
434
435 brisk_dash_window_set_filters_enabled(self, FALSE);
436
437 /* Special leader to control group association, hidden from view */
438 base->section_box_leader = gtk_radio_button_new(NULL);
439 gtk_box_pack_start(GTK_BOX(base->section_box_holder),
440 base->section_box_leader,
441 FALSE,
442 FALSE,
443 0);
444 gtk_widget_set_no_show_all(base->section_box_leader, TRUE);
445 gtk_widget_hide(base->section_box_leader);
446
447 brisk_dash_window_set_filters_enabled(self, TRUE);
448
449 /* Hook up keyboard events */
450 g_signal_connect(self,
451 "key-release-event",
452 G_CALLBACK(brisk_menu_window_key_release),
453 NULL);
454 g_signal_connect(self, "key-press-event", G_CALLBACK(brisk_menu_window_key_press), NULL);
455
456 brisk_menu_window_init_backends(base);
457
458 gtk_widget_show_all(layout);
459 }
460
461 /**
462 * brisk_dash_window_new:
463 *
464 * Return a newly created BriskDashWindow
465 */
brisk_dash_window_new(GtkWidget * relative_to)466 BriskMenuWindow *brisk_dash_window_new(GtkWidget *relative_to)
467 {
468 return g_object_new(BRISK_TYPE_DASH_WINDOW,
469 "type",
470 GTK_WINDOW_POPUP,
471 "relative-to",
472 relative_to,
473 NULL);
474 }
475
476 /**
477 * brisk_dash_window_associate_category:
478 *
479 * This will hook up the category button for events to enable us to filter the
480 * list based on the active category.
481 */
brisk_dash_window_associate_category(BriskMenuWindow * self,GtkWidget * button)482 static void brisk_dash_window_associate_category(BriskMenuWindow *self, GtkWidget *button)
483 {
484 g_signal_connect_swapped(button, "toggled", G_CALLBACK(brisk_dash_window_on_toggled), self);
485 g_signal_connect_swapped(button,
486 "enter-notify-event",
487 G_CALLBACK(brisk_dash_window_on_enter),
488 self);
489 g_signal_connect_swapped(button,
490 "focus-in-event",
491 G_CALLBACK(brisk_dash_window_on_enter),
492 self);
493 }
494
495 /**
496 * Fired by clicking a category button
497 */
brisk_dash_window_on_toggled(BriskMenuWindow * self,GtkWidget * button)498 static void brisk_dash_window_on_toggled(BriskMenuWindow *self, GtkWidget *button)
499 {
500 BriskDashCategoryButton *cat = NULL;
501
502 /* Skip a double signal due to using a group */
503 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
504 return;
505 }
506
507 cat = BRISK_DASH_CATEGORY_BUTTON(button);
508 g_object_get(cat, "section", &self->active_section, NULL);
509
510 /* Start the filter. */
511 brisk_dash_window_invalidate_filter(self, NULL);
512 }
513
514 /**
515 * Fired by entering into the category button
516 */
brisk_dash_window_on_enter(BriskMenuWindow * self,__brisk_unused__ GdkEvent * event,GtkWidget * button)517 static gboolean brisk_dash_window_on_enter(BriskMenuWindow *self, __brisk_unused__ GdkEvent *event,
518 GtkWidget *button)
519 {
520 GtkToggleButton *but = GTK_TOGGLE_BUTTON(button);
521
522 /* Whether we're in rollover mode */
523 if (!self->rollover) {
524 return GDK_EVENT_PROPAGATE;
525 }
526
527 if (gtk_toggle_button_get_active(but) || !gtk_widget_get_visible(button)) {
528 return GDK_EVENT_PROPAGATE;
529 }
530
531 /* Force activation */
532 gtk_widget_grab_focus(button);
533 gtk_toggle_button_set_active(but, TRUE);
534
535 return GDK_EVENT_PROPAGATE;
536 }
537
538 /**
539 * Load up the CSS assets
540 */
brisk_dash_window_load_css(GtkSettings * settings,const gchar * key,BriskDashWindow * self)541 static void brisk_dash_window_load_css(GtkSettings *settings, const gchar *key,
542 BriskDashWindow *self)
543 {
544 GtkCssProvider *css = NULL;
545 GtkStyleContext *context = NULL;
546 autofree(GFile) *file = NULL;
547 autofree(GError) *err = NULL;
548 GdkScreen *screen = NULL;
549 GdkRGBA color;
550
551 file = g_file_new_for_uri("resource://com/solus-project/brisk/menu/dash/styling.css");
552
553 context = gtk_widget_get_style_context(GTK_WIDGET(self));
554 if (gtk_style_context_lookup_color(context, "dark_bg_color", &color)) {
555 file = g_file_new_for_uri(
556 "resource://com/solus-project/brisk/menu/dash/styling-light.css");
557 }
558
559 if (!file) {
560 return;
561 }
562
563 screen = gtk_widget_get_screen(GTK_WIDGET(self));
564
565 if (self->css) {
566 gtk_style_context_remove_provider_for_screen(screen, GTK_STYLE_PROVIDER(self->css));
567 g_clear_object(&self->css);
568 }
569
570 css = gtk_css_provider_new();
571 self->css = css;
572
573 gtk_style_context_add_provider_for_screen(screen,
574 GTK_STYLE_PROVIDER(css),
575 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
576
577 if (!gtk_css_provider_load_from_file(css, file, &err)) {
578 g_warning("Failed to load CSS: %s\n", err->message);
579 return;
580 }
581 }
582
brisk_dash_window_key_activate(BriskDashWindow * self,__brisk_unused__ gpointer v)583 static void brisk_dash_window_key_activate(BriskDashWindow *self, __brisk_unused__ gpointer v)
584 {
585 autofree(GList) *kids = NULL;
586 GList *elem = NULL;
587 BriskMenuEntryButton *button = NULL;
588
589 kids = gtk_container_get_children(GTK_CONTAINER(self->apps));
590
591 for (elem = kids; elem; elem = elem->next) {
592 GtkWidget *widget = elem->data;
593
594 if (!gtk_widget_get_visible(widget) || !gtk_widget_get_child_visible(widget)) {
595 continue;
596 }
597
598 button = BRISK_MENU_ENTRY_BUTTON(gtk_bin_get_child(GTK_BIN(widget)));
599 break;
600 }
601 if (!button) {
602 return;
603 }
604 brisk_menu_entry_button_launch(button);
605 }
606
brisk_dash_window_activated(__brisk_unused__ BriskMenuWindow * self,GtkFlowBoxChild * row,__brisk_unused__ gpointer v)607 static void brisk_dash_window_activated(__brisk_unused__ BriskMenuWindow *self,
608 GtkFlowBoxChild *row, __brisk_unused__ gpointer v)
609 {
610 BriskMenuEntryButton *button = NULL;
611 GtkWidget *child = NULL;
612
613 child = gtk_bin_get_child(GTK_BIN(row));
614 if (!child) {
615 return;
616 }
617 if (!BRISK_IS_MENU_ENTRY_BUTTON(child)) {
618 return;
619 }
620 button = BRISK_MENU_ENTRY_BUTTON(child);
621 brisk_menu_entry_button_launch(button);
622 }
623
624 /**
625 * Enable or disable the filters between building of the menus
626 */
brisk_dash_window_set_filters_enabled(BriskDashWindow * self,gboolean enabled)627 static void brisk_dash_window_set_filters_enabled(BriskDashWindow *self, gboolean enabled)
628 {
629 BRISK_MENU_WINDOW(self)->filtering = enabled;
630 if (enabled) {
631 gtk_flow_box_set_filter_func(GTK_FLOW_BOX(self->apps),
632 brisk_dash_window_filter_apps,
633 self,
634 NULL);
635 gtk_flow_box_set_sort_func(GTK_FLOW_BOX(self->apps),
636 brisk_dash_window_sort,
637 self,
638 NULL);
639 return;
640 }
641 gtk_flow_box_set_filter_func(GTK_FLOW_BOX(self->apps), NULL, NULL, NULL);
642 gtk_flow_box_set_sort_func(GTK_FLOW_BOX(self->apps), NULL, NULL, NULL);
643 }
644
645 /**
646 * brisk_dash_window_filter_apps:
647 *
648 * Responsible for filtering the selection based on active group or search
649 * term.
650 */
brisk_dash_window_filter_apps(GtkFlowBoxChild * row,gpointer v)651 __brisk_pure__ static gboolean brisk_dash_window_filter_apps(GtkFlowBoxChild *row, gpointer v)
652 {
653 BriskMenuWindow *self = NULL;
654 GtkWidget *child = NULL;
655
656 self = BRISK_MENU_WINDOW(v);
657
658 if (!self->filtering) {
659 return FALSE;
660 }
661
662 /* Grab our Entry widget */
663 child = gtk_bin_get_child(GTK_BIN(row));
664
665 return brisk_menu_window_filter_apps(self, child);
666 }
667
brisk_dash_window_sort(GtkFlowBoxChild * row1,GtkFlowBoxChild * row2,gpointer v)668 static gint brisk_dash_window_sort(GtkFlowBoxChild *row1, GtkFlowBoxChild *row2, gpointer v)
669 {
670 GtkWidget *child1, *child2 = NULL;
671 BriskItem *itemA, *itemB = NULL;
672 autofree(gchar) *nameA = NULL;
673 autofree(gchar) *nameB = NULL;
674 BriskMenuWindow *self = NULL;
675
676 self = BRISK_MENU_WINDOW(v);
677
678 child1 = gtk_bin_get_child(GTK_BIN(row1));
679 child2 = gtk_bin_get_child(GTK_BIN(row2));
680
681 g_object_get(child1, "item", &itemA, NULL);
682 g_object_get(child2, "item", &itemB, NULL);
683
684 return brisk_menu_window_sort(self, itemA, itemB);
685 }
686
687 /*
688 * Editor modelines - https://www.wireshark.org/tools/modelines.html
689 *
690 * Local variables:
691 * c-basic-offset: 8
692 * tab-width: 8
693 * indent-tabs-mode: nil
694 * End:
695 *
696 * vi: set shiftwidth=8 tabstop=8 expandtab:
697 * :indentSize=8:tabSize=8:noTabs=true:
698 */
699