[gnome-software] Add the ability to add and show user-submitted application reviews



commit 5b304d259d24ad719f16b7d6d2a991f3cc4216a3
Author: Robert Ancell <robert ancell canonical com>
Date:   Tue Feb 9 12:19:07 2016 +0000

    Add the ability to add and show user-submitted application reviews
    
    Signed-off-by: Richard Hughes <richard hughsie com>

 po/POTFILES.in                   |    3 +
 src/Makefile.am                  |    8 +
 src/gnome-software.gresource.xml |    2 +
 src/gs-review-bar.c              |  107 +++++++++++++
 src/gs-review-bar.h              |   42 +++++
 src/gs-review-dialog.c           |   98 ++++++++++++
 src/gs-review-dialog.h           |   44 +++++
 src/gs-review-dialog.ui          |  246 +++++++++++++++++++++++++++++
 src/gs-review-histogram.c        |  129 +++++++++++++++
 src/gs-review-histogram.h        |   47 ++++++
 src/gs-review-histogram.ui       |  323 ++++++++++++++++++++++++++++++++++++++
 src/gs-shell-details.c           |  133 +++++++++++++++-
 src/gs-shell-details.ui          |   94 +++++++++++-
 src/gtk-style-hc.css             |    9 +
 src/gtk-style.css                |   19 +++
 15 files changed, 1296 insertions(+), 8 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aba0a13..fdf8ea3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -22,7 +22,10 @@ src/gs-main.c
 src/gs-page.c
 src/gs-plugin-loader.c
 src/gs-popular-tile.c
+src/gs-review-dialog.c
+[type: gettext/glade]src/gs-review-dialog.ui
 src/gs-review-row.c
+[type: gettext/glade]src/gs-review-histogram.ui
 [type: gettext/glade]src/gs-review-row.ui
 src/gs-screenshot-image.c
 src/gs-shell.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 74c3130..519143b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,6 +37,8 @@ UI_FILES =                                            \
        gs-app-row.ui                                   \
        gs-first-run-dialog.ui                          \
        gs-history-dialog.ui                            \
+       gs-review-dialog.ui                             \
+       gs-review-histogram.ui                          \
        gs-review-row.ui                                \
        gs-shell-category.ui                            \
        gs-shell-extras.ui                              \
@@ -143,6 +145,12 @@ gnome_software_SOURCES =                           \
        gs-progress-button.h                            \
        gs-review.c                                     \
        gs-review.h                                     \
+       gs-review-bar.c                                 \
+       gs-review-bar.h                                 \
+       gs-review-dialog.c                              \
+       gs-review-dialog.h                              \
+       gs-review-histogram.c                           \
+       gs-review-histogram.h                           \
        gs-review-row.c                                 \
        gs-review-row.h                                 \
        gs-screenshot-image.c                           \
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 746492d..261562f 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -13,6 +13,8 @@
   <file preprocess="xml-stripblanks">gs-app-row.ui</file>
   <file preprocess="xml-stripblanks">gs-first-run-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-history-dialog.ui</file>
+  <file preprocess="xml-stripblanks">gs-review-dialog.ui</file>
+  <file preprocess="xml-stripblanks">gs-review-histogram.ui</file>
   <file preprocess="xml-stripblanks">gs-review-row.ui</file>
   <file preprocess="xml-stripblanks">gs-shell-category.ui</file>
   <file preprocess="xml-stripblanks">gs-shell-extras.ui</file>
diff --git a/src/gs-review-bar.c b/src/gs-review-bar.c
new file mode 100644
index 0000000..b556789
--- /dev/null
+++ b/src/gs-review-bar.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#include "gs-review-bar.h"
+
+struct _GsReviewBar
+{
+       GtkBin           parent_instance;
+       gdouble          fraction;
+};
+
+G_DEFINE_TYPE (GsReviewBar, gs_review_bar, GTK_TYPE_BIN)
+
+/**
+ * gs_review_bar_set_fraction:
+ **/
+void
+gs_review_bar_set_fraction (GsReviewBar *bar, gdouble fraction)
+{
+       g_return_if_fail (GS_IS_REVIEW_BAR (bar));
+       bar->fraction = fraction;
+}
+
+/**
+ * gs_review_bar_init:
+ **/
+static void
+gs_review_bar_init (GsReviewBar *bar)
+{
+}
+
+/**
+ * gs_review_bar_draw:
+ **/
+static gboolean
+gs_review_bar_draw (GtkWidget *widget, cairo_t *cr)
+{
+       GtkStyleContext *context;
+       gdouble y_offset, bar_height;
+       GdkRGBA color;
+
+       context = gtk_widget_get_style_context (widget);
+
+       /* don't fill the complete height (too heavy beside GtkLabel of that height) */
+       y_offset = floor (0.15 * gtk_widget_get_allocated_height (widget));
+       bar_height = gtk_widget_get_allocated_height (widget) - (y_offset * 2);
+
+       gtk_render_background (context, cr,
+                              0, y_offset,
+                              gtk_widget_get_allocated_width (widget),
+                              bar_height);
+
+       cairo_rectangle (cr,
+                        0, y_offset,
+                        round (GS_REVIEW_BAR (widget)->fraction * gtk_widget_get_allocated_width (widget)),
+                        bar_height);
+       gtk_style_context_get_color (context, gtk_widget_get_state_flags (widget), &color);
+       cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
+       cairo_fill (cr);
+
+       return GTK_WIDGET_CLASS (gs_review_bar_parent_class)->draw (widget, cr);
+}
+
+/**
+ * gs_review_bar_class_init:
+ **/
+static void
+gs_review_bar_class_init (GsReviewBarClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       widget_class->draw = gs_review_bar_draw;
+}
+
+/**
+ * gs_review_bar_new:
+ **/
+GtkWidget *
+gs_review_bar_new (void)
+{
+       GsReviewBar *bar;
+       bar = g_object_new (GS_TYPE_REVIEW_BAR, NULL);
+       return GTK_WIDGET (bar);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-bar.h b/src/gs-review-bar.h
new file mode 100644
index 0000000..4510dc8
--- /dev/null
+++ b/src/gs-review-bar.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_REVIEW_BAR_H
+#define GS_REVIEW_BAR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_REVIEW_BAR (gs_review_bar_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsReviewBar, gs_review_bar, GS, REVIEW_BAR, GtkBin)
+
+GtkWidget      *gs_review_bar_new              (void);
+
+void            gs_review_bar_set_fraction     (GsReviewBar    *bar,
+                                                gdouble         fraction);
+
+G_END_DECLS
+
+#endif /* GS_REVIEW_BAR_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-dialog.c b/src/gs-review-dialog.c
new file mode 100644
index 0000000..a89dd1b
--- /dev/null
+++ b/src/gs-review-dialog.c
@@ -0,0 +1,98 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-review-dialog.h"
+#include "gs-star-widget.h"
+
+struct _GsReviewDialog
+{
+       GtkDialog        parent_instance;
+
+       GtkWidget       *star;
+       GtkWidget       *summary_entry;
+       GtkWidget       *text_view;
+};
+
+G_DEFINE_TYPE (GsReviewDialog, gs_review_dialog, GTK_TYPE_DIALOG)
+
+gint
+gs_review_dialog_get_rating (GsReviewDialog *dialog)
+{
+       return gs_star_widget_get_rating (GS_STAR_WIDGET (dialog->star));
+}
+
+void
+gs_review_dialog_set_rating (GsReviewDialog *dialog, gint rating)
+{
+       gs_star_widget_set_rating (GS_STAR_WIDGET (dialog->star), rating);
+}
+
+const gchar *
+gs_review_dialog_get_summary (GsReviewDialog *dialog)
+{
+       return gtk_entry_get_text (GTK_ENTRY (dialog->summary_entry));
+}
+
+gchar *
+gs_review_dialog_get_text (GsReviewDialog *dialog)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->text_view));
+       gtk_text_buffer_get_start_iter (buffer, &start);
+       gtk_text_buffer_get_end_iter (buffer, &end);
+       return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static void
+gs_review_dialog_init (GsReviewDialog *dialog)
+{
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+       gs_star_widget_set_icon_size (GS_STAR_WIDGET (dialog->star), 32);
+}
+
+static void
+gs_review_dialog_class_init (GsReviewDialogClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-review-dialog.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, star);
+       gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, summary_entry);
+       gtk_widget_class_bind_template_child (widget_class, GsReviewDialog, text_view);
+}
+
+GtkWidget *
+gs_review_dialog_new (void)
+{
+       return GTK_WIDGET (g_object_new (GS_TYPE_REVIEW_DIALOG,
+                                        "use-header-bar", TRUE,
+                                        NULL));
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-dialog.h b/src/gs-review-dialog.h
new file mode 100644
index 0000000..800d616
--- /dev/null
+++ b/src/gs-review-dialog.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_REVIEW_DIALOG_H
+#define GS_REVIEW_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_REVIEW_DIALOG (gs_review_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsReviewDialog, gs_review_dialog, GS, REVIEW_DIALOG, GtkDialog)
+
+GtkWidget      *gs_review_dialog_new           (void);
+gint            gs_review_dialog_get_rating    (GsReviewDialog *dialog);
+void            gs_review_dialog_set_rating    (GsReviewDialog *dialog,
+                                                gint            rating);
+const gchar    *gs_review_dialog_get_summary   (GsReviewDialog *dialog);
+gchar          *gs_review_dialog_get_text      (GsReviewDialog *dialog);
+
+G_END_DECLS
+
+#endif /* GS_REVIEW_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-dialog.ui b/src/gs-review-dialog.ui
new file mode 100644
index 0000000..aa4b109
--- /dev/null
+++ b/src/gs-review-dialog.ui
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GsReviewDialog" parent="GtkDialog">
+    <action-widgets>
+      <action-widget response="cancel">cancel_button</action-widget>
+      <action-widget response="ok">post_button</action-widget>
+    </action-widgets>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Post Review</property>
+    <property name="modal">True</property>
+    <property name="default_width">600</property>
+    <property name="default_height">300</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="use_header_bar">1</property>
+    <child internal-child="headerbar">
+      <object class="GtkHeaderBar">
+        <property name="show_close_button">False</property>
+        <child>
+          <object class="GtkButton" id="cancel_button">
+            <property name="label" translatable="yes">_Cancel</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+          </object>
+          <packing>
+            <property name="pack-type">start</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="post_button">
+            <property name="label" translatable="yes">_Post</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox">
+        <property name="can_focus">False</property>
+        <property name="margin_start">40</property>
+        <property name="margin_end">40</property>
+        <property name="margin_top">25</property>
+        <property name="margin_bottom">25</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">9</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">20</property>
+            <child>
+              <object class="GtkBox" id="box4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Rating</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GsStarWidget" id="star">
+                    <property name="visible">True</property>
+                    <property name="halign">center</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Summary</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Give a short summary of your review, for 
example: “Great app, would recommend”.</property>
+                    <property name="wrap">True</property>
+                    <property name="xalign">0</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="summary_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Review</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label5">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">What do you think of the app? Try to give 
reasons for your views.</property>
+                    <property name="wrap">True</property>
+                    <property name="xalign">0</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="text_view_scroll">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTextView" id="text_view">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="height-request">120</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gs-review-histogram.c b/src/gs-review-histogram.c
new file mode 100644
index 0000000..340982f
--- /dev/null
+++ b/src/gs-review-histogram.c
@@ -0,0 +1,129 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-review-histogram.h"
+#include "gs-review-bar.h"
+
+typedef struct
+{
+       GtkWidget       *bar1;
+       GtkWidget       *bar2;
+       GtkWidget       *bar3;
+       GtkWidget       *bar4;
+       GtkWidget       *bar5;
+       GtkWidget       *label_count1;
+       GtkWidget       *label_count2;
+       GtkWidget       *label_count3;
+       GtkWidget       *label_count4;
+       GtkWidget       *label_count5;
+       GtkWidget       *label_total;
+} GsReviewHistogramPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsReviewHistogram, gs_review_histogram, GTK_TYPE_BIN)
+
+static void
+set_label (GtkWidget *label, guint value)
+{
+       g_autofree gchar *text = NULL;
+       text = g_strdup_printf ("%u", value);
+       gtk_label_set_text (GTK_LABEL (label), text);
+}
+
+/**
+ * gs_review_histogram_set_ratings:
+ **/
+void
+gs_review_histogram_set_ratings (GsReviewHistogram *histogram,
+                                GArray *review_ratings)
+{
+       GsReviewHistogramPrivate *priv;
+       gdouble max;
+       gint count[5];
+       guint i;
+
+       g_return_if_fail (GS_IS_REVIEW_HISTOGRAM (histogram));
+       priv = gs_review_histogram_get_instance_private (histogram);
+
+       /* Scale to maximum value */
+       for (i = 0; i < 5; i++)
+               count[i] = g_array_index (review_ratings, gint, i + 1);
+       max = count[0];
+       max = count[1] > max ? count[1] : max;
+       max = count[2] > max ? count[2] : max;
+       max = count[3] > max ? count[3] : max;
+       max = count[4] > max ? count[4] : max;
+
+       gs_review_bar_set_fraction (GS_REVIEW_BAR (priv->bar5), count[4] / max);
+       set_label (priv->label_count5, count[4]);
+       gs_review_bar_set_fraction (GS_REVIEW_BAR (priv->bar4), count[3] / max);
+       set_label (priv->label_count4, count[3]);
+       gs_review_bar_set_fraction (GS_REVIEW_BAR (priv->bar3), count[2] / max);
+       set_label (priv->label_count3, count[2]);
+       gs_review_bar_set_fraction (GS_REVIEW_BAR (priv->bar2), count[1] / max);
+       set_label (priv->label_count2, count[1]);
+       gs_review_bar_set_fraction (GS_REVIEW_BAR (priv->bar1), count[0] / max);
+       set_label (priv->label_count1, count[0]);
+       set_label (priv->label_total, count[0] + count[1] + count[2] + count[3] + count[4]);
+}
+
+static void
+gs_review_histogram_init (GsReviewHistogram *histogram)
+{
+       gtk_widget_init_template (GTK_WIDGET (histogram));
+}
+
+static void
+gs_review_histogram_class_init (GsReviewHistogramClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-review-histogram.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, bar5);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, bar4);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, bar3);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, bar2);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, bar1);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_count5);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_count4);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_count3);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_count2);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_count1);
+       gtk_widget_class_bind_template_child_private (widget_class, GsReviewHistogram, label_total);
+}
+
+/**
+ * gs_review_histogram_new:
+ **/
+GtkWidget *
+gs_review_histogram_new (void)
+{
+       GsReviewHistogram *histogram;
+       histogram = g_object_new (GS_TYPE_REVIEW_HISTOGRAM, NULL);
+       return GTK_WIDGET (histogram);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-histogram.h b/src/gs-review-histogram.h
new file mode 100644
index 0000000..ea12f40
--- /dev/null
+++ b/src/gs-review-histogram.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_REVIEW_HISTOGRAM_H
+#define GS_REVIEW_HISTOGRAM_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_REVIEW_HISTOGRAM (gs_review_histogram_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (GsReviewHistogram, gs_review_histogram, GS, REVIEW_HISTOGRAM, GtkBin)
+
+struct _GsReviewHistogramClass
+{
+       GtkBinClass      parent_class;
+};
+
+GtkWidget      *gs_review_histogram_new                        (void);
+
+void            gs_review_histogram_set_ratings                (GsReviewHistogram *histogram,
+                                                                GArray *review_ratings);
+
+G_END_DECLS
+
+#endif /* GS_REVIEW_HISTOGRAM_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-review-histogram.ui b/src/gs-review-histogram.ui
new file mode 100644
index 0000000..45d7523
--- /dev/null
+++ b/src/gs-review-histogram.ui
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.10 -->
+  <template class="GsReviewHistogram" parent="GtkBin">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkGrid" id="grid1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="row-spacing">10</property>
+        <property name="column-spacing">5</property>
+        <child>
+          <object class="GtkImage" id="star5">
+            <property name="visible">True</property>
+            <property name="icon_name">starred-symbolic</property>
+            <style>
+              <class name="star"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="star_label5">
+            <property name="visible">True</property>
+            <property name="label">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GsReviewBar" id="bar5">
+            <property name="visible">True</property>
+            <property name="margin-left">25</property>
+            <property name="width-request">120</property>
+            <style>
+              <class name="reviewbar"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">2</property>
+            <property name="top-attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_count5">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">0</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="star4">
+            <property name="visible">True</property>
+            <property name="icon_name">starred-symbolic</property>
+            <style>
+              <class name="star"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">1</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="star_label4">
+            <property name="visible">True</property>
+            <property name="label">4</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">1</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GsReviewBar" id="bar4">
+            <property name="visible">True</property>
+            <property name="margin-left">25</property>
+            <style>
+              <class name="reviewbar"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">2</property>
+            <property name="top-attach">1</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_count4">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">1</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="star3">
+            <property name="visible">True</property>
+            <property name="icon_name">starred-symbolic</property>
+            <style>
+              <class name="star"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="star_label3">
+            <property name="visible">True</property>
+            <property name="label">3</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GsReviewBar" id="bar3">
+            <property name="visible">True</property>
+            <property name="margin-left">25</property>
+            <style>
+              <class name="reviewbar"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">2</property>
+            <property name="top-attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_count3">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">2</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="star2">
+            <property name="visible">True</property>
+            <property name="icon_name">starred-symbolic</property>
+            <style>
+              <class name="star"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">3</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="star_label2">
+            <property name="visible">True</property>
+            <property name="label">2</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">3</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GsReviewBar" id="bar2">
+            <property name="visible">True</property>
+            <property name="margin-left">25</property>
+            <style>
+              <class name="reviewbar"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">2</property>
+            <property name="top-attach">3</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_count2">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">3</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="star1">
+            <property name="visible">True</property>
+            <property name="icon_name">starred-symbolic</property>
+            <style>
+              <class name="star"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">4</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="star_label1">
+            <property name="visible">True</property>
+            <property name="label">1</property>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">4</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GsReviewBar" id="bar1">
+            <property name="visible">True</property>
+            <property name="margin-left">25</property>
+            <style>
+              <class name="reviewbar"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">2</property>
+            <property name="top-attach">4</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_count1">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">4</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Total</property>
+            <property name="halign">start</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">5</property>
+            <property name="width">3</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label_total">
+            <property name="visible">True</property>
+            <property name="label">0</property>
+            <property name="margin-left">5</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">3</property>
+            <property name="top-attach">5</property>
+            <property name="width">1</property>
+            <property name="height">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index c7316ca..aa931ba 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -34,6 +34,9 @@
 #include "gs-screenshot-image.h"
 #include "gs-progress-button.h"
 #include "gs-star-widget.h"
+#include "gs-review-histogram.h"
+#include "gs-review-dialog.h"
+#include "gs-review-row.h"
 
 typedef enum {
        GS_SHELL_DETAILS_STATE_LOADING,
@@ -50,7 +53,6 @@ struct _GsShellDetails
        GCancellable            *cancellable;
        GsApp                   *app;
        GsShell                 *shell;
-       GtkWidget               *star;
        SoupSession             *session;
 
        GtkWidget               *application_details_icon;
@@ -59,7 +61,8 @@ struct _GsShellDetails
        GtkWidget               *box_addons;
        GtkWidget               *box_details;
        GtkWidget               *box_details_description;
-       GtkWidget               *box_details_header;
+       GtkWidget               *star;
+       GtkWidget               *label_review_count;
        GtkWidget               *box_details_screenshot;
        GtkWidget               *box_details_screenshot_main;
        GtkWidget               *box_details_screenshot_thumbnails;
@@ -89,6 +92,10 @@ struct _GsShellDetails
        GtkWidget               *label_details_tag_webapp;
        GtkWidget               *label_details_info_text;
        GtkWidget               *list_box_addons;
+       GtkWidget               *box_reviews;
+       GtkWidget               *histogram;
+       GtkWidget               *button_review;
+       GtkWidget               *list_box_reviews;
        GtkWidget               *scrolledwindow_details;
        GtkWidget               *spinner_details;
        GtkWidget               *spinner_install_remove;
@@ -572,6 +579,7 @@ static void
 gs_shell_details_refresh_all (GsShellDetails *self)
 {
        GPtrArray *history;
+       GArray *review_ratings;
        GdkPixbuf *pixbuf = NULL;
        GList *addons;
        GtkWidget *widget;
@@ -729,6 +737,22 @@ gs_shell_details_refresh_all (GsShellDetails *self)
                } else {
                        gtk_widget_set_visible (self->star, FALSE);
                }
+               review_ratings = gs_app_get_review_ratings (self->app);
+               if (review_ratings != NULL) {
+                       gtk_widget_set_visible (self->histogram, TRUE);
+                       gs_review_histogram_set_ratings (GS_REVIEW_HISTOGRAM (self->histogram),
+                                                        review_ratings);
+               } else {
+                       gtk_widget_set_visible (self->histogram, FALSE);
+               }
+               if (gs_app_get_reviews (self->app) != NULL) {
+                       g_autofree gchar *text = NULL;
+                       gtk_widget_set_visible (self->label_review_count, TRUE);
+                       text = g_strdup_printf ("(%u)", gs_app_get_reviews (self->app)->len);
+                       gtk_label_set_text (GTK_LABEL (self->label_review_count), text);
+               } else {
+                       gtk_widget_set_visible (self->label_review_count, FALSE);
+               }
                break;
        }
 
@@ -947,6 +971,31 @@ gs_shell_details_refresh_addons (GsShellDetails *self)
        }
 }
 
+static void
+gs_shell_details_refresh_reviews (GsShellDetails *self)
+{
+       GPtrArray *reviews;
+       guint i;
+
+       if (!gs_plugin_loader_get_plugin_supported (self->plugin_loader,
+                                                   "gs_plugin_review_submit"))
+               return;
+
+       gs_container_remove_all (GTK_CONTAINER (self->list_box_reviews));
+
+       /* add all the reviews */
+       reviews = gs_app_get_reviews (self->app);
+       for (i = 0; i < reviews->len; i++) {
+               GsReview *review = g_ptr_array_index (reviews, i);
+               GtkWidget *row = gs_review_row_new (review);
+               gtk_container_add (GTK_CONTAINER (self->list_box_reviews), row);
+               gtk_widget_show (row);
+       }
+
+       /* FIXME: show the button only if the user never reviewed */
+       gtk_widget_set_visible (self->button_review, TRUE);
+}
+
 /**
  * gs_shell_details_app_refine_cb:
  **/
@@ -986,6 +1035,7 @@ gs_shell_details_app_refine_cb (GObject *source,
 
        gs_shell_details_refresh_screenshots (self);
        gs_shell_details_refresh_addons (self);
+       gs_shell_details_refresh_reviews (self);
        gs_shell_details_refresh_all (self);
        gs_shell_details_set_state (self, GS_SHELL_DETAILS_STATE_READY);
 }
@@ -1056,6 +1106,7 @@ gs_shell_details_filename_to_app_cb (GObject *source,
        gs_shell_details_switch_to (self);
        gs_shell_details_refresh_screenshots (self);
        gs_shell_details_refresh_addons (self);
+       gs_shell_details_refresh_reviews (self);
        gs_shell_details_refresh_all (self);
        gs_shell_details_set_state (self, GS_SHELL_DETAILS_STATE_READY);
 }
@@ -1287,6 +1338,65 @@ gs_shell_details_app_set_ratings_cb (GObject *source,
        }
 }
 
+
+/**
+ * gs_shell_details_app_set_review_cb:
+ **/
+static void
+gs_shell_details_app_set_review_cb (GObject *source,
+                               GAsyncResult *res,
+                               gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       GsShellDetails *self = GS_SHELL_DETAILS (user_data);
+       g_autoptr(GError) error = NULL;
+
+       if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error)) {
+               g_warning ("failed to set review %s: %s",
+                          gs_app_get_id (self->app), error->message);
+       }
+}
+
+/**
+ * gs_shell_details_write_review_cb:
+ **/
+static void
+gs_shell_details_write_review_cb (GtkButton *button,
+                                 GsShellDetails *self)
+{
+       GtkWidget *dialog;
+       GtkResponseType response;
+
+       dialog = gs_review_dialog_new ();
+
+       gtk_window_set_transient_for (GTK_WINDOW (dialog), gs_shell_get_window (self->shell));
+       response = gtk_dialog_run (GTK_DIALOG (dialog));
+       if (response == GTK_RESPONSE_OK) {
+               g_autoptr(GsReview) review = NULL;
+               g_autoptr(GDateTime) now = NULL;
+               g_autofree gchar *text = NULL;
+
+               review = gs_review_new ();
+               gs_review_set_summary (review, gs_review_dialog_get_summary (GS_REVIEW_DIALOG (dialog)));
+               text = gs_review_dialog_get_text (GS_REVIEW_DIALOG (dialog));
+               gs_review_set_text (review, text);
+               gs_review_set_rating (review, gs_review_dialog_get_rating (GS_REVIEW_DIALOG (dialog)));
+               gs_review_set_version (review, gs_app_get_version (self->app));
+               now = g_date_time_new_now_local ();
+               gs_review_set_date (review, now);
+
+               /* call into the plugins to set the new value */
+               gs_plugin_loader_review_action_async (self->plugin_loader,
+                                                     self->app,
+                                                     review,
+                                                     GS_PLUGIN_LOADER_ACTION_REVIEW_SUBMIT,
+                                                     self->cancellable,
+                                                     gs_shell_details_app_set_review_cb,
+                                                     self);
+       }
+       gtk_widget_destroy (dialog);
+}
+
 /**
  * gs_shell_details_rating_changed_cb:
  **/
@@ -1341,14 +1451,18 @@ gs_shell_details_setup (GsShellDetails *self,
        self->builder = g_object_ref (builder);
        self->cancellable = g_object_ref (cancellable);
 
+       /* Show review widgets if we have plugins that provide them */
+       if (gs_plugin_loader_get_plugin_supported (plugin_loader,
+                                                  "gs_plugin_review_submit"))
+               gtk_widget_set_visible (self->box_reviews, TRUE);
+       g_signal_connect (self->button_review, "clicked",
+                         G_CALLBACK (gs_shell_details_write_review_cb),
+                         self);
+
        /* set up star ratings */
-       self->star = gs_star_widget_new ();
        g_signal_connect (self->star, "rating-changed",
                          G_CALLBACK (gs_shell_details_rating_changed_cb),
                          self);
-       gtk_widget_set_visible (self->star, TRUE);
-       gtk_widget_set_valign (self->star, GTK_ALIGN_START);
-       gtk_box_pack_start (GTK_BOX (self->box_details_header), self->star, FALSE, FALSE, 0);
 
        /* setup details */
        g_signal_connect (self->button_install, "clicked",
@@ -1420,7 +1534,8 @@ gs_shell_details_class_init (GsShellDetailsClass *klass)
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_addons);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_details);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_details_description);
-       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_details_header);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, star);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, label_review_count);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_details_screenshot);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_details_screenshot_main);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, 
box_details_screenshot_thumbnails);
@@ -1450,6 +1565,10 @@ gs_shell_details_class_init (GsShellDetailsClass *klass)
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, label_details_tag_webapp);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, label_details_info_text);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, list_box_addons);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, box_reviews);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, histogram);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, button_review);
+       gtk_widget_class_bind_template_child (widget_class, GsShellDetails, list_box_reviews);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, scrolledwindow_details);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, spinner_details);
        gtk_widget_class_bind_template_child (widget_class, GsShellDetails, spinner_install_remove);
diff --git a/src/gs-shell-details.ui b/src/gs-shell-details.ui
index b67d6e8..97b7f67 100644
--- a/src/gs-shell-details.ui
+++ b/src/gs-shell-details.ui
@@ -227,6 +227,45 @@
                             <property name="position">1</property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkBox" id="star_box">
+                            <property name="visible">True</property>
+                            <property name="valign">start</property>
+                            <child>
+                              <object class="GsStarWidget" id="star">
+                                <property name="visible">True</property>
+                                <property name="halign">start</property>
+                                <property name="valign">center</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="label_review_count">
+                                <property name="visible">True</property>
+                                <property name="margin_start">5</property>
+                                <property name="halign">start</property>
+                                <property name="valign">center</property>
+                                <style>
+                                  <class name="dim-label"/>
+                                </style>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -1124,7 +1163,60 @@
                         <property name="position">12</property>
                       </packing>
                     </child>
-
+                    <child>
+                      <object class="GtkBox" id="box_reviews">
+                        <property name="orientation">vertical</property>
+                        <property name="margin_top">28</property>
+                        <child>
+                          <object class="GtkLabel" id="application_details_reviews_title">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_bottom">20</property>
+                            <property name="halign">start</property>
+                            <property name="valign">start</property>
+                            <property name="hexpand">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Reviews</property>
+                            <style>
+                              <class name="application-reviews-title"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GsReviewHistogram" id="histogram">
+                            <property name="halign">start</property>
+                            <property name="valign">center</property>
+                            <property name="margin_bottom">20</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="button_review">
+                            <property name="use_underline">True</property>
+                            <property name="label" translatable="yes">_Write a Review</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="halign">start</property>
+                            <property name="valign">start</property>
+                            <property name="margin_bottom">20</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkListBox" id="list_box_reviews">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="selection_mode">none</property>
+                            <style>
+                              <class name="review-listbox"/>
+                            </style>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">12</property>
+                      </packing>
+                    </child>
                   </object>
                 </child>
               </object>
diff --git a/src/gtk-style-hc.css b/src/gtk-style-hc.css
index a44213e..974c9ad 100644
--- a/src/gtk-style-hc.css
+++ b/src/gtk-style-hc.css
@@ -100,6 +100,15 @@
 .application-details-summary {
 }
 
+.application-reviews-title {
+       font-weight: bold;
+       font-size: 14px;
+}
+
+.review-summary {
+       font-weight: bold;
+}
+
 .application-details-description {
 }
 
diff --git a/src/gtk-style.css b/src/gtk-style.css
index 78b5cfa..263e4ff 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -138,6 +138,25 @@
 
 .install-progress:dir(rtl) { background-position: 100% bottom; }
 
+.application-reviews-title {
+       font-weight: bold;
+       font-size: 14px;
+}
+
+.review-summary {
+       font-weight: bold;
+}
+
+.review-listbox {
+       all: unset;
+}
+
+.reviewbar {
+       background-image: none;
+       background-color: #babdb6;
+       color: #555753;
+}
+
 .error-label {
        text-shadow: none;
 }


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