[gnome-builder/wip/chergert/layout: 13/118] more work on ripping up the editor



commit 8fc005b6e03a515dcf489a91724d46d40df70cb8
Author: Christian Hergert <chergert redhat com>
Date:   Tue Jun 27 16:02:14 2017 -0700

    more work on ripping up the editor

 TODO.txt                                           |   32 ++
 data/gtk/menus.ui                                  |   82 +--
 data/themes/Adwaita-shared.css                     |   91 +---
 data/themes/shared.css                             |    7 +-
 data/themes/shared/shared-layout.css               |   80 +++
 libide/editor/ide-editor-perspective.c             |  274 ++++++++--
 libide/editor/ide-editor-perspective.h             |   13 +-
 libide/editor/ide-editor-search-bar.c              |  590 ++++++++++++++++++++
 libide/editor/ide-editor-search-bar.h              |   40 ++
 libide/editor/ide-editor-search-bar.ui             |  276 +++++++++
 libide/editor/ide-editor-view-settings.c           |   10 +-
 libide/editor/ide-editor-view.c                    |  127 ++++-
 libide/editor/ide-editor-view.h                    |    4 +-
 libide/editor/ide-editor-view.ui                   |   14 +
 libide/layout/ide-layout-grid-actions.c            |   71 ---
 libide/layout/ide-layout-grid.c                    |  129 +++++-
 libide/layout/ide-layout-grid.h                    |    5 +
 libide/layout/ide-layout-stack-header.c            |    1 +
 libide/layout/ide-layout-stack-header.h            |    2 +-
 libide/layout/ide-layout-stack.c                   |   23 +
 libide/layout/ide-layout-stack.h                   |    3 +
 libide/layout/ide-layout-view.c                    |    2 +
 libide/layout/ide-layout.c                         |  167 +------
 libide/layout/ide-layout.h                         |   16 +-
 libide/libide.gresource.xml                        |    2 +
 libide/meson.build                                 |    5 +-
 plugins/beautifier/gb-beautifier-workbench-addin.c |    4 +-
 plugins/color-picker/gb-color-picker-prefs.c       |    2 +-
 .../gb-color-picker-workbench-addin-private.h      |    2 +-
 .../color-picker/gb-color-picker-workbench-addin.c |   20 +-
 plugins/comment-code/gbp-comment-code-view-addin.c |    4 +-
 plugins/project-tree/gb-project-tree-actions.c     |    2 +-
 .../project-tree/gb-project-tree-editor-addin.c    |    2 +-
 .../gbp-quick-highlight-view-addin.c               |    8 +-
 plugins/retab/gbp-retab-view-addin.c               |    4 +-
 plugins/symbol-tree/symbol-tree-panel.c            |    2 +-
 plugins/symbol-tree/symbol-tree.c                  |    8 +-
 37 files changed, 1649 insertions(+), 475 deletions(-)
---
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..ada637d
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,32 @@
+
+ - Search entry context menu (use a GMenu and merging this time)
+   Might also require DzlWidgetActionGroup.
+
+ - Enter, Up, and Down accelerators for search/replace entries.
+
+ - perspective focus_location
+
+ - other perspective interface methods
+
+ - can we make perspectives the plugin interface?
+
+ - Check if buffer is already in visible stack before adding new view
+
+ - view-added/removed/etc on IdeLayoutGrid
+
+ - Remove get_right_edge() etc, switch with "classification" for panels
+
+ - Ability to disable plugin by default
+
+ - Plugins to fix for API
+   - devhelp
+   - command bar / vim
+   - project-tree
+   - terminal
+   - symbol-tree
+
+ - Remove accelerators from perspective, use shortcut engine
+
+ - Move panel position settings to org.gnome.builder.editor maybe
+
+
diff --git a/data/gtk/menus.ui b/data/gtk/menus.ui
index 189b3ec..2283aed 100644
--- a/data/gtk/menus.ui
+++ b/data/gtk/menus.ui
@@ -61,27 +61,6 @@
         <attribute name="verb-icon">builder-view-right-pane-symbolic</attribute>
       </item>
     </section>
-<!--
-    <section>
-      <attribute name="id">juntion-section</attribute>
-      <attribute name="display-hint">horizontal-buttons</attribute>
-      <item>
-        <attribute name="label" translatable="yes">_Reload</attribute>
-        <attribute name="action">win.revert</attribute>
-        <attribute name="verb-icon">view-refresh-symbolic</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Print…</attribute>
-        <attribute name="action">win.print</attribute>
-        <attribute name="verb-icon">printer-symbolic</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Fullscreen</attribute>
-        <attribute name="action">win.fullscreen</attribute>
-        <attribute name="verb-icon">view-fullscreen-symbolic</attribute>
-      </item>
-    </section>
--->
     <section id="gear-menu-new-section">
       <item>
         <attribute name="label" translatable="yes">_New File</attribute>
@@ -262,61 +241,50 @@
       </item>
     </section>
   </menu>
-  <menu id="ide-layout-stack-menu">
-    <section id="ide-layout-stack-menu-splits-section">
-      <attribute name="display-hint">horizontal-buttons</attribute>
-      <attribute name="label" translatable="yes">Split</attribute>
+  <menu id="ide-layout-stack-frame-menu">
+    <section id="ide-layout-stack-frame-section">
+      <attribute name="label" translatable="yes">Frame</attribute>
       <item>
-        <attribute name="label" translatable="yes">Split Left</attribute>
-        <attribute name="action">view-stack.split-left</attribute>
-        <attribute name="verb-icon">builder-split-tab-left-symbolic</attribute>
+        <attribute name="label" translatable="yes">Move Left</attribute>
+        <attribute name="action">layoutstack.move-left</attribute>
       </item>
       <item>
-        <attribute name="label" translatable="yes">Split Right</attribute>
-        <attribute name="action">view-stack.split-right</attribute>
-        <attribute name="verb-icon">builder-split-tab-right-symbolic</attribute>
+        <attribute name="label" translatable="yes">Move Right</attribute>
+        <attribute name="action">layoutstack.move-right</attribute>
       </item>
       <item>
-        <attribute name="label" translatable="yes">Split Down</attribute>
-        <attribute name="action">view-stack.split-down</attribute>
-        <attribute name="verb-icon">builder-split-tab-symbolic</attribute>
+        <attribute name="action">layoutstack.split-view</attribute>
+        <attribute name="label" translatable="yes">Split</attribute>
       </item>
-    </section>
-    <section id="ide-layout-stack-menu-move-section">
-      <attribute name="display-hint">horizontal-buttons</attribute>
-      <attribute name="label" translatable="yes">Move</attribute>
       <item>
-        <attribute name="label" translatable="yes">Move Left</attribute>
-        <attribute name="action">view-stack.move-left</attribute>
-        <attribute name="verb-icon">builder-move-left-symbolic</attribute>
+        <attribute name="action">layoutgrid.close-stack</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
       </item>
+    </section>
+  </menu>
+  <menu id="ide-editor-view-document-menu">
+    <section id="editor-document-section">
+      <attribute name="label" translatable="yes">Document</attribute>
       <item>
-        <attribute name="label" translatable="yes">Move Right</attribute>
-        <attribute name="action">view-stack.move-right</attribute>
-        <attribute name="verb-icon">builder-move-right-symbolic</attribute>
+        <attribute name="label" translatable="yes">Open in New Frame</attribute>
+        <attribute name="action">layoutstack.open-in-new-frame</attribute>
       </item>
-    </section>
-    <section id="ide-layout-stack-menu-preview-section"/>
-    <section id="ide-layout-stack-menu-save-section">
       <item>
-        <attribute name="label" translatable="yes">_Save</attribute>
-        <attribute name="action">view.save</attribute>
+        <attribute name="label" translatable="yes">Open Preview</attribute>
       </item>
       <item>
-        <attribute name="label" translatable="yes">_Save As</attribute>
-        <attribute name="action">view.save-as</attribute>
+        <attribute name="label" translatable="yes">Print…</attribute>
       </item>
     </section>
-    <section id="ide-layout-stack-menu-print-section">
+    <section id="editor-document-preferences-section">
       <item>
-        <attribute name="label" translatable="yes">_Print</attribute>
-        <attribute name="action">view.print</attribute>
+        <attribute name="label" translatable="yes">Document Preferences</attribute>
       </item>
     </section>
-    <section id="ide-layout-stack-menu-close-section">
+    <section id="editor-document-close-section">
       <item>
-        <attribute name="label" translatable="yes">_Close</attribute>
-        <attribute name="action">view-stack.close</attribute>
+        <attribute name="action">layoutstack.close-view</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
       </item>
     </section>
   </menu>
diff --git a/data/themes/Adwaita-shared.css b/data/themes/Adwaita-shared.css
index d90bdd8..408611b 100644
--- a/data/themes/Adwaita-shared.css
+++ b/data/themes/Adwaita-shared.css
@@ -1,95 +1,8 @@
 @import url("resource:///org/gnome/builder/themes/shared.css");
 
-
-/*
- * Layout tab and tab bar tweaks
- *
- * The following makes the layout stack header look similar to a tab bar.
- */
-layouttabbar > box > button {
-  opacity: 0.5;
-}
-layouttabbar > box > button:hover {
-  opacity: 0.75;
-}
-layouttabbar > box > button:active {
-  opacity: 1;
-}
-layouttabbar button {
-  border: none;
-  box-shadow: none;
-  background: transparent;
-}
-layouttab label {
-  padding: 5px;
-}
-layouttab {
-  background-color: @theme_bg_color;
-
-  border-bottom: 3px;
-  border-bottom-style: solid;
-
-  border-right: 1px solid alpha(@borders, 0.75);
-  border-left: 1px solid alpha(@borders, 0.75);
-
-  border-bottom-left-radius: 3px;
-  border-bottom-right-radius: 3px;
-}
-layouttab:backdrop {
-  border-right-color: alpha(@borders, 0.75);
-  border-left-color: alpha(@borders, 0.75);
-  box-shadow: none;
-}
-layouttabbar:backdrop {
-  box-shadow: none;
-}
-layouttab separator.vertical {
-  margin-top: 7px;
-  margin-bottom: 7px;
-  opacity: 0.75;
+idelayoutstackheader > button:last-child {
+  margin-right: 3px;
 }
-layouttab separator.vertical:backdrop {
-  opacity: 0.3;
-}
-layouttab button:disabled,
-layouttab button {
-  background: none;
-  border: none;
-  box-shadow: none;
-  padding-left: 4px;
-  padding-right: 4px;
-}
-
-
-/*
- * Close button styling for layouttab.
- */
-layouttab > box > button:last-child image {
-  color: @theme_fg_color;
-  opacity: 0.3;
-  margin: 2px;
-  border: 1px solid transparent;
-  border-radius: 3px;
-}
-layouttab > box > button:last-child:hover image {
-  opacity: .75;
-  transition-duration: 250ms;
-  border: 1px solid @borders;
-}
-layouttab > box > button:last-child:active image {
-  opacity: .8;
-  background-image: linear-gradient(shade(@theme_bg_color, 0.9), @theme_bg_color);
-}
-layouttab > box > button:last-child:backdrop image {
-  opacity: .1;
-}
-
-
-layout {
-  border: 1px solid alpha(@borders, 0.75);
-  -PnlDockBin-handle-size: 1;
-}
-
 
 entry.search-missing {
   background-color: #cc0000;
diff --git a/data/themes/shared.css b/data/themes/shared.css
index 746f586..d084ec0 100644
--- a/data/themes/shared.css
+++ b/data/themes/shared.css
@@ -1,3 +1,5 @@
+@import url("resource:///org/gnome/builder/themes/shared/shared-layout.css");
+
 /* work around some gtk padding issue */
 filechooser actionbar button.combo {
   padding: 0;
@@ -9,7 +11,7 @@ filechooser actionbar button.combo {
 }
 
 /* styling for editor search */
-frame.gb-search-frame {
+ideeditorsearchbar {
   background-image: linear-gradient(shade(@theme_bg_color,1.05), @theme_bg_color);
   padding: 6px;
   border-style: solid;
@@ -20,9 +22,6 @@ frame.gb-search-frame {
   border-bottom-left-radius: 5px;
   border-bottom-right-radius: 5px;
 }
-frame.gb-search-frame border {
-  border: none;
-}
 
 /* styling for the search and spellchecker list close button */
 frame.gb-search-frame > box > grid:first-child > button.close:disabled,
diff --git a/data/themes/shared/shared-layout.css b/data/themes/shared/shared-layout.css
new file mode 100644
index 0000000..7eeb6f5
--- /dev/null
+++ b/data/themes/shared/shared-layout.css
@@ -0,0 +1,80 @@
+idelayoutstackheader {
+  min-height: 26px;
+}
+
+idelayoutstackheader button {
+  border: none;
+  border-radius: 0;
+  background: transparent;
+  box-shadow: none;
+  padding: 0;
+  margin: 0;
+}
+
+idelayoutstackheader button:not(:disabled) image {
+  opacity: 0.55;
+}
+
+idelayoutstackheader button:checked image,
+idelayoutstackheader button:not(:disabled):hover image {
+  opacity: 1;
+}
+
+idelayoutstackheader button:checked,
+idelayoutstackheader button:hover {
+  background: shade(@theme_bg_color, 0.9);
+}
+
+idelayoutstackheader button:active {
+  background: shade(@theme_bg_color, 0.85);
+}
+
+idelayoutstackheader > button {
+  padding-left: 12px;
+  padding-right: 12px;
+}
+
+idelayoutstackheader * button:first-child > image,
+idelayoutstackheader * button:last-child:dir(rtl) > image {
+  padding-left: 12px;
+  padding-right: 10px;
+}
+
+idelayoutstackheader * button:first-child:dir(rtl) > image,
+idelayoutstackheader * button:last-child > image {
+  padding-right: 12px;
+  padding-left: 9px;
+}
+
+idelayoutgrid.handle {
+  border: 1px solid @borders;
+}
+
+popover.symbols-button {
+  padding: 12px;
+}
+
+popover.symbols-button treeview {
+  background: transparent;
+  color: @theme_text_color;
+}
+
+popover.title-popover scrolledwindow {
+  min-width: 300px;
+}
+
+popover.title-popover list {
+  background: transparent;
+}
+
+popover.title-popover list row {
+  padding: 6px;
+}
+
+popover.title-popover list row button {
+  margin: 0;
+  padding: 0;
+  box-shadow: none;
+  border: none;
+  background: transparent;
+}
diff --git a/libide/editor/ide-editor-perspective.c b/libide/editor/ide-editor-perspective.c
index 42c44d6..4aa65b0 100644
--- a/libide/editor/ide-editor-perspective.c
+++ b/libide/editor/ide-editor-perspective.c
@@ -18,59 +18,30 @@
 
 #define G_LOG_DOMAIN "ide-editor-perspective"
 
-#include "ide-editor-perspective.h"
+#include <glib/gi18n.h>
+
+#include "editor/ide-editor-perspective.h"
+#include "editor/ide-editor-view.h"
+#include "workbench/ide-perspective.h"
 
 struct _IdeEditorPerspective
 {
   IdeLayout      parent_instance;
 
+  /* Template widgets */
   IdeLayoutGrid *grid;
 };
 
-enum {
-  PROP_0,
-  N_PROPS
-};
-
-G_DEFINE_TYPE (IdeEditorPerspective, ide_editor_perspective, IDE_TYPE_LAYOUT)
-
-static GParamSpec *properties [N_PROPS];
-
-static void
-ide_editor_perspective_get_property (GObject    *object,
-                                     guint       prop_id,
-                                     GValue     *value,
-                                     GParamSpec *pspec)
-{
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
+static void perspective_iface_init (IdePerspectiveInterface *iface);
 
-static void
-ide_editor_perspective_set_property (GObject      *object,
-                                     guint         prop_id,
-                                     const GValue *value,
-                                     GParamSpec   *pspec)
-{
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
+G_DEFINE_TYPE_WITH_CODE (IdeEditorPerspective, ide_editor_perspective, IDE_TYPE_LAYOUT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
 
 static void
 ide_editor_perspective_class_init (IdeEditorPerspectiveClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  object_class->get_property = ide_editor_perspective_get_property;
-  object_class->set_property = ide_editor_perspective_set_property;
-
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-editor-perspective.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeEditorPerspective, grid);
 }
@@ -78,6 +49,7 @@ ide_editor_perspective_class_init (IdeEditorPerspectiveClass *klass)
 static void
 ide_editor_perspective_init (IdeEditorPerspective *self)
 {
+  gtk_widget_init_template (GTK_WIDGET (self));
 }
 
 /**
@@ -98,3 +70,229 @@ ide_editor_perspective_get_grid (IdeEditorPerspective *self)
 
   return self->grid;
 }
+
+void
+ide_editor_perspective_focus_location (IdeEditorPerspective *self,
+                                       IdeSourceLocation    *location)
+{
+  g_return_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_return_if_fail (location != NULL);
+
+  /* TODO: */
+}
+
+void
+ide_editor_perspective_focus_buffer_in_current_stack (IdeEditorPerspective *self,
+                                                      IdeBuffer            *buffer)
+{
+  IdeLayoutStack *stack;
+  IdeEditorView *view;
+
+  g_return_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_return_if_fail (IDE_IS_BUFFER (buffer));
+
+  stack = ide_layout_grid_get_current_stack (self->grid);
+
+  /* TODO: Check if buffer is in stack */
+
+  view = g_object_new (IDE_TYPE_EDITOR_VIEW,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+
+  gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (view));
+}
+
+/**
+ * ide_editor_perspective_get_active_view:
+ * @self: a #IdeEditorPerspective
+ *
+ * Gets the active view for the perspective, or %NULL if there is not one.
+ *
+ * Returns: (nullable) (transfer none): An #IdeLayoutView or %NULL.
+ *
+ * Since: 3.26
+ */
+IdeLayoutView *
+ide_editor_perspective_get_active_view (IdeEditorPerspective *self)
+{
+  IdeLayoutStack *stack;
+
+  g_return_val_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self), NULL);
+
+  stack = ide_layout_grid_get_current_stack (self->grid);
+
+  return ide_layout_stack_get_visible_child (stack);
+}
+
+/**
+ * ide_editor_perspective_get_right_edge:
+ *
+ * Returns: (transfer none): A #GtkWidget
+ */
+GtkWidget *
+ide_editor_perspective_get_right_edge (IdeEditorPerspective *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self), NULL);
+  return dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self));
+}
+
+/**
+ * ide_editor_perspective_get_bottom_edge:
+ *
+ * Returns: (transfer none): A #GtkWidget
+ */
+GtkWidget *
+ide_editor_perspective_get_bottom_edge (IdeEditorPerspective *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_PERSPECTIVE (self), NULL);
+  return dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+}
+
+static void
+set_reveal_child_without_transition (DzlDockRevealer *revealer,
+                                     gboolean         reveal)
+{
+  DzlDockRevealerTransitionType type;
+
+  g_assert (DZL_IS_DOCK_REVEALER (revealer));
+
+  type = dzl_dock_revealer_get_transition_type (revealer);
+  dzl_dock_revealer_set_transition_type (revealer, DZL_DOCK_REVEALER_TRANSITION_TYPE_NONE);
+  dzl_dock_revealer_set_reveal_child (revealer, reveal);
+  dzl_dock_revealer_set_transition_type (revealer, type);
+}
+
+static void
+ide_editor_perspective_restore_panel_state (IdeEditorPerspective *self)
+{
+  g_autoptr(GSettings) settings = NULL;
+  GtkWidget *pane;
+  gboolean reveal;
+  guint position;
+
+  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+
+  /* TODO: This belongs in editor settings probably */
+
+  settings = g_settings_new ("org.gnome.builder.workbench");
+
+  pane = dzl_dock_bin_get_left_edge (DZL_DOCK_BIN (self));
+  reveal = g_settings_get_boolean (settings, "left-visible");
+  position = g_settings_get_int (settings, "left-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), reveal);
+
+  pane = dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self));
+  reveal = g_settings_get_boolean (settings, "right-visible");
+  position = g_settings_get_int (settings, "right-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), reveal);
+
+  pane = dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+  reveal = g_settings_get_boolean (settings, "bottom-visible");
+  position = g_settings_get_int (settings, "bottom-position");
+  dzl_dock_revealer_set_position (DZL_DOCK_REVEALER (pane), position);
+  set_reveal_child_without_transition (DZL_DOCK_REVEALER (pane), reveal);
+}
+
+static void
+ide_editor_perspective_save_panel_state (IdeEditorPerspective *self)
+{
+  g_autoptr(GSettings) settings = NULL;
+  GtkWidget *pane;
+  gboolean reveal;
+  guint position;
+
+  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+
+  /* TODO: possibly belongs in editor settings */
+  settings = g_settings_new ("org.gnome.builder.workbench");
+
+  pane = dzl_dock_bin_get_left_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "left-visible", reveal);
+  g_settings_set_int (settings, "left-position", position);
+
+  pane = dzl_dock_bin_get_right_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "right-visible", reveal);
+  g_settings_set_int (settings, "right-position", position);
+
+  pane = dzl_dock_bin_get_bottom_edge (DZL_DOCK_BIN (self));
+  position = dzl_dock_revealer_get_position (DZL_DOCK_REVEALER (pane));
+  reveal = dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (pane));
+  g_settings_set_boolean (settings, "bottom-visible", reveal);
+  g_settings_set_int (settings, "bottom-position", position);
+}
+
+static void
+ide_editor_perspective_views_foreach (IdePerspective *perspective,
+                                      GtkCallback     callback,
+                                      gpointer        user_data)
+{
+  IdeEditorPerspective *self = (IdeEditorPerspective *)perspective;
+
+  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+  g_assert (callback != NULL);
+
+  ide_layout_grid_foreach_view (self->grid, callback, user_data);
+}
+
+static gchar *
+ide_editor_perspective_get_id (IdePerspective *perspective)
+{
+  return g_strdup ("editor");
+}
+
+static gchar *
+ide_editor_perspective_get_icon_name (IdePerspective *perspective)
+{
+  return g_strdup ("builder-editor-symbolic");
+}
+
+static gchar *
+ide_editor_perspective_get_accelerator (IdePerspective *perspective)
+{
+  return g_strdup ("<Alt>1");
+}
+
+static gchar *
+ide_editor_perspective_get_title (IdePerspective *perspective)
+{
+  return g_strdup (_("Editor"));
+}
+
+static void
+ide_editor_perspective_restore_state (IdePerspective *perspective)
+{
+  IdeEditorPerspective *self = (IdeEditorPerspective *)perspective;
+  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+  ide_editor_perspective_restore_panel_state (self);
+}
+
+static gboolean
+ide_editor_perspective_agree_to_shutdown (IdePerspective *perspective)
+{
+  IdeEditorPerspective *self = (IdeEditorPerspective *)perspective;
+
+  g_assert (IDE_IS_EDITOR_PERSPECTIVE (self));
+
+  ide_editor_perspective_save_panel_state (self);
+
+  return TRUE;
+}
+
+static void
+perspective_iface_init (IdePerspectiveInterface *iface)
+{
+  iface->agree_to_shutdown = ide_editor_perspective_agree_to_shutdown;
+  iface->get_accelerator = ide_editor_perspective_get_accelerator;
+  iface->get_icon_name = ide_editor_perspective_get_icon_name;
+  iface->get_id = ide_editor_perspective_get_id;
+  iface->get_title = ide_editor_perspective_get_title;
+  iface->restore_state = ide_editor_perspective_restore_state;
+  iface->views_foreach = ide_editor_perspective_views_foreach;
+}
diff --git a/libide/editor/ide-editor-perspective.h b/libide/editor/ide-editor-perspective.h
index 7e2dcb5..47b102e 100644
--- a/libide/editor/ide-editor-perspective.h
+++ b/libide/editor/ide-editor-perspective.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include "diagnostics/ide-source-location.h"
 #include "layout/ide-layout.h"
 #include "layout/ide-layout-grid.h"
 
@@ -27,6 +28,16 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeEditorPerspective, ide_editor_perspective, IDE, EDITOR_PERSPECTIVE, IdeLayout)
 
-IdeLayoutGrid *ide_editor_perspective_get_grid (IdeEditorPerspective *self);
+IdeLayoutGrid *ide_editor_perspective_get_grid                      (IdeEditorPerspective *self);
+void           ide_editor_perspective_focus_location                (IdeEditorPerspective *self,
+                                                                     IdeSourceLocation    *location);
+void           ide_editor_perspective_focus_buffer_in_current_stack (IdeEditorPerspective *self,
+                                                                     IdeBuffer            *buffer);
+IdeLayoutView *ide_editor_perspective_get_active_view               (IdeEditorPerspective *self);
+
+/* We want this to use "classifications" rather than "edges" */
+GtkWidget     *ide_editor_perspective_get_right_edge                (IdeEditorPerspective *self);
+GtkWidget     *ide_editor_perspective_get_bottom_edge               (IdeEditorPerspective *self);
+
 
 G_END_DECLS
diff --git a/libide/editor/ide-editor-search-bar.c b/libide/editor/ide-editor-search-bar.c
new file mode 100644
index 0000000..8b844b7
--- /dev/null
+++ b/libide/editor/ide-editor-search-bar.c
@@ -0,0 +1,590 @@
+/* ide-editor-search-bar.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-editor-search-bar"
+
+#include <glib/gi18n.h>
+#include <libgd/gd-tagged-entry.h>
+
+#include "ide-macros.h"
+
+#include "editor/ide-editor-search-bar.h"
+
+struct _IdeEditorSearchBar
+{
+  DzlBin                   parent_instance;
+
+  /* Owned references */
+  GtkSourceSearchSettings *search_settings;
+  GtkSourceSearchContext  *search_context;
+  DzlSignalGroup          *search_context_signals;
+  GdTaggedEntryTag        *search_entry_tag;
+
+  /* Weak pointers */
+  IdeBuffer               *buffer;
+  IdeSourceView           *view;
+
+  /* Template widgets */
+  GtkCheckButton          *case_sensitive;
+  GtkButton               *close_button;
+  GtkButton               *replace_all_button;
+  GtkButton               *replace_button;
+  GtkSearchEntry          *replace_entry;
+  GdTaggedEntry           *search_entry;
+  GtkGrid                 *search_options;
+  GtkCheckButton          *use_regex;
+  GtkCheckButton          *whole_word;
+};
+
+enum {
+  PROP_0,
+  PROP_BUFFER,
+  PROP_VIEW,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (IdeEditorSearchBar, ide_editor_search_bar, DZL_TYPE_BIN)
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+maybe_escape_regex (GBinding     *binding,
+                    const GValue *from_value,
+                    GValue       *to_value,
+                    gpointer      user_data)
+{
+  IdeEditorSearchBar *self = user_data;
+
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (from_value != NULL);
+  g_assert (to_value != NULL);
+
+  if (g_value_get_string (from_value) == NULL)
+    g_value_set_static_string (to_value, "");
+  else
+    {
+      const gchar *entry_text = g_value_get_string (from_value);
+      g_autofree gchar *unescaped = NULL;
+
+      if (!gtk_source_search_settings_get_regex_enabled (self->search_settings))
+        entry_text = unescaped = gtk_source_utils_unescape_search_text (entry_text);
+
+      g_value_set_string (to_value, entry_text);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+pacify_null_text (GBinding     *binding,
+                  const GValue *from_value,
+                  GValue       *to_value,
+                  gpointer      user_data)
+{
+  g_assert (from_value != NULL);
+  g_assert (to_value != NULL);
+
+  if (g_value_get_string (from_value) == NULL)
+    g_value_set_static_string (to_value, "");
+  else
+    g_value_copy (from_value, to_value);
+
+  return TRUE;
+}
+
+static void
+update_replace_actions_sensitivity (IdeEditorSearchBar *self)
+{
+  g_autoptr(GError) regex_error = NULL;
+  g_autoptr(GError) replace_regex_error = NULL;
+  GtkTextBuffer *buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+  const gchar *search_text;
+  const gchar *replace_text;
+  gint pos;
+  gint count;
+  gboolean enable_replace;
+  gboolean enable_replace_all;
+  gboolean replace_regex_valid;
+
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+  if (self->search_context == NULL ||
+      self->view == NULL ||
+      self->buffer == NULL ||
+      self->search_settings == NULL)
+    return;
+
+  buffer = GTK_TEXT_BUFFER (self->buffer);
+
+  gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+  replace_text = gtk_entry_get_text (GTK_ENTRY (self->replace_entry));
+
+  /* Gather enough info to determine if Replace or Replace All would make sense */
+  search_text = gtk_entry_get_text (GTK_ENTRY (self->search_entry));
+  pos = gtk_source_search_context_get_occurrence_position (self->search_context, &begin, &end);
+  count = gtk_source_search_context_get_occurrences_count (self->search_context);
+  regex_error = gtk_source_search_context_get_regex_error (self->search_context);
+  replace_regex_valid = gtk_source_search_settings_get_regex_enabled (self->search_settings) ?
+                        g_regex_check_replacement (replace_text, NULL, &replace_regex_error) :
+                        TRUE;
+
+  enable_replace = (!ide_str_empty0 (search_text) &&
+                    regex_error == NULL &&
+                    replace_regex_valid &&
+                    pos > 0);
+
+  enable_replace_all = (!ide_str_empty0 (search_text) &&
+                        regex_error == NULL &&
+                        replace_regex_valid &&
+                        count > 0);
+
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-entry", "replace",
+                             "enabled", enable_replace,
+                             NULL);
+  dzl_gtk_widget_action_set (GTK_WIDGET (self), "search-entry", "replace-all",
+                             "enabled", enable_replace_all,
+                             NULL);
+}
+
+static void
+on_notify_search_text (IdeEditorSearchBar      *self,
+                       GParamSpec              *pspec,
+                       GtkSourceSearchSettings *search_settings)
+{
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (GTK_SOURCE_IS_SEARCH_SETTINGS (search_settings));
+
+  update_replace_actions_sensitivity (self);
+}
+
+static void
+set_position_label (IdeEditorSearchBar *self,
+                    const gchar        *text)
+{
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+  if (ide_str_empty0 (text))
+    {
+      if (self->search_entry_tag != NULL)
+        {
+          gd_tagged_entry_remove_tag (self->search_entry, self->search_entry_tag);
+          g_clear_object (&self->search_entry_tag);
+        }
+
+      return;
+    }
+
+  if (self->search_entry_tag == NULL)
+    {
+      self->search_entry_tag = gd_tagged_entry_tag_new ("");
+      gd_tagged_entry_add_tag (self->search_entry, self->search_entry_tag);
+      gd_tagged_entry_tag_set_style (self->search_entry_tag,
+                                     "gb-search-entry-occurrences-tag");
+    }
+
+  gd_tagged_entry_tag_set_label (self->search_entry_tag, text);
+}
+
+static void
+update_search_position_label (IdeEditorSearchBar *self)
+{
+  g_autofree gchar *text = NULL;
+  GtkStyleContext *context;
+  GtkTextBuffer *buffer;
+  GtkTextIter begin;
+  GtkTextIter end;
+  const gchar *search_text;
+  gint count;
+  gint pos;
+
+  g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+
+  if (self->buffer == NULL || self->search_context == NULL)
+    return;
+
+  buffer = GTK_TEXT_BUFFER (self->buffer);
+
+  gtk_text_buffer_get_selection_bounds (buffer, &begin, &end);
+  pos = gtk_source_search_context_get_occurrence_position (self->search_context, &begin, &end);
+  count = gtk_source_search_context_get_occurrences_count (self->search_context);
+
+  if ((pos == -1) || (count == -1))
+    {
+      /*
+       * We are not yet done scanning the buffer.
+       * We will be updated when we know more, so just hide it for now.
+       */
+      set_position_label (self, NULL);
+      return;
+    }
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (self->search_entry));
+  search_text = gtk_entry_get_text (GTK_ENTRY (self->search_entry));
+
+  /* We use our own error class because we don't want to colide with styling
+   * from GTK+ themes.
+   */
+  if ((count == 0) && !ide_str_empty0 (search_text))
+    gtk_style_context_add_class (context, "search-missing");
+  else
+    gtk_style_context_remove_class (context, "search-missing");
+
+  /* translators: first %u is the Nth position of second %u N occurrences */
+  text = g_strdup_printf (_("%u of %u"), pos, count);
+  set_position_label (self, text);
+}
+
+static void
+on_notify_occurrences_count (IdeEditorSearchBar     *self,
+                             GParamSpec             *pspec,
+                             GtkSourceSearchContext *search_context)
+{
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (search_context));
+
+  update_search_position_label (self);
+  update_replace_actions_sensitivity (self);
+}
+
+static void
+on_notify_regex_error (IdeEditorSearchBar     *self,
+                       GParamSpec             *pspec,
+                       GtkSourceSearchContext *search_context)
+{
+  g_autoptr(GError) error = NULL;
+  PangoAttrList *attrs = NULL;
+  const gchar *tooltip_text = NULL;
+
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (GTK_SOURCE_IS_SEARCH_CONTEXT (search_context));
+
+  /*
+   * If the regular expression is invalid, add a white squiggly underline;
+   * otherwise remove it. We will also set the tooltip-text to the error
+   * that occurred while parsing the regex.
+   */
+
+  error = gtk_source_search_context_get_regex_error (search_context);
+
+  if (error != NULL)
+    {
+      attrs = pango_attr_list_new ();
+      pango_attr_list_insert (attrs, pango_attr_underline_new (PANGO_UNDERLINE_ERROR));
+      pango_attr_list_insert (attrs, pango_attr_underline_color_new (65535, 65535, 65535));
+      tooltip_text = error->message;
+    }
+
+  gtk_entry_set_attributes (GTK_ENTRY (self->search_entry), attrs);
+  gtk_widget_set_tooltip_text (GTK_WIDGET (self->search_entry), tooltip_text);
+
+  update_replace_actions_sensitivity (self);
+
+  pango_attr_list_unref (attrs);
+}
+
+static void
+check_replace_text (IdeEditorSearchBar *self)
+{
+  g_autoptr(GError) error = NULL;
+  PangoAttrList *attrs = NULL;
+  const gchar *tooltip_text = NULL;
+
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (self->search_settings != NULL);
+
+  if (self->search_context == NULL)
+    return;
+
+  /*
+   * If the replace expression is invalid, add a white squiggly underline;
+   * otherwise remove it. Also set the error message to the tooltip text
+   * so that the user can get some info on the error.
+   */
+  if (gtk_source_search_settings_get_regex_enabled (self->search_settings))
+    {
+      const gchar *replace_text;
+
+      replace_text = gtk_entry_get_text (GTK_ENTRY (self->replace_entry));
+
+      if (!g_regex_check_replacement (replace_text, NULL, &error))
+        {
+          attrs = pango_attr_list_new ();
+          pango_attr_list_insert (attrs, pango_attr_underline_new (PANGO_UNDERLINE_ERROR));
+          pango_attr_list_insert (attrs, pango_attr_underline_color_new (65535, 65535, 65535));
+          tooltip_text = error->message;
+        }
+    }
+
+  gtk_entry_set_attributes (GTK_ENTRY (self->replace_entry), attrs);
+  gtk_widget_set_tooltip_text (GTK_WIDGET (self->replace_entry), tooltip_text);
+
+  pango_attr_list_unref (attrs);
+}
+
+static void
+on_notify_regex_enabled (IdeEditorSearchBar      *self,
+                         GParamSpec              *pspec,
+                         GtkSourceSearchSettings *search_settings)
+{
+  g_assert (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_assert (GTK_SOURCE_IS_SEARCH_SETTINGS (search_settings));
+
+  check_replace_text (self);
+}
+
+static void
+ide_editor_search_bar_finalize (GObject *object)
+{
+  IdeEditorSearchBar *self = (IdeEditorSearchBar *)object;
+
+  ide_clear_weak_pointer (&self->buffer);
+  ide_clear_weak_pointer (&self->view);
+
+  g_clear_object (&self->search_context);
+  g_clear_object (&self->search_settings);
+  g_clear_object (&self->search_context_signals);
+  g_clear_object (&self->search_entry_tag);
+
+  G_OBJECT_CLASS (ide_editor_search_bar_parent_class)->finalize (object);
+}
+
+static void
+ide_editor_search_bar_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  IdeEditorSearchBar *self = IDE_EDITOR_SEARCH_BAR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      g_value_set_object (value, ide_editor_search_bar_get_buffer (self));
+      break;
+
+    case PROP_VIEW:
+      g_value_set_object (value, ide_editor_search_bar_get_view (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_editor_search_bar_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  IdeEditorSearchBar *self = IDE_EDITOR_SEARCH_BAR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      ide_editor_search_bar_set_buffer (self, g_value_get_object (value));
+      break;
+
+    case PROP_VIEW:
+      ide_editor_search_bar_set_view (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_editor_search_bar_class_init (IdeEditorSearchBarClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = ide_editor_search_bar_finalize;
+  object_class->get_property = ide_editor_search_bar_get_property;
+  object_class->set_property = ide_editor_search_bar_set_property;
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/builder/ui/ide-editor-search-bar.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, case_sensitive);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, close_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_all_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, replace_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, search_entry);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, search_options);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, use_regex);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorSearchBar, whole_word);
+
+  gtk_widget_class_set_css_name (widget_class, "ideeditorsearchbar");
+
+  g_type_ensure (GD_TYPE_TAGGED_ENTRY);
+}
+
+static void
+ide_editor_search_bar_init (IdeEditorSearchBar *self)
+{
+  g_autoptr(GSimpleActionGroup) group = NULL;
+  static const gchar *proxy_names[] = {
+    "case-sensitive",
+    "at-word-boundaries",
+    "regex-enabled",
+    "wrap-around",
+  };
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->search_settings = gtk_source_search_settings_new ();
+
+  g_object_bind_property_full (self->search_entry, "text",
+                               self->search_settings, "search-text",
+                               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+                               maybe_escape_regex, pacify_null_text,
+                               self, NULL);
+
+  self->search_context_signals = dzl_signal_group_new (GTK_SOURCE_TYPE_SEARCH_CONTEXT);
+
+  dzl_signal_group_connect_swapped (self->search_context_signals,
+                                    "notify::occurrences-count",
+                                    G_CALLBACK (on_notify_occurrences_count),
+                                    self);
+
+  dzl_signal_group_connect_swapped (self->search_context_signals,
+                                    "notify::regex-error",
+                                    G_CALLBACK (on_notify_regex_error),
+                                    self);
+
+  g_signal_connect_object (self->search_settings,
+                           "notify::search-text",
+                           G_CALLBACK (on_notify_search_text),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->search_settings,
+                           "notify::regex-enabled",
+                           G_CALLBACK (on_notify_regex_enabled),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  group = g_simple_action_group_new ();
+
+  for (guint i = 0; i < G_N_ELEMENTS (proxy_names); i++)
+    {
+      g_autoptr(GPropertyAction) action = NULL;
+      const gchar *name = proxy_names[i];
+
+      action = g_property_action_new (name, self->search_settings, name);
+      g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
+    }
+
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "search-entry", G_ACTION_GROUP (group));
+}
+
+GtkWidget *
+ide_editor_search_bar_new (void)
+{
+  return g_object_new (IDE_TYPE_EDITOR_SEARCH_BAR, NULL);
+}
+
+/**
+ * ide_editor_search_bar_get_buffer:
+ * @self: a #IdeEditorSearchBar
+ *
+ * Gets the buffer used by the search bar.
+ *
+ * Returns: (nullable) (transfer none): An #IdeBuffer or %NULL
+ *
+ * Since: 3.26
+ */
+IdeBuffer *
+ide_editor_search_bar_get_buffer (IdeEditorSearchBar *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), NULL);
+
+  return self->buffer;
+}
+
+/**
+ * ide_editor_search_bar_set_buffer:
+ * @self: a #IdeEditorSearchBar
+ *
+ * Sets the buffer used by the search bar.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_search_bar_set_buffer (IdeEditorSearchBar *self,
+                                  IdeBuffer          *buffer)
+{
+  g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_return_if_fail (!buffer || IDE_IS_BUFFER (buffer));
+
+  if (ide_set_weak_pointer (&self->buffer, buffer))
+    {
+      g_clear_object (&self->search_context);
+      dzl_signal_group_set_target (self->search_context_signals, NULL);
+
+      if (buffer != NULL)
+        {
+          self->search_context = gtk_source_search_context_new (GTK_SOURCE_BUFFER (buffer),
+                                                                self->search_settings);
+          dzl_signal_group_set_target (self->search_context_signals, self->search_context);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUFFER]);
+    }
+}
+
+/**
+ * ide_editor_search_bar_get_view:
+ * @self: a #IdeEditorSearchBar
+ *
+ * Gets the view used by the search bar.
+ *
+ * Returns: (nullable) (transfer none): An #IdeBuffer or %NULL
+ *
+ * Since: 3.26
+ */
+IdeSourceView *
+ide_editor_search_bar_get_view (IdeEditorSearchBar *self)
+{
+  g_return_val_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self), NULL);
+
+  return self->view;
+}
+
+/**
+ * ide_editor_search_bar_set_view:
+ * @self: a #IdeEditorSearchBar
+ *
+ * Sets the view used by the search bar.
+ *
+ * Since: 3.26
+ */
+void
+ide_editor_search_bar_set_view (IdeEditorSearchBar *self,
+                                IdeSourceView      *view)
+{
+  g_return_if_fail (IDE_IS_EDITOR_SEARCH_BAR (self));
+  g_return_if_fail (!view || IDE_IS_SOURCE_VIEW (view));
+
+  if (ide_set_weak_pointer (&self->view, view))
+    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VIEW]);
+}
diff --git a/libide/editor/ide-editor-search-bar.h b/libide/editor/ide-editor-search-bar.h
new file mode 100644
index 0000000..6dd67ad
--- /dev/null
+++ b/libide/editor/ide-editor-search-bar.h
@@ -0,0 +1,40 @@
+/* ide-editor-search-bar.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <dazzle.h>
+
+#include "buffers/ide-buffer.h"
+#include "sourceview/ide-source-view.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_SEARCH_BAR (ide_editor_search_bar_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorSearchBar, ide_editor_search_bar, IDE, EDITOR_SEARCH_BAR, DzlBin)
+
+GtkWidget     *ide_editor_search_bar_new        (void);
+IdeBuffer     *ide_editor_search_bar_get_buffer (IdeEditorSearchBar *self);
+void           ide_editor_search_bar_set_buffer (IdeEditorSearchBar *self,
+                                                 IdeBuffer          *buffer);
+IdeSourceView *ide_editor_search_bar_get_view   (IdeEditorSearchBar *self);
+void           ide_editor_search_bar_set_view   (IdeEditorSearchBar *self,
+                                                 IdeSourceView      *view);
+
+G_END_DECLS
diff --git a/libide/editor/ide-editor-search-bar.ui b/libide/editor/ide-editor-search-bar.ui
new file mode 100644
index 0000000..7d6e39b
--- /dev/null
+++ b/libide/editor/ide-editor-search-bar.ui
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeEditorSearchBar" parent="DzlBin">
+    <child>
+      <object class="GtkBox">
+        <property name="visible">true</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">7</property>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">true</property>
+            <property name="can_focus">false</property>
+            <property name="row_spacing">8</property>
+            <property name="column_spacing">8</property>
+            <child>
+              <object class="GdTaggedEntry" id="search_entry">
+                <property name="visible">true</property>
+                <property name="tag-close-visible">false</property>
+                <property name="can_focus">true</property>
+                <property name="width-chars">20</property>
+                <property name="max-width-chars">30</property>
+                <property name="primary_icon_name">edit-find-symbolic</property>
+                <property name="primary_icon_activatable">false</property>
+                <property name="primary_icon_sensitive">false</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSearchEntry" id="replace_entry">
+                <property name="visible">false</property>
+                <property name="can_focus">true</property>
+                <property name="width-chars">20</property>
+                <property name="max-width-chars">30</property>
+                <property name="primary_icon_name">edit-find-replace-symbolic</property>
+                <property name="primary_icon_activatable">false</property>
+                <property name="primary_icon_sensitive">false</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="homogeneous">true</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="valign">center</property>
+                <style>
+                  <class name="linked"/>
+                </style>
+                <child>
+                  <object class="GtkButton">
+                    <property name="action-name">frame.previous-search-result</property>
+                    <property name="visible">true</property>
+                    <property name="can_focus">false</property>
+                    <property name="receives_default">true</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">true</property>
+                        <property name="can_focus">false</property>
+                        <property name="icon_name">go-up-symbolic</property>
+                        <property name="icon_size">1</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">false</property>
+                    <property name="fill">true</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton">
+                    <property name="action-name">frame.next-search-result</property>
+                    <property name="visible">true</property>
+                    <property name="can_focus">false</property>
+                    <property name="receives_default">true</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">true</property>
+                        <property name="can_focus">false</property>
+                        <property name="icon_name">go-down-symbolic</property>
+                        <property name="icon_size">1</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">false</property>
+                    <property name="fill">true</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="replace_button">
+                <property name="label" translatable="yes">Replace</property>
+                <property name="action-name">search-entry.replace</property>
+                <property name="visible">false</property>
+                <property name="can_focus">true</property>
+                <property name="receives_default">true</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="replace_all_button">
+                <property name="label" translatable="yes">Replace All</property>
+                <property name="action-name">search-entry.replace-all</property>
+                <property name="visible">false</property>
+                <property name="can_focus">true</property>
+                <property name="receives_default">true</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="homogeneous">true</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="valign">center</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkToggleButton">
+                    <property name="action-name">search-entry.toggle-search-replace</property>
+                    <property name="action-target">true</property>
+                    <property name="tooltip-text" translatable="yes">Switch between Search and 
Search-and-Replace</property>
+                    <property name="visible">true</property>
+                    <property name="can_focus">true</property>
+                    <property name="receives_default">true</property>
+                    <property name="image_position">right</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">true</property>
+                        <property name="can_focus">false</property>
+                        <property name="icon_name">edit-find-replace-symbolic</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">false</property>
+                    <property name="fill">true</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToggleButton">
+                    <property name="action-name">search-entry.toggle-search-options</property>
+                    <property name="action-target">true</property>
+                    <property name="tooltip-text" translatable="yes">Show or hide search options such as 
case sensitivity</property>
+                    <property name="visible">true</property>
+                    <property name="can_focus">true</property>
+                    <property name="receives_default">true</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">true</property>
+                        <property name="can_focus">false</property>
+                        <property name="icon_name">emblem-system-symbolic</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">false</property>
+                    <property name="fill">true</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="visible">true</property>
+                <property name="action-name">search-entry.exit-search</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="focus_on_click">false</property>
+                <style>
+                  <class name="close"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">true</property>
+                    <property name="icon_name">window-close-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">4</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">false</property>
+            <property name="fill">true</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="search_options">
+            <property name="visible">false</property>
+            <property name="can_focus">false</property>
+            <property name="column_spacing">8</property>
+            <child>
+              <object class="GtkCheckButton" id="use_regex">
+                <property name="action-name">search-entry.regex-enabled</property>
+                <property name="label" translatable="yes">Regular expressions</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="receives_default">false</property>
+                <property name="xalign">0</property>
+                <property name="draw_indicator">true</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="case_sensitive">
+                <property name="action-name">search-entry.case-sensitive</property>
+                <property name="label" translatable="yes">Case sensitive</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="receives_default">false</property>
+                <property name="xalign">0</property>
+                <property name="draw_indicator">true</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="whole_word">
+                <property name="action-name">search-entry.at-word-boundaries</property>
+                <property name="label" translatable="yes">Match whole word only</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="receives_default">false</property>
+                <property name="xalign">0</property>
+                <property name="draw_indicator">true</property>
+              </object>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">false</property>
+            <property name="fill">true</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/libide/editor/ide-editor-view-settings.c b/libide/editor/ide-editor-view-settings.c
index 9eb39f3..7c32684 100644
--- a/libide/editor/ide-editor-view-settings.c
+++ b/libide/editor/ide-editor-view-settings.c
@@ -21,6 +21,7 @@
 #include "ide-editor-private.h"
 
 static GSettings *editor_settings;
+static GSettings *insight_settings;
 
 static gboolean
 get_smart_home_end (GValue   *value,
@@ -58,7 +59,7 @@ _ide_editor_view_init_settings (IdeEditorView *self)
   if (editor_settings == NULL)
     editor_settings = g_settings_new ("org.gnome.builder.editor");
 
-  source_view = ide_editor_view_get_source_view (self);
+  source_view = ide_editor_view_get_view (self);
   buffer = ide_editor_view_get_buffer (self);
 
   g_settings_bind (editor_settings, "highlight-current-line",
@@ -114,4 +115,11 @@ _ide_editor_view_init_settings (IdeEditorView *self)
   g_settings_bind (editor_settings, "auto-hide-map",
                    self, "auto-hide-map",
                    G_SETTINGS_BIND_GET);
+
+  if (insight_settings == NULL)
+    insight_settings = g_settings_new ("org.gnome.builder.code-insight");
+
+  g_settings_bind (insight_settings, "word-completion",
+                   source_view, "enable-word-completion",
+                   G_SETTINGS_BIND_GET);
 }
diff --git a/libide/editor/ide-editor-view.c b/libide/editor/ide-editor-view.c
index 831d7bf..2fccfd4 100644
--- a/libide/editor/ide-editor-view.c
+++ b/libide/editor/ide-editor-view.c
@@ -22,35 +22,107 @@
 #include <libpeas/peas.h>
 
 #include "ide-editor-private.h"
+#include "ide-editor-search-bar.h"
 #include "ide-editor-view.h"
 #include "ide-editor-view-addin.h"
 
 struct _IdeEditorView
 {
-  IdeLayoutView      parent_instance;
+  IdeLayoutView       parent_instance;
 
-  PeasExtensionSet  *addins;
+  PeasExtensionSet   *addins;
 
-  IdeBuffer         *buffer;
-  DzlBindingGroup   *buffer_bindings;
-  DzlSignalGroup    *buffer_signals;
+  IdeBuffer          *buffer;
+  DzlBindingGroup    *buffer_bindings;
+  DzlSignalGroup     *buffer_signals;
 
-  GtkOverlay        *overlay;
-  IdeSourceView     *source_view;
-  GtkScrolledWindow *scroller;
+  GtkOverlay         *overlay;
+  IdeSourceView      *source_view;
+  GtkScrolledWindow  *scroller;
+  IdeEditorSearchBar *search_bar;
+  GtkRevealer        *search_revealer;
 };
 
 enum {
   PROP_0,
   PROP_BUFFER,
+  PROP_VIEW,
   N_PROPS
 };
 
+enum {
+  DND_TARGET_URI_LIST = 100,
+};
+
 G_DEFINE_TYPE (IdeEditorView, ide_editor_view, IDE_TYPE_LAYOUT_VIEW)
 
 static GParamSpec *properties [N_PROPS];
 
 static void
+ide_editor_view_drag_data_received (IdeEditorView    *self,
+                                    GdkDragContext   *context,
+                                    gint              x,
+                                    gint              y,
+                                    GtkSelectionData *selection_data,
+                                    guint             info,
+                                    guint             timestamp,
+                                    IdeSourceView    *source_view)
+{
+  g_auto(GStrv) uri_list = NULL;
+
+  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_SOURCE_VIEW (source_view));
+
+  switch (info)
+    {
+    case DND_TARGET_URI_LIST:
+      uri_list = dzl_dnd_get_uri_list (selection_data);
+
+      if (uri_list != NULL)
+        {
+          GVariantBuilder *builder;
+          GVariant *variant;
+          guint i;
+
+          builder = g_variant_builder_new (G_VARIANT_TYPE_STRING_ARRAY);
+          for (i = 0; uri_list [i]; i++)
+            g_variant_builder_add (builder, "s", uri_list [i]);
+          variant = g_variant_builder_end (builder);
+          g_variant_builder_unref (builder);
+
+          /*
+           * request that we get focus first so the workbench will deliver the
+           * document to us in the case it is not already open
+           */
+          gtk_widget_grab_focus (GTK_WIDGET (self));
+          dzl_gtk_widget_action (GTK_WIDGET (self), "workbench", "open-uri-list", variant);
+        }
+
+      gtk_drag_finish (context, TRUE, FALSE, timestamp);
+      break;
+
+    default:
+      break;
+    }
+}
+
+static gboolean
+ide_editor_view_focus_in_event (IdeEditorView *self,
+                                GdkEventFocus *focus,
+                                IdeSourceView *source_view)
+{
+  g_assert (IDE_IS_EDITOR_VIEW (self));
+  g_assert (IDE_IS_SOURCE_VIEW (source_view));
+
+  gtk_revealer_set_reveal_child (self->search_revealer, FALSE);
+
+  if (self->buffer != NULL)
+    ide_buffer_check_for_volume_change (self->buffer);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
 ide_editor_view_buffer_modified_changed (IdeEditorView   *self,
                                          GtkSourceBuffer *buffer)
 {
@@ -213,6 +285,16 @@ ide_editor_view_constructed (GObject *object)
   _ide_editor_view_init_actions (self);
   _ide_editor_view_init_shortcuts (self);
   _ide_editor_view_init_settings (self);
+
+  g_signal_connect_swapped (self->source_view,
+                            "drag-data-received",
+                            G_CALLBACK (ide_editor_view_drag_data_received),
+                            self);
+
+  g_signal_connect_swapped (self->source_view,
+                            "focus-in-event",
+                            G_CALLBACK (ide_editor_view_focus_in_event),
+                            self);
 }
 
 static void
@@ -255,6 +337,10 @@ ide_editor_view_get_property (GObject    *object,
       g_value_set_object (value, ide_editor_view_get_buffer (self));
       break;
 
+    case PROP_VIEW:
+      g_value_set_object (value, ide_editor_view_get_view (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -302,17 +388,31 @@ ide_editor_view_class_init (IdeEditorViewClass *klass)
                          IDE_TYPE_BUFFER,
                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_VIEW] =
+    g_param_spec_object ("view",
+                         "View",
+                         "The view for editing the buffer",
+                         IDE_TYPE_SOURCE_VIEW,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-editor-view.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeEditorView, overlay);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorView, scroller);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, search_bar);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorView, search_revealer);
   gtk_widget_class_bind_template_child (widget_class, IdeEditorView, source_view);
+
+  g_type_ensure (IDE_TYPE_SOURCE_VIEW);
+  g_type_ensure (IDE_TYPE_EDITOR_SEARCH_BAR);
 }
 
 static void
 ide_editor_view_init (IdeEditorView *self)
 {
+  GtkTargetList *target_list;
+
   gtk_widget_init_template (GTK_WIDGET (self));
 
   ide_layout_view_set_can_split (IDE_LAYOUT_VIEW (self), TRUE);
@@ -343,6 +443,13 @@ ide_editor_view_init (IdeEditorView *self)
    */
   self->buffer_bindings = dzl_binding_group_new ();
   dzl_binding_group_bind (self->buffer_bindings, "title", self, "title", 0);
+
+  /*
+   * Setup Drag and Drop support.
+   */
+  target_list = gtk_drag_dest_get_target_list (GTK_WIDGET (self->source_view));
+  if (target_list != NULL)
+    gtk_target_list_add_uri_targets (target_list, DND_TARGET_URI_LIST);
 }
 
 /**
@@ -364,7 +471,7 @@ ide_editor_view_get_buffer (IdeEditorView *self)
 }
 
 /**
- * ide_editor_view_get_source_view:
+ * ide_editor_view_get_view:
  * @self: a #IdeEditorView
  *
  * Gets the #IdeSourceView that is part of the #IdeEditorView.
@@ -374,7 +481,7 @@ ide_editor_view_get_buffer (IdeEditorView *self)
  * Since: 3.26
  */
 IdeSourceView *
-ide_editor_view_get_source_view (IdeEditorView *self)
+ide_editor_view_get_view (IdeEditorView *self)
 {
   g_return_val_if_fail (IDE_IS_EDITOR_VIEW (self), NULL);
 
diff --git a/libide/editor/ide-editor-view.h b/libide/editor/ide-editor-view.h
index 2e0a979..d7243fe 100644
--- a/libide/editor/ide-editor-view.h
+++ b/libide/editor/ide-editor-view.h
@@ -30,7 +30,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeEditorView, ide_editor_view, IDE, EDITOR_VIEW, IdeLayoutView)
 
-IdeBuffer       *ide_editor_view_get_buffer      (IdeEditorView *self);
-IdeSourceView   *ide_editor_view_get_source_view (IdeEditorView *self);
+IdeBuffer     *ide_editor_view_get_buffer (IdeEditorView *self);
+IdeSourceView *ide_editor_view_get_view   (IdeEditorView *self);
 
 G_END_DECLS
diff --git a/libide/editor/ide-editor-view.ui b/libide/editor/ide-editor-view.ui
index 58a717f..f774973 100644
--- a/libide/editor/ide-editor-view.ui
+++ b/libide/editor/ide-editor-view.ui
@@ -4,6 +4,20 @@
     <child>
       <object class="GtkOverlay" id="overlay">
         <property name="visible">true</property>
+        <child type="overlay">
+          <object class="GtkRevealer" id="search_revealer">
+            <property name="halign">end</property>
+            <property name="margin-right">24</property>
+            <property name="reveal-child">false</property>
+            <property name="valign">start</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeEditorSearchBar" id="search_bar">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
         <child>
           <object class="GtkScrolledWindow" id="scroller">
             <property name="visible">true</property>
diff --git a/libide/layout/ide-layout-grid.c b/libide/layout/ide-layout-grid.c
index c574738..751f0b7 100644
--- a/libide/layout/ide-layout-grid.c
+++ b/libide/layout/ide-layout-grid.c
@@ -23,14 +23,23 @@
 
 typedef struct
 {
+  /* Owned references */
   DzlSignalGroup *toplevel_signals;
   GQueue          focus_column;
+
+  /*
+   * This unowned reference is simply used to compare to a new focus
+   * view to see if we have changed our current view. It is not to
+   * be used directly, only for pointer comparison.
+   */
+  IdeLayoutView  *_last_focused_view;
 } IdeLayoutGridPrivate;
 
 enum {
   PROP_0,
   PROP_CURRENT_COLUMN,
   PROP_CURRENT_STACK,
+  PROP_CURRENT_VIEW,
   N_PROPS
 };
 
@@ -110,12 +119,16 @@ ide_layout_grid_after_set_focus (IdeLayoutGrid *self,
                                  GtkWidget     *widget,
                                  GtkWidget     *toplevel)
 {
+  IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+
   g_assert (IDE_IS_LAYOUT_GRID (self));
   g_assert (!widget || GTK_IS_WIDGET (widget));
   g_assert (GTK_IS_WINDOW (toplevel));
 
   if (widget != NULL)
     {
+      GtkWidget *view;
+
       if (gtk_widget_is_ancestor (widget, GTK_WIDGET (self)))
         {
           GtkWidget *column = gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_GRID_COLUMN);
@@ -123,6 +136,17 @@ ide_layout_grid_after_set_focus (IdeLayoutGrid *self,
           if (column != NULL)
             ide_layout_grid_set_current_column (self, IDE_LAYOUT_GRID_COLUMN (column));
         }
+
+      /*
+       * self->_last_focused_view is an unowned reference, we only
+       * use it for pointer comparison, nothing more.
+       */
+      view = gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_VIEW);
+      if (view != (GtkWidget *)priv->_last_focused_view)
+        {
+          priv->_last_focused_view = (IdeLayoutView *)view;
+          g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_VIEW]);
+        }
     }
 }
 
@@ -339,16 +363,31 @@ ide_layout_grid_class_init (IdeLayoutGridClass *klass)
   properties [PROP_CURRENT_STACK] =
     g_param_spec_object ("current-stack",
                          "Current Stack",
-                         "The most recent focused IdeLayoutStack",
+                         "The most recently focused IdeLayoutStack",
                          IDE_TYPE_LAYOUT_STACK,
-                         (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_CURRENT_VIEW] =
+    g_param_spec_object ("current-view",
+                         "Current View",
+                         "The most recently focused IdeLayoutView",
+                         IDE_TYPE_LAYOUT_VIEW,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   gtk_widget_class_set_css_name (widget_class, "idelayoutgrid");
 
+  /**
+   * IdeLayoutGrid::create-stack:
+   * @self: an #IdeLayoutGrid
+   *
+   * Creates a new stack to be added to the grid.
+   *
+   * Returns: (transfer full): A newly created #IdeLayoutStack
+   */
   signals [CREATE_STACK] =
-    g_signal_new ("create-stack",
+    g_signal_new (g_intern_static_string ("create-stack"),
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (IdeLayoutGridClass, create_stack),
@@ -571,3 +610,87 @@ ide_layout_grid_set_current_column (IdeLayoutGrid       *self,
   g_warning ("%s does not contain %s",
              G_OBJECT_TYPE_NAME (self), G_OBJECT_TYPE_NAME (column));
 }
+
+/**
+ * ide_layout_grid_get_current_view:
+ * @self: a #IdeLayoutGrid
+ *
+ * Gets the most recent view used by the user as determined by tracking
+ * the window focus.
+ *
+ * Returns: (transfer none): An #IdeLayoutView or %NULL
+ *
+ * Since: 3.26
+ */
+IdeLayoutView *
+ide_layout_grid_get_current_view (IdeLayoutGrid *self)
+{
+  IdeLayoutStack *stack;
+
+  g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+
+  stack = ide_layout_grid_get_current_stack (self);
+
+  if (stack != NULL)
+    ide_layout_stack_get_visible_child (stack);
+
+  return NULL;
+}
+
+static void
+collect_views (GtkWidget *widget,
+               GPtrArray *ar)
+{
+  if (IDE_IS_LAYOUT_VIEW (widget))
+    g_ptr_array_add (ar, widget);
+}
+
+/**
+ * ide_layout_grid_foreach_view:
+ * @self: a #IdeLayoutGrid
+ * @callback: (scope call) (closure user_data): A callback for each view
+ * @user_data: user data for @callback
+ *
+ * This function will call @callback for every view found in @self.
+ *
+ * Since: 3.26
+ */
+void
+ide_layout_grid_foreach_view (IdeLayoutGrid *self,
+                              GtkCallback    callback,
+                              gpointer       user_data)
+{
+  g_autoptr(GPtrArray) views = NULL;
+  guint n_columns;
+
+  g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+  g_return_if_fail (callback != NULL);
+
+  views = g_ptr_array_new ();
+
+  n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
+
+  for (guint i = 0; i < n_columns; i++)
+    {
+      GtkWidget *column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
+      guint n_stacks;
+
+      g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+
+      n_stacks = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
+
+      for (guint j = 0; j < n_stacks; j++)
+        {
+          GtkWidget *stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j);
+
+          g_assert (IDE_IS_LAYOUT_STACK (stack));
+
+          ide_layout_stack_foreach_view (IDE_LAYOUT_STACK (stack),
+                                         (GtkCallback) collect_views,
+                                         views);
+        }
+    }
+
+  for (guint i = 0; i < views->len; i++)
+    callback (g_ptr_array_index (views, i), user_data);
+}
diff --git a/libide/layout/ide-layout-grid.h b/libide/layout/ide-layout-grid.h
index 36eb512..53ba67e 100644
--- a/libide/layout/ide-layout-grid.h
+++ b/libide/layout/ide-layout-grid.h
@@ -22,6 +22,7 @@
 
 #include "ide-layout-grid-column.h"
 #include "ide-layout-stack.h"
+#include "ide-layout-view.h"
 
 G_BEGIN_DECLS
 
@@ -50,5 +51,9 @@ IdeLayoutGridColumn *ide_layout_grid_get_current_column (IdeLayoutGrid       *se
 void                 ide_layout_grid_set_current_column (IdeLayoutGrid       *self,
                                                          IdeLayoutGridColumn *column);
 IdeLayoutStack      *ide_layout_grid_get_current_stack  (IdeLayoutGrid       *self);
+IdeLayoutView       *ide_layout_grid_get_current_view   (IdeLayoutGrid       *self);
+void                 ide_layout_grid_foreach_view       (IdeLayoutGrid       *self,
+                                                         GtkCallback          callback,
+                                                         gpointer             user_data);
 
 G_END_DECLS
diff --git a/libide/layout/ide-layout-stack-header.c b/libide/layout/ide-layout-stack-header.c
index 82178cc..cc9dd56 100644
--- a/libide/layout/ide-layout-stack-header.c
+++ b/libide/layout/ide-layout-stack-header.c
@@ -415,6 +415,7 @@ ide_layout_stack_header_new (void)
  * ide_layout_stack_header_add_custom_title:
  * @self: a #IdeLayoutStackHeader
  * @widget: A #GtkWidget
+ * @priority: the sort priority
  *
  * This will add @widget to the title area with @priority determining the
  * sort order of the child.
diff --git a/libide/layout/ide-layout-stack-header.h b/libide/layout/ide-layout-stack-header.h
index 15ea9f0..5952836 100644
--- a/libide/layout/ide-layout-stack-header.h
+++ b/libide/layout/ide-layout-stack-header.h
@@ -30,7 +30,7 @@ GtkWidget *ide_layout_stack_header_new              (void);
 void       ide_layout_stack_header_set_title        (IdeLayoutStackHeader *self,
                                                      const gchar          *title);
 void       ide_layout_stack_header_add_custom_title (IdeLayoutStackHeader *self,
-                                                     GtkWidget            *custom_title,
+                                                     GtkWidget            *widget,
                                                      gint                  priority);
 
 G_END_DECLS
diff --git a/libide/layout/ide-layout-stack.c b/libide/layout/ide-layout-stack.c
index ffb9e75..f5f30ac 100644
--- a/libide/layout/ide-layout-stack.c
+++ b/libide/layout/ide-layout-stack.c
@@ -821,3 +821,26 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
   gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (view));
   g_object_unref (view);
 }
+
+/**
+ * ide_layout_stack_foreach_view:
+ * @self: a #IdeLayoutStack
+ * @callback: (scope call) (closure user_data): A callback for each view
+ * @user_data: user data for @callback
+ *
+ * This function will call @callback for every view found in @self.
+ *
+ * Since: 3.26
+ */
+void
+ide_layout_stack_foreach_view (IdeLayoutStack *self,
+                               GtkCallback     callback,
+                               gpointer        user_data)
+{
+  IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+  g_return_if_fail (callback != NULL);
+
+  gtk_container_foreach (GTK_CONTAINER (priv->stack), callback, user_data);
+}
diff --git a/libide/layout/ide-layout-stack.h b/libide/layout/ide-layout-stack.h
index c487d09..68715f2 100644
--- a/libide/layout/ide-layout-stack.h
+++ b/libide/layout/ide-layout-stack.h
@@ -63,5 +63,8 @@ void           ide_layout_stack_agree_to_close_async  (IdeLayoutStack       *sel
 gboolean       ide_layout_stack_agree_to_close_finish (IdeLayoutStack       *self,
                                                        GAsyncResult         *result,
                                                        GError              **error);
+void           ide_layout_stack_foreach_view          (IdeLayoutStack       *self,
+                                                       GtkCallback           callback,
+                                                       gpointer              user_data);
 
 G_END_DECLS
diff --git a/libide/layout/ide-layout-view.c b/libide/layout/ide-layout-view.c
index ade87b0..3bcae94 100644
--- a/libide/layout/ide-layout-view.c
+++ b/libide/layout/ide-layout-view.c
@@ -249,6 +249,8 @@ ide_layout_view_class_init (IdeLayoutViewClass *klass)
    *
    * This signal will only be emitted when #IdeLayoutView:can-split is
    * set to %TRUE. The default is %FALSE.
+   *
+   * Returns: (transfer full): A newly created #IdeLayoutView
    */
   signals [CREATE_SPLIT_VIEW] =
     g_signal_new (g_intern_static_string ("create-split-view"),
diff --git a/libide/layout/ide-layout.c b/libide/layout/ide-layout.c
index 44a047c..4a44458 100644
--- a/libide/layout/ide-layout.c
+++ b/libide/layout/ide-layout.c
@@ -16,121 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "ide-layout.h"
-#include "ide-layout-pane.h"
-#include "ide-layout-view.h"
+#define G_LOG_DOMAIN "ide-layout"
 
-typedef struct
-{
-  GtkWidget *active_view;
-
-  gulong     focus_handler;
-} IdeLayoutPrivate;
-
-G_DEFINE_TYPE_WITH_PRIVATE (IdeLayout, ide_layout, DZL_TYPE_DOCK_BIN)
-
-enum {
-  PROP_0,
-  PROP_ACTIVE_VIEW,
-  LAST_PROP
-};
-
-static GParamSpec *properties [LAST_PROP];
-
-static void
-ide_layout_active_view_weak_cb (IdeLayout *self,
-                                GtkWidget *where_view_was)
-{
-  IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
-
-  g_assert (IDE_IS_LAYOUT (self));
-
-  if (where_view_was == priv->active_view)
-    {
-      priv->active_view = NULL;
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE_VIEW]);
-    }
-}
-
-static void
-ide_layout_set_active_view (IdeLayout *self,
-                            GtkWidget *active_view)
-{
-  IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
-
-  g_assert (IDE_IS_LAYOUT (self));
-  g_assert (!active_view || GTK_IS_WIDGET (active_view));
-
-  if (active_view != priv->active_view)
-    {
-      if (priv->active_view != NULL)
-        {
-          g_object_weak_unref (G_OBJECT (priv->active_view),
-                               (GWeakNotify)ide_layout_active_view_weak_cb,
-                               self);
-          priv->active_view = NULL;
-        }
-
-      if (active_view != NULL)
-        {
-          priv->active_view = active_view;
-          g_object_weak_ref (G_OBJECT (priv->active_view),
-                             (GWeakNotify)ide_layout_active_view_weak_cb,
-                             self);
-        }
-
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE_VIEW]);
-    }
-}
-
-static void
-ide_layout_toplevel_set_focus (IdeLayout *self,
-                               GtkWidget *widget,
-                               GtkWidget *toplevel)
-{
-  g_assert (IDE_IS_LAYOUT (self));
-
-  if (widget != NULL && !IDE_IS_LAYOUT_VIEW (widget))
-    widget = gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_VIEW);
-
-  if (widget != NULL)
-    ide_layout_set_active_view (self, widget);
-}
+#include "layout/ide-layout.h"
+#include "layout/ide-layout-pane.h"
+#include "layout/ide-layout-view.h"
 
-static void
-ide_layout_hierarchy_changed (GtkWidget *widget,
-                              GtkWidget *old_toplevel)
-{
-  IdeLayout *self = (IdeLayout *)widget;
-  IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
-  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))
-    {
-      g_signal_handler_disconnect (old_toplevel, priv->focus_handler);
-      priv->focus_handler = 0;
-
-      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;
-        }
-    }
-
-  toplevel = gtk_widget_get_toplevel (widget);
-
-  if (GTK_IS_WINDOW (toplevel))
-    priv->focus_handler =
-      g_signal_connect_swapped (toplevel,
-                                "set-focus",
-                                G_CALLBACK (ide_layout_toplevel_set_focus),
-                                self);
-}
+G_DEFINE_TYPE (IdeLayout, ide_layout, DZL_TYPE_DOCK_BIN)
 
 static GtkWidget *
 ide_layout_create_edge (DzlDockBin *dock)
@@ -144,63 +36,14 @@ ide_layout_create_edge (DzlDockBin *dock)
 }
 
 static void
-ide_layout_get_property (GObject    *object,
-                         guint       prop_id,
-                         GValue     *value,
-                         GParamSpec *pspec)
-{
-  IdeLayout *self = IDE_LAYOUT (object);
-
-  switch (prop_id)
-    {
-    case PROP_ACTIVE_VIEW:
-      g_value_set_object (value, ide_layout_get_active_view (self));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-    }
-}
-
-static void
 ide_layout_class_init (IdeLayoutClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   DzlDockBinClass *dock_bin_class = DZL_DOCK_BIN_CLASS (klass);
 
-  object_class->get_property = ide_layout_get_property;
-
-  widget_class->hierarchy_changed = ide_layout_hierarchy_changed;
-
   dock_bin_class->create_edge = ide_layout_create_edge;
-
-  properties [PROP_ACTIVE_VIEW] =
-    g_param_spec_object ("active-view",
-                         "Active View",
-                         "Active View",
-                         GTK_TYPE_WIDGET,
-                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_properties (object_class, LAST_PROP, properties);
 }
 
 static void
 ide_layout_init (IdeLayout *self)
 {
 }
-
-/**
- * ide_layout_get_active_view:
- *
- * Returns: (transfer none) (nullable): An #IdeLayoutView or %NULL.
- */
-GtkWidget *
-ide_layout_get_active_view (IdeLayout *self)
-{
-  IdeLayoutPrivate *priv = ide_layout_get_instance_private (self);
-
-  g_return_val_if_fail (IDE_IS_LAYOUT (self), NULL);
-
-  return priv->active_view;
-}
diff --git a/libide/layout/ide-layout.h b/libide/layout/ide-layout.h
index 50534ee..0b2154e 100644
--- a/libide/layout/ide-layout.h
+++ b/libide/layout/ide-layout.h
@@ -16,8 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_LAYOUT_H
-#define IDE_LAYOUT_H
+#pragma once
 
 #include <dazzle.h>
 
@@ -30,10 +29,15 @@ G_DECLARE_DERIVABLE_TYPE (IdeLayout, ide_layout, IDE, LAYOUT, DzlDockBin)
 struct _IdeLayoutClass
 {
   DzlDockBinClass parent_class;
-};
 
-GtkWidget *ide_layout_get_active_view (IdeLayout *self);
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
+};
 
 G_END_DECLS
-
-#endif /* IDE_LAYOUT_H */
diff --git a/libide/libide.gresource.xml b/libide/libide.gresource.xml
index 305a6db..ac4239e 100644
--- a/libide/libide.gresource.xml
+++ b/libide/libide.gresource.xml
@@ -32,6 +32,7 @@
     <file compressed="true" alias="Arc-shared.css">../data/themes/Arc-shared.css</file>
 
     <file compressed="true" alias="shared.css">../data/themes/shared.css</file>
+    <file compressed="true" alias="shared/shared-layout.css">../data/themes/shared/shared-layout.css</file>
   </gresource>
 
   <gresource prefix="/org/gnome/builder/modelines">
@@ -54,6 +55,7 @@
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack-header.ui">layout/ide-layout-stack-header.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack.ui">layout/ide-layout-stack.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-perspective.ui">editor/ide-editor-perspective.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-search-bar.ui">editor/ide-editor-search-bar.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-spell-widget.ui">spellcheck/ide-editor-spell-widget.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-view.ui">editor/ide-editor-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-greeter-perspective.ui">greeter/ide-greeter-perspective.ui</file>
diff --git a/libide/meson.build b/libide/meson.build
index 8370103..fb14de0 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -459,10 +459,12 @@ libide_sources = libide_generated_headers + libide_public_sources + [
   'editor/ide-editor-plugin.c',
   'editor/ide-editor-print-operation.c',
   'editor/ide-editor-print-operation.h',
+  'editor/ide-editor-private.h',
+  'editor/ide-editor-search-bar.c',
+  'editor/ide-editor-search-bar.h',
   'editor/ide-editor-view-actions.c',
   'editor/ide-editor-view-settings.c',
   'editor/ide-editor-view-shortcuts.c',
-  'editor/ide-editor-private.h',
   'editor/ide-editor-workbench-addin.c',
   'editor/ide-editor-workbench-addin.h',
   'gconstructor.h',
@@ -480,7 +482,6 @@ libide_sources = libide_generated_headers + libide_public_sources + [
   'keybindings/ide-keybindings.h',
   'keybindings/ide-shortcuts-window.c',
   'keybindings/ide-shortcuts-window.h',
-  'layout/ide-layout-grid-actions.c',
   'layout/ide-layout-grid-column-actions.c',
   'layout/ide-layout-private.h',
   'layout/ide-layout-stack-actions.c',
diff --git a/plugins/beautifier/gb-beautifier-workbench-addin.c 
b/plugins/beautifier/gb-beautifier-workbench-addin.c
index e8caf55..ebe90e9 100644
--- a/plugins/beautifier/gb-beautifier-workbench-addin.c
+++ b/plugins/beautifier/gb-beautifier-workbench-addin.c
@@ -71,7 +71,7 @@ view_activate_beautify_action_cb (GSimpleAction *action,
   if (view == NULL || !IDE_IS_EDITOR_VIEW (view))
     return;
 
-  source_view = ide_editor_view_get_active_source_view (view);
+  source_view = ide_editor_view_get_view (view);
   if (!GTK_SOURCE_IS_VIEW (source_view))
     {
       g_warning ("Beautifier Plugin: the view is not a GtkSourceView");
@@ -244,7 +244,7 @@ setup_view_cb (GtkWidget                  *widget,
 
   g_object_set_data (G_OBJECT (view), "gb-beautifier-workbench-addin", self);
 
-  source_view = ide_editor_view_get_active_source_view (view);
+  source_view = ide_editor_view_get_view (view);
   g_signal_connect_object (source_view,
                            "populate-popup",
                            G_CALLBACK (view_populate_popup),
diff --git a/plugins/color-picker/gb-color-picker-prefs.c b/plugins/color-picker/gb-color-picker-prefs.c
index 2e68e53..8deb8a0 100644
--- a/plugins/color-picker/gb-color-picker-prefs.c
+++ b/plugins/color-picker/gb-color-picker-prefs.c
@@ -370,7 +370,7 @@ generate_palette_button_clicked_cb (GbColorPickerPrefs *self,
   g_assert (GTK_IS_BUTTON (button));
 
   view = IDE_EDITOR_VIEW (self->addin->active_view);
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_document (view));
+  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (view));
 
   palette = gstyle_palette_new_from_buffer (buffer, NULL, NULL, NULL, NULL);
   if (palette != NULL)
diff --git a/plugins/color-picker/gb-color-picker-workbench-addin-private.h 
b/plugins/color-picker/gb-color-picker-workbench-addin-private.h
index 96e1387..b35f302 100644
--- a/plugins/color-picker/gb-color-picker-workbench-addin-private.h
+++ b/plugins/color-picker/gb-color-picker-workbench-addin-private.h
@@ -30,7 +30,7 @@ struct _GbColorPickerWorkbenchAddin
   GHashTable            *views;
   IdeWorkbench          *workbench;
   IdeEditorPerspective  *editor;
-  GtkWidget             *active_view;
+  IdeLayoutView         *active_view;
   GtkWidget             *dock;
   GtkWidget             *color_panel;
   GbColorPickerPrefs    *prefs;
diff --git a/plugins/color-picker/gb-color-picker-workbench-addin.c 
b/plugins/color-picker/gb-color-picker-workbench-addin.c
index 3c70926..6bafdff 100644
--- a/plugins/color-picker/gb-color-picker-workbench-addin.c
+++ b/plugins/color-picker/gb-color-picker-workbench-addin.c
@@ -141,7 +141,7 @@ get_view_monitor (GbColorPickerWorkbenchAddin *self,
   g_assert (GB_IS_COLOR_PICKER_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_EDITOR_VIEW (view));
 
-  buffer = ide_editor_view_get_document (view);
+  buffer = ide_editor_view_get_buffer (view);
   if (buffer == NULL)
     return NULL;
 
@@ -237,7 +237,7 @@ monitor_color_found_cb (GbColorPickerWorkbenchAddin  *self,
   if (self->active_view == NULL)
     return;
 
-  active_buffer = ide_editor_view_get_document (IDE_EDITOR_VIEW (self->active_view));
+  active_buffer = ide_editor_view_get_buffer (IDE_EDITOR_VIEW (self->active_view));
   if (active_buffer != NULL && self->dock != NULL)
     {
       gstyle_color_fill_rgba (color, &rgba);
@@ -337,7 +337,7 @@ activate_color_picker_action_cb (GbColorPickerWorkbenchAddin *self,
       else
         init_dock (self);
 
-      buffer = ide_editor_view_get_document (view);
+      buffer = ide_editor_view_get_buffer (view);
       monitor = g_object_get_data (G_OBJECT (buffer), "monitor");
       if (monitor == NULL)
         {
@@ -460,30 +460,30 @@ gb_color_picker_workbench_addin_load (IdeWorkbenchAddin *addin,
                                       IdeWorkbench      *workbench)
 {
   GbColorPickerWorkbenchAddin *self = (GbColorPickerWorkbenchAddin *)addin;
-  IdeLayout *layout;
+  IdeLayoutGrid *grid;
 
   g_assert (GB_IS_COLOR_PICKER_WORKBENCH_ADDIN (addin));
   g_assert (IDE_IS_WORKBENCH (workbench));
 
   ide_set_weak_pointer (&self->workbench, workbench);
   self->editor = IDE_EDITOR_PERSPECTIVE (ide_workbench_get_perspective_by_name (workbench, "editor"));
-  layout = ide_editor_perspective_get_layout (self->editor);
+  grid = ide_editor_perspective_get_grid (self->editor);
 
   ide_perspective_views_foreach (IDE_PERSPECTIVE (self->editor), (GtkCallback)setup_view_cb, self);
-  self->active_view = ide_editor_perspective_get_active_view (self->editor);
+  self->active_view = ide_layout_grid_get_current_view (grid);
 
-  g_signal_connect_object (self->editor,
+  g_signal_connect_object (grid,
                            "view-added",
                            G_CALLBACK (view_added_cb),
                            self,
                            G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->editor,
+  g_signal_connect_object (grid,
                            "view-removed",
                            G_CALLBACK (view_removed_cb),
                            self,
                            G_CONNECT_SWAPPED);
-  g_signal_connect_object (layout,
-                           "notify::active-view",
+  g_signal_connect_object (grid,
+                           "notify::current-view",
                            G_CALLBACK (active_view_changed_cb),
                            self,
                            G_CONNECT_SWAPPED);
diff --git a/plugins/comment-code/gbp-comment-code-view-addin.c 
b/plugins/comment-code/gbp-comment-code-view-addin.c
index 18b6a27..3b8103f 100644
--- a/plugins/comment-code/gbp-comment-code-view-addin.c
+++ b/plugins/comment-code/gbp-comment-code-view-addin.c
@@ -280,8 +280,8 @@ gbp_comment_code_view_addin_comment_action (GSimpleAction *action,
 
   g_assert (G_IS_SIMPLE_ACTION (action));
 
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_document (editor_view));
-  source_view = ide_editor_view_get_active_source_view (editor_view);
+  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (editor_view));
+  source_view = ide_editor_view_get_view (editor_view);
   if (source_view == NULL || !GTK_SOURCE_IS_VIEW (source_view))
     return;
 
diff --git a/plugins/project-tree/gb-project-tree-actions.c b/plugins/project-tree/gb-project-tree-actions.c
index 6039e5e..ca2b2ed 100644
--- a/plugins/project-tree/gb-project-tree-actions.c
+++ b/plugins/project-tree/gb-project-tree-actions.c
@@ -726,7 +726,7 @@ gb_project_tree_actions_close_views_cb (GtkWidget *widget,
     {
       IdeBuffer *buffer;
 
-      buffer = ide_editor_view_get_document (IDE_EDITOR_VIEW (view));
+      buffer = ide_editor_view_get_buffer (IDE_EDITOR_VIEW (view));
 
       if (buffer == removal->document)
         removal->views = g_list_prepend (removal->views, g_object_ref (view));
diff --git a/plugins/project-tree/gb-project-tree-editor-addin.c 
b/plugins/project-tree/gb-project-tree-editor-addin.c
index ce6960b..38f9539 100644
--- a/plugins/project-tree/gb-project-tree-editor-addin.c
+++ b/plugins/project-tree/gb-project-tree-editor-addin.c
@@ -63,7 +63,7 @@ gb_project_tree_editor_addin_reveal (GSimpleAction *action,
   tree = g_object_get_data (G_OBJECT (workbench), "GB_PROJECT_TREE");
   g_assert (GB_IS_PROJECT_TREE (tree));
 
-  buffer = ide_editor_view_get_document (self->view);
+  buffer = ide_editor_view_get_buffer (self->view);
   g_assert (IDE_IS_BUFFER (buffer));
 
   ifile = ide_buffer_get_file (buffer);
diff --git a/plugins/quick-highlight/gbp-quick-highlight-view-addin.c 
b/plugins/quick-highlight/gbp-quick-highlight-view-addin.c
index 70e3821..c57453e 100644
--- a/plugins/quick-highlight/gbp-quick-highlight-view-addin.c
+++ b/plugins/quick-highlight/gbp-quick-highlight-view-addin.c
@@ -103,7 +103,7 @@ gbp_quick_highlight_view_addin_match (GbpQuickHighlightViewAddin *self)
 
   g_assert (GBP_IS_QUICK_HIGHLIGHT_VIEW_ADDIN (self));
 
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_document (self->editor_view));
+  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (self->editor_view));
 
   if (gtk_text_buffer_get_selection_bounds (buffer, &begin, &end))
     {
@@ -200,7 +200,7 @@ gbp_quick_highlight_view_addin_enabled_changed (GbpQuickHighlightViewAddin *self
   g_assert (GBP_IS_QUICK_HIGHLIGHT_VIEW_ADDIN (self));
   g_assert (G_IS_SETTINGS (settings));
 
-  buffer = ide_editor_view_get_document (self->editor_view);
+  buffer = ide_editor_view_get_buffer (self->editor_view);
   enabled = g_settings_get_boolean (settings, "enabled");
 
   if (!self->enabled && enabled)
@@ -235,7 +235,7 @@ gbp_quick_highlight_view_addin_load (IdeEditorViewAddin *addin,
 
   self->editor_view = view;
 
-  buffer = GTK_SOURCE_BUFFER (ide_editor_view_get_document (view));
+  buffer = GTK_SOURCE_BUFFER (ide_editor_view_get_buffer (view));
 
   self->insert_mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
 
@@ -300,7 +300,7 @@ gbp_quick_highlight_view_addin_unload (IdeEditorViewAddin *addin,
 
   g_assert (GBP_IS_QUICK_HIGHLIGHT_VIEW_ADDIN (self));
 
-  buffer = GTK_SOURCE_BUFFER (ide_editor_view_get_document (view));
+  buffer = GTK_SOURCE_BUFFER (ide_editor_view_get_buffer (view));
 
   ide_clear_source (&self->queued_update);
 
diff --git a/plugins/retab/gbp-retab-view-addin.c b/plugins/retab/gbp-retab-view-addin.c
index 3d0e88a..8304a39 100644
--- a/plugins/retab/gbp-retab-view-addin.c
+++ b/plugins/retab/gbp-retab-view-addin.c
@@ -135,8 +135,8 @@ gbp_retab_view_addin_action (GSimpleAction *action,
 
   g_assert (G_IS_SIMPLE_ACTION (action));
 
-  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_document (editor_view));
-  source_view = ide_editor_view_get_active_source_view (editor_view);
+  buffer = GTK_TEXT_BUFFER (ide_editor_view_get_buffer (editor_view));
+  source_view = ide_editor_view_get_view (editor_view);
   if (source_view == NULL || !GTK_SOURCE_IS_VIEW (source_view))
     return;
 
diff --git a/plugins/symbol-tree/symbol-tree-panel.c b/plugins/symbol-tree/symbol-tree-panel.c
index 8480383..dfc8c90 100644
--- a/plugins/symbol-tree/symbol-tree-panel.c
+++ b/plugins/symbol-tree/symbol-tree-panel.c
@@ -152,7 +152,7 @@ refresh_tree (SymbolTreePanel *self)
   if ((active_view = ide_editor_perspective_get_active_view (IDE_EDITOR_PERSPECTIVE (perspective))) &&
       IDE_IS_EDITOR_VIEW (active_view))
     {
-      document = ide_editor_view_get_document (IDE_EDITOR_VIEW  (active_view));
+      document = ide_editor_view_get_buffer (IDE_EDITOR_VIEW  (active_view));
       if (IDE_IS_BUFFER (document))
         change_count = ide_buffer_get_change_count (IDE_BUFFER (document));
     }
diff --git a/plugins/symbol-tree/symbol-tree.c b/plugins/symbol-tree/symbol-tree.c
index 287691c..768f530 100644
--- a/plugins/symbol-tree/symbol-tree.c
+++ b/plugins/symbol-tree/symbol-tree.c
@@ -53,7 +53,7 @@ notify_active_view_cb (SymbolTree  *self,
                        GParamFlags *pspec,
                        IdeLayout   *layout)
 {
-  GtkWidget *active_view;
+  IdeLayotuView *active_view = NULL;
   IdeBuffer *buffer;
 
   IDE_ENTRY;
@@ -72,10 +72,12 @@ notify_active_view_cb (SymbolTree  *self,
       ide_clear_weak_pointer (&self->buffer);
     }
 
-  active_view = ide_layout_get_active_view (layout);
+  if (IDE_IS_EDITOR_PERSPECTIVE (layout))
+    active_view = ide_editor_perspective_get_active_view (IDE_EDITOR_PERPSECTIVE (layout));
+
   if (IDE_IS_EDITOR_VIEW (active_view))
     {
-      buffer = ide_editor_view_get_document (IDE_EDITOR_VIEW (active_view));
+      buffer = ide_editor_view_get_buffer (IDE_EDITOR_VIEW (active_view));
       if (ide_buffer_get_symbol_resolver (buffer) == NULL)
         {
           ide_set_weak_pointer (&self->buffer, buffer);


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