diff --git a/resources/login-shell.css b/resources/css/login-shell.css similarity index 79% rename from resources/login-shell.css rename to resources/css/login-shell.css index 476560c..38bd4f3 100644 --- a/resources/login-shell.css +++ b/resources/css/login-shell.css @@ -1,4 +1,4 @@ -@import url("resource:///dev/iohub/diya/shell/virtual-keyboard.css"); +@import url("resource:///dev/iohub/diya/shell/css/virtual-keyboard.css"); #diya_login_shell { diff --git a/resources/session-shell.css b/resources/css/session-shell.css similarity index 78% rename from resources/session-shell.css rename to resources/css/session-shell.css index 89795fd..82e8a71 100644 --- a/resources/session-shell.css +++ b/resources/css/session-shell.css @@ -1,4 +1,4 @@ -@import url("resource:///dev/iohub/diya/shell/virtual-keyboard.css"); +@import url("resource:///dev/iohub/diya/shell/css/virtual-keyboard.css"); diya-taskbar { diff --git a/resources/virtual-keyboard.css b/resources/css/virtual-keyboard.css similarity index 100% rename from resources/virtual-keyboard.css rename to resources/css/virtual-keyboard.css diff --git a/resources/gresource-login.xml b/resources/gresource-login.xml index 3df818d..478bfb0 100644 --- a/resources/gresource-login.xml +++ b/resources/gresource-login.xml @@ -1,9 +1,10 @@ - - resources/login-shell.css - resources/virtual-keyboard.css - resources/default.keymap - + + resources/css/login-shell.css + resources/css/virtual-keyboard.css + + + resources/vkb/default.keymap \ No newline at end of file diff --git a/resources/gresource-session.xml b/resources/gresource-session.xml index f89f093..9b50c8a 100644 --- a/resources/gresource-session.xml +++ b/resources/gresource-session.xml @@ -1,10 +1,17 @@ - - resources/session-shell.css - resources/virtual-keyboard.css - resources/default.keymap - resources/ui/dashboard.ui - resources/ui/taskbar.ui + + resources/vkb/default.keymap + + + resources/ui/dashboard.ui + resources/ui/taskbar.ui + + + resources/css/session-shell.css + resources/css/virtual-keyboard.css + + + resources/icons/scalable/gear.svg \ No newline at end of file diff --git a/resources/icons/scalable/gear.svg b/resources/icons/scalable/gear.svg new file mode 100644 index 0000000..91ffeed --- /dev/null +++ b/resources/icons/scalable/gear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/ui/taskbar.ui b/resources/ui/taskbar.ui index 815276f..0818a63 100644 --- a/resources/ui/taskbar.ui +++ b/resources/ui/taskbar.ui @@ -6,6 +6,7 @@ 400 + 1 horizontal @@ -14,6 +15,19 @@ + + + + 1 + never + + + horizontal + + + + + diff --git a/resources/default.keymap b/resources/vkb/default.keymap similarity index 100% rename from resources/default.keymap rename to resources/vkb/default.keymap diff --git a/resources/tpl.keymap b/resources/vkb/tpl.keymap similarity index 100% rename from resources/tpl.keymap rename to resources/vkb/tpl.keymap diff --git a/src/foreign.c b/src/foreign.c index 11223cb..fc5b56f 100644 --- a/src/foreign.c +++ b/src/foreign.c @@ -184,6 +184,7 @@ static void toplevel_handle_app_id(void *data, assert(win); g_object_set(win, "appid", app_id, NULL); g_debug("New appid for: %s", diya_object_to_string(win)); + g_signal_emit_by_name(shell, DIYA_SIGNAL_FOREIGN_WINDOW_ADDED, (void *)win); } static void toplevel_handle_output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, @@ -218,9 +219,10 @@ static void toplevel_handle_state(void *data, wstate |= DIYA_WIN_STATE_FOCUS; break; } - g_debug("toplevel_handle_state: (%.04x) active: %d, full screen %d, maximize %d, minimize %d", wstate, wstate & DIYA_WIN_STATE_FOCUS, + g_debug("toplevel_handle_state %s: (%.04x) active: %d, full screen %d, maximize %d, minimize %d", diya_object_to_string(win), wstate, wstate & DIYA_WIN_STATE_FOCUS, wstate & DIYA_WIN_STATE_FULLSCREEN, wstate & DIYA_WIN_STATE_MAXIMIZE, wstate & DIYA_WIN_STATE_MINIMIZE); g_object_set(win, "state", wstate, NULL); + g_signal_emit_by_name(shell, DIYA_SIGNAL_FOREIGN_WINDOW_STATE_CHANGED, (void *)win, (guint)wstate); } static void toplevel_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) @@ -229,6 +231,7 @@ static void toplevel_handle_done(void *data, assert(shell); DiyaForeignWindow *win = diya_session_shell_get_window(shell, handle); assert(win); + g_debug("toplevel_handle_done: finish"); g_signal_emit_by_name(shell, DIYA_SIGNAL_FOREIGN_WINDOW_CHANGED, (void *)win); } static void toplevel_handle_closed(void *data, @@ -335,6 +338,9 @@ DiyaShell* diya_foreign_window_get_shell(DiyaForeignWindow* self) } void diya_foreign_window_set_state(DiyaForeignWindow* self, enum diya_win_state state, bool value) { + DiyaShell* shell = diya_foreign_window_get_shell(self); + DiyaWayland* wl = diya_shell_get_wayland(shell); + assert(wl); if(state & DIYA_WIN_STATE_MINIMIZE) { if(value) @@ -361,9 +367,6 @@ void diya_foreign_window_set_state(DiyaForeignWindow* self, enum diya_win_state if(state & DIYA_WIN_STATE_FULLSCREEN) { - DiyaShell* shell = diya_foreign_window_get_shell(self); - DiyaWayland* wl = diya_shell_get_wayland(shell); - assert(wl); struct wl_output* output = diya_wayland_get_output(wl, 0); if(value) { @@ -374,9 +377,56 @@ void diya_foreign_window_set_state(DiyaForeignWindow* self, enum diya_win_state zwlr_foreign_toplevel_handle_v1_unset_fullscreen(self->handle); } } + + if(state & DIYA_WIN_STATE_FOCUS) + { + if(value) + { + struct wl_seat* seat = diya_wayland_get_seat(wl); + zwlr_foreign_toplevel_handle_v1_activate(self->handle, seat); + } + } } bool diya_foreign_window_is_toplevel(DiyaForeignWindow* self) { return self->parent_win == NULL; +} + +static gint app_info_str_cmp(GAppInfo* info, const char* id) +{ + return g_strcmp0(g_app_info_get_executable(info), id); +} + +GAppInfo* diya_foreign_window_get_app_info(DiyaForeignWindow* self) +{ + if(! self->appid) + { + return NULL; + } + GList* apps = g_app_info_get_all(); + GList* found = g_list_find_custom(apps, self->appid, (GCompareFunc)app_info_str_cmp); + GAppInfo* info = NULL; + if(found && found->data) + { + assert(G_IS_APP_INFO(found->data)); + info = g_object_ref(found->data); + } + g_list_free_full(apps, g_object_unref); + return info; +} + +enum diya_win_state diya_foreign_window_get_state(DiyaForeignWindow* window) +{ + return window->state; +} + +DiyaForeignWindow* diya_foreign_window_get_top_level(DiyaForeignWindow* self) +{ + DiyaForeignWindow* win = self; + while(win->parent_win) + { + win = win->parent_win; + } + return win; } \ No newline at end of file diff --git a/src/foreign.h b/src/foreign.h index 45b7220..590f81d 100644 --- a/src/foreign.h +++ b/src/foreign.h @@ -22,5 +22,8 @@ void diya_session_shell_foreign_toplevel_register(struct wl_registry *registry, DiyaShell* diya_foreign_window_get_shell(DiyaForeignWindow* window); void diya_foreign_window_set_state(DiyaForeignWindow* window, enum diya_win_state state, bool value); +enum diya_win_state diya_foreign_window_get_state(DiyaForeignWindow* window); bool diya_foreign_window_is_toplevel(DiyaForeignWindow* window); +DiyaForeignWindow* diya_foreign_window_get_top_level(DiyaForeignWindow* window); +GAppInfo* diya_foreign_window_get_app_info(DiyaForeignWindow* window); #endif \ No newline at end of file diff --git a/src/launcher.c b/src/launcher.c index 8e05820..85cf0d9 100644 --- a/src/launcher.c +++ b/src/launcher.c @@ -25,24 +25,7 @@ enum static GParamSpec *g_prop[N_PROPERTIES] = {0}; - -static void on_foreign_window_change(DiyaSessionShell* shell, DiyaForeignWindow * win, gpointer data) -{ - (void) data; - assert(win); - assert(shell); - g_warning("on_foreign_window_change: WINDOW CHANGE %s, shell %s", diya_object_to_string(DIYA_OBJECT(win)), diya_object_to_string(DIYA_OBJECT(shell))); -} - -static void on_foreign_window_removed(DiyaSessionShell* shell, DiyaForeignWindow * win, gpointer data) -{ - (void) data; - assert(win); - assert(shell); - g_warning("on_foreign_window_removed: WINDOW REMOVED %s, shell %s", diya_object_to_string(DIYA_OBJECT(win)), diya_object_to_string(DIYA_OBJECT(shell))); -} - - +/* static void show_windows(GtkWidget *widget,gpointer data) { (void) widget; @@ -63,6 +46,7 @@ static void show_windows(GtkWidget *widget,gpointer data) } } +*/ static void diya_launcher_dispose(GObject* object) { @@ -158,46 +142,15 @@ void diya_session_shell_launcher_init(DiyaSessionShell * shell) g_object_set(shell, "launchpad", self, NULL); //g_signal_connect (GTK_WINDOW(dashboard), "destroy", G_CALLBACK (on_launcher_destroy), NULL); - - /* - GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_set_homogeneous (GTK_BOX (box), TRUE); - gtk_window_set_child (GTK_WINDOW (gtk_window), box); - gtk_widget_set_halign (box, GTK_ALIGN_CENTER); - gtk_widget_set_valign (box, GTK_ALIGN_CENTER); - GtkWidget * button = gtk_button_new_with_label ("lock"); - g_signal_connect (button, "clicked", G_CALLBACK (session_lock), shell); - gtk_box_append (GTK_BOX (box), button); - - button = gtk_button_new_with_label ("show all windows"); - g_signal_connect (button, "clicked", G_CALLBACK (show_windows), shell); - gtk_box_append (GTK_BOX (box), button); - - button = gtk_button_new_with_label ("Dashboard"); - g_signal_connect (button, "clicked", G_CALLBACK (show_dashboard), shell); - gtk_box_append (GTK_BOX (box), button); - - //g_signal_connect (gtk_window, "orientation-changed", G_CALLBACK (on_orientation_changed), NULL); - */ - gtk_window_present (GTK_WINDOW (taskbar)); - - g_signal_connect (shell, DIYA_SIGNAL_FOREIGN_WINDOW_CHANGED, G_CALLBACK (on_foreign_window_change), NULL); - g_signal_connect (shell, DIYA_SIGNAL_FOREIGN_WINDOW_REMOVED, G_CALLBACK (on_foreign_window_removed), NULL); - GList *apps = g_app_info_get_all (); - GList * l; - for (l = apps; l != NULL; l = l->next) + GtkIconTheme* theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); + char**icons = gtk_icon_theme_get_icon_names (theme); + g_warning("List all icons"); + for(int i = 0; icons[i] != NULL; i++) { - gpointer element_data = l->data; - - g_warning("%s", g_app_info_get_display_name(element_data)); - g_warning("%s", g_app_info_get_id(element_data)); - g_warning("%s", g_app_info_get_name(element_data)); - g_warning("%s", g_app_info_get_executable(element_data)); - //g_warning("%s", g_app_info_get_icon(element_data)); + g_debug("ICON: %s", icons[i]); } - g_list_free_full(apps, g_object_unref); } void diya_launcher_show_dashboard(DiyaLauncher* self, gboolean value) diff --git a/src/session-shell.c b/src/session-shell.c index 52d32c4..8b5cfb1 100644 --- a/src/session-shell.c +++ b/src/session-shell.c @@ -183,6 +183,31 @@ static void diya_session_shell_class_init(DiyaSessionShellClass *class) G_TYPE_NONE, 1, G_TYPE_POINTER); + g_signal_new(DIYA_SIGNAL_FOREIGN_WINDOW_STATE_CHANGED, + DIYA_TYPE_SESSION_SHELL, + G_SIGNAL_DETAILED | + G_SIGNAL_ACTION | + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_POINTER, + G_TYPE_UINT); + g_signal_new(DIYA_SIGNAL_FOREIGN_WINDOW_ADDED, + DIYA_TYPE_SESSION_SHELL, + G_SIGNAL_DETAILED | + G_SIGNAL_ACTION | + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); g_signal_new(DIYA_SIGNAL_FOREIGN_WINDOW_REMOVED, DIYA_TYPE_SESSION_SHELL, G_SIGNAL_DETAILED | diff --git a/src/session-shell.h b/src/session-shell.h index 0954573..080d0e7 100644 --- a/src/session-shell.h +++ b/src/session-shell.h @@ -6,6 +6,8 @@ #include "base.h" #define DIYA_SIGNAL_FOREIGN_WINDOW_CHANGED "foreign-window-changed" +#define DIYA_SIGNAL_FOREIGN_WINDOW_STATE_CHANGED "foreign-window-state-changed" +#define DIYA_SIGNAL_FOREIGN_WINDOW_ADDED "foreign-window-added" #define DIYA_SIGNAL_FOREIGN_WINDOW_REMOVED "foreign-window-removed" #define DIYA_SIGNAL_SESSION_LOCKED "session-locked" #define DIYA_SIGNAL_SESSION_UNLOCKED "session-unlocked" diff --git a/src/shell.c b/src/shell.c index 01cd8e4..1b3a929 100644 --- a/src/shell.c +++ b/src/shell.c @@ -105,7 +105,7 @@ static void diya_shell_reload_theme(DiyaShell *shell) else { g_warning("diya_shell_reload_theme: Unable to load CSS from file: %s. Fallback to default theme", css_file); - gchar* css_resource = g_strconcat("/dev/iohub/diya/shell/", priv->name, ".css", NULL); + gchar* css_resource = g_strconcat("/dev/iohub/diya/shell/css/", priv->name, ".css", NULL); gtk_css_provider_load_from_resource(priv->css_provider, css_resource); g_free(css_resource); } diff --git a/src/virtual-keyboard.c b/src/virtual-keyboard.c index ce3fccb..9ac2887 100644 --- a/src/virtual-keyboard.c +++ b/src/virtual-keyboard.c @@ -326,7 +326,7 @@ DiyaVirtualKeyboard *diya_virtual_keyboard_new(DiyaShell *shell, const gchar *ke { GError *error = NULL; g_warning("No keymap file specified. Loading default keymap from resource"); - GBytes *bytes = g_resources_lookup_data("/dev/iohub/diya/shell/default.keymap", 0, &error); + GBytes *bytes = g_resources_lookup_data("/dev/iohub/diya/shell/vkb/default.keymap", 0, &error); if (error != NULL) { g_critical("Unable to read keymap file from resource %s", error->message); diff --git a/src/widgets/dashboard-widget.c b/src/widgets/dashboard-widget.c index c58b482..9206495 100644 --- a/src/widgets/dashboard-widget.c +++ b/src/widgets/dashboard-widget.c @@ -19,6 +19,14 @@ static guint diya_dashboard_list_model_get_n_items(GListModel *list) static gpointer diya_dashboard_list_model_get_item(GListModel *list, guint position) { DiyaDashboardListModel *self = DIYA_DASHBOARD_LIST_MODEL(list); + if(self->appinfos == NULL) + { + return NULL; + } + if(position >= g_list_length(self->appinfos)) + { + return NULL; + } return g_object_ref(g_list_nth_data(self->appinfos, position)); } @@ -71,7 +79,7 @@ void diya_dashboard_list_model_append(DiyaDashboardListModel *self, GAppInfo *in g_list_model_items_changed(G_LIST_MODEL(self), g_list_length(self->appinfos) - 1, 0, 1); } -static gint diya_dashboard_app_info_cmd(GAppInfo *a, GAppInfo *b) +static gint diya_dashboard_app_info_cmp(GAppInfo *a, GAppInfo *b) { return !g_app_info_equal(a, b); } @@ -79,7 +87,7 @@ static gint diya_dashboard_app_info_cmd(GAppInfo *a, GAppInfo *b) gboolean diya_dashboard_list_model_contain(DiyaDashboardListModel *self, GAppInfo *info) { GList *found = NULL; - found = g_list_find_custom(self->appinfos, info, (GCompareFunc)diya_dashboard_app_info_cmd); + found = g_list_find_custom(self->appinfos, info, (GCompareFunc)diya_dashboard_app_info_cmp); return found != NULL; } @@ -163,11 +171,11 @@ static GtkWidget *diya_dashboard_create_app_icon_widget(void *item, void *user_d return widget; } -static int diya_dashboard_app_info_cmp(GAppInfo *a, GAppInfo *b, gpointer user_data) +static int diya_dashboard_app_info_sort_cmp(GAppInfo *a, GAppInfo *b, gpointer user_data) { (void)user_data; // Unused parameter - const gchar *name_a = g_app_info_get_id(a); - const gchar *name_b = g_app_info_get_id(b); + const gchar *name_a = g_app_info_get_display_name(a); + const gchar *name_b = g_app_info_get_display_name(b); return g_strcmp0(name_a, name_b); } @@ -229,7 +237,7 @@ static void diya_dashboard_widget_init(DiyaDashboardWidget *self) // g_signal_connect(event_controller, "key-pressed", G_CALLBACK(on_diya_dashboard_key_press), self); // gtk_widget_add_controller(GTK_WIDGET(self), event_controller); self->list_model = g_object_new(DIYA_TYPE_DASHBOARD_LIST_MODEL, NULL); - self->sorter = GTK_SORTER(gtk_custom_sorter_new((GCompareDataFunc)diya_dashboard_app_info_cmp, NULL, NULL)); + self->sorter = GTK_SORTER(gtk_custom_sorter_new((GCompareDataFunc)diya_dashboard_app_info_sort_cmp, NULL, NULL)); self->sort_model = gtk_sort_list_model_new(G_LIST_MODEL(self->list_model), self->sorter); self->filter = GTK_FILTER(gtk_custom_filter_new((GtkCustomFilterFunc)diya_app_info_filter_func, self, NULL)); self->proxy_model = gtk_filter_list_model_new(G_LIST_MODEL(self->sort_model), self->filter); @@ -322,7 +330,7 @@ static void diya_dashboard_widget_class_init(DiyaDashboardWidgetClass *class) // DiyaShellWindowClass* base_class = DIYA_SHELL_WINDOW_CLASS(class); // base_class->setup = diya_dashboard_widget_setup; - gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), "/dev/iohub/diya/shell/dashboard.ui"); + gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), "/dev/iohub/diya/shell/ui/dashboard.ui"); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), DiyaDashboardWidget, revealer); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), DiyaDashboardWidget, search_entry); gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), DiyaDashboardWidget, app_list_box); diff --git a/src/widgets/taskbar-widget.c b/src/widgets/taskbar-widget.c index 6b2ae24..bb28d92 100644 --- a/src/widgets/taskbar-widget.c +++ b/src/widgets/taskbar-widget.c @@ -1,11 +1,121 @@ #include "taskbar-widget.h" +#include "foreign.h" +#define DEFAULT_APP_ICON_NAME "application-x-executable-symbolic" + +/** + * Implementation of list model + * + */ +struct _DiyaTaskbarListModel +{ + GObject parent_instance; + GList *apps; +}; + +static guint diya_taskbar_list_model_get_n_items(GListModel *list) +{ + DiyaTaskbarListModel *self = DIYA_TASKBAR_LIST_MODEL(list); + return g_list_length(self->apps); +} + +static gpointer diya_taskbar_list_model_get_item(GListModel *list, guint position) +{ + DiyaTaskbarListModel *self = DIYA_TASKBAR_LIST_MODEL(list); + if(self->apps == NULL) + { + return NULL; + } + if(position >= g_list_length(self->apps)) + { + return NULL; + } + return g_object_ref(g_list_nth_data(self->apps, position)); +} + +static GType diya_taskbar_list_model_get_item_type(GListModel *list) +{ + (void)list; // Unused parameter + return DIYA_TYPE_FOREIGN_WINDOW; +} + +static void diya_taskbar_list_model_interface_init(GListModelInterface *iface) +{ + iface->get_n_items = diya_taskbar_list_model_get_n_items; + iface->get_item = diya_taskbar_list_model_get_item; + iface->get_item_type = diya_taskbar_list_model_get_item_type; +} + +G_DEFINE_TYPE_WITH_CODE(DiyaTaskbarListModel, diya_taskbar_list_model, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, diya_taskbar_list_model_interface_init)) + +static void diya_taskbar_list_model_finalize(GObject *object) +{ + g_debug("diya_taskbar_list_model_finalize"); + DiyaTaskbarListModel *self = DIYA_TASKBAR_LIST_MODEL(object); + g_list_free_full(self->apps, g_object_unref); + G_OBJECT_CLASS(diya_taskbar_list_model_parent_class)->finalize(object); +} + +static void diya_taskbar_list_model_dispose(GObject *object) +{ + (void)object; + g_debug("diya_taskbar_list_model_dispose"); + G_OBJECT_CLASS(diya_taskbar_list_model_parent_class)->dispose(object); +} + +static void diya_taskbar_list_model_init(DiyaTaskbarListModel *self) +{ + self->apps = NULL; +} + +static void diya_taskbar_list_model_class_init(DiyaTaskbarListModelClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + gobject_class->finalize = diya_taskbar_list_model_finalize; + gobject_class->dispose = diya_taskbar_list_model_dispose; +} + +void diya_taskbar_list_model_append(DiyaTaskbarListModel *self, DiyaForeignWindow *window) +{ + self->apps = g_list_append(self->apps, g_object_ref(window)); + // self->appinfos = g_list_reverse(self->appinfos); + g_list_model_items_changed(G_LIST_MODEL(self), g_list_length(self->apps) - 1, 0, 1); +} + +void diya_taskbar_list_model_remove(DiyaTaskbarListModel *self, DiyaForeignWindow *window) +{ + gint index = g_list_index(self->apps, window); + if (index < 0) + { + return; + } + self->apps = g_list_remove(self->apps, window); + g_object_unref(window); + g_list_model_items_changed(G_LIST_MODEL(self), index, 1, 0); +} + +gboolean diya_taskbar_list_model_contain(DiyaTaskbarListModel *self, DiyaForeignWindow *info) +{ + GList *found = NULL; + found = g_list_find(self->apps, info); + return found != NULL; +} + +/** + * Implement of taskbar widget + * + */ struct _DiyaTaskbarWidget { DiyaShellWindow parent; GtkWidget *btn_toggle; bool active_dashboard; + GtkWidget *apps_list; + GtkSingleSelection *proxy_model; + DiyaTaskbarListModel *model; + GtkListItemFactory *factory; + guint selection_changed_sig_id; }; enum @@ -17,52 +127,216 @@ enum static GParamSpec *g_prop[N_PROPERTIES] = {0}; +G_DEFINE_TYPE(DiyaTaskbarWidget, diya_taskbar_widget, DIYA_TYPE_SHELL_WINDOW) -G_DEFINE_TYPE (DiyaTaskbarWidget, diya_taskbar_widget, DIYA_TYPE_SHELL_WINDOW) - -static void diya_taskbar_widget_dispose(GObject* object) +static void diya_taskbar_widget_dispose(GObject *object) { - (void) object; + DiyaTaskbarWidget *self = DIYA_TASKBAR_WIDGET(object); g_debug("diya_taskbar_widget_dispose"); + if (self->proxy_model) + { + g_object_unref(self->proxy_model); + } + if (self->factory) + { + g_object_unref(self->factory); + } G_OBJECT_CLASS(diya_taskbar_widget_parent_class)->dispose(object); } -static void diya_taskbar_widget_init(DiyaTaskbarWidget * self) +static void diya_taskbar_setup_apps_list_cb(GtkListItemFactory *factory, GtkListItem *list_item) { - g_debug("diya_taskbar_widget_init"); - gtk_widget_init_template (GTK_WIDGET(self)); - self->active_dashboard = false; - //builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui"); - // GtkWindow* win = GTK_WINDOW(self); + (void) factory; + GtkWidget *image; + image = gtk_image_new(); + gtk_image_set_icon_size(GTK_IMAGE(image), GTK_ICON_SIZE_LARGE); + gtk_list_item_set_child(list_item, image); +} - // g_object_bind_property(self->btn_toggle, "active", self->revealer, "reveal-child", G_SETTINGS_BIND_DEFAULT); - // gtk_widget_set_can_focus(self->revealer, false); +static void diya_taskbar_bind_apps_list_cb(GtkListItemFactory *factory, GtkListItem *list_item) +{ + (void) factory; + GtkWidget *image; + DiyaForeignWindow *win; + image = gtk_list_item_get_child(list_item); + win = gtk_list_item_get_item(list_item); + GAppInfo* info = diya_foreign_window_get_app_info(win); + if(info) + { + assert(G_IS_APP_INFO(info)); + gtk_image_set_from_gicon(GTK_IMAGE(image), g_app_info_get_icon(info)); + g_object_unref(info); + } + else + { + // get default icon from theme + GtkIconTheme* theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); + if(gtk_icon_theme_has_icon(theme, DEFAULT_APP_ICON_NAME)) + { + gtk_image_set_from_icon_name(GTK_IMAGE(image), DEFAULT_APP_ICON_NAME); + } + else + { + // fallback to default icon in resource + gtk_image_set_from_resource(GTK_IMAGE(image), "/dev/iohub/diya/shell/icons/scalable/gear"); + } + } +} - //g_signal_connect (GTK_WINDOW(taskbar), "destroy", G_CALLBACK (on_launcher_destroy), NULL); - - // int layer shell for window - gtk_layer_init_for_window (GTK_WINDOW(self)); - // anchor window to all edges - gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_LEFT, true); - gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_RIGHT, true); - gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_TOP, false); - gtk_layer_set_anchor (GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_BOTTOM, true); - gtk_layer_set_namespace(GTK_WINDOW(self), "diya-taskbar"); - // set margin on window - for (int i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++) - gtk_layer_set_margin (GTK_WINDOW(self), i, 0); - gtk_layer_set_layer (GTK_WINDOW(self), GTK_LAYER_SHELL_LAYER_TOP); - gtk_layer_set_keyboard_mode (GTK_WINDOW(self), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); - // the top launcher shall be exclusive - gtk_layer_auto_exclusive_zone_enable (GTK_WINDOW(self)); +static guint g_list_model_get_item_index(GListModel *model, gpointer item) +{ + for(guint i = 0; i < g_list_model_get_n_items(model); i++) + { + if(g_list_model_get_item(model, i) == item) + { + return i; + } + } + return GTK_INVALID_LIST_POSITION; +} - g_object_bind_property(self->btn_toggle, "active", self, DIYA_PROP_TASKBAR_SHOW_DASHBOARD, G_BINDING_BIDIRECTIONAL); +static void on_foreign_window_state_changed(DiyaSessionShell* shell, DiyaForeignWindow* win, enum diya_win_state state, DiyaTaskbarWidget *self) +{ + (void) shell; + DiyaForeignWindow *top_level = diya_foreign_window_get_top_level(win); + guint index = g_list_model_get_item_index(G_LIST_MODEL(self->model), top_level); + guint current_index = gtk_single_selection_get_selected(self->proxy_model); + g_debug("Current index %d, selected index %d", current_index, index); + + g_signal_handler_block(self->proxy_model, self->selection_changed_sig_id); + if((state & DIYA_WIN_STATE_FOCUS) && !(state & DIYA_WIN_STATE_MINIMIZE)) + { + if(index != GTK_INVALID_LIST_POSITION) + { + gtk_selection_model_select_item(GTK_SELECTION_MODEL(self->proxy_model), index, true); + } + } + else + { + // get the widget under the cursor + + if(current_index == index) + { + gtk_selection_model_unselect_item(GTK_SELECTION_MODEL(self->proxy_model), current_index); + } + } + g_signal_handler_unblock(self->proxy_model, self->selection_changed_sig_id); +} + +static void on_foreign_window_added(DiyaSessionShell *shell, DiyaForeignWindow *win, DiyaTaskbarWidget *self) +{ + (void) shell; + g_debug("Window added %s (toplevel %d)", diya_object_to_string(win), diya_foreign_window_is_toplevel(win)); + if(!diya_foreign_window_is_toplevel(win)) + { + return; + } + if(diya_taskbar_list_model_contain(self->model, win)) + { + return; + } + diya_taskbar_list_model_append(self->model, win); +} + +static void on_foreign_window_removed(DiyaSessionShell *shell, DiyaForeignWindow *win, DiyaTaskbarWidget *self) +{ + (void) shell; + if (!diya_foreign_window_is_toplevel(win)) + { + return; + } + if (diya_taskbar_list_model_contain(self->model, win)) + { + diya_taskbar_list_model_remove(self->model, win); + } +} + +static void diya_taskbar_list_model_selection_changed_cb(GtkSelectionModel *model, guint position, guint n_items, gpointer user_data) +{ + (void) n_items; + (void) user_data; + (void) position; + DiyaForeignWindow *win = gtk_single_selection_get_selected_item(GTK_SINGLE_SELECTION(model)); + DiyaTaskbarWidget *self = DIYA_TASKBAR_WIDGET(user_data); + if(gtk_selection_model_is_selected(GTK_SELECTION_MODEL(model), position)) + { + g_debug("Activating window: %s", diya_object_to_string(win)); + enum diya_win_state state = diya_foreign_window_get_state(win); + if(state & DIYA_WIN_STATE_MINIMIZE) + { + diya_foreign_window_set_state(win, DIYA_WIN_STATE_MINIMIZE, false); + } + if(self->active_dashboard) + { + g_object_set(self, DIYA_PROP_TASKBAR_SHOW_DASHBOARD, !self->active_dashboard, NULL); + } + diya_foreign_window_set_state(win, DIYA_WIN_STATE_FOCUS, true); + } + else + { + diya_foreign_window_set_state(win, DIYA_WIN_STATE_MINIMIZE, true); + } +} + +static void diya_taskbar_applist_enter(GtkEventControllerFocus *controller, DiyaTaskbarWidget* self) +{ + g_debug("diya_taskbar_applist_enter"); } +static void diya_taskbar_applist_leave(GtkEventControllerFocus *controller, DiyaTaskbarWidget* self) +{ + g_debug("diya_taskbar_applist_leaver"); +} + +static void diya_taskbar_widget_init(DiyaTaskbarWidget *self) +{ + g_debug("diya_taskbar_widget_init"); + gtk_widget_init_template(GTK_WIDGET(self)); + self->active_dashboard = false; + + // int layer shell for window + gtk_layer_init_for_window(GTK_WINDOW(self)); + // anchor window to all edges + gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_LEFT, true); + gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_RIGHT, true); + gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_TOP, false); + gtk_layer_set_anchor(GTK_WINDOW(self), GTK_LAYER_SHELL_EDGE_BOTTOM, true); + gtk_layer_set_namespace(GTK_WINDOW(self), "diya-taskbar"); + // set margin on window + for (int i = 0; i < GTK_LAYER_SHELL_EDGE_ENTRY_NUMBER; i++) + gtk_layer_set_margin(GTK_WINDOW(self), i, 0); + gtk_layer_set_layer(GTK_WINDOW(self), GTK_LAYER_SHELL_LAYER_TOP); + gtk_layer_set_keyboard_mode(GTK_WINDOW(self), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); + // the top launcher shall be exclusive + gtk_layer_auto_exclusive_zone_enable(GTK_WINDOW(self)); + + g_object_bind_property(self->btn_toggle, "active", self, DIYA_PROP_TASKBAR_SHOW_DASHBOARD, G_BINDING_BIDIRECTIONAL); + + self->factory = gtk_signal_list_item_factory_new(); + g_signal_connect(self->factory, "setup", G_CALLBACK(diya_taskbar_setup_apps_list_cb), NULL); + g_signal_connect(self->factory, "bind", G_CALLBACK(diya_taskbar_bind_apps_list_cb), NULL); + // applists + self->model = DIYA_TASKBAR_LIST_MODEL(g_object_new(DIYA_TYPE_TASKBAR_LIST_MODEL, NULL)); + self->proxy_model = gtk_single_selection_new (G_LIST_MODEL(self->model)); + gtk_single_selection_set_autoselect(self->proxy_model, false); + gtk_single_selection_set_can_unselect(self->proxy_model, true); + gtk_list_view_set_model(GTK_LIST_VIEW(self->apps_list), GTK_SELECTION_MODEL(self->proxy_model)); + gtk_list_view_set_factory(GTK_LIST_VIEW(self->apps_list), GTK_LIST_ITEM_FACTORY(self->factory)); + // gtk_list_view_set_single_click_activate(GTK_LIST_VIEW(self->apps_list), true); + + self->selection_changed_sig_id = g_signal_connect(GTK_SELECTION_MODEL(self->proxy_model), "selection-changed", G_CALLBACK(diya_taskbar_list_model_selection_changed_cb), self); + + // event handle + GtkEventController *controller = gtk_event_controller_focus_new(); + g_signal_connect(controller, "enter", G_CALLBACK(diya_taskbar_applist_enter), self); + g_signal_connect(controller, "leave", G_CALLBACK(diya_taskbar_applist_leave), self); + gtk_widget_add_controller(GTK_WIDGET(self->apps_list), GTK_EVENT_CONTROLLER(controller)); +} + static void diya_taskbar_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { - DiyaTaskbarWidget * self = DIYA_TASKBAR_WIDGET(object); + DiyaTaskbarWidget *self = DIYA_TASKBAR_WIDGET(object); switch (property_id) { @@ -70,26 +344,25 @@ static void diya_taskbar_set_property(GObject *object, guint property_id, const self->active_dashboard = g_value_get_boolean(value); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void diya_taskbar_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { - DiyaTaskbarWidget * self = DIYA_TASKBAR_WIDGET(object); + DiyaTaskbarWidget *self = DIYA_TASKBAR_WIDGET(object); switch (property_id) { case PROP_SHOW_DASHBOARD: g_value_set_boolean(value, self->active_dashboard); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } - /* static void diya_dashboard_toggle(GtkToggleButton* btn, DiyaTaskbarWidget* self) { @@ -105,24 +378,36 @@ static void diya_dashboard_toggle(GtkToggleButton* btn, DiyaTaskbarWidget* self) } */ +static void diya_taskbar_widget_setup(DiyaShellWindow* win) +{ + DiyaTaskbarWidget* self = DIYA_TASKBAR_WIDGET(win); + DiyaSessionShell *shell = DIYA_SESSION_SHELL(diya_shell_window_get_shell(DIYA_SHELL_WINDOW(self))); + g_signal_connect(shell, DIYA_SIGNAL_FOREIGN_WINDOW_ADDED, G_CALLBACK(on_foreign_window_added), self); + g_signal_connect(shell, DIYA_SIGNAL_FOREIGN_WINDOW_STATE_CHANGED, G_CALLBACK(on_foreign_window_state_changed), self); + g_signal_connect(shell, DIYA_SIGNAL_FOREIGN_WINDOW_REMOVED, G_CALLBACK(on_foreign_window_removed), self); +} -static void diya_taskbar_widget_class_init(DiyaTaskbarWidgetClass* class) +static void diya_taskbar_widget_class_init(DiyaTaskbarWidgetClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS(class); gobject_class->dispose = diya_taskbar_widget_dispose; gobject_class->set_property = diya_taskbar_set_property; gobject_class->get_property = diya_taskbar_get_property; - gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),"/dev/iohub/diya/shell/taskbar.ui"); - gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), DiyaTaskbarWidget, btn_toggle); + DiyaShellWindowClass *window_class = DIYA_SHELL_WINDOW_CLASS(class); + window_class->setup = diya_taskbar_widget_setup; + + gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), "/dev/iohub/diya/shell/ui/taskbar.ui"); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), DiyaTaskbarWidget, btn_toggle); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), DiyaTaskbarWidget, apps_list); // gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), diya_dashboard_toggle); gtk_widget_class_set_css_name(GTK_WIDGET_CLASS(class), "diya-taskbar"); - //gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS (class), ExampleAppWindow, stack); - //gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed); + // gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS (class), ExampleAppWindow, stack); + // gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed); - g_prop[PROP_SHOW_DASHBOARD] = g_param_spec_boolean(DIYA_PROP_TASKBAR_SHOW_DASHBOARD, NULL, "Show/hide dashboard", false, G_PARAM_READWRITE); // - - g_object_class_install_properties (gobject_class, N_PROPERTIES, g_prop); + g_prop[PROP_SHOW_DASHBOARD] = g_param_spec_boolean(DIYA_PROP_TASKBAR_SHOW_DASHBOARD, NULL, "Show/hide dashboard", false, G_PARAM_READWRITE); // + + g_object_class_install_properties(gobject_class, N_PROPERTIES, g_prop); } \ No newline at end of file diff --git a/src/widgets/taskbar-widget.h b/src/widgets/taskbar-widget.h index 0dd605d..cf9d3b4 100644 --- a/src/widgets/taskbar-widget.h +++ b/src/widgets/taskbar-widget.h @@ -4,9 +4,12 @@ #include "session-shell.h" #include "base-widgets.h" -#define DIYA_PROP_TASKBAR_SHOW_DASHBOARD "show-dashboard" +#define DIYA_PROP_TASKBAR_SHOW_DASHBOARD "show-taskbar" #define DIYA_TYPE_TASKBAR_WIDGET (diya_taskbar_widget_get_type()) G_DECLARE_FINAL_TYPE (DiyaTaskbarWidget, diya_taskbar_widget, DIYA, TASKBAR_WIDGET, DiyaShellWindow) +#define DIYA_TYPE_TASKBAR_LIST_MODEL (diya_taskbar_list_model_get_type()) +G_DECLARE_FINAL_TYPE (DiyaTaskbarListModel, diya_taskbar_list_model, DIYA, TASKBAR_LIST_MODEL, GObject) + #endif \ No newline at end of file