[gnome-builder/wip/chergert/perspective] libide: add IdeLayout
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/perspective] libide: add IdeLayout
- Date: Wed, 11 Nov 2015 08:29:50 +0000 (UTC)
commit 2ee8ff94ba981d62ca9715eeb7f13b1923651e6f
Author: Christian Hergert <chergert redhat com>
Date: Wed Nov 11 00:29:28 2015 -0800
libide: add IdeLayout
This is a rename from GbWorkspace. There is certainly more I'd like to
add to this (such as pane splits), but this will keep us moving on the
refactor so we can merge things sooner.
data/ui/ide-layout-pane.ui | 42 ++
data/ui/ide-layout.ui | 30 +
libide/Makefile.am | 6 +-
libide/ide-layout-manager.c | 72 --
libide/ide-layout-manager.h | 60 --
libide/ide-layout-pane.c | 378 ++++++++++
libide/ide-layout-pane.h | 43 ++
libide/ide-layout.c | 1218 +++++++++++++++++++++++++++++++++
libide/ide-layout.h | 45 ++
libide/ide.h | 3 +-
libide/resources/libide.gresource.xml | 2 +
11 files changed, 1764 insertions(+), 135 deletions(-)
---
diff --git a/data/ui/ide-layout-pane.ui b/data/ui/ide-layout-pane.ui
new file mode 100644
index 0000000..52a6f4f
--- /dev/null
+++ b/data/ui/ide-layout-pane.ui
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.16 -->
+ <template class="IdeLayoutPane" parent="GtkBin">
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">true</property>
+ <property name="vexpand">false</property>
+ <style>
+ <class name="header"/>
+ <class name="notebook"/>
+ </style>
+ <child type="center">
+ <object class="GtkStackSwitcher" id="stack_switcher">
+ <property name="margin-top">3</property>
+ <property name="margin-bottom">3</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="stack">stack</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="expand">true</property>
+ <property name="homogeneous">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/ide-layout.ui b/data/ui/ide-layout.ui
new file mode 100644
index 0000000..09f2813
--- /dev/null
+++ b/data/ui/ide-layout.ui
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.8 -->
+ <template class="IdeLayout" parent="GtkOverlay">
+ <child type="overlay">
+ <object class="GbWorkspacePane" id="left_pane">
+ <property name="position">left</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GbWorkspacePane" id="right_pane">
+ <property name="position">right</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GbWorkspacePane" id="bottom_pane">
+ <property name="position">bottom</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GbWorkspacePane" id="content_pane">
+ <property name="position">top</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 19fb883..ee834c3 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -92,8 +92,10 @@ libide_1_0_la_public_sources = \
ide-indent-style.h \
ide-indenter.c \
ide-indenter.h \
- ide-layout-manager.c \
- ide-layout-manager.h \
+ ide-layout.c \
+ ide-layout.h \
+ ide-layout-pane.c \
+ ide-layout-pane.h \
ide-log.c \
ide-log.h \
ide-macros.h \
diff --git a/libide/ide-layout-pane.c b/libide/ide-layout-pane.c
new file mode 100644
index 0000000..2ab0fcd
--- /dev/null
+++ b/libide/ide-layout-pane.c
@@ -0,0 +1,378 @@
+/* ide-layout-pane.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "egg-signal-group.h"
+
+#include "ide-layout-pane.h"
+
+struct _IdeLayoutPane
+{
+ GtkBin parent_instance;
+
+ GtkBox *box;
+ GtkStackSwitcher *stack_switcher;
+ GtkStack *stack;
+
+ EggSignalGroup *toplevel_signals;
+
+ GdkRectangle handle_pos;
+
+ GtkPositionType position;
+};
+
+G_DEFINE_TYPE (IdeLayoutPane, ide_layout_pane, GTK_TYPE_BIN)
+
+enum {
+ PROP_0,
+ PROP_POSITION,
+ LAST_PROP
+};
+
+enum {
+ STYLE_PROP_0,
+ STYLE_PROP_HANDLE_SIZE,
+ LAST_STYLE_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+static GParamSpec *styleParamSpecs [LAST_STYLE_PROP];
+
+static gboolean
+ide_layout_pane_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ IdeLayoutPane *self = (IdeLayoutPane *)widget;
+ GtkStyleContext *style_context;
+ gboolean ret;
+
+ g_assert (IDE_IS_LAYOUT_PANE (self));
+ g_assert (cr != NULL);
+
+ ret = GTK_WIDGET_CLASS (ide_layout_pane_parent_class)->draw (widget, cr);
+
+ style_context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_save (style_context);
+ gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_PANE_SEPARATOR);
+ gtk_render_handle (style_context, cr,
+ self->handle_pos.x,
+ self->handle_pos.y,
+ self->handle_pos.width,
+ self->handle_pos.height);
+ gtk_style_context_restore (style_context);
+
+ return ret;
+}
+
+static void
+ide_layout_pane_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
+{
+ IdeLayoutPane *self = (IdeLayoutPane *)widget;
+ GtkWidget *child;
+ GtkAllocation child_alloc;
+ gint handle_size;
+
+ g_assert (IDE_IS_LAYOUT_PANE (self));
+
+ gtk_widget_set_allocation (widget, alloc);
+
+ child = gtk_bin_get_child (GTK_BIN (self));
+ if (child == NULL || !gtk_widget_get_visible (child))
+ return;
+
+ gtk_widget_style_get (widget, "handle-size", &handle_size, NULL);
+
+ child_alloc = *alloc;
+
+ switch (self->position)
+ {
+ case GTK_POS_LEFT:
+ child_alloc.width -= handle_size;
+ self->handle_pos.x = child_alloc.x + child_alloc.width;
+ self->handle_pos.width = handle_size;
+ self->handle_pos.height = child_alloc.height;
+ self->handle_pos.y = child_alloc.y;
+ break;
+
+ case GTK_POS_RIGHT:
+ child_alloc.x += handle_size;
+ child_alloc.width -= handle_size;
+ self->handle_pos.x = alloc->x;
+ self->handle_pos.width = handle_size;
+ self->handle_pos.height = child_alloc.height;
+ self->handle_pos.y = child_alloc.y;
+ break;
+
+ case GTK_POS_BOTTOM:
+ child_alloc.y += handle_size;
+ child_alloc.height -= handle_size;
+ self->handle_pos.x = alloc->x;
+ self->handle_pos.width = alloc->width;
+ self->handle_pos.height = handle_size;
+ self->handle_pos.y = alloc->y;
+ break;
+
+ case GTK_POS_TOP:
+ self->handle_pos.x = 0;
+ self->handle_pos.y = 0;
+ self->handle_pos.width = 0;
+ self->handle_pos.height = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ gtk_widget_size_allocate (child, &child_alloc);
+}
+
+static void
+ide_layout_pane_grab_focus (GtkWidget *widget)
+{
+ IdeLayoutPane *self= (IdeLayoutPane *)widget;
+ GtkWidget *child;
+
+ child = gtk_stack_get_visible_child (self->stack);
+ if (child != NULL)
+ gtk_widget_grab_focus (child);
+}
+
+static void
+workbench_focus_changed (GtkWidget *toplevel,
+ GtkWidget *focus,
+ IdeLayoutPane *self)
+{
+ GtkStyleContext *style_context;
+ GtkWidget *parent;
+
+ g_assert (GTK_IS_WIDGET (toplevel));
+ g_assert (!focus || GTK_IS_WIDGET (focus));
+ g_assert (IDE_IS_LAYOUT_PANE (self));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ parent = focus;
+
+ while (parent && (parent != (GtkWidget *)self))
+ {
+ if (GTK_IS_POPOVER (parent))
+ parent = gtk_popover_get_relative_to (GTK_POPOVER (parent));
+ else
+ parent = gtk_widget_get_parent (parent);
+ }
+
+ if (parent == NULL)
+ gtk_style_context_remove_class (style_context, "focused");
+ else
+ gtk_style_context_add_class (style_context, "focused");
+}
+
+static void
+ide_layout_pane_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *old_parent)
+{
+ IdeLayoutPane *self = (IdeLayoutPane *)widget;
+ GtkWidget *toplevel;
+
+ g_assert (IDE_IS_LAYOUT_PANE (self));
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ if (!GTK_IS_WINDOW (toplevel))
+ toplevel = NULL;
+
+ egg_signal_group_set_target (self->toplevel_signals, toplevel);
+}
+
+static void
+ide_layout_pane_dispose (GObject *object)
+{
+ IdeLayoutPane *self = (IdeLayoutPane *)object;
+
+ g_clear_object (&self->toplevel_signals);
+
+ G_OBJECT_CLASS (ide_layout_pane_parent_class)->dispose (object);
+}
+
+static void
+ide_layout_pane_finalize (GObject *object)
+{
+ IdeLayoutPane *self = (IdeLayoutPane *)object;
+
+ self->stack = NULL;
+ self->stack_switcher = NULL;
+
+ G_OBJECT_CLASS (ide_layout_pane_parent_class)->finalize (object);
+}
+
+static void
+ide_layout_pane_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeLayoutPane *self = IDE_LAYOUT_PANE (object);
+
+ switch (prop_id)
+ {
+ case PROP_POSITION:
+ g_value_set_enum (value, ide_layout_pane_get_position (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_layout_pane_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeLayoutPane *self = IDE_LAYOUT_PANE (object);
+
+ switch (prop_id)
+ {
+ case PROP_POSITION:
+ ide_layout_pane_set_position (self, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_layout_pane_class_init (IdeLayoutPaneClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = ide_layout_pane_dispose;
+ object_class->finalize = ide_layout_pane_finalize;
+ object_class->get_property = ide_layout_pane_get_property;
+ object_class->set_property = ide_layout_pane_set_property;
+
+ widget_class->draw = ide_layout_pane_draw;
+ widget_class->grab_focus = ide_layout_pane_grab_focus;
+ widget_class->hierarchy_changed = ide_layout_pane_hierarchy_changed;
+ widget_class->size_allocate = ide_layout_pane_size_allocate;
+
+ /**
+ * IdeLayoutPane:position:
+ *
+ * The position at which to place the pane. This also dictates which
+ * direction that animations will occur.
+ *
+ * For example, setting to %GTK_POS_LEFT will result in the resize grip
+ * being placed on the right, and animations to and from the leftmost
+ * of the allocation.
+ */
+ properties [PROP_POSITION] =
+ g_param_spec_enum ("position",
+ "Position",
+ "The position of the pane.",
+ GTK_TYPE_POSITION_TYPE,
+ GTK_POS_LEFT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ styleParamSpecs [STYLE_PROP_HANDLE_SIZE] =
+ g_param_spec_int ("handle-size",
+ "Handle Size",
+ "Width of handle.",
+ 0, G_MAXINT,
+ 1,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ gtk_widget_class_install_style_property (widget_class,
+ styleParamSpecs [STYLE_PROP_HANDLE_SIZE]);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-layout-pane.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeLayoutPane, box);
+ gtk_widget_class_bind_template_child_internal (widget_class, IdeLayoutPane, stack);
+ gtk_widget_class_bind_template_child_internal (widget_class, IdeLayoutPane, stack_switcher);
+}
+
+static void
+ide_layout_pane_init (IdeLayoutPane *self)
+{
+ self->toplevel_signals = egg_signal_group_new (GTK_TYPE_WINDOW);
+ egg_signal_group_connect_object (self->toplevel_signals,
+ "set-focus",
+ G_CALLBACK (workbench_focus_changed),
+ self,
+ G_CONNECT_AFTER);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+ide_layout_pane_new (void)
+{
+ return g_object_new (IDE_TYPE_LAYOUT_PANE, NULL);
+}
+
+GtkPositionType
+ide_layout_pane_get_position (IdeLayoutPane *self)
+{
+ g_return_val_if_fail (IDE_IS_LAYOUT_PANE (self), GTK_POS_LEFT);
+
+ return self->position;
+}
+
+void
+ide_layout_pane_set_position (IdeLayoutPane *self,
+ GtkPositionType position)
+{
+ g_return_if_fail (IDE_IS_LAYOUT_PANE (self));
+ g_return_if_fail (position >= GTK_POS_LEFT);
+ g_return_if_fail (position <= GTK_POS_BOTTOM);
+
+ if (position != self->position)
+ {
+ self->position = position;
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_POSITION]);
+ }
+}
+
+void
+ide_layout_pane_add_page (IdeLayoutPane *self,
+ GtkWidget *page,
+ const gchar *title,
+ const gchar *icon_name)
+{
+ gtk_container_add_with_properties (GTK_CONTAINER (self->stack), page,
+ "icon-name", icon_name,
+ "title", title,
+ NULL);
+}
+
+void
+ide_layout_pane_remove_page (IdeLayoutPane *self,
+ GtkWidget *page)
+{
+ g_return_if_fail (IDE_IS_LAYOUT_PANE (self));
+ g_return_if_fail (GTK_IS_WIDGET (page));
+
+ gtk_container_remove (GTK_CONTAINER (self->stack), page);
+}
diff --git a/libide/ide-layout-pane.h b/libide/ide-layout-pane.h
new file mode 100644
index 0000000..ab944b9
--- /dev/null
+++ b/libide/ide-layout-pane.h
@@ -0,0 +1,43 @@
+/* ide-layout-pane.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#ifndef IDE_LAYOUT_PANE_H
+#define IDE_LAYOUT_PANE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LAYOUT_PANE (ide_layout_pane_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeLayoutPane, ide_layout_pane, IDE, LAYOUT_PANE, GtkBin)
+
+GtkWidget *ide_layout_pane_new (void);
+GtkPositionType ide_layout_pane_get_position (IdeLayoutPane *self);
+void ide_layout_pane_set_position (IdeLayoutPane *self,
+ GtkPositionType position);
+void ide_layout_pane_add_page (IdeLayoutPane *self,
+ GtkWidget *page,
+ const gchar *title,
+ const gchar *icon_name);
+void ide_layout_pane_remove_page (IdeLayoutPane *self,
+ GtkWidget *page);
+
+G_END_DECLS
+
+#endif /* IDE_LAYOUT_PANE_H */
diff --git a/libide/ide-layout.c b/libide/ide-layout.c
new file mode 100644
index 0000000..40e3230
--- /dev/null
+++ b/libide/ide-layout.c
@@ -0,0 +1,1218 @@
+/* ide-layout.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#include <egg-animation.h>
+#include <glib/gi18n.h>
+#include <ide.h>
+#include <string.h>
+
+#include "ide-layout.h"
+#include "ide-layout-pane.h"
+
+#define ANIMATION_MODE EGG_ANIMATION_EASE_IN_OUT_QUAD
+#define ANIMATION_DURATION 250
+#define HORIZ_GRIP_EXTRA 5
+#define VERT_GRIP_EXTRA 5
+#define MIN_POSITION 100
+
+typedef struct
+{
+ GtkWidget *widget;
+ GtkAdjustment *adjustment;
+ EggAnimation *animation;
+ GdkWindow *handle;
+ GtkAllocation handle_pos;
+ GtkAllocation alloc;
+ gint min_width;
+ gint min_height;
+ gint nat_width;
+ gint nat_height;
+ gint position;
+ gint restore_position;
+ GdkCursorType cursor_type;
+ GtkPositionType type : 4;
+ guint reveal : 1;
+ guint hiding : 1;
+ guint showing : 1;
+} IdeLayoutChild;
+
+typedef struct
+{
+ IdeLayoutChild children[4];
+ GtkGesture *pan_gesture;
+ IdeLayoutChild *drag_child;
+ gdouble drag_position;
+} IdeLayoutPrivate;
+
+static void buildable_init_iface (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeLayout, ide_layout, GTK_TYPE_OVERLAY,
+ G_ADD_PRIVATE (IdeLayout)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_init_iface))
+
+enum {
+ PROP_0,
+ PROP_BOTTOM_PANE,
+ PROP_CONTENT_PANE,
+ PROP_LEFT_PANE,
+ PROP_RIGHT_PANE,
+ LAST_PROP
+};
+
+enum {
+ CHILD_PROP_0,
+ CHILD_PROP_REVEAL,
+ CHILD_PROP_POSITION,
+ LAST_CHILD_PROP
+};
+
+static GtkBuildableIface *ide_layout_parent_buildable_iface;
+static GParamSpec *properties [LAST_PROP];
+static GParamSpec *child_properties [LAST_CHILD_PROP];
+
+static void
+ide_layout_move_resize_handle (IdeLayout *self,
+ GtkPositionType type)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ IdeLayoutChild *child;
+ GtkAllocation alloc;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert ((type == GTK_POS_LEFT) ||
+ (type == GTK_POS_RIGHT) ||
+ (type == GTK_POS_BOTTOM));
+
+ child = &priv->children [type];
+
+ if (child->handle == NULL)
+ return;
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ switch (type)
+ {
+ case GTK_POS_LEFT:
+ child->handle_pos.x = alloc.x + child->alloc.x + child->alloc.width - HORIZ_GRIP_EXTRA;
+ child->handle_pos.y = alloc.y + child->alloc.y;
+ child->handle_pos.width = 2 * HORIZ_GRIP_EXTRA;
+ child->handle_pos.height = child->alloc.height;
+ break;
+
+ case GTK_POS_RIGHT:
+ child->handle_pos.x = alloc.x + child->alloc.x - HORIZ_GRIP_EXTRA;
+ child->handle_pos.y = alloc.y + child->alloc.y;
+ child->handle_pos.width = 2 * HORIZ_GRIP_EXTRA;
+ child->handle_pos.height = child->alloc.height;
+ break;
+
+ case GTK_POS_BOTTOM:
+ child->handle_pos.x = alloc.x + child->alloc.x;
+ child->handle_pos.y = alloc.y + child->alloc.y - VERT_GRIP_EXTRA;
+ child->handle_pos.width = child->alloc.width;
+ child->handle_pos.height = 2 * VERT_GRIP_EXTRA;
+ break;
+
+ case GTK_POS_TOP:
+ default:
+ break;
+ }
+
+ if (!gtk_widget_get_child_visible (child->widget))
+ memset (&child->handle_pos, 0, sizeof child->handle_pos);
+
+ if (gtk_widget_get_mapped (GTK_WIDGET (self)))
+ gdk_window_move_resize (child->handle,
+ child->handle_pos.x,
+ child->handle_pos.y,
+ child->handle_pos.width,
+ child->handle_pos.height);
+}
+
+static void
+ide_layout_create_handle_window (IdeLayout *self,
+ GtkPositionType type)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ IdeLayoutChild *child;
+ GtkAllocation alloc;
+ GdkWindowAttr attributes = { 0 };
+ GdkWindow *parent;
+ GdkDisplay *display;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert ((type == GTK_POS_LEFT) ||
+ (type == GTK_POS_RIGHT) ||
+ (type == GTK_POS_BOTTOM));
+
+ display = gtk_widget_get_display (GTK_WIDGET (self));
+ parent = gtk_widget_get_window (GTK_WIDGET (self));
+
+ g_assert (GDK_IS_DISPLAY (display));
+ g_assert (GDK_IS_WINDOW (parent));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ child = &priv->children [type];
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.x = child->handle_pos.x;
+ attributes.y = child->handle_pos.y;
+ attributes.width = child->handle_pos.width;
+ attributes.height = child->handle_pos.height;
+ attributes.visual = gtk_widget_get_visual (GTK_WIDGET (self));
+ attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (self));
+ attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_POINTER_MOTION_MASK);
+ attributes.cursor = gdk_cursor_new_for_display (display, child->cursor_type);
+
+ child->handle = gdk_window_new (parent, &attributes, (GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y));
+ gtk_widget_register_window (GTK_WIDGET (self), child->handle);
+
+ g_clear_object (&attributes.cursor);
+}
+
+static void
+ide_layout_destroy_handle_window (IdeLayout *self,
+ GtkPositionType type)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ IdeLayoutChild *child;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ child = &priv->children [type];
+
+ if (child->handle)
+ {
+ gdk_window_hide (child->handle);
+ gtk_widget_unregister_window (GTK_WIDGET (self), child->handle);
+ gdk_window_destroy (child->handle);
+ child->handle = NULL;
+ }
+}
+
+static void
+ide_layout_relayout (IdeLayout *self,
+ const GtkAllocation *alloc)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ IdeLayoutChild *left;
+ IdeLayoutChild *right;
+ IdeLayoutChild *content;
+ IdeLayoutChild *bottom;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (alloc != NULL);
+
+ left = &priv->children [GTK_POS_LEFT];
+ right = &priv->children [GTK_POS_RIGHT];
+ content = &priv->children [GTK_POS_TOP];
+ bottom = &priv->children [GTK_POS_BOTTOM];
+
+ /*
+ * Determine everything as if we are animating in/out or the child is visible.
+ */
+
+ if (left->reveal)
+ {
+ left->alloc.x = 0;
+ left->alloc.y = 0;
+ left->alloc.width = left->position;
+ left->alloc.height = alloc->height;
+
+ left->alloc.x -= gtk_adjustment_get_value (left->adjustment) * left->position;
+ }
+ else
+ {
+ left->alloc.x = -left->position;
+ left->alloc.y = 0;
+ left->alloc.width = left->position;
+ left->alloc.height = alloc->height;
+ }
+
+ if (right->reveal)
+ {
+ right->alloc.x = alloc->width - right->position;
+ right->alloc.y = 0;
+ right->alloc.width = right->position;
+ right->alloc.height = alloc->height;
+
+ right->alloc.x += gtk_adjustment_get_value (right->adjustment) * right->position;
+ }
+ else
+ {
+ right->alloc.x = alloc->width;
+ right->alloc.y = 0;
+ right->alloc.width = right->position;
+ right->alloc.height = alloc->height;
+ }
+
+ if (bottom->reveal)
+ {
+ bottom->alloc.x = left->alloc.x + left->alloc.width;
+ bottom->alloc.y = alloc->height - bottom->position;
+ bottom->alloc.width = right->alloc.x - bottom->alloc.x;
+ bottom->alloc.height = bottom->position;
+
+ bottom->alloc.y += gtk_adjustment_get_value (bottom->adjustment) * bottom->position;
+ }
+ else
+ {
+ bottom->alloc.x = left->alloc.x + left->alloc.width;
+ bottom->alloc.y = alloc->height;
+ bottom->alloc.width = right->alloc.x - bottom->alloc.x;
+ bottom->alloc.height = bottom->position;
+ }
+
+ if (content->reveal)
+ {
+ content->alloc.x = left->alloc.x + left->alloc.width;
+ content->alloc.y = 0;
+ content->alloc.width = right->alloc.x - content->alloc.x;
+ content->alloc.height = bottom->alloc.y;
+
+ content->alloc.y -= gtk_adjustment_get_value (content->adjustment) * content->alloc.height;
+ }
+ else
+ {
+ content->alloc.x = left->alloc.x + left->alloc.width;
+ content->alloc.y = -bottom->alloc.y;
+ content->alloc.width = right->alloc.x - content->alloc.x;
+ content->alloc.height = bottom->alloc.y;
+ }
+
+ /*
+ * Now adjust for child visibility.
+ *
+ * We need to ensure we don't give the non-visible children an allocation
+ * as it will interfere with hit targets.
+ */
+ if (!gtk_widget_get_child_visible (content->widget))
+ memset (&content->alloc, 0, sizeof content->alloc);
+ if (!gtk_widget_get_child_visible (left->widget))
+ memset (&left->alloc, 0, sizeof left->alloc);
+ if (!gtk_widget_get_child_visible (right->widget))
+ memset (&right->alloc, 0, sizeof right->alloc);
+ if (!gtk_widget_get_child_visible (bottom->widget))
+ memset (&bottom->alloc, 0, sizeof bottom->alloc);
+
+ ide_layout_move_resize_handle (self, GTK_POS_LEFT);
+ ide_layout_move_resize_handle (self, GTK_POS_RIGHT);
+ ide_layout_move_resize_handle (self, GTK_POS_BOTTOM);
+}
+
+static void
+ide_layout_size_allocate (GtkWidget *widget,
+ GtkAllocation *alloc)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gint i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (alloc != NULL);
+
+ ide_layout_relayout (self, alloc);
+
+ GTK_WIDGET_CLASS (ide_layout_parent_class)->size_allocate (widget, alloc);
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ if ((child->handle != NULL) &&
+ gtk_widget_get_visible (child->widget) &&
+ gtk_widget_get_child_visible (child->widget))
+ gdk_window_raise (child->handle);
+ }
+}
+
+static IdeLayoutChild *
+ide_layout_child_find (IdeLayout *self,
+ GtkWidget *child)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gint i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *item = &priv->children [i];
+
+ if (item->widget == child)
+ return item;
+ }
+
+ g_warning ("Child of type %s was not found in this IdeLayout.",
+ g_type_name (G_OBJECT_TYPE (child)));
+
+ return NULL;
+}
+
+static void
+ide_layout_animation_cb (gpointer data)
+{
+ g_autoptr(GtkWidget) child = data;
+ GtkWidget *parent;
+ IdeLayout *self;
+ IdeLayoutChild *item;
+
+ g_assert (GTK_IS_WIDGET (child));
+
+ parent = gtk_widget_get_parent (child);
+ if (!IDE_IS_LAYOUT (parent))
+ return;
+
+ self = IDE_LAYOUT (parent);
+
+ item = ide_layout_child_find (self, child);
+ if (item == NULL)
+ return;
+
+ if (item->hiding)
+ {
+ gtk_widget_set_child_visible (item->widget, FALSE);
+ if (item->restore_position > item->position)
+ item->position = item->restore_position;
+ }
+
+ item->showing = FALSE;
+ item->hiding = FALSE;
+ item->reveal = gtk_adjustment_get_value (item->adjustment) == 0.0;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+
+ gtk_container_child_notify (GTK_CONTAINER (self), child, "reveal");
+}
+
+static gboolean
+ide_layout_get_child_position (GtkOverlay *overlay,
+ GtkWidget *child,
+ GtkAllocation *alloc)
+{
+ IdeLayout *self = (IdeLayout *)overlay;
+ IdeLayoutChild *item;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+ g_assert (alloc != NULL);
+
+ if (!(item = ide_layout_child_find (self, child)))
+ return FALSE;
+
+ *alloc = item->alloc;
+
+ return TRUE;
+}
+
+static guint
+ide_layout_child_get_position (IdeLayout *self,
+ GtkWidget *child)
+{
+ IdeLayoutChild *item;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ if (!(item = ide_layout_child_find (self, child)))
+ return FALSE;
+
+ return item->position;
+}
+
+static void
+ide_layout_child_set_position (IdeLayout *self,
+ GtkWidget *child,
+ guint position)
+{
+ IdeLayoutChild *item;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ if (!(item = ide_layout_child_find (self, child)))
+ return;
+
+ item->position = position;
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+
+ gtk_container_child_notify (GTK_CONTAINER (self), child, "position");
+}
+
+static gboolean
+ide_layout_child_get_reveal (IdeLayout *self,
+ GtkWidget *child)
+{
+ IdeLayoutChild *item;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ if (!(item = ide_layout_child_find (self, child)))
+ return FALSE;
+
+ return item->reveal;
+}
+
+static void
+ide_layout_child_set_reveal (IdeLayout *self,
+ GtkWidget *child,
+ gboolean reveal)
+{
+ IdeLayoutChild *item;
+ GdkFrameClock *frame_clock;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_WIDGET (child));
+
+ reveal = !!reveal;
+
+ if (!(item = ide_layout_child_find (self, child)) || (item->reveal == reveal))
+ return;
+
+ if (item->animation != NULL)
+ {
+ egg_animation_stop (item->animation);
+ ide_clear_weak_pointer (&item->animation);
+ }
+
+ item->reveal = TRUE;
+ item->showing = reveal;
+ item->hiding = !reveal;
+
+ if (item->position > MIN_POSITION)
+ {
+ item->restore_position = item->position;
+ gtk_container_child_notify (GTK_CONTAINER (self), item->widget, "position");
+ }
+
+ gtk_widget_set_child_visible (child, TRUE);
+
+ frame_clock = gtk_widget_get_frame_clock (child);
+
+ if (gtk_widget_get_realized (GTK_WIDGET (self)))
+ {
+ item->animation = egg_object_animate_full (item->adjustment,
+ ANIMATION_MODE,
+ ANIMATION_DURATION,
+ frame_clock,
+ ide_layout_animation_cb,
+ g_object_ref (child),
+ "value", reveal ? 0.0 : 1.0,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (item->animation), (gpointer *)&item->animation);
+ }
+ else
+ {
+ item->reveal = reveal;
+ gtk_adjustment_set_value (item->adjustment, reveal ? 0.0 : 1.0);
+ gtk_container_child_notify (GTK_CONTAINER (self), item->widget, "reveal");
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static void
+ide_layout_get_child_property (GtkContainer *container,
+ GtkWidget *child,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeLayout *self = (IdeLayout *)container;
+
+ switch (prop_id)
+ {
+ case CHILD_PROP_REVEAL:
+ g_value_set_boolean (value, ide_layout_child_get_reveal (self, child));
+ break;
+
+ case CHILD_PROP_POSITION:
+ g_value_set_uint (value, ide_layout_child_get_position (self, child));
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+ }
+}
+
+static void
+ide_layout_set_child_property (GtkContainer *container,
+ GtkWidget *child,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeLayout *self = (IdeLayout *)container;
+
+ switch (prop_id)
+ {
+ case CHILD_PROP_REVEAL:
+ ide_layout_child_set_reveal (self, child, g_value_get_boolean (value));
+ break;
+
+ case CHILD_PROP_POSITION:
+ ide_layout_child_set_position (self, child, g_value_get_uint (value));
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec);
+ }
+}
+
+static void
+ide_layout_get_preferred_width (GtkWidget *widget,
+ gint *min_width,
+ gint *nat_width)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gint i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ if (gtk_widget_get_visible (child->widget))
+ gtk_widget_get_preferred_width (child->widget, &child->min_width, &child->nat_width);
+ }
+
+ *min_width = priv->children [GTK_POS_LEFT].min_width
+ + priv->children [GTK_POS_RIGHT].min_width
+ + MAX (priv->children [GTK_POS_TOP].min_width,
+ priv->children [GTK_POS_BOTTOM].min_width);
+ *nat_width = priv->children [GTK_POS_LEFT].nat_width
+ + priv->children [GTK_POS_RIGHT].nat_width
+ + MAX (priv->children [GTK_POS_TOP].nat_width,
+ priv->children [GTK_POS_BOTTOM].nat_width);
+}
+
+static void
+ide_layout_get_preferred_height (GtkWidget *widget,
+ gint *min_height,
+ gint *nat_height)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gint i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ if (gtk_widget_get_visible (child->widget))
+ gtk_widget_get_preferred_height (child->widget, &child->min_height, &child->nat_height);
+ }
+
+ *min_height = MAX (MAX (priv->children [GTK_POS_LEFT].min_height,
+ priv->children [GTK_POS_RIGHT].min_height),
+ (priv->children [GTK_POS_BOTTOM].position +
+ priv->children [GTK_POS_TOP].min_height));
+
+ *nat_height = MAX (MAX (priv->children [GTK_POS_LEFT].nat_height,
+ priv->children [GTK_POS_RIGHT].nat_height),
+ (priv->children [GTK_POS_BOTTOM].position +
+ priv->children [GTK_POS_TOP].nat_height));
+}
+
+static GtkSizeRequestMode
+ide_layout_get_request_mode (GtkWidget *widget)
+{
+ return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static GtkAdjustment *
+ide_layout_create_adjustment (IdeLayout *self)
+{
+ GtkAdjustment *adj;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ adj = g_object_new (GTK_TYPE_ADJUSTMENT,
+ "lower", 0.0,
+ "upper", 1.0,
+ "value", 0.0,
+ NULL);
+
+ g_signal_connect_object (adj,
+ "value-changed",
+ G_CALLBACK (gtk_widget_queue_resize),
+ self,
+ G_CONNECT_SWAPPED);
+
+ return adj;
+}
+
+static void
+ide_layout_drag_begin_cb (IdeLayout *self,
+ gdouble x,
+ gdouble y,
+ GtkGesturePan *pan)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ IdeLayoutChild *left;
+ IdeLayoutChild *right;
+ IdeLayoutChild *bottom;
+ GdkEventSequence *sequence;
+ const GdkEvent *event;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_GESTURE_PAN (pan));
+
+ left = &priv->children [GTK_POS_LEFT];
+ right = &priv->children [GTK_POS_RIGHT];
+ bottom = &priv->children [GTK_POS_BOTTOM];
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (pan));
+ event = gtk_gesture_get_last_event (GTK_GESTURE (pan), sequence);
+
+ if (event->any.window == left->handle)
+ {
+ gtk_gesture_pan_set_orientation (pan, GTK_ORIENTATION_HORIZONTAL);
+ priv->drag_child = left;
+ }
+ else if (event->any.window == right->handle)
+ {
+ gtk_gesture_pan_set_orientation (pan, GTK_ORIENTATION_HORIZONTAL);
+ priv->drag_child = right;
+ }
+ else if (event->any.window == bottom->handle)
+ {
+ gtk_gesture_pan_set_orientation (pan, GTK_ORIENTATION_VERTICAL);
+ priv->drag_child = bottom;
+ }
+ else
+ {
+ gtk_gesture_set_state (GTK_GESTURE (pan), GTK_EVENT_SEQUENCE_DENIED);
+ priv->drag_child = NULL;
+ return;
+ }
+
+ priv->drag_position = MAX (priv->drag_child->position, MIN_POSITION);
+ gtk_gesture_set_state (GTK_GESTURE (pan), GTK_EVENT_SEQUENCE_CLAIMED);
+ gtk_container_child_notify (GTK_CONTAINER (self), priv->drag_child->widget, "position");
+}
+
+static void
+ide_layout_drag_end_cb (IdeLayout *self,
+ gdouble x,
+ gdouble y,
+ GtkGesturePan *pan)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ GdkEventSequence *sequence;
+ GtkEventSequenceState state;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_GESTURE_PAN (pan));
+
+ if (priv->drag_child == NULL)
+ return;
+
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (pan));
+ state = gtk_gesture_get_sequence_state (GTK_GESTURE (pan), sequence);
+ if (state == GTK_EVENT_SEQUENCE_DENIED)
+ {
+ priv->drag_child = NULL;
+ return;
+ }
+
+ if (priv->drag_child->position < MIN_POSITION)
+ {
+ gtk_container_child_set (GTK_CONTAINER (self), priv->drag_child->widget,
+ "reveal", FALSE,
+ NULL);
+ priv->drag_child->restore_position = priv->drag_position;
+ }
+
+ gtk_container_child_notify (GTK_CONTAINER (self), priv->drag_child->widget, "position");
+
+ priv->drag_child = NULL;
+ priv->drag_position = 0;
+}
+
+static void
+ide_layout_pan_cb (IdeLayout *self,
+ GtkPanDirection direction,
+ gdouble offset,
+ GtkGesturePan *pan)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ GtkAllocation alloc;
+ gint target_position = 0;
+ gint center_min_width;
+ gint left_max;
+ gint right_max;
+ gint bottom_max;
+
+ g_assert (IDE_IS_LAYOUT (self));
+ g_assert (GTK_IS_GESTURE_PAN (pan));
+ g_assert (priv->drag_child != NULL);
+
+ /*
+ * NOTE: This is trickier than it looks, so I choose to be
+ * very verbose. Feel free to clean it up.
+ */
+
+ gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
+
+ switch (direction)
+ {
+ case GTK_PAN_DIRECTION_LEFT:
+ if (priv->drag_child->type == GTK_POS_LEFT)
+ target_position = priv->drag_position - offset;
+ else if (priv->drag_child->type == GTK_POS_RIGHT)
+ target_position = priv->drag_position + offset;
+ break;
+
+ case GTK_PAN_DIRECTION_RIGHT:
+ if (priv->drag_child->type == GTK_POS_LEFT)
+ target_position = priv->drag_position + offset;
+ else if (priv->drag_child->type == GTK_POS_RIGHT)
+ target_position = priv->drag_position - offset;
+ break;
+
+ case GTK_PAN_DIRECTION_UP:
+ if (priv->drag_child->type == GTK_POS_BOTTOM)
+ target_position = priv->drag_position + offset;
+ break;
+
+ case GTK_PAN_DIRECTION_DOWN:
+ if (priv->drag_child->type == GTK_POS_BOTTOM)
+ target_position = priv->drag_position - offset;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ center_min_width = MAX (priv->children [GTK_POS_BOTTOM].min_width,
+ priv->children [GTK_POS_TOP].min_width);
+ left_max = alloc.width - priv->children [GTK_POS_RIGHT].alloc.width - center_min_width;
+ right_max = alloc.width - priv->children [GTK_POS_LEFT].position - center_min_width;
+ bottom_max = alloc.height - priv->children [GTK_POS_TOP].min_height;
+
+ switch (priv->drag_child->type)
+ {
+ case GTK_POS_LEFT:
+ target_position = MIN (left_max, target_position);
+ break;
+
+ case GTK_POS_RIGHT:
+ target_position = MIN (right_max, target_position);
+ break;
+
+ case GTK_POS_BOTTOM:
+ target_position = MIN (bottom_max, target_position);
+ break;
+
+ case GTK_POS_TOP:
+ default:
+ g_assert_not_reached ();
+ }
+
+ priv->drag_child->position = MAX (0, target_position);
+
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+}
+
+static GtkGesture *
+ide_layout_create_pan_gesture (IdeLayout *self,
+ GtkOrientation orientation)
+{
+ GtkGesture *gesture;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ gesture = gtk_gesture_pan_new (GTK_WIDGET (self), orientation);
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+ gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
+
+ g_signal_connect_object (gesture,
+ "drag-begin",
+ G_CALLBACK (ide_layout_drag_begin_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (gesture,
+ "drag-end",
+ G_CALLBACK (ide_layout_drag_end_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (gesture,
+ "pan",
+ G_CALLBACK (ide_layout_pan_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ return gesture;
+}
+
+static void
+ide_layout_realize (GtkWidget *widget)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ GTK_WIDGET_CLASS (ide_layout_parent_class)->realize (widget);
+
+ ide_layout_create_handle_window (self, GTK_POS_LEFT);
+ ide_layout_create_handle_window (self, GTK_POS_RIGHT);
+ ide_layout_create_handle_window (self, GTK_POS_BOTTOM);
+}
+
+static void
+ide_layout_unrealize (GtkWidget *widget)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ ide_layout_destroy_handle_window (self, GTK_POS_LEFT);
+ ide_layout_destroy_handle_window (self, GTK_POS_RIGHT);
+ ide_layout_destroy_handle_window (self, GTK_POS_BOTTOM);
+
+ GTK_WIDGET_CLASS (ide_layout_parent_class)->unrealize (widget);
+}
+
+static void
+ide_layout_map (GtkWidget *widget)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gint i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ GTK_WIDGET_CLASS (ide_layout_parent_class)->map (widget);
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ if (child->handle != NULL)
+ gdk_window_show (child->handle);
+ }
+}
+
+static void
+ide_layout_unmap (GtkWidget *widget)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ int i;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ if (child->handle != NULL)
+ gdk_window_hide (child->handle);
+ }
+
+ GTK_WIDGET_CLASS (ide_layout_parent_class)->unmap (widget);
+}
+
+static GObject *
+ide_layout_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *childname)
+{
+ IdeLayout *self = (IdeLayout *)buildable;
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ /*
+ * Override default get_internal_child to handle RTL vs LTR.
+ */
+ if (ide_str_equal0 (childname, "left_pane"))
+ return G_OBJECT (ide_layout_get_left_pane (self));
+ else if (ide_str_equal0 (childname, "right_pane"))
+ return G_OBJECT (ide_layout_get_right_pane (self));
+
+ return ide_layout_parent_buildable_iface->get_internal_child (buildable, builder, childname);
+}
+
+static void
+ide_layout_grab_focus (GtkWidget *widget)
+{
+ IdeLayout *self = (IdeLayout *)widget;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ g_assert (IDE_IS_LAYOUT (self));
+
+ gtk_widget_grab_focus (priv->children [GTK_POS_TOP].widget);
+}
+
+static void
+ide_layout_finalize (GObject *object)
+{
+ IdeLayout *self = (IdeLayout *)object;
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+ {
+ IdeLayoutChild *child = &priv->children [i];
+
+ ide_clear_weak_pointer (&child->animation);
+ g_clear_object (&child->adjustment);
+ }
+
+ g_clear_object (&priv->pan_gesture);
+
+ G_OBJECT_CLASS (ide_layout_parent_class)->finalize (object);
+}
+
+static void
+ide_layout_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeLayout *self = IDE_LAYOUT (object);
+
+ switch (prop_id)
+ {
+ case PROP_LEFT_PANE:
+ g_value_set_object (value, ide_layout_get_left_pane (self));
+ break;
+
+ case PROP_RIGHT_PANE:
+ g_value_set_object (value, ide_layout_get_right_pane (self));
+ break;
+
+ case PROP_BOTTOM_PANE:
+ g_value_set_object (value, ide_layout_get_bottom_pane (self));
+ break;
+
+ case PROP_CONTENT_PANE:
+ g_value_set_object (value, ide_layout_get_content_pane (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_layout_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+buildable_init_iface (GtkBuildableIface *iface)
+{
+ ide_layout_parent_buildable_iface = g_type_interface_peek_parent (iface);
+
+ iface->get_internal_child = ide_layout_get_internal_child;
+}
+
+static void
+ide_layout_class_init (IdeLayoutClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+ GtkOverlayClass *overlay_class = GTK_OVERLAY_CLASS (klass);
+
+ object_class->finalize = ide_layout_finalize;
+ object_class->get_property = ide_layout_get_property;
+ object_class->set_property = ide_layout_set_property;
+
+ widget_class->get_preferred_height = ide_layout_get_preferred_height;
+ widget_class->get_preferred_width = ide_layout_get_preferred_width;
+ widget_class->get_request_mode = ide_layout_get_request_mode;
+ widget_class->map = ide_layout_map;
+ widget_class->unmap = ide_layout_unmap;
+ widget_class->realize = ide_layout_realize;
+ widget_class->unrealize = ide_layout_unrealize;
+ widget_class->size_allocate = ide_layout_size_allocate;
+ widget_class->grab_focus = ide_layout_grab_focus;
+
+ container_class->get_child_property = ide_layout_get_child_property;
+ container_class->set_child_property = ide_layout_set_child_property;
+
+ overlay_class->get_child_position = ide_layout_get_child_position;
+
+ properties [PROP_LEFT_PANE] =
+ g_param_spec_object ("left-pane",
+ "Left Pane",
+ "The left workspace pane.",
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_RIGHT_PANE] =
+ g_param_spec_object ("right-pane",
+ "Right Pane",
+ "The right workspace pane.",
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_BOTTOM_PANE] =
+ g_param_spec_object ("bottom-pane",
+ "Bottom Pane",
+ "The bottom workspace pane.",
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CONTENT_PANE] =
+ g_param_spec_object ("content-pane",
+ "Content Pane",
+ "The content workspace pane.",
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ child_properties [CHILD_PROP_POSITION] =
+ g_param_spec_uint ("position",
+ "Position",
+ "The position of the pane relative to its edge.",
+ 0, G_MAXUINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_POSITION,
+ child_properties [CHILD_PROP_POSITION]);
+
+ child_properties [CHILD_PROP_REVEAL] =
+ g_param_spec_boolean ("reveal",
+ "Reveal",
+ "If the pane should be revealed.",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ gtk_container_class_install_child_property (container_class, CHILD_PROP_REVEAL,
+ child_properties [CHILD_PROP_REVEAL]);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-layout.ui");
+
+ gtk_widget_class_bind_template_child_full (widget_class, "bottom_pane", TRUE,
+ G_PRIVATE_OFFSET (IdeLayout, children[GTK_POS_BOTTOM].widget));
+ gtk_widget_class_bind_template_child_full (widget_class, "content_pane", TRUE,
+ G_PRIVATE_OFFSET (IdeLayout, children[GTK_POS_TOP].widget));
+ gtk_widget_class_bind_template_child_full (widget_class, "left_pane", TRUE,
+ G_PRIVATE_OFFSET (IdeLayout, children[GTK_POS_LEFT].widget));
+ gtk_widget_class_bind_template_child_full (widget_class, "right_pane", TRUE,
+ G_PRIVATE_OFFSET (IdeLayout, children[GTK_POS_RIGHT].widget));
+}
+
+static void
+ide_layout_init (IdeLayout *self)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ priv->children [GTK_POS_LEFT].type = GTK_POS_LEFT;
+ priv->children [GTK_POS_LEFT].reveal = TRUE;
+ priv->children [GTK_POS_LEFT].position = 250;
+ priv->children [GTK_POS_LEFT].adjustment = ide_layout_create_adjustment (self);
+ priv->children [GTK_POS_LEFT].cursor_type = GDK_SB_H_DOUBLE_ARROW;
+
+ priv->children [GTK_POS_RIGHT].type = GTK_POS_RIGHT;
+ priv->children [GTK_POS_RIGHT].reveal = TRUE;
+ priv->children [GTK_POS_RIGHT].position = 250;
+ priv->children [GTK_POS_RIGHT].adjustment = ide_layout_create_adjustment (self);
+ priv->children [GTK_POS_RIGHT].cursor_type = GDK_SB_H_DOUBLE_ARROW;
+
+ priv->children [GTK_POS_BOTTOM].type = GTK_POS_BOTTOM;
+ priv->children [GTK_POS_BOTTOM].reveal = TRUE;
+ priv->children [GTK_POS_BOTTOM].position = 150;
+ priv->children [GTK_POS_BOTTOM].adjustment = ide_layout_create_adjustment (self);
+ priv->children [GTK_POS_BOTTOM].cursor_type = GDK_SB_V_DOUBLE_ARROW;
+
+ priv->children [GTK_POS_TOP].type = GTK_POS_TOP;
+ priv->children [GTK_POS_TOP].reveal = TRUE;
+ priv->children [GTK_POS_TOP].adjustment = ide_layout_create_adjustment (self);
+
+ priv->pan_gesture = ide_layout_create_pan_gesture (self, GTK_ORIENTATION_HORIZONTAL);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+ide_layout_new (void)
+{
+ return g_object_new (IDE_TYPE_LAYOUT, NULL);
+}
+
+GtkWidget *
+ide_layout_get_left_pane (IdeLayout *self)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_LAYOUT (self), NULL);
+
+ if (gtk_widget_get_state_flags (GTK_WIDGET (self)) & GTK_STATE_FLAG_DIR_RTL)
+ return priv->children [GTK_POS_RIGHT].widget;
+ else
+ return priv->children [GTK_POS_LEFT].widget;
+}
+
+GtkWidget *
+ide_layout_get_right_pane (IdeLayout *self)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_LAYOUT (self), NULL);
+
+ if (gtk_widget_get_state_flags (GTK_WIDGET (self)) & GTK_STATE_FLAG_DIR_RTL)
+ return priv->children [GTK_POS_LEFT].widget;
+ else
+ return priv->children [GTK_POS_RIGHT].widget;
+}
+
+GtkWidget *
+ide_layout_get_bottom_pane (IdeLayout *self)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_LAYOUT (self), NULL);
+
+ return priv->children [GTK_POS_BOTTOM].widget;
+}
+
+GtkWidget *
+ide_layout_get_content_pane (IdeLayout *self)
+{
+ IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_LAYOUT (self), NULL);
+
+ return priv->children [GTK_POS_TOP].widget;
+}
diff --git a/libide/ide-layout.h b/libide/ide-layout.h
new file mode 100644
index 0000000..f293025
--- /dev/null
+++ b/libide/ide-layout.h
@@ -0,0 +1,45 @@
+/* ide-layout.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#ifndef IDE_LAYOUT_H
+#define IDE_LAYOUT_H
+
+#include <gtk/gtk.h>
+
+#include "ide-layout-pane.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LAYOUT (ide_layout_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeLayout, ide_layout, IDE, LAYOUT, GtkOverlay)
+
+struct _IdeLayoutClass
+{
+ GtkOverlayClass parent_instance;
+};
+
+GtkWidget *ide_layout_new (void);
+GtkWidget *ide_layout_get_left_pane (IdeLayout *self);
+GtkWidget *ide_layout_get_right_pane (IdeLayout *self);
+GtkWidget *ide_layout_get_bottom_pane (IdeLayout *self);
+GtkWidget *ide_layout_get_content_pane (IdeLayout *self);
+
+G_END_DECLS
+
+#endif /* IDE_LAYOUT_H */
diff --git a/libide/ide.h b/libide/ide.h
index 7168b3b..8d86215 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -57,7 +57,8 @@ G_BEGIN_DECLS
#include "ide-highlight-engine.h"
#include "ide-highlighter.h"
#include "ide-indenter.h"
-#include "ide-layout-manager.h"
+#include "ide-layout.h"
+#include "ide-layout-pane.h"
#include "ide-log.h"
#include "ide-macros.h"
#include "ide-object.h"
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index 35d6560..957ad37 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -25,6 +25,8 @@
<file alias="ui/ide-editor-perspective.ui">../../data/ui/ide-editor-perspective.ui</file>
<file alias="ui/ide-greeter-perspective.ui">../../data/ui/ide-greeter-perspective.ui</file>
<file alias="ui/ide-greeter-project-row.ui">../../data/ui/ide-greeter-project-row.ui</file>
+ <file alias="ui/ide-layout.ui">../../data/ui/ide-layout.ui</file>
+ <file alias="ui/ide-layout-pane.ui">../../data/ui/ide-layout-pane.ui</file>
<file alias="ui/ide-preferences-entry.ui">../../data/ui/ide-preferences-entry.ui</file>
<file alias="ui/ide-preferences-font-button.ui">../../data/ui/ide-preferences-font-button.ui</file>
<file alias="ui/ide-preferences-group.ui">../../data/ui/ide-preferences-group.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]