[gnome-software/wip/hughsie/banner-next-gen] Implement the new featured banner carousel design



commit 2395a907e628078d686b60dd4b6f10f61e9dd5b7
Author: Richard Hughes <richard hughsie com>
Date:   Tue Jul 16 14:44:05 2019 +0100

    Implement the new featured banner carousel design
    
    Fixes https://gitlab.gnome.org/GNOME/gnome-software/issues/425

 src/gs-overview-page.c  | 121 ++++++++++++++++++++++--------------------------
 src/gs-overview-page.ui |  73 +++++++++++++++++++++++------
 src/gtk-style.css       |  19 ++++++++
 3 files changed, 134 insertions(+), 79 deletions(-)
---
diff --git a/src/gs-overview-page.c b/src/gs-overview-page.c
index d6371c20..ad3f24ce 100644
--- a/src/gs-overview-page.c
+++ b/src/gs-overview-page.c
@@ -48,7 +48,8 @@ typedef struct
        GtkWidget               *infobar_third_party;
        GtkWidget               *label_third_party;
        GtkWidget               *stack_featured;
-       GtkWidget               *box_featured_switcher;
+       GtkWidget               *button_featured_back;
+       GtkWidget               *button_featured_forwards;
        GtkWidget               *box_overview;
        GtkWidget               *box_popular;
        GtkWidget               *box_popular_rotating;
@@ -347,22 +348,19 @@ out:
        gs_overview_page_decrement_action_cnt (self);
 }
 
-static gboolean
-gs_overview_page_featured_rotate_cb (gpointer user_data)
+static void
+_feature_banner_forward (GsOverviewPage *self)
 {
-       GsOverviewPage *self = GS_OVERVIEW_PAGE (user_data);
        GsOverviewPagePrivate *priv = gs_overview_page_get_instance_private (self);
        GtkWidget *visible_child;
        GtkWidget *next_child = NULL;
-       GList *button_link;
        GList *banner_link;
        g_autoptr(GList) banners = NULL;
-       g_autoptr(GList) buttons = NULL;
 
        visible_child = gtk_stack_get_visible_child (GTK_STACK (priv->stack_featured));
        banners = gtk_container_get_children (GTK_CONTAINER (priv->stack_featured));
        if (banners == NULL)
-               return G_SOURCE_CONTINUE;
+               return;
 
        /* find banner after the currently visible one */
        for (banner_link = banners; banner_link != NULL; banner_link = banner_link->next) {
@@ -374,24 +372,43 @@ gs_overview_page_featured_rotate_cb (gpointer user_data)
                }
        }
        if (next_child == NULL)
-               next_child = g_list_nth_data (banners, 0);
-
+               next_child = g_list_first(banners)->data;
        gtk_stack_set_visible_child (GTK_STACK (priv->stack_featured), next_child);
+}
+
+static void
+_feature_banner_back (GsOverviewPage *self)
+{
+       GsOverviewPagePrivate *priv = gs_overview_page_get_instance_private (self);
+       GtkWidget *visible_child;
+       GtkWidget *next_child = NULL;
+       GList *banner_link;
+       g_autoptr(GList) banners = NULL;
+
+       visible_child = gtk_stack_get_visible_child (GTK_STACK (priv->stack_featured));
+       banners = gtk_container_get_children (GTK_CONTAINER (priv->stack_featured));
+       if (banners == NULL)
+               return;
 
-       /* update switcher */
-       buttons = gtk_container_get_children (GTK_CONTAINER (priv->box_featured_switcher));
-       for (banner_link = banners, button_link = buttons;
-            banner_link != NULL && button_link != NULL;
-            banner_link = banner_link->next, button_link = button_link->next) {
-               GtkWidget *event_box = button_link->data, *label;
-
-               label = gtk_bin_get_child (GTK_BIN (event_box));
-               if (banner_link->data == next_child)
-                       gtk_label_set_label (GTK_LABEL (label), FEATURED_SWITCHER_ACTIVE_TEXT);
-               else
-                       gtk_label_set_label (GTK_LABEL (label), FEATURED_SWITCHER_INACTIVE_TEXT);
+       /* find banner before the currently visible one */
+       for (banner_link = banners; banner_link != NULL; banner_link = banner_link->next) {
+               GtkWidget *child = banner_link->data;
+               if (child == visible_child) {
+                       if (banner_link->prev != NULL)
+                               next_child = banner_link->prev->data;
+                       break;
+               }
        }
+       if (next_child == NULL)
+               next_child = g_list_last(banners)->data;
+       gtk_stack_set_visible_child (GTK_STACK (priv->stack_featured), next_child);
+}
 
+static gboolean
+gs_overview_page_featured_rotate_cb (gpointer user_data)
+{
+       GsOverviewPage *self = GS_OVERVIEW_PAGE (user_data);
+       _feature_banner_forward (self);
        return G_SOURCE_CONTINUE;
 }
 
@@ -399,7 +416,6 @@ static void
 featured_reset_rotate_timer (GsOverviewPage *self)
 {
        GsOverviewPagePrivate *priv = gs_overview_page_get_instance_private (self);
-
        if (priv->featured_rotate_timer_id != 0)
                g_source_remove (priv->featured_rotate_timer_id);
        priv->featured_rotate_timer_id = g_timeout_add_seconds (FEATURED_ROTATE_TIME,
@@ -408,31 +424,17 @@ featured_reset_rotate_timer (GsOverviewPage *self)
 }
 
 static void
-featured_switcher_clicked (GtkWidget *event_box, GdkEventButton *event, gpointer data)
+_featured_back_clicked_cb (GsCategoryTile *tile, gpointer data)
 {
        GsOverviewPage *self = GS_OVERVIEW_PAGE (data);
-       GsOverviewPagePrivate *priv = gs_overview_page_get_instance_private (self);
-       g_autoptr(GList) buttons = NULL;
-       g_autoptr(GList) banners = NULL;
-       GList *button_link, *banner_link;
+       _feature_banner_back (self);
+}
 
-       buttons = gtk_container_get_children (GTK_CONTAINER (priv->box_featured_switcher));
-       banners = gtk_container_get_children (GTK_CONTAINER (priv->stack_featured));
-       for (button_link = buttons, banner_link = banners;
-            button_link != NULL && banner_link != NULL;
-            button_link = button_link->next, banner_link = banner_link->next) {
-               GtkWidget *e = button_link->data;
-               GtkWidget *label;
-
-               label = gtk_bin_get_child (GTK_BIN (e));
-               if (e == event_box) {
-                       gtk_stack_set_visible_child (GTK_STACK (priv->stack_featured), banner_link->data);
-                       featured_reset_rotate_timer (self);
-                       gtk_label_set_label (GTK_LABEL (label), FEATURED_SWITCHER_ACTIVE_TEXT);
-               } else {
-                       gtk_label_set_label (GTK_LABEL (label), FEATURED_SWITCHER_INACTIVE_TEXT);
-               }
-       }
+static void
+_featured_forward_clicked_cb (GsCategoryTile *tile, gpointer data)
+{
+       GsOverviewPage *self = GS_OVERVIEW_PAGE (data);
+       _feature_banner_forward (self);
 }
 
 static void
@@ -457,8 +459,6 @@ gs_overview_page_get_featured_cb (GObject *source_object,
 
        gtk_widget_hide (priv->featured_heading);
        gs_container_remove_all (GTK_CONTAINER (priv->stack_featured));
-       gtk_widget_hide (priv->box_featured_switcher);
-       gs_container_remove_all (GTK_CONTAINER (priv->box_featured_switcher));
        if (list == NULL) {
                g_warning ("failed to get featured apps: %s",
                           error->message);
@@ -478,31 +478,13 @@ gs_overview_page_get_featured_cb (GObject *source_object,
        }
        for (guint i = 0; i < gs_app_list_length (list); i++) {
                GsApp *app = gs_app_list_index (list, i);
-               GtkWidget *event_box;
-               GtkWidget *label;
-               GtkWidget *tile;
-
-               tile = gs_feature_tile_new (app);
+               GtkWidget *tile = gs_feature_tile_new (app);
                g_signal_connect (tile, "clicked",
                                  G_CALLBACK (app_tile_clicked), self);
                gtk_container_add (GTK_CONTAINER (priv->stack_featured), tile);
-
-               event_box = gtk_event_box_new ();
-               gtk_widget_show (event_box);
-               g_signal_connect (event_box, "button_release_event",
-                         G_CALLBACK (featured_switcher_clicked), self);
-               gtk_container_add (GTK_CONTAINER (priv->box_featured_switcher), event_box);
-
-               label = gtk_label_new (i == 0 ? FEATURED_SWITCHER_ACTIVE_TEXT : 
FEATURED_SWITCHER_INACTIVE_TEXT);
-               gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                                            "switcher-label");
-               gtk_widget_show (label);
-               gtk_container_add (GTK_CONTAINER (event_box), label);
        }
        gtk_widget_show (priv->featured_heading);
 
-       gtk_widget_set_visible (priv->box_featured_switcher, gs_app_list_length (list) > 1);
-
        priv->empty = FALSE;
        featured_reset_rotate_timer (self);
 
@@ -990,6 +972,12 @@ gs_overview_page_init (GsOverviewPage *self)
 {
        GsOverviewPagePrivate *priv = gs_overview_page_get_instance_private (self);
        gtk_widget_init_template (GTK_WIDGET (self));
+
+       g_signal_connect (priv->button_featured_back, "clicked",
+                         G_CALLBACK (_featured_back_clicked_cb), self);
+       g_signal_connect (priv->button_featured_forwards, "clicked",
+                         G_CALLBACK (_featured_forward_clicked_cb), self);
+
        priv->settings = g_settings_new ("org.gnome.software");
 }
 
@@ -1052,7 +1040,8 @@ gs_overview_page_class_init (GsOverviewPageClass *klass)
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, infobar_third_party);
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, label_third_party);
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, stack_featured);
-       gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, box_featured_switcher);
+       gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, button_featured_back);
+       gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, button_featured_forwards);
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, box_overview);
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, box_popular);
        gtk_widget_class_bind_template_child_private (widget_class, GsOverviewPage, box_popular_rotating);
diff --git a/src/gs-overview-page.ui b/src/gs-overview-page.ui
index 7097cd2d..78e3583c 100644
--- a/src/gs-overview-page.ui
+++ b/src/gs-overview-page.ui
@@ -92,23 +92,70 @@
                           </object>
                         </child>
                         <child>
-                          <object class="GtkStack" id="stack_featured">
+                          <object class="GtkOverlay" id="overlay">
                             <property name="visible">True</property>
                             <property name="halign">fill</property>
-                            <property name="transition-type">crossfade</property>
+                            <property name="valign">fill</property>
+                            <property name="height-request">150</property>
+                            <child type="overlay">
+                              <object class="GtkStack" id="stack_featured">
+                                <property name="visible">True</property>
+                                <property name="halign">fill</property>
+                                <property name="transition-type">crossfade</property>
+                              </object>
+                            </child>
+                            <child type="overlay">
+                              <object class="GtkButton" id="button_featured_back">
+                                <property name="visible">True</property>
+                                <property name="use_underline">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="halign">start</property>
+                                <property name="valign">center</property>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="button_featured_back_accessible">
+                                    <property name="accessible-name" translatable="yes">Previous</property>
+                                  </object>
+                                </child>
+                                <style>
+                                  <class name="featured-button"/>
+                                  <class name="featured-button-left"/>
+                                </style>
+                                <child>
+                                  <object class="GtkImage" id="back_image">
+                                    <property name="visible">True</property>
+                                    <property name="icon_name">go-previous-symbolic</property>
+                                    <property name="icon_size">1</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child type="overlay">
+                              <object class="GtkButton" id="button_featured_forwards">
+                                <property name="visible">True</property>
+                                <property name="use_underline">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="halign">end</property>
+                                <property name="valign">center</property>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="button_featured_forwards_accessible">
+                                    <property name="accessible-name" translatable="yes">Next</property>
+                                  </object>
+                                </child>
+                                <style>
+                                  <class name="featured-button"/>
+                                  <class name="featured-button-right"/>
+                                </style>
+                                <child>
+                                  <object class="GtkImage" id="forwards_image">
+                                    <property name="visible">True</property>
+                                    <property name="icon_name">go-next-symbolic</property>
+                                    <property name="icon_size">1</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
                           </object>
                         </child>
-                        <child>
-                          <object class="GtkBox" id="box_featured_switcher">
-                            <property name="visible">False</property>
-                            <property name="halign">center</property>
-                            <property name="homogeneous">True</property>
-                            <property name="orientation">horizontal</property>
-                            <property name="spacing">18</property>
-                            <property name="margin-top">6</property>
-                          </object>
-                        </child>
-
                         <child>
                           <object class="GtkLabel" id="category_heading">
                             <property name="visible">True</property>
diff --git a/src/gtk-style.css b/src/gtk-style.css
index 8e4d2793..e7a80354 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -524,3 +524,22 @@ flowboxchild {
 .switcher-label {
        opacity: 0.5;
 }
+
+.featured-button {
+       background-image: none;
+       background-color: shade(@theme_bg_color, 0.8);
+       color: @theme_selected_fg_color;
+       opacity: 0.8;
+       border-radius: 60px;
+       box-shadow: none
+}
+
+.featured-button-left {
+       margin-left: -20px;
+       padding: 10px 10px 10px 20px;
+}
+
+.featured-button-right {
+       margin-right: -20px;
+       padding: 10px 20px 10px 10px;
+}


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