Commit 816ffed2 authored by Jonny Lamb's avatar Jonny Lamb
Browse files

launcher: add new app launcher window and replace old grid with it

parent f49d4231
......@@ -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 \
......
......@@ -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();
......
/*
* 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;
}
/*
* 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__ */
......@@ -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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment