[gnome-control-center/wip/feborges/new-users-panel: 17/18] user-accounts: Introduce arrow frame (UmArrowFrame)



commit eb9994258200ab60b14713451afee55a0f3481ef
Author: Felipe Borges <felipeborges gnome org>
Date:   Fri Jul 8 12:46:11 2016 +0200

    user-accounts: Introduce arrow frame (UmArrowFrame)
    
    Type of GtkFrame which points to an item in an UmCarousel.

 panels/user-accounts/Makefile.am                  |    2 +
 panels/user-accounts/data/carousel.ui             |    3 +-
 panels/user-accounts/data/user-accounts-dialog.ui |    7 +-
 panels/user-accounts/um-arrow-frame.c             |  450 +++++++++++++++++++++
 panels/user-accounts/um-arrow-frame.h             |   41 ++
 panels/user-accounts/um-carousel.c                |    2 +-
 panels/user-accounts/um-user-panel.c              |    5 +
 7 files changed, 505 insertions(+), 5 deletions(-)
---
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index c77359d..68dcfd9 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -50,6 +50,8 @@ libuser_accounts_la_SOURCES =         \
        run-passwd.c                    \
        um-carousel.h                   \
        um-carousel.c                   \
+       um-arrow-frame.h                \
+       um-arrow-frame.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
index 56a0e37..6082900 100644
--- a/panels/user-accounts/data/carousel.ui
+++ b/panels/user-accounts/data/carousel.ui
@@ -3,11 +3,12 @@
   <!-- interface-requires gtk+ 3.8 -->
   <template class="UmCarousel" parent="GtkGrid">
     <property name="visible">True</property>
+    <property name="valign">start</property>
     <child>
       <object class="GtkOverlay">
         <property name="visible">True</property>
         <property name="expand">True</property>
-        <property name="border_width">15</property>
+        <property name="border_width">30</property>
         <child>
           <object class="GtkStack" id="stack">
             <property name="visible">True</property>
diff --git a/panels/user-accounts/data/user-accounts-dialog.ui 
b/panels/user-accounts/data/user-accounts-dialog.ui
index 800af5a..59f402c 100644
--- a/panels/user-accounts/data/user-accounts-dialog.ui
+++ b/panels/user-accounts/data/user-accounts-dialog.ui
@@ -20,8 +20,6 @@
         <child>
           <object class="GtkVBox" id="accounts-vbox">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
-            <property name="border_width">0</property>
             <child>
               <object class="UmCarousel" id="carousel">
                 <property name="visible">True</property>
@@ -29,8 +27,11 @@
               </object>
             </child>
             <child>
-              <object class="GtkFrame" id="hbox2">
+              <object class="UmArrowFrame" id="arrow_frame">
                 <property name="visible">True</property>
+                <style>
+                  <class name="background"/>
+                </style>
                 <child>
                   <object class="GtkVBox" id="main-user-vbox">
                     <property name="visible">True</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..742d08c
--- /dev/null
+++ b/panels/user-accounts/um-arrow-frame.c
@@ -0,0 +1,450 @@
+/* um-arrow-frame.c
+ *
+ * Copyright (C) 2016 Red Hat, Inc,
+ *
+ * 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/>.
+ *
+ * Writen by: Felipe Borges <felipeborges gnome org>,
+ *            Georges Basile Stavracas Neto <georges stavracas gmail com>
+ */
+
+#include "um-arrow-frame.h"
+
+#define ARROW_HEIGHT 16
+#define ARROW_WIDTH  36
+#define HANDLE_GAP   (ARROW_HEIGHT + 5)
+
+typedef struct {
+  UmCarousel *carousel;
+  GtkWidget  *item;
+
+  GdkWindow *handle_window;
+  gdouble offset;
+  gint margin_top;
+} UmArrowFramePrivate;
+
+struct _UmArrowFrame {
+  GtkFrame parent;
+
+  UmArrowFramePrivate *priv;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (UmArrowFrame, um_arrow_frame, GTK_TYPE_FRAME)
+
+GtkWidget*
+um_arrow_frame_new (void)
+{
+  return g_object_new (UM_TYPE_ARROW_FRAME, NULL);
+}
+
+static void
+um_arrow_frame_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (um_arrow_frame_parent_class)->finalize (object);
+}
+
+static void um_arrow_frame_set_item (UmCarousel *carousel, GtkToggleButton *item, UmArrowFrame *frame);
+
+static void
+item_destroyed (UmArrowFrame *frame)
+{
+  um_arrow_frame_set_carousel (frame, NULL);
+}
+
+static gint
+um_arrow_frame__get_row_x (UmArrowFrame *frame)
+{
+  GtkWidget *widget;
+  GtkWidget *row;
+  gint row_width;
+  gint dest_x;
+
+  widget = GTK_WIDGET (frame);
+
+  if (!frame->priv->item)
+    return gtk_widget_get_allocated_width (widget) / 2;
+
+  row = GTK_WIDGET (frame->priv->item);
+  row_width = gtk_widget_get_allocated_width (row);
+
+  gtk_widget_translate_coordinates (row,
+                                    widget,
+                                    row_width / 2,
+                                    0,
+                                    &dest_x,
+                                    NULL);
+
+  return CLAMP (dest_x,
+                0,
+                gtk_widget_get_allocated_width (widget));
+}
+
+static void
+um_arrow_frame__draw_arrow (UmArrowFrame *frame,
+                            cairo_t      *cr)
+{
+  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);
+
+  tip_x = um_arrow_frame__get_row_x (frame);
+  start_x = tip_x - (ARROW_WIDTH / 2);
+  end_x = tip_x + (ARROW_WIDTH / 2);
+
+  start_y = end_y = border.top + ARROW_HEIGHT;
+  tip_y = 0;
+  border_width = border.top;
+
+  /* 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 void
+um_arrow_frame__draw_background (UmArrowFrame *frame,
+                                 cairo_t      *cr)
+{
+  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);
+
+  start_x = margin.left;
+  end_x = alloc.width + margin.right;
+
+  start_y = margin.top + ARROW_HEIGHT;
+  end_y = alloc.height - margin.bottom;
+
+  start_gap = ((end_y - start_y + ARROW_WIDTH) / 2);
+  end_gap = ((end_y - start_y + ARROW_WIDTH) / 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,
+                        GTK_POS_TOP,
+                        start_gap,
+                        end_gap);
+
+}
+
+static gboolean
+um_arrow_frame_draw (GtkWidget *widget,
+                     cairo_t   *cr)
+{
+  UmArrowFrame *frame = UM_ARROW_FRAME (widget);
+  GtkWidget *child;
+
+  um_arrow_frame__draw_background (frame, cr);
+  um_arrow_frame__draw_arrow (frame, cr);
+
+  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)
+{
+  UmArrowFramePrivate *priv;
+
+  GTK_FRAME_CLASS (um_arrow_frame_parent_class)->compute_child_allocation (frame, allocation);
+
+  priv = UM_ARROW_FRAME (frame)->priv;
+}
+
+static void
+um_arrow_frame_get_preferred_height (GtkWidget *widget,
+                                     gint      *minimum_height,
+                                     gint      *natural_height)
+{
+  UmArrowFramePrivate *priv;
+
+  priv = UM_ARROW_FRAME (widget)->priv;
+
+  GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->get_preferred_height (widget,
+                                                                        minimum_height,
+                                                                        natural_height);
+
+  *minimum_height += ARROW_HEIGHT;
+  *natural_height += ARROW_HEIGHT;
+
+  *natural_height = MAX (*minimum_height, *natural_height + priv->offset);
+}
+
+static void
+um_arrow_frame_realize (GtkWidget *widget)
+{
+  UmArrowFramePrivate *priv;
+  GtkAllocation allocation;
+  GdkWindowAttr attributes = { 0 };
+  GdkDisplay *display;
+  GdkWindow *parent_window;
+  gint attributes_mask;
+
+  priv = UM_ARROW_FRAME (widget)->priv;
+  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 = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  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_size_allocate (GtkWidget     *widget,
+                              GtkAllocation *allocation)
+{
+  UmArrowFramePrivate *priv;
+
+  priv = UM_ARROW_FRAME (widget)->priv;
+
+  GTK_WIDGET_CLASS (um_arrow_frame_parent_class)->size_allocate (widget, allocation);
+
+  allocation->y = priv->margin_top - ARROW_HEIGHT;
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (gtk_widget_get_realized (widget))
+    {
+      gdk_window_move_resize (priv->handle_window,
+                              allocation->x,
+                              allocation->y,
+                              allocation->width,
+                              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;
+
+  widget_class->draw = um_arrow_frame_draw;
+  widget_class->get_preferred_height = um_arrow_frame_get_preferred_height;
+  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)
+{
+  GtkStyleProvider *provider;
+
+  self->priv = um_arrow_frame_get_instance_private (self);
+
+  provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ());
+  gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider), "* {border-top: solid 1px; border-color: 
@borders }", -1, NULL);
+  gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (self)),
+                                  provider,
+                                 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+  g_object_unref (provider);
+}
+
+static void
+um_arrow_frame_set_item (UmCarousel      *carousel,
+                         GtkToggleButton *item,
+                         UmArrowFrame    *frame)
+{
+  g_return_if_fail (UM_IS_ARROW_FRAME (frame));
+
+  if (frame->priv->item) {
+    g_signal_handlers_disconnect_by_func (frame->priv->item, item_destroyed, frame);
+  }
+
+  frame->priv->item = GTK_WIDGET (item);
+
+  if (item) {
+    g_signal_connect_swapped (item, "destroy", G_CALLBACK (item_destroyed), frame);
+
+    gtk_widget_queue_draw (GTK_WIDGET (frame));
+  }
+}
+
+static void
+um_arrow_frame_set_margin_top (GtkWidget     *widget,
+                               GtkAllocation *allocation,
+                               UmArrowFrame  *frame)
+{
+  frame->priv->margin_top = allocation->height;
+}
+
+void
+um_arrow_frame_set_carousel (UmArrowFrame *frame,
+                             UmCarousel   *carousel)
+{
+  if (carousel == NULL)
+    return;
+
+  frame->priv->carousel = carousel;
+
+  g_signal_connect (carousel, "size-allocate", G_CALLBACK (um_arrow_frame_set_margin_top), frame);
+  g_signal_connect (carousel, "item-activated", G_CALLBACK (um_arrow_frame_set_item), 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..090cc02
--- /dev/null
+++ b/panels/user-accounts/um-arrow-frame.h
@@ -0,0 +1,41 @@
+/* um-arrow-frame.c
+ *
+ * Copyright (C) 2016 Red Hat, Inc,
+ *
+ * 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/>.
+ *
+ * Writen by: Felipe Borges <felipeborges gnome org>,
+ *            Georges Basile Stavracas Neto <georges stavracas gmail com>
+ */
+
+#ifndef UM_ARROW_FRAME_H
+#define UM_ARROW_FRAME_H
+
+#include <gtk/gtk.h>
+#include "um-carousel.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_carousel      (UmArrowFrame          *frame,
+                                                                  UmCarousel            *carousel);
+
+G_END_DECLS
+
+#endif /* UM_ARROW_FRAME_H */
diff --git a/panels/user-accounts/um-carousel.c b/panels/user-accounts/um-carousel.c
index 6bf7037..ffc382c 100644
--- a/panels/user-accounts/um-carousel.c
+++ b/panels/user-accounts/um-carousel.c
@@ -93,7 +93,7 @@ create_item (UmCarousel *self,
         priv->current_button = GTK_RADIO_BUTTON (item);
 
         gtk_container_add (GTK_CONTAINER (item), child);
-        gtk_widget_show (GTK_WIDGET (item));
+        gtk_widget_show (GTK_WIDGET (child));
 
         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (item), FALSE);
         gtk_style_context_add_class (gtk_widget_get_style_context (item), "flat");
diff --git a/panels/user-accounts/um-user-panel.c b/panels/user-accounts/um-user-panel.c
index 63e8d3c..948701e 100644
--- a/panels/user-accounts/um-user-panel.c
+++ b/panels/user-accounts/um-user-panel.c
@@ -41,6 +41,7 @@
 
 #include "shell/cc-editable-entry.h"
 
+#include "um-arrow-frame.h"
 #include "um-editable-button.h"
 #include "um-editable-combo.h"
 #include "um-user-image.h"
@@ -76,6 +77,7 @@ struct _CcUserPanelPrivate {
         GSettings *login_screen_settings;
 
         UmCarousel *carousel;
+        UmArrowFrame *arrow_frame;
         GtkWidget *main_box;
         GPermission *permission;
         GtkWidget *language_chooser;
@@ -1244,6 +1246,7 @@ cc_user_panel_init (CcUserPanel *self)
         type = um_user_image_get_type ();
         type = um_cell_renderer_user_image_get_type ();
         type = um_carousel_get_type ();
+        type = um_arrow_frame_get_type ();
 
         gtk_widget_set_size_request (GTK_WIDGET (self), -1, 350);
 
@@ -1270,6 +1273,8 @@ cc_user_panel_init (CcUserPanel *self)
         d->history_dialog = um_history_dialog_new ();
 
         d->carousel = UM_CAROUSEL (get_widget (d, "carousel"));
+        d->arrow_frame = UM_ARROW_FRAME (get_widget (d, "arrow_frame"));
+        um_arrow_frame_set_carousel (d->arrow_frame, d->carousel);
         g_signal_connect (d->carousel, "item-activated", G_CALLBACK (item_toggled), d);
         d->selected_user = NULL;
         d->users_list = g_list_store_new (act_user_get_type ());


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]