[gnome-builder] libide/editor: add plumbing for editor info bar
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/editor: add plumbing for editor info bar
- Date: Sun, 4 Sep 2022 16:13:24 +0000 (UTC)
commit b075aa03131fa663f383927b7ed05e83b50493bc
Author: Christian Hergert <chergert redhat com>
Date: Sun Sep 4 09:13:10 2022 -0700
libide/editor: add plumbing for editor info bar
This is just the scaffolding for the infobar, it isn't functional yet as
we need to wire up actions correctly.
Related #1769
src/libide/editor/ide-editor-info-bar-private.h | 33 +++
src/libide/editor/ide-editor-info-bar.c | 328 ++++++++++++++++++++++++
src/libide/editor/ide-editor-info-bar.ui | 103 ++++++++
src/libide/editor/ide-editor-page.c | 2 +
src/libide/editor/ide-editor-page.ui | 7 +
src/libide/editor/libide-editor.gresource.xml | 1 +
src/libide/editor/meson.build | 2 +
7 files changed, 476 insertions(+)
---
diff --git a/src/libide/editor/ide-editor-info-bar-private.h b/src/libide/editor/ide-editor-info-bar-private.h
new file mode 100644
index 000000000..e9644c2c6
--- /dev/null
+++ b/src/libide/editor/ide-editor-info-bar-private.h
@@ -0,0 +1,33 @@
+/* ide-editor-info-bar-private.h
+ *
+ * Copyright 2021-2022 Christian Hergert <unknown domain org>
+ *
+ * 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 <libide-code.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_INFO_BAR (ide_editor_info_bar_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorInfoBar, ide_editor_info_bar, IDE, EDITOR_INFO_BAR, GtkWidget)
+
+GtkWidget *_ide_editor_info_bar_new (IdeBuffer *buffer);
+
+G_END_DECLS
diff --git a/src/libide/editor/ide-editor-info-bar.c b/src/libide/editor/ide-editor-info-bar.c
new file mode 100644
index 000000000..5d470eb75
--- /dev/null
+++ b/src/libide/editor/ide-editor-info-bar.c
@@ -0,0 +1,328 @@
+/* ide-editor-info-bar.c
+ *
+ * Copyright 2021-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-editor-info-bar"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libide-gui.h>
+
+#include "ide-editor-info-bar-private.h"
+
+struct _IdeEditorInfoBar
+{
+ GtkWidget parent_instance;
+
+ IdeBuffer *buffer;
+
+ GtkBox *box;
+
+ /* Discard widgetry */
+ GtkInfoBar *discard_infobar;
+ GtkButton *discard;
+ GtkButton *save;
+ GtkLabel *title;
+ GtkLabel *subtitle;
+
+ /* Permission denied infobar */
+ GtkInfoBar *access_infobar;
+ GtkButton *access_subtitle;
+ GtkButton *access_title;
+ GtkButton *access_try_admin;
+};
+
+enum {
+ PROP_0,
+ PROP_BUFFER,
+ N_PROPS
+};
+
+G_DEFINE_TYPE (IdeEditorInfoBar, ide_editor_info_bar, GTK_TYPE_WIDGET)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_editor_info_bar_update (IdeEditorInfoBar *self)
+{
+ g_assert (IDE_IS_EDITOR_INFO_BAR (self));
+ g_assert (IDE_IS_BUFFER (self->buffer));
+
+ /* Ignore things if we're busy to avoid flapping */
+ switch (ide_buffer_get_state (self->buffer))
+ {
+ case IDE_BUFFER_STATE_READY:
+ case IDE_BUFFER_STATE_FAILED:
+ break;
+
+ case IDE_BUFFER_STATE_LOADING:
+ case IDE_BUFFER_STATE_SAVING:
+ default:
+ gtk_info_bar_set_revealed (self->discard_infobar, FALSE);
+ return;
+ }
+
+ if (ide_buffer_get_changed_on_volume (self->buffer))
+ {
+ gtk_button_set_label (self->discard, _("_Discard Changes and Reload"));
+ gtk_button_set_use_underline (self->discard, TRUE);
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (self->discard), "page.discard-changes");
+ gtk_label_set_label (self->title, _("File Has Changed on Disk"));
+ gtk_label_set_label (self->subtitle, _("The file has been changed by another program."));
+ gtk_widget_show (GTK_WIDGET (self->discard));
+ gtk_widget_hide (GTK_WIDGET (self->save));
+ gtk_info_bar_set_revealed (self->discard_infobar, TRUE);
+ }
+#if 0
+ /* TODO: If we end up doing drafts like g-t-e, we might need this */
+ else if (_ide_buffer_get_was_restored (self->buffer))
+ {
+ if (ide_buffer_get_file (self->buffer) == NULL)
+ {
+ gtk_button_set_label (self->save, _("Save _As…"));
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (self->save), "page.save-as");
+ gtk_label_set_label (self->title, _("Buffer Restored"));
+ gtk_label_set_label (self->subtitle, _("Unsaved buffer has been restored."));
+ gtk_widget_hide (GTK_WIDGET (self->discard));
+ gtk_widget_show (GTK_WIDGET (self->save));
+ }
+ else
+ {
+ gtk_button_set_label (self->save, _("_Save…"));
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (self->save), "page.confirm-save");
+ gtk_button_set_label (self->discard, _("_Discard…"));
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (self->discard), "page.confirm-discard-changes");
+ gtk_label_set_label (self->title, _("Draft Changes Restored"));
+ gtk_label_set_label (self->subtitle, _("Unsaved changes to the buffer have been restored."));
+ gtk_widget_show (GTK_WIDGET (self->discard));
+ gtk_widget_show (GTK_WIDGET (self->save));
+ }
+
+ gtk_info_bar_set_revealed (self->discard_infobar, TRUE);
+ }
+#endif
+ else
+ {
+ gtk_info_bar_set_revealed (self->discard_infobar, FALSE);
+ }
+}
+
+static void
+ide_editor_info_bar_wrap_button_label (GtkButton *button)
+{
+ GtkWidget *label;
+
+ g_assert (GTK_IS_BUTTON (button));
+
+ label = gtk_button_get_child (button);
+ g_assert (GTK_IS_LABEL (label));
+
+ gtk_label_set_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+}
+
+static void
+on_notify_cb (IdeEditorInfoBar *self,
+ GParamSpec *pspec,
+ IdeBuffer *buffer)
+{
+ g_assert (IDE_IS_EDITOR_INFO_BAR (self));
+ g_assert (IDE_IS_BUFFER (buffer));
+
+ ide_editor_info_bar_update (self);
+}
+
+static void
+on_response_cb (IdeEditorInfoBar *self,
+ int response,
+ GtkInfoBar *infobar)
+{
+ g_assert (IDE_IS_EDITOR_INFO_BAR (self));
+ g_assert (GTK_IS_INFO_BAR (infobar));
+
+ gtk_info_bar_set_revealed (infobar, FALSE);
+}
+
+static void
+on_try_admin_cb (IdeEditorInfoBar *self,
+ GtkButton *button)
+{
+ g_assert (IDE_IS_EDITOR_INFO_BAR (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+#if 0
+ _ide_buffer_use_admin (self->buffer);
+#endif
+}
+
+static void
+on_try_again_cb (IdeEditorInfoBar *self,
+ GtkButton *button)
+{
+ IdeBufferManager *buffer_manager;
+ IdeContext *context;
+
+ g_assert (IDE_IS_EDITOR_INFO_BAR (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+ context = ide_widget_get_context (GTK_WIDGET (button));
+ buffer_manager = ide_buffer_manager_from_context (context);
+
+ ide_buffer_manager_load_file_async (buffer_manager,
+ ide_buffer_get_file (self->buffer),
+ IDE_BUFFER_OPEN_FLAGS_FORCE_RELOAD,
+ NULL, /* TODO: Progress */
+ NULL, NULL, NULL);
+}
+
+static void
+ide_editor_info_bar_dispose (GObject *object)
+{
+ IdeEditorInfoBar *self = (IdeEditorInfoBar *)object;
+
+ g_clear_object (&self->buffer);
+ g_clear_pointer ((GtkWidget **)&self->box, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (ide_editor_info_bar_parent_class)->dispose (object);
+}
+
+static void
+ide_editor_info_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorInfoBar *self = IDE_EDITOR_INFO_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUFFER:
+ g_value_set_object (value, self->buffer);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_info_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEditorInfoBar *self = IDE_EDITOR_INFO_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_BUFFER:
+ if (g_set_object (&self->buffer, g_value_get_object (value)))
+ {
+#if 0
+ /* TODO: If we end up suggesting admin:// URIs */
+ g_object_bind_property (self->buffer, "suggest-admin",
+ self->access_try_admin, "visible",
+ G_BINDING_SYNC_CREATE);
+#endif
+ g_object_bind_property (self->buffer, "failed",
+ self->access_infobar, "revealed",
+ G_BINDING_SYNC_CREATE);
+ g_signal_connect_object (self->buffer,
+ "notify::busy",
+ G_CALLBACK (on_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->buffer,
+ "notify::changed-on-volume",
+ G_CALLBACK (on_notify_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_info_bar_class_init (IdeEditorInfoBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = ide_editor_info_bar_dispose;
+ object_class->get_property = ide_editor_info_bar_get_property;
+ object_class->set_property = ide_editor_info_bar_set_property;
+
+ properties [PROP_BUFFER] =
+ g_param_spec_object ("buffer",
+ "Buffer",
+ "The buffer to monitor",
+ IDE_TYPE_BUFFER,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-editor/ide-editor-info-bar.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, access_infobar);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, access_try_admin);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, access_subtitle);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, access_title);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, box);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, discard);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, discard_infobar);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, save);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, subtitle);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorInfoBar, title);
+ gtk_widget_class_bind_template_callback (widget_class, on_try_admin_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_try_again_cb);
+}
+
+static void
+ide_editor_info_bar_init (IdeEditorInfoBar *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ /*
+ * Ensure buttons with long labels can wrap text and are
+ * center-justified, so the infobar can fit narrow screens.
+ */
+ ide_editor_info_bar_wrap_button_label (self->access_try_admin);
+ ide_editor_info_bar_wrap_button_label (self->discard);
+
+ g_signal_connect_object (self->discard_infobar,
+ "response",
+ G_CALLBACK (on_response_cb),
+ self,
+ G_CONNECT_SWAPPED);
+}
+
+GtkWidget *
+_ide_editor_info_bar_new (IdeBuffer *buffer)
+{
+ g_return_val_if_fail (IDE_IS_BUFFER (buffer), NULL);
+
+ return g_object_new (IDE_TYPE_EDITOR_INFO_BAR,
+ "buffer", buffer,
+ NULL);
+}
diff --git a/src/libide/editor/ide-editor-info-bar.ui b/src/libide/editor/ide-editor-info-bar.ui
new file mode 100644
index 000000000..d75e251ad
--- /dev/null
+++ b/src/libide/editor/ide-editor-info-bar.ui
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <template class="IdeEditorInfoBar" parent="GtkWidget">
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkInfoBar" id="access_infobar">
+ <property name="message-type">error</property>
+ <property name="show-close-button">false</property>
+ <property name="revealed">false</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="access_title">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Could Not Open File</property>
+ <property name="wrap">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="access_subtitle">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">You do not have permission to open the
file.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="access_try_again">
+ <property name="label" translatable="yes">_Retry</property>
+ <property name="use-underline">True</property>
+ <signal name="clicked" handler="on_try_again_cb" swapped="true"/>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="access_try_admin">
+ <property name="label" translatable="yes">Open As _Administrator</property>
+ <property name="use-underline">True</property>
+ <property name="visible">false</property>
+ <signal name="clicked" handler="on_try_admin_cb" swapped="true"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkInfoBar" id="discard_infobar">
+ <property name="message-type">warning</property>
+ <property name="show-close-button">True</property>
+ <property name="revealed">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Document Restored</property>
+ <property name="wrap">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="subtitle">
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Unsaved document has been restored.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="discard">
+ <property name="label" translatable="yes">_Discard…</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ <child type="action">
+ <object class="GtkButton" id="save">
+ <property name="margin-start">6</property>
+ <property name="use-underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="discard"/>
+ <widget name="save"/>
+ </widgets>
+ </object>
+</interface>
diff --git a/src/libide/editor/ide-editor-page.c b/src/libide/editor/ide-editor-page.c
index 9747c9a39..8bf6ebb1e 100644
--- a/src/libide/editor/ide-editor-page.c
+++ b/src/libide/editor/ide-editor-page.c
@@ -27,6 +27,7 @@
#include <libide-code.h>
#include <libide-threading.h>
+#include "ide-editor-info-bar-private.h"
#include "ide-editor-page-addin.h"
#include "ide-editor-page-private.h"
#include "ide-editor-print-operation.h"
@@ -680,6 +681,7 @@ ide_editor_page_class_init (IdeEditorPageClass *klass)
panel_widget_class_install_action (panel_widget_class, "editor.print", NULL, print_action);
panel_widget_class_install_action (panel_widget_class, "editor.format", NULL, format_action);
+ g_type_ensure (IDE_TYPE_EDITOR_INFO_BAR);
g_type_ensure (IDE_TYPE_EDITOR_SEARCH_BAR);
}
diff --git a/src/libide/editor/ide-editor-page.ui b/src/libide/editor/ide-editor-page.ui
index 74efd2763..2870675ab 100644
--- a/src/libide/editor/ide-editor-page.ui
+++ b/src/libide/editor/ide-editor-page.ui
@@ -2,6 +2,13 @@
<interface>
<template class="IdeEditorPage" parent="IdePage">
<property name="can-maximize">true</property>
+ <child type="content">
+ <object class="IdeEditorInfoBar" id="info_bar">
+ <binding name="buffer">
+ <lookup name="buffer">IdeEditorPage</lookup>
+ </binding>
+ </object>
+ </child>
<child type="content">
<object class="IdeScrubberRevealer" id="scrubber_revealer">
<property name="hexpand">true</property>
diff --git a/src/libide/editor/libide-editor.gresource.xml b/src/libide/editor/libide-editor.gresource.xml
index d9d0869bd..d29b0f8f5 100644
--- a/src/libide/editor/libide-editor.gresource.xml
+++ b/src/libide/editor/libide-editor.gresource.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/libide-editor/">
+ <file preprocess="xml-stripblanks">ide-editor-info-bar.ui</file>
<file preprocess="xml-stripblanks">ide-editor-page.ui</file>
<file preprocess="xml-stripblanks">ide-editor-search-bar.ui</file>
<file preprocess="xml-stripblanks">ide-editor-workspace.ui</file>
diff --git a/src/libide/editor/meson.build b/src/libide/editor/meson.build
index d4487cfa6..c92f82269 100644
--- a/src/libide/editor/meson.build
+++ b/src/libide/editor/meson.build
@@ -20,6 +20,7 @@ libide_editor_public_headers = [
]
libide_editor_private_headers = [
+ 'ide-editor-info-bar-private.h',
'ide-editor-page-private.h',
'ide-editor-print-operation.h',
'ide-editor-save-delegate.h',
@@ -42,6 +43,7 @@ libide_editor_public_sources = [
libide_editor_private_sources = [
'ide-editor-init.c',
+ 'ide-editor-info-bar.c',
'ide-editor-page-settings.c',
'ide-editor-print-operation.c',
'ide-editor-save-delegate.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]