From c90a9f4e13c8705589eab523e303af7f8cb9ede2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 01/11] README: explain -U to run initially unlocked. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guido Günther --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9b04a0ab..364bb7d6 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,15 @@ way to get going is to do the following: When running from the source tree start the compositor *rootston*. Then start *phosh* using: - _build/run + _build/run -U or in one command: - ../wlroots/_build/rootston/rootston -E _build/run -C ./rootston.ini + ../wlroots/_build/rootston/rootston -E '_build/run -U' -C ./rootston.ini -This will make sure the needed gsettings schema is found. +This will make sure the needed gsettings schema is found. The '-U' option makes +sure the shell is not locked on startup so you can test with arbitrary +passwords. ### Running from the Debian packages If installed via the Debian packages you can also run phosh via gnome-session. -- GitLab From 608d5a5b00019f4a55fce064618a40a991c4fab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 02/11] settings: round bottom corners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guido Günther --- src/style.css | 8 ++++++++ src/ui/settings-menu.ui | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/style.css b/src/style.css index 19ead204..75d22013 100644 --- a/src/style.css +++ b/src/style.css @@ -1,3 +1,4 @@ +/* Top bar panel */ .phosh-panel { font: 15px lato-regular; } @@ -6,3 +7,10 @@ background-image: none; border-width: 0; } + +/* Settings menu */ +.phosh-settings-menu { + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + } + diff --git a/src/ui/settings-menu.ui b/src/ui/settings-menu.ui index 9309e531..45d23bfd 100644 --- a/src/ui/settings-menu.ui +++ b/src/ui/settings-menu.ui @@ -237,5 +237,8 @@ + -- GitLab From 972abf01c97dcd4c2292079e6dbe19e9001834ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 03/11] favorites: Use an ui file This is in preparation for the app switcher --- src/favorites.c | 51 +++++++++--------- src/phosh.gresources.xml | 1 + src/ui/favorites.ui | 108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 26 deletions(-) create mode 100644 src/ui/favorites.ui diff --git a/src/favorites.c b/src/favorites.c index 0ae1bc47..7275145c 100644 --- a/src/favorites.c +++ b/src/favorites.c @@ -26,9 +26,9 @@ static guint signals[N_SIGNALS] = { 0 }; typedef struct { - GtkWidget *scroll; + GtkWidget *sw_favorites; GtkWidget *evbox; - GtkWidget *flowbox; + GtkWidget *fb_favorites; GSettings *settings; } PhoshFavoritesPrivate; @@ -128,7 +128,7 @@ add_weston_terminal (PhoshFavorites *self) "circular"); g_signal_connect_swapped (btn, "clicked", G_CALLBACK (term_btn_clicked), self); - gtk_flow_box_insert (GTK_FLOW_BOX (priv->flowbox), btn, -1); + gtk_flow_box_insert (GTK_FLOW_BOX (priv->fb_favorites), btn, -1); } @@ -142,14 +142,14 @@ favorites_changed (GSettings *settings, GtkWidget *btn; /* Remove all favorites first */ - gtk_container_foreach (GTK_CONTAINER (priv->flowbox), + gtk_container_foreach (GTK_CONTAINER (priv->fb_favorites), (GtkCallback) gtk_widget_destroy, NULL); for (gint i = 0; i < g_strv_length (favorites); i++) { gchar *fav = favorites[i]; btn = add_favorite (self, fav); if (btn) - gtk_flow_box_insert (GTK_FLOW_BOX (priv->flowbox), btn, -1); + gtk_flow_box_insert (GTK_FLOW_BOX (priv->fb_favorites), btn, -1); } g_strfreev (favorites); add_weston_terminal (self); @@ -201,32 +201,23 @@ phosh_favorites_constructed (GObject *object) "phosh-favorites"); /* Flowbox */ - priv->flowbox = gtk_widget_new (GTK_TYPE_FLOW_BOX, - "halign", GTK_ALIGN_START, - "valign", GTK_ALIGN_CENTER, - "selection-mode", GTK_SELECTION_NONE, - "orientation", GTK_ORIENTATION_VERTICAL, - NULL); - gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX(priv->flowbox), G_MAXINT); - gtk_flow_box_set_homogeneous (GTK_FLOW_BOX(priv->flowbox), TRUE); - - /* Scrolled window */ - priv->scroll = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scroll), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_NEVER); - gtk_container_add (GTK_CONTAINER (priv->scroll), priv->flowbox); - - /* Eventbox */ - priv->evbox = gtk_event_box_new (); - gtk_container_add (GTK_CONTAINER (priv->evbox), priv->scroll); + priv->fb_favorites = gtk_widget_new (GTK_TYPE_FLOW_BOX, + "halign", GTK_ALIGN_CENTER, + "valign", GTK_ALIGN_START, + "selection-mode", GTK_SELECTION_NONE, + "orientation", GTK_ORIENTATION_HORIZONTAL, + NULL); + gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX(priv->fb_favorites), G_MAXINT); + gtk_flow_box_set_homogeneous (GTK_FLOW_BOX(priv->fb_favorites), TRUE); + gtk_container_add (GTK_CONTAINER (priv->sw_favorites), priv->fb_favorites); + gtk_widget_show_all (GTK_WIDGET(priv->evbox)); + + /* Close on click */ g_signal_connect_swapped (priv->evbox, "button_press_event", G_CALLBACK (evbox_button_press_event_cb), self); gtk_widget_set_events (priv->evbox, GDK_BUTTON_PRESS_MASK); - gtk_container_add (GTK_CONTAINER (self), priv->evbox); - priv->settings = g_settings_new ("sm.puri.phosh"); g_signal_connect (priv->settings, "changed::favorites", G_CALLBACK (favorites_changed), self); @@ -250,10 +241,17 @@ static void phosh_favorites_class_init (PhoshFavoritesClass *klass) { GObjectClass *object_class = (GObjectClass *)klass; + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->dispose = phosh_favorites_dispose; object_class->constructed = phosh_favorites_constructed; + gtk_widget_class_set_template_from_resource (widget_class, + "/sm/puri/phosh/ui/favorites.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, evbox); + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, sw_favorites); + signals[APP_LAUNCHED] = g_signal_new ("app-launched", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); @@ -266,6 +264,7 @@ phosh_favorites_class_init (PhoshFavoritesClass *klass) static void phosh_favorites_init (PhoshFavorites *self) { + gtk_widget_init_template (GTK_WIDGET (self)); } diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index 4cc7ea7d..1e98cfb8 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -1,6 +1,7 @@ + ui/favorites.ui ui/home.ui ui/lockscreen.ui ui/settings-menu.ui diff --git a/src/ui/favorites.ui b/src/ui/favorites.ui new file mode 100644 index 00000000..65e81aef --- /dev/null +++ b/src/ui/favorites.ui @@ -0,0 +1,108 @@ + + + + + + -- GitLab From e090bb64359ac183fcbe621ce2a76efbaee106a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 04/11] Add version two of the phosh-private protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want the phosh_private_xdg_app_switcher for application switching. Signed-off-by: Guido Günther --- protocol/phosh-private.xml | 58 ++++++++++++++++++++++++++++++++++---- src/phosh-wayland.c | 2 +- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/protocol/phosh-private.xml b/protocol/phosh-private.xml index 79b9b9b0..4eede1e5 100644 --- a/protocol/phosh-private.xml +++ b/protocol/phosh-private.xml @@ -1,5 +1,5 @@ - + Private protocol between phosh and the compositor. @@ -9,13 +9,61 @@ summary="an invalid argument was provided in a request"/> - - - - Rotate the display clockwise 0, 90, 180 or 270 degree. + + + + Rotate the output clockwise 0, 90, 180 or 270 degree. + + + + + + + + + + The interface is meant to list xdg surfaces (see the xdg-shell + stable wayland protocol) and to raise these surfaces to the top + of the window stack. + + It's up to the compositor if it only lists surfaces of the + xdg-shell stable protocol or also surfaces using unstable versions of + the xdg-shell protocol. + + + + + + + + Request to list xdg shell toplevels. + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/phosh-wayland.c b/src/phosh-wayland.c index b3a8baf4..654e1ab2 100644 --- a/src/phosh-wayland.c +++ b/src/phosh-wayland.c @@ -59,7 +59,7 @@ registry_handle_global (void *data, registry, name, &phosh_private_interface, - 1); + 2); } else if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) { priv->layer_shell = wl_registry_bind ( registry, -- GitLab From 013fcd92408e1f64844865feea182753dd4fc734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 05/11] Depend on recent gio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Later patches will use g_desktop_app_info_get_locale_string. See https://source.puri.sm/Librem5/wlroots/merge_requests/16#note_4911 Signed-off-by: Guido Günther --- src/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meson.build b/src/meson.build index 65f6c83f..3ba0023e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -51,6 +51,7 @@ phosh_sources = [ ] phosh_deps = [ + dependency('gio-2.0', version: '>= 2.56'), dependency('gcr-3', version: '>= 3.7.5'), dependency('gio-2.0', version: '>=2.50.0'), dependency('gio-unix-2.0', version: '>=2.50.0'), -- GitLab From 21f2c079da72bceb0862d02cf8fe7f9ba3db40b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 06/11] tests: Add more stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed by the new favorites screen. Signed-off-by: Guido Günther --- tests/phoshstub.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/phoshstub.c b/tests/phoshstub.c index 6a529db2..ea6010ab 100644 --- a/tests/phoshstub.c +++ b/tests/phoshstub.c @@ -23,3 +23,23 @@ phosh_shell_get_usable_area (PhoshShell *self, gint *x, gint *y, gint *width, g *height = 128; return; } + + +PhoshWayland * +phosh_wayland_get_default (void) +{ + return NULL; +} + +struct phosh_private* +phosh_wayland_get_phosh_private (PhoshWayland *self) +{ + return NULL; +} + +PhoshMonitor * +phosh_shell_get_primary_monitor (PhoshShell *self) +{ + return NULL; +} + -- GitLab From fc0c14d53f91910e80d08f1bd3eb339cb0cff715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 07/11] tests: Allow tests to depend on other source files from phosh as well --- tests/meson.build | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 764f5369..af141acc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -21,25 +21,27 @@ test_link_args = [ '-fPIC', ] -test_stubs = [ +# Deps for all tests +test_deps = [ 'phoshstub.c', wl_proto_sources, ] -cls_names = [ - 'favorites', +tests = [ + ['favorites', [ '@0@/src/app.c'.format(meson.source_root())]] ] -foreach cls_name : cls_names - t = executable('test-@0@'.format(cls_name), - ['test-@0@.c'.format(cls_name), - '../src/@0@.c'.format(cls_name), - test_stubs], +foreach test : tests + t = executable('test-@0@'.format(test[0]), + ['test-@0@.c'.format(test[0]), + '@0@/src/@1@.c'.format(meson.source_root(), test[0]), + test[1], + test_deps], c_args: test_cflags, link_args: test_link_args, dependencies: phosh_deps, ) - test(cls_name, t, env: test_env) + test(test[0], t, env: test_env) endforeach endif -- GitLab From 6ae0906ded9cfdce8b33d93fb67d1c032ee73892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 08/11] tests: add gresources to test dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Required if tests use ui files. Signed-off-by: Guido Günther --- tests/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/meson.build b/tests/meson.build index af141acc..10c7f5af 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -25,6 +25,7 @@ test_link_args = [ test_deps = [ 'phoshstub.c', wl_proto_sources, + phosh_resources, ] tests = [ -- GitLab From 7c94e29f845df75d82b6c48eea0287a5ab5c9560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 09/11] Add PhoshApp object This represents a single app. Currently we only use the icon (determined via the app_id) but we can also use the surface's content in the future. --- src/app.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++ src/app.h | 17 +++ src/meson.build | 2 + src/style.css | 16 +++ tests/meson.build | 1 + tests/test-app.c | 28 +++++ 6 files changed, 357 insertions(+) create mode 100644 src/app.c create mode 100644 src/app.h create mode 100644 tests/test-app.c diff --git a/src/app.c b/src/app.c new file mode 100644 index 00000000..12e52dc8 --- /dev/null +++ b/src/app.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2018 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "phosh-app" + +#include "config.h" +#include "app.h" +#include "phosh.h" + +#include + +/** + * SECTION:phosh-app + * @short_description: An app in the faovorites overview + * @Title: PhoshApp + * + * The #PhoshApp is used to select a running application + * in the favorites overview. + */ + +#define APP_ICON_SIZE GTK_ICON_SIZE_DIALOG + +enum { + PHOSH_APP_PROP_0, + PHOSH_APP_PROP_APP_ID, + PHOSH_APP_PROP_TITLE, + PHOSH_APP_PROP_LAST_PROP, +}; +static GParamSpec *props[PHOSH_APP_PROP_LAST_PROP]; + +typedef struct +{ + GtkWidget *image; + GtkWidget *box; + + char *app_id; + char *title; + GDesktopAppInfo *info; +} PhoshAppPrivate; + + +struct _PhoshApp +{ + GtkButton parent; +}; + +G_DEFINE_TYPE_WITH_PRIVATE(PhoshApp, phosh_app, GTK_TYPE_BUTTON) + + +static void +phosh_app_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + PhoshApp *self = PHOSH_APP (object); + PhoshAppPrivate *priv = phosh_app_get_instance_private(self); + + switch (property_id) { + case PHOSH_APP_PROP_APP_ID: + g_free (priv->app_id); + priv->app_id = g_value_dup_string (value); + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_APP_PROP_APP_ID]); + break; + case PHOSH_APP_PROP_TITLE: + g_free (priv->title); + priv->title = g_value_dup_string (value); + g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_APP_PROP_TITLE]); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +phosh_app_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + PhoshApp *self = PHOSH_APP (object); + PhoshAppPrivate *priv = phosh_app_get_instance_private(self); + + switch (property_id) { + case PHOSH_APP_PROP_APP_ID: + g_value_set_string (value, priv->app_id); + break; + case PHOSH_APP_PROP_TITLE: + g_value_set_string (value, priv->title); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +/* In GTK+4 we can just call gtk_image_new_from_gicon since it got rid of the + restrictive size element */ +static GtkWidget * +get_image_from_gicon (PhoshApp *self, int scale) +{ + PhoshAppPrivate *priv = phosh_app_get_instance_private (self); + GtkIconTheme *theme = gtk_icon_theme_get_for_screen (gdk_screen_get_default ()); + GIcon *icon; + GError *err = NULL; + g_autoptr(GdkPixbuf) pixbuf = NULL; + g_autoptr(GtkIconInfo) info = NULL; + + icon = g_app_info_get_icon (G_APP_INFO (priv->info)); + info = gtk_icon_theme_lookup_by_gicon_for_scale(theme, icon, 128, scale, 0); + if (!info) { + g_warning ("Failed to lookup icon for %s", priv->app_id); + return NULL; + } + pixbuf = gtk_icon_info_load_icon (info, &err); + if (!pixbuf) { + g_warning ("Failed to load icon for %s: %s", priv->app_id, err->message); + return NULL; + } + return gtk_image_new_from_pixbuf (pixbuf); +} + + +static GtkWidget * +get_missing_image (int scale) +{ + GtkIconTheme *theme = gtk_icon_theme_get_for_screen (gdk_screen_get_default ()); + GError *err = NULL; + g_autoptr(GdkPixbuf) pixbuf = NULL; + g_autoptr(GtkIconInfo) info = NULL; + + info = gtk_icon_theme_lookup_icon_for_scale(theme, "image-missing", 128, scale, 0); + pixbuf = gtk_icon_info_load_icon (info, &err); + return gtk_image_new_from_pixbuf (pixbuf); +} + + +static void +phosh_app_constructed (GObject *object) +{ + PhoshApp *self = PHOSH_APP (object); + PhoshAppPrivate *priv = phosh_app_get_instance_private (self); + GtkWidget *lbl_name = NULL, *lbl_title = NULL; + g_autofree gchar *desktop_id = NULL; + g_autofree gchar *name = NULL; + gint scale = 1; + PhoshMonitor *monitor = phosh_shell_get_primary_monitor (phosh_shell_get_default()); + + if (monitor) + scale = monitor->scale; + + desktop_id = g_strdup_printf ("%s.desktop", priv->app_id); + g_return_if_fail (desktop_id); + priv->info = g_desktop_app_info_new (desktop_id); + if (priv->info) { + priv->image = get_image_from_gicon (self, scale); + name = g_desktop_app_info_get_locale_string (priv->info, "Name"); + lbl_name = gtk_label_new (name ? name : priv->app_id); + } else { + lbl_name = gtk_label_new (priv->app_id); + } + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(lbl_name)), + "phosh-app-name"); + + if (!priv->info || !priv->image) { + priv->image = get_missing_image (scale); + } + + if (priv->title) { + lbl_title = gtk_label_new (priv->title); + gtk_label_set_max_width_chars (GTK_LABEL (lbl_title), 30); + gtk_label_set_ellipsize (GTK_LABEL (lbl_title), PANGO_ELLIPSIZE_MIDDLE); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(lbl_title)), + "phosh-app-title"); + } + + priv->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (priv->box), priv->image, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (priv->box), lbl_name, FALSE, FALSE, 0); + if (lbl_title) { + gtk_box_pack_start (GTK_BOX (priv->box), lbl_title, FALSE, FALSE, 0); + } + gtk_container_add (GTK_CONTAINER (self), priv->box); + + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(self)), + "phosh-app-btn"); + + G_OBJECT_CLASS (phosh_app_parent_class)->constructed (object); +} + + +static void +phosh_app_dispose (GObject *object) +{ + PhoshApp *self = PHOSH_APP (object); + PhoshAppPrivate *priv = phosh_app_get_instance_private (self); + + g_clear_object (&priv->info); + + G_OBJECT_CLASS (phosh_app_parent_class)->dispose (object); +} + + +static void +phosh_app_finalize (GObject *object) +{ + PhoshApp *self = PHOSH_APP (object); + PhoshAppPrivate *priv = phosh_app_get_instance_private (self); + + g_free (priv->app_id); + g_free (priv->title); + + G_OBJECT_CLASS (phosh_app_parent_class)->finalize (object); +} + + + +static void +phosh_app_class_init (PhoshAppClass *klass) +{ + GObjectClass *object_class = (GObjectClass *)klass; + + object_class->constructed = phosh_app_constructed; + object_class->dispose = phosh_app_dispose; + object_class->finalize = phosh_app_finalize; + + object_class->set_property = phosh_app_set_property; + object_class->get_property = phosh_app_get_property; + + props[PHOSH_APP_PROP_APP_ID] = + g_param_spec_string ( + "app-id", + "app-id", + "The application id", + "", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + props[PHOSH_APP_PROP_TITLE] = + g_param_spec_string ( + "title", + "title", + "The window's title", + "", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PHOSH_APP_PROP_LAST_PROP, props); +} + + +static void +phosh_app_init (PhoshApp *self) +{ +} + + +GtkWidget * +phosh_app_new (const char *app_id, const char *title) +{ + return g_object_new (PHOSH_TYPE_APP, + "app-id", app_id, + "title", title, + NULL); +} + + +const char * +phosh_app_get_app_id (PhoshApp *self) +{ + PhoshAppPrivate *priv; + + g_return_val_if_fail (PHOSH_IS_APP (self), NULL); + priv = phosh_app_get_instance_private (self); + + return priv->app_id; +} + + +const char * +phosh_app_get_title (PhoshApp *self) +{ + PhoshAppPrivate *priv; + + g_return_val_if_fail (PHOSH_IS_APP (self), NULL); + priv = phosh_app_get_instance_private (self); + + return priv->title; +} diff --git a/src/app.h b/src/app.h new file mode 100644 index 00000000..46c3e357 --- /dev/null +++ b/src/app.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Purism SPC + * + * SPDX-License-Identifier: GPL-3.0+ + */ +#pragma once + +#include + +#define PHOSH_TYPE_APP (phosh_app_get_type()) + +G_DECLARE_FINAL_TYPE (PhoshApp, phosh_app, PHOSH, APP, GtkButton) + +GtkWidget * phosh_app_new (const char *app_id, const char *title); +const char *phosh_app_get_app_id (PhoshApp *self); +const char *phosh_app_get_title (PhoshApp *self); + diff --git a/src/meson.build b/src/meson.build index 3ba0023e..f7eeef18 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,6 +9,8 @@ phosh_resources = gnome.compile_resources( ) phosh_sources = [ + 'app.c', + 'app.h', 'auth.c', 'auth.h', 'background.c', diff --git a/src/style.css b/src/style.css index 75d22013..2e344de0 100644 --- a/src/style.css +++ b/src/style.css @@ -14,3 +14,19 @@ border-bottom-left-radius: 10px; } +/* Favorites / Home screen */ +.phosh-app-btn { + background-image: none; + background-color: black; + border-width: 3px; + border-radius: 10px; +} + +.phosh-app-name { + font-size: larger; + font-weight: bold; +} + +.phosh-app-title { + font-size: larger; +} diff --git a/tests/meson.build b/tests/meson.build index 10c7f5af..64abea62 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -29,6 +29,7 @@ test_deps = [ ] tests = [ + ['app', []], ['favorites', [ '@0@/src/app.c'.format(meson.source_root())]] ] diff --git a/tests/test-app.c b/tests/test-app.c new file mode 100644 index 00000000..de678af4 --- /dev/null +++ b/tests/test-app.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + * Author: Guido Günther + */ + +#include "app.h" + +static void +test_phosh_app_new(void) +{ + PhoshApp *app = PHOSH_APP (phosh_app_new ("com.example.foo", "bar")); + g_assert (app); + g_assert_cmpstr (phosh_app_get_app_id (app), ==, "com.example.foo"); + g_assert_cmpstr (phosh_app_get_title (app), ==, "bar"); + gtk_widget_destroy (GTK_WIDGET (app)); +} + + +gint +main (gint argc, + gchar *argv[]) +{ + gtk_test_init (&argc, &argv, NULL); + + g_test_add_func("/phosh/app/new", test_phosh_app_new); + return g_test_run(); +} -- GitLab From 825937808d6ac8ba69ee4cf29f4df41314b594ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 30 Aug 2018 16:26:54 +0200 Subject: [PATCH 10/11] favorites: Use PhoshApp and phosh-private wayland protocol for app switching --- src/favorites.c | 149 +++++++++++++++++++++++++++++++++++++++++--- src/phosh.c | 23 +++---- src/style.css | 5 ++ src/ui/favorites.ui | 124 ++++++++++++++++++++---------------- 4 files changed, 225 insertions(+), 76 deletions(-) diff --git a/src/favorites.c b/src/favorites.c index 7275145c..e8b66de0 100644 --- a/src/favorites.c +++ b/src/favorites.c @@ -13,12 +13,16 @@ #include "config.h" #include "favorites.h" +#include "app.h" #include "phosh.h" +#include "phosh-private-client-protocol.h" +#include "phosh-wayland.h" #include enum { APP_LAUNCHED, + APP_RAISED, SELECTION_ABORTED, N_SIGNALS }; @@ -26,10 +30,19 @@ static guint signals[N_SIGNALS] = { 0 }; typedef struct { + /* Favorites */ + GtkWidget *evbox_favorites; GtkWidget *sw_favorites; - GtkWidget *evbox; GtkWidget *fb_favorites; GSettings *settings; + + /* Running apps */ + GtkWidget *evbox_running_apps; + GtkWidget *sw_running_apps; + GtkWidget *fb_running_apps; + GtkWidget *box_running_apps; + struct phosh_private_xdg_switcher *xdg_switcher; + } PhoshFavoritesPrivate; @@ -41,6 +54,86 @@ struct _PhoshFavorites G_DEFINE_TYPE_WITH_PRIVATE(PhoshFavorites, phosh_favorites, GTK_TYPE_WINDOW) +static void +app_clicked_cb (GtkButton *btn, gpointer user_data) +{ + PhoshFavorites *self = PHOSH_FAVORITES (user_data); + PhoshFavoritesPrivate *priv; + PhoshApp *app = PHOSH_APP (btn); + + g_return_if_fail (PHOSH_IS_FAVORITES (self)); + priv = phosh_favorites_get_instance_private (self); + g_return_if_fail (priv->xdg_switcher); + + g_debug("Will raise %s (%s)", + phosh_app_get_app_id (app), + phosh_app_get_title (app)); + + phosh_private_xdg_switcher_raise_xdg_surface (priv->xdg_switcher, + phosh_app_get_app_id (app), + phosh_app_get_title (app)); + g_signal_emit(self, signals[APP_RAISED], 0); +} + + + +static void +handle_xdg_switcher_xdg_surface ( + void *data, struct phosh_private_xdg_switcher *phosh_private_xdg_switcher, + const char *app_id, + const char *title) +{ + PhoshFavorites *self = data; + PhoshFavoritesPrivate *priv; + GtkWidget *app; + + g_return_if_fail (PHOSH_IS_FAVORITES (self)); + priv = phosh_favorites_get_instance_private (self); + + g_debug ("Building activator for '%s' (%s)", app_id, title); + app = phosh_app_new (app_id, title); + gtk_flow_box_insert (GTK_FLOW_BOX (priv->fb_running_apps), app, -1); + + g_signal_connect (app, "clicked", G_CALLBACK (app_clicked_cb), self); + gtk_widget_show (GTK_WIDGET (self)); +} + + +static void +handle_xdg_switcher_list_xdg_surfaces_done( + void *data, + struct phosh_private_xdg_switcher *phosh_private_xdg_switcher) +{ + g_debug ("Got all apps"); +} + + +static const struct phosh_private_xdg_switcher_listener xdg_switcher_listener = { + handle_xdg_switcher_xdg_surface, + handle_xdg_switcher_list_xdg_surfaces_done, +}; + + +static void +get_running_apps (PhoshFavorites *self) +{ + PhoshFavoritesPrivate *priv = phosh_favorites_get_instance_private (self); + struct phosh_private *phosh_private; + + phosh_private = phosh_wayland_get_phosh_private ( + phosh_wayland_get_default ()); + + if (!phosh_private) { + g_debug ("Skipping app list due to missing phosh_private protocol extension"); + return; + } + + priv->xdg_switcher = phosh_private_get_xdg_switcher (phosh_private); + phosh_private_xdg_switcher_add_listener (priv->xdg_switcher, &xdg_switcher_listener, self); + phosh_private_xdg_switcher_list_xdg_surfaces (priv->xdg_switcher); +} + + static void term_btn_clicked (PhoshFavorites *self, GtkButton *btn) @@ -97,7 +190,7 @@ add_favorite (PhoshFavorites *self, "circular"); gtk_button_set_image (GTK_BUTTON (btn), image); - g_object_set (image, "margin", 20, NULL); + g_object_set (image, "margin", 10, NULL); g_object_set_data (G_OBJECT (btn), "favorites", self); g_signal_connect (btn, "clicked", G_CALLBACK (favorite_clicked_cb), info); @@ -159,7 +252,13 @@ favorites_changed (GSettings *settings, static gboolean draw_cb (GtkWidget *widget, cairo_t *cr, gpointer unused) { - cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.1); + GtkStyleContext *context = gtk_widget_get_style_context (widget); + GdkRGBA c; + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &c); + G_GNUC_END_IGNORE_DEPRECATIONS + cairo_set_source_rgba (cr, c.red, c.green, c.blue, 0.8); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); return FALSE; @@ -200,28 +299,49 @@ phosh_favorites_constructed (GObject *object) gtk_widget_get_style_context (GTK_WIDGET (self)), "phosh-favorites"); - /* Flowbox */ + /* Favorites */ priv->fb_favorites = gtk_widget_new (GTK_TYPE_FLOW_BOX, "halign", GTK_ALIGN_CENTER, "valign", GTK_ALIGN_START, "selection-mode", GTK_SELECTION_NONE, "orientation", GTK_ORIENTATION_HORIZONTAL, NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(priv->fb_favorites)), + "phosh-favorites-flowbox"); gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX(priv->fb_favorites), G_MAXINT); gtk_flow_box_set_homogeneous (GTK_FLOW_BOX(priv->fb_favorites), TRUE); gtk_container_add (GTK_CONTAINER (priv->sw_favorites), priv->fb_favorites); - gtk_widget_show_all (GTK_WIDGET(priv->evbox)); - + gtk_widget_show (GTK_WIDGET(priv->evbox_favorites)); /* Close on click */ - g_signal_connect_swapped (priv->evbox, "button_press_event", + g_signal_connect_swapped (priv->evbox_favorites, "button_press_event", G_CALLBACK (evbox_button_press_event_cb), self); - gtk_widget_set_events (priv->evbox, GDK_BUTTON_PRESS_MASK); + gtk_widget_set_events (priv->evbox_favorites, GDK_BUTTON_PRESS_MASK); priv->settings = g_settings_new ("sm.puri.phosh"); g_signal_connect (priv->settings, "changed::favorites", G_CALLBACK (favorites_changed), self); favorites_changed (priv->settings, "favorites", self); + + /* Running apps */ + priv->fb_running_apps = gtk_widget_new (GTK_TYPE_FLOW_BOX, + "halign", GTK_ALIGN_CENTER, + "valign", GTK_ALIGN_FILL, + "selection-mode", GTK_SELECTION_NONE, + "orientation", GTK_ORIENTATION_HORIZONTAL, + NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(priv->fb_running_apps)), + "phosh-running-apps-flowbox"); + gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX(priv->fb_running_apps), G_MAXINT); + gtk_flow_box_set_homogeneous (GTK_FLOW_BOX(priv->fb_running_apps), TRUE); + gtk_container_add (GTK_CONTAINER (priv->sw_running_apps), priv->fb_running_apps); + gtk_widget_show (GTK_WIDGET(priv->evbox_running_apps)); + /* Close on click */ + g_signal_connect_swapped (priv->evbox_running_apps, "button_press_event", + G_CALLBACK (evbox_button_press_event_cb), + self); + gtk_widget_set_events (priv->evbox_running_apps, GDK_BUTTON_PRESS_MASK); + get_running_apps (self); } @@ -233,6 +353,11 @@ phosh_favorites_dispose (GObject *object) g_clear_object (&priv->settings); + if (priv->xdg_switcher) { + phosh_private_xdg_switcher_destroy (priv->xdg_switcher); + priv->xdg_switcher = NULL; + } + G_OBJECT_CLASS (phosh_favorites_parent_class)->dispose (object); } @@ -249,12 +374,18 @@ phosh_favorites_class_init (PhoshFavoritesClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/phosh/ui/favorites.ui"); - gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, evbox); + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, evbox_favorites); gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, sw_favorites); + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, evbox_running_apps); + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, sw_running_apps); + gtk_widget_class_bind_template_child_private (widget_class, PhoshFavorites, box_running_apps); signals[APP_LAUNCHED] = g_signal_new ("app-launched", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + signals[APP_RAISED] = g_signal_new ("app-raised", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, + NULL, G_TYPE_NONE, 0); signals[SELECTION_ABORTED] = g_signal_new ("selection-aborted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); diff --git a/src/phosh.c b/src/phosh.c index 3489a6d3..927bd5c9 100644 --- a/src/phosh.c +++ b/src/phosh.c @@ -105,19 +105,8 @@ close_menu (struct popup **popup) static void -app_launched_cb (PhoshShell *self, - PhoshFavorites *favorites) -{ - PhoshShellPrivate *priv = phosh_shell_get_instance_private (self); - - g_return_if_fail (priv->favorites); - close_menu (&priv->favorites); -} - - -static void -favorites_selection_aborted (PhoshShell *self, - PhoshFavorites *favorites) +close_favorites_menu_cb (PhoshShell *self, + PhoshFavorites *favorites) { PhoshShellPrivate *priv = phosh_shell_get_instance_private (self); @@ -222,11 +211,15 @@ favorites_activated_cb (PhoshShell *self, g_signal_connect_swapped (priv->favorites->window, "app-launched", - G_CALLBACK(app_launched_cb), + G_CALLBACK(close_favorites_menu_cb), + self); + g_signal_connect_swapped (priv->favorites->window, + "app-raised", + G_CALLBACK(close_favorites_menu_cb), self); g_signal_connect_swapped (priv->favorites->window, "selection-aborted", - G_CALLBACK(favorites_selection_aborted), + G_CALLBACK(close_favorites_menu_cb), self); } diff --git a/src/style.css b/src/style.css index 2e344de0..31c144c2 100644 --- a/src/style.css +++ b/src/style.css @@ -30,3 +30,8 @@ .phosh-app-title { font-size: larger; } + +.phosh-running-apps-flowbox { + padding-top: 10px; + padding-bottom: 10px; +} diff --git a/src/ui/favorites.ui b/src/ui/favorites.ui index 65e81aef..4fa8f5dc 100644 --- a/src/ui/favorites.ui +++ b/src/ui/favorites.ui @@ -8,75 +8,95 @@ - - evbox + True False + True + vertical - + True False - True + 10 vertical - + + True + False + 3 + 3 + Running Apps + center + + + + + + False + True + 0 + + + + True False - vertical - - - True - False - Running Applications - - - False - True - 0 - - - - lb_runnng_apps + True - False - 12 - 12 - 5 - 5 - none + True + in + + + - - True - True - 1 - True True + 1 + + + + + + True + True + 0 + + + + + True + False + True + vertical + + + True + False + 3 + 3 + Favorites + center + + + + + + False + True 0 - + + evbox True False - True - vertical - - - True - False - Favorites - - - False - True - 0 - - sw_favorites @@ -87,20 +107,20 @@ - - True - True - 1 - - False + True True 1 + + False + True + 1 + -- GitLab From cc3e30b2f5bd7d0f1fe13e8b6ab5dd87b01f58e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Fri, 31 Aug 2018 16:25:59 +0200 Subject: [PATCH 11/11] README: Document how to run the tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guido Günther --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 364bb7d6..7e56a8e5 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,13 @@ way to get going is to do the following: ninja -C _build ninja -C _build install +# Testing + +To run the tests run + + ninja -C _build test + +For details see the *.gitlab-ci.yml* file. ## Running ### Running from the source tree -- GitLab