summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/Makefile.am2
-rw-r--r--shell/gtk-shell.c44
-rw-r--r--shell/launcher.c420
-rw-r--r--shell/launcher.h43
-rw-r--r--shell/style.css18
5 files changed, 502 insertions, 25 deletions
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 2d63195..6231337 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -29,6 +29,8 @@ weston_gtk_shell_SOURCES = \
panel.h \
vertical-clock.c \
vertical-clock.h \
+ launcher.c \
+ launcher.h \
weston-gtk-shell-resources.c \
weston-gtk-shell-resources.h \
desktop-shell-client-protocol.h \
diff --git a/shell/gtk-shell.c b/shell/gtk-shell.c
index 36f059a..83c4b4a 100644
--- a/shell/gtk-shell.c
+++ b/shell/gtk-shell.c
@@ -12,7 +12,7 @@
#include "app-icon.h"
#include "clock.h"
#include "favorites.h"
-#include "launcher-grid.h"
+#include "launcher.h"
#include "panel.h"
#include "vertical-clock.h"
@@ -79,18 +79,22 @@ desktop_shell_configure(void *data,
{
struct desktop *desktop = data;
int window_height;
+ int grid_width, grid_height;
gtk_widget_set_size_request (desktop->background->window,
width, height);
- gtk_widget_set_size_request (desktop->launcher_grid->window,
- width - 56, height);
-
/* TODO: make this height a little nicer */
window_height = height * WESTON_GTK_PANEL_HEIGHT_RATIO;
gtk_window_resize (GTK_WINDOW (desktop->panel->window),
WESTON_GTK_PANEL_WIDTH, window_height);
+ weston_gtk_launcher_calculate (WESTON_GTK_LAUNCHER(desktop->launcher_grid->window),
+ &grid_width, &grid_height, NULL);
+ gtk_widget_set_size_request (desktop->launcher_grid->window,
+ grid_width,
+ grid_height);
+
shell_helper_move_surface(desktop->helper,
desktop->panel->surface,
0, (height - window_height) / 2);
@@ -104,6 +108,11 @@ desktop_shell_configure(void *data,
WESTON_GTK_PANEL_WIDTH,
(height - window_height) / 2);
+ shell_helper_move_surface(desktop->helper,
+ desktop->launcher_grid->surface,
+ - grid_width,
+ ((height - window_height) / 2) + WESTON_GTK_CLOCK_HEIGHT);
+
desktop_shell_desktop_ready(desktop->shell);
/* TODO: why does the panel signal leave on drawing for
@@ -135,35 +144,20 @@ static void
launcher_grid_create (struct desktop *desktop)
{
struct element *launcher_grid;
- GtkWidget *grid;
+ GdkWindow *gdk_window;
launcher_grid = malloc (sizeof *launcher_grid);
memset (launcher_grid, 0, sizeof *launcher_grid);
- launcher_grid->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
- gtk_window_set_title(GTK_WINDOW(launcher_grid->window), "gtk shell");
- gtk_window_set_decorated(GTK_WINDOW(launcher_grid->window), FALSE);
- gtk_widget_realize(launcher_grid->window);
-
- grid = launcher_grid_new ();
+ launcher_grid->window = weston_gtk_launcher_new (desktop->background->window);
+ gdk_window = gtk_widget_get_window(launcher_grid->window);
+ launcher_grid->surface = gdk_wayland_window_get_wl_surface(gdk_window);
- gtk_container_add (GTK_CONTAINER (launcher_grid->window), grid);
-
- gtk_widget_show_all (grid);
+ gtk_widget_show_all(launcher_grid->window);
desktop->launcher_grid = launcher_grid;
}
-static void
-launcher_grid_toggle (GtkWidget *widget, struct desktop *desktop)
-{
- gboolean grid_visible;
-
- grid_visible = gtk_widget_is_visible (desktop->launcher_grid->window);
- gtk_widget_set_visible (desktop->launcher_grid->window, !grid_visible);
-}
-
static GtkWidget *
clock_create (struct desktop *desktop)
{
@@ -445,8 +439,8 @@ main(int argc, char *argv[])
css_setup(desktop);
panel_create(desktop);
clock_create(desktop);
- launcher_grid_create (desktop);
background_create(desktop);
+ launcher_grid_create (desktop);
gtk_main();
diff --git a/shell/launcher.c b/shell/launcher.c
new file mode 100644
index 0000000..679b40a
--- /dev/null
+++ b/shell/launcher.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2014 Collabora Ltd.
+ *
+ * Author: Jonny Lamb <jonny.lamb@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include "launcher.h"
+
+#include "clock.h"
+#include "panel.h"
+#include "shell-app-system.h"
+
+enum {
+ PROP_0,
+ PROP_BACKGROUND,
+};
+
+enum {
+ APP_LAUNCHED,
+ N_SIGNALS
+};
+static guint signals[N_SIGNALS] = { 0 };
+
+struct WestonGtkLauncherPrivate {
+ /* background widget so we know the output size */
+ GtkWidget *background;
+ ShellAppSystem *app_system;
+ GtkWidget *scrolled_window;
+ GtkWidget *grid;
+};
+
+G_DEFINE_TYPE(WestonGtkLauncher, weston_gtk_launcher, GTK_TYPE_WINDOW)
+
+/* each grid item is 114x114 */
+#define GRID_ITEM_WIDTH 114
+
+static void
+weston_gtk_launcher_init (WestonGtkLauncher *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ WESTON_GTK_LAUNCHER_TYPE,
+ WestonGtkLauncherPrivate);
+}
+
+static gint
+sort_apps (gconstpointer a,
+ gconstpointer b)
+{
+ GAppInfo *info1 = G_APP_INFO (a);
+ GAppInfo *info2 = G_APP_INFO (b);
+ gchar *s1, *s2;
+ gint ret;
+
+ s1 = g_utf8_casefold (g_app_info_get_display_name (info1), -1);
+ s2 = g_utf8_casefold (g_app_info_get_display_name (info2), -1);
+
+ ret = g_strcmp0 (s1, s2);
+
+ g_free (s1);
+ g_free (s2);
+
+ return ret;
+}
+
+static gboolean
+get_child_position_cb (GtkOverlay *overlay,
+ GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ GtkOverlayClass *klass = GTK_OVERLAY_GET_CLASS (overlay);
+
+ klass->get_child_position (overlay, widget, allocation);
+
+ /* use the same valign and halign properties, but given we have a
+ * border of 1px, respect it and don't draw the overlay widget over
+ * the border. */
+ allocation->x += 1;
+ allocation->y -= 1;
+ allocation->width -= 2;
+
+ return TRUE;
+}
+
+static gboolean
+app_launched_idle_cb (gpointer data)
+{
+ WestonGtkLauncher *self = data;
+ GtkAdjustment *adjustment;
+
+ /* make the scrolled window go back to the top */
+
+ adjustment = gtk_scrolled_window_get_vadjustment (
+ GTK_SCROLLED_WINDOW (self->priv->scrolled_window));
+
+ gtk_adjustment_set_value (adjustment, 0.0);
+
+ return FALSE;
+}
+
+static void
+clicked_cb (GtkWidget *widget,
+ GDesktopAppInfo *info)
+{
+ WestonGtkLauncher *self;
+
+ g_app_info_launch (G_APP_INFO (info), NULL, NULL, NULL);
+
+ self = g_object_get_data (G_OBJECT (widget), "launcher");
+ g_assert (self);
+ g_signal_emit (self, signals[APP_LAUNCHED], 0);
+
+ /* do this in an idle so it's not done so obviously onscreen */
+ g_idle_add (app_launched_idle_cb, self);
+}
+
+static gboolean
+app_enter_cb (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *revealer)
+{
+ gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), TRUE);
+ return FALSE;
+}
+
+static gboolean
+app_leave_cb (GtkWidget *widget,
+ GdkEvent *event,
+ GtkWidget *revealer)
+{
+ gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), FALSE);
+ return FALSE;
+}
+
+static GtkWidget *
+app_launcher_new_from_desktop_info (WestonGtkLauncher *self,
+ GDesktopAppInfo *info)
+{
+ GIcon *icon;
+ GtkWidget *alignment;
+ GtkWidget *image;
+ GtkWidget *button;
+ GtkWidget *overlay;
+ GtkWidget *revealer;
+ GtkWidget *label;
+ GtkWidget *ebox;
+
+ /* we need an ebox to catch enter and leave events */
+ ebox = gtk_event_box_new ();
+ gtk_style_context_add_class (gtk_widget_get_style_context (ebox),
+ "wgs-grid-item");
+
+ /* we use an overlay so we can have the app icon showing but use a
+ * GtkRevealer to show a label of the app's name. */
+ overlay = gtk_overlay_new ();
+ gtk_container_add (GTK_CONTAINER (ebox), overlay);
+
+ /* ...but each item has a border of 1px and we don't want the
+ * revealer to paint into the border, so overload this function to
+ * know where to put it. */
+ g_signal_connect (overlay, "get-child-position",
+ G_CALLBACK (get_child_position_cb), NULL);
+
+ revealer = gtk_revealer_new ();
+ g_object_set (revealer,
+ "halign", GTK_ALIGN_FILL, /* all the width */
+ "valign", GTK_ALIGN_END, /* only at the bottom */
+ NULL);
+ gtk_revealer_set_transition_type (GTK_REVEALER (revealer),
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (revealer), FALSE);
+ gtk_overlay_add_overlay (GTK_OVERLAY (overlay), revealer);
+
+ /* app name */
+ label = gtk_label_new (g_app_info_get_display_name (G_APP_INFO (info)));
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "wgs-grid-label");
+ gtk_container_add (GTK_CONTAINER (revealer), label);
+
+ /* icon button to load the app */
+ alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_container_add (GTK_CONTAINER (overlay), alignment);
+
+ icon = g_app_info_get_icon (G_APP_INFO (info));
+ image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
+ button = gtk_button_new ();
+ gtk_style_context_remove_class (
+ gtk_widget_get_style_context (button),
+ "button");
+ gtk_style_context_remove_class (
+ gtk_widget_get_style_context (button),
+ "image-button");
+ gtk_button_set_image (GTK_BUTTON (button), image);
+ g_object_set (image,
+ "margin", 30,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (alignment), button);
+
+ /* TODO: a bit ugly */
+ g_object_set_data (G_OBJECT (button), "launcher", self);
+ g_signal_connect (button, "clicked", G_CALLBACK (clicked_cb), info);
+
+ /* now we have set everything up, we can refernce the ebox and the
+ * revealer. enter will show the label and leave will hide the label. */
+ g_signal_connect (ebox, "enter-notify-event", G_CALLBACK (app_enter_cb), revealer);
+ g_signal_connect (ebox, "leave-notify-event", G_CALLBACK (app_leave_cb), revealer);
+
+ return ebox;
+}
+
+static void
+installed_changed_cb (ShellAppSystem *app_system,
+ WestonGtkLauncher *self)
+{
+ GHashTable *entries = shell_app_system_get_entries (app_system);
+ GList *l, *values;
+
+ gint output_width, output_height;
+ guint cols;
+ guint left, top;
+
+ /* remove all children first */
+ gtk_container_foreach (GTK_CONTAINER (self->priv->grid),
+ (GtkCallback) gtk_widget_destroy, NULL);
+
+ values = g_hash_table_get_values (entries);
+ values = g_list_sort (values, sort_apps);
+
+ weston_gtk_launcher_calculate (self, NULL, NULL, &cols);
+ cols--; /* because we start from zero here */
+
+ left = top = 0;
+ for (l = values; l; l = l->next)
+ {
+ GDesktopAppInfo *info = G_DESKTOP_APP_INFO (l->data);
+ GtkWidget *app = app_launcher_new_from_desktop_info (self, info);
+
+ gtk_grid_attach (GTK_GRID (self->priv->grid), app, left++, top, 1, 1);
+
+ if (left > cols)
+ {
+ left = 0;
+ top++;
+ }
+ }
+
+ g_list_free (values);
+
+ gtk_widget_show_all (self->priv->grid);
+}
+
+static void
+background_size_allocate_cb (GtkWidget *widget,
+ GdkRectangle *allocation,
+ WestonGtkLauncher *self)
+{
+ installed_changed_cb (self->priv->app_system, self);
+}
+
+static void
+weston_gtk_launcher_constructed (GObject *object)
+{
+ WestonGtkLauncher *self = WESTON_GTK_LAUNCHER (object);
+
+ G_OBJECT_CLASS (weston_gtk_launcher_parent_class)->constructed (object);
+
+ /* window properties */
+ gtk_window_set_title (GTK_WINDOW (self), "gtk shell");
+ gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
+ gtk_widget_realize (GTK_WIDGET (self));
+
+ /* make it black and slightly alpha */
+ gtk_style_context_add_class (
+ gtk_widget_get_style_context (GTK_WIDGET (self)),
+ "wgs-grid");
+
+ /* scroll it */
+ self->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (self), self->priv->scrolled_window);
+
+ /* main grid for apps */
+ self->priv->grid = gtk_grid_new ();
+ gtk_container_add (GTK_CONTAINER (self->priv->scrolled_window),
+ self->priv->grid);
+
+ /* fill the grid with apps */
+ self->priv->app_system = shell_app_system_get_default ();
+ g_signal_connect (self->priv->app_system, "installed-changed",
+ G_CALLBACK (installed_changed_cb), self);
+
+ /* refill the grid if the background is changed */
+ g_assert (self->priv->background != NULL);
+ g_signal_connect (self->priv->background, "size-allocate",
+ G_CALLBACK (background_size_allocate_cb), self);
+
+ /* now actually fill the grid */
+ installed_changed_cb (self->priv->app_system, self);
+}
+
+static void
+weston_gtk_launcher_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ WestonGtkLauncher *self = WESTON_GTK_LAUNCHER (object);
+
+ switch (param_id)
+ {
+ case PROP_BACKGROUND:
+ g_value_set_object (value, self->priv->background);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+weston_gtk_launcher_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ WestonGtkLauncher *self = WESTON_GTK_LAUNCHER (object);
+
+ switch (param_id)
+ {
+ case PROP_BACKGROUND:
+ self->priv->background = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+weston_gtk_launcher_class_init (WestonGtkLauncherClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *)klass;
+
+ object_class->constructed = weston_gtk_launcher_constructed;
+ object_class->get_property = weston_gtk_launcher_get_property;
+ object_class->set_property = weston_gtk_launcher_set_property;
+
+ g_object_class_install_property (object_class, PROP_BACKGROUND,
+ g_param_spec_object ("background",
+ "background",
+ "The #GtkWidget of the background of the output",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ 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);
+
+ g_type_class_add_private (object_class, sizeof (WestonGtkLauncherPrivate));
+}
+
+GtkWidget *
+weston_gtk_launcher_new (GtkWidget *background_widget)
+{
+ return g_object_new (WESTON_GTK_LAUNCHER_TYPE,
+ "background", background_widget,
+ NULL);
+}
+
+void
+weston_gtk_launcher_calculate (WestonGtkLauncher *self,
+ gint *grid_window_width,
+ gint *grid_window_height,
+ gint *grid_cols)
+{
+ gint output_width, output_height;
+ gint panel_height;
+ gint usable_width, usable_height;
+ guint cols, rows;
+
+ gtk_widget_get_size_request (self->priv->background,
+ &output_width, &output_height);
+
+ panel_height = output_height * WESTON_GTK_PANEL_HEIGHT_RATIO;
+
+ /* the scrollbar is 13 pixels wide */
+ usable_width = output_width - WESTON_GTK_PANEL_WIDTH - 13;
+
+ /* we want the height from under the clock which means we need to
+ * work out where the clock is, then add the clock height */
+ usable_height = output_height - (((output_height - panel_height) / 2) + WESTON_GTK_CLOCK_HEIGHT);
+
+ cols = (guint) (usable_width / GRID_ITEM_WIDTH);
+ rows = (guint) (usable_height / GRID_ITEM_WIDTH);
+
+ /* we don't want any blank space at the bottom of the grid so make
+ * sure it's full and resize if necessary */
+ /* TODO: this might not actually be a great idea. */
+ if (cols > 0)
+ {
+ guint num_apps;
+
+ num_apps = g_hash_table_size (
+ shell_app_system_get_entries (self->priv->app_system));
+
+ /* worst case cols = 1 */
+ while ((num_apps % cols) > 0)
+ cols--;
+ }
+
+ if (grid_window_width)
+ *grid_window_width = (cols * GRID_ITEM_WIDTH) + 13; /* add back the 13 for the scrollbar */
+
+ if (grid_window_height)
+ *grid_window_height = (rows * GRID_ITEM_WIDTH);
+
+ if (grid_cols)
+ *grid_cols = cols;
+}
diff --git a/shell/launcher.h b/shell/launcher.h
new file mode 100644
index 0000000..3bd10b0
--- /dev/null
+++ b/shell/launcher.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Collabora Ltd.
+ *
+ * Author: Jonny Lamb <jonny.lamb@collabora.co.uk>
+ */
+
+#ifndef __WESTON_GTK_LAUNCHER_H__
+#define __WESTON_GTK_LAUNCHER_H__
+
+#include <gtk/gtk.h>
+
+#define WESTON_GTK_LAUNCHER_TYPE (weston_gtk_launcher_get_type ())
+#define WESTON_GTK_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WESTON_GTK_LAUNCHER_TYPE, WestonGtkLauncher))
+#define WESTON_GTK_LAUNCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WESTON_GTK_LAUNCHER_TYPE, WestonGtkLauncherClass))
+#define WESTON_GTK_IS_LAUNCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WESTON_GTK_LAUNCHER_TYPE))
+#define WESTON_GTK_IS_LAUNCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WESTON_GTK_LAUNCHER_TYPE))
+#define WESTON_GTK_LAUNCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WESTON_GTK_LAUNCHER_TYPE, WestonGtkLauncherClass))
+
+typedef struct WestonGtkLauncher WestonGtkLauncher;
+typedef struct WestonGtkLauncherClass WestonGtkLauncherClass;
+typedef struct WestonGtkLauncherPrivate WestonGtkLauncherPrivate;
+
+struct WestonGtkLauncher
+{
+ GtkWindow parent;
+
+ WestonGtkLauncherPrivate *priv;
+};
+
+struct WestonGtkLauncherClass
+{
+ GtkWindowClass parent_class;
+};
+
+GType weston_gtk_launcher_get_type (void) G_GNUC_CONST;
+
+GtkWidget * weston_gtk_launcher_new (GtkWidget *background_widget);
+
+void weston_gtk_launcher_calculate (WestonGtkLauncher *self,
+ gint *grid_window_width, gint *grid_window_height,
+ gint *grid_cols);
+
+#endif /* __WESTON_GTK_LAUNCHER_H__ */
diff --git a/shell/style.css b/shell/style.css
index 4cb3fb5..b623430 100644
--- a/shell/style.css
+++ b/shell/style.css
@@ -29,3 +29,21 @@
background-color: #929292;
color: white;
}
+
+.wgs-grid {
+ background-color: alpha(white, 0.5);
+}
+
+.wgs-grid-item {
+ background-color: alpha(black, 0.7);
+ color: white;
+ font: Droid Sans 12;
+ border-style: solid;
+ border-color: #6d6d6d;
+ border-width: 1px;
+ border-radius: 1px;
+}
+
+.wgs-grid-label {
+ background-color: #929292;
+} \ No newline at end of file