[gnome-builder/wip/chergert/layout] editor: add IdeEditorSidebar widget
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/layout] editor: add IdeEditorSidebar widget
- Date: Fri, 30 Jun 2017 06:18:26 +0000 (UTC)
commit 3f273f0a4b8a361423ef180ae50e2ce7c3192ccc
Author: Christian Hergert <chergert redhat com>
Date: Thu Jun 29 22:56:37 2017 -0700
editor: add IdeEditorSidebar widget
libide/editor/ide-editor-sidebar.c | 405 +++++++++++++++++++++++++++++++++++
libide/editor/ide-editor-sidebar.h | 41 ++++
libide/editor/ide-editor-sidebar.ui | 104 +++++++++
libide/ide.h | 1 +
libide/libide.gresource.xml | 1 +
libide/meson.build | 2 +
6 files changed, 554 insertions(+), 0 deletions(-)
---
diff --git a/libide/editor/ide-editor-sidebar.c b/libide/editor/ide-editor-sidebar.c
new file mode 100644
index 0000000..07000df
--- /dev/null
+++ b/libide/editor/ide-editor-sidebar.c
@@ -0,0 +1,405 @@
+/* ide-editor-sidebar.c
+ *
+ * Copyright (C) 2017 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-editor-sidebar"
+
+#include <dazzle.h>
+
+#include "editor/ide-editor-sidebar.h"
+#include "layout/ide-layout-private.h"
+#include "layout/ide-layout-stack.h"
+#include "layout/ide-layout-view.h"
+
+/**
+ * SECTION:ide-editor-sidebar
+ * @title: IdeEditorSidebar
+ * @short_description: The left sidebar for the editor
+ *
+ * The #IdeEditorSidebar is the widget displayed on the left of the
+ * #IdeEditorPerspective. It contains an open document list, and then the
+ * various sections that have been added to the sidebar.
+ *
+ * Use ide_editor_sidebar_add_section() to add a section to the sidebar.
+ */
+
+struct _IdeEditorSidebar
+{
+ GtkBox parent_instance;
+
+ /* Template widgets */
+ GtkStackSwitcher *stack_switcher;
+ GtkListBox *open_pages_list_box;
+ GtkBox *open_pages_section;
+ GtkLabel *section_title;
+ GtkImage *section_menu_button;
+ GtkStack *stack;
+};
+
+G_DEFINE_TYPE (IdeEditorSidebar, ide_editor_sidebar, GTK_TYPE_BOX)
+
+static void
+ide_editor_sidebar_update_title (IdeEditorSidebar *self)
+{
+ g_autofree gchar *title = NULL;
+ GtkWidget *visible_child;
+
+ g_assert (IDE_IS_EDITOR_SIDEBAR (self));
+
+ if (NULL != (visible_child = gtk_stack_get_visible_child (self->stack)))
+ gtk_container_child_get (GTK_CONTAINER (self->stack), visible_child,
+ "title", &title,
+ NULL);
+
+ gtk_label_set_label (self->section_title, title);
+}
+
+static void
+ide_editor_sidebar_stack_notify_visible_child (IdeEditorSidebar *self,
+ GParamSpec *pspec,
+ GtkStack *stack)
+{
+ g_assert (IDE_IS_EDITOR_SIDEBAR (self));
+ g_assert (G_IS_PARAM_SPEC_OBJECT (pspec));
+ g_assert (GTK_IS_STACK (stack));
+
+ ide_editor_sidebar_update_title (self);
+}
+
+static void
+ide_editor_sidebar_open_pages_row_activated (IdeEditorSidebar *self,
+ GtkListBoxRow *row,
+ GtkListBox *list_box)
+{
+ IdeLayoutView *view;
+ GtkWidget *stack;
+
+ g_assert (IDE_IS_EDITOR_SIDEBAR (self));
+ g_assert (GTK_IS_LIST_BOX_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ view = g_object_get_data (G_OBJECT (row), "IDE_LAYOUT_VIEW");
+ g_assert (IDE_IS_LAYOUT_VIEW (view));
+
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_LAYOUT_STACK);
+ g_assert (IDE_IS_LAYOUT_STACK (stack));
+
+ ide_layout_stack_set_visible_child (IDE_LAYOUT_STACK (stack), view);
+
+ gtk_widget_grab_focus (GTK_WIDGET (view));
+}
+
+static void
+ide_editor_sidebar_destroy (GtkWidget *widget)
+{
+ IdeEditorSidebar *self = (IdeEditorSidebar *)widget;
+
+ if (self->open_pages_list_box != NULL)
+ gtk_list_box_bind_model (self->open_pages_list_box, NULL, NULL, NULL, NULL);
+
+ GTK_WIDGET_CLASS (ide_editor_sidebar_parent_class)->destroy (widget);
+}
+
+static void
+ide_editor_sidebar_class_init (IdeEditorSidebarClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->destroy = ide_editor_sidebar_destroy;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-editor-sidebar.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, open_pages_list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, open_pages_section);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, section_menu_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, section_title);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, stack);
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorSidebar, stack_switcher);
+ gtk_widget_class_set_css_name (widget_class, "ideeditorsidebar");
+}
+
+static void
+ide_editor_sidebar_init (IdeEditorSidebar *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+
+ g_signal_connect_swapped (self->open_pages_list_box,
+ "row-activated",
+ G_CALLBACK (ide_editor_sidebar_open_pages_row_activated),
+ self);
+
+ g_signal_connect_swapped (self->stack,
+ "notify::visible-child",
+ G_CALLBACK (ide_editor_sidebar_stack_notify_visible_child),
+ self);
+}
+
+/**
+ * ide_editor_sidebar_new:
+ *
+ * Creates a new #IdeEditorSidebar instance.
+ *
+ * Returns: (transfer full): A new #IdeEditorSidebar
+ *
+ * Since: 3.26
+ */
+GtkWidget *
+ide_editor_sidebar_new (void)
+{
+ return g_object_new (IDE_TYPE_EDITOR_SIDEBAR, NULL);
+}
+
+static void
+fixup_stack_switcher_button (GtkWidget *widget,
+ gpointer user_data)
+{
+ g_assert (GTK_IS_RADIO_BUTTON (widget));
+
+ /*
+ * We need to set hexpand on each of the radiobuttons inside the stack
+ * switcher to match our designs.
+ */
+ gtk_widget_set_hexpand (widget, TRUE);
+}
+
+/**
+ * ide_editor_sidebar_add_section:
+ * @self: a #IdeEditorSidebar
+ * @id: (nullable): an optional id for the section
+ * @title: the title of the section
+ * @icon_name: the icon name for the section's icon
+ * @menu_id: (nullable): an optional menu-id to display
+ * @menu_icon_name: (nullable): an optional icon-name for displaying the menu
+ * @section: the widget to display in the sidebar
+ *
+ * Adds a new section to the #IdeEditorSidebar. @icon_name will be used to
+ * display an icon for the section. @title should contain the title to display
+ * above the section.
+ *
+ * If you want to be able to switch to the section manually, you should set @id
+ * so that ide_editor_sidebar_set_section_id() will allow you to use id.
+ *
+ * To remove your section, call gtk_widget_destroy() on @section.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_sidebar_add_section (IdeEditorSidebar *self,
+ const gchar *id,
+ const gchar *title,
+ const gchar *icon_name,
+ const gchar *menu_id,
+ const gchar *menu_icon_name,
+ GtkWidget *section)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SIDEBAR (self));
+ g_return_if_fail (title != NULL);
+ g_return_if_fail (icon_name != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (section));
+
+ g_object_set_data (G_OBJECT (section),
+ "IDE_EDITOR_SIDEBAR_MENU_ID",
+ (gpointer) g_intern_string (menu_id));
+
+ g_object_set_data (G_OBJECT (section),
+ "IDE_EDITOR_SIDEBAR_MENU_ICON_NAME",
+ (gpointer) g_intern_string (menu_icon_name));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->stack), section,
+ "name", id,
+ "title", title,
+ "icon-name", icon_name,
+ NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (self->stack_switcher),
+ fixup_stack_switcher_button,
+ NULL);
+
+ ide_editor_sidebar_update_title (self);
+}
+
+/**
+ * ide_editor_sidebar_get_section_id:
+ * @self: a #IdeEditorSidebar
+ *
+ * Gets the id of the current section.
+ *
+ * Returns: (nullable): The id of the current section if it registered one.
+ *
+ * Since: 3.26
+ */
+const gchar *
+ide_editor_sidebar_get_section_id (IdeEditorSidebar *self)
+{
+ g_return_val_if_fail (IDE_IS_EDITOR_SIDEBAR (self), NULL);
+
+ return gtk_stack_get_visible_child_name (self->stack);
+}
+
+/**
+ * ide_editor_sidebar_set_section_id:
+ * @self: a #IdeEditorSidebar
+ * @section_id: a section id to switch to
+ *
+ * Changes the current section to @section_id.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_sidebar_set_section_id (IdeEditorSidebar *self,
+ const gchar *section_id)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SIDEBAR (self));
+ g_return_if_fail (section_id != NULL);
+
+ gtk_stack_set_visible_child_name (self->stack, section_id);
+}
+
+static void
+ide_editor_sidebar_close_view (GtkButton *button,
+ IdeLayoutView *view)
+{
+ GtkWidget *stack;
+
+ g_assert (GTK_IS_BUTTON (button));
+ g_assert (IDE_IS_LAYOUT_VIEW (view));
+
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_LAYOUT_STACK);
+
+ if (stack != NULL)
+ _ide_layout_stack_request_close (IDE_LAYOUT_STACK (stack), view);
+}
+
+static GtkWidget *
+create_open_page_row (gpointer item,
+ gpointer user_data)
+{
+ IdeLayoutView *view = item;
+ GtkListBoxRow *row;
+ GtkButton *button;
+ GtkImage *image;
+ GtkLabel *label;
+ GtkBox *box;
+
+ g_assert (IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_EDITOR_SIDEBAR (user_data));
+
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "visible", TRUE,
+ NULL);
+ g_object_set_data (G_OBJECT (row), "IDE_LAYOUT_VIEW", view);
+
+ box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (box));
+
+ image = g_object_new (GTK_TYPE_IMAGE,
+ "icon-size", GTK_ICON_SIZE_MENU,
+ "hexpand", FALSE,
+ "visible", TRUE,
+ NULL);
+ g_object_bind_property (view, "icon-name", image, "icon-name",
+ G_BINDING_SYNC_CREATE);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (image));
+
+ label = g_object_new (DZL_TYPE_BOLDING_LABEL,
+ "ellipsize", PANGO_ELLIPSIZE_START,
+ "visible", TRUE,
+ "hexpand", TRUE,
+ "xalign", 0.0f,
+ NULL);
+ g_object_bind_property (view, "title", label, "label", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (view, "modified", label, "bold", G_BINDING_SYNC_CREATE);
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (label));
+
+ button = g_object_new (GTK_TYPE_BUTTON,
+ "visible", TRUE,
+ "hexpand", FALSE,
+ NULL);
+ g_signal_connect_object (button,
+ "clicked",
+ G_CALLBACK (ide_editor_sidebar_close_view),
+ view, 0);
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (button), "flat");
+ gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (button));
+
+ image = g_object_new (GTK_TYPE_IMAGE,
+ "icon-size", GTK_ICON_SIZE_MENU,
+ "icon-name", "window-close-symbolic",
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (image));
+
+ return GTK_WIDGET (row);
+}
+
+static void
+ide_editor_sidebar_open_pages_items_changed (IdeEditorSidebar *self,
+ guint position,
+ guint added,
+ guint removed,
+ GListModel *model)
+{
+ g_assert (IDE_IS_EDITOR_SIDEBAR (self));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ /*
+ * Sets the visibility of our page list widgets only when the listmodel has
+ * views within it. We try to be careful about being safe when the widget is
+ * in destruction and an items-changed signal arrives.
+ */
+
+ if (self->open_pages_section != NULL)
+ {
+ gboolean has_items = g_list_model_get_n_items (model) > 0;
+ gtk_widget_set_visible (GTK_WIDGET (self->open_pages_section), has_items);
+ }
+}
+
+/**
+ * _ide_editor_sidebar_set_open_pages:
+ * @self: a #IdeEditorSidebar
+ * @open_pages: A #GListModel describing the open pages
+ *
+ * This private function is used to set the GListModel to use for the list
+ * of open pages in the sidebar. It should contain a list of IdeLayoutView
+ * which we will use to keep the rows up to date.
+ */
+void
+_ide_editor_sidebar_set_open_pages (IdeEditorSidebar *self,
+ GListModel *open_pages)
+{
+ g_return_if_fail (IDE_IS_EDITOR_SIDEBAR (self));
+ g_return_if_fail (!open_pages || G_IS_LIST_MODEL (open_pages));
+ g_return_if_fail (!open_pages ||
+ g_list_model_get_item_type (open_pages) == IDE_TYPE_LAYOUT_VIEW);
+
+ if (open_pages != NULL)
+ g_signal_connect_object (open_pages,
+ "items-changed",
+ G_CALLBACK (ide_editor_sidebar_open_pages_items_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_list_box_bind_model (self->open_pages_list_box,
+ open_pages,
+ create_open_page_row,
+ self, NULL);
+}
diff --git a/libide/editor/ide-editor-sidebar.h b/libide/editor/ide-editor-sidebar.h
new file mode 100644
index 0000000..bf3a88c
--- /dev/null
+++ b/libide/editor/ide-editor-sidebar.h
@@ -0,0 +1,41 @@
+/* ide-editor-sidebar.h
+ *
+ * Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SIDEBAR (ide_editor_sidebar_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorSidebar, ide_editor_sidebar, IDE, EDITOR_SIDEBAR, GtkBox)
+
+GtkWidget *ide_editor_sidebar_new (void);
+const gchar *ide_editor_sidebar_get_section_id (IdeEditorSidebar *self);
+void ide_editor_sidebar_set_section_id (IdeEditorSidebar *self,
+ const gchar *section_id);
+void ide_editor_sidebar_add_section (IdeEditorSidebar *self,
+ const gchar *id,
+ const gchar *title,
+ const gchar *icon_name,
+ const gchar *menu_id,
+ const gchar *menu_icon_name,
+ GtkWidget *section);
+
+G_END_DECLS
diff --git a/libide/editor/ide-editor-sidebar.ui b/libide/editor/ide-editor-sidebar.ui
new file mode 100644
index 0000000..a64f0de
--- /dev/null
+++ b/libide/editor/ide-editor-sidebar.ui
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdeEditorSidebar" parent="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkStackSwitcher" id="stack_switcher">
+ <property name="halign">fill</property>
+ <property name="hexpand">true</property>
+ <property name="stack">stack</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="DzlMultiPaned">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox" id="open_pages_section">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Open Pages</property>
+ <property name="xalign">0.0</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ <class name="title"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="DzlElasticBin">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">never</property>
+ <property name="propagate-natural-height">true</property>
+ <property name="min-content-height">30</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkListBox" id="open_pages_list_box">
+ <property name="selection-mode">none</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="open-pages"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="vexpand">true</property>
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="section_title">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ <style>
+ <class name="dim-label"/>
+ <class name="title"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="section_menu_button">
+ <style>
+ <class name="image-button"/>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="section_image">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="interpolate-size">false</property>
+ <property name="homogeneous">false</property>
+ <property name="vexpand">true</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/libide/ide.h b/libide/ide.h
index bc13274..35f38fe 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -73,6 +73,7 @@ G_BEGIN_DECLS
#include "doap/ide-doap-person.h"
#include "doap/ide-doap.h"
#include "editor/ide-editor-perspective.h"
+#include "editor/ide-editor-sidebar.h"
#include "editor/ide-editor-view-addin.h"
#include "editor/ide-editor-view.h"
#include "files/ide-file-settings.h"
diff --git a/libide/libide.gresource.xml b/libide/libide.gresource.xml
index c47f8ef..5e17350 100644
--- a/libide/libide.gresource.xml
+++ b/libide/libide.gresource.xml
@@ -57,6 +57,7 @@
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-perspective.ui">editor/ide-editor-perspective.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-layout-stack-controls.ui">editor/ide-editor-layout-stack-controls.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-search-bar.ui">editor/ide-editor-search-bar.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-sidebar.ui">editor/ide-editor-sidebar.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-spell-widget.ui">spellcheck/ide-editor-spell-widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-editor-view.ui">editor/ide-editor-view.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="ide-greeter-perspective.ui">greeter/ide-greeter-perspective.ui</file>
diff --git a/libide/meson.build b/libide/meson.build
index 099bf97..8ec2994 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -99,6 +99,7 @@ libide_public_headers = [
'doap/ide-doap-person.h',
'doap/ide-doap.h',
'editor/ide-editor-perspective.h',
+ 'editor/ide-editor-sidebar.h',
'editor/ide-editor-view-addin.h',
'editor/ide-editor-view.h',
'files/ide-file-settings.h',
@@ -291,6 +292,7 @@ libide_public_sources = [
'doap/ide-doap-person.c',
'doap/ide-doap.c',
'editor/ide-editor-perspective.c',
+ 'editor/ide-editor-sidebar.c',
'editor/ide-editor-view-addin.c',
'editor/ide-editor-view.c',
'files/ide-file-settings.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]