[gnome-builder] panel: rebase IdeLayout and IdeLayoutPane on panel-gtk



commit e9c4d1f2ecd907fbab70cd18b11ce9a9c6aef275
Author: Christian Hergert <chergert redhat com>
Date:   Tue Mar 22 19:02:19 2016 -0700

    panel: rebase IdeLayout and IdeLayoutPane on panel-gtk
    
    Just in time for 3.20, we are landing the panel cleanup. This primarily
    allows us to improve things for the dot releases without breaking ABI,
    and additionally will fix a bunch of resize warnings introduced by gadgets
    in GTK+.
    
    To the user, things should be pretty much the same. However, we've laid
    the groundwork to be able to incrementally add panel dragging and
    reordering in upcoming releases.
    
    Additionally, we'll be able to add a floating layer to the system in good
    time (PnlDockOverlay).

 data/theme/Adwaita-dark.css                     |    6 +-
 data/theme/Adwaita-panels.css                   |   50 +
 data/theme/Adwaita-shared.css                   |   22 +-
 data/theme/Adwaita.css                          |    6 +-
 data/ui/ide-editor-perspective.ui               |  104 +-
 data/ui/ide-layout-pane.ui                      |   27 +-
 data/ui/ide-layout.ui                           |   30 -
 doc/reference/libide/libide-sections.txt        |    4 -
 libide/Makefile.am                              |    8 +
 libide/editor/ide-editor-perspective.c          |   52 +-
 libide/editor/ide-editor-workbench-addin.c      |    6 +
 libide/ide-application-actions.c                |    6 +-
 libide/ide-layout-pane.c                        |  312 +------
 libide/ide-layout-pane.h                        |   15 +-
 libide/ide-layout-stack.c                       |    6 +
 libide/ide-layout.c                             | 1290 +----------------------
 libide/ide-layout.h                             |   15 +-
 libide/ide-workbench.c                          |    6 +-
 libide/resources/libide.gresource.xml           |    2 +-
 plugins/build-tools/gbp-build-log-panel.c       |    6 +-
 plugins/build-tools/gbp-build-log-panel.h       |    2 +-
 plugins/build-tools/gbp-build-log-panel.ui      |    2 +-
 plugins/build-tools/gbp-build-panel.c           |    6 +-
 plugins/build-tools/gbp-build-panel.h           |    2 +-
 plugins/build-tools/gbp-build-panel.ui          |    2 +-
 plugins/build-tools/gbp-build-workbench-addin.c |   16 +-
 plugins/devhelp/gbp-devhelp-editor-view-addin.c |    2 +-
 plugins/devhelp/gbp-devhelp-panel.c             |   11 +-
 plugins/devhelp/gbp-devhelp-panel.h             |    4 +-
 plugins/devhelp/gbp-devhelp-search-provider.c   |    2 +-
 plugins/devhelp/gbp-devhelp-workbench-addin.c   |   11 +-
 plugins/project-tree/gb-project-tree-addin.c    |   37 +-
 plugins/symbol-tree/symbol-tree-panel.c         |    7 +-
 plugins/symbol-tree/symbol-tree-panel.h         |    4 +-
 plugins/symbol-tree/symbol-tree-panel.ui        |   31 +-
 plugins/symbol-tree/symbol-tree.c               |   17 +-
 plugins/sysmon/gb-sysmon-addin.c                |   16 +-
 plugins/sysmon/gb-sysmon-panel.c                |    7 +-
 plugins/sysmon/gb-sysmon-panel.h                |    4 +-
 plugins/sysmon/gb-sysmon-panel.ui               |    3 +-
 plugins/terminal/gb-terminal-workbench-addin.c  |   27 +-
 plugins/todo/todo_plugin/__init__.py            |   10 +-
 po/POTFILES.in                                  |    5 +
 tests/Makefile.am                               |    2 +-
 44 files changed, 322 insertions(+), 1881 deletions(-)
---
diff --git a/data/theme/Adwaita-dark.css b/data/theme/Adwaita-dark.css
index a7b2ef6..f78d386 100644
--- a/data/theme/Adwaita-dark.css
+++ b/data/theme/Adwaita-dark.css
@@ -51,14 +51,14 @@ layouttab:backdrop {
 }
 
 
-layoutpane > box.vertical:first-child > box.horizontal {
+tabstrip {
   background-color: #303535;
   box-shadow: 0 4px 3px -5px #2c3030 inset,
               0 -1px 0 #1c1f1f inset;
 }
-layoutpane:backdrop > box.vertical:first-child > box.horizontal {
+tabstrip:backdrop {
   box-shadow: 0 -1px 0 #2c3030 inset;
 }
-layoutpane stackswitcher button:checked {
+tabstrip tab:checked {
   box-shadow: 0 -2px 0 #215d9c inset;
 }
diff --git a/data/theme/Adwaita-panels.css b/data/theme/Adwaita-panels.css
new file mode 100644
index 0000000..e45cd7a
--- /dev/null
+++ b/data/theme/Adwaita-panels.css
@@ -0,0 +1,50 @@
+dockbin {
+  border: 1px solid alpha(@borders, 0.75);
+  -PnlDockBin-handle-size: 1;
+}
+
+dockpaned {
+  border: 1px solid @borders;
+}
+
+tabstrip {
+  padding: 0 6px 0 6px;
+}
+
+tab {
+  background-image: none;
+  background-color: transparent;
+  border-color: transparent;
+  border-style: solid;
+  color: @theme_fg_color;
+  min-height: 39px;
+  outline-offset: -6px;
+  transition-duration: 200ms;
+  transition-timing-function: ease;
+}
+
+tab label {
+  font-size: 0.7em;
+}
+
+tab > * {
+  padding: 0 6px 0 6px;
+}
+
+dockoverlayedge {
+  background-color: @theme_bg_color;
+}
+
+dockoverlayedge tabstrip {
+  padding: 0;
+  border: none;
+}
+
+dockoverlayedge.left-edge tab:checked {
+  border-right-color: @theme_selected_bg_color;
+  border-bottom-color: transparent;
+}
+dockoverlayedge.right-edge tab:checked {
+  border-left-color: @theme_selected_bg_color;
+  border-bottom-color: transparent;
+}
diff --git a/data/theme/Adwaita-shared.css b/data/theme/Adwaita-shared.css
index d3a36fc..cbf6b7f 100644
--- a/data/theme/Adwaita-shared.css
+++ b/data/theme/Adwaita-shared.css
@@ -1,4 +1,5 @@
 @import url("resource:///org/gnome/builder/theme/shared.css");
+ import url("resource:///org/gnome/builder/theme/Adwaita-panels.css");
 
 
 /*
@@ -109,22 +110,7 @@ layouttab > box > button:last-child:backdrop image {
 }
 
 
-/*
- * The following tweaks the layout panel headers
- */
-layoutpane {
-  -IdeLayoutPane-handle-size: 1;
-  -gtk-icon-source: none;
-}
-layoutpane stackswitcher button {
-  min-width: 0;
-  border: none;
-  background: transparent;
-  box-shadow: none;
-  padding: 0 3px 0 3px;
-  border-radius: 0;
+layout {
+  border: 1px solid alpha(@borders, 0.75);
+  -PnlDockBin-handle-size: 1;
 }
-layoutpane .pane-separator {
-  background-color: @borders;
-}
-
diff --git a/data/theme/Adwaita.css b/data/theme/Adwaita.css
index 8563a30..25afe05 100644
--- a/data/theme/Adwaita.css
+++ b/data/theme/Adwaita.css
@@ -49,14 +49,14 @@ layouttab:backdrop {
 }
 
 
-layoutpane > box.vertical:first-child > box.horizontal {
+tabstrip {
   background-color: #d6d6d6;
   box-shadow: 0 4px 3px -5px #aaa inset,
               0 -1px 0 #a1a1a1 inset;
 }
-layoutpane:backdrop > box.vertical:first-child > box.horizontal {
+tabstrip:backdrop {
   box-shadow: 0 -1px 0 #a1a1a1 inset;
 }
-layoutpane stackswitcher button:checked {
+tabstrip tab:checked {
   box-shadow: 0 -2px 0 #4a90d9 inset;
 }
diff --git a/data/ui/ide-editor-perspective.ui b/data/ui/ide-editor-perspective.ui
index ceda61d..55bb06b 100644
--- a/data/ui/ide-editor-perspective.ui
+++ b/data/ui/ide-editor-perspective.ui
@@ -2,69 +2,61 @@
 <interface>
   <!-- interface-requires gtk+ 3.18 -->
   <template class="IdeEditorPerspective" parent="IdeLayout">
-    <child internal-child="content_pane">
-      <object class="IdeLayoutPane">
-        <child internal-child="header">
+    <child type="center">
+      <object class="GtkStack" id="content_stack">
+        <property name="visible">true</property>
+        <child>
           <object class="GtkBox">
-            <property name="visible">false</property>
-          </object>
-        </child>
-        <child internal-child="stack">
-          <object class="GtkStack" id="content_stack">
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">text-editor-symbolic</property>
+                <property name="pixel-size">128</property>
+                <property name="visible">true</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+            </child>
             <child>
-              <object class="GtkBox">
-                <property name="halign">center</property>
-                <property name="valign">center</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">12</property>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">No open files</property>
                 <property name="visible">true</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="icon-name">text-editor-symbolic</property>
-                    <property name="pixel-size">128</property>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel">
-                    <property name="label" translatable="yes">No open files</property>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                    <attributes>
-                      <attribute name="scale" value="2.0"/>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel">
-                    <property name="label" translatable="yes">Try opening a file by typing in the search box 
at the top</property>
-                    <property name="wrap">true</property>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
-                  </object>
-                </child>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+                <attributes>
+                  <attribute name="scale" value="2.0"/>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
               </object>
-              <packing>
-                <property name="name">empty_state</property>
-              </packing>
             </child>
             <child>
-              <object class="IdeLayoutGrid" id="grid">
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Try opening a file by typing in the search box at 
the top</property>
+                <property name="wrap">true</property>
                 <property name="visible">true</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
               </object>
-              <packing>
-                <property name="name">grid</property>
-              </packing>
             </child>
           </object>
+          <packing>
+            <property name="name">empty_state</property>
+          </packing>
+        </child>
+        <child>
+          <object class="IdeLayoutGrid" id="grid">
+            <property name="visible">true</property>
+          </object>
+          <packing>
+            <property name="name">grid</property>
+          </packing>
         </child>
       </object>
     </child>
@@ -80,7 +72,7 @@
         </style>
         <child>
           <object class="GtkToggleButton">
-            <property name="action-name">panels.left</property>
+            <property name="action-name">dockbin.left-visible</property>
             <property name="tooltip-text" translatable="yes">Enable / Disable left panel. Shortcut: 
F9</property>
             <property name="focus-on-click">false</property>
             <property name="visible">true</property>
@@ -97,7 +89,7 @@
         </child>
         <child>
           <object class="GtkToggleButton">
-            <property name="action-name">panels.bottom</property>
+            <property name="action-name">dockbin.bottom-visible</property>
             <property name="tooltip-text" translatable="yes">Enable / Disable bottom panel. Shortcut: Ctrl + 
F9</property>
             <property name="focus-on-click">false</property>
             <property name="visible">true</property>
@@ -114,7 +106,7 @@
         </child>
         <child>
           <object class="GtkToggleButton">
-            <property name="action-name">panels.right</property>
+            <property name="action-name">dockbin.right-visible</property>
             <property name="tooltip-text" translatable="yes">Enable / Disable right panel. Shortcut: Shift + 
F9</property>
             <property name="focus-on-click">false</property>
             <property name="visible">true</property>
diff --git a/data/ui/ide-layout-pane.ui b/data/ui/ide-layout-pane.ui
index 7b7f3ea..ddf495e 100644
--- a/data/ui/ide-layout-pane.ui
+++ b/data/ui/ide-layout-pane.ui
@@ -1,32 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.16 -->
-  <template class="IdeLayoutPane" parent="GtkBin">
+  <template class="IdeLayoutPane" parent="PnlDockBinEdge">
     <child>
-      <object class="GtkBox" id="box">
-        <property name="orientation">vertical</property>
+      <object class="PnlDockStack" id="dock_stack">
+        <property name="expand">true</property>
         <property name="visible">true</property>
-        <child>
-          <object class="GtkBox" id="header">
-            <property name="visible">true</property>
-            <property name="vexpand">false</property>
-            <child type="center">
-              <object class="GtkStackSwitcher" id="stack_switcher">
-                <property name="stack">stack</property>
-                <property name="visible">true</property>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkStack" id="stack">
-            <property name="transition-duration">150</property>
-            <property name="transition-type">crossfade</property>
-            <property name="expand">true</property>
-            <property name="homogeneous">false</property>
-            <property name="visible">true</property>
-          </object>
-        </child>
       </object>
     </child>
   </template>
diff --git a/doc/reference/libide/libide-sections.txt b/doc/reference/libide/libide-sections.txt
index 49f5e6d..168dafc 100644
--- a/doc/reference/libide/libide-sections.txt
+++ b/doc/reference/libide/libide-sections.txt
@@ -976,10 +976,6 @@ ide_language_defaults_init_finish
 IDE_TYPE_LAYOUT
 IdeLayoutClass
 ide_layout_new
-ide_layout_get_left_pane
-ide_layout_get_right_pane
-ide_layout_get_bottom_pane
-ide_layout_get_content_pane
 ide_layout_get_active_view
 IdeLayout
 </SECTION>
diff --git a/libide/Makefile.am b/libide/Makefile.am
index bf6145b..4c204dc 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -446,9 +446,11 @@ libide_1_0_la_includes = \
        -I$(top_srcdir)/contrib/egg \
        -I$(top_srcdir)/contrib/gd \
        -I$(top_srcdir)/contrib/nautilus \
+       -I$(top_srcdir)/contrib/pnl \
        -I$(top_srcdir)/contrib/search \
        -I$(top_srcdir)/contrib/tmpl \
        -I$(top_srcdir)/contrib/xml \
+       -I$(top_builddir)/contrib/pnl \
        -I$(top_builddir)/contrib/tmpl \
        -I$(top_builddir)/data/icons/hicolor \
        -I$(srcdir) \
@@ -500,6 +502,7 @@ libide_1_0_la_LIBADD = \
        $(top_builddir)/contrib/egg/libegg-private.la \
        $(top_builddir)/contrib/gd/libgd.la \
        $(top_builddir)/contrib/nautilus/libnautilus.la \
+       $(top_builddir)/contrib/pnl/libpanel-gtk.la \
        $(top_builddir)/contrib/search/libsearch.la \
        $(top_builddir)/contrib/tmpl/libtemplate-glib-1.0.la \
        $(top_builddir)/contrib/xml/libxml.la \
@@ -577,6 +580,7 @@ INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all
 INTROSPECTION_COMPILER_ARGS = \
        --includedir=$(srcdir) \
        --includedir=$(top_builddir)/contrib/egg \
+       --includedir=$(top_builddir)/contrib/pnl \
        --includedir=$(top_builddir)/contrib/tmpl
 
 introspection_sources = \
@@ -596,6 +600,7 @@ Ide_1_0_gir_LIBS = \
 Ide_1_0_gir_FILES = $(introspection_sources)
 Ide_1_0_gir_SCANNERFLAGS = \
        --include-uninstalled=$(top_builddir)/contrib/egg/Egg-1.0.gir \
+       --include-uninstalled=$(top_builddir)/contrib/pnl/Pnl-1.0.gir \
        --include-uninstalled=$(top_builddir)/contrib/tmpl/Template-1.0.gir \
        --c-include="ide.h" \
        -n Ide \
@@ -624,15 +629,18 @@ libide_1_0_vapi_DEPS = \
        gtk+-3.0 \
        gtksourceview-3.0 \
        libpeas-1.0 \
+       panel-gtk \
        template-glib-1.0 \
        egg-private
 libide_1_0_vapi_METADATADIRS = $(srcdir)
 libide_1_0_vapi_FILES = Ide-1.0.gir
 libide_1_0_vapi_VAPIDIRS = \
        $(top_builddir)/contrib/egg \
+       $(top_builddir)/contrib/pnl \
        $(top_builddir)/contrib/tmpl
 libide_1_0_vapi_GIRDIRS = \
        $(top_builddir)/contrib/egg \
+       $(top_builddir)/contrib/pnl \
        $(top_builddir)/contrib/tmpl
 
 libide-1.0.deps: Makefile
diff --git a/libide/editor/ide-editor-perspective.c b/libide/editor/ide-editor-perspective.c
index e81dedf..71ee5f6 100644
--- a/libide/editor/ide-editor-perspective.c
+++ b/libide/editor/ide-editor-perspective.c
@@ -76,29 +76,23 @@ ide_editor_perspective_restore_panel_state (IdeEditorPerspective *self)
 
   settings = g_settings_new ("org.gnome.builder.workbench");
 
-  pane = ide_layout_get_left_pane (IDE_LAYOUT (self));
+  pane = pnl_dock_bin_get_left_edge (PNL_DOCK_BIN (self));
   reveal = g_settings_get_boolean (settings, "left-visible");
   position = g_settings_get_int (settings, "left-position");
-  gtk_container_child_set (GTK_CONTAINER (self), pane,
-                           "position", position,
-                           "reveal", reveal,
-                           NULL);
+  pnl_dock_revealer_set_reveal_child (PNL_DOCK_REVEALER (pane), reveal);
+  pnl_dock_revealer_set_position (PNL_DOCK_REVEALER (pane), position);
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (self));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (self));
   reveal = g_settings_get_boolean (settings, "right-visible");
   position = g_settings_get_int (settings, "right-position");
-  gtk_container_child_set (GTK_CONTAINER (self), pane,
-                           "position", position,
-                           "reveal", reveal,
-                           NULL);
+  pnl_dock_revealer_set_reveal_child (PNL_DOCK_REVEALER (pane), reveal);
+  pnl_dock_revealer_set_position (PNL_DOCK_REVEALER (pane), position);
 
-  pane = ide_layout_get_bottom_pane (IDE_LAYOUT (self));
+  pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (self));
   reveal = g_settings_get_boolean (settings, "bottom-visible");
   position = g_settings_get_int (settings, "bottom-position");
-  gtk_container_child_set (GTK_CONTAINER (self), pane,
-                           "position", position,
-                           "reveal", reveal,
-                           NULL);
+  pnl_dock_revealer_set_reveal_child (PNL_DOCK_REVEALER (pane), reveal);
+  pnl_dock_revealer_set_position (PNL_DOCK_REVEALER (pane), position);
 }
 
 static void
@@ -113,27 +107,21 @@ ide_editor_perspective_save_panel_state (IdeEditorPerspective *self)
 
   settings = g_settings_new ("org.gnome.builder.workbench");
 
-  pane = ide_layout_get_left_pane (IDE_LAYOUT (self));
-  gtk_container_child_get (GTK_CONTAINER (IDE_LAYOUT (self)), pane,
-                           "reveal", &reveal,
-                           "position", &position,
-                           NULL);
+  pane = pnl_dock_bin_get_left_edge (PNL_DOCK_BIN (self));
+  position = pnl_dock_revealer_get_position (PNL_DOCK_REVEALER (pane));
+  reveal = pnl_dock_revealer_get_reveal_child (PNL_DOCK_REVEALER (pane));
   g_settings_set_boolean (settings, "left-visible", reveal);
   g_settings_set_int (settings, "left-position", position);
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (self));
-  gtk_container_child_get (GTK_CONTAINER (IDE_LAYOUT (self)), pane,
-                           "reveal", &reveal,
-                           "position", &position,
-                           NULL);
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (self));
+  position = pnl_dock_revealer_get_position (PNL_DOCK_REVEALER (pane));
+  reveal = pnl_dock_revealer_get_reveal_child (PNL_DOCK_REVEALER (pane));
   g_settings_set_boolean (settings, "right-visible", reveal);
   g_settings_set_int (settings, "right-position", position);
 
-  pane = ide_layout_get_bottom_pane (IDE_LAYOUT (self));
-  gtk_container_child_get (GTK_CONTAINER (IDE_LAYOUT (self)), pane,
-                           "reveal", &reveal,
-                           "position", &position,
-                           NULL);
+  pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (self));
+  position = pnl_dock_revealer_get_position (PNL_DOCK_REVEALER (pane));
+  reveal = pnl_dock_revealer_get_reveal_child (PNL_DOCK_REVEALER (pane));
   g_settings_set_boolean (settings, "bottom-visible", reveal);
   g_settings_set_int (settings, "bottom-position", position);
 }
@@ -441,8 +429,8 @@ ide_editor_perspective_init (IdeEditorPerspective *self)
   g_action_map_add_action_entries (G_ACTION_MAP (self->actions), entries,
                                    G_N_ELEMENTS (entries), self);
 
-  actions = gtk_widget_get_action_group (GTK_WIDGET (self), "panels");
-  gtk_widget_insert_action_group (GTK_WIDGET (self->titlebar), "panels", actions);
+  actions = gtk_widget_get_action_group (GTK_WIDGET (self), "dockbin");
+  gtk_widget_insert_action_group (GTK_WIDGET (self->titlebar), "dockbin", actions);
 
   ide_editor_perspective_restore_panel_state (self);
 
diff --git a/libide/editor/ide-editor-workbench-addin.c b/libide/editor/ide-editor-workbench-addin.c
index e394322..fc1da1e 100644
--- a/libide/editor/ide-editor-workbench-addin.c
+++ b/libide/editor/ide-editor-workbench-addin.c
@@ -33,6 +33,7 @@
 struct _IdeEditorWorkbenchAddin
 {
   GObject               parent_instance;
+  PnlDockManager       *manager;
   IdeEditorPerspective *perspective;
   IdeWorkbench         *workbench;
 };
@@ -65,7 +66,10 @@ ide_editor_workbench_addin_load (IdeWorkbenchAddin *addin,
 
   self->workbench = workbench;
 
+  self->manager = pnl_dock_manager_new ();
+
   self->perspective = g_object_new (IDE_TYPE_EDITOR_PERSPECTIVE,
+                                    "manager", self->manager,
                                     "visible", TRUE,
                                     NULL);
   ide_workbench_add_perspective (workbench, IDE_PERSPECTIVE (self->perspective));
@@ -90,6 +94,8 @@ ide_editor_workbench_addin_unload (IdeWorkbenchAddin *addin,
   self->workbench = NULL;
 
   ide_workbench_remove_perspective (workbench, perspective);
+
+  g_clear_object (&self->manager);
 }
 
 static gboolean
diff --git a/libide/ide-application-actions.c b/libide/ide-application-actions.c
index 6f92ab6..e9cd7b0 100644
--- a/libide/ide-application-actions.c
+++ b/libide/ide-application-actions.c
@@ -309,9 +309,9 @@ ide_application_actions_init (IdeApplication *self)
   /*
    * FIXME: Once we get a new shortcuts engine, port these to that.
    */
-  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "panels.left", left);
-  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "panels.right", right);
-  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "panels.bottom", bottom);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "dockbin.left-visible", left);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "dockbin.right-visible", right);
+  gtk_application_set_accels_for_action (GTK_APPLICATION (self), "dockbin.bottom-visible", bottom);
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.preferences", preferences);
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "perspective.global-search", global_search);
   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "perspective.new-file", new_file);
diff --git a/libide/ide-layout-pane.c b/libide/ide-layout-pane.c
index fb54f07..0d51d78 100644
--- a/libide/ide-layout-pane.c
+++ b/libide/ide-layout-pane.c
@@ -27,154 +27,29 @@
 
 struct _IdeLayoutPane
 {
-  GtkBin            parent_instance;
-
-  GtkBox           *box;
-  GtkBox           *header;
-  GtkStackSwitcher *stack_switcher;
-  GtkStack         *stack;
+  PnlDockBinEdge    parent_instance;
 
   EggSignalGroup   *toplevel_signals;
 
-  gulong            notify_stack_signal_handler;
-
-  GdkRectangle      handle_pos;
-
-  GtkPositionType   position;
+  PnlDockStack     *dock_stack;
 };
 
-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;
-}
+G_DEFINE_TYPE (IdeLayoutPane, ide_layout_pane, PNL_TYPE_DOCK_BIN_EDGE)
 
 static void
-ide_layout_pane_size_allocate (GtkWidget     *widget,
-                               GtkAllocation *alloc)
+ide_layout_pane_add (GtkContainer *container,
+                     GtkWidget    *widget)
 {
-  IdeLayoutPane *self = (IdeLayoutPane *)widget;
-  GtkWidget *child;
-  GtkAllocation child_alloc;
-  gint handle_size;
+  IdeLayoutPane *self = (IdeLayoutPane *)container;
 
   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
-on_stack_changed (GtkWidget *widget)
-{
-  GtkWidget *child;
-
-  g_assert (GTK_IS_WIDGET (widget));
-
-  child = gtk_stack_get_visible_child (GTK_STACK (widget));
-
-  if (child != NULL)
-    gtk_widget_grab_focus (child);
+  if (PNL_IS_DOCK_WIDGET (widget))
+    gtk_container_add (GTK_CONTAINER (self->dock_stack), widget);
+  else
+    GTK_CONTAINER_CLASS (ide_layout_pane_parent_class)->add (container, widget);
 }
 
-
 static void
 workbench_focus_changed (GtkWidget     *toplevel,
                          GtkWidget     *focus,
@@ -219,10 +94,6 @@ ide_layout_pane_hierarchy_changed (GtkWidget *widget,
     toplevel = NULL;
 
   egg_signal_group_set_target (self->toplevel_signals, toplevel);
-
-  if (IDE_IS_WORKBENCH (toplevel) && gtk_widget_get_visible (GTK_WIDGET (self->stack_switcher)))
-    gtk_size_group_add_widget (IDE_WORKBENCH (toplevel)->header_size_group,
-                               GTK_WIDGET (self->stack_switcher));
 }
 
 static void
@@ -230,116 +101,28 @@ ide_layout_pane_dispose (GObject *object)
 {
   IdeLayoutPane *self = (IdeLayoutPane *)object;
 
-  if (self->notify_stack_signal_handler > 0 && GTK_IS_STACK (self->stack))
-    ide_clear_signal_handler (self->stack, &self->notify_stack_signal_handler);
-
   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);
+  GtkContainerClass *container_class = GTK_CONTAINER_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;
-
-  gtk_widget_class_set_css_name (widget_class, "layoutpane");
-
-  /**
-   * 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);
+  container_class->add = ide_layout_pane_add;
 
-  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_css_name (widget_class, "layoutpane");
 
   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, header);
-  gtk_widget_class_bind_template_child_internal (widget_class, IdeLayoutPane, stack_switcher);
+  gtk_widget_class_bind_template_child (widget_class, IdeLayoutPane, dock_stack);
 }
 
 static void
@@ -353,71 +136,4 @@ ide_layout_pane_init (IdeLayoutPane *self)
                                    G_CONNECT_AFTER);
 
   gtk_widget_init_template (GTK_WIDGET (self));
-
-  self->notify_stack_signal_handler = g_signal_connect (self->stack,
-                                                        "notify::visible-child",
-                                                        G_CALLBACK (on_stack_changed),
-                                                        NULL);
-}
-
-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]);
-    }
-}
-
-/**
- * ide_layout_pane_add_page:
- * @self: An #IdeLayoutPane
- * @page: A #GtkWidget
- * @title: the title for the page
- * @icon_name: (nullable): the icon name
- *
- */
-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,
-#if 0
-                                     "icon-name", icon_name,
-#endif
-                                     "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
index ab944b9..8112e61 100644
--- a/libide/ide-layout-pane.h
+++ b/libide/ide-layout-pane.h
@@ -19,24 +19,13 @@
 #ifndef IDE_LAYOUT_PANE_H
 #define IDE_LAYOUT_PANE_H
 
-#include <gtk/gtk.h>
+#include <pnl.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_DECLARE_FINAL_TYPE (IdeLayoutPane, ide_layout_pane, IDE, LAYOUT_PANE, PnlDockBinEdge)
 
 G_END_DECLS
 
diff --git a/libide/ide-layout-stack.c b/libide/ide-layout-stack.c
index 3d25c61..8c46988 100644
--- a/libide/ide-layout-stack.c
+++ b/libide/ide-layout-stack.c
@@ -442,6 +442,7 @@ static void
 ide_layout_stack_init (IdeLayoutStack *self)
 {
   GtkStyleContext *context;
+  GList *focus_chain = NULL;
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
@@ -463,6 +464,11 @@ ide_layout_stack_init (IdeLayoutStack *self)
                            G_CONNECT_SWAPPED);
 
   ide_widget_set_context_handler (self, ide_layout_stack_context_handler);
+
+  focus_chain = g_list_prepend (focus_chain, self->tab_bar);
+  focus_chain = g_list_prepend (focus_chain, self->stack);
+  gtk_container_set_focus_chain (GTK_CONTAINER (self), focus_chain);
+  g_list_free (focus_chain);
 }
 
 GtkWidget *
diff --git a/libide/ide-layout.c b/libide/ide-layout.c
index c530293..3fd16e8 100644
--- a/libide/ide-layout.c
+++ b/libide/ide-layout.c
@@ -16,1005 +16,26 @@
  * 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;
+#include "ide-layout-view.h"
 
 typedef struct
 {
-  IdeLayoutChild    children[4];
+  GtkWidget *active_view;
 
-  GActionMap       *actions;
-  GtkGesture       *pan_gesture;
-  IdeLayoutChild   *drag_child;
-  gdouble           drag_position;
-
-  GtkWidget        *active_view;
-  gulong            focus_handler;
+  gulong     focus_handler;
 } IdeLayoutPrivate;
 
-static void buildable_init_iface (GtkBuildableIface *iface);
-static const gchar *action_names[] = { "left", "right", NULL, "bottom" };
-
-G_DEFINE_TYPE_WITH_CODE (IdeLayout, ide_layout, GTK_TYPE_OVERLAY,
-                         G_ADD_PRIVATE (IdeLayout)
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_init_iface))
+G_DEFINE_TYPE_WITH_PRIVATE (IdeLayout, ide_layout, PNL_TYPE_DOCK_BIN)
 
 enum {
   PROP_0,
   PROP_ACTIVE_VIEW,
-  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_child_get_preferred_size (IdeLayoutChild      *child,
-                                     const GtkAllocation *alloc)
-{
-  GtkRequisition min_req = { 0 };
-  GtkRequisition nat_req = { 0 };
-
-  g_assert (child != NULL);
-  g_assert (child->widget != NULL);
-  g_assert (alloc != NULL);
-
-  gtk_widget_get_preferred_size (child->widget, &min_req, &nat_req);
-
-  child->min_height = min_req.height;
-  child->nat_height = nat_req.height;
-  child->min_width = min_req.width;
-  child->nat_width = nat_req.width;
-}
-
-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];
-
-  ide_layout_child_get_preferred_size (left, alloc);
-  ide_layout_child_get_preferred_size (right, alloc);
-  ide_layout_child_get_preferred_size (content, alloc);
-  ide_layout_child_get_preferred_size (bottom, alloc);
-
-  /*
-   * 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_allocate (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_allocate (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)
-{
-  IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
-  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");
-    }
-
-  if (action_names [item->type] != NULL)
-    {
-      GAction *action;
-
-      action = g_action_map_lookup_action (priv->actions, action_names [item->type]);
-
-      if (G_IS_SIMPLE_ACTION (action))
-        g_simple_action_set_state (G_SIMPLE_ACTION (action),
-                                   g_variant_new_boolean (reveal));
-    }
-
-  gtk_widget_queue_allocate (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_allocate),
-                           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_allocate (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 GParamSpec *properties [LAST_PROP];
 
 static void
 ide_layout_active_view_weak_cb (IdeLayout *self,
@@ -1085,6 +106,7 @@ ide_layout_hierarchy_changed (GtkWidget *widget,
   GtkWidget *toplevel;
 
   g_assert (IDE_IS_LAYOUT (self));
+  g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
 
   if ((old_toplevel != NULL) && (priv->focus_handler != 0))
     {
@@ -1103,33 +125,15 @@ ide_layout_hierarchy_changed (GtkWidget *widget,
                                 self);
 }
 
-static void
-ide_layout_finalize (GObject *object)
+static GtkWidget *
+ide_layout_create_edge (PnlDockBin *dock)
 {
-  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];
+  g_assert (IDE_IS_LAYOUT (dock));
 
-      ide_clear_weak_pointer (&child->animation);
-      g_clear_object (&child->adjustment);
-    }
-
-  if (priv->active_view)
-    {
-      g_object_weak_unref (G_OBJECT (priv->active_view),
-                           (GWeakNotify)ide_layout_active_view_weak_cb,
-                           self);
-      priv->active_view = NULL;
-    }
-
-  g_clear_object (&priv->pan_gesture);
-  g_clear_object (&priv->actions);
-
-  G_OBJECT_CLASS (ide_layout_parent_class)->finalize (object);
+  return g_object_new (IDE_TYPE_LAYOUT_PANE,
+                       "visible", TRUE,
+                       "reveal-child", FALSE,
+                       NULL);
 }
 
 static void
@@ -1146,91 +150,25 @@ ide_layout_get_property (GObject    *object,
       g_value_set_object (value, ide_layout_get_active_view (self));
       break;
 
-    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);
+      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);
+  PnlDockBinClass *dock_bin_class = PNL_DOCK_BIN_CLASS (klass);
 
-  object_class->finalize = ide_layout_finalize;
   object_class->get_property = ide_layout_get_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->hierarchy_changed = ide_layout_hierarchy_changed;
-  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;
 
-  gtk_widget_class_set_css_name (widget_class, "layout");
-
-  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));
+  dock_bin_class->create_edge = ide_layout_create_edge;
 
-  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));
+  gtk_widget_class_set_css_name (widget_class, "layout");
 
   properties [PROP_ACTIVE_VIEW] =
     g_param_spec_object ("active-view",
@@ -1240,205 +178,11 @@ ide_layout_class_init (IdeLayoutClass *klass)
                          (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_activate_left (GSimpleAction *action,
-                          GVariant      *param,
-                          gpointer       user_data)
-{
-  IdeLayout *self = user_data;
-  GtkWidget *child;
-  gboolean reveal;
-
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_LAYOUT (self));
-
-  child = ide_layout_get_left_pane (self);
-  reveal = g_variant_get_boolean (param);
-  gtk_container_child_set (GTK_CONTAINER (self), child, "reveal", reveal, NULL);
-}
-
-static void
-ide_layout_activate_right (GSimpleAction *action,
-                           GVariant      *param,
-                           gpointer       user_data)
-{
-  IdeLayout *self = user_data;
-  GtkWidget *child;
-  gboolean reveal;
-
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_LAYOUT (self));
-
-  child = ide_layout_get_right_pane (self);
-  reveal = g_variant_get_boolean (param);
-  gtk_container_child_set (GTK_CONTAINER (self), child, "reveal", reveal, NULL);
 }
 
 static void
-ide_layout_activate_bottom (GSimpleAction *action,
-                            GVariant      *param,
-                            gpointer       user_data)
-{
-  IdeLayout *self = user_data;
-  GtkWidget *child;
-  gboolean reveal;
-
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (IDE_IS_LAYOUT (self));
-
-  child = ide_layout_get_bottom_pane (self);
-  reveal = g_variant_get_boolean (param);
-  gtk_container_child_set (GTK_CONTAINER (self), child, "reveal", reveal, NULL);
-}
-
-static const GActionEntry action_entries[] = {
-  { "left", NULL, NULL, "true", ide_layout_activate_left },
-  { "right", NULL, NULL, "true", ide_layout_activate_right },
-  { "bottom", NULL, NULL, "true", ide_layout_activate_bottom  },
-};
-
-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));
-
-  priv->actions = G_ACTION_MAP (g_simple_action_group_new ());
-  g_action_map_add_action_entries (G_ACTION_MAP (priv->actions),
-                                   action_entries,
-                                   G_N_ELEMENTS (action_entries),
-                                   self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "panels",
-                                  G_ACTION_GROUP (priv->actions));
-}
-
-GtkWidget *
-ide_layout_new (void)
-{
-  return g_object_new (IDE_TYPE_LAYOUT, NULL);
-}
-
-/**
- * ide_layout_get_left_pane:
- *
- * Returns: (transfer none): A #GtkWidget.
- */
-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;
-}
-
-/**
- * ide_layout_get_right_pane:
- *
- * Returns: (transfer none): A #GtkWidget.
- */
-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;
-}
-
-/**
- * ide_layout_get_bottom_pane:
- *
- * Returns: (transfer none): A #GtkWidget.
- */
-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;
-}
-
-/**
- * ide_layout_get_content_pane:
- *
- * Returns: (transfer none): A #GtkWidget.
- */
-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
index 98698d6..409427c 100644
--- a/libide/ide-layout.h
+++ b/libide/ide-layout.h
@@ -19,27 +19,20 @@
 #ifndef IDE_LAYOUT_H
 #define IDE_LAYOUT_H
 
-#include <gtk/gtk.h>
-
-#include "ide-layout-pane.h"
+#include <pnl.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_LAYOUT (ide_layout_get_type())
 
-G_DECLARE_DERIVABLE_TYPE (IdeLayout, ide_layout, IDE, LAYOUT, GtkOverlay)
+G_DECLARE_DERIVABLE_TYPE (IdeLayout, ide_layout, IDE, LAYOUT, PnlDockBin)
 
 struct _IdeLayoutClass
 {
-  GtkOverlayClass parent_instance;
+  PnlDockBinClass parent_class;
 };
 
-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);
-GtkWidget *ide_layout_get_active_view  (IdeLayout *self);
+GtkWidget *ide_layout_get_active_view (IdeLayout *self);
 
 G_END_DECLS
 
diff --git a/libide/ide-workbench.c b/libide/ide-workbench.c
index 9b7556c..decf659 100644
--- a/libide/ide-workbench.c
+++ b/libide/ide-workbench.c
@@ -780,11 +780,7 @@ ide_workbench_show_parents (GtkWidget *widget)
   parent = gtk_widget_get_parent (widget);
 
   if (IDE_IS_LAYOUT_PANE (widget))
-    {
-      gtk_container_child_set (GTK_CONTAINER (parent), widget,
-                               "reveal", TRUE,
-                               NULL);
-    }
+    pnl_dock_revealer_set_reveal_child (PNL_DOCK_REVEALER (widget), TRUE);
 
   if (IDE_IS_PERSPECTIVE (widget))
     ide_workbench_set_visible_perspective (ide_widget_get_workbench (widget),
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index be892d8..5117d97 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -20,6 +20,7 @@
   <gresource prefix="/org/gnome/builder/theme">
     <file compressed="true" alias="Adwaita.css">../../data/theme/Adwaita.css</file>
     <file compressed="true" alias="Adwaita-dark.css">../../data/theme/Adwaita-dark.css</file>
+    <file compressed="true" alias="Adwaita-panels.css">../../data/theme/Adwaita-panels.css</file>
     <file compressed="true" alias="Adwaita-shared.css">../../data/theme/Adwaita-shared.css</file>
 
     <file compressed="true" alias="Arc.css">../../data/theme/Arc.css</file>
@@ -57,7 +58,6 @@
     <file compressed="true" 
alias="ide-genesis-perspective.ui">../../data/ui/ide-genesis-perspective.ui</file>
     <file compressed="true" 
alias="ide-greeter-perspective.ui">../../data/ui/ide-greeter-perspective.ui</file>
     <file compressed="true" 
alias="ide-greeter-project-row.ui">../../data/ui/ide-greeter-project-row.ui</file>
-    <file compressed="true" alias="ide-layout.ui">../../data/ui/ide-layout.ui</file>
     <file compressed="true" alias="ide-layout-tab.ui">../../data/ui/ide-layout-tab.ui</file>
     <file compressed="true" alias="ide-layout-tab-bar.ui">../../data/ui/ide-layout-tab-bar.ui</file>
     <file compressed="true" alias="ide-layout-pane.ui">../../data/ui/ide-layout-pane.ui</file>
diff --git a/plugins/build-tools/gbp-build-log-panel.c b/plugins/build-tools/gbp-build-log-panel.c
index 8e13762..e677398 100644
--- a/plugins/build-tools/gbp-build-log-panel.c
+++ b/plugins/build-tools/gbp-build-log-panel.c
@@ -27,7 +27,7 @@
 
 struct _GbpBuildLogPanel
 {
-  GtkBin             parent_instance;
+  PnlDockWidget      parent_instance;
 
   IdeBuildResult    *result;
   EggSignalGroup    *signals;
@@ -46,7 +46,7 @@ enum {
   LAST_PROP
 };
 
-G_DEFINE_TYPE (GbpBuildLogPanel, gbp_build_log_panel, GTK_TYPE_BIN)
+G_DEFINE_TYPE (GbpBuildLogPanel, gbp_build_log_panel, PNL_TYPE_DOCK_WIDGET)
 
 static GParamSpec *properties [LAST_PROP];
 
@@ -246,6 +246,8 @@ gbp_build_log_panel_init (GbpBuildLogPanel *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
+  g_object_set (self, "title", _("Build Output"), NULL);
+
   gbp_build_log_panel_reset_view (self);
 
   self->signals = egg_signal_group_new (IDE_TYPE_BUILD_RESULT);
diff --git a/plugins/build-tools/gbp-build-log-panel.h b/plugins/build-tools/gbp-build-log-panel.h
index 9cc1a58..df407ba 100644
--- a/plugins/build-tools/gbp-build-log-panel.h
+++ b/plugins/build-tools/gbp-build-log-panel.h
@@ -26,7 +26,7 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_BUILD_LOG_PANEL (gbp_build_log_panel_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpBuildLogPanel, gbp_build_log_panel, GBP, BUILD_LOG_PANEL, GtkBin)
+G_DECLARE_FINAL_TYPE (GbpBuildLogPanel, gbp_build_log_panel, GBP, BUILD_LOG_PANEL, PnlDockWidget)
 
 void gbp_build_log_panel_set_result (GbpBuildLogPanel *self,
                                      IdeBuildResult   *result);
diff --git a/plugins/build-tools/gbp-build-log-panel.ui b/plugins/build-tools/gbp-build-log-panel.ui
index 299fc55..1e054f4 100644
--- a/plugins/build-tools/gbp-build-log-panel.ui
+++ b/plugins/build-tools/gbp-build-log-panel.ui
@@ -1,5 +1,5 @@
 <interface>
-  <template class="GbpBuildLogPanel" parent="GtkBin">
+  <template class="GbpBuildLogPanel" parent="PnlDockWidget">
     <child>
       <object class="GtkScrolledWindow" id="scroller">
         <property name="visible">true</property>
diff --git a/plugins/build-tools/gbp-build-panel.c b/plugins/build-tools/gbp-build-panel.c
index 1b703bd..81e6c02 100644
--- a/plugins/build-tools/gbp-build-panel.c
+++ b/plugins/build-tools/gbp-build-panel.c
@@ -28,7 +28,7 @@
 
 struct _GbpBuildPanel
 {
-  GtkBin            parent_instance;
+  PnlDockWidget     parent_instance;
 
   IdeBuildResult   *result;
   EggSignalGroup   *signals;
@@ -50,7 +50,7 @@ struct _GbpBuildPanel
   guint             warning_count;
 };
 
-G_DEFINE_TYPE (GbpBuildPanel, gbp_build_panel, GTK_TYPE_BIN)
+G_DEFINE_TYPE (GbpBuildPanel, gbp_build_panel, PNL_TYPE_DOCK_WIDGET)
 
 enum {
   PROP_0,
@@ -423,6 +423,8 @@ gbp_build_panel_init (GbpBuildPanel *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 
+  g_object_set (self, "title", _("Build"), NULL);
+
   self->signals = egg_signal_group_new (IDE_TYPE_BUILD_RESULT);
 
   egg_signal_group_connect_object (self->signals,
diff --git a/plugins/build-tools/gbp-build-panel.h b/plugins/build-tools/gbp-build-panel.h
index 5d2f8bc..86f7527 100644
--- a/plugins/build-tools/gbp-build-panel.h
+++ b/plugins/build-tools/gbp-build-panel.h
@@ -26,7 +26,7 @@ G_BEGIN_DECLS
 
 #define GBP_TYPE_BUILD_PANEL (gbp_build_panel_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpBuildPanel, gbp_build_panel, GBP, BUILD_PANEL, GtkBin)
+G_DECLARE_FINAL_TYPE (GbpBuildPanel, gbp_build_panel, GBP, BUILD_PANEL, PnlDockWidget)
 
 void gbp_build_panel_set_result (GbpBuildPanel  *self,
                                  IdeBuildResult *result);
diff --git a/plugins/build-tools/gbp-build-panel.ui b/plugins/build-tools/gbp-build-panel.ui
index 9b29a5c..77a9ebc 100644
--- a/plugins/build-tools/gbp-build-panel.ui
+++ b/plugins/build-tools/gbp-build-panel.ui
@@ -1,5 +1,5 @@
 <interface>
-  <template class="GbpBuildPanel" parent="GtkBin">
+  <template class="GbpBuildPanel" parent="PnlDockWidget">
     <child>
       <object class="GtkBox" id="toplevel">
         <property name="visible">true</property>
diff --git a/plugins/build-tools/gbp-build-workbench-addin.c b/plugins/build-tools/gbp-build-workbench-addin.c
index f074b71..8194a10 100644
--- a/plugins/build-tools/gbp-build-workbench-addin.c
+++ b/plugins/build-tools/gbp-build-workbench-addin.c
@@ -262,17 +262,16 @@ gbp_build_workbench_addin_load (IdeWorkbenchAddin *addin,
   configuration = ide_configuration_manager_get_current (configuration_manager);
 
   editor = ide_workbench_get_perspective_by_name (workbench, "editor");
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (editor));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (editor));
   self->panel = g_object_new (GBP_TYPE_BUILD_PANEL,
                               "configuration-manager", configuration_manager,
                               "visible", TRUE,
                               NULL);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel), _("Build"), NULL);
+  gtk_container_add (GTK_CONTAINER (pane), GTK_WIDGET (self->panel));
 
-  pane = ide_layout_get_bottom_pane (IDE_LAYOUT (editor));
+  pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (editor));
   self->build_log_panel = g_object_new (GBP_TYPE_BUILD_LOG_PANEL, NULL);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->build_log_panel),
-                            _("Build Output"), NULL);
+  gtk_container_add (GTK_CONTAINER (pane), GTK_WIDGET (self->build_log_panel));
 
   gtk_widget_insert_action_group (GTK_WIDGET (workbench), "build-tools",
                                   G_ACTION_GROUP (self->actions));
@@ -292,8 +291,6 @@ gbp_build_workbench_addin_unload (IdeWorkbenchAddin *addin,
                                   IdeWorkbench      *workbench)
 {
   GbpBuildWorkbenchAddin *self = (GbpBuildWorkbenchAddin *)addin;
-  IdePerspective *editor;
-  GtkWidget *pane;
 
   g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
   g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
@@ -306,9 +303,8 @@ gbp_build_workbench_addin_unload (IdeWorkbenchAddin *addin,
 
   gtk_widget_insert_action_group (GTK_WIDGET (workbench), "build-tools", NULL);
 
-  editor = ide_workbench_get_perspective_by_name (workbench, "editor");
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (editor));
-  ide_layout_pane_remove_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel));
+  gtk_widget_destroy (GTK_WIDGET (self->panel));
+  self->panel = NULL;
 }
 
 static void
diff --git a/plugins/devhelp/gbp-devhelp-editor-view-addin.c b/plugins/devhelp/gbp-devhelp-editor-view-addin.c
index 73fb07b..e67047e 100644
--- a/plugins/devhelp/gbp-devhelp-editor-view-addin.c
+++ b/plugins/devhelp/gbp-devhelp-editor-view-addin.c
@@ -45,7 +45,7 @@ request_documentation_cb (GbpDevhelpEditorViewAddin *self,
   if (layout == NULL)
     return;
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (layout));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (layout));
   panel = ide_widget_find_child_typed (pane, GBP_TYPE_DEVHELP_PANEL);
   gbp_devhelp_panel_focus_search (GBP_DEVHELP_PANEL (panel), word);
 }
diff --git a/plugins/devhelp/gbp-devhelp-panel.c b/plugins/devhelp/gbp-devhelp-panel.c
index 757cd2d..903da6a 100644
--- a/plugins/devhelp/gbp-devhelp-panel.c
+++ b/plugins/devhelp/gbp-devhelp-panel.c
@@ -17,6 +17,7 @@
  */
 
 #include <devhelp/devhelp.h>
+#include <glib/gi18n.h>
 #include <ide.h>
 
 #include "gbp-devhelp-panel.h"
@@ -24,13 +25,13 @@
 
 struct _GbpDevhelpPanel
 {
-  GtkBin         parent_instance;
+  PnlDockWidget  parent_instance;
 
   DhBookManager *books;
   DhSidebar     *sidebar;
 };
 
-G_DEFINE_TYPE (GbpDevhelpPanel, gbp_devhelp_panel, GTK_TYPE_BIN)
+G_DEFINE_TYPE (GbpDevhelpPanel, gbp_devhelp_panel, PNL_TYPE_DOCK_WIDGET)
 
 enum {
   PROP_0,
@@ -183,19 +184,15 @@ gbp_devhelp_panel_class_init (GbpDevhelpPanelClass *klass)
 static void
 gbp_devhelp_panel_init (GbpDevhelpPanel *self)
 {
+  g_object_set (self, "title", _("Documentation"), NULL);
 }
 
 void
 gbp_devhelp_panel_focus_search (GbpDevhelpPanel *self,
                                 const gchar     *keyword)
 {
-  IdeWorkbench *workbench;
-
   g_return_if_fail (GBP_IS_DEVHELP_PANEL (self));
 
-  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
-  ide_workbench_focus (workbench, GTK_WIDGET (self->sidebar));
-
   dh_sidebar_set_search_focus (self->sidebar);
 
   if (keyword)
diff --git a/plugins/devhelp/gbp-devhelp-panel.h b/plugins/devhelp/gbp-devhelp-panel.h
index 40f6c4d..494bc7a 100644
--- a/plugins/devhelp/gbp-devhelp-panel.h
+++ b/plugins/devhelp/gbp-devhelp-panel.h
@@ -19,13 +19,13 @@
 #ifndef GBP_DEVHELP_PANEL_H
 #define GBP_DEVHELP_PANEL_H
 
-#include <gtk/gtk.h>
+#include <pnl.h>
 
 G_BEGIN_DECLS
 
 #define GBP_TYPE_DEVHELP_PANEL (gbp_devhelp_panel_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpDevhelpPanel, gbp_devhelp_panel, GBP, DEVHELP_PANEL, GtkBin)
+G_DECLARE_FINAL_TYPE (GbpDevhelpPanel, gbp_devhelp_panel, GBP, DEVHELP_PANEL, PnlDockWidget)
 
 void gbp_devhelp_panel_set_uri      (GbpDevhelpPanel *self,
                                      const gchar     *uri);
diff --git a/plugins/devhelp/gbp-devhelp-search-provider.c b/plugins/devhelp/gbp-devhelp-search-provider.c
index cc8dbf5..e1e18b5 100644
--- a/plugins/devhelp/gbp-devhelp-search-provider.c
+++ b/plugins/devhelp/gbp-devhelp-search-provider.c
@@ -174,7 +174,7 @@ gbp_devhelp_search_provider_activate (IdeSearchProvider *provider,
   editor = ide_workbench_get_perspective_by_name (IDE_WORKBENCH (toplevel), "editor");
   g_assert (editor != NULL);
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (editor));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (editor));
   g_assert (pane != NULL);
 
   panel = ide_widget_find_child_typed (pane, GBP_TYPE_DEVHELP_PANEL);
diff --git a/plugins/devhelp/gbp-devhelp-workbench-addin.c b/plugins/devhelp/gbp-devhelp-workbench-addin.c
index 2844720..f21ffc8 100644
--- a/plugins/devhelp/gbp-devhelp-workbench-addin.c
+++ b/plugins/devhelp/gbp-devhelp-workbench-addin.c
@@ -79,15 +79,15 @@ gbp_devhelp_workbench_addin_load (IdeWorkbenchAddin *addin,
   perspective = ide_workbench_get_perspective_by_name (workbench, "editor");
   g_assert (IDE_IS_LAYOUT (perspective));
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (perspective));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (perspective));
   g_assert (IDE_IS_LAYOUT_PANE (pane));
 
   self->panel = g_object_new (GBP_TYPE_DEVHELP_PANEL,
                               "book-manager", self->books,
+                              "expand", TRUE,
                               "visible", TRUE,
                               NULL);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel),
-                            _("Documentation"), "devhelp-symbolic");
+  gtk_container_add (GTK_CONTAINER (pane), GTK_WIDGET (self->panel));
 
   action = g_simple_action_new ("focus-devhelp-search", NULL);
   g_signal_connect_object (action, "activate", G_CALLBACK (focus_devhelp_search), self, 0);
@@ -114,10 +114,11 @@ gbp_devhelp_workbench_addin_unload (IdeWorkbenchAddin *addin,
   perspective = ide_workbench_get_perspective_by_name (workbench, "editor");
   g_assert (IDE_IS_LAYOUT (perspective));
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (perspective));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (perspective));
   g_assert (IDE_IS_LAYOUT_PANE (pane));
 
-  ide_layout_pane_remove_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel));
+  gtk_widget_destroy (GTK_WIDGET (self->panel));
+  self->panel = NULL;
 
   g_action_map_remove_action (G_ACTION_MAP (workbench), "focus-devhelp-search");
 
diff --git a/plugins/project-tree/gb-project-tree-addin.c b/plugins/project-tree/gb-project-tree-addin.c
index 359a6dd..4a7eede 100644
--- a/plugins/project-tree/gb-project-tree-addin.c
+++ b/plugins/project-tree/gb-project-tree-addin.c
@@ -27,8 +27,10 @@ static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
 
 struct _GbProjectTreeAddin
 {
-  GObject  jparent_instance;
-  IdeTree *tree;
+  GObject    parent_instance;
+
+  IdeTree   *tree;
+  GtkWidget *panel;
 };
 
 G_DEFINE_TYPE_EXTENDED (GbProjectTreeAddin, gb_project_tree_addin, G_TYPE_OBJECT, 0,
@@ -59,12 +61,10 @@ gb_project_tree_addin_grid_empty (GbProjectTreeAddin *self,
   layout = gtk_widget_get_ancestor (GTK_WIDGET (grid), IDE_TYPE_LAYOUT);
   g_assert (layout != NULL);
 
-  pane = ide_layout_get_left_pane (IDE_LAYOUT (layout));
+  pane = pnl_dock_bin_get_left_edge (PNL_DOCK_BIN (layout));
   g_assert (pane != NULL);
 
-  gtk_container_child_set (GTK_CONTAINER (layout), GTK_WIDGET (pane),
-                           "reveal", TRUE,
-                           NULL);
+  pnl_dock_revealer_set_reveal_child (PNL_DOCK_REVEALER (pane), TRUE);
 }
 
 static void
@@ -84,10 +84,10 @@ gb_project_tree_addin_load (IdeWorkbenchAddin *addin,
   editor = ide_workbench_get_perspective_by_name (workbench, "editor");
   g_assert (editor != NULL);
 
-  pane = ide_layout_get_left_pane (IDE_LAYOUT (editor));
+  pane = pnl_dock_bin_get_left_edge (PNL_DOCK_BIN (editor));
   g_assert (pane != NULL);
 
-  content = ide_layout_get_content_pane (IDE_LAYOUT (editor));
+  content = pnl_dock_bin_get_center_widget (PNL_DOCK_BIN (editor));
   g_assert (content != NULL);
 
   grid = ide_widget_find_child_typed (content, IDE_TYPE_LAYOUT_GRID);
@@ -108,8 +108,13 @@ gb_project_tree_addin_load (IdeWorkbenchAddin *addin,
                              NULL);
   gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (self->tree));
 
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), scroller,
-                            _("Project"), "folder-symbolic");
+  self->panel = g_object_new (PNL_TYPE_DOCK_WIDGET,
+                              "expand", TRUE,
+                              "title", _("Project"),
+                              "visible", TRUE,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (self->panel), GTK_WIDGET (scroller));
+  gtk_container_add (GTK_CONTAINER (pane), GTK_WIDGET (self->panel));
 
   ide_widget_set_context_handler (self->tree, gb_project_tree_addin_context_set);
 
@@ -121,20 +126,12 @@ gb_project_tree_addin_unload (IdeWorkbenchAddin *addin,
                               IdeWorkbench      *workbench)
 {
   GbProjectTreeAddin *self = (GbProjectTreeAddin *)addin;
-  IdePerspective *editor;
-  GtkWidget *pane;
 
   g_assert (IDE_IS_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_WORKBENCH (workbench));
 
-  editor = ide_workbench_get_perspective_by_name (workbench, "editor");
-  g_assert (editor != NULL);
-
-  pane = ide_layout_get_left_pane (IDE_LAYOUT (editor));
-  g_assert (pane != NULL);
-
-  ide_layout_pane_remove_page (IDE_LAYOUT_PANE (pane),
-                               gtk_widget_get_parent (GTK_WIDGET (self->tree)));
+  gtk_widget_destroy (self->panel);
+  self->panel = NULL;
 }
 
 static void
diff --git a/plugins/symbol-tree/symbol-tree-panel.c b/plugins/symbol-tree/symbol-tree-panel.c
index ae1c093..8145fd4 100644
--- a/plugins/symbol-tree/symbol-tree-panel.c
+++ b/plugins/symbol-tree/symbol-tree-panel.c
@@ -34,7 +34,7 @@
 
 struct _SymbolTreePanel
 {
-  GtkBox parent_instance;
+  PnlDockWidget   parent_instance;
 
   GCancellable   *cancellable;
   EggTaskCache   *symbols_cache;
@@ -47,7 +47,7 @@ struct _SymbolTreePanel
   guint           refresh_tree_timeout;
 };
 
-G_DEFINE_TYPE (SymbolTreePanel, symbol_tree_panel, GTK_TYPE_BOX)
+G_DEFINE_TYPE (SymbolTreePanel, symbol_tree_panel, PNL_TYPE_DOCK_WIDGET)
 
 static void refresh_tree (SymbolTreePanel *self);
 
@@ -331,7 +331,8 @@ symbol_tree_panel_init (SymbolTreePanel *self)
                                             NULL);
 
   gtk_widget_init_template (GTK_WIDGET (self));
-  gtk_widget_show (GTK_WIDGET (self));
+
+  g_object_set (self, "title", _("Symbols"), NULL);
 
   root = ide_tree_node_new ();
   ide_tree_set_root (self->tree, root);
diff --git a/plugins/symbol-tree/symbol-tree-panel.h b/plugins/symbol-tree/symbol-tree-panel.h
index 0a6599c..128fb99 100644
--- a/plugins/symbol-tree/symbol-tree-panel.h
+++ b/plugins/symbol-tree/symbol-tree-panel.h
@@ -19,13 +19,13 @@
 #ifndef SYMBOL_TREE_PANEL_H
 #define SYMBOL_TREE_PANEL_H
 
-#include <gtk/gtk.h>
+#include <ide.h>
 
 G_BEGIN_DECLS
 
 #define SYMBOL_TYPE_TREE_PANEL (symbol_tree_panel_get_type())
 
-G_DECLARE_FINAL_TYPE (SymbolTreePanel, symbol_tree_panel, SYMBOL, TREE_PANEL, GtkBox)
+G_DECLARE_FINAL_TYPE (SymbolTreePanel, symbol_tree_panel, SYMBOL, TREE_PANEL, PnlDockWidget)
 
 void symbol_tree_panel_reset (SymbolTreePanel *self);
 
diff --git a/plugins/symbol-tree/symbol-tree-panel.ui b/plugins/symbol-tree/symbol-tree-panel.ui
index e7f0c3a..c6f3050 100644
--- a/plugins/symbol-tree/symbol-tree-panel.ui
+++ b/plugins/symbol-tree/symbol-tree-panel.ui
@@ -1,25 +1,30 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.16 -->
-  <template class="SymbolTreePanel" parent="GtkBox">
+  <template class="SymbolTreePanel" parent="PnlDockWidget">
     <property name="vexpand">true</property>
     <property name="visible">true</property>
-    <property name="orientation">vertical</property>
     <child>
-      <object class="GtkSearchEntry" id="search_entry">
-        <property name="visible">true</property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkScrolledWindow">
-        <property name="expand">true</property>
+      <object class="GtkBox">
         <property name="visible">true</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkSearchEntry" id="search_entry">
+            <property name="visible">true</property>
+          </object>
+        </child>
         <child>
-          <object class="IdeTree" id="tree">
-            <property name="activate-on-single-click">true</property>
-            <property name="headers-visible">false</property>
-            <property name="show-icons">true</property>
+          <object class="GtkScrolledWindow">
+            <property name="expand">true</property>
             <property name="visible">true</property>
+            <child>
+              <object class="IdeTree" id="tree">
+                <property name="activate-on-single-click">true</property>
+                <property name="headers-visible">false</property>
+                <property name="show-icons">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
           </object>
         </child>
       </object>
diff --git a/plugins/symbol-tree/symbol-tree.c b/plugins/symbol-tree/symbol-tree.c
index 10c3ef2..785521a 100644
--- a/plugins/symbol-tree/symbol-tree.c
+++ b/plugins/symbol-tree/symbol-tree.c
@@ -74,15 +74,13 @@ symbol_tree_load (IdeWorkbenchAddin *addin,
                            self,
                            G_CONNECT_SWAPPED);
 
-  right_pane = ide_layout_get_right_pane (IDE_LAYOUT (perspective));
+  right_pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (perspective));
   g_assert (right_pane != NULL);
 
-  self->panel = g_object_new (SYMBOL_TYPE_TREE_PANEL, NULL);
-
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (right_pane),
-                            GTK_WIDGET (self->panel),
-                            _("Symbols"),
-                            "lang-function-symbolic");
+  self->panel = g_object_new (SYMBOL_TYPE_TREE_PANEL,
+                              "visible", TRUE,
+                              NULL);
+  gtk_container_add (GTK_CONTAINER (right_pane), GTK_WIDGET (self->panel));
 
   gtk_container_child_set (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (self->panel))),
                            GTK_WIDGET (self->panel),
@@ -106,10 +104,11 @@ symbol_tree_unload (IdeWorkbenchAddin *addin,
   perspective = ide_workbench_get_perspective_by_name (workbench, "editor");
   g_assert (IDE_IS_LAYOUT (perspective));
 
-  pane = ide_layout_get_right_pane (IDE_LAYOUT (perspective));
+  pane = pnl_dock_bin_get_right_edge (PNL_DOCK_BIN (perspective));
   g_assert (IDE_IS_LAYOUT_PANE (pane));
 
-  ide_layout_pane_remove_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel));
+  gtk_widget_destroy (GTK_WIDGET (self->panel));
+  self->panel = NULL;
 }
 
 
diff --git a/plugins/sysmon/gb-sysmon-addin.c b/plugins/sysmon/gb-sysmon-addin.c
index 5a93899..b41710e 100644
--- a/plugins/sysmon/gb-sysmon-addin.c
+++ b/plugins/sysmon/gb-sysmon-addin.c
@@ -53,23 +53,29 @@ gb_sysmon_addin_load (IdeWorkbenchAddin *addin,
   g_assert (editor != NULL);
   g_assert (IDE_IS_LAYOUT (editor));
 
-  pane = ide_layout_get_bottom_pane (IDE_LAYOUT (editor));
+  pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (editor));
   panel = g_object_new (GB_TYPE_SYSMON_PANEL,
+                        "expand", TRUE,
                         "visible", TRUE,
                         NULL);
   ide_set_weak_pointer (&self->panel, panel);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane),
-                            GTK_WIDGET (panel),
-                            _("System Monitor"),
-                            "utilities-system-monitor-symbolic");
+  gtk_container_add (GTK_CONTAINER (pane), GTK_WIDGET (panel));
 }
 
 static void
 gb_sysmon_addin_unload (IdeWorkbenchAddin *addin,
                         IdeWorkbench      *workbench)
 {
+  GbSysmonAddin *self = (GbSysmonAddin *)addin;
+
   g_assert (GB_IS_SYSMON_ADDIN (addin));
   g_assert (IDE_IS_WORKBENCH (workbench));
+
+  if (self->panel != NULL)
+    {
+      gtk_widget_destroy (self->panel);
+      ide_clear_weak_pointer (&self->panel);
+    }
 }
 
 static void
diff --git a/plugins/sysmon/gb-sysmon-panel.c b/plugins/sysmon/gb-sysmon-panel.c
index dd25afe..d8b25df 100644
--- a/plugins/sysmon/gb-sysmon-panel.c
+++ b/plugins/sysmon/gb-sysmon-panel.c
@@ -16,18 +16,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <glib/gi18n.h>
 #include <realtime-graphs.h>
 
 #include "gb-sysmon-panel.h"
 
 struct _GbSysmonPanel
 {
-  GtkBox      parent_instance;
-  RgCpuGraph *cpu_graph;
+  PnlDockWidget  parent_instance;
+  RgCpuGraph    *cpu_graph;
 };
 
-G_DEFINE_TYPE (GbSysmonPanel, gb_sysmon_panel, GTK_TYPE_BOX)
+G_DEFINE_TYPE (GbSysmonPanel, gb_sysmon_panel, PNL_TYPE_DOCK_WIDGET)
 
 static void
 gb_sysmon_panel_finalize (GObject *object)
diff --git a/plugins/sysmon/gb-sysmon-panel.h b/plugins/sysmon/gb-sysmon-panel.h
index 195e77f..02d5950 100644
--- a/plugins/sysmon/gb-sysmon-panel.h
+++ b/plugins/sysmon/gb-sysmon-panel.h
@@ -19,13 +19,13 @@
 #ifndef GB_SYSMON_PANEL_H
 #define GB_SYSMON_PANEL_H
 
-#include <gtk/gtk.h>
+#include <ide.h>
 
 G_BEGIN_DECLS
 
 #define GB_TYPE_SYSMON_PANEL (gb_sysmon_panel_get_type())
 
-G_DECLARE_FINAL_TYPE (GbSysmonPanel, gb_sysmon_panel, GB, SYSMON_PANEL, GtkBox)
+G_DECLARE_FINAL_TYPE (GbSysmonPanel, gb_sysmon_panel, GB, SYSMON_PANEL, PnlDockWidget)
 
 G_END_DECLS
 
diff --git a/plugins/sysmon/gb-sysmon-panel.ui b/plugins/sysmon/gb-sysmon-panel.ui
index 196b564..eed139e 100644
--- a/plugins/sysmon/gb-sysmon-panel.ui
+++ b/plugins/sysmon/gb-sysmon-panel.ui
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.16 -->
-  <template class="GbSysmonPanel" parent="GtkBox">
+  <template class="GbSysmonPanel" parent="PnlDockWidget">
+    <property name="title" translatable="yes">System Monitor</property>
     <property name="visible">true</property>
     <child>
       <object class="RgCpuGraph" id="cpu_graph">
diff --git a/plugins/terminal/gb-terminal-workbench-addin.c b/plugins/terminal/gb-terminal-workbench-addin.c
index 2845e18..8b0a412 100644
--- a/plugins/terminal/gb-terminal-workbench-addin.c
+++ b/plugins/terminal/gb-terminal-workbench-addin.c
@@ -27,7 +27,9 @@ struct _GbTerminalWorkbenchAddin
   GObject         parent_instance;
 
   IdeWorkbench   *workbench;
+
   GbTerminalView *panel_terminal;
+  GtkWidget      *panel_dock_widget;
 };
 
 static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
@@ -83,21 +85,28 @@ gb_terminal_workbench_addin_load (IdeWorkbenchAddin *addin,
 
   if (self->panel_terminal == NULL)
     {
+      self->panel_dock_widget = g_object_new (PNL_TYPE_DOCK_WIDGET,
+                                              "expand", TRUE,
+                                              "title", _("Terminal"),
+                                              "visible", TRUE,
+                                              NULL);
       self->panel_terminal = g_object_new (GB_TYPE_TERMINAL_VIEW,
                                            "visible", TRUE,
                                            NULL);
+      gtk_container_add (GTK_CONTAINER (self->panel_dock_widget),
+                         GTK_WIDGET (self->panel_terminal));
+
       g_object_add_weak_pointer (G_OBJECT (self->panel_terminal),
                                  (gpointer *)&self->panel_terminal);
+      g_object_add_weak_pointer (G_OBJECT (self->panel_dock_widget),
+                                 (gpointer *)&self->panel_dock_widget);
     }
 
   perspective = ide_workbench_get_perspective_by_name (workbench, "editor");
   g_assert (IDE_IS_LAYOUT (perspective));
 
-  bottom_pane = ide_layout_get_bottom_pane (IDE_LAYOUT (perspective));
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (bottom_pane),
-                            GTK_WIDGET (self->panel_terminal),
-                            _("Terminal"),
-                            "utilities-terminal-symbolic");
+  bottom_pane = pnl_dock_bin_get_bottom_edge (PNL_DOCK_BIN (perspective));
+  gtk_container_add (GTK_CONTAINER (bottom_pane), GTK_WIDGET (self->panel_dock_widget));
 }
 
 static void
@@ -110,12 +119,10 @@ gb_terminal_workbench_addin_unload (IdeWorkbenchAddin *addin,
 
   g_action_map_remove_action (G_ACTION_MAP (self->workbench), "new-terminal");
 
-  if (self->panel_terminal != NULL)
+  if (self->panel_dock_widget != NULL)
     {
-      GtkWidget *parent;
-
-      parent = gtk_widget_get_parent (GTK_WIDGET (self->panel_terminal));
-      gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (self->panel_terminal));
+      gtk_widget_destroy (self->panel_dock_widget);
+      self->panel_dock_widget = NULL;
     }
 }
 
diff --git a/plugins/todo/todo_plugin/__init__.py b/plugins/todo/todo_plugin/__init__.py
index b95a9dd..18e461a 100644
--- a/plugins/todo/todo_plugin/__init__.py
+++ b/plugins/todo/todo_plugin/__init__.py
@@ -27,6 +27,7 @@ from gi.repository import Gio
 from gi.repository import GLib
 from gi.repository import GObject
 from gi.repository import Gtk
+from gi.repository import Pnl
 
 from gettext import gettext as _
 import re
@@ -56,8 +57,8 @@ class TodoWorkbenchAddin(GObject.Object, Ide.WorkbenchAddin):
         # Create our panel to display results
         self.panel = TodoPanel(workdir, visible=True)
         editor = workbench.get_perspective_by_name('editor')
-        pane = editor.get_bottom_pane()
-        pane.add_page(self.panel, _("Todo"), None)
+        pane = editor.get_bottom_edge()
+        pane.add(self.panel)
 
         # Mine the directory in a background thread
         self.mine(workdir)
@@ -171,10 +172,13 @@ class TodoItem(GObject.Object):
             return msg[:msg.index('\n')].strip()
         return msg.strip()
 
-class TodoPanel(Gtk.Bin):
+class TodoPanel(Pnl.DockWidget):
     def __init__(self, basedir, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
+        self.props.title = _("Todo")
+        self.props.expand = True
+
         self.basedir = basedir
         self.model = Gtk.ListStore(TodoItem)
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0b11090..06a092f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -119,3 +119,8 @@ plugins/terminal/gb-terminal-workbench-addin.c
 [type: gettext/glade]plugins/terminal/gtk/menus.ui
 plugins/todo/todo_plugin/__init__.py
 plugins/vala-pack/ide-vala-preferences-addin.vala
+contrib/pnl/pnl-animation.c
+plugins/build-tools/gbp-build-log-panel.c
+plugins/build-tools/gbp-build-panel.c
+plugins/devhelp/gbp-devhelp-panel.c
+plugins/sysmon/gb-sysmon-panel.ui
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a09c4f7..a9810f2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -61,7 +61,7 @@ tests_ldflags = \
 misc_programs =
 
 TESTS_ENVIRONMENT= \
-       
GI_TYPELIB_PATH="$(top_builddir)/libide:$(top_builddir)/contrib/tmpl:$(top_builddir)/contrib/egg:$(GI_TYPELIB_PATH)"
 \
+       
GI_TYPELIB_PATH="$(top_builddir)/libide:$(top_builddir)/contrib/tmpl:$(top_builddir)/contrib/egg:$(top_builddir)/contrib/pnl:$(GI_TYPELIB_PATH)"
 \
        GB_IN_TREE_PLUGINS=1 \
        G_TEST_SRCDIR="$(abs_srcdir)" \
        G_TEST_BUILDDIR="$(abs_builddir)" \


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