413 lines
14 KiB
C
413 lines
14 KiB
C
#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
|
|
{
|
|
NO_PROP,
|
|
PROP_SHOW_DASHBOARD,
|
|
N_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *g_prop[N_PROPERTIES] = {0};
|
|
|
|
G_DEFINE_TYPE(DiyaTaskbarWidget, diya_taskbar_widget, DIYA_TYPE_SHELL_WINDOW)
|
|
|
|
static void diya_taskbar_widget_dispose(GObject *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_setup_apps_list_cb(GtkListItemFactory *factory, GtkListItem *list_item)
|
|
{
|
|
(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);
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_SHOW_DASHBOARD:
|
|
self->active_dashboard = g_value_get_boolean(value);
|
|
break;
|
|
default:
|
|
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);
|
|
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);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
static void diya_dashboard_toggle(GtkToggleButton* btn, DiyaTaskbarWidget* self)
|
|
{
|
|
g_warning("TODDLDLDL");
|
|
if(gtk_toggle_button_get_active(btn))
|
|
{
|
|
gtk_layer_set_exclusive_zone(GTK_WINDOW(self), 0);
|
|
}
|
|
else
|
|
{
|
|
gtk_layer_auto_exclusive_zone_enable (GTK_WINDOW(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)
|
|
{
|
|
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;
|
|
|
|
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);
|
|
|
|
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);
|
|
} |