[gnome-control-center/wip/feborges/new-users-panel: 36/36] user-accounts: Introduce users list carousel (UmCarousel)
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/feborges/new-users-panel: 36/36] user-accounts: Introduce users list carousel (UmCarousel)
- Date: Thu, 23 Jun 2016 13:15:40 +0000 (UTC)
commit 60f88ab62e0dca4da0f0273752c0d3abcd75fc98
Author: Felipe Borges <felipeborges gnome org>
Date: Wed Jun 22 17:42:24 2016 +0200
user-accounts: Introduce users list carousel (UmCarousel)
panels/user-accounts/Makefile.am | 4 +
panels/user-accounts/data/carousel.ui | 71 +++
panels/user-accounts/data/user-accounts-dialog.ui | 5 +
panels/user-accounts/um-arrow-frame.c | 628 +++++++++++++++++++++
panels/user-accounts/um-arrow-frame.h | 37 ++
panels/user-accounts/um-carousel.c | 204 +++++++
panels/user-accounts/um-carousel.h | 22 +
panels/user-accounts/um-user-panel.c | 8 +
panels/user-accounts/user-accounts.gresource.xml | 1 +
9 files changed, 980 insertions(+), 0 deletions(-)
---
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index d473bc5..c77359d 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -36,6 +36,8 @@ libuser_accounts_la_SOURCES = \
pw-utils.h \
pw-utils.c \
um-photo-dialog.h \
+ um-carousel.h \
+ um-carousel.c \
um-photo-dialog.c \
cc-crop-area.h \
cc-crop-area.c \
@@ -46,6 +48,8 @@ libuser_accounts_la_SOURCES = \
fingerprint-strings.h \
run-passwd.h \
run-passwd.c \
+ um-carousel.h \
+ um-carousel.c \
um-editable-button.h \
um-editable-button.c \
um-editable-combo.h \
diff --git a/panels/user-accounts/data/carousel.ui b/panels/user-accounts/data/carousel.ui
new file mode 100644
index 0000000..dc13fe9
--- /dev/null
+++ b/panels/user-accounts/data/carousel.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 3.8 -->
+ <template class="UmCarousel" parent="GtkGrid">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkOverlay">
+ <property name="visible">True</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <property name="transition_type">slide-left</property>
+ <property name="transition_duration">400</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkButton" id="go_back_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-size">3</property>
+ <property name="icon_name">go-previous-symbolic</property>
+ </object>
+ </child>
+ <signal name="clicked" handler="um_carousel_go_back_button_clicked" object="UmCarousel"
swapped="no"/>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="go_next_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon-size">3</property>
+ <property name="icon_name">go-next-symbolic</property>
+ </object>
+ </child>
+ <signal name="clicked" handler="um_carousel_go_next_button_clicked" object="UmCarousel"
swapped="no"/>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/user-accounts/data/user-accounts-dialog.ui
b/panels/user-accounts/data/user-accounts-dialog.ui
index bb68c97..c481a71 100644
--- a/panels/user-accounts/data/user-accounts-dialog.ui
+++ b/panels/user-accounts/data/user-accounts-dialog.ui
@@ -23,6 +23,11 @@
<property name="orientation">vertical</property>
<property name="border_width">12</property>
<child>
+ <object class="UmCarousel" id="carousel">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">18</property>
diff --git a/panels/user-accounts/um-arrow-frame.c b/panels/user-accounts/um-arrow-frame.c
new file mode 100644
index 0000000..2e2c8dc
--- /dev/null
+++ b/panels/user-accounts/um-arrow-frame.c
@@ -0,0 +1,628 @@
+/* gtd-arrow-frame.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "um-arrow-frame.h"
+
+typedef struct
+{
+ GtkWidget *row;
+
+ GtkGesture *pan_gesture;
+
+ GdkWindow *handle_window;
+ gdouble offset;
+ gint moving : 1;
+} UmArrowFramePrivate;
+
+struct _UmArrowFrame
+{
+ GtkFrame parent;
+
+ /*<private>*/
+ UmArrowFramePrivate *priv;
+};
+
+#define ARROW_HEIGHT 28
+#define ARROW_WIDTH 8
+#define HANDLE_GAP (ARROW_WIDTH + 5)
+
+G_DEFINE_TYPE_WITH_PRIVATE (UmArrowFrame, um_arrow_frame, GTK_TYPE_FRAME)
+
+enum {
+ PROP_0,
+ PROP_TASK_ROW,
+ LAST_PROP
+};
+
+GtkWidget*
+um_arrow_frame_new (void)
+{
+ return g_object_new (UM_TYPE_ARROW_FRAME, NULL);
+}
+
+static void
+on_drag_begin_cb (UmArrowFrame *frame,
+ gdouble x,
+ gdouble y,
+ GtkGesturePan *gesture)
+{
+ UmArrowFramePrivate *priv = frame->priv;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+
+ priv->moving = FALSE;
+
+ if (event->any.window == priv->handle_window)
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+ else
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static void
+on_pan_cb (GtkWidget *widget,
+ GtkPanDirection direction,
+ gdouble offset,
+ GtkGesturePan *pan)
+{
+ UmArrowFramePrivate *priv;
+ GtkTextDirection dir;
+ gdouble offset_x;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+ dir = gtk_widget_get_direction (widget);
+
+ priv->moving = TRUE;
+
+ gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (pan),
+ &offset_x,
+ NULL);
+
+ if (dir == GTK_TEXT_DIR_RTL)
+ priv->offset = MAX (0, offset_x);
+ else
+ priv->offset = MAX (0, priv->offset + -1 * offset_x);
+
+
+ gtk_widget_queue_resize (widget);
+}
+
+static void
+on_drag_end_cb (UmArrowFrame *frame,
+ gdouble x,
+ gdouble y,
+ GtkGesturePan *pan)
+{
+ UmArrowFramePrivate *priv = frame->priv;
+
+ if (!priv->moving)
+ gtk_gesture_set_state (GTK_GESTURE (pan), GTK_EVENT_SEQUENCE_DENIED);
+}
+
+static void
+um_arrow_frame_finalize (GObject *object)
+{
+ UmArrowFrame *self = (UmArrowFrame *)object;
+ UmArrowFramePrivate *priv = um_arrow_frame_get_instance_private (self);
+
+ g_clear_object (&priv->pan_gesture);
+
+ G_OBJECT_CLASS (um_arrow_frame_parent_class)->finalize (object);
+}
+
+static void
+um_arrow_frame_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UmArrowFrame *self = UM_ARROW_FRAME (object);
+
+ switch (prop_id)
+ {
+ case PROP_TASK_ROW:
+ g_value_set_object (value, self->priv->row);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+um_arrow_frame_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UmArrowFrame *self = UM_ARROW_FRAME (object);
+
+ switch (prop_id)
+ {
+ case PROP_TASK_ROW:
+ um_arrow_frame_set_row (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+um_arrow_frame__row_destroyed (UmArrowFrame *frame)
+{
+ um_arrow_frame_set_row (frame, NULL);
+}
+
+static gint
+um_arrow_frame__get_row_y (UmArrowFrame *frame)
+{
+ GtkWidget *widget;
+ GtkWidget *row;
+ gint row_height;
+ gint dest_y;
+
+ widget = GTK_WIDGET (frame);
+
+ if (!frame->priv->row)
+ return gtk_widget_get_allocated_height (widget) / 2;
+
+ row = GTK_WIDGET (frame->priv->row);
+ row_height = gtk_widget_get_allocated_height (row);
+
+ gtk_widget_translate_coordinates (row,
+ widget,
+ 0,
+ row_height / 2,
+ NULL,
+ &dest_y);
+
+ return CLAMP (dest_y,
+ 0,
+ gtk_widget_get_allocated_height (widget));
+}
+
+static void
+um_arrow_frame__draw_arrow (UmArrowFrame *frame,
+ cairo_t *cr,
+ GtkTextDirection dir)
+{
+ GtkWidget *widget = GTK_WIDGET (frame);
+ GtkStyleContext *context;
+ GtkAllocation alloc;
+ GtkStateFlags state;
+ GtkBorder border;
+ GdkRGBA border_color;
+ gint border_width;
+ gint start_x;
+ gint start_y;
+ gint tip_x;
+ gint tip_y;
+ gint end_x;
+ gint end_y;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_style_context_get_state (context);
+
+ gtk_style_context_get_border (context,
+ state,
+ &border);
+
+ gtk_style_context_get (context,
+ state,
+ GTK_STYLE_PROPERTY_BORDER_COLOR, &border_color,
+ NULL);
+
+ /* widget size */
+ gtk_widget_get_allocation (widget, &alloc);
+
+ if (dir == GTK_TEXT_DIR_LTR)
+ {
+ start_x = ARROW_WIDTH + border.left;
+ tip_x = 0;
+ end_x = ARROW_WIDTH + border.left;
+ border_width = border.left;
+ }
+ else
+ {
+ start_x = alloc.width - ARROW_WIDTH - border.right;
+ tip_x = alloc.width;
+ end_x = alloc.width - ARROW_WIDTH - border.right;
+ border_width = border.right;
+ }
+
+ tip_y = um_arrow_frame__get_row_y (frame);
+ start_y = tip_y - (ARROW_HEIGHT / 2);
+ end_y = tip_y + (ARROW_HEIGHT / 2);
+
+ /* draw arrow */
+ cairo_save (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to (cr, start_x, start_y);
+ cairo_line_to (cr, tip_x, tip_y);
+ cairo_line_to (cr, end_x, end_y);
+
+ /*
+ * Don't allow that gtk_render_background renders
+ * anything out of (tip_x, start_y) (end_x, end_y).
+ */
+ cairo_clip (cr);
+
+ /* render the arrow background */
+ gtk_render_background (context,
+ cr,
+ 0,
+ 0,
+ alloc.width,
+ alloc.height);
+
+ /* draw the border */
+ if (border_width > 0)
+ {
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+ gtk_style_context_get_border_color (context,
+ state,
+ &border_color);
+
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ gdk_cairo_set_source_rgba (cr, &border_color);
+
+ cairo_set_line_width (cr, 1);
+ cairo_move_to (cr, start_x, start_y);
+ cairo_line_to (cr, tip_x, tip_y);
+ cairo_line_to (cr, end_x, end_y);
+
+ cairo_set_line_width (cr, border_width + 1);
+ cairo_stroke (cr);
+ }
+
+ cairo_restore (cr);
+}
+
+static GtkGesture*
+um_arrow_frame__create_pan_gesture (UmArrowFrame *frame)
+{
+ GtkGesture *gesture;
+
+ gesture = gtk_gesture_pan_new (GTK_WIDGET (frame), GTK_ORIENTATION_HORIZONTAL);
+
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
+
+ g_signal_connect_swapped (gesture,
+ "drag-begin",
+ G_CALLBACK (on_drag_begin_cb),
+ frame);
+ g_signal_connect_swapped (gesture,
+ "pan",
+ G_CALLBACK (on_pan_cb),
+ frame);
+ g_signal_connect_swapped (gesture,
+ "drag-end",
+ G_CALLBACK (on_drag_end_cb),
+ frame);
+
+ return gesture;
+}
+
+static void
+um_arrow_frame__draw_background (UmArrowFrame *frame,
+ cairo_t *cr,
+ GtkTextDirection dir)
+{
+ GtkWidget *widget = GTK_WIDGET (frame);
+ GtkStyleContext *context;
+ GtkAllocation alloc;
+ GtkStateFlags state;
+ GtkBorder margin;
+ gint start_x;
+ gint start_y;
+ gint start_gap;
+ gint end_x;
+ gint end_y;
+ gint end_gap;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_style_context_get_state (context);
+
+ /* widget size */
+ gtk_widget_get_allocation (widget, &alloc);
+
+ /* margin */
+ gtk_style_context_get_margin (context,
+ state,
+ &margin);
+
+ if (dir == GTK_TEXT_DIR_LTR)
+ {
+ start_x = ARROW_WIDTH + margin.left;
+ end_x = alloc.width - margin.right;
+ }
+ else
+ {
+ start_x = margin.left;
+ end_x = alloc.width - margin.right - ARROW_WIDTH;
+ }
+
+
+ start_y = margin.top;
+ end_y = alloc.height - margin.bottom;
+
+ start_gap = ((end_y - start_y - ARROW_HEIGHT) / 2);
+ end_gap = ((end_y - start_y + ARROW_HEIGHT) / 2);
+
+ gtk_render_background (context,
+ cr,
+ start_x,
+ start_y,
+ end_x,
+ end_y);
+
+ gtk_render_frame_gap (context,
+ cr,
+ start_x,
+ start_y,
+ end_x,
+ end_y,
+ dir == GTK_TEXT_DIR_LTR ? GTK_POS_LEFT : GTK_POS_RIGHT,
+ start_gap,
+ end_gap);
+
+}
+
+static gboolean
+um_arrow_frame_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ UmArrowFrame *frame = UM_ARROW_FRAME (widget);
+ GtkTextDirection direction;
+ GtkWidget *child;
+
+ direction = gtk_widget_get_direction (widget);
+
+ um_arrow_frame__draw_background (frame,
+ cr,
+ direction);
+
+ um_arrow_frame__draw_arrow (frame,
+ cr,
+ direction);
+
+ child = gtk_bin_get_child (GTK_BIN (widget));
+
+ if (child)
+ {
+ gtk_container_propagate_draw (GTK_CONTAINER (widget),
+ child,
+ cr);
+ }
+
+ return TRUE;
+}
+
+static void
+um_arrow_frame_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *allocation)
+{
+ GTK_FRAME_CLASS (um_arrow_frame_parent_class)->compute_child_allocation (frame, allocation);
+
+ allocation->width -= ARROW_WIDTH;
+
+ if (gtk_widget_get_direction (GTK_WIDGET (frame)) != GTK_TEXT_DIR_RTL)
+ {
+ allocation->x += ARROW_WIDTH;
+ }
+}
+
+static void
+um_arrow_frame_get_preferred_width (GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ UmArrowFramePrivate *priv;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+
+ GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->get_preferred_width (widget,
+ minimum_width,
+ natural_width);
+
+ *minimum_width += ARROW_WIDTH;
+ *natural_width += ARROW_WIDTH;
+
+ *natural_width = MAX (*minimum_width, *natural_width + priv->offset);
+}
+
+static void
+um_arrow_frame_realize (GtkWidget *widget)
+{
+ UmArrowFramePrivate *priv;
+ GtkTextDirection dir;
+ GtkAllocation allocation;
+ GdkWindowAttr attributes = { 0 };
+ GdkDisplay *display;
+ GdkWindow *parent_window;
+ gint attributes_mask;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+ dir = gtk_widget_get_direction (widget);
+ display = gtk_widget_get_display (widget);
+ parent_window = gtk_widget_get_parent_window (widget);
+
+ gtk_widget_set_realized (widget, TRUE);
+
+ gtk_widget_set_window (widget, parent_window);
+ g_object_ref (parent_window);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.x = dir == GTK_TEXT_DIR_LTR ? allocation.x : allocation.height - HANDLE_GAP;
+ attributes.y = allocation.y;
+ attributes.width = HANDLE_GAP;
+ attributes.height = allocation.height;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.cursor = gdk_cursor_new_for_display (display, GDK_SB_H_DOUBLE_ARROW);
+ attributes.event_mask = gtk_widget_get_events (widget);
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+ attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
+
+ priv->handle_window = gdk_window_new (parent_window,
+ &attributes,
+ attributes_mask);
+
+ gtk_widget_register_window (widget, priv->handle_window);
+
+ g_clear_object (&attributes.cursor);
+}
+
+static void
+um_arrow_frame_unrealize (GtkWidget *widget)
+{
+ UmArrowFramePrivate *priv;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+
+ if (priv->handle_window)
+ {
+ gdk_window_hide (priv->handle_window);
+ gtk_widget_unregister_window (widget, priv->handle_window);
+ g_clear_pointer (&priv->handle_window, gdk_window_destroy);
+ }
+
+ GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->unrealize (widget);
+}
+
+static void
+um_arrow_frame_map (GtkWidget *widget)
+{
+ UmArrowFramePrivate *priv;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+
+ if (priv->handle_window)
+ gdk_window_show (priv->handle_window);
+
+ GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->map (widget);
+}
+
+static void
+um_arrow_frame_unmap (GtkWidget *widget)
+{
+ UmArrowFramePrivate *priv;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+
+ if (priv->handle_window)
+ gdk_window_hide (priv->handle_window);
+
+ GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->unmap (widget);
+}
+
+static void
+um_arrow_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ UmArrowFramePrivate *priv;
+ GtkTextDirection dir;
+
+ priv = UM_ARROW_FRAME (widget)->priv;
+ dir = gtk_widget_get_direction (widget);
+
+ GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->size_allocate (widget, allocation);
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ gdk_window_move_resize (priv->handle_window,
+ dir == GTK_TEXT_DIR_RTL ? allocation->width - HANDLE_GAP : allocation->x,
+ allocation->y,
+ HANDLE_GAP,
+ allocation->height);
+
+ gdk_window_raise (priv->handle_window);
+ }
+}
+
+static void
+um_arrow_frame_class_init (UmArrowFrameClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkFrameClass *frame_class = GTK_FRAME_CLASS (klass);
+
+ object_class->finalize = um_arrow_frame_finalize;
+ object_class->get_property = um_arrow_frame_get_property;
+ object_class->set_property = um_arrow_frame_set_property;
+
+ widget_class->draw = um_arrow_frame_draw;
+ widget_class->get_preferred_width = um_arrow_frame_get_preferred_width;
+ widget_class->map = um_arrow_frame_map;
+ widget_class->unmap = um_arrow_frame_unmap;
+ widget_class->realize = um_arrow_frame_realize;
+ widget_class->unrealize = um_arrow_frame_unrealize;
+ widget_class->size_allocate = um_arrow_frame_size_allocate;
+
+ frame_class->compute_child_allocation = um_arrow_frame_compute_child_allocation;
+
+ gtk_widget_class_set_css_name (widget_class, "arrow-frame");
+}
+
+static void
+um_arrow_frame_init (UmArrowFrame *self)
+{
+ self->priv = um_arrow_frame_get_instance_private (self);
+
+ self->priv->pan_gesture = um_arrow_frame__create_pan_gesture (self);
+}
+
+void
+um_arrow_frame_set_row (UmArrowFrame *frame,
+ GtkWidget *row)
+{
+ g_return_if_fail (UM_IS_ARROW_FRAME (frame));
+
+ if (frame->priv->row)
+ {
+ g_signal_handlers_disconnect_by_func (frame->priv->row, um_arrow_frame__row_destroyed, frame);
+ }
+
+ frame->priv->row = row;
+
+ if (row)
+ {
+ g_signal_connect_swapped (row,
+ "destroy",
+ G_CALLBACK (um_arrow_frame__row_destroyed),
+ frame);
+
+ gtk_widget_queue_draw (GTK_WIDGET (frame));
+ }
+}
diff --git a/panels/user-accounts/um-arrow-frame.h b/panels/user-accounts/um-arrow-frame.h
new file mode 100644
index 0000000..1008c44
--- /dev/null
+++ b/panels/user-accounts/um-arrow-frame.h
@@ -0,0 +1,37 @@
+/* gtd-arrow-frame.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UM_ARROW_FRAME_H
+#define UM_ARROW_FRAME_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define UM_TYPE_ARROW_FRAME (um_arrow_frame_get_type())
+
+G_DECLARE_FINAL_TYPE (UmArrowFrame, um_arrow_frame, UM, ARROW_FRAME, GtkFrame)
+
+GtkWidget* um_arrow_frame_new (void);
+
+void um_arrow_frame_set_row (UmArrowFrame *frame,
+ GtkWidget *row);
+
+G_END_DECLS
+
+#endif /* UM_ARROW_FRAME_H */
diff --git a/panels/user-accounts/um-carousel.c b/panels/user-accounts/um-carousel.c
new file mode 100644
index 0000000..54c71b0
--- /dev/null
+++ b/panels/user-accounts/um-carousel.c
@@ -0,0 +1,204 @@
+#include "um-carousel.h"
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#define ITEMS_PER_PAGE 3
+
+typedef struct {
+ GSequence *children;
+ GSequence *pages;
+ gint current_page;
+
+ /* Widgets */
+ GtkStack *stack;
+} UmCarouselPrivate;
+
+struct _UmCarousel {
+ GtkGrid parent;
+
+ UmCarouselPrivate *priv;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (UmCarousel, um_carousel, GTK_TYPE_GRID)
+
+void
+um_carousel_insert (UmCarousel *self,
+ GtkWidget *child,
+ gint position)
+{
+ UmCarouselPrivate *priv;
+ GSequenceIter *iter = NULL;
+ GtkWidget *box;
+ gint n_children;
+
+ priv = self->priv;
+
+ n_children = g_sequence_get_length (priv->children);
+ if (n_children > 0 && (n_children % ITEMS_PER_PAGE == 0)) {
+ gchar *page_name;
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ page_name = g_strdup_printf ("%d", g_sequence_get_length (priv->pages));
+ gtk_stack_add_named (priv->stack, box, page_name);
+
+ g_sequence_append (priv->pages, box);
+ }
+ else {
+ iter = g_sequence_get_end_iter (priv->pages);
+ iter = g_sequence_iter_prev (iter);
+ box = g_sequence_get (iter);
+ }
+
+ gtk_box_pack_end (GTK_BOX (box), child, TRUE, TRUE, 0);
+ gtk_widget_show_all (box);
+
+ if (position == 0)
+ g_sequence_prepend (priv->children, child);
+ else if (position == -1)
+ g_sequence_append (priv->children, child);
+ else {
+ iter = g_sequence_get_iter_at_pos (priv->children, position);
+ g_sequence_insert_before (iter, child);
+ }
+}
+
+GtkWidget *
+um_carousel_get_child_at_index (UmCarousel *self,
+ gint index)
+{
+ UmCarouselPrivate *priv;
+ GSequenceIter *iter;
+
+ priv = self->priv;
+
+ iter = g_sequence_get_iter_at_pos (priv->children, index);
+ if (!g_sequence_iter_is_end (iter))
+ return g_sequence_get (iter);
+
+ return NULL;
+}
+
+static void
+um_carousel_go_back_button_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ UmCarouselPrivate *priv;
+ gchar *page_name;
+
+ priv = UM_CAROUSEL (user_data)->priv;
+
+ priv->current_page--;
+ if (priv->current_page < 0)
+ priv->current_page = g_sequence_get_length (priv->pages) - 1;
+
+ page_name = g_strdup_printf ("%d", priv->current_page);
+
+ gtk_stack_set_visible_child_name (priv->stack, page_name);
+}
+
+static void
+um_carousel_go_next_button_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ UmCarouselPrivate *priv;
+ gchar *page_name;
+
+ priv = UM_CAROUSEL (user_data)->priv;
+
+ priv->current_page++;
+ if (priv->current_page >= g_sequence_get_length (priv->pages))
+ priv->current_page = 0;
+
+ page_name = g_strdup_printf ("%d", priv->current_page);
+ gtk_stack_set_visible_child_name (priv->stack, page_name);
+}
+
+enum {
+ PROP_0,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+UmCarousel *
+um_carousel_new (void)
+{
+ return g_object_new (UM_TYPE_CAROUSEL, NULL);
+}
+
+static void
+um_carousel_finalize (GObject *object)
+{
+ UmCarousel *self = (UmCarousel *)object;
+ UmCarouselPrivate *priv = um_carousel_get_instance_private (self);
+
+ G_OBJECT_CLASS (um_carousel_parent_class)->finalize (object);
+}
+
+static void
+um_carousel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UmCarousel *self = UM_CAROUSEL (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+um_carousel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UmCarousel *self = UM_CAROUSEL (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+um_carousel_class_init (UmCarouselClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = um_carousel_finalize;
+ object_class->get_property = um_carousel_get_property;
+ object_class->set_property = um_carousel_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/control-center/user-accounts/carousel.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, UmCarousel, stack);
+
+ gtk_widget_class_bind_template_callback (widget_class, um_carousel_go_back_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, um_carousel_go_next_button_clicked);
+}
+
+static void
+um_carousel_init (UmCarousel *self)
+{
+ UmCarouselPrivate *priv = um_carousel_get_instance_private (self);
+ GtkWidget *initial_box;
+
+ self->priv = priv;
+
+ priv->children = g_sequence_new (NULL);
+ priv->pages = g_sequence_new (NULL);
+ priv->current_page = 0;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ initial_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ g_sequence_append (priv->pages, initial_box);
+ gtk_stack_add_named (priv->stack, initial_box, "0");
+ gtk_widget_show_all (GTK_WIDGET (priv->stack));
+}
diff --git a/panels/user-accounts/um-carousel.h b/panels/user-accounts/um-carousel.h
new file mode 100644
index 0000000..3c31979
--- /dev/null
+++ b/panels/user-accounts/um-carousel.h
@@ -0,0 +1,22 @@
+#ifndef UM_CAROUSEL_H
+#define UM_CAROUSEL_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define UM_TYPE_CAROUSEL (um_carousel_get_type())
+
+G_DECLARE_FINAL_TYPE (UmCarousel, um_carousel, UM, CAROUSEL, GtkGrid)
+
+UmCarousel *um_carousel_new (void);
+GtkWidget *um_carousel_get_child_at_index (UmCarousel *self,
+ gint index);
+void um_carousel_insert (UmCarousel *self,
+ GtkWidget *child,
+ gint position);
+
+G_END_DECLS
+
+#endif /* UM_CAROUSEL_H */
diff --git a/panels/user-accounts/um-user-panel.c b/panels/user-accounts/um-user-panel.c
index 2b6ea7e..2f682a1 100644
--- a/panels/user-accounts/um-user-panel.c
+++ b/panels/user-accounts/um-user-panel.c
@@ -45,6 +45,7 @@
#include "um-editable-combo.h"
#include "um-user-image.h"
#include "um-cell-renderer-user-image.h"
+#include "um-carousel.h"
#include "um-account-dialog.h"
#include "cc-language-chooser.h"
@@ -74,6 +75,7 @@ struct _CcUserPanelPrivate {
GtkWidget *notification;
GSettings *login_screen_settings;
+ UmCarousel *carousel;
GtkWidget *main_box;
GPermission *permission;
GtkWidget *language_chooser;
@@ -244,6 +246,9 @@ user_added (ActUserManager *um, ActUser *user, CcUserPanelPrivate *d)
d->other_iter = gtk_tree_iter_copy (&iter);
g_free (title);
}
+
+ widget = gtk_label_new (get_real_or_user_name (user));
+ um_carousel_insert (d->carousel, widget, -1);
}
static void
@@ -1742,6 +1747,7 @@ cc_user_panel_init (CcUserPanel *self)
type = um_editable_combo_get_type ();
type = um_user_image_get_type ();
type = um_cell_renderer_user_image_get_type ();
+ type = um_carousel_get_type ();
gtk_widget_set_size_request (GTK_WIDGET (self), -1, 350);
@@ -1768,6 +1774,8 @@ cc_user_panel_init (CcUserPanel *self)
d->history_dialog = um_history_dialog_new ();
setup_main_window (self);
+ d->carousel = UM_CAROUSEL (get_widget (d, "carousel"));
+
context = gtk_widget_get_style_context (get_widget (d, "list-scrolledwindow"));
gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
context = gtk_widget_get_style_context (get_widget (d, "add-remove-toolbar"));
diff --git a/panels/user-accounts/user-accounts.gresource.xml
b/panels/user-accounts/user-accounts.gresource.xml
index ad57a8a..4bedc2d 100644
--- a/panels/user-accounts/user-accounts.gresource.xml
+++ b/panels/user-accounts/user-accounts.gresource.xml
@@ -7,6 +7,7 @@
<file alias="password-dialog.ui" preprocess="xml-stripblanks">data/password-dialog.ui</file>
<file alias="history-dialog.ui" preprocess="xml-stripblanks">data/history-dialog.ui</file>
<file alias="user-accounts-dialog.ui" preprocess="xml-stripblanks">data/user-accounts-dialog.ui</file>
+ <file alias="carousel.ui" preprocess="xml-stripblanks">data/carousel.ui</file>
<file alias="left-index-finger.png">data/icons/left-index-finger.png</file>
<file alias="left-middle-finger.png">data/icons/left-middle-finger.png</file>
<file alias="left-little-finger.png">data/icons/left-little-finger.png</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]