[gnome-builder] libide/gtk: add IdeInstallButton



commit 36c3b37dc0e836c6af154000fbe74fe69ea0adca
Author: Christian Hergert <chergert redhat com>
Date:   Mon Aug 22 17:56:32 2022 -0700

    libide/gtk: add IdeInstallButton
    
    This is meant to be similar to the install button we had in previous
    versions of Builder. Just a stack w/ two buttons and progress shown
    at the bottom.
    
    Surely we can do nicer things, but this will get us by for now.

 src/libide/gtk/ide-gtk-init.c           |   2 +
 src/libide/gtk/ide-install-button.c     | 291 ++++++++++++++++++++++++++++++++
 src/libide/gtk/ide-install-button.h     |  44 +++++
 src/libide/gtk/ide-install-button.ui    |  43 +++++
 src/libide/gtk/libide-gtk.gresource.xml |   2 +
 src/libide/gtk/libide-gtk.h             |   1 +
 src/libide/gtk/meson.build              |   2 +
 src/libide/gtk/style.css                |  10 ++
 src/libide/gui/ide-application.c        |   1 +
 9 files changed, 396 insertions(+)
---
diff --git a/src/libide/gtk/ide-gtk-init.c b/src/libide/gtk/ide-gtk-init.c
index ef758fec2..d6a798939 100644
--- a/src/libide/gtk/ide-gtk-init.c
+++ b/src/libide/gtk/ide-gtk-init.c
@@ -29,6 +29,7 @@
 #include "ide-file-chooser-entry.h"
 #include "ide-gtk-private.h"
 #include "ide-enum-object.h"
+#include "ide-install-button.h"
 #include "ide-progress-icon.h"
 #include "ide-radio-box.h"
 #include "ide-scrubber-revealer.h"
@@ -44,6 +45,7 @@ _ide_gtk_init (void)
   g_type_ensure (IDE_TYPE_ENUM_OBJECT);
   g_type_ensure (IDE_TYPE_ENTRY_POPOVER);
   g_type_ensure (IDE_TYPE_FILE_CHOOSER_ENTRY);
+  g_type_ensure (IDE_TYPE_INSTALL_BUTTON);
   g_type_ensure (IDE_TYPE_PROGRESS_ICON);
   g_type_ensure (IDE_TYPE_RADIO_BOX);
   g_type_ensure (IDE_TYPE_SCRUBBER_REVEALER);
diff --git a/src/libide/gtk/ide-install-button.c b/src/libide/gtk/ide-install-button.c
new file mode 100644
index 000000000..a3fda105a
--- /dev/null
+++ b/src/libide/gtk/ide-install-button.c
@@ -0,0 +1,291 @@
+/* ide-install-button.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-install-button"
+
+#include "config.h"
+
+#include "ide-install-button.h"
+
+struct _IdeInstallButton
+{
+  GtkWidget        parent_instance;
+
+  GCancellable    *cancellable;
+  IdeNotification *notification;
+
+  GtkStack        *stack;
+  GtkButton       *install;
+  GtkButton       *cancel;
+  GtkCssProvider  *css;
+
+  guint            disposed : 1;
+};
+
+enum {
+  PROP_0,
+  PROP_LABEL,
+  N_PROPS
+};
+
+enum {
+  CANCEL,
+  INSTALL,
+  N_SIGNALS
+};
+
+G_DEFINE_FINAL_TYPE (IdeInstallButton, ide_install_button, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+progress_changed_cb (IdeInstallButton *self,
+                     GParamSpec       *pspec,
+                     IdeNotification  *notif)
+{
+  g_autofree gchar *css = NULL;
+  double progress;
+  guint percentage;
+
+  g_assert (IDE_IS_INSTALL_BUTTON (self));
+  g_assert (IDE_IS_NOTIFICATION (notif));
+
+  progress = ide_notification_get_progress (notif);
+  percentage = CLAMP (progress * 100., .0, 100.);
+
+  if (percentage == 0)
+    css = g_strdup (".install-progress { background-size: 0; }");
+  else if (percentage == 100)
+    css = g_strdup (".install-progress { background-size: 100%; }");
+  else
+    css = g_strdup_printf (".install-progress { background-size: %u%%; }", percentage);
+
+  gtk_css_provider_load_from_data (self->css, css, -1);
+
+  if (progress >= 1.)
+    {
+      g_clear_object (&self->cancellable);
+      g_clear_object (&self->notification);
+      gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->install));
+    }
+}
+
+static void
+install_clicked_cb (IdeInstallButton *self,
+                    GtkButton        *button)
+{
+  g_assert (IDE_IS_INSTALL_BUTTON (self));
+
+  g_clear_object (&self->cancellable);
+  g_clear_object (&self->notification);
+
+  self->cancellable = g_cancellable_new ();
+  self->notification = ide_notification_new ();
+
+  g_signal_connect_object (self->notification,
+                           "notify::progress",
+                           G_CALLBACK (progress_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_emit (self, signals [INSTALL], 0, self->notification, self->cancellable);
+
+  if (self->notification != NULL)
+    progress_changed_cb (self, NULL, self->notification);
+}
+
+static void
+cancel_clicked_cb (IdeInstallButton *self,
+                   GtkButton        *button)
+{
+  g_assert (IDE_IS_INSTALL_BUTTON (self));
+
+  g_signal_emit (self, signals [CANCEL], 0, self->notification, self->cancellable);
+}
+
+static void
+real_install (IdeInstallButton *self,
+              IdeNotification  *notification,
+              GCancellable     *cancellable)
+{
+  g_assert (IDE_IS_INSTALL_BUTTON (self));
+  g_assert (IDE_IS_NOTIFICATION (notification));
+  g_assert (G_IS_CANCELLABLE (cancellable));
+
+  gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->cancel));
+}
+
+static void
+real_cancel (IdeInstallButton *self,
+             IdeNotification  *notification,
+             GCancellable     *cancellable)
+{
+  g_assert (IDE_IS_INSTALL_BUTTON (self));
+  g_assert (IDE_IS_NOTIFICATION (notification));
+  g_assert (G_IS_CANCELLABLE (cancellable));
+
+  g_cancellable_cancel (cancellable);
+
+  g_clear_object (&self->cancellable);
+  g_clear_object (&self->notification);
+
+  gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->install));
+}
+
+static void
+ide_install_button_dispose (GObject *object)
+{
+  IdeInstallButton *self = (IdeInstallButton *)object;
+  GtkWidget *child;
+
+  self->disposed = TRUE;
+
+  g_clear_object (&self->cancellable);
+  g_clear_object (&self->notification);
+
+  while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
+    gtk_widget_unparent (child);
+
+  G_OBJECT_CLASS (ide_install_button_parent_class)->dispose (object);
+}
+
+static void
+ide_install_button_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  IdeInstallButton *self = IDE_INSTALL_BUTTON (object);
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      g_value_set_string (value, ide_install_button_get_label (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_install_button_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  IdeInstallButton *self = IDE_INSTALL_BUTTON (object);
+
+  switch (prop_id)
+    {
+    case PROP_LABEL:
+      ide_install_button_set_label (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_install_button_class_init (IdeInstallButtonClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = ide_install_button_dispose;
+  object_class->get_property = ide_install_button_get_property;
+  object_class->set_property = ide_install_button_set_property;
+
+  properties [PROP_LABEL] =
+    g_param_spec_string ("label", NULL, NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  signals [INSTALL] =
+    g_signal_new_class_handler ("install",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_LAST,
+                                G_CALLBACK (real_install),
+                                NULL, NULL,
+                                NULL,
+                                G_TYPE_NONE, 2, IDE_TYPE_NOTIFICATION, G_TYPE_CANCELLABLE);
+
+  signals [CANCEL] =
+    g_signal_new_class_handler ("cancel",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_LAST,
+                                G_CALLBACK (real_cancel),
+                                NULL, NULL,
+                                NULL,
+                                G_TYPE_NONE, 2, IDE_TYPE_NOTIFICATION, G_TYPE_CANCELLABLE);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gtk/ide-install-button.ui");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+  gtk_widget_class_bind_template_child (widget_class, IdeInstallButton, cancel);
+  gtk_widget_class_bind_template_child (widget_class, IdeInstallButton, css);
+  gtk_widget_class_bind_template_child (widget_class, IdeInstallButton, install);
+  gtk_widget_class_bind_template_child (widget_class, IdeInstallButton, stack);
+  gtk_widget_class_bind_template_callback (widget_class, install_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, cancel_clicked_cb);
+}
+
+static void
+ide_install_button_init (IdeInstallButton *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (self->cancel)),
+                                  GTK_STYLE_PROVIDER (self->css),
+                                  G_MAXINT);
+}
+
+const char *
+ide_install_button_get_label (IdeInstallButton *self)
+{
+  g_return_val_if_fail (IDE_IS_INSTALL_BUTTON (self), NULL);
+
+  return gtk_button_get_label (self->install);
+}
+
+void
+ide_install_button_set_label (IdeInstallButton *self,
+                              const char       *label)
+{
+  g_return_if_fail (IDE_IS_INSTALL_BUTTON (self));
+
+  gtk_button_set_label (self->install, label);
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LABEL]);
+}
+
+void
+ide_install_button_cancel (IdeInstallButton *self)
+{
+  g_return_if_fail (IDE_IS_INSTALL_BUTTON (self));
+
+  if (self->disposed)
+    return;
+
+  if (gtk_stack_get_visible_child (self->stack) == GTK_WIDGET (self->cancel))
+    gtk_widget_activate (GTK_WIDGET (self->cancel));
+}
diff --git a/src/libide/gtk/ide-install-button.h b/src/libide/gtk/ide-install-button.h
new file mode 100644
index 000000000..52bc31f95
--- /dev/null
+++ b/src/libide/gtk/ide-install-button.h
@@ -0,0 +1,44 @@
+/* ide-install-button.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_INSTALL_BUTTON (ide_install_button_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeInstallButton, ide_install_button, IDE, INSTALL_BUTTON, GtkWidget)
+
+IDE_AVAILABLE_IN_ALL
+GtkWidget  *ide_install_button_new       (void);
+IDE_AVAILABLE_IN_ALL
+void        ide_install_button_cancel    (IdeInstallButton *self);
+IDE_AVAILABLE_IN_ALL
+const char *ide_install_button_get_label (IdeInstallButton *self);
+IDE_AVAILABLE_IN_ALL
+void        ide_install_button_set_label (IdeInstallButton *self,
+                                          const char       *label);
+
+G_END_DECLS
diff --git a/src/libide/gtk/ide-install-button.ui b/src/libide/gtk/ide-install-button.ui
new file mode 100644
index 000000000..433525989
--- /dev/null
+++ b/src/libide/gtk/ide-install-button.ui
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeInstallButton" parent="GtkWidget">
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="hhomogeneous">true</property>
+        <property name="vhomogeneous">true</property>
+        <property name="transition-type">crossfade</property>
+        <child>
+          <object class="GtkStackPage" id="install_page">
+            <property name="child">
+              <object class="GtkButton" id="install">
+                <style>
+                  <class name="text-button"/>
+                </style>
+                <property name="label" translatable="yes">_Install</property>
+                <property name="use-underline">true</property>
+                <signal name="clicked" handler="install_clicked_cb" swapped="true" 
object="IdeInstallButton"/>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage" id="cancel_page">
+            <property name="child">
+              <object class="GtkButton" id="cancel">
+                <style>
+                  <class name="text-button"/>
+                  <class name="install-progress"/>
+                </style>
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="use-underline">true</property>
+                <signal name="clicked" handler="cancel_clicked_cb" swapped="true" object="IdeInstallButton"/>
+              </object>
+            </property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkCssProvider" id="css">
+  </object>
+</interface>
diff --git a/src/libide/gtk/libide-gtk.gresource.xml b/src/libide/gtk/libide-gtk.gresource.xml
index 167fa8a18..fad9cc4f3 100644
--- a/src/libide/gtk/libide-gtk.gresource.xml
+++ b/src/libide/gtk/libide-gtk.gresource.xml
@@ -2,8 +2,10 @@
 <gresources>
   <gresource prefix="/org/gnome/libide-gtk">
     <file preprocess="xml-stripblanks">ide-entry-popover.ui</file>
+    <file preprocess="xml-stripblanks">ide-install-button.ui</file>
     <file preprocess="xml-stripblanks">ide-search-entry.ui</file>
     <file preprocess="xml-stripblanks">ide-shortcut-accel-dialog.ui</file>
     <file preprocess="xml-stripblanks">icons/enter-keyboard-shortcut.svg</file>
+    <file>style.css</file>
   </gresource>
 </gresources>
diff --git a/src/libide/gtk/libide-gtk.h b/src/libide/gtk/libide-gtk.h
index 21e326545..598bff738 100644
--- a/src/libide/gtk/libide-gtk.h
+++ b/src/libide/gtk/libide-gtk.h
@@ -29,6 +29,7 @@
 # include "ide-font-description.h"
 # include "ide-gtk.h"
 # include "ide-gtk-enums.h"
+# include "ide-install-button.h"
 # include "ide-joined-menu.h"
 # include "ide-menu-manager.h"
 # include "ide-progress-icon.h"
diff --git a/src/libide/gtk/meson.build b/src/libide/gtk/meson.build
index 037aebca8..d850c89a9 100644
--- a/src/libide/gtk/meson.build
+++ b/src/libide/gtk/meson.build
@@ -14,6 +14,7 @@ libide_gtk_public_headers = [
   'ide-file-manager.h',
   'ide-font-description.h',
   'ide-gtk.h',
+  'ide-install-button.h',
   'ide-joined-menu.h',
   'ide-menu-manager.h',
   'ide-progress-icon.h',
@@ -45,6 +46,7 @@ libide_gtk_public_sources = [
   'ide-file-manager.c',
   'ide-font-description.c',
   'ide-gtk.c',
+  'ide-install-button.c',
   'ide-joined-menu.c',
   'ide-menu-manager.c',
   'ide-progress-icon.c',
diff --git a/src/libide/gtk/style.css b/src/libide/gtk/style.css
new file mode 100644
index 000000000..d2e4e67c3
--- /dev/null
+++ b/src/libide/gtk/style.css
@@ -0,0 +1,10 @@
+.install-progress {
+  background-image: linear-gradient(to top, @accent_bg_color 2px, alpha(@accent_bg_color, 0) 2px);
+  background-repeat: no-repeat;
+  background-position: 0 bottom;
+  transition: none;
+  background-size: 0;
+}
+.install-progress:dir(rtl) {
+  background-position: 100% bottom;
+}
diff --git a/src/libide/gui/ide-application.c b/src/libide/gui/ide-application.c
index 76f24d725..9242c90bf 100644
--- a/src/libide/gui/ide-application.c
+++ b/src/libide/gui/ide-application.c
@@ -567,6 +567,7 @@ ide_application_init (IdeApplication *self)
   ide_application_load_typelibs (self);
 
   /* Ensure our core data is loaded early. */
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-gtk/");
   _ide_application_add_resources (self, "resource:///org/gnome/libide-tweaks/");
   _ide_application_add_resources (self, "resource:///org/gnome/libide-sourceview/");
   _ide_application_add_resources (self, "resource:///org/gnome/libide-gui/");


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