[gnome-builder] libide-gui: port workbench/workspace to GTK 4
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide-gui: port workbench/workspace to GTK 4
- Date: Tue, 12 Jul 2022 06:39:12 +0000 (UTC)
commit bc3e6bb23735dfcfd5ca4936fd8d788982983e4a
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 21:54:36 2022 -0700
libide-gui: port workbench/workspace to GTK 4
This uses libpanel for a lot of the workspace internals that used to be
in tree. Additionally, availability macros have been cleaned up, libdazzle
usage removed, pane and page cleanup, notebook tab support, and a lot more
here.
src/libide/gui/gtk/menus.ui | 51 +-
src/libide/gui/ide-frame.c | 1341 +++------------------
src/libide/gui/ide-frame.h | 66 +-
src/libide/gui/ide-frame.ui | 153 ++-
src/libide/gui/ide-grid-actions.c | 73 --
src/libide/gui/ide-grid.c | 1513 ++----------------------
src/libide/gui/ide-grid.h | 62 +-
src/libide/gui/ide-page-private.h | 29 +
src/libide/gui/ide-page.c | 682 ++++-------
src/libide/gui/ide-page.h | 134 +--
src/libide/gui/ide-page.ui | 26 +
src/libide/gui/ide-pane.c | 261 +++-
src/libide/gui/ide-pane.h | 32 +-
src/libide/gui/ide-panel-position.c | 174 +++
src/libide/gui/ide-panel-position.h | 74 ++
src/libide/gui/ide-primary-workspace-actions.c | 6 +-
src/libide/gui/ide-primary-workspace-private.h | 29 +
src/libide/gui/ide-primary-workspace.c | 220 +++-
src/libide/gui/ide-primary-workspace.h | 7 +-
src/libide/gui/ide-primary-workspace.ui | 120 +-
src/libide/gui/ide-workbench-addin.c | 114 +-
src/libide/gui/ide-workbench-addin.h | 60 +-
src/libide/gui/ide-workbench-private.h | 31 +
src/libide/gui/ide-workbench.c | 457 ++++---
src/libide/gui/ide-workbench.h | 75 +-
src/libide/gui/ide-workspace-addin.c | 52 +-
src/libide/gui/ide-workspace-addin.h | 29 +-
src/libide/gui/ide-workspace-private.h | 57 +
src/libide/gui/ide-workspace.c | 1378 ++++++++++++---------
src/libide/gui/ide-workspace.h | 136 ++-
src/libide/gui/libide-gui.gresource.xml | 3 +
src/libide/gui/meson.build | 13 +
src/libide/gui/style.css | 98 ++
33 files changed, 3113 insertions(+), 4443 deletions(-)
---
diff --git a/src/libide/gui/gtk/menus.ui b/src/libide/gui/gtk/menus.ui
index 91b72fddb..a5b20291d 100644
--- a/src/libide/gui/gtk/menus.ui
+++ b/src/libide/gui/gtk/menus.ui
@@ -1,15 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <menu id="ide-primary-workspace-surfaces-menu">
- <section id="ide-primary-workspace-surfaces-menu-section">
- <attribute name="label" translatable="yes">Switch Surface</attribute>
- </section>
- <section id="ide-primary-workspace-surfaces-menu-utils-section"/>
- </menu>
<menu id="ide-primary-workspace-menu">
+ <section id="ide-primary-workspace-menu-theme-section">
+ <item>
+ <attribute name="custom">theme_selector</attribute>
+ </item>
+ </section>
<section id="ide-primary-workspace-menu-projects-section"/>
<section id="ide-primary-workspace-menu-placeholder1"/>
- <section id="ide-primary-workspace-menu-placeholder2"/>
<section id="ide-primary-workspace-menu-close-section">
<item>
<attribute name="id">ide-primary-workspace-menu-close-project</attribute>
@@ -17,6 +15,7 @@
<attribute name="action">workbench.close</attribute>
</item>
</section>
+ <section id="ide-primary-workspace-menu-placeholder2"/>
<section id="ide-primary-workspace-menu-placeholder3"/>
<section id="ide-primary-workspace-menu-app-section">
<item>
@@ -28,7 +27,7 @@
<item>
<attribute name="id">ide-primary-workspace-menu-shortcuts</attribute>
<attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
- <attribute name="action">app.shortcuts</attribute>
+ <attribute name="action">win.show-help-overlay</attribute>
<attribute name="accel"><primary>question</attribute>
</item>
<item>
@@ -51,37 +50,11 @@
</item>
</section>
</menu>
- <menu id="ide-primary-workspace-new-menu">
- <section id="new-document-section">
- </section>
- <section id="open-document-section">
- </section>
- </menu>
- <menu id="run-menu">
- <section id="run-menu-section">
- <attribute name="label" translatable="yes">Run Options</attribute>
- <item>
- <attribute name="id">default-run-handler</attribute>
- <attribute name="action">run-manager.run-with-handler</attribute>
- <attribute name="target">run</attribute>
- <attribute name="label" translatable="yes">Run</attribute>
- <attribute name="verb-icon-name">builder-run-start-symbolic</attribute>
- <attribute name="accel"><Control>F5</attribute>
- </item>
- </section>
- </menu>
- <menu id="project-tree-menu">
- <section id="project-tree-menu-placeholder0"/>
- <section id="project-tree-menu-new-section"/>
- <section id="project-tree-menu-placeholder1"/>
- <section id="project-tree-menu-open-section"/>
- <section id="project-tree-menu-buildui"/>
- <section id="project-tree-menu-vcs"/>
- <section id="project-tree-menu-placeholder2"/>
- <section id="project-tree-menu-destructive-section"/>
- <section id="project-tree-menu-placeholder3"/>
- <section id="project-tree-menu-display-options-parent-section"/>
- <section id="project-tree-menu-placeholder4"/>
+ <menu id="new-document-menu">
+ <section id="new-document-section"/>
+ <section id="open-document-section"/>
+ <section id="new-browser-section"/>
+ <section id="new-terminal-section"/>
</menu>
</interface>
diff --git a/src/libide/gui/ide-frame.c b/src/libide/gui/ide-frame.c
index 793269914..9ac1f93e4 100644
--- a/src/libide/gui/ide-frame.c
+++ b/src/libide/gui/ide-frame.c
@@ -1,6 +1,6 @@
/* ide-frame.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2017-2021 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
@@ -22,20 +22,15 @@
#include "config.h"
-#include <dazzle.h>
+#include <adwaita.h>
#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
#include <libide-core.h>
#include <libide-threading.h>
-#include <libpeas/peas.h>
#include "ide-frame.h"
#include "ide-frame-addin.h"
-#include "ide-frame-header.h"
-#include "ide-frame-wrapper.h"
-#include "ide-gui-private.h"
-
-#define TRANSITION_DURATION 300
-#define DISTANCE_THRESHOLD(alloc) (MIN(250, (gint)((alloc)->width * .333)))
/**
* SECTION:ide-frame
@@ -48,140 +43,25 @@
*
* If there are no #IdePage visibile, then an empty state widget is
* displayed with some common information for the user.
- *
- * To simplify integration with other systems, #IdeFrame implements
- * the #GListModel interface for each of the #IdePage.
- *
- * Since: 3.32
*/
-typedef struct
-{
- DzlBindingGroup *bindings;
- DzlSignalGroup *signals;
- GPtrArray *pages;
- GPtrArray *in_transition;
- PeasExtensionSet *addins;
-
- /*
- * Our gestures are used to do interactive moves when the user
- * does a three finger swipe. We create the dummy gesture to
- * ensure things work, because it for some reason does not without
- * the dummy gesture set.
- *
- * https://bugzilla.gnome.org/show_bug.cgi?id=788914
- */
- GtkGesture *dummy;
- GtkGesture *pan;
- DzlBoxTheatric *pan_theatric;
- IdePage *pan_page;
-
- /* Template references */
- GtkBox *empty_placeholder;
- DzlEmptyState *failed_state;
- IdeFrameHeader *header;
- GtkStack *stack;
- GtkStack *top_stack;
- GtkEventBox *event_box;
-} IdeFramePrivate;
-
-typedef struct
+struct _IdeFrame
{
- IdeFrame *source;
- IdeFrame *dest;
- IdePage *page;
- DzlBoxTheatric *theatric;
-} AnimationState;
+ PanelFrame parent_instance;
+ PeasExtensionSet *addins;
+ guint use_tabbar : 1;
+};
+
+G_DEFINE_TYPE (IdeFrame, ide_frame, PANEL_TYPE_FRAME)
enum {
PROP_0,
- PROP_HAS_VIEW,
- PROP_VISIBLE_CHILD,
+ PROP_USE_TABBAR,
N_PROPS
};
-enum {
- CHANGE_CURRENT_PAGE,
- N_SIGNALS
-};
-
-static void buildable_iface_init (GtkBuildableIface *iface);
-static void list_model_iface_init (GListModelInterface *iface);
-static void animation_state_complete (gpointer data);
-
-G_DEFINE_TYPE_WITH_CODE (IdeFrame, ide_frame, GTK_TYPE_BOX,
- G_ADD_PRIVATE (IdeFrame)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)
- G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
-
-static GParamSpec *properties [N_PROPS];
-static guint signals [N_SIGNALS];
-
-static inline gboolean
-is_uninitialized (GtkAllocation *alloc)
-{
- return (alloc->x == -1 && alloc->y == -1 &&
- alloc->width == 1 && alloc->height == 1);
-}
-
-static void
-ide_frame_set_cursor (IdeFrame *self,
- const gchar *name)
-{
- GdkWindow *window;
- GdkDisplay *display;
- GdkCursor *cursor;
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (name != NULL);
-
- window = gtk_widget_get_window (GTK_WIDGET (self));
- display = gtk_widget_get_display (GTK_WIDGET (self));
- cursor = gdk_cursor_new_from_name (display, name);
-
- gdk_window_set_cursor (window, cursor);
-
- g_clear_object (&cursor);
-}
-
-static void
-ide_frame_page_failed (IdeFrame *self,
- GParamSpec *pspec,
- IdePage *page)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (IDE_IS_PAGE (page));
-
- if (ide_page_get_failed (page))
- gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
- else
- gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
-}
-
-static void
-ide_frame_bindings_notify_source (IdeFrame *self,
- GParamSpec *pspec,
- DzlBindingGroup *bindings)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- GObject *source;
-
- g_assert (DZL_IS_BINDING_GROUP (bindings));
- g_assert (pspec != NULL);
- g_assert (IDE_IS_FRAME (self));
-
- source = dzl_binding_group_get_source (bindings);
-
- if (source == NULL)
- {
- _ide_frame_header_set_title (priv->header, _("No Open Pages"));
- _ide_frame_header_set_modified (priv->header, FALSE);
- _ide_frame_header_set_background_rgba (priv->header, NULL);
- _ide_frame_header_set_foreground_rgba (priv->header, NULL);
- }
-}
+static GSettings *editor_settings;
+static GParamSpec *properties[N_PROPS];
static void
ide_frame_notify_addin_of_page (PeasExtensionSet *set,
@@ -202,215 +82,18 @@ ide_frame_notify_addin_of_page (PeasExtensionSet *set,
static void
ide_frame_notify_visible_child (IdeFrame *self,
- GParamSpec *pspec,
- GtkStack *stack)
+ GParamSpec *pspec)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- GtkWidget *visible_child;
+ PanelWidget *visible_child;
g_assert (IDE_IS_FRAME (self));
- g_assert (GTK_IS_STACK (stack));
-
- if (gtk_widget_in_destruction (GTK_WIDGET (self)))
- return;
-
- if ((visible_child = gtk_stack_get_visible_child (priv->stack)))
- {
- if (gtk_widget_in_destruction (visible_child))
- visible_child = NULL;
- }
-
- /*
- * Mux/Proxy actions to our level so that they also be activated
- * from the header bar without any weirdness by the View.
- */
- dzl_gtk_widget_mux_action_groups (GTK_WIDGET (self), visible_child,
- "IDE_FRAME_MUXED_ACTION");
-
- /* Update our bindings targets */
- dzl_binding_group_set_source (priv->bindings, visible_child);
- dzl_signal_group_set_target (priv->signals, visible_child);
-
- /* Show either the empty state, failed state, or actual page */
- if (visible_child != NULL &&
- ide_page_get_failed (IDE_PAGE (visible_child)))
- gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
- else if (visible_child != NULL)
- gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
- else
- gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->empty_placeholder));
-
- /* Allow the header to update settings */
- _ide_frame_header_update (priv->header, IDE_PAGE (visible_child));
- /* Ensure action state is up to date */
- _ide_frame_update_actions (self);
+ visible_child = panel_frame_get_visible_child (PANEL_FRAME (self));
- if (priv->addins != NULL)
- peas_extension_set_foreach (priv->addins,
+ if (self->addins != NULL)
+ peas_extension_set_foreach (self->addins,
ide_frame_notify_addin_of_page,
visible_child);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_CHILD]);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_VIEW]);
-}
-
-static void
-collect_widgets (GtkWidget *widget,
- gpointer user_data)
-{
- g_ptr_array_add (user_data, widget);
-}
-
-static void
-ide_frame_change_current_page (IdeFrame *self,
- gint direction)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_autoptr(GPtrArray) ar = NULL;
- GtkWidget *visible_child;
- gint position = 0;
-
- g_assert (IDE_IS_FRAME (self));
-
- if (direction < -1)
- direction = -1;
- else if (direction > 1)
- direction = 1;
-
- visible_child = gtk_stack_get_visible_child (priv->stack);
-
- if (visible_child == NULL)
- return;
-
- gtk_container_child_get (GTK_CONTAINER (priv->stack), visible_child,
- "position", &position,
- NULL);
-
- ar = g_ptr_array_new ();
- gtk_container_foreach (GTK_CONTAINER (priv->stack), collect_widgets, ar);
- if (ar->len == 0)
- g_return_if_reached ();
-
- position = (position + (int)ar->len - direction) % (int)ar->len;
-
- visible_child = g_ptr_array_index (ar, position);
- gtk_stack_set_visible_child (priv->stack, visible_child);
-}
-
-static void
-ide_frame_add (GtkContainer *container,
- GtkWidget *widget)
-{
- IdeFrame *self = (IdeFrame *)container;
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- if (IDE_IS_PAGE (widget))
- gtk_container_add (GTK_CONTAINER (priv->stack), widget);
- else
- GTK_CONTAINER_CLASS (ide_frame_parent_class)->add (container, widget);
-
- gtk_widget_queue_resize (GTK_WIDGET (self));
-}
-
-static void
-ide_frame_page_added (IdeFrame *self,
- IdePage *page)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- guint position;
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (IDE_IS_PAGE (page));
-
- /*
- * Make sure that the header has dismissed all of the popovers immediately.
- * We don't want them lingering while we do other UI work which might want to
- * grab focus, etc.
- */
- _ide_frame_header_popdown (priv->header);
-
- /* Notify GListModel consumers of the new page and it's position within
- * our stack of page widgets.
- */
- position = priv->pages->len;
- g_ptr_array_add (priv->pages, page);
- g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
-
- /*
- * Now ensure that the page is displayed and focus the widget so the
- * user can immediately start typing.
- */
- ide_frame_set_visible_child (self, page);
- gtk_widget_grab_focus (GTK_WIDGET (page));
-}
-
-static void
-ide_frame_page_removed (IdeFrame *self,
- IdePage *page)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (IDE_IS_PAGE (page));
-
- if (priv->pages != NULL)
- {
- guint position = 0;
-
- /* If this is the last page, hide the popdown now. We use our hide
- * variant instead of popdown so that we don't have jittery animations.
- */
- if (priv->pages->len == 1)
- _ide_frame_header_hide (priv->header);
-
- /*
- * Only remove the page if it is not in transition. We hold onto the
- * page during the transition so that we keep the list stable.
- */
- if (!g_ptr_array_find_with_equal_func (priv->in_transition, page, NULL, &position))
- {
- for (guint i = 0; i < priv->pages->len; i++)
- {
- if ((gpointer)page == g_ptr_array_index (priv->pages, i))
- {
- g_ptr_array_remove_index (priv->pages, i);
- g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
- }
- }
- }
- }
-}
-
-static void
-ide_frame_real_agree_to_close_async (IdeFrame *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_autoptr(IdeTask) task = NULL;
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_frame_real_agree_to_close_async);
- ide_task_set_priority (task, G_PRIORITY_LOW);
- ide_task_return_boolean (task, TRUE);
-}
-
-static gboolean
-ide_frame_real_agree_to_close_finish (IdeFrame *self,
- GAsyncResult *result,
- GError **error)
-{
- g_assert (IDE_IS_FRAME (self));
- g_assert (IDE_IS_TASK (result));
-
- return ide_task_propagate_boolean (IDE_TASK (result), error);
}
static void
@@ -430,7 +113,7 @@ ide_frame_addin_added (PeasExtensionSet *set,
ide_frame_addin_load (addin, self);
- visible_child = ide_frame_get_visible_child (self);
+ visible_child = IDE_PAGE (panel_frame_get_visible_child (PANEL_FRAME (self)));
if (visible_child != NULL)
ide_frame_addin_set_page (addin, visible_child);
@@ -454,351 +137,79 @@ ide_frame_addin_removed (PeasExtensionSet *set,
ide_frame_addin_unload (addin, self);
}
-static gboolean
-ide_frame_pan_begin (IdeFrame *self,
- GdkEventSequence *sequence,
- GtkGesturePan *gesture)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- GtkAllocation alloc;
- cairo_surface_t *surface = NULL;
- GdkDevice *device;
- IdePage *page;
- GdkWindow *window;
- GtkWidget *grid;
- cairo_t *cr;
- gdouble x, y;
- gboolean enable_animations;
- GdkModifierType state = 0;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (GTK_IS_GESTURE_PAN (gesture));
- g_assert (priv->pan_theatric == NULL);
-
- if (!(page = ide_frame_get_visible_child (self)) ||
- !(device = gtk_gesture_get_device (GTK_GESTURE (gesture))) ||
- !(window = gtk_widget_get_window (GTK_WIDGET (page))))
- goto failure;
-
- gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
- gdk_device_get_state (device, window, NULL, &state);
-
- g_object_get (gtk_settings_get_default (),
- "gtk-enable-animations", &enable_animations,
- NULL);
-
- if (sequence != NULL ||
- !enable_animations ||
- (state & GDK_SHIFT_MASK) == 0 ||
- is_uninitialized (&alloc) ||
- NULL == (surface = gdk_window_create_similar_surface (window,
- CAIRO_CONTENT_COLOR,
- alloc.width,
- alloc.height)))
- goto failure;
-
- gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
-
- cr = cairo_create (surface);
- gtk_widget_draw (GTK_WIDGET (page), cr);
- cairo_destroy (cr);
-
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
- gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
- &alloc.x, &alloc.y);
-
- priv->pan_page = g_object_ref (page);
- priv->pan_theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
- "surface", surface,
- "target", grid,
- "x", alloc.x + (gint)x,
- "y", alloc.y,
- "width", alloc.width,
- "height", alloc.height,
- NULL);
-
- g_clear_pointer (&surface, cairo_surface_destroy);
-
- /* Hide the page while we begin the possible transition to another
- * layout stack.
- */
- gtk_widget_hide (GTK_WIDGET (priv->pan_page));
-
- /*
- * Hide the mouse cursor until ide_frame_pan_end() is called.
- * It can be distracting otherwise (and we want to warp it to the new
- * grid column too).
- */
- ide_frame_set_cursor (self, "none");
-
- IDE_RETURN (TRUE);
-
-failure:
- if (sequence != NULL)
- gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-
- IDE_RETURN (FALSE);
-}
-
static void
-ide_frame_pan_update (IdeFrame *self,
- GdkEventSequence *sequence,
- GtkGestureSwipe *gesture)
+ide_frame_reload_addins (IdeFrame *self)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- GtkAllocation alloc;
- GtkWidget *grid;
- gdouble x, y;
-
IDE_ENTRY;
g_assert (IDE_IS_FRAME (self));
- g_assert (GTK_IS_GESTURE_PAN (gesture));
- g_assert (!priv->pan_theatric || DZL_IS_BOX_THEATRIC (priv->pan_theatric));
-
- if (priv->pan_theatric == NULL)
- {
- if (sequence != NULL)
- gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
- IDE_EXIT;
- }
-
- gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
- gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
- &alloc.x, &alloc.y);
-
- g_object_set (priv->pan_theatric,
- "x", alloc.x + (gint)x,
- NULL);
-
- IDE_EXIT;
-}
-
-static void
-ide_frame_pan_end (IdeFrame *self,
- GdkEventSequence *sequence,
- GtkGesturePan *gesture)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- IdeFramePrivate *dest_priv;
- IdeFrame *dest;
- GtkAllocation alloc;
- GtkWidget *grid;
- GtkWidget *column;
- gdouble x, y;
- gint direction;
- gint index = 0;
-
- IDE_ENTRY;
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (GTK_IS_GESTURE_PAN (gesture));
-
- if (priv->pan_theatric == NULL || priv->pan_page == NULL)
- IDE_GOTO (cleanup);
-
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
-
- if (x > DISTANCE_THRESHOLD (&alloc))
- direction = 1;
- else if (x < -DISTANCE_THRESHOLD (&alloc))
- direction = -1;
- else
- direction = 0;
-
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
- g_assert (grid != NULL);
- g_assert (IDE_IS_GRID (grid));
-
- column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN);
- g_assert (column != NULL);
- g_assert (IDE_IS_GRID_COLUMN (column));
-
- gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
- "index", &index,
- NULL);
-
- dest = _ide_grid_get_nth_stack (IDE_GRID (grid), index + direction);
- dest_priv = ide_frame_get_instance_private (dest);
- g_assert (dest != NULL);
- g_assert (IDE_IS_FRAME (dest));
-
- gtk_widget_get_allocation (GTK_WIDGET (dest), &alloc);
-
- if (!is_uninitialized (&alloc))
- {
- AnimationState *state;
-
- state = g_slice_new0 (AnimationState);
- state->source = g_object_ref (self);
- state->dest = g_object_ref (dest);
- state->page = g_object_ref (priv->pan_page);
- state->theatric = g_object_ref (priv->pan_theatric);
-
- gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->top_stack), grid, 0, 0,
- &alloc.x, &alloc.y);
-
- /*
- * Use EASE_OUT_CUBIC, because user initiated the beginning of the
- * acceleration curve just by swiping. No need to duplicate.
- */
- dzl_object_animate_full (state->theatric,
- DZL_ANIMATION_EASE_OUT_CUBIC,
- TRANSITION_DURATION,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- animation_state_complete,
- state,
- "x", alloc.x,
- "width", alloc.width,
- NULL);
-
- if (dest != self)
- {
- g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_page));
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_page));
- }
- IDE_TRACE_MSG ("Animating transition to %s column",
- dest != self ? "another" : "same");
- }
- else
- {
- g_autoptr(IdePage) page = g_object_ref (priv->pan_page);
-
- IDE_TRACE_MSG ("Moving page to a previously non-existant column");
-
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
- gtk_widget_show (GTK_WIDGET (page));
- gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
- }
-
-cleanup:
- g_clear_object (&priv->pan_theatric);
- g_clear_object (&priv->pan_page);
-
- gtk_widget_queue_draw (gtk_widget_get_toplevel (GTK_WIDGET (self)));
-
- ide_frame_set_cursor (self, "arrow");
-
- IDE_EXIT;
-}
-
-static void
-ide_frame_constructed (GObject *object)
-{
- IdeFrame *self = (IdeFrame *)object;
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_assert (IDE_IS_FRAME (self));
-
- G_OBJECT_CLASS (ide_frame_parent_class)->constructed (object);
-
- priv->addins = peas_extension_set_new (peas_engine_get_default (),
+ g_clear_object (&self->addins);
+ self->addins = peas_extension_set_new (peas_engine_get_default (),
IDE_TYPE_FRAME_ADDIN,
NULL);
-
- g_signal_connect (priv->addins,
+ g_signal_connect (self->addins,
"extension-added",
G_CALLBACK (ide_frame_addin_added),
self);
-
- g_signal_connect (priv->addins,
+ g_signal_connect (self->addins,
"extension-removed",
G_CALLBACK (ide_frame_addin_removed),
self);
+ peas_extension_set_foreach (self->addins, ide_frame_addin_added, self);
- peas_extension_set_foreach (priv->addins,
- ide_frame_addin_added,
- self);
-
- gtk_widget_add_events (GTK_WIDGET (priv->event_box), GDK_TOUCH_MASK);
- priv->pan = g_object_new (GTK_TYPE_GESTURE_PAN,
- "widget", priv->event_box,
- "orientation", GTK_ORIENTATION_HORIZONTAL,
- "n-points", 3,
- NULL);
- g_signal_connect_swapped (priv->pan,
- "begin",
- G_CALLBACK (ide_frame_pan_begin),
- self);
- g_signal_connect_swapped (priv->pan,
- "update",
- G_CALLBACK (ide_frame_pan_update),
- self);
- g_signal_connect_swapped (priv->pan,
- "end",
- G_CALLBACK (ide_frame_pan_end),
- self);
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan),
- GTK_PHASE_BUBBLE);
-
- /*
- * FIXME: Our priv->pan gesture does not activate unless we add another
- * dummy gesture. I currently have no idea why that is.
- *
- * https://bugzilla.gnome.org/show_bug.cgi?id=788914
- */
- priv->dummy = gtk_gesture_rotate_new (GTK_WIDGET (priv->event_box));
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->dummy),
- GTK_PHASE_BUBBLE);
+ IDE_EXIT;
}
static void
-ide_frame_grab_focus (GtkWidget *widget)
+status_page_pressed_cb (IdeFrame *self,
+ double x,
+ double y,
+ int n_press,
+ GtkGestureClick *click)
{
- IdeFrame *self = (IdeFrame *)widget;
- IdePage *child;
+ GtkRoot *root;
g_assert (IDE_IS_FRAME (self));
+ g_assert (GTK_IS_GESTURE_CLICK (click));
- child = ide_frame_get_visible_child (self);
-
- if (child != NULL)
- gtk_widget_grab_focus (GTK_WIDGET (child));
- else
- GTK_WIDGET_CLASS (ide_frame_parent_class)->grab_focus (widget);
+ root = gtk_widget_get_root (GTK_WIDGET (self));
+ gtk_root_set_focus (root, NULL);
}
static void
-ide_frame_destroy (GtkWidget *widget)
+ide_frame_constructed (GObject *object)
{
- IdeFrame *self = (IdeFrame *)widget;
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)object;
+ PanelFrameHeader *header;
g_assert (IDE_IS_FRAME (self));
- g_clear_object (&priv->addins);
+ G_OBJECT_CLASS (ide_frame_parent_class)->constructed (object);
- g_clear_pointer (&priv->in_transition, g_ptr_array_unref);
+ self->use_tabbar = g_settings_get_boolean (editor_settings, "use-tabbar");
+ if (self->use_tabbar)
+ header = PANEL_FRAME_HEADER (panel_frame_tab_bar_new ());
+ else
+ header = PANEL_FRAME_HEADER (panel_frame_header_bar_new ());
+ panel_frame_set_header (PANEL_FRAME (self), header);
+ g_settings_bind (editor_settings, "use-tabbar",
+ self, "use-tabbar",
+ G_SETTINGS_BIND_GET);
- if (priv->pages != NULL)
- {
- g_list_model_items_changed (G_LIST_MODEL (self), 0, priv->pages->len, 0);
- g_clear_pointer (&priv->pages, g_ptr_array_unref);
- }
+ ide_frame_reload_addins (self);
+}
- if (priv->bindings != NULL)
- {
- dzl_binding_group_set_source (priv->bindings, NULL);
- g_clear_object (&priv->bindings);
- }
+static void
+ide_frame_dispose (GObject *object)
+{
+ IdeFrame *self = (IdeFrame *)object;
- if (priv->signals != NULL)
- {
- dzl_signal_group_set_target (priv->signals, NULL);
- g_clear_object (&priv->signals);
- }
+ g_assert (IDE_IS_FRAME (self));
- g_clear_object (&priv->pan);
+ g_clear_object (&self->addins);
- GTK_WIDGET_CLASS (ide_frame_parent_class)->destroy (widget);
+ G_OBJECT_CLASS (ide_frame_parent_class)->dispose (object);
}
static void
@@ -811,12 +222,8 @@ ide_frame_get_property (GObject *object,
switch (prop_id)
{
- case PROP_HAS_VIEW:
- g_value_set_boolean (value, ide_frame_get_has_page (self));
- break;
-
- case PROP_VISIBLE_CHILD:
- g_value_set_object (value, ide_frame_get_visible_child (self));
+ case PROP_USE_TABBAR:
+ g_value_set_boolean (value, ide_frame_get_use_tabbar (self));
break;
default:
@@ -834,8 +241,8 @@ ide_frame_set_property (GObject *object,
switch (prop_id)
{
- case PROP_VISIBLE_CHILD:
- ide_frame_set_visible_child (self, g_value_get_object (value));
+ case PROP_USE_TABBAR:
+ ide_frame_set_use_tabbar (self, g_value_get_boolean (value));
break;
default:
@@ -848,121 +255,37 @@ ide_frame_class_init (IdeFrameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
object_class->constructed = ide_frame_constructed;
+ object_class->dispose = ide_frame_dispose;
object_class->get_property = ide_frame_get_property;
object_class->set_property = ide_frame_set_property;
- widget_class->destroy = ide_frame_destroy;
- widget_class->grab_focus = ide_frame_grab_focus;
-
- container_class->add = ide_frame_add;
-
- klass->agree_to_close_async = ide_frame_real_agree_to_close_async;
- klass->agree_to_close_finish = ide_frame_real_agree_to_close_finish;
-
- properties [PROP_HAS_VIEW] =
- g_param_spec_boolean ("has-page", NULL, NULL,
+ properties [PROP_USE_TABBAR] =
+ g_param_spec_boolean ("use-tabbar",
+ "Use Tabbar",
+ "If tabs should be used",
FALSE,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_VISIBLE_CHILD] =
- g_param_spec_object ("visible-child",
- "Visible Child",
- "The current page to be displayed",
- IDE_TYPE_PAGE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
- signals [CHANGE_CURRENT_PAGE] =
- g_signal_new_class_handler ("change-current-page",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_frame_change_current_page),
- NULL, NULL,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1, G_TYPE_INT);
-
- gtk_widget_class_set_css_name (widget_class, "ideframe");
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-frame.ui");
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, empty_placeholder);
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, failed_state);
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, header);
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, stack);
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, top_stack);
- gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, event_box);
-
- g_type_ensure (IDE_TYPE_FRAME_HEADER);
- g_type_ensure (IDE_TYPE_FRAME_WRAPPER);
- g_type_ensure (IDE_TYPE_SHORTCUT_LABEL);
+ gtk_widget_class_bind_template_callback (widget_class, status_page_pressed_cb);
}
static void
ide_frame_init (IdeFrame *self)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ if (editor_settings == NULL)
+ editor_settings = g_settings_new ("org.gnome.builder.editor");
gtk_widget_init_template (GTK_WIDGET (self));
- _ide_frame_init_actions (self);
- _ide_frame_init_shortcuts (self);
-
- priv->pages = g_ptr_array_new ();
- priv->in_transition = g_ptr_array_new_with_free_func (g_object_unref);
-
- priv->signals = dzl_signal_group_new (IDE_TYPE_PAGE);
-
- dzl_signal_group_connect_swapped (priv->signals,
- "notify::failed",
- G_CALLBACK (ide_frame_page_failed),
- self);
-
- priv->bindings = dzl_binding_group_new ();
-
- g_signal_connect_object (priv->bindings,
- "notify::source",
- G_CALLBACK (ide_frame_bindings_notify_source),
- self,
- G_CONNECT_SWAPPED);
-
- dzl_binding_group_bind (priv->bindings, "title",
- priv->header, "title",
- G_BINDING_SYNC_CREATE);
-
- dzl_binding_group_bind (priv->bindings, "modified",
- priv->header, "modified",
- G_BINDING_SYNC_CREATE);
-
- dzl_binding_group_bind (priv->bindings, "primary-color-bg",
- priv->header, "background-rgba",
- G_BINDING_SYNC_CREATE);
-
- dzl_binding_group_bind (priv->bindings, "primary-color-fg",
- priv->header, "foreground-rgba",
- G_BINDING_SYNC_CREATE);
-
- g_signal_connect_object (priv->stack,
- "notify::visible-child",
- G_CALLBACK (ide_frame_notify_visible_child),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (priv->stack,
- "add",
- G_CALLBACK (ide_frame_page_added),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
- g_signal_connect_object (priv->stack,
- "remove",
- G_CALLBACK (ide_frame_page_removed),
- self,
- G_CONNECT_SWAPPED);
-
- _ide_frame_header_set_pages (priv->header, G_LIST_MODEL (self));
- _ide_frame_header_update (priv->header, NULL);
+ g_signal_connect (self,
+ "notify::visible-child",
+ G_CALLBACK (ide_frame_notify_visible_child),
+ NULL);
}
GtkWidget *
@@ -972,496 +295,122 @@ ide_frame_new (void)
}
/**
- * ide_frame_set_visible_child:
- * @self: a #IdeFrame
+ * ide_frame_addin_find_by_module_name:
+ * @frame: An #IdeFrame
+ * @module_name: the module name which provides the addin
*
- * Sets the current page for the stack.
+ * This function will locate the #IdeFrameAddin that was registered by
+ * the plugin named @module_name (which should match the "Module" field
+ * provided in the .plugin file).
+ *
+ * If no module was found or that module does not implement the
+ * #IdeFrameAddinInterface, then %NULL is returned.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): An #IdeFrameAddin or %NULL
*/
-void
-ide_frame_set_visible_child (IdeFrame *self,
- IdePage *page)
+IdeFrameAddin *
+ide_frame_addin_find_by_module_name (IdeFrame *frame,
+ const gchar *module_name)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (IDE_IS_PAGE (page));
- g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (page)) == (GtkWidget *)priv->stack);
+ PeasExtension *ret = NULL;
+ PeasPluginInfo *plugin_info;
- gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (page));
-}
+ g_return_val_if_fail (IDE_IS_FRAME (frame), NULL);
+ g_return_val_if_fail (frame->addins != NULL, NULL);
+ g_return_val_if_fail (module_name != NULL, NULL);
-/**
- * ide_frame_get_visible_child:
- * @self: a #IdeFrame
- *
- * Gets the visible #IdePage if there is one; otherwise %NULL.
- *
- * Returns: (nullable) (transfer none): An #IdePage or %NULL
- *
- * Since: 3.32
- */
-IdePage *
-ide_frame_get_visible_child (IdeFrame *self)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name);
- g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
+ if (plugin_info != NULL)
+ ret = peas_extension_set_get_extension (frame->addins, plugin_info);
+ else
+ g_warning ("No addin could be found matching module \"%s\"", module_name);
- return IDE_PAGE (gtk_stack_get_visible_child (priv->stack));
+ return ret ? IDE_FRAME_ADDIN (ret) : NULL;
}
/**
- * ide_frame_get_titlebar:
+ * ide_frame_get_position:
* @self: a #IdeFrame
*
- * Gets the #IdeFrameHeader header that is at the top of the stack.
- *
- * Returns: (transfer none) (type IdeFrameHeader): The layout stack header.
+ * Gets the position in the grid of a frame.
*
- * Since: 3.32
+ * Returns: (transfer full): a new #IdePanelPosition
*/
-GtkWidget *
-ide_frame_get_titlebar (IdeFrame *self)
+IdePanelPosition *
+ide_frame_get_position (IdeFrame *self)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ IdePanelPosition *ret;
+ PanelGrid *grid;
+ guint n_columns;
g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
- return GTK_WIDGET (priv->header);
-}
-
-/**
- * ide_frame_get_has_page:
- * @self: an #IdeFrame
- *
- * Gets the "has-page" property.
- *
- * This property is a convenience to allow widgets to easily bind
- * properties based on whether or not a page is visible in the stack.
- *
- * Returns: %TRUE if the stack has a page
- *
- * Since: 3.32
- */
-gboolean
-ide_frame_get_has_page (IdeFrame *self)
-{
- IdePage *visible_child;
-
- g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
-
- visible_child = ide_frame_get_visible_child (self);
-
- return visible_child != NULL;
-}
-
-static void
-ide_frame_close_page_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- IdePage *page = (IdePage *)object;
- g_autoptr(IdeFrame) self = user_data;
- g_autoptr(GError) error = NULL;
- GtkWidget *toplevel;
- GtkWidget *focus;
- gboolean had_focus = FALSE;
-
- g_assert (IDE_IS_PAGE (page));
- g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_FRAME (self));
-
- if (!ide_page_agree_to_close_finish (page, result, &error))
- {
- g_message ("%s does not agree to close: %s",
- G_OBJECT_TYPE_NAME (page),
- error ? error->message : "No reason");
- return;
- }
-
- /* Keep track of whether or not the widget had focus (which
- * would happen if we were activated from a keybinding.
- */
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
- if (GTK_IS_WINDOW (toplevel) &&
- NULL != (focus = gtk_window_get_focus (GTK_WINDOW (toplevel))) &&
- (focus == GTK_WIDGET (page) ||
- gtk_widget_is_ancestor (focus, GTK_WIDGET (page))))
- had_focus = TRUE;
-
- /* Now we can destroy the child */
- gtk_widget_destroy (GTK_WIDGET (page));
-
- /* We don't want to leave the widget focus in an indeterminate
- * state so we immediately focus the next child in the stack.
- * But only do so if we had focus previously.
- */
- if (had_focus)
- {
- IdePage *visible_child = ide_frame_get_visible_child (self);
-
- if (visible_child != NULL)
- gtk_widget_grab_focus (GTK_WIDGET (visible_child));
- }
-}
-
-void
-_ide_frame_request_close (IdeFrame *self,
- IdePage *page)
-{
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (IDE_IS_PAGE (page));
-
- ide_page_agree_to_close_async (page,
- NULL,
- ide_frame_close_page_cb,
- g_object_ref (self));
-}
-
-static GType
-ide_frame_get_item_type (GListModel *model)
-{
- return IDE_TYPE_PAGE;
-}
-
-static guint
-ide_frame_get_n_items (GListModel *model)
-{
- IdeFrame *self = (IdeFrame *)model;
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_assert (IDE_IS_FRAME (self));
-
- return priv->pages ? priv->pages->len : 0;
-}
-
-static gpointer
-ide_frame_get_item (GListModel *model,
- guint position)
-{
- IdeFrame *self = (IdeFrame *)model;
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_assert (IDE_IS_FRAME (self));
- g_assert (position < priv->pages->len);
+ /* Frames are always in the center grid */
+ ret = ide_panel_position_new ();
+ ide_panel_position_set_edge (ret, PANEL_DOCK_POSITION_CENTER);
- return g_object_ref (g_ptr_array_index (priv->pages, position));
-}
+ /* Implausible but handle it anyway */
+ grid = PANEL_GRID (gtk_widget_get_ancestor (GTK_WIDGET (self), PANEL_TYPE_GRID));
+ if (grid == NULL)
+ return ret;
-static void
-list_model_iface_init (GListModelInterface *iface)
-{
- iface->get_n_items = ide_frame_get_n_items;
- iface->get_item = ide_frame_get_item;
- iface->get_item_type = ide_frame_get_item_type;
-}
+ n_columns = panel_grid_get_n_columns (grid);
-void
-ide_frame_agree_to_close_async (IdeFrame *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- IDE_FRAME_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
-}
-
-gboolean
-ide_frame_agree_to_close_finish (IdeFrame *self,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
- return IDE_FRAME_GET_CLASS (self)->agree_to_close_finish (self, result, error);
-}
-
-static void
-animation_state_complete (gpointer data)
-{
- IdeFramePrivate *priv;
- AnimationState *state = data;
-
- g_assert (state != NULL);
- g_assert (IDE_IS_FRAME (state->source));
- g_assert (IDE_IS_FRAME (state->dest));
- g_assert (IDE_IS_PAGE (state->page));
-
- /* Add the widget to the new stack */
- if (state->dest != state->source)
+ for (guint c = 0; c < n_columns; c++)
{
- gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->page));
+ PanelGridColumn *grid_column = panel_grid_get_column (grid, c);
+ guint n_rows = panel_grid_column_get_n_rows (grid_column);
- /* Now remove it from our temporary transition. Be careful in case we were
- * destroyed in the mean time.
- */
- priv = ide_frame_get_instance_private (state->source);
-
- if (priv->in_transition != NULL)
+ for (guint r = 0; r < n_rows; r++)
{
- guint position = 0;
+ PanelFrame *frame = panel_grid_column_get_row (grid_column, r);
- if (g_ptr_array_find_with_equal_func (priv->pages, state->page, NULL, &position))
+ if (frame == PANEL_FRAME (self))
{
- g_ptr_array_remove (priv->in_transition, state->page);
- g_ptr_array_remove_index (priv->pages, position);
- g_list_model_items_changed (G_LIST_MODEL (state->source), position, 1, 0);
+ ide_panel_position_set_column (ret, c);
+ ide_panel_position_set_row (ret, r);
+ return ret;
}
}
}
- /*
- * We might need to reshow the widget in cases where we are in a
- * three-finger-swipe of the page. There is also a chance that we
- * aren't the proper visible child and that needs to be restored now.
- */
- gtk_widget_show (GTK_WIDGET (state->page));
- ide_frame_set_visible_child (state->dest, state->page);
-
- g_clear_object (&state->source);
- g_clear_object (&state->dest);
- g_clear_object (&state->page);
- g_clear_object (&state->theatric);
- g_slice_free (AnimationState, state);
-}
+ g_critical ("Failed to locate frame within grid");
-void
-_ide_frame_transfer (IdeFrame *self,
- IdeFrame *dest,
- IdePage *page)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- IdeFramePrivate *dest_priv = ide_frame_get_instance_private (dest);
- const GdkRGBA *fg;
- const GdkRGBA *bg;
-
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (IDE_IS_FRAME (dest));
- g_return_if_fail (IDE_IS_PAGE (page));
- g_return_if_fail (GTK_WIDGET (priv->stack) == gtk_widget_get_parent (GTK_WIDGET (page)));
-
- /*
- * Inform the destination stack about our new primary colors so that it can
- * begin a transition to the new colors. We also want to do this upfront so
- * that we can reduce the amount of style invalidation caused during the
- * transitions.
- */
-
- fg = ide_page_get_primary_color_fg (page);
- bg = ide_page_get_primary_color_bg (page);
- _ide_frame_header_set_foreground_rgba (dest_priv->header, fg);
- _ide_frame_header_set_background_rgba (dest_priv->header, bg);
-
- /*
- * If both the old and the new stacks are mapped, we can animate
- * between them using a snapshot of the page. Well, we also need
- * to be sure they have a valid allocation, but that check is done
- * slightly after this because it makes things easier.
- */
- if (gtk_widget_get_mapped (GTK_WIDGET (self)) &&
- gtk_widget_get_mapped (GTK_WIDGET (dest)) &&
- gtk_widget_get_mapped (GTK_WIDGET (page)))
- {
- GtkAllocation alloc, dest_alloc;
- cairo_surface_t *surface = NULL;
- GdkWindow *window;
- GtkWidget *grid;
- gboolean enable_animations;
-
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
-
- gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
- gtk_widget_get_allocation (GTK_WIDGET (dest), &dest_alloc);
-
- g_object_get (gtk_settings_get_default (),
- "gtk-enable-animations", &enable_animations,
- NULL);
-
- if (enable_animations &&
- grid != NULL &&
- !is_uninitialized (&alloc) &&
- !is_uninitialized (&dest_alloc) &&
- dest_alloc.width > 0 && dest_alloc.height > 0 &&
- NULL != (window = gtk_widget_get_window (GTK_WIDGET (page))) &&
- NULL != (surface = gdk_window_create_similar_surface (window,
- CAIRO_CONTENT_COLOR,
- alloc.width,
- alloc.height)))
- {
- DzlBoxTheatric *theatric = NULL;
- AnimationState *state;
- cairo_t *cr;
-
- cr = cairo_create (surface);
- gtk_widget_draw (GTK_WIDGET (page), cr);
- cairo_destroy (cr);
-
- gtk_widget_translate_coordinates (GTK_WIDGET (priv->stack), grid, 0, 0,
- &alloc.x, &alloc.y);
- gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->stack), grid, 0, 0,
- &dest_alloc.x, &dest_alloc.y);
-
- theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
- "surface", surface,
- "height", alloc.height,
- "target", grid,
- "width", alloc.width,
- "x", alloc.x,
- "y", alloc.y,
- NULL);
-
- state = g_slice_new0 (AnimationState);
- state->source = g_object_ref (self);
- state->dest = g_object_ref (dest);
- state->page = g_object_ref (page);
- state->theatric = theatric;
-
- dzl_object_animate_full (theatric,
- DZL_ANIMATION_EASE_IN_OUT_CUBIC,
- TRANSITION_DURATION,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- animation_state_complete,
- state,
- "x", dest_alloc.x,
- "width", dest_alloc.width,
- "y", dest_alloc.y,
- "height", dest_alloc.height,
- NULL);
-
- /*
- * Mark the page as in-transition so that when we remove it
- * we can ignore the items-changed until the animation completes.
- */
- g_ptr_array_add (priv->in_transition, g_object_ref (page));
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
-
- cairo_surface_destroy (surface);
-
- return;
- }
- }
-
- g_object_ref (page);
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
- gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
- g_object_unref (page);
+ return ret;
}
-/**
- * ide_frame_foreach_page:
- * @self: a #IdeFrame
- * @callback: (scope call) (closure user_data): A callback for each page
- * @user_data: user data for @callback
- *
- * This function will call @callback for every page found in @self.
- *
- * Since: 3.32
- */
-void
-ide_frame_foreach_page (IdeFrame *self,
- GtkCallback callback,
- gpointer user_data)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (callback != NULL);
-
- gtk_container_foreach (GTK_CONTAINER (priv->stack), callback, user_data);
-}
-
-/**
- * ide_frame_addin_find_by_module_name:
- * @frame: An #IdeFrame
- * @module_name: the module name which provides the addin
- *
- * This function will locate the #IdeFrameAddin that was registered by
- * the plugin named @module_name (which should match the "Module" field
- * provided in the .plugin file).
- *
- * If no module was found or that module does not implement the
- * #IdeFrameAddinInterface, then %NULL is returned.
- *
- * Returns: (transfer none) (nullable): An #IdeFrameAddin or %NULL
- *
- * Since: 3.32
- */
-IdeFrameAddin *
-ide_frame_addin_find_by_module_name (IdeFrame *frame,
- const gchar *module_name)
+gboolean
+ide_frame_get_use_tabbar (IdeFrame *self)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (frame);
- PeasExtension *ret = NULL;
- PeasPluginInfo *plugin_info;
-
- g_return_val_if_fail (IDE_IS_FRAME (frame), NULL);
- g_return_val_if_fail (priv->addins != NULL, NULL);
- g_return_val_if_fail (module_name != NULL, NULL);
-
- plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name);
-
- if (plugin_info != NULL)
- ret = peas_extension_set_get_extension (priv->addins, plugin_info);
- else
- g_warning ("No addin could be found matching module \"%s\"", module_name);
+ g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
- return ret ? IDE_FRAME_ADDIN (ret) : NULL;
+ return self->use_tabbar;
}
void
-ide_frame_add_with_depth (IdeFrame *self,
- GtkWidget *widget,
- guint position)
+ide_frame_set_use_tabbar (IdeFrame *self,
+ gboolean use_tabbar)
{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- gtk_container_add_with_properties (GTK_CONTAINER (priv->stack), widget,
- "position", position,
- NULL);
-}
-static void
-ide_frame_add_child (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *object,
- const gchar *type)
-{
- IdeFrame *self = (IdeFrame *)buildable;
- GtkBuildableIface *parent = g_type_interface_peek_parent (GTK_BUILDABLE_GET_IFACE (buildable));
+ use_tabbar = !!use_tabbar;
- if (g_strcmp0 (type, "placeholder") == 0 && GTK_IS_WIDGET (object))
- ide_frame_set_placeholder (self, GTK_WIDGET (object));
- else
- parent->add_child (buildable, builder, object, type);
-}
+ if (use_tabbar != self->use_tabbar)
+ {
+ PanelFrameHeader *header;
-static void
-buildable_iface_init (GtkBuildableIface *iface)
-{
- iface->add_child = ide_frame_add_child;
-}
+ self->use_tabbar = use_tabbar;
-void
-ide_frame_set_placeholder (IdeFrame *self,
- GtkWidget *placeholder)
-{
- IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ if (self->use_tabbar)
+ header = PANEL_FRAME_HEADER (panel_frame_tab_bar_new ());
+ else
+ header = PANEL_FRAME_HEADER (panel_frame_header_bar_new ());
- g_return_if_fail (IDE_IS_FRAME (self));
- g_return_if_fail (!placeholder || GTK_IS_WIDGET (placeholder));
+ panel_frame_set_header (PANEL_FRAME (self), header);
- gtk_container_foreach (GTK_CONTAINER (priv->empty_placeholder),
- (GtkCallback) gtk_widget_destroy,
- NULL);
+ ide_frame_reload_addins (self);
- if (placeholder != NULL)
- gtk_container_add (GTK_CONTAINER (priv->empty_placeholder), placeholder);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_USE_TABBAR]);
+ }
}
diff --git a/src/libide/gui/ide-frame.h b/src/libide/gui/ide-frame.h
index 8b4078e5a..de758fcad 100644
--- a/src/libide/gui/ide-frame.h
+++ b/src/libide/gui/ide-frame.h
@@ -24,64 +24,26 @@
# error "Only <libide-gui.h> can be included directly."
#endif
-#include <gtk/gtk.h>
-#include <libide-core.h>
+#include <libpanel.h>
#include "ide-page.h"
+#include "ide-panel-position.h"
G_BEGIN_DECLS
#define IDE_TYPE_FRAME (ide_frame_get_type())
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeFrame, ide_frame, IDE, FRAME, GtkBox)
-
-struct _IdeFrameClass
-{
- GtkBoxClass parent_class;
-
- void (*agree_to_close_async) (IdeFrame *stack,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*agree_to_close_finish) (IdeFrame *stack,
- GAsyncResult *result,
- GError **error);
-
- /*< private >*/
- gpointer _reserved[16];
-};
-
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_frame_new (void);
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_frame_get_titlebar (IdeFrame *self);
-IDE_AVAILABLE_IN_3_32
-IdePage *ide_frame_get_visible_child (IdeFrame *self);
-IDE_AVAILABLE_IN_3_32
-void ide_frame_set_visible_child (IdeFrame *self,
- IdePage *page);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_frame_get_has_page (IdeFrame *self);
-IDE_AVAILABLE_IN_3_32
-void ide_frame_agree_to_close_async (IdeFrame *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_frame_agree_to_close_finish (IdeFrame *self,
- GAsyncResult *result,
- GError **error);
-IDE_AVAILABLE_IN_3_32
-void ide_frame_foreach_page (IdeFrame *self,
- GtkCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-void ide_frame_add_with_depth (IdeFrame *self,
- GtkWidget *widget,
- guint position);
-IDE_AVAILABLE_IN_3_34
-void ide_frame_set_placeholder (IdeFrame *self,
- GtkWidget *placeholder);
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeFrame, ide_frame, IDE, FRAME, PanelFrame)
+
+IDE_AVAILABLE_IN_ALL
+GtkWidget *ide_frame_new (void);
+IDE_AVAILABLE_IN_ALL
+IdePanelPosition *ide_frame_get_position (IdeFrame *self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_frame_get_use_tabbar (IdeFrame *self);
+IDE_AVAILABLE_IN_ALL
+void ide_frame_set_use_tabbar (IdeFrame *self,
+ gboolean use_tabbar);
G_END_DECLS
diff --git a/src/libide/gui/ide-frame.ui b/src/libide/gui/ide-frame.ui
index 793826110..8c9e363e2 100644
--- a/src/libide/gui/ide-frame.ui
+++ b/src/libide/gui/ide-frame.ui
@@ -1,55 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="IdeFrame" parent="GtkBox">
- <property name="orientation">vertical</property>
- <child>
- <object class="IdeFrameHeader" id="header">
- <property name="show-close-button">true</property>
- <property name="title" translatable="yes">No Open Pages</property>
- <property name="visible">true</property>
- </object>
- </child>
- <child>
- <object class="GtkEventBox" id="event_box">
- <property name="visible">true</property>
- <child>
- <object class="GtkStack" id="top_stack">
- <property name="expand">true</property>
- <property name="homogeneous">false</property>
- <property name="interpolate-size">false</property>
- <property name="visible">true</property>
- <child>
- <object class="GtkBox" id="empty_placeholder">
- <property name="expand">true</property>
- <property name="visible">true</property>
- <child>
- <object class="IdeFrameEmptyState">
- <property name="expand">true</property>
- <property name="visible">true</property>
- </object>
- </child>
+ <template class="IdeFrame" parent="PanelFrame">
+ <property name="placeholder">
+ <object class="AdwStatusPage" id="status">
+ <property name="title" translatable="yes">Open a File or Terminal</property>
+ <property name="description" translatable="yes">Use the page switcher above or use one of the
following:</property>
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="halign">center</property>
+ <property name="column-spacing">48</property>
+ <property name="row-spacing">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Search</property>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Ctrl+Enter</property>
+ <layout>
+ <property name="row">0</property>
+ <property name="column">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Project Sidebar</property>
+ <layout>
+ <property name="row">1</property>
+ <property name="column">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">F9</property>
+ <layout>
+ <property name="row">1</property>
+ <property name="column">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Open File</property>
+ <layout>
+ <property name="row">2</property>
+ <property name="column">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Ctrl+O</property>
+ <layout>
+ <property name="row">2</property>
+ <property name="column">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">New Terminal</property>
+ <layout>
+ <property name="row">3</property>
+ <property name="column">0</property>
+ </layout>
</object>
</child>
<child>
- <object class="DzlEmptyState" id="failed_state">
- <property name="icon-name">computer-fail-symbolic</property>
- <property name="pixel-size">160</property>
- <property name="title" translatable="yes">Uh oh, something went wrong</property>
- <property name="subtitle" translatable="yes">There was a failure while trying to perform the
operation.</property>
- <property name="visible">true</property>
+ <object class="GtkLabel">
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Ctrl+Shift+T</property>
+ <layout>
+ <property name="row">3</property>
+ <property name="column">1</property>
+ </layout>
</object>
</child>
<child>
- <object class="IdeFrameWrapper" id="stack">
- <property name="expand">true</property>
- <property name="homogeneous">false</property>
- <property name="interpolate-size">false</property>
- <property name="visible">true</property>
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <property name="homogeneous">true</property>
+ <layout>
+ <property name="row">4</property>
+ <property name="column">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">Open File…</property>
+ <property name="action-name">workbench.open</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="label" translatable="yes">New Terminal</property>
+ <property name="action-name">terminal.terminal-on-host</property>
+ <property name="action-target">''</property>
+ </object>
+ </child>
</object>
</child>
</object>
+ </property>
+ <child>
+ <object class="GtkGestureClick">
+ <property name="propagation-phase">bubble</property>
+ <signal name="pressed" handler="status_page_pressed_cb" swapped="true" object="IdeFrame"/>
+ </object>
</child>
</object>
- </child>
+ </property>
</template>
</interface>
diff --git a/src/libide/gui/ide-grid.c b/src/libide/gui/ide-grid.c
index e31b1c744..990dc1613 100644
--- a/src/libide/gui/ide-grid.c
+++ b/src/libide/gui/ide-grid.c
@@ -1,6 +1,6 @@
/* ide-grid.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,1516 +18,197 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-
#define G_LOG_DOMAIN "ide-grid"
#include "config.h"
-#include <string.h>
-
+#include "ide-frame.h"
#include "ide-grid.h"
-#include "ide-gui-private.h"
-
-/**
- * SECTION:ide-grid
- * @title: IdeGrid
- * @short_description: A grid for #IdePage
- *
- * The #IdeGrid provides a grid of pages that the user may
- * manipulate.
- *
- * Internally, this is implemented with #IdeGrid at the top
- * containing one or more of #IdeGridColumn. Those columns
- * contain one or more #IdeFrame. The stack can contain many
- * #IdePage.
- *
- * #IdeGrid implements the #GListModel interface to simplify
- * the process of listing (with deduplication) the pages that are
- * contianed within the #IdeGrid. If you would instead like
- * to see all possible pages in the stack, use the
- * ide_grid_foreach_page() API.
- *
- * Since: 3.32
- */
-
-typedef struct
-{
- /* Owned references */
- DzlSignalGroup *toplevel_signals;
- GQueue focus_column;
- GArray *stack_info;
-
- /*
- * This owned reference is our box highlight theatric that we
- * animate while doing a DnD drop interaction.
- */
- DzlBoxTheatric *drag_theatric;
- DzlAnimation *drag_anim;
-
- /*
- * This unowned reference is simply used to compare to a new focus
- * page to see if we have changed our current page. It is not to
- * be used directly, only for pointer comparison.
- */
- IdePage *_last_focused_page;
-
- /*
- * A GSource that is used to remove empty stacks that are unnecessary
- * (after a last stack item is removed).
- */
- guint cull_source;
-} IdeGridPrivate;
-
-typedef struct
-{
- IdeGridColumn *column;
- IdeFrame *stack;
- GdkRectangle area;
- gint drop;
- gint x;
- gint y;
-} DropLocate;
-typedef struct
+struct _IdeGrid
{
- IdeFrame *stack;
- guint len;
-} StackInfo;
-
-enum {
- PROP_0,
- PROP_CURRENT_COLUMN,
- PROP_CURRENT_STACK,
- PROP_CURRENT_PAGE,
- N_PROPS
-};
-
-enum {
- CREATE_FRAME,
- CREATE_VIEW,
- N_SIGNALS
-};
-
-enum {
- DROP_ONTO,
- DROP_ABOVE,
- DROP_BELOW,
- DROP_LEFT_OF,
- DROP_RIGHT_OF,
+ PanelGrid parent_instance;
};
-static void list_model_iface_init (GListModelInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (IdeGrid, ide_grid, DZL_TYPE_MULTI_PANED,
- G_ADD_PRIVATE (IdeGrid)
- G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
-
-static GParamSpec *properties [N_PROPS];
-static guint signals [N_SIGNALS];
-
-static void
-ide_grid_cull (IdeGrid *self)
-{
- guint n_columns;
-
- g_assert (IDE_IS_GRID (self));
-
- n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
-
- for (guint i = n_columns; i > 0; i--)
- {
- IdeGridColumn *column;
- guint n_stacks;
-
- column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i - 1));
- n_stacks = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
-
- if (n_columns == 1 && n_stacks == 1)
- return;
-
- for (guint j = n_stacks; j > 0; j--)
- {
- IdeFrame *stack;
- guint n_items;
-
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j - 1));
- n_items = g_list_model_get_n_items (G_LIST_MODEL (stack));
-
- if (n_items == 0)
- gtk_widget_destroy (GTK_WIDGET (stack));
- }
-
- if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column)) == 0)
- gtk_widget_destroy (GTK_WIDGET (column));
- }
-}
-
-static gboolean
-ide_grid_do_cull (gpointer data)
-{
- IdeGrid *self = data;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
-
- priv->cull_source = 0;
-
- ide_grid_cull (self);
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-ide_grid_queue_cull (IdeGrid *self)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
-
- if (priv->cull_source != 0)
- return;
-
- priv->cull_source = gdk_threads_add_idle_full (G_PRIORITY_HIGH,
- ide_grid_do_cull,
- g_object_ref (self),
- g_object_unref);
-}
-
-static void
-ide_grid_update_actions (IdeGrid *self)
-{
- guint n_children;
-
- g_assert (IDE_IS_GRID (self));
-
- n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
-
- for (guint i = 0; i < n_children; i++)
- {
- GtkWidget *column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
-
- g_assert (IDE_IS_GRID_COLUMN (column));
-
- _ide_grid_column_update_actions (IDE_GRID_COLUMN (column));
- }
-}
-
-static IdeFrame *
-ide_grid_real_create_frame (IdeGrid *self)
-{
- return g_object_new (IDE_TYPE_FRAME,
- "expand", TRUE,
- "visible", TRUE,
- NULL);
-}
-
-static GtkWidget *
-ide_grid_create_frame (IdeGrid *self)
-{
- IdeFrame *ret = NULL;
-
- g_assert (IDE_IS_GRID (self));
-
- g_signal_emit (self, signals [CREATE_FRAME], 0, &ret);
- g_return_val_if_fail (IDE_IS_FRAME (ret), NULL);
- return GTK_WIDGET (ret);
-}
-
-static GtkWidget *
-ide_grid_create_column (IdeGrid *self)
-{
- GtkWidget *stack;
-
- g_assert (IDE_IS_GRID (self));
-
- stack = ide_grid_create_frame (self);
-
- if (stack != NULL)
- {
- GtkWidget *column = g_object_new (IDE_TYPE_GRID_COLUMN,
- "visible", TRUE,
- NULL);
- gtk_container_add (GTK_CONTAINER (column), stack);
- return column;
- }
-
- return NULL;
-}
-
-static void
-ide_grid_after_set_focus (IdeGrid *self,
- GtkWidget *widget,
- GtkWidget *toplevel)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (!widget || GTK_IS_WIDGET (widget));
- g_assert (GTK_IS_WINDOW (toplevel));
-
- if (widget != NULL)
- {
- GtkWidget *column = NULL;
- GtkWidget *page;
-
- if (gtk_widget_is_ancestor (widget, GTK_WIDGET (self)))
- {
- column = gtk_widget_get_ancestor (widget, IDE_TYPE_GRID_COLUMN);
-
- if (column != NULL)
- ide_grid_set_current_column (self, IDE_GRID_COLUMN (column));
- }
-
- /*
- * self->_last_focused_page is an unowned reference, we only
- * use it for pointer comparison, nothing more.
- */
- page = gtk_widget_get_ancestor (widget, IDE_TYPE_PAGE);
- if (page != (GtkWidget *)priv->_last_focused_page)
- {
- priv->_last_focused_page = (IdePage *)page;
- ide_object_notify_in_main (self, properties [PROP_CURRENT_PAGE]);
-
- if (page != NULL && column != NULL)
- {
- GtkWidget *stack;
-
- stack = gtk_widget_get_ancestor (GTK_WIDGET (page), IDE_TYPE_FRAME);
- if (stack != NULL)
- ide_grid_column_set_current_stack (IDE_GRID_COLUMN (column),
- IDE_FRAME (stack));
- }
- }
- }
-}
-
-static void
-ide_grid_hierarchy_changed (GtkWidget *widget,
- GtkWidget *old_toplevel)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- GtkWidget *toplevel;
-
- g_assert (IDE_IS_GRID (self));
- g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
-
- /*
- * Setup focus tracking so that we can update our "current stack" when the
- * user selected focus changes.
- */
-
- toplevel = gtk_widget_get_toplevel (widget);
-
- if (GTK_IS_WINDOW (toplevel))
- dzl_signal_group_set_target (priv->toplevel_signals, toplevel);
- else
- dzl_signal_group_set_target (priv->toplevel_signals, NULL);
-
- /*
- * If we've been added to a widget and still do not have a stack added, then
- * we'll emit our ::create-frame signal to create that now. We do this here
- * to allow the consumer to connect to ::create-frame before adding the
- * widget to the hierarchy.
- */
-
- if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (widget)) == 0)
- {
- GtkWidget *column = ide_grid_create_column (self);
- gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (column));
- }
-}
-
-static void
-ide_grid_add (GtkContainer *container,
- GtkWidget *widget)
-{
- IdeGrid *self = (IdeGrid *)container;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (GTK_IS_WIDGET (widget));
-
- if (IDE_IS_GRID_COLUMN (widget))
- {
- GList *children;
-
- /* Add our column to the grid */
- g_queue_push_head (&priv->focus_column, widget);
- GTK_CONTAINER_CLASS (ide_grid_parent_class)->add (container, widget);
- ide_grid_set_current_column (self, IDE_GRID_COLUMN (widget));
- _ide_grid_column_update_actions (IDE_GRID_COLUMN (widget));
-
- /* Start monitoring all the stacks in the grid for pages */
- children = gtk_container_get_children (GTK_CONTAINER (widget));
- for (const GList *iter = children; iter; iter = iter->next)
- if (IDE_IS_FRAME (iter->data))
- _ide_grid_stack_added (self, iter->data);
- g_list_free (children);
- }
- else if (IDE_IS_FRAME (widget))
- {
- IdeGridColumn *column;
-
- column = ide_grid_get_current_column (self);
- gtk_container_add (GTK_CONTAINER (column), widget);
- ide_grid_set_current_column (self, column);
- }
- else if (IDE_IS_PAGE (widget))
- {
- IdeGridColumn *column = NULL;
- guint n_columns;
-
- /* If we have an empty layout stack, we'll prefer to add the
- * page to that. If we don't find an empty stack, we'll add
- * the page to the most recently focused stack.
- */
-
- n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
-
- for (guint i = 0; i < n_columns; i++)
- {
- GtkWidget *ele = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
-
- g_assert (IDE_IS_GRID_COLUMN (ele));
-
- if (_ide_grid_column_is_empty (IDE_GRID_COLUMN (ele)))
- {
- column = IDE_GRID_COLUMN (ele);
- break;
- }
- }
-
- if (column == NULL)
- column = ide_grid_get_current_column (self);
-
- g_assert (IDE_IS_GRID_COLUMN (column));
-
- gtk_container_add (GTK_CONTAINER (column), widget);
- }
- else
- {
- g_warning ("%s must be one of IdeFrame, IdePage, or IdeGrid",
- G_OBJECT_TYPE_NAME (self));
- return;
- }
-
- ide_grid_update_actions (self);
-}
-
-static void
-ide_grid_remove (GtkContainer *container,
- GtkWidget *widget)
-{
- IdeGrid *self = (IdeGrid *)container;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- gboolean notify = FALSE;
-
- g_assert (IDE_IS_GRID (self));
- g_assert (IDE_IS_GRID_COLUMN (widget));
-
- notify = g_queue_peek_head (&priv->focus_column) == (gpointer)widget;
- g_queue_remove (&priv->focus_column, widget);
-
- GTK_CONTAINER_CLASS (ide_grid_parent_class)->remove (container, widget);
-
- ide_grid_update_actions (self);
-
- if (notify)
- {
- GtkWidget *head = g_queue_peek_head (&priv->focus_column);
-
- if (head != NULL)
- gtk_widget_grab_focus (head);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_COLUMN]);
- }
-}
-
-static gboolean
-ide_grid_get_drop_area (IdeGrid *self,
- gint x,
- gint y,
- GdkRectangle *out_area,
- IdeGridColumn **out_column,
- IdeFrame **out_stack,
- gint *out_drop)
-{
- GtkAllocation alloc;
- GtkWidget *column;
- GtkWidget *stack = NULL;
-
- g_assert (IDE_IS_GRID (self));
- g_assert (out_area != NULL);
- g_assert (out_column != NULL);
- g_assert (out_stack != NULL);
- g_assert (out_drop != NULL);
-
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- column = dzl_multi_paned_get_at_point (DZL_MULTI_PANED (self), x + alloc.x, 0);
- if (column != NULL)
- stack = dzl_multi_paned_get_at_point (DZL_MULTI_PANED (column), 0, y + alloc.y);
-
- if (column != NULL && stack != NULL)
- {
- GtkAllocation stack_alloc;
-
- gtk_widget_get_allocation (stack, &stack_alloc);
-
- gtk_widget_translate_coordinates (stack,
- GTK_WIDGET (self),
- 0, 0,
- &stack_alloc.x, &stack_alloc.y);
-
- *out_area = stack_alloc;
- *out_column = IDE_GRID_COLUMN (column);
- *out_stack = IDE_FRAME (stack);
- *out_drop = DROP_ONTO;
-
- gtk_widget_translate_coordinates (GTK_WIDGET (self), stack, x, y, &x, &y);
-
- if (FALSE) {}
- else if (x < (stack_alloc.width / 4))
- {
- out_area->y = 0;
- out_area->height = alloc.height;
- out_area->width = stack_alloc.width / 4;
- *out_drop = DROP_LEFT_OF;
- }
- else if (x > (stack_alloc.width / 4 * 3))
- {
- out_area->y = 0;
- out_area->height = alloc.height;
- out_area->x = dzl_cairo_rectangle_x2 (&stack_alloc) - (stack_alloc.width / 4);
- out_area->width = stack_alloc.width / 4;
- *out_drop = DROP_RIGHT_OF;
- }
- else if (y < (stack_alloc.height / 4))
- {
- out_area->height = stack_alloc.height / 4;
- *out_drop = DROP_ABOVE;
- }
- else if (y > (stack_alloc.height / 4 * 3))
- {
- out_area->y = dzl_cairo_rectangle_y2 (&stack_alloc) - (stack_alloc.height / 4);
- out_area->height = stack_alloc.height / 4;
- *out_drop = DROP_BELOW;
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-ide_grid_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time_)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- IdeGridColumn *column = NULL;
- IdeFrame *stack = NULL;
- DzlAnimation *drag_anim;
- GdkRectangle area = {0};
- GtkAllocation alloc;
- gint drop = DROP_ONTO;
-
- g_assert (IDE_IS_GRID (self));
- g_assert (GDK_IS_DRAG_CONTEXT (context));
-
- if (priv->drag_anim != NULL)
- {
- dzl_animation_stop (priv->drag_anim);
- g_clear_weak_pointer (&priv->drag_anim);
- }
-
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- if (!ide_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
- return GDK_EVENT_PROPAGATE;
-
- if (priv->drag_theatric == NULL)
- {
- priv->drag_theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
- "x", area.x,
- "y", area.y,
- "width", area.width,
- "height", area.height,
- "alpha", 0.3,
- "background", "#729fcf",
- "target", self,
- NULL);
- return GDK_EVENT_STOP;
- }
-
- drag_anim = dzl_object_animate (priv->drag_theatric,
- DZL_ANIMATION_EASE_OUT_CUBIC,
- 100,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- "x", area.x,
- "width", area.width,
- "y", area.y,
- "height", area.height,
- NULL);
- g_set_weak_pointer (&priv->drag_anim, drag_anim);
-
- gtk_widget_queue_draw (GTK_WIDGET (self));
-
- return GDK_EVENT_STOP;
-}
-
-static void
-ide_grid_drag_data_received (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- GtkSelectionData *data,
- guint info,
- guint time_)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridColumn *column = NULL;
- IdeFrame *stack = NULL;
- g_auto(GStrv) uris = NULL;
- GdkRectangle area = {0};
- gint drop = DROP_ONTO;
-
- g_assert (IDE_IS_GRID (self));
- g_assert (GDK_IS_DRAG_CONTEXT (context));
-
- if (!ide_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
- return;
-
- g_assert (IDE_IS_GRID_COLUMN (column));
- g_assert (IDE_IS_FRAME (stack));
-
- if (!(uris = gtk_selection_data_get_uris (data)))
- return;
-
- for (guint i = 0; uris[i] != NULL; i++)
- {
- const gchar *uri = uris[i];
- IdePage *page = NULL;
- gint column_index = 0;
- gint stack_index = 0;
-
- g_signal_emit (self, signals [CREATE_VIEW], 0, uri, &page);
-
- if (page == NULL)
- {
- g_debug ("Failed to load IdePage for \"%s\"", uri);
- continue;
- }
-
- gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (column),
- "index", &column_index,
- NULL);
- gtk_container_child_get (GTK_CONTAINER (column), GTK_WIDGET (stack),
- "index", &stack_index,
- NULL);
-
- switch (drop)
- {
- case DROP_ONTO:
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
- break;
-
- case DROP_ABOVE:
- stack = IDE_FRAME (ide_grid_create_frame (self));
- gtk_container_add_with_properties (GTK_CONTAINER (column), GTK_WIDGET (stack),
- "index", stack_index,
- NULL);
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
- break;
-
- case DROP_BELOW:
- stack = IDE_FRAME (ide_grid_create_frame (self));
- gtk_container_add_with_properties (GTK_CONTAINER (column), GTK_WIDGET (stack),
- "index", stack_index + 1,
- NULL);
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
- break;
-
- case DROP_LEFT_OF:
- column = IDE_GRID_COLUMN (ide_grid_create_column (self));
- gtk_container_add_with_properties (GTK_CONTAINER (self), GTK_WIDGET (column),
- "index", column_index,
- NULL);
- gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (page));
- break;
-
- case DROP_RIGHT_OF:
- column = IDE_GRID_COLUMN (ide_grid_create_column (self));
- gtk_container_add_with_properties (GTK_CONTAINER (self), GTK_WIDGET (column),
- "index", column_index + 1,
- NULL);
- gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (page));
- break;
-
- default:
- g_assert_not_reached ();
- }
- }
-}
-
-static void
-ide_grid_drag_leave (GtkWidget *widget,
- GdkDragContext *context,
- guint time_)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (GDK_IS_DRAG_CONTEXT (context));
-
- if (priv->drag_anim != NULL)
- {
- dzl_animation_stop (priv->drag_anim);
- g_clear_weak_pointer (&priv->drag_anim);
- }
-
- g_clear_object (&priv->drag_theatric);
- gtk_widget_queue_draw (GTK_WIDGET (self));
-}
-
-static gboolean
-ide_grid_drag_failed (GtkWidget *widget,
- GdkDragContext *context,
- GtkDragResult result)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (GDK_IS_DRAG_CONTEXT (context));
-
- if (priv->drag_anim != NULL)
- {
- dzl_animation_stop (priv->drag_anim);
- g_clear_weak_pointer (&priv->drag_anim);
- }
-
- g_clear_object (&priv->drag_theatric);
- gtk_widget_queue_draw (GTK_WIDGET (self));
-
- return GDK_EVENT_PROPAGATE;
-}
-
-static void
-ide_grid_grab_focus (GtkWidget *widget)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeFrame *stack;
-
- g_assert (IDE_IS_GRID (self));
-
- stack = ide_grid_get_current_stack (self);
-
- if (stack != NULL)
- gtk_widget_grab_focus (GTK_WIDGET (stack));
- else
- GTK_WIDGET_CLASS (ide_grid_parent_class)->grab_focus (widget);
-}
-
-static void
-ide_grid_destroy (GtkWidget *widget)
-{
- IdeGrid *self = (IdeGrid *)widget;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- dzl_clear_source (&priv->cull_source);
-
- GTK_WIDGET_CLASS (ide_grid_parent_class)->destroy (widget);
-}
-
-static void
-ide_grid_finalize (GObject *object)
-{
- IdeGrid *self = (IdeGrid *)object;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (priv->focus_column.head == NULL);
- g_assert (priv->focus_column.tail == NULL);
- g_assert (priv->focus_column.length == 0);
-
- g_clear_pointer (&priv->stack_info, g_array_unref);
- g_clear_object (&priv->toplevel_signals);
-
- G_OBJECT_CLASS (ide_grid_parent_class)->finalize (object);
-}
-
-static void
-ide_grid_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+typedef struct
{
- IdeGrid *self = IDE_GRID (object);
-
- switch (prop_id)
- {
- case PROP_CURRENT_COLUMN:
- g_value_set_object (value, ide_grid_get_current_column (self));
- break;
-
- case PROP_CURRENT_STACK:
- g_value_set_object (value, ide_grid_get_current_stack (self));
- break;
+ IdePage *page;
+ guint column;
+ guint row;
+ guint depth;
+} PageInfo;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
+G_DEFINE_TYPE (IdeGrid, ide_grid, PANEL_TYPE_GRID)
-static void
-ide_grid_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+static PanelFrame *
+ide_grid_real_create_frame (PanelGrid *grid)
{
- IdeGrid *self = IDE_GRID (object);
-
- switch (prop_id)
- {
- case PROP_CURRENT_COLUMN:
- ide_grid_set_current_column (self, g_value_get_object (value));
- break;
+ g_assert (PANEL_IS_GRID (grid));
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
+ return PANEL_FRAME (ide_frame_new ());
}
static void
ide_grid_class_init (IdeGridClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
-
- object_class->finalize = ide_grid_finalize;
- object_class->get_property = ide_grid_get_property;
- object_class->set_property = ide_grid_set_property;
-
- widget_class->destroy = ide_grid_destroy;
- widget_class->drag_data_received = ide_grid_drag_data_received;
- widget_class->drag_motion = ide_grid_drag_motion;
- widget_class->drag_leave = ide_grid_drag_leave;
- widget_class->drag_failed = ide_grid_drag_failed;
- widget_class->grab_focus = ide_grid_grab_focus;
- widget_class->hierarchy_changed = ide_grid_hierarchy_changed;
-
- container_class->add = ide_grid_add;
- container_class->remove = ide_grid_remove;
-
- klass->create_frame = ide_grid_real_create_frame;
-
- properties [PROP_CURRENT_COLUMN] =
- g_param_spec_object ("current-column",
- "Current Column",
- "The most recently focused grid column",
- IDE_TYPE_GRID_COLUMN,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_CURRENT_STACK] =
- g_param_spec_object ("current-stack",
- "Current Stack",
- "The most recently focused IdeFrame",
- IDE_TYPE_FRAME,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ PanelGridClass *grid_class = PANEL_GRID_CLASS (klass);
- properties [PROP_CURRENT_PAGE] =
- g_param_spec_object ("current-page",
- "Current View",
- "The most recently focused IdePage",
- IDE_TYPE_PAGE,
- (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, "idegrid");
-
- /**
- * IdeGrid::create-frame:
- * @self: an #IdeGrid
- *
- * Creates a new stack to be added to the grid.
- *
- * Returns: (transfer full): A newly created #IdeFrame
- *
- * Since: 3.34
- */
- signals [CREATE_FRAME] =
- g_signal_new (g_intern_static_string ("create-frame"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeGridClass, create_frame),
- g_signal_accumulator_first_wins, NULL, NULL,
- IDE_TYPE_FRAME, 0);
-
- /**
- * IdeGrid::create-page:
- * @self: an #IdeGrid
- * @uri: the URI to open
- *
- * Creates a new page for @uri to be added to the grid.
- *
- * Returns: (transfer full): A newly created #IdePage
- *
- * Since: 3.32
- */
- signals [CREATE_VIEW] =
- g_signal_new (g_intern_static_string ("create-page"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeGridClass, create_page),
- g_signal_accumulator_first_wins, NULL, NULL,
- IDE_TYPE_PAGE,
- 1,
- G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+ grid_class->create_frame = ide_grid_real_create_frame;
}
static void
ide_grid_init (IdeGrid *self)
{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- static const GtkTargetEntry target_entries[] = {
- { (gchar *)"text/uri-list", 0, 0 },
- };
-
- gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
- GTK_ORIENTATION_HORIZONTAL);
-
- gtk_drag_dest_set (GTK_WIDGET (self),
- GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
- target_entries,
- G_N_ELEMENTS (target_entries),
- GDK_ACTION_COPY);
-
- priv->stack_info = g_array_new (FALSE, FALSE, sizeof (StackInfo));
-
- priv->toplevel_signals = dzl_signal_group_new (GTK_TYPE_WINDOW);
+ PanelGridColumn *column;
+ PanelFrame *row;
- dzl_signal_group_connect_object (priv->toplevel_signals,
- "set-focus",
- G_CALLBACK (ide_grid_after_set_focus),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ column = panel_grid_get_column (PANEL_GRID (self), 0);
+ row = panel_grid_column_get_row (column, 0);
- _ide_grid_init_actions (self);
+ (void)row;
}
-/**
- * ide_grid_new:
- *
- * Creates a new #IdeGrid.
- *
- * Returns: (transfer full): A newly created #IdeGrid
- *
- * Since: 3.32
- */
GtkWidget *
ide_grid_new (void)
{
return g_object_new (IDE_TYPE_GRID, NULL);
}
-/**
- * ide_grid_get_current_stack:
- * @self: a #IdeGrid
- *
- * Gets the most recently focused stack. This is useful when you want to open
- * a document on the stack the user last focused.
- *
- * Returns: (transfer none) (nullable): an #IdeFrame or %NULL.
- *
- * Since: 3.32
- */
-IdeFrame *
-ide_grid_get_current_stack (IdeGrid *self)
-{
- IdeGridColumn *column;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
-
- column = ide_grid_get_current_column (self);
- if (column != NULL)
- return ide_grid_column_get_current_stack (column);
-
- return NULL;
-}
-
-/**
- * ide_grid_get_nth_column:
- * @self: a #IdeGrid
- * @nth: the index of the column, or -1
- *
- * Gets the @nth column from the grid.
- *
- * If @nth is -1, then a new column at the beginning of the
- * grid is created.
- *
- * If @nth is >= the number of columns in the grid, then a new
- * column at the end of the grid is created.
- *
- * Returns: (transfer none): An #IdeGridColumn.
- *
- * Since: 3.32
- */
-IdeGridColumn *
-ide_grid_get_nth_column (IdeGrid *self,
- gint nth)
-{
- GtkWidget *column;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
-
- if (nth < 0)
- {
- column = ide_grid_create_column (self);
- gtk_container_add_with_properties (GTK_CONTAINER (self), column,
- "index", 0,
- NULL);
- }
- else if (nth >= dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self)))
- {
- column = ide_grid_create_column (self);
- gtk_container_add (GTK_CONTAINER (self), column);
- }
- else
- {
- column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), nth);
- }
-
- g_return_val_if_fail (IDE_IS_GRID_COLUMN (column), NULL);
-
- return IDE_GRID_COLUMN (column);
-}
-
-/*
- * _ide_grid_get_nth_stack:
- *
- * This will get the @nth stack. If it does not yet exist,
- * it will be created.
- *
- * If nth == -1, a new stack will be created at index 0.
- *
- * If nth >= the number of stacks, a new stack will be created
- * at the end of the grid.
- *
- * Returns: (not nullable) (transfer none): An #IdeFrame.
- */
-IdeFrame *
-_ide_grid_get_nth_stack (IdeGrid *self,
- gint nth)
-{
- IdeGridColumn *column;
- IdeFrame *stack;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
-
- column = ide_grid_get_nth_column (self, nth);
- stack = ide_grid_column_get_current_stack (IDE_GRID_COLUMN (column));
-
- g_return_val_if_fail (IDE_IS_FRAME (stack), NULL);
-
- return stack;
-}
-
-/**
- * _ide_grid_get_nth_stack_for_column:
- * @self: an #IdeGrid
- * @column: an #IdeGridColumn
- * @nth: the index of the column, between -1 and G_MAXINT
- *
- * This will get the @nth stack within @column. If a matching stack
- * cannot be found, it will be created.
- *
- * If @nth is less-than 0, a new column will be inserted at the top.
- *
- * If @nth is greater-than the number of stacks, then a new stack
- * will be created at the bottom.
- *
- * Returns: (not nullable) (transfer none): An #IdeFrame.
- *
- * Since: 3.32
- */
-IdeFrame *
-_ide_grid_get_nth_stack_for_column (IdeGrid *self,
- IdeGridColumn *column,
- gint nth)
-{
- GtkWidget *stack;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
- g_return_val_if_fail (IDE_IS_GRID_COLUMN (column), NULL);
- g_return_val_if_fail (gtk_widget_get_parent (GTK_WIDGET (column)) == GTK_WIDGET (self), NULL);
-
- if (nth < 0)
- {
- stack = ide_grid_create_frame (self);
- gtk_container_add_with_properties (GTK_CONTAINER (column), stack,
- "index", 0,
- NULL);
- }
- else if (nth >= dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column)))
- {
- stack = ide_grid_create_frame (self);
- gtk_container_add (GTK_CONTAINER (self), stack);
- }
- else
- {
- stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), nth);
- }
-
- g_assert (IDE_IS_FRAME (stack));
-
- return IDE_FRAME (stack);
-}
-
-/**
- * ide_grid_get_current_column:
- * @self: a #IdeGrid
- *
- * Gets the most recently focused column of the grid.
- *
- * Returns: (transfer none) (not nullable): An #IdeGridColumn
- *
- * Since: 3.32
- */
-IdeGridColumn *
-ide_grid_get_current_column (IdeGrid *self)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- GtkWidget *ret = NULL;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
-
- if (priv->focus_column.head != NULL)
- ret = priv->focus_column.head->data;
- else if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self)) > 0)
- ret = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), 0);
-
- if (ret == NULL)
- {
- ret = ide_grid_create_column (self);
- gtk_container_add (GTK_CONTAINER (self), ret);
- }
-
- g_return_val_if_fail (IDE_IS_GRID_COLUMN (ret), NULL);
-
- return IDE_GRID_COLUMN (ret);
-}
-
-/**
- * ide_grid_set_current_column:
- * @self: an #IdeGrid
- * @column: (nullable): an #IdeGridColumn or %NULL
- *
- * Sets the current column for the grid. Generally this is automatically
- * updated for you when the focus changes within the workbench.
- *
- * @column can be %NULL out of convenience.
- *
- * Since: 3.32
- */
-void
-ide_grid_set_current_column (IdeGrid *self,
- IdeGridColumn *column)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- GList *iter;
-
- g_return_if_fail (IDE_IS_GRID (self));
- g_return_if_fail (!column || IDE_IS_GRID_COLUMN (column));
-
- if (column == NULL)
- return;
-
- if (gtk_widget_get_parent (GTK_WIDGET (column)) != GTK_WIDGET (self))
- {
- g_warning ("Attempt to set current column with non-descendant");
- return;
- }
-
- if (NULL != (iter = g_queue_find (&priv->focus_column, column)))
- {
- g_queue_unlink (&priv->focus_column, iter);
- g_queue_push_head_link (&priv->focus_column, iter);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_COLUMN]);
- ide_grid_update_actions (self);
- return;
- }
-
- g_warning ("%s does not contain %s",
- G_OBJECT_TYPE_NAME (self), G_OBJECT_TYPE_NAME (column));
-}
-
-/**
- * ide_grid_get_current_page:
- * @self: a #IdeGrid
- *
- * Gets the most recent page used by the user as determined by tracking
- * the window focus.
- *
- * Returns: (transfer none): An #IdePage or %NULL
- *
- * Since: 3.32
- */
-IdePage *
-ide_grid_get_current_page (IdeGrid *self)
-{
- IdeFrame *stack;
-
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
-
- stack = ide_grid_get_current_stack (self);
-
- if (stack != NULL)
- return ide_frame_get_visible_child (stack);
-
- return NULL;
-}
-
-static void
-collect_pages (GtkWidget *widget,
- GPtrArray *ar)
-{
- if (IDE_IS_PAGE (widget))
- g_ptr_array_add (ar, widget);
-}
-
/**
* ide_grid_foreach_page:
* @self: a #IdeGrid
- * @callback: (scope call) (closure user_data): A callback for each page
- * @user_data: user data for @callback
+ * @callback: (scope call): callback to execute for each page found
+ * @user_data: closure data for @callback
*
- * This function will call @callback for every page found in @self.
- *
- * Since: 3.32
+ * Calls @callback for each #IdePage found in the grid.
*/
void
-ide_grid_foreach_page (IdeGrid *self,
- GtkCallback callback,
- gpointer user_data)
+ide_grid_foreach_page (IdeGrid *self,
+ IdePageCallback callback,
+ gpointer user_data)
{
- g_autoptr(GPtrArray) pages = NULL;
+ g_autoptr(GArray) pages = NULL;
guint n_columns;
g_return_if_fail (IDE_IS_GRID (self));
g_return_if_fail (callback != NULL);
- pages = g_ptr_array_new ();
-
- n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
+ pages = g_array_new (FALSE, FALSE, sizeof (PageInfo));
+ n_columns = panel_grid_get_n_columns (PANEL_GRID (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_GRID_COLUMN (column));
-
- n_stacks = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
+ PanelGridColumn *column = panel_grid_get_column (PANEL_GRID (self), i);
+ guint n_rows = panel_grid_column_get_n_rows (column);
- for (guint j = 0; j < n_stacks; j++)
+ for (guint j = 0; j < n_rows; j++)
{
- GtkWidget *stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j);
+ PanelFrame *frame = panel_grid_column_get_row (column, j);
+ guint n_pages = panel_frame_get_n_pages (frame);
- g_assert (IDE_IS_FRAME (stack));
+ for (guint k = 0; k < n_pages; k++)
+ {
+ PanelWidget *widget = panel_frame_get_page (frame, k);
- ide_frame_foreach_page (IDE_FRAME (stack),
- (GtkCallback) collect_pages,
- pages);
+ if (IDE_IS_PAGE (widget))
+ {
+ PageInfo info = { IDE_PAGE (widget), i, j, k };
+ g_array_append_val (pages, info);
+ }
+ }
}
}
for (guint i = 0; i < pages->len; i++)
- callback (g_ptr_array_index (pages, i), user_data);
-}
-
-static GType
-ide_grid_get_item_type (GListModel *model)
-{
- return IDE_TYPE_PAGE;
-}
-
-static guint
-ide_grid_get_n_items (GListModel *model)
-{
- IdeGrid *self = (IdeGrid *)model;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- guint n_items = 0;
-
- g_assert (IDE_IS_GRID (self));
-
- for (guint i = 0; i < priv->stack_info->len; i++)
- n_items += g_array_index (priv->stack_info, StackInfo, i).len;
-
- return n_items;
-}
-
-static gpointer
-ide_grid_get_item (GListModel *model,
- guint position)
-{
- IdeGrid *self = (IdeGrid *)model;
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
-
- g_assert (IDE_IS_GRID (self));
- g_assert (position < ide_grid_get_n_items (model));
-
- for (guint i = 0; i < priv->stack_info->len; i++)
{
- const StackInfo *info = &g_array_index (priv->stack_info, StackInfo, i);
-
- if (position >= info->len)
- {
- position -= info->len;
- continue;
- }
-
- return g_list_model_get_item (G_LIST_MODEL (info->stack), position);
+ const PageInfo *info = &g_array_index (pages, PageInfo, i);
+ callback (info->page, user_data);
}
-
- g_warning ("Failed to locate position %u within %s",
- position, G_OBJECT_TYPE_NAME (self));
-
- return NULL;
}
-static void
-list_model_iface_init (GListModelInterface *iface)
+guint
+ide_grid_count_pages (IdeGrid *self)
{
- iface->get_item_type = ide_grid_get_item_type;
- iface->get_n_items = ide_grid_get_n_items;
- iface->get_item = ide_grid_get_item;
-}
+ guint count = 0;
+ guint n_columns;
-static void
-ide_grid_stack_items_changed (IdeGrid *self,
- guint position,
- guint removed,
- guint added,
- IdeFrame *stack)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- guint real_position = 0;
+ g_return_val_if_fail (IDE_IS_GRID (self), 0);
- g_assert (IDE_IS_GRID (self));
- g_assert (IDE_IS_FRAME (stack));
+ n_columns = panel_grid_get_n_columns (PANEL_GRID (self));
- for (guint i = 0; i < priv->stack_info->len; i++)
+ for (guint i = 0; i < n_columns; i++)
{
- StackInfo *info = &g_array_index (priv->stack_info, StackInfo, i);
+ PanelGridColumn *column = panel_grid_get_column (PANEL_GRID (self), i);
+ guint n_rows = panel_grid_column_get_n_rows (column);
- if (info->stack == stack)
+ for (guint j = 0; j < n_rows; j++)
{
- info->len -= removed;
- info->len += added;
+ PanelFrame *frame = panel_grid_column_get_row (column, j);
+ guint n_pages = panel_frame_get_n_pages (frame);
- g_list_model_items_changed (G_LIST_MODEL (self),
- real_position + position,
- removed,
- added);
-
- ide_object_notify_in_main (G_OBJECT (self), properties [PROP_CURRENT_PAGE]);
-
- ide_grid_queue_cull (self);
-
- return;
+ count += n_pages;
}
-
- real_position += info->len;
}
- g_warning ("Failed to locate %s within %s",
- G_OBJECT_TYPE_NAME (stack), G_OBJECT_TYPE_NAME (self));
+ return count;
}
void
-_ide_grid_stack_added (IdeGrid *self,
- IdeFrame *stack)
+ide_grid_get_page_position (IdeGrid *self,
+ IdePage *page,
+ guint *out_column,
+ guint *out_row,
+ guint *out_depth)
{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- StackInfo info = { 0 };
- guint n_items;
+ GtkWidget *frame;
+ GtkWidget *column;
+ GtkWidget *grid;
+ guint n_pages;
+ guint n_rows;
+ guint n_columns;
g_return_if_fail (IDE_IS_GRID (self));
- g_return_if_fail (IDE_IS_FRAME (stack));
- g_return_if_fail (G_IS_LIST_MODEL (stack));
-
- info.stack = stack;
- info.len = 0;
+ g_return_if_fail (IDE_IS_PAGE (self));
- g_array_append_val (priv->stack_info, info);
+ *out_column = 0;
+ *out_row = 0;
+ *out_depth = 0;
- g_signal_connect_object (stack,
- "items-changed",
- G_CALLBACK (ide_grid_stack_items_changed),
- self,
- G_CONNECT_SWAPPED);
-
- n_items = g_list_model_get_n_items (G_LIST_MODEL (stack));
- ide_grid_stack_items_changed (self, 0, 0, n_items, stack);
-}
-
-void
-_ide_grid_stack_removed (IdeGrid *self,
- IdeFrame *stack)
-{
- IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- guint position = 0;
-
- g_return_if_fail (IDE_IS_GRID (self));
- g_return_if_fail (IDE_IS_FRAME (stack));
+ if (!(frame = gtk_widget_get_ancestor (GTK_WIDGET (page), PANEL_TYPE_FRAME)) ||
+ !(column = gtk_widget_get_ancestor (GTK_WIDGET (frame), PANEL_TYPE_GRID_COLUMN)) ||
+ !(grid = gtk_widget_get_ancestor (GTK_WIDGET (column), PANEL_TYPE_GRID)))
+ return;
- g_signal_handlers_disconnect_by_func (stack,
- G_CALLBACK (ide_grid_stack_items_changed),
- self);
+ n_pages = panel_frame_get_n_pages (PANEL_FRAME (frame));
+ n_rows = panel_grid_column_get_n_rows (PANEL_GRID_COLUMN (column));
+ n_columns = panel_grid_get_n_columns (PANEL_GRID (grid));
- for (guint i = 0; i < priv->stack_info->len; i++)
+ for (guint i = 0; i < n_pages; i++)
{
- const StackInfo info = g_array_index (priv->stack_info, StackInfo, i);
+ PanelWidget *widget = panel_frame_get_page (PANEL_FRAME (frame), i);
- if (info.stack == stack)
+ if (widget == PANEL_WIDGET (page))
{
- g_array_remove_index (priv->stack_info, i);
- g_list_model_items_changed (G_LIST_MODEL (self), position, info.len, 0);
+ *out_depth = i;
break;
}
}
-}
-
-static void
-count_pages_cb (GtkWidget *widget,
- gpointer data)
-{
- (*(guint *)data)++;
-}
-
-guint
-ide_grid_count_pages (IdeGrid *self)
-{
- guint count = 0;
-
- g_return_val_if_fail (IDE_IS_GRID (self), 0);
-
- ide_grid_foreach_page (self, count_pages_cb, &count);
-
- return count;
-}
-
-/**
- * ide_grid_focus_neighbor:
- * @self: An #IdeGrid
- * @dir: the direction for the focus change
- *
- * Attempts to focus a neighbor #IdePage in the grid based on
- * the direction requested.
- *
- * If an #IdePage was focused, it will be returned to the caller.
- *
- * Returns: (transfer none) (nullable): An #IdePage or %NULL
- *
- * Since: 3.32
- */
-IdePage *
-ide_grid_focus_neighbor (IdeGrid *self,
- GtkDirectionType dir)
-{
- IdeGridColumn *column;
- IdeFrame *stack;
- IdePage *page = NULL;
- guint stack_pos = 0;
- guint column_pos = 0;
- guint n_children;
- g_return_val_if_fail (IDE_IS_GRID (self), NULL);
- g_return_val_if_fail (dir <= GTK_DIR_RIGHT, NULL);
-
- /* Make sure we have a current page and stack */
- if (NULL == (stack = ide_grid_get_current_stack (self)) ||
- NULL == (column = ide_grid_get_current_column (self)))
- return NULL;
-
- gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (column),
- "index", &column_pos,
- NULL);
-
- gtk_container_child_get (GTK_CONTAINER (column), GTK_WIDGET (stack),
- "index", &stack_pos,
- NULL);
-
- switch (dir)
+ for (guint i = 0; i < n_rows; i++)
{
- case GTK_DIR_DOWN:
- n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
- if (n_children - stack_pos == 1)
- return NULL;
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos + 1));
- page = ide_frame_get_visible_child (stack);
- break;
-
- case GTK_DIR_RIGHT:
- n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
- if (n_children - column_pos == 1)
- return NULL;
- column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos + 1));
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- page = ide_frame_get_visible_child (stack);
- break;
-
- case GTK_DIR_UP:
- if (stack_pos == 0)
- return NULL;
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos - 1));
- page = ide_frame_get_visible_child (stack);
- break;
-
- case GTK_DIR_LEFT:
- if (column_pos == 0)
- return NULL;
- column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos - 1));
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- page = ide_frame_get_visible_child (stack);
- break;
-
- case GTK_DIR_TAB_FORWARD:
- if (!ide_grid_focus_neighbor (self, GTK_DIR_DOWN) &&
- !ide_grid_focus_neighbor (self, GTK_DIR_RIGHT))
+ if (PANEL_FRAME (frame) == panel_grid_column_get_row (PANEL_GRID_COLUMN (column), i))
{
- column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), 0));
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- page = ide_frame_get_visible_child (stack);
+ *out_row = i;
+ break;
}
- break;
+ }
- case GTK_DIR_TAB_BACKWARD:
- if (!ide_grid_focus_neighbor (self, GTK_DIR_UP) &&
- !ide_grid_focus_neighbor (self, GTK_DIR_LEFT))
+ for (guint i = 0; i < n_columns; i++)
+ {
+ if (PANEL_GRID_COLUMN (column) == panel_grid_get_column (PANEL_GRID (grid), i))
{
- n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
- column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), n_children - 1));
- stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- page = ide_frame_get_visible_child (stack);
+ *out_column = i;
+ break;
}
- break;
-
- default:
- g_assert_not_reached ();
}
-
- if (page != NULL)
- gtk_widget_child_focus (GTK_WIDGET (page), GTK_DIR_TAB_FORWARD);
-
- return page;
}
diff --git a/src/libide/gui/ide-grid.h b/src/libide/gui/ide-grid.h
index 04516e6b5..e15a86d4d 100644
--- a/src/libide/gui/ide-grid.h
+++ b/src/libide/gui/ide-grid.h
@@ -1,6 +1,6 @@
/* ide-grid.h
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,54 +24,32 @@
# error "Only <libide-gui.h> can be included directly."
#endif
-#include <dazzle.h>
+#include <libpanel.h>
+
#include <libide-core.h>
-#include "ide-grid-column.h"
-#include "ide-frame.h"
#include "ide-page.h"
G_BEGIN_DECLS
#define IDE_TYPE_GRID (ide_grid_get_type())
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeGrid, ide_grid, IDE, GRID, DzlMultiPaned)
-
-struct _IdeGridClass
-{
- DzlMultiPanedClass parent_class;
-
- IdeFrame *(*create_frame) (IdeGrid *self);
- IdePage *(*create_page) (IdeGrid *self,
- const gchar *uri);
-
- /*< private >*/
- gpointer _reserved[16];
-};
-
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_grid_new (void);
-IDE_AVAILABLE_IN_3_32
-IdeGridColumn *ide_grid_get_nth_column (IdeGrid *self,
- gint nth);
-IDE_AVAILABLE_IN_3_32
-IdePage *ide_grid_focus_neighbor (IdeGrid *self,
- GtkDirectionType dir);
-IDE_AVAILABLE_IN_3_32
-IdeGridColumn *ide_grid_get_current_column (IdeGrid *self);
-IDE_AVAILABLE_IN_3_32
-void ide_grid_set_current_column (IdeGrid *self,
- IdeGridColumn *column);
-IDE_AVAILABLE_IN_3_32
-IdeFrame *ide_grid_get_current_stack (IdeGrid *self);
-IDE_AVAILABLE_IN_3_32
-IdePage *ide_grid_get_current_page (IdeGrid *self);
-IDE_AVAILABLE_IN_3_32
-guint ide_grid_count_pages (IdeGrid *self);
-IDE_AVAILABLE_IN_3_32
-void ide_grid_foreach_page (IdeGrid *self,
- GtkCallback callback,
- gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeGrid, ide_grid, IDE, GRID, PanelGrid)
+
+IDE_AVAILABLE_IN_ALL
+GtkWidget *ide_grid_new (void);
+IDE_AVAILABLE_IN_ALL
+guint ide_grid_count_pages (IdeGrid *self);
+IDE_AVAILABLE_IN_ALL
+void ide_grid_get_page_position (IdeGrid *self,
+ IdePage *page,
+ guint *column,
+ guint *row,
+ guint *depth);
+IDE_AVAILABLE_IN_ALL
+void ide_grid_foreach_page (IdeGrid *self,
+ IdePageCallback callback,
+ gpointer user_data);
G_END_DECLS
diff --git a/src/libide/gui/ide-page-private.h b/src/libide/gui/ide-page-private.h
new file mode 100644
index 000000000..002a692c8
--- /dev/null
+++ b/src/libide/gui/ide-page-private.h
@@ -0,0 +1,29 @@
+/* ide-page-private.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-page.h"
+
+G_BEGIN_DECLS
+
+GList *_ide_page_get_mru_link (IdePage *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-page.c b/src/libide/gui/ide-page.c
index a52bfd89b..514374ef2 100644
--- a/src/libide/gui/ide-page.c
+++ b/src/libide/gui/ide-page.c
@@ -22,44 +22,36 @@
#include "config.h"
-#include <libide-threading.h>
#include <string.h>
+#include <libide-gtk.h>
+#include <libide-threading.h>
+
+#include "ide-application.h"
#include "ide-gui-global.h"
-#include "ide-gui-private.h"
-#include "ide-page.h"
-#include "ide-workspace.h"
+#include "ide-page-private.h"
+#include "ide-workspace-private.h"
typedef struct
{
- GList mru_link;
+ GList mru_link;
- const gchar *menu_id;
- const gchar *icon_name;
- gchar *title;
- GIcon *icon;
+ const char *menu_id;
- GdkRGBA primary_color_bg;
- GdkRGBA primary_color_fg;
+ GtkBox *content_box;
+ GtkOverlay *overlay;
+ GtkProgressBar *progress_bar;
- guint failed : 1;
- guint modified : 1;
- guint can_split : 1;
- guint primary_color_bg_set : 1;
- guint primary_color_fg_set : 1;
+ guint failed : 1;
+ guint modified : 1;
+ guint can_split : 1;
} IdePagePrivate;
enum {
PROP_0,
PROP_CAN_SPLIT,
PROP_FAILED,
- PROP_ICON,
- PROP_ICON_NAME,
PROP_MENU_ID,
- PROP_MODIFIED,
- PROP_PRIMARY_COLOR_BG,
- PROP_PRIMARY_COLOR_FG,
- PROP_TITLE,
N_PROPS
};
@@ -68,11 +60,24 @@ enum {
N_SIGNALS
};
-G_DEFINE_TYPE_WITH_PRIVATE (IdePage, ide_page, GTK_TYPE_BOX)
+static void buildable_iface_init (GtkBuildableIface *iface);
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdePage, ide_page, PANEL_TYPE_WIDGET,
+ G_ADD_PRIVATE (IdePage)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+
+static GtkBuildableIface *parent_buildable;
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
+GList *
+_ide_page_get_mru_link (IdePage *self)
+{
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
+ g_assert (IDE_IS_PAGE (self));
+ return &priv->mru_link;
+}
+
static void
ide_page_real_agree_to_close_async (IdePage *self,
GCancellable *cancellable,
@@ -102,51 +107,37 @@ ide_page_real_agree_to_close_finish (IdePage *self,
}
static void
-find_focus_child (GtkWidget *widget,
- gboolean *handled)
+ide_page_root (GtkWidget *widget)
{
- if (!*handled)
- *handled = gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
-}
+ IdePage *self = (IdePage *)widget;
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
+ GtkWidget *toplevel;
-static void
-ide_page_grab_focus (GtkWidget *widget)
-{
- gboolean handled = FALSE;
+ g_assert (IDE_IS_PAGE (self));
- g_assert (IDE_IS_PAGE (widget));
+ GTK_WIDGET_CLASS (ide_page_parent_class)->root (widget);
- /*
- * This default grab_focus override just looks for the first child (generally
- * something like a scrolled window) and tries to move forward on focusing
- * the child widget. In most cases, this should work without intervention
- * from the child subclass.
- */
+ toplevel = GTK_WIDGET (gtk_widget_get_native (widget));
- gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) find_focus_child, &handled);
+ if (IDE_IS_WORKSPACE (toplevel))
+ _ide_workspace_add_page_mru (IDE_WORKSPACE (toplevel), &priv->mru_link);
}
static void
-ide_page_hierarchy_changed (GtkWidget *widget,
- GtkWidget *previous_toplevel)
+ide_page_unroot (GtkWidget *widget)
{
IdePage *self = (IdePage *)widget;
IdePagePrivate *priv = ide_page_get_instance_private (self);
GtkWidget *toplevel;
g_assert (IDE_IS_PAGE (self));
- g_assert (!previous_toplevel || GTK_IS_WIDGET (previous_toplevel));
-
- if (IDE_IS_WORKSPACE (previous_toplevel))
- _ide_workspace_remove_page_mru (IDE_WORKSPACE (previous_toplevel), &priv->mru_link);
- if (GTK_WIDGET_CLASS (ide_page_parent_class)->hierarchy_changed)
- GTK_WIDGET_CLASS (ide_page_parent_class)->hierarchy_changed (widget, previous_toplevel);
-
- toplevel = gtk_widget_get_toplevel (widget);
+ toplevel = GTK_WIDGET (gtk_widget_get_native (widget));
if (IDE_IS_WORKSPACE (toplevel))
- _ide_workspace_add_page_mru (IDE_WORKSPACE (toplevel), &priv->mru_link);
+ _ide_workspace_remove_page_mru (IDE_WORKSPACE (toplevel), &priv->mru_link);
+
+ GTK_WIDGET_CLASS (ide_page_parent_class)->unroot (widget);
}
/**
@@ -157,8 +148,6 @@ ide_page_hierarchy_changed (GtkWidget *widget,
* workspaces MRU (most-recently-used) queue.
*
* Pages should call this when their contents have been focused.
- *
- * Since: 3.32
*/
void
ide_page_mark_used (IdePage *self)
@@ -175,12 +164,6 @@ ide_page_mark_used (IdePage *self)
static void
ide_page_finalize (GObject *object)
{
- IdePage *self = (IdePage *)object;
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_clear_pointer (&priv->title, g_free);
- g_clear_object (&priv->icon);
-
G_OBJECT_CLASS (ide_page_parent_class)->finalize (object);
}
@@ -202,34 +185,10 @@ ide_page_get_property (GObject *object,
g_value_set_boolean (value, ide_page_get_failed (self));
break;
- case PROP_ICON_NAME:
- g_value_set_static_string (value, ide_page_get_icon_name (self));
- break;
-
- case PROP_ICON:
- g_value_set_object (value, ide_page_get_icon (self));
- break;
-
case PROP_MENU_ID:
g_value_set_static_string (value, ide_page_get_menu_id (self));
break;
- case PROP_MODIFIED:
- g_value_set_boolean (value, ide_page_get_modified (self));
- break;
-
- case PROP_PRIMARY_COLOR_BG:
- g_value_set_boxed (value, ide_page_get_primary_color_bg (self));
- break;
-
- case PROP_PRIMARY_COLOR_FG:
- g_value_set_boxed (value, ide_page_get_primary_color_fg (self));
- break;
-
- case PROP_TITLE:
- g_value_set_string (value, ide_page_get_title (self));
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -253,34 +212,10 @@ ide_page_set_property (GObject *object,
ide_page_set_failed (self, g_value_get_boolean (value));
break;
- case PROP_ICON_NAME:
- ide_page_set_icon_name (self, g_value_get_string (value));
- break;
-
- case PROP_ICON:
- ide_page_set_icon (self, g_value_get_object (value));
- break;
-
case PROP_MENU_ID:
ide_page_set_menu_id (self, g_value_get_string (value));
break;
- case PROP_MODIFIED:
- ide_page_set_modified (self, g_value_get_boolean (value));
- break;
-
- case PROP_PRIMARY_COLOR_BG:
- ide_page_set_primary_color_bg (self, g_value_get_boxed (value));
- break;
-
- case PROP_PRIMARY_COLOR_FG:
- ide_page_set_primary_color_fg (self, g_value_get_boxed (value));
- break;
-
- case PROP_TITLE:
- ide_page_set_title (self, g_value_get_string (value));
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -296,8 +231,8 @@ ide_page_class_init (IdePageClass *klass)
object_class->get_property = ide_page_get_property;
object_class->set_property = ide_page_set_property;
- widget_class->grab_focus = ide_page_grab_focus;
- widget_class->hierarchy_changed = ide_page_hierarchy_changed;
+ widget_class->root = ide_page_root;
+ widget_class->unroot = ide_page_unroot;
klass->agree_to_close_async = ide_page_real_agree_to_close_async;
klass->agree_to_close_finish = ide_page_real_agree_to_close_finish;
@@ -316,20 +251,6 @@ ide_page_class_init (IdePageClass *klass)
FALSE,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
- properties [PROP_ICON] =
- g_param_spec_object ("icon",
- "Icon",
- "A GIcon for the view",
- G_TYPE_ICON,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_ICON_NAME] =
- g_param_spec_string ("icon-name",
- "Icon Name",
- "The icon-name describing the view content",
- "text-x-generic-symbolic",
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
properties [PROP_MENU_ID] =
g_param_spec_string ("menu-id",
"Menu ID",
@@ -337,56 +258,6 @@ ide_page_class_init (IdePageClass *klass)
NULL,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
- properties [PROP_MODIFIED] =
- g_param_spec_boolean ("modified",
- "Modified",
- "If the view has been modified from the saved content",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdePage:primary-color-bg:
- *
- * The "primary-color-bg" property should describe the primary color
- * of the content of the view (if any).
- *
- * This can be used by the layout stack to alter the color of the
- * header to match that of the content.
- *
- * Since: 3.32
- */
- properties [PROP_PRIMARY_COLOR_BG] =
- g_param_spec_boxed ("primary-color-bg",
- "Primary Color Background",
- "The primary foreground color of the content",
- GDK_TYPE_RGBA,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * IdePage:primary-color-fg:
- *
- * The "primary-color-fg" property should describe the foreground
- * to use for content above primary-color-bg.
- *
- * This can be used by the layout stack to alter the color of the
- * foreground to match that of the content.
- *
- * Since: 3.32
- */
- properties [PROP_PRIMARY_COLOR_FG] =
- g_param_spec_boxed ("primary-color-fg",
- "Primary Color Foreground",
- "The primary foreground color of the content",
- GDK_TYPE_RGBA,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_TITLE] =
- g_param_spec_string ("title",
- "Title",
- "The title of the document or view",
- NULL,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
@@ -401,8 +272,6 @@ ide_page_class_init (IdePageClass *klass)
* set to %TRUE. The default is %FALSE.
*
* Returns: (transfer full): A newly created #IdePage
- *
- * Since: 3.32
*/
signals [CREATE_SPLIT] =
g_signal_new (g_intern_static_string ("create-split"),
@@ -411,58 +280,28 @@ ide_page_class_init (IdePageClass *klass)
G_STRUCT_OFFSET (IdePageClass, create_split),
g_signal_accumulator_first_wins, NULL,
NULL, IDE_TYPE_PAGE, 0);
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
+ gtk_widget_class_set_css_name (widget_class, "page");
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-page.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdePage, content_box);
+ gtk_widget_class_bind_template_child_private (widget_class, IdePage, overlay);
+ gtk_widget_class_bind_template_child_private (widget_class, IdePage, progress_bar);
}
static void
ide_page_init (IdePage *self)
{
IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new ();
- gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+ gtk_widget_init_template (GTK_WIDGET (self));
- priv->mru_link.data = self;
- priv->icon_name = g_intern_string ("text-x-generic-symbolic");
+ panel_widget_set_kind (PANEL_WIDGET (self), PANEL_WIDGET_KIND_DOCUMENT);
- /* Add an action group out of convenience to plugins that want to
- * stash a simple action somewhere.
- */
- gtk_widget_insert_action_group (GTK_WIDGET (self), "view", G_ACTION_GROUP (group));
-}
-
-GtkWidget *
-ide_page_new (void)
-{
- return g_object_new (IDE_TYPE_PAGE, NULL);
-}
-
-const gchar *
-ide_page_get_title (IdePage *self)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
-
- return priv->title;
-}
-
-void
-ide_page_set_title (IdePage *self,
- const gchar *title)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_PAGE (self));
-
- if (g_strcmp0 (title, priv->title) != 0)
- {
- g_free (priv->title);
- priv->title = g_strdup (title);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
- }
+ priv->mru_link.data = self;
}
-const gchar *
+const char *
ide_page_get_menu_id (IdePage *self)
{
IdePagePrivate *priv = ide_page_get_instance_private (self);
@@ -473,8 +312,8 @@ ide_page_get_menu_id (IdePage *self)
}
void
-ide_page_set_menu_id (IdePage *self,
- const gchar *menu_id)
+ide_page_set_menu_id (IdePage *self,
+ const char *menu_id)
{
IdePagePrivate *priv = ide_page_get_instance_private (self);
@@ -484,7 +323,13 @@ ide_page_set_menu_id (IdePage *self,
if (menu_id != priv->menu_id)
{
+ GMenu *menu;
+
priv->menu_id = menu_id;
+
+ menu = ide_application_get_menu_by_id (IDE_APPLICATION_DEFAULT, menu_id);
+ panel_widget_set_menu_model (PANEL_WIDGET (self), G_MENU_MODEL (menu));
+
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MENU_ID]);
}
}
@@ -539,99 +384,6 @@ ide_page_set_failed (IdePage *self,
}
}
-gboolean
-ide_page_get_modified (IdePage *self)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_PAGE (self), FALSE);
-
- return priv->modified;
-}
-
-void
-ide_page_set_modified (IdePage *self,
- gboolean modified)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_PAGE (self));
-
- modified = !!modified;
-
- if (priv->modified != modified)
- {
- priv->modified = modified;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODIFIED]);
- }
-}
-
-/**
- * ide_page_get_icon:
- * @self: a #IdePage
- *
- * Gets the #GIcon to represent the view.
- *
- * Returns: (transfer none) (nullable): A #GIcon or %NULL
- *
- * Since: 3.32
- */
-GIcon *
-ide_page_get_icon (IdePage *self)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
-
- if (priv->icon == NULL)
- {
- if (priv->icon_name != NULL)
- priv->icon = g_icon_new_for_string (priv->icon_name, NULL);
- }
-
- return priv->icon;
-}
-
-void
-ide_page_set_icon (IdePage *self,
- GIcon *icon)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_PAGE (self));
-
- if (g_set_object (&priv->icon, icon))
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON]);
-}
-
-const gchar *
-ide_page_get_icon_name (IdePage *self)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
-
- return priv->icon_name;
-}
-
-void
-ide_page_set_icon_name (IdePage *self,
- const gchar *icon_name)
-{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
-
- g_return_if_fail (IDE_IS_PAGE (self));
-
- icon_name = g_intern_string (icon_name);
-
- if (icon_name != priv->icon_name)
- {
- priv->icon_name = icon_name;
- g_clear_object (&priv->icon);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
- }
-}
-
gboolean
ide_page_get_can_split (IdePage *self)
{
@@ -669,8 +421,6 @@ ide_page_set_can_split (IdePage *self,
* The view should be added to an #IdeLayoutStack where appropriate.
*
* Returns: (nullable) (transfer full): A newly created #IdePage or %NULL.
- *
- * Since: 3.32
*/
IdePage *
ide_page_create_split (IdePage *self)
@@ -690,206 +440,238 @@ ide_page_create_split (IdePage *self)
}
/**
- * ide_page_get_primary_color_bg:
+ * ide_page_report_error:
* @self: a #IdePage
+ * @format: a printf-style format string
*
- * Gets the #IdePage:primary-color-bg property if it has been set.
- *
- * The primary-color-bg can be used to alter the color of the layout
- * stack header to match the document contents.
- *
- * Returns: (transfer none) (nullable): a #GdkRGBA or %NULL.
+ * This function reports an error to the user in the layout view.
*
- * Since: 3.32
+ * @format should be a printf-style format string followed by the
+ * arguments for the format.
*/
-const GdkRGBA *
-ide_page_get_primary_color_bg (IdePage *self)
+void
+ide_page_report_error (IdePage *self,
+ const char *format,
+ ...)
{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
+ g_autofree char *message = NULL;
+ GtkInfoBar *infobar;
+ GtkLabel *label;
+ va_list args;
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
+ g_return_if_fail (IDE_IS_PAGE (self));
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
- return priv->primary_color_bg_set ? &priv->primary_color_bg : NULL;
+ infobar = g_object_new (GTK_TYPE_INFO_BAR,
+ "message-type", GTK_MESSAGE_WARNING,
+ "show-close-button", TRUE,
+ "visible", TRUE,
+ NULL);
+ g_signal_connect (infobar,
+ "response",
+ G_CALLBACK (gtk_widget_unparent),
+ NULL);
+ g_signal_connect (infobar,
+ "close",
+ G_CALLBACK (gtk_widget_unparent),
+ NULL);
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", message,
+ "visible", TRUE,
+ "wrap", TRUE,
+ "xalign", 0.0f,
+ NULL);
+
+ gtk_info_bar_add_child (infobar, GTK_WIDGET (label));
+ gtk_widget_insert_after (GTK_WIDGET (infobar), GTK_WIDGET (self), NULL);
}
/**
- * ide_page_set_primary_color_bg:
+ * ide_page_get_file_or_directory:
* @self: a #IdePage
- * @primary_color_bg: (nullable): a #GdkRGBA or %NULL
*
- * Sets the #IdePage:primary-color-bg property.
- * If @primary_color_bg is %NULL, the property is unset.
+ * Gets a #GFile representing a file or directory that best maps to this
+ * page. A terminal might use the current working directory while an editor
+ * or designer might use the backing file.
*
- * Since: 3.32
+ * Returns: (transfer full) (nullable): a #GFile or %NULL
*/
+GFile *
+ide_page_get_file_or_directory (IdePage *self)
+{
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
+
+ if (IDE_PAGE_GET_CLASS (self)->get_file_or_directory)
+ return IDE_PAGE_GET_CLASS (self)->get_file_or_directory (self);
+
+ return NULL;
+}
+
void
-ide_page_set_primary_color_bg (IdePage *self,
- const GdkRGBA *primary_color_bg)
+ide_page_add_content_widget (IdePage *self,
+ GtkWidget *widget)
{
IdePagePrivate *priv = ide_page_get_instance_private (self);
- gboolean old_set;
- GdkRGBA old;
g_return_if_fail (IDE_IS_PAGE (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
- old_set = priv->primary_color_bg_set;
- old = priv->primary_color_bg;
+ gtk_box_append (priv->content_box, widget);
+}
- if (primary_color_bg != NULL)
- {
- priv->primary_color_bg = *primary_color_bg;
- priv->primary_color_bg_set = TRUE;
- }
- else
+static void
+ide_page_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *object,
+ const char *name)
+{
+ IdePage *self = (IdePage *)buildable;
+
+ g_assert (IDE_IS_PAGE (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (G_IS_OBJECT (object));
+
+ if (GTK_IS_WIDGET (object))
{
- memset (&priv->primary_color_bg, 0, sizeof priv->primary_color_bg);
- priv->primary_color_bg_set = FALSE;
+ if (g_strcmp0 (name, "content") == 0)
+ {
+ ide_page_add_content_widget (self, GTK_WIDGET (object));
+ return;
+ }
}
- if (old_set != priv->primary_color_bg_set ||
- !gdk_rgba_equal (&old, &priv->primary_color_bg))
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIMARY_COLOR_BG]);
+ parent_buildable->add_child (buildable, builder, object, name);
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+ parent_buildable = g_type_interface_peek_parent (iface);
+ iface->add_child = ide_page_add_child;
}
/**
- * ide_page_get_primary_color_fg:
+ * ide_page_set_progress:
* @self: a #IdePage
+ * @notification: (nullable): an #IdeNotification or %NULL
*
- * Gets the #IdePage:primary-color-fg property if it has been set.
- *
- * The primary-color-fg can be used to alter the foreground color of the layout
- * stack header to match the document contents.
- *
- * Returns: (transfer none) (nullable): a #GdkRGBA or %NULL.
+ * Set interactive progress for the page.
*
- * Since: 3.32
+ * When the operation is completed, the caller shoudl call this method
+ * again and reutrn a value of %NULL for @notification.
*/
-const GdkRGBA *
-ide_page_get_primary_color_fg (IdePage *self)
+void
+ide_page_set_progress (IdePage *self,
+ IdeNotification *notification)
{
IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
+ g_return_if_fail (IDE_IS_PAGE (self));
+ g_return_if_fail (!notification || IDE_IS_NOTIFICATION (notification));
+
+ if (notification == NULL)
+ {
+ ide_gtk_widget_hide_with_fade (GTK_WIDGET (priv->progress_bar));
+ return;
+ }
- return priv->primary_color_fg_set ? &priv->primary_color_fg : NULL;
+ gtk_progress_bar_set_fraction (priv->progress_bar, .0);
+ gtk_widget_show (GTK_WIDGET (priv->progress_bar));
+ g_object_bind_property (notification, "progress",
+ priv->progress_bar, "fraction",
+ G_BINDING_SYNC_CREATE);
}
/**
- * ide_page_set_primary_color_fg:
+ * ide_page_get_position:
* @self: a #IdePage
- * @primary_color_fg: (nullable): a #GdkRGBA or %NULL
*
- * Sets the #IdePage:primary-color-fg property.
- * If @primary_color_fg is %NULL, the property is unset.
+ * Gets the position of a page within the workspace.
*
- * Since: 3.32
+ * Returns: (transfer full) (nullable): an #IdePanelPosition or %NULL
+ * if the page is not rooted.
*/
-void
-ide_page_set_primary_color_fg (IdePage *self,
- const GdkRGBA *primary_color_fg)
+IdePanelPosition *
+ide_page_get_position (IdePage *self)
{
- IdePagePrivate *priv = ide_page_get_instance_private (self);
- gboolean old_set;
- GdkRGBA old;
+ IdePanelPosition *position;
+ GtkWidget *frame;
+ guint n_pages;
- g_return_if_fail (IDE_IS_PAGE (self));
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
- old_set = priv->primary_color_fg_set;
- old = priv->primary_color_fg;
+ if (!(frame = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_FRAME)))
+ return NULL;
- if (primary_color_fg != NULL)
- {
- priv->primary_color_fg = *primary_color_fg;
- priv->primary_color_fg_set = TRUE;
- }
- else
+ if (!(position = ide_frame_get_position (IDE_FRAME (frame))))
+ return NULL;
+
+ n_pages = panel_frame_get_n_pages (PANEL_FRAME (frame));
+
+ for (guint i = 0; i < n_pages; i++)
{
- memset (&priv->primary_color_fg, 0, sizeof priv->primary_color_fg);
- priv->primary_color_fg_set = FALSE;
+ if (panel_frame_get_page (PANEL_FRAME (frame), i) == PANEL_WIDGET (self))
+ {
+ ide_panel_position_set_depth (position, i);
+ return position;
+ }
}
- if (old_set != priv->primary_color_fg_set ||
- !gdk_rgba_equal (&old, &priv->primary_color_fg))
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIMARY_COLOR_FG]);
+ g_critical ("Failed to find page within frame");
+
+ return position;
}
-/**
- * ide_page_report_error:
- * @self: a #IdePage
- * @format: a printf-style format string
- *
- * This function reports an error to the user in the layout view.
- *
- * @format should be a printf-style format string followed by the
- * arguments for the format.
- *
- * Since: 3.32
- */
void
-ide_page_report_error (IdePage *self,
- const gchar *format,
- ...)
+ide_page_destroy (IdePage *self)
{
- g_autofree gchar *message = NULL;
- GtkInfoBar *infobar;
- GtkWidget *content_area;
- GtkLabel *label;
- va_list args;
+ GtkWidget *frame;
g_return_if_fail (IDE_IS_PAGE (self));
- va_start (args, format);
- message = g_strdup_vprintf (format, args);
- va_end (args);
+ if ((frame = gtk_widget_get_ancestor (GTK_WIDGET (self), PANEL_TYPE_FRAME)))
+ panel_frame_remove (PANEL_FRAME (frame), PANEL_WIDGET (self));
+}
- infobar = g_object_new (GTK_TYPE_INFO_BAR,
- "message-type", GTK_MESSAGE_WARNING,
- "show-close-button", TRUE,
- "visible", TRUE,
- NULL);
- g_signal_connect (infobar,
- "response",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
- g_signal_connect (infobar,
- "close",
- G_CALLBACK (gtk_widget_destroy),
- NULL);
+void
+ide_page_observe (IdePage *self,
+ IdePage **location)
+{
+ g_return_if_fail (IDE_IS_PAGE (self));
+ g_return_if_fail (location != NULL);
- label = g_object_new (GTK_TYPE_LABEL,
- "label", message,
- "visible", TRUE,
- "wrap", TRUE,
- "xalign", 0.0f,
- NULL);
+ *location = self;
+ g_signal_connect_swapped (self,
+ "destroy",
+ G_CALLBACK (g_nullify_pointer),
+ location);
+}
- content_area = gtk_info_bar_get_content_area (infobar);
- gtk_container_add (GTK_CONTAINER (content_area), GTK_WIDGET (label));
+void
+ide_page_unobserve (IdePage *self,
+ IdePage **location)
+{
+ g_return_if_fail (IDE_IS_PAGE (self));
+ g_return_if_fail (location != NULL);
- gtk_container_add_with_properties (GTK_CONTAINER (self), GTK_WIDGET (infobar),
- "position", 0,
- NULL);
+ g_signal_handlers_disconnect_by_func (self,
+ G_CALLBACK (g_nullify_pointer),
+ location);
+ *location = NULL;
}
-/**
- * ide_page_get_file_or_directory:
- * @self: a #IdePage
- *
- * Gets a #GFile representing a file or directory that best maps to this
- * page. A terminal might use the current working directory while an editor
- * or designer might use the backing file.
- *
- * Returns: (transfer full) (nullable): a #GFile or %NULL
- *
- * Since: 3.40
- */
-GFile *
-ide_page_get_file_or_directory (IdePage *self)
+void
+ide_clear_page (IdePage **location)
{
- g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
+ IdePage *self = *location;
- if (IDE_PAGE_GET_CLASS (self)->get_file_or_directory)
- return IDE_PAGE_GET_CLASS (self)->get_file_or_directory (self);
+ if (self == NULL)
+ return;
- return NULL;
+ ide_page_unobserve (self, location);
+ ide_page_destroy (self);
}
diff --git a/src/libide/gui/ide-page.h b/src/libide/gui/ide-page.h
index 90a29cf7f..c0bbbafae 100644
--- a/src/libide/gui/ide-page.h
+++ b/src/libide/gui/ide-page.h
@@ -24,19 +24,25 @@
# error "Only <libide-gui.h> can be included directly."
#endif
-#include <gtk/gtk.h>
+#include <libpanel.h>
+
#include <libide-core.h>
+#include "ide-panel-position.h"
+
G_BEGIN_DECLS
#define IDE_TYPE_PAGE (ide_page_get_type())
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, GtkBox)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, PanelWidget)
+
+typedef void (*IdePageCallback) (IdePage *page,
+ gpointer user_data);
struct _IdePageClass
{
- GtkBoxClass parent_class;
+ PanelWidgetClass parent_class;
void (*agree_to_close_async) (IdePage *self,
GCancellable *cancellable,
@@ -49,74 +55,60 @@ struct _IdePageClass
GFile *(*get_file_or_directory) (IdePage *self);
/*< private >*/
- gpointer _reserved[16];
+ gpointer _reserved[8];
};
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_page_new (void);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_page_get_can_split (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_can_split (IdePage *self,
- gboolean can_split);
-IDE_AVAILABLE_IN_3_32
-IdePage *ide_page_create_split (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_page_get_icon_name (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_icon_name (IdePage *self,
- const gchar *icon_name);
-IDE_AVAILABLE_IN_3_32
-GIcon *ide_page_get_icon (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_icon (IdePage *self,
- GIcon *icon);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_page_get_failed (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_failed (IdePage *self,
- gboolean failed);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_page_get_menu_id (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_menu_id (IdePage *self,
- const gchar *menu_id);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_page_get_modified (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_modified (IdePage *self,
- gboolean modified);
-IDE_AVAILABLE_IN_3_32
-const gchar *ide_page_get_title (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_title (IdePage *self,
- const gchar *title);
-IDE_AVAILABLE_IN_3_32
-const GdkRGBA *ide_page_get_primary_color_bg (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_primary_color_bg (IdePage *self,
- const GdkRGBA *primary_color_bg);
-IDE_AVAILABLE_IN_3_32
-const GdkRGBA *ide_page_get_primary_color_fg (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_set_primary_color_fg (IdePage *self,
- const GdkRGBA *primary_color_fg);
-IDE_AVAILABLE_IN_3_32
-void ide_page_agree_to_close_async (IdePage *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-gboolean ide_page_agree_to_close_finish (IdePage *self,
- GAsyncResult *result,
- GError **error);
-IDE_AVAILABLE_IN_3_32
-void ide_page_mark_used (IdePage *self);
-IDE_AVAILABLE_IN_3_32
-void ide_page_report_error (IdePage *self,
- const gchar *format,
- ...) G_GNUC_PRINTF (2, 3);
-IDE_AVAILABLE_IN_3_40
-GFile *ide_page_get_file_or_directory (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_page_get_can_split (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_set_can_split (IdePage *self,
+ gboolean can_split);
+IDE_AVAILABLE_IN_ALL
+IdePage *ide_page_create_split (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_page_get_failed (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_set_failed (IdePage *self,
+ gboolean failed);
+IDE_AVAILABLE_IN_ALL
+const gchar *ide_page_get_menu_id (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_set_menu_id (IdePage *self,
+ const gchar *menu_id);
+IDE_AVAILABLE_IN_ALL
+void ide_page_agree_to_close_async (IdePage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_page_agree_to_close_finish (IdePage *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_ALL
+void ide_page_mark_used (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_report_error (IdePage *self,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+IDE_AVAILABLE_IN_ALL
+GFile *ide_page_get_file_or_directory (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_set_progress (IdePage *self,
+ IdeNotification *notification);
+IDE_AVAILABLE_IN_ALL
+IdePanelPosition *ide_page_get_position (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_page_observe (IdePage *self,
+ IdePage **location);
+IDE_AVAILABLE_IN_ALL
+void ide_page_unobserve (IdePage *self,
+ IdePage **location);
+IDE_AVAILABLE_IN_ALL
+void ide_page_destroy (IdePage *self);
+IDE_AVAILABLE_IN_ALL
+void ide_clear_page (IdePage **location);
+IDE_AVAILABLE_IN_ALL
+void ide_page_add_content_widget (IdePage *self,
+ GtkWidget *widget);
G_END_DECLS
diff --git a/src/libide/gui/ide-page.ui b/src/libide/gui/ide-page.ui
new file mode 100644
index 000000000..7e000d28e
--- /dev/null
+++ b/src/libide/gui/ide-page.ui
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdePage" parent="PanelWidget">
+ <property name="hexpand">true</property>
+ <property name="vexpand">true</property>
+ <property name="icon-name">text-x-generic-symbolic</property>
+ <child>
+ <object class="GtkOverlay" id="overlay">
+ <child>
+ <object class="GtkBox" id="content_box">
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkProgressBar" id="progress_bar">
+ <property name="valign">start</property>
+ <property name="hexpand">true</property>
+ <style>
+ <class name="osd"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-pane.c b/src/libide/gui/ide-pane.c
index bb1c7ec0a..073a66b78 100644
--- a/src/libide/gui/ide-pane.c
+++ b/src/libide/gui/ide-pane.c
@@ -22,18 +22,154 @@
#include "config.h"
+#include <libide-tree.h>
+
#include "ide-pane.h"
-G_DEFINE_TYPE (IdePane, ide_pane, DZL_TYPE_DOCK_WIDGET)
+typedef struct
+{
+ GList *popovers;
+} IdePanePrivate;
+
+static void popover_positioner_iface_init (IdePopoverPositionerInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdePane, ide_pane, PANEL_TYPE_WIDGET,
+ G_ADD_PRIVATE (IdePane)
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_POPOVER_POSITIONER, popover_positioner_iface_init))
+
+static gboolean
+release_popover_in_idle (gpointer data)
+{
+ struct {
+ IdePane *self;
+ GtkPopover *popover;
+ } *pair = data;
+
+ gtk_widget_unparent (GTK_WIDGET (pair->popover));
+ g_clear_object (&pair->self);
+ g_clear_object (&pair->popover);
+ g_slice_free1 (sizeof *pair, pair);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ide_pane_popover_closed_cb (IdePane *self,
+ GtkPopover *popover)
+{
+ IdePanePrivate *priv = ide_pane_get_instance_private (self);
+ struct {
+ IdePane *self;
+ GtkPopover *popover;
+ } *pair;
+
+ g_assert (IDE_IS_PANE (self));
+ g_assert (GTK_IS_POPOVER (popover));
+
+ g_signal_handlers_disconnect_by_func (popover,
+ G_CALLBACK (ide_pane_popover_closed_cb),
+ self);
+ priv->popovers = g_list_remove (priv->popovers, popover);
+
+ /* Perform the unparent from the idle as the popover menu will not be
+ * activating the action until after the popover is closed. That way
+ * we don't lose our action muxer before the action is fired.
+ */
+ pair = g_slice_alloc0 (sizeof *pair);
+ pair->self = g_object_ref (self);
+ pair->popover = g_object_ref (popover);
+ g_idle_add_full (G_PRIORITY_HIGH,
+ release_popover_in_idle,
+ pair,
+ NULL);
+
+}
+
+static void
+ide_pane_popover_positioner_present (IdePopoverPositioner *positioner,
+ GtkPopover *popover,
+ GtkWidget *relative_to,
+ const GdkRectangle *pointing_to)
+{
+ IdePane *self = (IdePane *)positioner;
+ IdePanePrivate *priv = ide_pane_get_instance_private (self);
+ g_autoptr(IdePanelPosition) position = NULL;
+ PanelDockPosition edge = 0;
+ GdkRectangle translated;
+ double x, y;
+
+ g_assert (IDE_IS_PANE (self));
+ g_assert (GTK_IS_POPOVER (popover));
+ g_assert (GTK_IS_WIDGET (relative_to));
+ g_assert (pointing_to != NULL);
+
+ if ((position = ide_pane_get_position (self)) &&
+ ide_panel_position_get_edge (position, &edge))
+ {
+ if (edge == PANEL_DOCK_POSITION_START)
+ gtk_popover_set_position (popover, GTK_POS_RIGHT);
+ else
+ gtk_popover_set_position (popover, GTK_POS_LEFT);
+ }
+
+ gtk_widget_translate_coordinates (GTK_WIDGET (relative_to),
+ GTK_WIDGET (self),
+ pointing_to->x, pointing_to->y,
+ &x, &y);
+ translated = (GdkRectangle) { x, y, pointing_to->width, pointing_to->height };
+ gtk_popover_set_pointing_to (popover, &translated);
+
+ priv->popovers = g_list_append (priv->popovers, popover);
+ gtk_widget_set_parent (GTK_WIDGET (popover), GTK_WIDGET (self));
+ g_signal_connect_object (popover,
+ "closed",
+ G_CALLBACK (ide_pane_popover_closed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_popover_popup (popover);
+}
+
+static void
+popover_positioner_iface_init (IdePopoverPositionerInterface *iface)
+{
+ iface->present = ide_pane_popover_positioner_present;
+}
+
+static void
+ide_pane_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ IdePane *self = (IdePane *)widget;
+ IdePanePrivate *priv = ide_pane_get_instance_private (self);
+
+ g_assert (IDE_IS_PANE (self));
+
+ GTK_WIDGET_CLASS (ide_pane_parent_class)->size_allocate (widget, width, height, baseline);
+
+ for (const GList *iter = priv->popovers; iter != NULL; iter = iter->next)
+ {
+ GtkPopover *popover = iter->data;
+
+ g_assert (GTK_IS_POPOVER (popover));
+
+ gtk_popover_present (popover);
+ }
+}
static void
ide_pane_class_init (IdePaneClass *klass)
{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->size_allocate = ide_pane_size_allocate;
}
static void
ide_pane_init (IdePane *self)
{
+ panel_widget_set_kind (PANEL_WIDGET (self), PANEL_WIDGET_KIND_UTILITY);
}
/**
@@ -44,11 +180,130 @@ ide_pane_init (IdePane *self)
* These widgets are meant to be added to #IdePanel widgets.
*
* Returns: (transfer full): a new #IdePane
- *
- * Since: 3.32
*/
GtkWidget *
ide_pane_new (void)
{
return g_object_new (IDE_TYPE_PANE, NULL);
}
+
+void
+ide_pane_destroy (IdePane *self)
+{
+ GtkWidget *frame;
+
+ g_return_if_fail (IDE_IS_PANE (self));
+
+ if ((frame = gtk_widget_get_ancestor (GTK_WIDGET (self), PANEL_TYPE_FRAME)))
+ panel_frame_remove (PANEL_FRAME (frame), PANEL_WIDGET (self));
+}
+
+void
+ide_pane_observe (IdePane *self,
+ IdePane **location)
+{
+ g_return_if_fail (IDE_IS_PANE (self));
+ g_return_if_fail (location != NULL);
+
+ *location = self;
+ g_signal_connect_swapped (self,
+ "destroy",
+ G_CALLBACK (g_nullify_pointer),
+ location);
+}
+
+void
+ide_pane_unobserve (IdePane *self,
+ IdePane **location)
+{
+ g_return_if_fail (IDE_IS_PANE (self));
+ g_return_if_fail (location != NULL);
+
+ g_signal_handlers_disconnect_by_func (self,
+ G_CALLBACK (g_nullify_pointer),
+ location);
+ *location = NULL;
+}
+
+void
+ide_clear_pane (IdePane **location)
+{
+ IdePane *self = *location;
+
+ if (self == NULL)
+ return;
+
+ ide_pane_unobserve (self, location);
+ ide_pane_destroy (self);
+}
+
+/**
+ * ide_pane_get_position:
+ * @self: a #IdePane
+ *
+ * Gets the position of the panel or %NULL
+ *
+ * Returns: (transfer full) (nullable): an #IdePanelPosition or %NULL
+ */
+IdePanelPosition *
+ide_pane_get_position (IdePane *self)
+{
+ static GType dock_child_type = G_TYPE_INVALID;
+ PanelDockPosition position;
+ IdePanelPosition *ret;
+ GtkWidget *frame;
+ GtkWidget *paned;
+ GtkWidget *edge;
+ guint n_pages;
+ int depth = 0;
+ int row_or_column = 0;
+
+ g_return_val_if_fail (IDE_IS_PANE (self), NULL);
+
+ if (dock_child_type == G_TYPE_INVALID)
+ {
+ if (!(dock_child_type = g_type_from_name ("PanelDockChild")))
+ g_return_val_if_reached (NULL);
+ }
+
+ if (!(frame = gtk_widget_get_ancestor (GTK_WIDGET (self), PANEL_TYPE_FRAME)))
+ g_return_val_if_reached (NULL);
+
+ n_pages = panel_frame_get_n_pages (PANEL_FRAME (frame));
+
+ for (guint i = 0; i < n_pages; i++)
+ {
+ if (panel_frame_get_page (PANEL_FRAME (frame), i) == PANEL_WIDGET (self))
+ {
+ depth = i;
+ break;
+ }
+ }
+
+ if (!(paned = gtk_widget_get_ancestor (frame, PANEL_TYPE_PANED)))
+ g_return_val_if_reached (NULL);
+
+ for (GtkWidget *child = gtk_widget_get_first_child (paned);
+ child != NULL && !gtk_widget_is_ancestor (frame, child);
+ child = gtk_widget_get_next_sibling (child))
+ row_or_column++;
+
+ if (!(edge = gtk_widget_get_ancestor (paned, dock_child_type)))
+ g_return_val_if_reached (NULL);
+
+ g_object_get (edge,
+ "position", &position,
+ NULL);
+
+ ret = ide_panel_position_new ();
+ ide_panel_position_set_edge (ret, position);
+ ide_panel_position_set_depth (ret, depth);
+
+ if (position == PANEL_DOCK_POSITION_START ||
+ position == PANEL_DOCK_POSITION_END)
+ ide_panel_position_set_row (ret, row_or_column);
+ else
+ ide_panel_position_set_column (ret, row_or_column);
+
+ return ret;
+}
diff --git a/src/libide/gui/ide-pane.h b/src/libide/gui/ide-pane.h
index 0eed763d5..0aea03cb6 100644
--- a/src/libide/gui/ide-pane.h
+++ b/src/libide/gui/ide-pane.h
@@ -1,6 +1,6 @@
/* ide-pane.h
*
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2022 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,25 +24,37 @@
# error "Only <libide-gui.h> can be included directly."
#endif
-#include <dazzle.h>
+#include <libpanel.h>
+
#include <libide-core.h>
+#include "ide-panel-position.h"
+
G_BEGIN_DECLS
#define IDE_TYPE_PANE (ide_pane_get_type())
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdePane, ide_pane, IDE, PANE, DzlDockWidget)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdePane, ide_pane, IDE, PANE, PanelWidget)
struct _IdePaneClass
{
- DzlDockWidgetClass parent_class;
-
- /*< private >*/
- gpointer _reserved[16];
+ PanelWidgetClass parent_class;
};
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_pane_new (void);
+IDE_AVAILABLE_IN_ALL
+GtkWidget *ide_pane_new (void);
+IDE_AVAILABLE_IN_ALL
+void ide_pane_destroy (IdePane *self);
+IDE_AVAILABLE_IN_ALL
+void ide_pane_observe (IdePane *self,
+ IdePane **location);
+IDE_AVAILABLE_IN_ALL
+void ide_pane_unobserve (IdePane *self,
+ IdePane **location);
+IDE_AVAILABLE_IN_ALL
+void ide_clear_pane (IdePane **location);
+IDE_AVAILABLE_IN_ALL
+IdePanelPosition *ide_pane_get_position (IdePane *self);
G_END_DECLS
diff --git a/src/libide/gui/ide-panel-position.c b/src/libide/gui/ide-panel-position.c
new file mode 100644
index 000000000..2d8acdd20
--- /dev/null
+++ b/src/libide/gui/ide-panel-position.c
@@ -0,0 +1,174 @@
+/* ide-panel-position.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-panel-position"
+
+#include "config.h"
+
+#include "ide-panel-position.h"
+
+struct _IdePanelPosition
+{
+ guint column : 8;
+ guint row : 8;
+ guint depth : 9;
+ PanelDockPosition edge : 3;
+ guint column_set : 1;
+ guint row_set : 1;
+ guint depth_set : 1;
+ guint edge_set : 1;
+};
+
+G_DEFINE_BOXED_TYPE (IdePanelPosition, ide_panel_position, ide_panel_position_ref, ide_panel_position_unref)
+
+IdePanelPosition *
+ide_panel_position_new (void)
+{
+ return g_rc_box_alloc0 (sizeof (IdePanelPosition));
+}
+
+IdePanelPosition *
+ide_panel_position_ref (IdePanelPosition *self)
+{
+ return g_rc_box_acquire (self);
+}
+
+void
+ide_panel_position_unref (IdePanelPosition *self)
+{
+ g_rc_box_release (self);
+}
+
+gboolean
+ide_panel_position_get_edge (IdePanelPosition *self,
+ PanelDockPosition *edge)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ if (edge != NULL)
+ *edge = self->edge;
+
+ return self->edge_set;
+}
+
+void
+ide_panel_position_set_edge (IdePanelPosition *self,
+ PanelDockPosition edge)
+{
+ g_return_if_fail (self != NULL);
+
+ self->edge = edge;
+ self->edge_set = TRUE;
+}
+
+/**
+ * ide_panel_position_get_column:
+ * @self: a #IdePanelPosition
+ * @column: (out): a location for a column
+ *
+ * Returns: %TRUE if the column is set
+ */
+gboolean
+ide_panel_position_get_column (IdePanelPosition *self,
+ guint *column)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ if (column != NULL)
+ *column = self->column;
+
+ return self->column_set;
+}
+
+void
+ide_panel_position_set_column (IdePanelPosition *self,
+ guint column)
+{
+ g_return_if_fail (self != NULL);
+
+ self->column = column;
+ self->column_set = TRUE;
+}
+
+/**
+ * ide_panel_position_get_row:
+ * @self: a #IdePanelPosition
+ * @row: (out): a location for the row
+ *
+ * Returns: %TRUE if the row is set
+ */
+gboolean
+ide_panel_position_get_row (IdePanelPosition *self,
+ guint *row)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ if (row != NULL)
+ *row = self->row;
+
+ return self->row_set;
+}
+
+void
+ide_panel_position_set_row (IdePanelPosition *self,
+ guint row)
+{
+ g_return_if_fail (self != NULL);
+
+ self->row = row;
+ self->row_set = TRUE;
+}
+
+/**
+ * ide_panel_position_get_depth:
+ * @self: a #IdePanelPosition
+ * @depth: (out): a location for the depth
+ *
+ * Returns: %TRUE if the depth is set
+ */
+gboolean
+ide_panel_position_get_depth (IdePanelPosition *self,
+ guint *depth)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ if (depth != NULL)
+ *depth = self->depth;
+
+ return self->depth_set;
+}
+
+void
+ide_panel_position_set_depth (IdePanelPosition *self,
+ guint depth)
+{
+ g_return_if_fail (self != NULL);
+
+ self->depth = depth;
+ self->depth_set = TRUE;
+}
+
+gboolean
+ide_panel_position_is_indeterminate (IdePanelPosition *self)
+{
+ g_return_val_if_fail (self != NULL, TRUE);
+
+ return !self->column_set || !self->row_set || !self->edge_set;
+}
diff --git a/src/libide/gui/ide-panel-position.h b/src/libide/gui/ide-panel-position.h
new file mode 100644
index 000000000..127459a9a
--- /dev/null
+++ b/src/libide/gui/ide-panel-position.h
@@ -0,0 +1,74 @@
+/* ide-panel-position.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libpanel.h>
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PANEL_POSITION (ide_panel_position_get_type())
+
+typedef struct _IdePanelPosition IdePanelPosition;
+
+IDE_AVAILABLE_IN_ALL
+GType ide_panel_position_get_type (void) G_GNUC_CONST;
+IDE_AVAILABLE_IN_ALL
+IdePanelPosition *ide_panel_position_new (void);
+IDE_AVAILABLE_IN_ALL
+IdePanelPosition *ide_panel_position_ref (IdePanelPosition *self);
+IDE_AVAILABLE_IN_ALL
+void ide_panel_position_unref (IdePanelPosition *self);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_panel_position_get_edge (IdePanelPosition *self,
+ PanelDockPosition *edge);
+IDE_AVAILABLE_IN_ALL
+void ide_panel_position_set_edge (IdePanelPosition *self,
+ PanelDockPosition edge);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_panel_position_get_row (IdePanelPosition *self,
+ guint *row);
+IDE_AVAILABLE_IN_ALL
+void ide_panel_position_set_row (IdePanelPosition *self,
+ guint row);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_panel_position_get_column (IdePanelPosition *self,
+ guint *column);
+IDE_AVAILABLE_IN_ALL
+void ide_panel_position_set_column (IdePanelPosition *self,
+ guint column);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_panel_position_get_depth (IdePanelPosition *self,
+ guint *depth);
+IDE_AVAILABLE_IN_ALL
+void ide_panel_position_set_depth (IdePanelPosition *self,
+ guint depth);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_panel_position_is_indeterminate (IdePanelPosition *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdePanelPosition, ide_panel_position_unref)
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-primary-workspace-actions.c b/src/libide/gui/ide-primary-workspace-actions.c
index 669b12225..e40563a99 100644
--- a/src/libide/gui/ide-primary-workspace-actions.c
+++ b/src/libide/gui/ide-primary-workspace-actions.c
@@ -23,12 +23,12 @@
#include "config.h"
#include <glib/gi18n.h>
-#include <libide-foundry.h>
#include <libpeas/peas.h>
+#include <libide-foundry.h>
+
#include "ide-gui-global.h"
-#include "ide-gui-private.h"
-#include "ide-primary-workspace.h"
+#include "ide-primary-workspace-private.h"
typedef struct
{
diff --git a/src/libide/gui/ide-primary-workspace-private.h b/src/libide/gui/ide-primary-workspace-private.h
new file mode 100644
index 000000000..38d134025
--- /dev/null
+++ b/src/libide/gui/ide-primary-workspace-private.h
@@ -0,0 +1,29 @@
+/* ide-primary-workspace-private.h
+ *
+ * Copyright 2017-2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-primary-workspace.h"
+
+G_BEGIN_DECLS
+
+void _ide_primary_workspace_init_actions (IdePrimaryWorkspace *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-primary-workspace.c b/src/libide/gui/ide-primary-workspace.c
index 1a7ca43c8..4735b19af 100644
--- a/src/libide/gui/ide-primary-workspace.c
+++ b/src/libide/gui/ide-primary-workspace.c
@@ -22,14 +22,15 @@
#include "config.h"
+#include "ide-frame.h"
+#include "ide-grid.h"
#include "ide-gui-global.h"
-#include "ide-gui-private.h"
#include "ide-header-bar.h"
+#include "ide-notifications-button.h"
#include "ide-omni-bar.h"
-#include "ide-primary-workspace.h"
+#include "ide-primary-workspace-private.h"
#include "ide-run-button.h"
-#include "ide-surface.h"
-#include "ide-window-settings-private.h"
+#include "ide-workspace-private.h"
/**
* SECTION:ide-primary-workspace
@@ -42,10 +43,6 @@
*
* See ide_workbench_open_async() for how to select another workspace type
* when opening a project.
- *
- * Returns: (transfer full): an #IdePrimaryWorkspace
- *
- * Since: 3.32
*/
struct _IdePrimaryWorkspace
@@ -54,10 +51,17 @@ struct _IdePrimaryWorkspace
/* Template widgets */
IdeHeaderBar *header_bar;
- DzlMenuButton *surface_menu_button;
IdeRunButton *run_button;
GtkLabel *project_title;
- DzlShortcutTooltip *search_tooltip;
+ GtkMenuButton *add_button;
+ PanelDock *dock;
+ PanelPaned *edge_start;
+ PanelPaned *edge_end;
+ PanelPaned *edge_bottom;
+ IdeGrid *grid;
+ GtkOverlay *overlay;
+ IdeOmniBar *omni_bar;
+ IdeJoinedMenu *build_menu;
};
G_DEFINE_FINAL_TYPE (IdePrimaryWorkspace, ide_primary_workspace, IDE_TYPE_WORKSPACE)
@@ -67,8 +71,10 @@ ide_primary_workspace_context_set (IdeWorkspace *workspace,
IdeContext *context)
{
IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+ IdeConfigManager *config_manager;
IdeProjectInfo *project_info;
IdeWorkbench *workbench;
+ GMenuModel *config_menu;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
@@ -80,58 +86,216 @@ ide_primary_workspace_context_set (IdeWorkspace *workspace,
project_info = ide_workbench_get_project_info (workbench);
if (project_info)
- g_object_bind_property (project_info, "name", self->project_title, "label",
+ g_object_bind_property (project_info, "name",
+ self->project_title, "label",
G_BINDING_SYNC_CREATE);
+
+ config_manager = ide_config_manager_from_context (context);
+ config_menu = ide_config_manager_get_menu (config_manager);
+ ide_joined_menu_prepend_menu (self->build_menu, G_MENU_MODEL (config_menu));
+}
+
+static void
+ide_primary_workspace_add_page (IdeWorkspace *workspace,
+ IdePage *page,
+ IdePanelPosition *position)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ _ide_workspace_add_widget (workspace,
+ PANEL_WIDGET (page),
+ position,
+ self->edge_start,
+ self->edge_end,
+ self->edge_bottom,
+ self->grid);
+}
+
+static void
+ide_primary_workspace_add_pane (IdeWorkspace *workspace,
+ IdePane *pane,
+ IdePanelPosition *position)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ _ide_workspace_add_widget (workspace,
+ PANEL_WIDGET (pane),
+ position,
+ self->edge_start,
+ self->edge_end,
+ self->edge_bottom,
+ self->grid);
}
static void
-ide_primary_workspace_surface_set (IdeWorkspace *workspace,
- IdeSurface *surface)
+ide_primary_workspace_add_grid_column (IdeWorkspace *workspace,
+ guint position)
+{
+ panel_grid_insert_column (PANEL_GRID (IDE_PRIMARY_WORKSPACE (workspace)->grid), position);
+}
+
+static void
+ide_primary_workspace_add_overlay (IdeWorkspace *workspace,
+ GtkWidget *overlay)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ gtk_overlay_add_overlay (self->overlay, overlay);
+}
+
+static void
+ide_primary_workspace_remove_overlay (IdeWorkspace *workspace,
+ GtkWidget *overlay)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ gtk_overlay_remove_overlay (self->overlay, overlay);
+}
+
+static IdeFrame *
+ide_primary_workspace_get_most_recent_frame (IdeWorkspace *workspace)
{
IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
- g_assert (!surface || IDE_IS_SURFACE (surface));
- if (DZL_IS_DOCK_ITEM (surface))
- {
- g_autofree gchar *icon_name = NULL;
+ return IDE_FRAME (panel_grid_get_most_recent_frame (PANEL_GRID (self->grid)));
+}
+
+static PanelFrame *
+ide_primary_workspace_get_frame_at_position (IdeWorkspace *workspace,
+ IdePanelPosition *position)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+ g_assert (position != NULL);
- icon_name = dzl_dock_item_get_icon_name (DZL_DOCK_ITEM (surface));
- g_object_set (self->surface_menu_button,
- "icon-name", icon_name,
- NULL);
- }
+ return _ide_workspace_find_frame (workspace,
+ position,
+ self->edge_start,
+ self->edge_end,
+ self->edge_bottom,
+ self->grid);
+}
- IDE_WORKSPACE_CLASS (ide_primary_workspace_parent_class)->surface_set (workspace, surface);
+static gboolean
+ide_primary_workspace_can_search (IdeWorkspace *workspace)
+{
+ return TRUE;
+}
+
+static IdeHeaderBar *
+ide_primary_workspace_get_header_bar (IdeWorkspace *workspace)
+{
+ return IDE_PRIMARY_WORKSPACE (workspace)->header_bar;
+}
+
+static void
+ide_primary_workspace_foreach_page (IdeWorkspace *workspace,
+ IdePageCallback callback,
+ gpointer user_data)
+{
+ ide_grid_foreach_page (IDE_PRIMARY_WORKSPACE (workspace)->grid, callback, user_data);
+}
+
+static void
+ide_primary_workspace_dispose (GObject *object)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)object;
+
+ /* Ensure that the grid is removed first so that it will cleanup
+ * addins/pages/etc before we ever get to removing the workspace
+ * addins as part of the parent class.
+ */
+ panel_dock_remove (self->dock, GTK_WIDGET (self->grid));
+ self->grid = NULL;
+
+ G_OBJECT_CLASS (ide_primary_workspace_parent_class)->dispose (object);
}
static void
ide_primary_workspace_class_init (IdePrimaryWorkspaceClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
IdeWorkspaceClass *workspace_class = IDE_WORKSPACE_CLASS (klass);
- ide_workspace_class_set_kind (workspace_class, "primary");
+ object_class->dispose = ide_primary_workspace_dispose;
- workspace_class->surface_set = ide_primary_workspace_surface_set;
+ workspace_class->add_grid_column = ide_primary_workspace_add_grid_column;
+ workspace_class->add_overlay = ide_primary_workspace_add_overlay;
+ workspace_class->add_page = ide_primary_workspace_add_page;
+ workspace_class->add_pane = ide_primary_workspace_add_pane;
+ workspace_class->can_search = ide_primary_workspace_can_search;
workspace_class->context_set = ide_primary_workspace_context_set;
+ workspace_class->foreach_page = ide_primary_workspace_foreach_page;
+ workspace_class->get_frame_at_position = ide_primary_workspace_get_frame_at_position;
+ workspace_class->get_header_bar = ide_primary_workspace_get_header_bar;
+ workspace_class->get_most_recent_frame = ide_primary_workspace_get_most_recent_frame;
+ workspace_class->remove_overlay = ide_primary_workspace_remove_overlay;
+
+ ide_workspace_class_set_kind (workspace_class, "primary");
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-primary-workspace.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, add_button);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, build_menu);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, dock);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, edge_bottom);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, edge_end);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, edge_start);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, grid);
gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, header_bar);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, omni_bar);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, overlay);
gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, project_title);
gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, run_button);
- gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, search_tooltip);
- gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, surface_menu_button);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Return, GDK_CONTROL_MASK,
"workbench.global-search", NULL);
+
+ g_type_ensure (IDE_TYPE_GRID);
+ g_type_ensure (IDE_TYPE_NOTIFICATIONS_BUTTON);
+ g_type_ensure (IDE_TYPE_OMNI_BAR);
g_type_ensure (IDE_TYPE_RUN_BUTTON);
}
static void
ide_primary_workspace_init (IdePrimaryWorkspace *self)
{
+ GMenu *build_menu;
+ GMenu *menu;
+
gtk_widget_init_template (GTK_WIDGET (self));
+ menu = ide_application_get_menu_by_id (IDE_APPLICATION_DEFAULT, "new-document-menu");
+ gtk_menu_button_set_menu_model (self->add_button, G_MENU_MODEL (menu));
+
+ build_menu = ide_application_get_menu_by_id (IDE_APPLICATION_DEFAULT, "build-menu");
+ ide_joined_menu_append_menu (self->build_menu, G_MENU_MODEL (build_menu));
+
_ide_primary_workspace_init_actions (self);
- _ide_window_settings_register (GTK_WINDOW (self));
+}
+
+/**
+ * ide_primary_workspace_get_omni_bar:
+ * @self: an #IdePrimaryWorkspace
+ *
+ * Retrieves the #IdeOmniBar of @self.
+ *
+ * Returns: (transfer none): an #IdeOmniBar
+ */
+IdeOmniBar *
+ide_primary_workspace_get_omni_bar (IdePrimaryWorkspace *self)
+{
+ g_return_val_if_fail (IDE_IS_PRIMARY_WORKSPACE (self), NULL);
+
+ return self->omni_bar;
}
diff --git a/src/libide/gui/ide-primary-workspace.h b/src/libide/gui/ide-primary-workspace.h
index a20da72eb..b326ad52e 100644
--- a/src/libide/gui/ide-primary-workspace.h
+++ b/src/libide/gui/ide-primary-workspace.h
@@ -25,14 +25,17 @@
#endif
#include "ide-application.h"
-#include "ide-surface.h"
+#include "ide-omni-bar.h"
#include "ide-workspace.h"
G_BEGIN_DECLS
#define IDE_TYPE_PRIMARY_WORKSPACE (ide_primary_workspace_get_type())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdePrimaryWorkspace, ide_primary_workspace, IDE, PRIMARY_WORKSPACE, IdeWorkspace)
+IDE_AVAILABLE_IN_ALL
+IdeOmniBar *ide_primary_workspace_get_omni_bar (IdePrimaryWorkspace *self);
+
G_END_DECLS
diff --git a/src/libide/gui/ide-primary-workspace.ui b/src/libide/gui/ide-primary-workspace.ui
index d9dfddef4..7cc961235 100644
--- a/src/libide/gui/ide-primary-workspace.ui
+++ b/src/libide/gui/ide-primary-workspace.ui
@@ -4,72 +4,106 @@
<child type="titlebar">
<object class="IdeHeaderBar" id="header_bar">
<property name="menu-id">ide-primary-workspace-menu</property>
- <property name="show-close-button">true</property>
- <property name="show-fullscreen-button">false</property>
- <property name="visible">true</property>
<child type="left">
- <object class="IdeSurfacesButton" id="surface_menu_button">
- <property name="focus-on-click">false</property>
- <property name="menu-id">ide-primary-workspace-surfaces-menu</property>
- <property name="show-accels">true</property>
- <property name="show-arrow">true</property>
- <property name="show-icons">true</property>
- <!-- disable transitions since they'll cause jitter with the
- whole surface changing below it. -->
- <property name="transitions-enabled">false</property>
- <property name="has-tooltip">true</property>
- <property name="tooltip-text" translatable="yes">Switch surface</property>
+ <object class="GtkMenuButton" id="add_button">
+ <property name="icon-name">list-add-symbolic</property>
+ <property name="always-show-arrow">true</property>
+ </object>
+ </child>
+ <child type="left">
+ <object class="PanelDockSwitcher">
+ <property name="dock">dock</property>
+ <property name="position">start</property>
</object>
</child>
<child type="title">
- <object class="IdeOmniBar" id="omni_bar">
- <property name="halign">center</property>
- <property name="hexpand">false</property>
- <property name="hexpand-set">true</property>
- <property name="visible">true</property>
- <child type="placeholder">
- <object class="GtkLabel" id="project_title">
- <property name="ellipsize">end</property>
- <property name="visible">true</property>
- <property name="xalign">0.0</property>
+ <object class="AdwClamp">
+ <property name="orientation">horizontal</property>
+ <property name="maximum-size">500</property>
+ <child>
+ <object class="IdeOmniBar" id="omni_bar">
+ <property name="icon-name">builder-build-symbolic</property>
+ <property name="action-name">build-manager.build</property>
+ <property name="menu-model">build_menu</property>
+ <child type="placeholder">
+ <object class="GtkLabel" id="project_title">
+ <property name="ellipsize">end</property>
+ <property name="xalign">0.0</property>
+ <property name="width-chars">5</property>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
<child type="right-of-center">
<object class="IdeRunButton" id="run_button">
- <property name="visible">true</property>
</object>
</child>
<child type="right">
- <object class="IdeSearchButton" id="search_button">
- <property name="visible">true</property>
- <child internal-child="entry">
- <object class="DzlSuggestionEntry">
- <property name="max-width-chars">20</property>
+ <object class="IdeNotificationsButton" id="notifications_button"/>
+ </child>
+ <child type="right">
+ <object class="PanelDockSwitcher">
+ <property name="dock">dock</property>
+ <property name="position">end</property>
+ </object>
+ </child>
+ <child type="right">
+ <object class="GtkButton" id="search_button">
+ <property name="action-name">workbench.global-search</property>
+ <property name="icon-name">edit-find-symbolic</property>
+ <property name="margin-end">3</property>
+ <property name="margin-start">3</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="PanelDock" id="dock">
+ <property name="start-width">300</property>
+ <property name="end-width">300</property>
+ <property name="reveal-start">true</property>
+ <property name="vexpand">true</property>
+ <child type="center">
+ <object class="GtkOverlay" id="overlay">
+ <child>
+ <object class="IdeGrid" id="grid">
</object>
</child>
</object>
</child>
- <child type="right">
- <object class="GtkRevealer">
- <property name="reveal-child">false</property>
- <property name="transition-type">slide-left</property>
- <property name="visible">true</property>
+ <child type="start">
+ <object class="PanelPaned" id="edge_start">
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+ <child type="end">
+ <object class="PanelPaned" id="edge_end">
+ <property name="orientation">vertical</property>
<child>
- <object class="IdeNotificationsButton" id="notifications_button">
- <property name="show-theatric">false</property>
- <property name="visible">true</property>
+ <object class="PanelFrame">
</object>
</child>
</object>
</child>
+ <child type="bottom">
+ <object class="PanelPaned" id="edge_bottom">
+ <property name="orientation">horizontal</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child internal-child="statusbar">
+ <object class="PanelStatusbar">
+ <child type="suffix">
+ <object class="PanelDockSwitcher">
+ <property name="dock">dock</property>
+ <property name="position">bottom</property>
+ </object>
+ </child>
</object>
</child>
</template>
- <object class="DzlShortcutTooltip" id="search_tooltip">
- <property name="title" translatable="yes">Search your project</property>
- <property name="command-id">org.gnome.builder.workspace.global-search</property>
- <property name="widget">search_button</property>
- </object>
+ <object class="IdeJoinedMenu" id="build_menu"/>
</interface>
diff --git a/src/libide/gui/ide-workbench-addin.c b/src/libide/gui/ide-workbench-addin.c
index 3876fe118..8b0ba2ce9 100644
--- a/src/libide/gui/ide-workbench-addin.c
+++ b/src/libide/gui/ide-workbench-addin.c
@@ -26,23 +26,6 @@
G_DEFINE_INTERFACE (IdeWorkbenchAddin, ide_workbench_addin, G_TYPE_OBJECT)
-static void ide_workbench_addin_real_open_at_async (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *hint,
- gint at_line,
- gint at_line_offset,
- IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-static void ide_workbench_addin_real_open_async (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *hint,
- IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-
static void
ide_workbench_addin_real_load_project_async (IdeWorkbenchAddin *self,
IdeProjectInfo *project_info,
@@ -91,62 +74,28 @@ static void
ide_workbench_addin_real_open_async (IdeWorkbenchAddin *self,
GFile *file,
const gchar *hint,
+ int at_line,
+ int at_line_offset,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
- IdeWorkbenchAddinInterface *iface;
-
g_assert (IDE_IS_WORKBENCH_ADDIN (self));
g_assert (G_IS_FILE (file));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- iface = IDE_WORKBENCH_ADDIN_GET_IFACE (self);
-
- if (iface->open_at_async == (gpointer)ide_workbench_addin_real_open_at_async)
- {
- ide_task_report_new_error (self, callback, user_data,
- ide_workbench_addin_real_open_async,
- G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- "Opening files is not supported");
- return;
- }
-
- iface->open_at_async (self, file, hint, -1, -1, flags, cancellable, callback, user_data);
-}
-
-static void
-ide_workbench_addin_real_open_at_async (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *hint,
- gint at_line,
- gint at_line_offset,
- IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- IdeWorkbenchAddinInterface *iface;
-
- g_assert (IDE_IS_WORKBENCH_ADDIN (self));
- g_assert (G_IS_FILE (file));
- g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- iface = IDE_WORKBENCH_ADDIN_GET_IFACE (self);
-
- if (iface->open_async == (gpointer)ide_workbench_addin_real_open_async)
- {
- ide_task_report_new_error (self, callback, user_data,
- ide_workbench_addin_real_open_at_async,
- G_IO_ERROR,
- G_IO_ERROR_NOT_SUPPORTED,
- "Opening files is not supported");
- return;
- }
-
- iface->open_async (self, file, hint, flags, cancellable, callback, user_data);
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_async (self,
+ file,
+ hint,
+ at_line,
+ at_line_offset,
+ flags,
+ position,
+ cancellable,
+ callback,
+ user_data);
}
static gboolean
@@ -165,7 +114,6 @@ ide_workbench_addin_default_init (IdeWorkbenchAddinInterface *iface)
iface->unload_project_async = ide_workbench_addin_real_unload_project_async;
iface->unload_project_finish = ide_workbench_addin_real_unload_project_finish;
iface->open_async = ide_workbench_addin_real_open_async;
- iface->open_at_async = ide_workbench_addin_real_open_at_async;
iface->open_finish = ide_workbench_addin_real_open_finish;
}
@@ -297,7 +245,10 @@ void
ide_workbench_addin_open_async (IdeWorkbenchAddin *self,
GFile *file,
const gchar *content_type,
+ int at_line,
+ int at_line_offset,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -309,38 +260,15 @@ ide_workbench_addin_open_async (IdeWorkbenchAddin *self,
IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_async (self,
file,
content_type,
+ at_line,
+ at_line_offset,
flags,
+ position,
cancellable,
callback,
user_data);
}
-void
-ide_workbench_addin_open_at_async (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *content_type,
- gint at_line,
- gint at_line_offset,
- IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
- g_return_if_fail (G_IS_FILE (file));
- g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
- IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_at_async (self,
- file,
- content_type,
- at_line,
- at_line_offset,
- flags,
- cancellable,
- callback,
- user_data);
-}
-
gboolean
ide_workbench_addin_open_finish (IdeWorkbenchAddin *self,
GAsyncResult *result,
@@ -363,8 +291,6 @@ ide_workbench_addin_open_finish (IdeWorkbenchAddin *self,
*
* This is helpful for plugins that want to react to VCS changes such as
* changing branches, or tracking commits.
- *
- * Since: 3.32
*/
void
ide_workbench_addin_vcs_changed (IdeWorkbenchAddin *self,
@@ -387,8 +313,6 @@ ide_workbench_addin_vcs_changed (IdeWorkbenchAddin *self,
* It is useful for situations where you do not need to influence the
* project loading, but do need to perform operations after it has
* completed.
- *
- * Since: 3.32
*/
void
ide_workbench_addin_project_loaded (IdeWorkbenchAddin *self,
diff --git a/src/libide/gui/ide-workbench-addin.h b/src/libide/gui/ide-workbench-addin.h
index 7f333824a..971380058 100644
--- a/src/libide/gui/ide-workbench-addin.h
+++ b/src/libide/gui/ide-workbench-addin.h
@@ -20,6 +20,11 @@
#pragma once
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include "ide-panel-position.h"
#include "ide-workbench.h"
#include "ide-workspace.h"
@@ -27,7 +32,7 @@ G_BEGIN_DECLS
#define IDE_TYPE_WORKBENCH_ADDIN (ide_workbench_addin_get_type ())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (IdeWorkbenchAddin, ide_workbench_addin, IDE, WORKBENCH_ADDIN, GObject)
struct _IdeWorkbenchAddinInterface
@@ -67,16 +72,10 @@ struct _IdeWorkbenchAddinInterface
void (*open_async) (IdeWorkbenchAddin *self,
GFile *file,
const gchar *content_type,
+ int at_line,
+ int at_line_offset,
IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- void (*open_at_async) (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *content_type,
- gint at_line,
- gint at_line_offset,
- IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -87,72 +86,65 @@ struct _IdeWorkbenchAddinInterface
IdeVcs *vcs);
};
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_load (IdeWorkbenchAddin *self,
IdeWorkbench *workbench);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_unload (IdeWorkbenchAddin *self,
IdeWorkbench *workbench);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_load_project_async (IdeWorkbenchAddin *self,
IdeProjectInfo *project_info,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_addin_load_project_finish (IdeWorkbenchAddin *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_unload_project_async (IdeWorkbenchAddin *self,
IdeProjectInfo *project_info,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_addin_unload_project_finish (IdeWorkbenchAddin *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_project_loaded (IdeWorkbenchAddin *self,
IdeProjectInfo *project_info);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_workspace_added (IdeWorkbenchAddin *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_workspace_removed (IdeWorkbenchAddin *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_addin_can_open (IdeWorkbenchAddin *self,
GFile *file,
const gchar *content_type,
gint *priority);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_open_async (IdeWorkbenchAddin *self,
GFile *file,
const gchar *content_type,
+ int at_line,
+ int at_line_offset,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-void ide_workbench_addin_open_at_async (IdeWorkbenchAddin *self,
- GFile *file,
- const gchar *content_type,
- gint at_line,
- gint at_line_offset,
- IdeBufferOpenFlags flags,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_addin_open_finish (IdeWorkbenchAddin *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_addin_vcs_changed (IdeWorkbenchAddin *self,
IdeVcs *vcs);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkbenchAddin *ide_workbench_addin_find_by_module_name (IdeWorkbench *workbench,
const gchar *module_name);
diff --git a/src/libide/gui/ide-workbench-private.h b/src/libide/gui/ide-workbench-private.h
new file mode 100644
index 000000000..68bc88638
--- /dev/null
+++ b/src/libide/gui/ide-workbench-private.h
@@ -0,0 +1,31 @@
+/* ide-workbench-private.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-workbench.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+gboolean _ide_workbench_is_last_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workbench.c b/src/libide/gui/ide-workbench.c
index 8da7c6e1b..0369761b9 100644
--- a/src/libide/gui/ide-workbench.c
+++ b/src/libide/gui/ide-workbench.c
@@ -23,9 +23,11 @@
#include "config.h"
#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
#include <libide-debugger.h>
+#include <libide-gtk.h>
#include <libide-threading.h>
-#include <libpeas/peas.h>
#include "ide-build-private.h"
#include "ide-context-private.h"
@@ -34,13 +36,13 @@
#include "ide-transfer-manager-private.h"
#include "ide-application.h"
-#include "ide-command-manager.h"
#include "ide-gui-global.h"
-#include "ide-gui-private.h"
+#include "ide-preferences-window.h"
#include "ide-primary-workspace.h"
-#include "ide-workbench.h"
+#include "ide-shortcut-manager-private.h"
#include "ide-workbench-addin.h"
-#include "ide-workspace.h"
+#include "ide-workbench-private.h"
+#include "ide-workspace-private.h"
/**
* SECTION:ide-workbench
@@ -54,8 +56,6 @@
* Usually, windows within the #IdeWorkbench are an #IdeWorkspace. They can
* react to changes in the #IdeContext or its descendants to represent the
* project and it's state.
- *
- * Since: 3.32
*/
struct _IdeWorkbench
@@ -66,17 +66,17 @@ struct _IdeWorkbench
GQueue mru_queue;
/* Owned references */
- PeasExtensionSet *addins;
- GCancellable *cancellable;
- IdeContext *context;
- IdeBuildSystem *build_system;
- IdeProjectInfo *project_info;
- IdeVcs *vcs;
- IdeVcsMonitor *vcs_monitor;
- IdeSearchEngine *search_engine;
+ PeasExtensionSet *addins;
+ GCancellable *cancellable;
+ IdeContext *context;
+ IdeBuildSystem *build_system;
+ IdeProjectInfo *project_info;
+ IdeVcs *vcs;
+ IdeVcsMonitor *vcs_monitor;
+ IdeSearchEngine *search_engine;
/* Various flags */
- guint unloaded : 1;
+ guint unloaded : 1;
};
typedef struct
@@ -86,6 +86,7 @@ typedef struct
GFile *file;
gchar *hint;
gchar *content_type;
+ IdePanelPosition *position;
IdeBufferOpenFlags flags;
gint at_line;
gint at_line_offset;
@@ -112,32 +113,40 @@ enum {
N_PROPS
};
-static void ide_workbench_action_close (IdeWorkbench *self,
- GVariant *param);
-static void ide_workbench_action_open (IdeWorkbench *self,
- GVariant *param);
-static void ide_workbench_action_dump_tasks (IdeWorkbench *self,
- GVariant *param);
-static void ide_workbench_action_object_tree (IdeWorkbench *self,
- GVariant *param);
-static void ide_workbench_action_inspector (IdeWorkbench *self,
- GVariant *param);
-static void ide_workbench_action_reload_all (IdeWorkbench *self,
- GVariant *param);
-
-
-DZL_DEFINE_ACTION_GROUP (IdeWorkbench, ide_workbench, {
+static void ide_workbench_action_close (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_open (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_open_uri (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_dump_tasks (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_object_tree (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_inspector (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_reload_all (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_global_search (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_configure (IdeWorkbench *self,
+ GVariant *param);
+
+IDE_DEFINE_ACTION_GROUP (IdeWorkbench, ide_workbench, {
{ "close", ide_workbench_action_close },
{ "open", ide_workbench_action_open },
+ { "open-uri", ide_workbench_action_open_uri, "s" },
{ "reload-files", ide_workbench_action_reload_all },
+ { "global-search", ide_workbench_action_global_search },
+ { "configure", ide_workbench_action_configure },
+ { "configure-page", ide_workbench_action_configure, "s" },
{ "-inspector", ide_workbench_action_inspector },
{ "-object-tree", ide_workbench_action_object_tree },
{ "-dump-tasks", ide_workbench_action_dump_tasks },
})
G_DEFINE_FINAL_TYPE_WITH_CODE (IdeWorkbench, ide_workbench, GTK_TYPE_WINDOW_GROUP,
- G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
- ide_workbench_init_action_group))
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, ide_workbench_init_action_group))
static GParamSpec *properties [N_PROPS];
@@ -182,8 +191,6 @@ ignore_error (GError *error)
* Helper to get the #IdeWorkbench for a given context.
*
* Returns: (transfer none) (nullable): an #IdeWorkbench or %NULL
- *
- * Since: 3.40
*/
IdeWorkbench *
ide_workbench_from_context (IdeContext *context)
@@ -271,7 +278,7 @@ ide_workbench_addin_added_cb (PeasExtensionSet *set,
ide_workbench_addin_load_project_async (addin, self->project_info, NULL, NULL, NULL);
ide_workbench_foreach_workspace (self,
- (GtkCallback)ide_workbench_addin_added_workspace_cb,
+ (IdeWorkspaceCallback)ide_workbench_addin_added_workspace_cb,
addin);
}
@@ -293,7 +300,7 @@ ide_workbench_addin_removed_cb (PeasExtensionSet *set,
* track them for cleanup.
*/
ide_workbench_foreach_workspace (self,
- (GtkCallback)ide_workbench_addin_removed_workspace_cb,
+ (IdeWorkspaceCallback)ide_workbench_addin_removed_workspace_cb,
addin);
ide_workbench_addin_unload (addin, self);
@@ -314,7 +321,7 @@ ide_workbench_notify_context_title (IdeWorkbench *self,
title = ide_context_dup_title (context);
formatted = g_strdup_printf (_("Builder — %s"), title);
ide_workbench_foreach_workspace (self,
- (GtkCallback)gtk_window_set_title,
+ (IdeWorkspaceCallback)gtk_window_set_title,
formatted);
}
@@ -378,9 +385,6 @@ ide_workbench_constructed (GObject *object)
peas_extension_set_foreach (self->addins,
ide_workbench_addin_added_cb,
self);
-
- /* Load command providers (which may register shortcuts) */
- (void)ide_command_manager_from_context (self->context);
}
static void
@@ -419,6 +423,10 @@ ide_workbench_get_property (GObject *object,
g_value_set_object (value, ide_workbench_get_context (self));
break;
+ case PROP_VCS:
+ g_value_set_object (value, ide_workbench_get_vcs (self));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -462,8 +470,6 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
*
* The #IdeContext is the root #IdeObject used in the tree of
* objects representing the project and the workings of the IDE.
- *
- * Since: 3.32
*/
properties [PROP_CONTEXT] =
g_param_spec_object ("context",
@@ -479,8 +485,6 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
* system that is currently loaded for the project.
*
* The #IdeVcs is registered by an #IdeWorkbenchAddin when loading a project.
- *
- * Since: 3.32
*/
properties [PROP_VCS] =
g_param_spec_object ("vcs",
@@ -495,6 +499,7 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
static void
ide_workbench_init (IdeWorkbench *self)
{
+ ide_workbench_set_action_enabled (self, "configure", FALSE);
}
static void
@@ -548,8 +553,6 @@ ide_workbench_find_addin (IdeWorkbench *self,
* be created based on the kind of workspace you want to display to the user.
*
* Returns: an #IdeWorkbench
- *
- * Since: 3.32
*/
IdeWorkbench *
ide_workbench_new (void)
@@ -565,8 +568,6 @@ ide_workbench_new (void)
* Creates a new #IdeWorkbench using @context for the #IdeWorkbench:context.
*
* Returns: (transfer full): an #IdeWorkbench
- *
- * Since: 3.32
*/
IdeWorkbench *
ide_workbench_new_for_context (IdeContext *context)
@@ -585,8 +586,6 @@ ide_workbench_new_for_context (IdeContext *context)
* Gets the #IdeContext for the workbench.
*
* Returns: (transfer none): an #IdeContext
- *
- * Since: 3.32
*/
IdeContext *
ide_workbench_get_context (IdeWorkbench *self)
@@ -604,8 +603,6 @@ ide_workbench_get_context (IdeWorkbench *self)
* Finds the #IdeWorkbench associated with a widget.
*
* Returns: (nullable) (transfer none): an #IdeWorkbench or %NULL
- *
- * Since: 3.32
*/
IdeWorkbench *
ide_workbench_from_widget (GtkWidget *widget)
@@ -621,7 +618,7 @@ ide_workbench_from_widget (GtkWidget *widget)
* just need to get the toplevel window group property, and cast.
*/
- if ((toplevel = gtk_widget_get_toplevel (widget)) &&
+ if ((toplevel = GTK_WIDGET (gtk_widget_get_native (widget))) &&
GTK_IS_WINDOW (toplevel) &&
(group = gtk_window_get_group (GTK_WINDOW (toplevel))) &&
IDE_IS_WORKBENCH (group))
@@ -633,18 +630,16 @@ ide_workbench_from_widget (GtkWidget *widget)
/**
* ide_workbench_foreach_workspace:
* @self: an #IdeWorkbench
- * @callback: (scope call): a #GtkCallback to call for each #IdeWorkspace
+ * @callback: (scope call): a #IdeWorkspaceCallback to call for each #IdeWorkspace
* @user_data: user data for @callback
*
* Iterates the available workspaces in the workbench. Workspaces are iterated
* in most-recently-used order.
- *
- * Since: 3.32
*/
void
-ide_workbench_foreach_workspace (IdeWorkbench *self,
- GtkCallback callback,
- gpointer user_data)
+ide_workbench_foreach_workspace (IdeWorkbench *self,
+ IdeWorkspaceCallback callback,
+ gpointer user_data)
{
GList *copy;
@@ -659,12 +654,32 @@ ide_workbench_foreach_workspace (IdeWorkbench *self,
{
IdeWorkspace *workspace = iter->data;
g_assert (IDE_IS_WORKSPACE (workspace));
- callback (GTK_WIDGET (workspace), user_data);
+ callback (workspace, user_data);
}
g_list_free (copy);
}
+typedef struct
+{
+ IdePageCallback callback;
+ gpointer user_data;
+} ForeachPage;
+
+static void
+ide_workbench_foreach_page_cb (IdeWorkspace *workspace,
+ gpointer user_data)
+{
+ ForeachPage *state = user_data;
+
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (state != NULL);
+ g_assert (state->callback != NULL);
+
+ ide_workspace_foreach_page (workspace, state->callback, state->user_data);
+}
+
+
/**
* ide_workbench_foreach_page:
* @self: a #IdeWorkbench
@@ -673,40 +688,30 @@ ide_workbench_foreach_workspace (IdeWorkbench *self,
*
* Calls @callback for every page loaded in the workbench, by iterating
* workspaces in order of most-recently-used.
- *
- * Since: 3.32
*/
void
-ide_workbench_foreach_page (IdeWorkbench *self,
- GtkCallback callback,
- gpointer user_data)
+ide_workbench_foreach_page (IdeWorkbench *self,
+ IdePageCallback callback,
+ gpointer user_data)
{
- GList *copy;
+ ForeachPage state = {callback, user_data};
g_return_if_fail (IDE_IS_WORKBENCH (self));
g_return_if_fail (callback != NULL);
- /* Make a copy to be safe against auto-cleanup removals */
- copy = g_list_copy (self->mru_queue.head);
- for (const GList *iter = copy; iter; iter = iter->next)
- {
- IdeWorkspace *workspace = iter->data;
- g_assert (IDE_IS_WORKSPACE (workspace));
- ide_workspace_foreach_page (workspace, callback, user_data);
- }
- g_list_free (copy);
+ ide_workbench_foreach_workspace (self, ide_workbench_foreach_page_cb, &state);
}
static void
-ide_workbench_workspace_has_toplevel_focus_cb (IdeWorkbench *self,
- GParamSpec *pspec,
- IdeWorkspace *workspace)
+ide_workbench_workspace_is_active_cb (IdeWorkbench *self,
+ GParamSpec *pspec,
+ IdeWorkspace *workspace)
{
g_assert (IDE_IS_WORKBENCH (self));
g_assert (IDE_IS_WORKSPACE (workspace));
g_assert (gtk_window_get_group (GTK_WINDOW (workspace)) == GTK_WINDOW_GROUP (self));
- if (gtk_window_has_toplevel_focus (GTK_WINDOW (workspace)))
+ if (gtk_window_is_active (GTK_WINDOW (workspace)))
{
GList *mru_link = _ide_workspace_get_mru_link (workspace);
@@ -731,6 +736,7 @@ insert_action_groups_foreach_cb (IdeWorkspace *workspace,
} groups[] = {
{ "config-manager", IDE_TYPE_CONFIG_MANAGER },
{ "build-manager", IDE_TYPE_BUILD_MANAGER },
+ { "debug-manager", IDE_TYPE_DEBUG_MANAGER },
{ "device-manager", IDE_TYPE_DEVICE_MANAGER },
{ "run-manager", IDE_TYPE_RUN_MANAGER },
{ "test-manager", IDE_TYPE_TEST_MANAGER },
@@ -757,15 +763,13 @@ insert_action_groups_foreach_cb (IdeWorkspace *workspace,
* @workspace: an #IdeWorkspace
*
* Adds @workspace to @workbench.
- *
- * Since: 3.32
*/
void
ide_workbench_add_workspace (IdeWorkbench *self,
IdeWorkspace *workspace)
{
g_autoptr(GPtrArray) addins = NULL;
- IdeCommandManager *command_manager;
+ IdeShortcutManager *shortcuts;
GList *mru_link;
g_return_if_fail (IDE_IS_MAIN_THREAD ());
@@ -808,10 +812,14 @@ ide_workbench_add_workspace (IdeWorkbench *self,
if (self->project_info != NULL)
insert_action_groups_foreach_cb (workspace, self);
+ /* Setup capture shortcut controller for workspace */
+ shortcuts = ide_shortcut_manager_from_context (self->context);
+ _ide_workspace_set_shortcut_model (workspace, G_LIST_MODEL (shortcuts));
+
/* Track toplevel focus changes to maintain a most-recently-used queue. */
g_signal_connect_object (workspace,
- "notify::has-toplevel-focus",
- G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+ "notify::is-active",
+ G_CALLBACK (ide_workbench_workspace_is_active_cb),
self,
G_CONNECT_SWAPPED);
@@ -839,10 +847,6 @@ ide_workbench_add_workspace (IdeWorkbench *self,
formatted = g_strdup_printf (_("Builder — %s"), title);
gtk_window_set_title (GTK_WINDOW (workspace), formatted);
}
-
- /* Load shortcuts for commands */
- command_manager = ide_command_manager_from_context (self->context);
- _ide_command_manager_init_shortcuts (command_manager, workspace);
}
/**
@@ -851,15 +855,12 @@ ide_workbench_add_workspace (IdeWorkbench *self,
* @workspace: an #IdeWorkspace
*
* Removes @workspace from @workbench.
- *
- * Since: 3.32
*/
void
ide_workbench_remove_workspace (IdeWorkbench *self,
IdeWorkspace *workspace)
{
g_autoptr(GPtrArray) addins = NULL;
- IdeCommandManager *command_manager;
GList *list;
GList *mru_link;
guint count = 0;
@@ -872,13 +873,9 @@ ide_workbench_remove_workspace (IdeWorkbench *self,
mru_link = _ide_workspace_get_mru_link (workspace);
g_queue_unlink (&self->mru_queue, mru_link);
g_signal_handlers_disconnect_by_func (workspace,
- G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+ G_CALLBACK (ide_workbench_workspace_is_active_cb),
self);
- /* Remove any shortcuts that were registered by command providers */
- command_manager = ide_command_manager_from_context (self->context);
- _ide_command_manager_unload_shortcuts (command_manager, workspace);
-
/* Notify all the addins about losing the workspace. */
if ((addins = ide_workbench_collect_addins (self)))
{
@@ -927,8 +924,6 @@ ide_workbench_remove_workspace (IdeWorkbench *self,
*
* Requests that @workspace be raised in the windows of @self, and
* displayed to the user.
- *
- * Since: 3.32
*/
void
ide_workbench_focus_workspace (IdeWorkbench *self,
@@ -1004,7 +999,7 @@ ide_workbench_load_project_completed (IdeWorkbench *self,
/* Give workspaces access to the various GActionGroups */
ide_workbench_foreach_workspace (self,
- (GtkCallback)insert_action_groups_foreach_cb,
+ (IdeWorkspaceCallback)insert_action_groups_foreach_cb,
self);
/* Notify addins that projects have loaded */
@@ -1018,6 +1013,9 @@ ide_workbench_load_project_completed (IdeWorkbench *self,
build_manager = ide_build_manager_from_context (self->context);
_ide_build_manager_start (build_manager);
+ /* Enable actions that are available to projects */
+ ide_workbench_set_action_enabled (self, "configure", TRUE);
+
ide_task_return_boolean (task, TRUE);
}
@@ -1127,8 +1125,6 @@ ide_workbench_init_foundry_cb (GObject *object,
*
* @callback should call ide_workbench_load_project_finish() to obtain the
* result of the open request.
- *
- * Since: 3.32
*/
void
ide_workbench_load_project_async (IdeWorkbench *self,
@@ -1283,8 +1279,6 @@ ide_workbench_load_project_async (IdeWorkbench *self,
*
* Returns: %TRUE if the project was successfully opened; otherwise %FALSE
* and @error is set.
- *
- * Since: 3.32
*/
gboolean
ide_workbench_load_project_finish (IdeWorkbench *self,
@@ -1390,13 +1384,38 @@ ide_workbench_action_reload_all (IdeWorkbench *self,
ide_buffer_manager_reload_all_async (bufmgr, NULL, NULL, NULL);
}
+static void
+ide_workbench_action_open_response_cb (IdeWorkbench *self,
+ int response,
+ GtkFileChooserNative *chooser)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (GTK_IS_FILE_CHOOSER_NATIVE (chooser));
+
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ g_autoptr(GListModel) model = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (chooser));
+ guint n_items = g_list_model_get_n_items (model);
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(GFile) file = g_list_model_get_item (model, i);
+
+ g_assert (G_IS_FILE (file));
+
+ ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL, NULL);
+ }
+ }
+
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
+}
+
static void
ide_workbench_action_open (IdeWorkbench *self,
GVariant *param)
{
GtkFileChooserNative *chooser;
IdeWorkspace *workspace;
- gint ret;
g_assert (IDE_IS_WORKBENCH (self));
g_assert (param == NULL);
@@ -1409,26 +1428,55 @@ ide_workbench_action_open (IdeWorkbench *self,
_("_Open"),
_("_Cancel"));
gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (chooser), FALSE);
- gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), FALSE);
gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
- ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (chooser));
+ g_signal_connect_object (chooser,
+ "response",
+ G_CALLBACK (ide_workbench_action_open_response_cb),
+ self,
+ G_CONNECT_SWAPPED);
- if (ret == GTK_RESPONSE_ACCEPT)
- {
- g_autoslist(GFile) files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (chooser));
+ gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser));
+}
- for (const GSList *iter = files; iter; iter = iter->next)
- {
- GFile *file = iter->data;
+static void
+ide_workbench_action_open_uri (IdeWorkbench *self,
+ GVariant *param)
+{
+ g_autoptr(GFile) file = NULL;
- g_assert (G_IS_FILE (file));
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
+
+ file = g_file_new_for_uri (g_variant_get_string (param, NULL));
+ ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL, NULL);
+
+ IDE_EXIT;
+}
+
+static void
+ide_workbench_action_global_search (IdeWorkbench *self,
+ GVariant *param)
+{
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (param == NULL);
+
+ for (const GList *iter = self->mru_queue.head; iter; iter = iter->next)
+ {
+ IdeWorkspace *workspace = iter->data;
- ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL);
+ if (_ide_workspace_can_search (workspace))
+ {
+ _ide_workspace_begin_global_search (workspace);
+ IDE_EXIT;
}
}
- gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
+ IDE_EXIT;
}
/**
@@ -1438,8 +1486,6 @@ ide_workbench_action_open (IdeWorkbench *self,
* Gets the search engine for the workbench, if any.
*
* Returns: (transfer none): an #IdeSearchEngine
- *
- * Since: 3.32
*/
IdeSearchEngine *
ide_workbench_get_search_engine (IdeWorkbench *self)
@@ -1462,8 +1508,6 @@ ide_workbench_get_search_engine (IdeWorkbench *self)
* currently, loading.
*
* Returns: (transfer none) (nullable): an #IdeProjectInfo or %NULL
- *
- * Since: 3.32
*/
IdeProjectInfo *
ide_workbench_get_project_info (IdeWorkbench *self)
@@ -1482,6 +1526,8 @@ ide_workbench_unload_foundry_cb (GObject *object,
g_autoptr(GError) error = NULL;
IdeWorkbench *self;
+ IDE_ENTRY;
+
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
@@ -1497,22 +1543,38 @@ ide_workbench_unload_foundry_cb (GObject *object,
ide_object_destroy (IDE_OBJECT (self->context));
g_clear_object (&self->context);
}
+
+ IDE_EXIT;
}
static void
ide_workbench_unload_project_completed (IdeWorkbench *self,
IdeTask *task)
{
+ GList *copy;
+
+ IDE_ENTRY;
+
g_assert (IDE_IS_WORKBENCH (self));
g_assert (IDE_IS_TASK (task));
g_clear_object (&self->addins);
- ide_workbench_foreach_workspace (self, (GtkCallback)gtk_widget_destroy, NULL);
+
+ copy = g_list_copy_deep (self->mru_queue.head, (GCopyFunc)g_object_ref, NULL);
+ for (const GList *iter = copy; iter; iter = iter->next)
+ {
+ IdeWorkspace *workspace = iter->data;
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ gtk_window_destroy (GTK_WINDOW (workspace));
+ }
+ g_list_free_full (copy, g_object_unref);
_ide_foundry_unload_async (self->context,
ide_task_get_cancellable (task),
ide_workbench_unload_foundry_cb,
g_object_ref (task));
+
+ IDE_EXIT;
}
static void
ide_workbench_unload_project_cb (GObject *object,
@@ -1525,6 +1587,8 @@ ide_workbench_unload_project_cb (GObject *object,
IdeWorkbench *self;
GPtrArray *addins;
+ IDE_ENTRY;
+
g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
@@ -1547,6 +1611,8 @@ ide_workbench_unload_project_cb (GObject *object,
if (addins->len == 0)
ide_workbench_unload_project_completed (self, task);
+
+ IDE_EXIT;
}
/**
@@ -1560,8 +1626,6 @@ ide_workbench_unload_project_cb (GObject *object,
*
* All #IdeWorkspace windows will be closed after calling this
* function.
- *
- * Since: 3.32
*/
void
ide_workbench_unload_async (IdeWorkbench *self,
@@ -1573,6 +1637,8 @@ ide_workbench_unload_async (IdeWorkbench *self,
g_autoptr(GPtrArray) addins = NULL;
GApplication *app;
+ IDE_ENTRY;
+
g_return_if_fail (IDE_IS_WORKBENCH (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
@@ -1582,7 +1648,7 @@ ide_workbench_unload_async (IdeWorkbench *self,
if (self->unloaded)
{
ide_task_return_boolean (task, TRUE);
- return;
+ IDE_EXIT;
}
self->unloaded = TRUE;
@@ -1611,7 +1677,7 @@ ide_workbench_unload_async (IdeWorkbench *self,
if (self->project_info == NULL)
{
ide_workbench_unload_project_completed (self, g_steal_pointer (&task));
- return;
+ IDE_EXIT;
}
addins = ide_workbench_collect_addins (self);
@@ -1620,7 +1686,7 @@ ide_workbench_unload_async (IdeWorkbench *self,
if (addins->len == 0)
{
ide_workbench_unload_project_completed (self, task);
- return;
+ IDE_EXIT;
}
for (guint i = 0; i < addins->len; i++)
@@ -1638,6 +1704,8 @@ ide_workbench_unload_async (IdeWorkbench *self,
* task isn't freed while it hasn't yet finished running asynchronously.
*/
task = NULL;
+
+ IDE_EXIT;
}
/**
@@ -1650,8 +1718,6 @@ ide_workbench_unload_async (IdeWorkbench *self,
*
* Returns: %TRUE if the workbench was unloaded successfully,
* otherwise %FALSE and @error is set.
- *
- * Since: 3.32
*/
gboolean
ide_workbench_unload_finish (IdeWorkbench *self,
@@ -1708,8 +1774,6 @@ ide_workbench_open_all_cb (GObject *object,
*
* Call ide_workbench_open_finish() from @callback to complete this
* operation.
- *
- * Since: 3.32
*/
void
ide_workbench_open_all_async (IdeWorkbench *self,
@@ -1752,6 +1816,7 @@ ide_workbench_open_all_async (IdeWorkbench *self,
file,
hint,
IDE_BUFFER_OPEN_FLAGS_NONE,
+ NULL,
cancellable,
ide_workbench_open_all_cb,
g_object_ref (task));
@@ -1764,6 +1829,7 @@ ide_workbench_open_all_async (IdeWorkbench *self,
* @file: a #GFile
* @hint: (nullable): an optional hint about what addin to use
* @flags: optional flags when opening the file
+ * @position: (nullable): a position to open the page
* @cancellable: (nullable): a #GCancellable
* @callback: a #GAsyncReadyCallback to execute upon completion
* @user_data: closure data for @callback
@@ -1775,14 +1841,13 @@ ide_workbench_open_all_async (IdeWorkbench *self,
* module name of the plugin.
*
* @flags may be ignored by some backends.
- *
- * Since: 3.32
*/
void
ide_workbench_open_async (IdeWorkbench *self,
GFile *file,
const gchar *hint,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -1797,6 +1862,7 @@ ide_workbench_open_async (IdeWorkbench *self,
-1,
-1,
flags,
+ position,
cancellable,
callback,
user_data);
@@ -1853,15 +1919,16 @@ ide_workbench_open_cb (GObject *object,
next = g_ptr_array_index (o->addins, 0);
- ide_workbench_addin_open_at_async (next,
- o->file,
- o->content_type,
- o->at_line,
- o->at_line_offset,
- o->flags,
- cancellable,
- ide_workbench_open_cb,
- g_steal_pointer (&task));
+ ide_workbench_addin_open_async (next,
+ o->file,
+ o->content_type,
+ o->at_line,
+ o->at_line_offset,
+ o->flags,
+ o->position,
+ cancellable,
+ ide_workbench_open_cb,
+ g_steal_pointer (&task));
}
static gint
@@ -1970,15 +2037,16 @@ ide_workbench_open_query_info_cb (GObject *object,
first = g_ptr_array_index (o->addins, 0);
- ide_workbench_addin_open_at_async (first,
- o->file,
- o->content_type,
- o->at_line,
- o->at_line_offset,
- o->flags,
- cancellable,
- ide_workbench_open_cb,
- g_steal_pointer (&task));
+ ide_workbench_addin_open_async (first,
+ o->file,
+ o->content_type,
+ o->at_line,
+ o->at_line_offset,
+ o->flags,
+ o->position,
+ cancellable,
+ ide_workbench_open_cb,
+ g_steal_pointer (&task));
}
/**
@@ -2004,8 +2072,6 @@ ide_workbench_open_query_info_cb (GObject *object,
*
* Use ide_workbench_open_finish() to receive teh result of this
* asynchronous operation.
- *
- * Since: 3.32
*/
void
ide_workbench_open_at_async (IdeWorkbench *self,
@@ -2014,10 +2080,12 @@ ide_workbench_open_at_async (IdeWorkbench *self,
gint at_line,
gint at_line_offset,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ g_autoptr(IdePanelPosition) local_position = NULL;
g_autoptr(IdeTask) task = NULL;
g_autoptr(GPtrArray) addins = NULL;
IdeWorkbench *other;
@@ -2028,6 +2096,9 @@ ide_workbench_open_at_async (IdeWorkbench *self,
g_return_if_fail (self->unloaded == FALSE);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+ if (position == NULL)
+ position = local_position = ide_panel_position_new ();
+
/* Possibly re-route opening the file to another workbench if we
* discover the file is a better fit over there.
*/
@@ -2041,6 +2112,7 @@ ide_workbench_open_at_async (IdeWorkbench *self,
at_line,
at_line_offset,
flags,
+ NULL,
cancellable,
callback,
user_data);
@@ -2078,6 +2150,7 @@ ide_workbench_open_at_async (IdeWorkbench *self,
o->flags = flags;
o->at_line = at_line;
o->at_line_offset = at_line_offset;
+ o->position = ide_panel_position_ref (position);
ide_task_set_task_data (task, o, open_free);
g_file_query_info_async (file,
@@ -2100,8 +2173,6 @@ ide_workbench_open_at_async (IdeWorkbench *self,
*
* Returns: %TRUE if the file was successfully opened; otherwise
* %FALSE and @error is set.
- *
- * Since: 3.32
*/
gboolean
ide_workbench_open_finish (IdeWorkbench *self,
@@ -2122,8 +2193,6 @@ ide_workbench_open_finish (IdeWorkbench *self,
* deliver events such as opening new pages.
*
* Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
- *
- * Since: 3.32
*/
IdeWorkspace *
ide_workbench_get_current_workspace (IdeWorkbench *self)
@@ -2141,8 +2210,6 @@ ide_workbench_get_current_workspace (IdeWorkbench *self)
* @self: a #IdeWorkbench
*
* This function will attempt to raise the most recently focused workspace.
- *
- * Since: 3.32
*/
void
ide_workbench_activate (IdeWorkbench *self)
@@ -2179,8 +2246,6 @@ ide_workbench_propagate_vcs_cb (PeasExtensionSet *set,
* Gets the #IdeVcs that has been loaded for the workbench, if any.
*
* Returns: (transfer none) (nullable): an #IdeVcs or %NULL
- *
- * Since: 3.32
*/
IdeVcs *
ide_workbench_get_vcs (IdeWorkbench *self)
@@ -2197,8 +2262,6 @@ ide_workbench_get_vcs (IdeWorkbench *self)
* Gets the #IdeVcsMonitor for the workbench, if any.
*
* Returns: (transfer none) (nullable): an #IdeVcsMonitor or %NULL
- *
- * Since: 3.32
*/
IdeVcsMonitor *
ide_workbench_get_vcs_monitor (IdeWorkbench *self)
@@ -2247,8 +2310,6 @@ ide_workbench_vcs_notify_branch_name_cb (IdeWorkbench *self,
* @vcs: (nullable): an #IdeVcs
*
* Sets the #IdeVcs for the workbench.
- *
- * Since: 3.32
*/
void
ide_workbench_set_vcs (IdeWorkbench *self,
@@ -2302,8 +2363,6 @@ ide_workbench_set_vcs (IdeWorkbench *self,
* Gets the #IdeBuildSystem for the workbench, if any.
*
* Returns: (transfer none) (nullable): an #IdeBuildSystem or %NULL
- *
- * Since: 3.32
*/
IdeBuildSystem *
ide_workbench_get_build_system (IdeWorkbench *self)
@@ -2336,8 +2395,6 @@ remove_non_matching_build_systems_cb (IdeObject *child,
* If @build_system is %NULL, then a fallback build system will be used
* instead. It does not provide building capabilities, but allows for some
* components that require a build system to continue functioning.
- *
- * Since: 3.32
*/
void
ide_workbench_set_build_system (IdeWorkbench *self,
@@ -2386,8 +2443,6 @@ ide_workbench_set_build_system (IdeWorkbench *self,
* Gets the most-recently-used workspace that matches @type.
*
* Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
- *
- * Since: 3.32
*/
IdeWorkspace *
ide_workbench_get_workspace_by_type (IdeWorkbench *self,
@@ -2425,8 +2480,6 @@ _ide_workbench_is_last_workspace (IdeWorkbench *self,
* workbench.
*
* Returns: %TRUE if the workbench has a project
- *
- * Since: 3.32
*/
gboolean
ide_workbench_has_project (IdeWorkbench *self)
@@ -2445,8 +2498,6 @@ ide_workbench_has_project (IdeWorkbench *self)
* Finds the addin (if any) matching the plugin's @module_name.
*
* Returns: (transfer none) (nullable): an #IdeWorkbenchAddin or %NULL
- *
- * Since: 3.32
*/
IdeWorkbenchAddin *
ide_workbench_addin_find_by_module_name (IdeWorkbench *workbench,
@@ -2533,8 +2584,6 @@ ide_workbench_resolve_file_worker (IdeTask *task,
*
* If no file was discovered, some attempt will be made to locate a file
* that matches appropriately.
- *
- * Since: 3.32
*/
void
ide_workbench_resolve_file_async (IdeWorkbench *self,
@@ -2589,8 +2638,6 @@ ide_workbench_resolve_file_async (IdeWorkbench *self,
* Completes an asynchronous request to ide_workbench_resolve_file_async().
*
* Returns: (transfer full): a #GFile, or %NULL and @error is set
- *
- * Since: 3.32
*/
GFile *
ide_workbench_resolve_file_finish (IdeWorkbench *self,
@@ -2609,3 +2656,57 @@ ide_workbench_resolve_file_finish (IdeWorkbench *self,
IDE_RETURN (g_steal_pointer (&ret));
}
+
+static void
+ide_workbench_action_configure (IdeWorkbench *self,
+ GVariant *param)
+{
+ const char *page = NULL;
+ GtkWindow *window;
+ GList *windows;
+ gboolean found = FALSE;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ if (param && g_variant_is_of_type (param, G_VARIANT_TYPE_STRING))
+ page = g_variant_get_string (param, NULL);
+
+ windows = gtk_window_group_list_windows (GTK_WINDOW_GROUP (self));
+
+ for (const GList *iter = windows; iter; iter = iter->next)
+ {
+ window = iter->data;
+
+ if (IDE_IS_PREFERENCES_WINDOW (window) &&
+ ide_preferences_window_get_mode (IDE_PREFERENCES_WINDOW (window)) == IDE_PREFERENCES_MODE_PROJECT)
+ {
+ gtk_window_present (window);
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_list_free (windows);
+
+ if (!found)
+ {
+ g_autofree char *title = NULL;
+ g_autofree char *window_title = NULL;
+
+ title = ide_context_dup_title (self->context);
+ window_title = g_strdup_printf (_("Builder — %s"), title);
+
+ window = g_object_new (IDE_TYPE_PREFERENCES_WINDOW,
+ "mode", IDE_PREFERENCES_MODE_PROJECT,
+ "context", self->context,
+ "default-width", 1050,
+ "default-height", 700,
+ "title", window_title,
+ NULL);
+ gtk_window_group_add_window (GTK_WINDOW_GROUP (self), window);
+ gtk_window_present (window);
+ }
+
+ if (page != NULL)
+ ide_preferences_window_set_page (IDE_PREFERENCES_WINDOW (window), page);
+}
diff --git a/src/libide/gui/ide-workbench.h b/src/libide/gui/ide-workbench.h
index 3014a8ab0..b534049b8 100644
--- a/src/libide/gui/ide-workbench.h
+++ b/src/libide/gui/ide-workbench.h
@@ -36,88 +36,90 @@ G_BEGIN_DECLS
#define IDE_TYPE_WORKBENCH (ide_workbench_get_type())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeWorkbench, ide_workbench, IDE, WORKBENCH, GtkWindowGroup)
-IDE_AVAILABLE_IN_3_40
+IDE_AVAILABLE_IN_ALL
IdeWorkbench *ide_workbench_from_context (IdeContext *context);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkbench *ide_workbench_new (void);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkbench *ide_workbench_new_for_context (IdeContext *context);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_activate (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeProjectInfo *ide_workbench_get_project_info (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_has_project (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeContext *ide_workbench_get_context (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkspace *ide_workbench_get_current_workspace (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkspace *ide_workbench_get_workspace_by_type (IdeWorkbench *self,
GType type);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeSearchEngine *ide_workbench_get_search_engine (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeWorkbench *ide_workbench_from_widget (GtkWidget *widget);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_add_workspace (IdeWorkbench *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_remove_workspace (IdeWorkbench *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_focus_workspace (IdeWorkbench *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_foreach_workspace (IdeWorkbench *self,
- GtkCallback callback,
+ IdeWorkspaceCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_foreach_page (IdeWorkbench *self,
- GtkCallback callback,
+ IdePageCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_load_project_async (IdeWorkbench *self,
IdeProjectInfo *project_info,
GType workspace_type,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_load_project_finish (IdeWorkbench *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_unload_async (IdeWorkbench *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_unload_finish (IdeWorkbench *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_open_async (IdeWorkbench *self,
GFile *file,
const gchar *hint,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_open_at_async (IdeWorkbench *self,
GFile *file,
const gchar *hint,
gint at_line,
gint at_line_offset,
IdeBufferOpenFlags flags,
+ IdePanelPosition *position,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_open_all_async (IdeWorkbench *self,
GFile **files,
guint n_files,
@@ -125,32 +127,37 @@ void ide_workbench_open_all_async (IdeWorkbench *self
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
gboolean ide_workbench_open_finish (IdeWorkbench *self,
GAsyncResult *result,
GError **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeVcs *ide_workbench_get_vcs (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_set_vcs (IdeWorkbench *self,
IdeVcs *vcs);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeVcsMonitor *ide_workbench_get_vcs_monitor (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
IdeBuildSystem *ide_workbench_get_build_system (IdeWorkbench *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_set_build_system (IdeWorkbench *self,
IdeBuildSystem *build_system);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workbench_resolve_file_async (IdeWorkbench *self,
const gchar *filename,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
GFile *ide_workbench_resolve_file_finish (IdeWorkbench *self,
GAsyncResult *result,
GError **error);
+static inline IdeWorkbench *
+ide_workspace_get_workbench (IdeWorkspace *workspace)
+{
+ return IDE_WORKBENCH (gtk_window_get_group (GTK_WINDOW (workspace)));
+}
G_END_DECLS
diff --git a/src/libide/gui/ide-workspace-addin.c b/src/libide/gui/ide-workspace-addin.c
index 3b43dffc4..f6acde158 100644
--- a/src/libide/gui/ide-workspace-addin.c
+++ b/src/libide/gui/ide-workspace-addin.c
@@ -37,8 +37,6 @@
* addin will only be loaded in the primary workspace. You may specify
* multiple workspace kinds such as `primary` or `secondary` separated
* by a comma such as `primary,secondary;`.
- *
- * Since: 3.32
*/
G_DEFINE_INTERFACE (IdeWorkspaceAddin, ide_workspace_addin, G_TYPE_OBJECT)
@@ -56,8 +54,6 @@ ide_workspace_addin_default_init (IdeWorkspaceAddinInterface *iface)
*
* This is a good place to modify the workspace from your addin.
* Remember to unmodify the workspace in ide_workspace_addin_unload().
- *
- * Since: 3.32
*/
void
ide_workspace_addin_load (IdeWorkspaceAddin *self,
@@ -79,8 +75,6 @@ ide_workspace_addin_load (IdeWorkspaceAddin *self,
*
* This is a good place to unmodify the workspace from anything you
* did in ide_workspace_addin_load().
- *
- * Since: 3.32
*/
void
ide_workspace_addin_unload (IdeWorkspaceAddin *self,
@@ -95,46 +89,20 @@ ide_workspace_addin_unload (IdeWorkspaceAddin *self,
}
/**
- * ide_workspace_addin_surface_set:
- * @self: an #IdeWorkspaceAddin
- * @surface: (nullable): an #IdeSurface or %NULL
- *
- * This function is called to notify the addin of the current surface.
- * It may be set to %NULL before unloading the addin to allow addins
- * to do surface change state handling and cleanup in one function.
+ * ide_workspace_addin_page_changed:
+ * @self: a #IdeWorkspaceAddin
+ * @page: (nullable): an #IdePage or %NULL
*
- * Since: 3.32
+ * Called when the current page has changed based on focus within
+ * the workspace.
*/
void
-ide_workspace_addin_surface_set (IdeWorkspaceAddin *self,
- IdeSurface *surface)
+ide_workspace_addin_page_changed (IdeWorkspaceAddin *self,
+ IdePage *page)
{
- g_return_if_fail (IDE_IS_MAIN_THREAD ());
g_return_if_fail (IDE_IS_WORKSPACE_ADDIN (self));
- g_return_if_fail (!surface || IDE_IS_SURFACE (surface));
-
- if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->surface_set)
- IDE_WORKSPACE_ADDIN_GET_IFACE (self)->surface_set (self, surface);
-}
-
-/**
- * ide_workspace_addin_can_close:
- * @self: an #IdeWorkspaceAddin
- *
- * This method is called to determine if the workspace can close. If the addin
- * needs to prevent the workspace closing, then return %FALSE; otherwise %TRUE.
- *
- * Returns: %TRUE if the workspace can close; otherwise %FALSE.
- *
- * Since: 3.34
- */
-gboolean
-ide_workspace_addin_can_close (IdeWorkspaceAddin *self)
-{
- g_return_val_if_fail (IDE_IS_WORKSPACE_ADDIN (self), TRUE);
-
- if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->can_close)
- return IDE_WORKSPACE_ADDIN_GET_IFACE (self)->can_close (self);
+ g_return_if_fail (!page || IDE_IS_PAGE (page));
- return TRUE;
+ if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->page_changed)
+ IDE_WORKSPACE_ADDIN_GET_IFACE (self)->page_changed (self, page);
}
diff --git a/src/libide/gui/ide-workspace-addin.h b/src/libide/gui/ide-workspace-addin.h
index 49e0d70a3..4c915a841 100644
--- a/src/libide/gui/ide-workspace-addin.h
+++ b/src/libide/gui/ide-workspace-addin.h
@@ -20,33 +20,44 @@
#pragma once
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-page.h"
#include "ide-workspace.h"
G_BEGIN_DECLS
#define IDE_TYPE_WORKSPACE_ADDIN (ide_workspace_addin_get_type())
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (IdeWorkspaceAddin, ide_workspace_addin, IDE, WORKSPACE_ADDIN, GObject)
struct _IdeWorkspaceAddinInterface
{
GTypeInterface parent_iface;
- void (*load) (IdeWorkspaceAddin *self,
- IdeWorkspace *workspace);
- void (*unload) (IdeWorkspaceAddin *self,
- IdeWorkspace *workspace);
+ void (*load) (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+ void (*unload) (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+ void (*page_changed) (IdeWorkspaceAddin *self,
+ IdePage *page);
};
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workspace_addin_load (IdeWorkspaceAddin *self,
IdeWorkspace *workspace);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
void ide_workspace_addin_unload (IdeWorkspaceAddin *self,
IdeWorkspace *workspace);
- IdeSurface *surface);
-IDE_AVAILABLE_IN_3_40
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_addin_page_changed (IdeWorkspaceAddin *self,
+ IdePage *page);
+IDE_AVAILABLE_IN_ALL
IdeWorkspaceAddin *ide_workspace_addin_find_by_module_name (IdeWorkspace *workspace,
const gchar *module_name);
diff --git a/src/libide/gui/ide-workspace-private.h b/src/libide/gui/ide-workspace-private.h
new file mode 100644
index 000000000..691d303ba
--- /dev/null
+++ b/src/libide/gui/ide-workspace-private.h
@@ -0,0 +1,57 @@
+/* ide-workspace-private.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-grid.h"
+#include "ide-panel-position.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+void _ide_workspace_init_actions (IdeWorkspace *self);
+GList *_ide_workspace_get_mru_link (IdeWorkspace *self);
+void _ide_workspace_add_page_mru (IdeWorkspace *self,
+ GList *mru_link);
+void _ide_workspace_remove_page_mru (IdeWorkspace *self,
+ GList *mru_link);
+void _ide_workspace_move_front_page_mru (IdeWorkspace *workspace,
+ GList *mru_link);
+void _ide_workspace_set_context (IdeWorkspace *workspace,
+ IdeContext *context);
+gboolean _ide_workspace_can_search (IdeWorkspace *self);
+void _ide_workspace_begin_global_search (IdeWorkspace *self);
+void _ide_workspace_add_widget (IdeWorkspace *workspace,
+ PanelWidget *widget,
+ IdePanelPosition *position,
+ PanelPaned *dock_start,
+ PanelPaned *dock_end,
+ PanelPaned *dock_bottom,
+ IdeGrid *grid);
+PanelFrame *_ide_workspace_find_frame (IdeWorkspace *workspace,
+ IdePanelPosition *position,
+ PanelPaned *dock_start,
+ PanelPaned *dock_end,
+ PanelPaned *dock_bottom,
+ IdeGrid *grid);
+void _ide_workspace_set_shortcut_model (IdeWorkspace *self,
+ GListModel *shortcuts);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workspace.c b/src/libide/gui/ide-workspace.c
index 3d92bb4c8..92b180436 100644
--- a/src/libide/gui/ide-workspace.c
+++ b/src/libide/gui/ide-workspace.c
@@ -22,14 +22,20 @@
#include "config.h"
+#include <libide-search.h>
#include <libide-plugins.h>
#include "ide-gui-global.h"
-#include "ide-gui-private.h"
-#include "ide-workspace.h"
+#include "ide-page-private.h"
+#include "ide-search-popover-private.h"
+#include "ide-shortcut-bundle-private.h"
#include "ide-workspace-addin.h"
+#include "ide-workspace-private.h"
+#include "ide-workbench-private.h"
#define MUX_ACTIONS_KEY "IDE_WORKSPACE_MUX_ACTIONS"
+#define GET_PRIORITY(w) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"PRIORITY"))
+#define SET_PRIORITY(w,i) g_object_set_data(G_OBJECT(w),"PRIORITY",GINT_TO_POINTER(i))
typedef struct
{
@@ -55,58 +61,92 @@ typedef struct
*/
IdeExtensionSetAdapter *addins;
- /* We use an overlay as our top-most child so that plugins can potentially
- * render any widget a layer above the UI.
- */
- GtkOverlay *overlay;
+ /* A statusbar, if any, that was added to the workspace */
+ PanelStatusbar *statusbar;
- /* All workspaces are comprised of a series of "surfaces". However there may
- * only ever be a single surface in a workspace (such as the editor workspace
- * which is dedicated for editing).
- */
- GtkStack *surfaces;
+ /* The global search for the workspace, if any */
+ IdeSearchPopover *search_popover;
- /* The event box ensures that we can have events that will be used by the
- * fullscreen overlay so that it gets delivery of crossing events.
- */
- GtkEventBox *event_box;
- GtkBox *vbox;
+ /* GListModel of GtkShortcut w/ capture/bubble filters */
+ GtkFilterListModel *shortcut_model_bubble;
+ GtkFilterListModel *shortcut_model_capture;
/* A MRU that is updated as pages are focused. It allows us to move through
* the pages in the order they've been most-recently focused.
*/
GQueue page_mru;
- guint in_key_press : 1;
+ /* Queued source to save window size/etc */
+ guint queued_window_save;
+
+ /* Vertical box for children */
+ GtkBox *box;
+
+ /* Weak pointer to the current page. */
+ gpointer current_page_ptr;
} IdeWorkspacePrivate;
typedef struct
{
- GtkCallback callback;
- gpointer user_data;
+ IdePageCallback callback;
+ gpointer user_data;
} ForeachPage;
-enum {
- SURFACE_SET,
- N_SIGNALS
-};
-
enum {
PROP_0,
PROP_CONTEXT,
- PROP_VISIBLE_SURFACE,
N_PROPS
};
static void buildable_iface_init (GtkBuildableIface *iface);
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeWorkspace, ide_workspace, HDY_TYPE_APPLICATION_WINDOW,
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeWorkspace, ide_workspace, ADW_TYPE_APPLICATION_WINDOW,
G_ADD_PRIVATE (IdeWorkspace)
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
-static GtkBuildableIface *parent_builder;
static GParamSpec *properties [N_PROPS];
-static guint signals [N_SIGNALS];
+static GSettings *settings;
+
+static void
+ide_workspace_attach_shortcuts (IdeWorkspace *self,
+ GtkWidget *widget)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ GtkEventController *controller;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (G_IS_LIST_MODEL (priv->shortcut_model_bubble));
+ g_assert (G_IS_LIST_MODEL (priv->shortcut_model_capture));
+
+ controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (priv->shortcut_model_capture));
+ gtk_event_controller_set_name (controller, "ide-shortcuts-capture");
+ gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
+ gtk_event_controller_set_propagation_limit (controller, GTK_LIMIT_NONE);
+ gtk_widget_add_controller (widget, g_steal_pointer (&controller));
+
+ controller = gtk_shortcut_controller_new_for_model (G_LIST_MODEL (priv->shortcut_model_bubble));
+ gtk_event_controller_set_name (controller, "ide-shortcuts-bubble");
+ gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
+ gtk_event_controller_set_propagation_limit (controller, GTK_LIMIT_NONE);
+ gtk_widget_add_controller (widget, g_steal_pointer (&controller));
+}
+
+static IdePage *
+ide_workspace_get_focus_page (IdeWorkspace *self)
+{
+ GtkWidget *focus;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ if ((focus = gtk_root_get_focus (GTK_ROOT (self))))
+ {
+ if (!IDE_IS_PAGE (focus))
+ focus = gtk_widget_get_ancestor (focus, IDE_TYPE_PAGE);
+ }
+
+ return IDE_PAGE (focus);
+}
static void
ide_workspace_addin_added_cb (IdeExtensionSetAdapter *set,
@@ -116,6 +156,7 @@ ide_workspace_addin_added_cb (IdeExtensionSetAdapter *set,
{
IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
IdeWorkspace *self = user_data;
+ IdePage *page;
g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
g_assert (plugin_info != NULL);
@@ -126,6 +167,9 @@ ide_workspace_addin_added_cb (IdeExtensionSetAdapter *set,
peas_plugin_info_get_module_name (plugin_info));
ide_workspace_addin_load (addin, self);
+
+ if ((page = ide_workspace_get_focus_page (self)))
+ ide_workspace_addin_page_changed (addin, page);
}
static void
@@ -145,10 +189,27 @@ ide_workspace_addin_removed_cb (IdeExtensionSetAdapter *set,
g_debug ("Unloading workspace addin from module %s",
peas_plugin_info_get_module_name (plugin_info));
- ide_workspace_addin_surface_set (addin, NULL);
+ ide_workspace_addin_page_changed (addin, NULL);
ide_workspace_addin_unload (addin, self);
}
+static void
+ide_workspace_addin_page_changed_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
+ IdePage *page = user_data;
+
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
+ g_assert (!page || IDE_IS_PAGE (page));
+
+ ide_workspace_addin_page_changed (addin, page);
+}
+
static void
ide_workspace_real_context_set (IdeWorkspace *self,
IdeContext *context)
@@ -181,305 +242,288 @@ ide_workspace_real_context_set (IdeWorkspace *self,
}
static void
-ide_workspace_addin_surface_set_cb (IdeExtensionSetAdapter *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_workspace_close_request_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
- IdeSurface *surface = user_data;
+ IdeWorkspace *self = (IdeWorkspace *)object;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ g_autoptr(GError) error = NULL;
- g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
- g_assert (plugin_info != NULL);
- g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
- g_assert (!surface || IDE_IS_SURFACE (surface));
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->agree_to_close_finish (self, result, &error))
+ {
+ IdeWorkbench *workbench = IDE_WORKBENCH (gtk_window_get_group (GTK_WINDOW (self)));
+
+ if (ide_workbench_has_project (workbench) &&
+ _ide_workbench_is_last_workspace (workbench, self))
+ {
+ gtk_widget_hide (GTK_WIDGET (self));
+ ide_workbench_unload_async (workbench, NULL, NULL, NULL);
+ return;
+ }
- ide_workspace_addin_surface_set (addin, surface);
+ g_cancellable_cancel (priv->cancellable);
+ ide_workbench_remove_workspace (workbench, self);
+ gtk_window_destroy (GTK_WINDOW (self));
+ }
+}
+
+static gboolean
+ide_workspace_close_request (GtkWindow *window)
+{
+ IdeWorkspace *self = (IdeWorkspace *)window;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ IDE_WORKSPACE_GET_CLASS (self)->agree_to_close_async (self,
+ NULL,
+ ide_workspace_close_request_cb,
+ NULL);
+
+ return TRUE;
}
static void
-ide_workspace_real_surface_set (IdeWorkspace *self,
- IdeSurface *surface)
+ide_workspace_notify_focus_widget (IdeWorkspace *self,
+ GParamSpec *pspec,
+ gpointer user_data)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ IdePage *focus;
+
+ IDE_ENTRY;
- g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_WORKSPACE (self));
- g_assert (!surface || IDE_IS_SURFACE (surface));
+ g_assert (pspec != NULL);
+ g_assert (user_data == NULL);
+
+ focus = ide_workspace_get_focus_page (self);
+
+ if (priv->current_page_ptr != (gpointer)focus)
+ {
+ /* Focus changed, but old page is still valid */
+ if (focus == NULL)
+ IDE_EXIT;
+
+ /* Focus changed, and we have a new widget */
+ g_set_weak_pointer (&priv->current_page_ptr, focus);
- if (priv->addins != NULL)
- ide_extension_set_adapter_foreach (priv->addins,
- ide_workspace_addin_surface_set_cb,
- surface);
+ /* And move this page to the front of the MRU */
+ _ide_workspace_move_front_page_mru (self, _ide_page_get_mru_link (focus));
+
+ if (priv->addins != NULL)
+ {
+ g_object_ref (focus);
+ ide_extension_set_adapter_foreach (priv->addins,
+ ide_workspace_addin_page_changed_cb,
+ focus);
+ g_object_unref (focus);
+ }
+ }
+
+ IDE_EXIT;
}
/**
- * ide_workspace_foreach_surface:
- * @self: a #IdeWorkspace
- * @callback: (scope call): a #GtkCallback to execute for every surface
- * @user_data: user data for @callback
- *
- * Calls callback for every #IdeSurface based #GtkWidget that is registered
- * in the workspace.
+ * ide_workspace_class_set_kind:
+ * @klass: a #IdeWorkspaceClass
*
- * Since: 3.32
+ * Sets the shorthand name for the kind of workspace. This is used to limit
+ * what #IdeWorkspaceAddin may load within the workspace.
*/
void
-ide_workspace_foreach_surface (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data)
+ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
+ const gchar *kind)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
-
- g_assert (IDE_IS_WORKSPACE (self));
- g_assert (callback != NULL);
+ g_return_if_fail (IDE_IS_WORKSPACE_CLASS (klass));
- gtk_container_foreach (GTK_CONTAINER (priv->surfaces), callback, user_data);
+ klass->kind = g_intern_string (kind);
}
static void
-ide_workspace_agree_to_shutdown_cb (GtkWidget *widget,
- gpointer user_data)
+ide_workspace_real_foreach_page (IdeWorkspace *self,
+ IdePageCallback callback,
+ gpointer user_data)
{
- gboolean *blocked = user_data;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_SURFACE (widget));
- g_assert (blocked != NULL);
-
- *blocked |= !ide_surface_agree_to_shutdown (IDE_SURFACE (widget));
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (callback != NULL);
}
static void
-ide_workspace_addin_can_close_cb (IdeExtensionSetAdapter *adapter,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_workspace_agree_to_close_async (IdeWorkspace *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
- gboolean *blocked = user_data;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
- g_assert (blocked != NULL);
-
- *blocked |= !ide_workspace_addin_can_close (addin);
+ g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
+ g_task_return_boolean (task, TRUE);
}
static gboolean
-ide_workspace_agree_to_shutdown (IdeWorkspace *self)
+ide_workspace_agree_to_close_finish (IdeWorkspace *self,
+ GAsyncResult *result,
+ GError **error)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- gboolean blocked = FALSE;
-
- g_assert (IDE_IS_MAIN_THREAD ());
- g_assert (IDE_IS_WORKSPACE (self));
-
- ide_workspace_foreach_surface (self, ide_workspace_agree_to_shutdown_cb, &blocked);
-
- if (!blocked)
- ide_extension_set_adapter_foreach (priv->addins,
- ide_workspace_addin_can_close_cb,
- &blocked);
-
- return !blocked;
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static gboolean
-ide_workspace_delete_event (GtkWidget *widget,
- GdkEventAny *any)
+ide_workspace_save_settings (gpointer data)
{
- IdeWorkspace *self = (IdeWorkspace *)widget;
+ IdeWorkspace *self = data;
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- IdeWorkbench *workbench;
+ GdkRectangle geom = {0};
+ gboolean maximized;
g_assert (IDE_IS_WORKSPACE (self));
- g_assert (any != NULL);
-
- /* TODO:
- *
- * If there are any active transfers, we want to ask the user if they
- * are sure they want to exit and risk losing them. We can allow them
- * to be completed in the background.
- *
- * Note that we only want to do this on the final workspace window.
- */
-
- if (!ide_workspace_agree_to_shutdown (self))
- return GDK_EVENT_STOP;
-
- g_cancellable_cancel (priv->cancellable);
-
- workbench = ide_widget_get_workbench (widget);
-
- if (ide_workbench_has_project (workbench) &&
- _ide_workbench_is_last_workspace (workbench, self))
- {
- gtk_widget_hide (GTK_WIDGET (self));
- ide_workbench_unload_async (workbench, NULL, NULL, NULL);
- return GDK_EVENT_STOP;
- }
-
- return GDK_EVENT_PROPAGATE;
-}
-static void
-ide_workspace_notify_surface_cb (IdeWorkspace *self,
- GParamSpec *pspec,
- GtkStack *surfaces)
-{
- GtkWidget *visible_child;
- IdeHeaderBar *header_bar;
+ priv->queued_window_save = 0;
- g_assert (IDE_IS_WORKSPACE (self));
- g_assert (GTK_IS_STACK (surfaces));
+ if (!gtk_widget_get_realized (GTK_WIDGET (self)) ||
+ !gtk_widget_get_visible (GTK_WIDGET (self)) ||
+ !IDE_WORKSPACE_GET_CLASS (self)->save_size (self, &geom.width, &geom.height))
+ return G_SOURCE_REMOVE;
- visible_child = gtk_stack_get_visible_child (surfaces);
- if (!IDE_IS_SURFACE (visible_child))
- visible_child = NULL;
+ if (settings == NULL)
+ settings = g_settings_new ("org.gnome.builder");
- if (visible_child != NULL)
- gtk_widget_grab_focus (visible_child);
+ maximized = gtk_window_is_maximized (GTK_WINDOW (self));
- if ((header_bar = ide_workspace_get_header_bar (self)))
- {
- if (visible_child != NULL)
- dzl_gtk_widget_mux_action_groups (GTK_WIDGET (header_bar), visible_child, MUX_ACTIONS_KEY);
- else
- dzl_gtk_widget_mux_action_groups (GTK_WIDGET (header_bar), NULL, MUX_ACTIONS_KEY);
- }
+ g_settings_set (settings, "window-size", "(ii)", geom.width, geom.height);
+ g_settings_set_boolean (settings, "window-maximized", maximized);
- g_signal_emit (self, signals [SURFACE_SET], 0, visible_child);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_SURFACE]);
+ return G_SOURCE_REMOVE;
}
static void
-ide_workspace_destroy (GtkWidget *widget)
+ide_workspace_size_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
{
IdeWorkspace *self = (IdeWorkspace *)widget;
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- GtkWindowGroup *group;
g_assert (IDE_IS_WORKSPACE (self));
- ide_clear_and_destroy_object (&priv->addins);
+ GTK_WIDGET_CLASS (ide_workspace_parent_class)->size_allocate (widget, width, height, baseline);
- group = gtk_window_get_group (GTK_WINDOW (self));
- if (IDE_IS_WORKBENCH (group))
- ide_workbench_remove_workspace (IDE_WORKBENCH (group), self);
+ if (priv->search_popover != NULL)
+ ide_search_popover_present (priv->search_popover, width, height);
- GTK_WIDGET_CLASS (ide_workspace_parent_class)->destroy (widget);
+ if (priv->queued_window_save == 0 &&
+ IDE_WORKSPACE_GET_CLASS (self)->save_size != NULL)
+ priv->queued_window_save = g_timeout_add_seconds (1, ide_workspace_save_settings, self);
}
-/**
- * ide_workspace_class_set_kind:
- * @klass: a #IdeWorkspaceClass
- *
- * Sets the shorthand name for the kind of workspace. This is used to limit
- * what #IdeWorkspaceAddin may load within the workspace.
- *
- * Since: 3.32
- */
-void
-ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
- const gchar *kind)
+static void
+ide_workspace_restore_size (IdeWorkspace *workspace,
+ int width,
+ int height)
{
- g_return_if_fail (IDE_IS_WORKSPACE_CLASS (klass));
+ g_assert (IDE_IS_WORKSPACE (workspace));
- klass->kind = g_intern_string (kind);
+ gtk_window_set_default_size (GTK_WINDOW (workspace), width, height);
}
-
-static void
-ide_workspace_foreach_page_cb (GtkWidget *widget,
- gpointer user_data)
+static gboolean
+ide_workspace_save_size (IdeWorkspace *workspace,
+ int *width,
+ int *height)
{
- ForeachPage *state = user_data;
+ g_assert (IDE_IS_WORKSPACE (workspace));
+
+ gtk_window_get_default_size (GTK_WINDOW (workspace), width, height);
- if (IDE_IS_SURFACE (widget))
- ide_surface_foreach_page (IDE_SURFACE (widget), state->callback, state->user_data);
+ return TRUE;
}
static void
-ide_workspace_real_foreach_page (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data)
+ide_workspace_realize (GtkWidget *widget)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- ForeachPage state = { callback, user_data };
+ IdeWorkspace *self = (IdeWorkspace *)widget;
+ GdkRectangle geom = {0};
+ gboolean maximized = FALSE;
g_assert (IDE_IS_WORKSPACE (self));
- g_assert (callback != NULL);
- gtk_container_foreach (GTK_CONTAINER (priv->surfaces),
- ide_workspace_foreach_page_cb,
- &state);
-}
+ if (settings == NULL)
+ settings = g_settings_new ("org.gnome.builder");
-#if 0
-static void
-ide_workspace_set_surface_fullscreen_cb (GtkWidget *widget,
- gpointer user_data)
-{
- g_assert (GTK_IS_WIDGET (widget));
+ g_settings_get (settings, "window-size", "(ii)", &geom.width, &geom.height);
+ g_settings_get (settings, "window-maximized", "b", &maximized);
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->restore_size)
+ IDE_WORKSPACE_GET_CLASS (self)->restore_size (self, geom.width, geom.height);
- if (IDE_IS_SURFACE (widget))
- _ide_surface_set_fullscreen (IDE_SURFACE (widget), !!user_data);
+ GTK_WIDGET_CLASS (ide_workspace_parent_class)->realize (widget);
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->restore_size)
+ {
+ if (maximized)
+ gtk_window_maximize (GTK_WINDOW (self));
+ }
}
-static void
-ide_workspace_real_set_fullscreen (DzlApplicationWindow *window,
- gboolean fullscreen)
+static IdeFrame *
+ide_workspace_real_get_most_recent_frame (IdeWorkspace *self)
{
- IdeWorkspace *self = (IdeWorkspace *)window;
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- GtkWidget *titlebar;
+ IdePage *page;
g_assert (IDE_IS_WORKSPACE (self));
- DZL_APPLICATION_WINDOW_CLASS (ide_workspace_parent_class)->set_fullscreen (window, fullscreen);
-
- titlebar = dzl_application_window_get_titlebar (window);
- gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (titlebar), !fullscreen);
+ if (!(page = ide_workspace_get_most_recent_page (self)))
+ return NULL;
- gtk_container_foreach (GTK_CONTAINER (priv->surfaces),
- ide_workspace_set_surface_fullscreen_cb,
- GUINT_TO_POINTER (fullscreen));
+ return IDE_FRAME (gtk_widget_get_ancestor (GTK_WIDGET (page), IDE_TYPE_FRAME));
}
-#endif
-static void
-ide_workspace_grab_focus (GtkWidget *widget)
+static gboolean
+ide_workspace_real_can_search (IdeWorkspace *self)
{
- IdeWorkspace *self = (IdeWorkspace *)widget;
- IdeSurface *surface;
-
- g_assert (IDE_IS_WORKSPACE (self));
+ return FALSE;
+}
- if ((surface = ide_workspace_get_visible_surface (self)))
- gtk_widget_grab_focus (GTK_WIDGET (surface));
+static IdeHeaderBar *
+ide_workspace_real_get_header_bar (IdeWorkspace *workspace)
+{
+ return NULL;
}
-static gboolean
-ide_workspace_key_press_event (GtkWidget *widget,
- GdkEventKey *event)
+static void
+ide_workspace_dispose (GObject *object)
{
- IdeWorkspace *self = (IdeWorkspace *)widget;
+ IdeWorkspace *self = (IdeWorkspace *)object;
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- gboolean ret;
+ GtkWindowGroup *group;
g_assert (IDE_IS_WORKSPACE (self));
- g_assert (event != NULL);
- /* Be re-entrant safe from the shortcut manager */
- if (priv->in_key_press)
- return GTK_WIDGET_CLASS (ide_workspace_parent_class)->key_press_event (widget, event);
+ g_clear_pointer ((GtkWidget **)&priv->search_popover, gtk_widget_unparent);
+
+ g_clear_weak_pointer (&priv->current_page_ptr);
+
+ /* Unload addins immediately */
+ ide_clear_and_destroy_object (&priv->addins);
+
+ /* Remove the workspace from the workbench MRU/etc */
+ group = gtk_window_get_group (GTK_WINDOW (self));
+ if (IDE_IS_WORKBENCH (group))
+ ide_workbench_remove_workspace (IDE_WORKBENCH (group), self);
- priv->in_key_press = TRUE;
- ret = dzl_shortcut_manager_handle_event (NULL, event, widget);
- priv->in_key_press = FALSE;
+ /* Chain up to ensure the GtkWindow cleans up any widgets or other
+ * state attached to the workspace. We keep the context alive during
+ * this process.
+ */
+ G_OBJECT_CLASS (ide_workspace_parent_class)->dispose (object);
- return ret;
+ /* A reference is held during this so it is safe to run code after
+ * chaining up to dispose. Force release teh context now.
+ */
+ g_clear_object (&priv->context);
}
static void
@@ -490,6 +534,7 @@ ide_workspace_finalize (GObject *object)
g_clear_object (&priv->context);
g_clear_object (&priv->cancellable);
+ g_clear_handle_id (&priv->queued_window_save, g_source_remove);
G_OBJECT_CLASS (ide_workspace_parent_class)->finalize (object);
}
@@ -508,29 +553,6 @@ ide_workspace_get_property (GObject *object,
g_value_set_object (value, ide_workspace_get_context (self));
break;
- case PROP_VISIBLE_SURFACE:
- g_value_set_object (value, ide_workspace_get_visible_surface (self));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-ide_workspace_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- IdeWorkspace *self = IDE_WORKSPACE (object);
-
- switch (prop_id)
- {
- case PROP_VISIBLE_SURFACE:
- ide_workspace_set_visible_surface (self, g_value_get_object (value));
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -541,30 +563,32 @@ ide_workspace_class_init (IdeWorkspaceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- //DzlApplicationWindowClass *window_class = DZL_APPLICATION_WINDOW_CLASS (klass);
+ GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass);
+ object_class->dispose = ide_workspace_dispose;
object_class->finalize = ide_workspace_finalize;
object_class->get_property = ide_workspace_get_property;
- object_class->set_property = ide_workspace_set_property;
- widget_class->destroy = ide_workspace_destroy;
- widget_class->delete_event = ide_workspace_delete_event;
- widget_class->grab_focus = ide_workspace_grab_focus;
- widget_class->key_press_event = ide_workspace_key_press_event;
+ widget_class->realize = ide_workspace_realize;
+ widget_class->size_allocate = ide_workspace_size_allocate;
- //window_class->set_fullscreen = ide_workspace_real_set_fullscreen;
+ window_class->close_request = ide_workspace_close_request;
- klass->foreach_page = ide_workspace_real_foreach_page;
+ klass->agree_to_close_async = ide_workspace_agree_to_close_async;
+ klass->agree_to_close_finish = ide_workspace_agree_to_close_finish;
+ klass->can_search = ide_workspace_real_can_search;
klass->context_set = ide_workspace_real_context_set;
- klass->surface_set = ide_workspace_real_surface_set;
+ klass->foreach_page = ide_workspace_real_foreach_page;
+ klass->get_most_recent_frame = ide_workspace_real_get_most_recent_frame;
+ klass->restore_size = ide_workspace_restore_size;
+ klass->save_size = ide_workspace_save_size;
+ klass->get_header_bar = ide_workspace_real_get_header_bar;
/**
* IdeWorkspace:context:
*
* The "context" property is the #IdeContext for the workspace. This is set
* when the workspace joins a workbench.
- *
- * Since: 3.32
*/
properties [PROP_CONTEXT] =
g_param_spec_object ("context",
@@ -573,51 +597,9 @@ ide_workspace_class_init (IdeWorkspaceClass *klass)
IDE_TYPE_CONTEXT,
(G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
- /**
- * IdeWorkspace:visible-surface:
- *
- * The "visible-surface" property contains the currently foremost surface
- * in the workspaces stack of surfaces. Usually, this is the editor surface,
- * but may be other surfaces such as build preferences, profiler, etc.
- *
- * Since: 3.32
- */
- properties [PROP_VISIBLE_SURFACE] =
- g_param_spec_object ("visible-surface",
- "Visible Surface",
- "The currently visible surface",
- IDE_TYPE_SURFACE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
g_object_class_install_properties (object_class, N_PROPS, properties);
- /**
- * IdeWorkspace::surface-set:
- * @self: an #IdeWorkspace
- * @surface: (nullable): an #IdeSurface
- *
- * The "surface-set" signal is emitted when the current surface changes
- * within the workspace.
- *
- * Since: 3.32
- */
- signals [SURFACE_SET] =
- g_signal_new ("surface-set",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeWorkspaceClass, surface_set),
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, IDE_TYPE_SURFACE);
- g_signal_set_va_marshaller (signals [SURFACE_SET],
- G_TYPE_FROM_CLASS (klass),
- g_cclosure_marshal_VOID__OBJECTv);
-
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-workspace.ui");
- gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, event_box);
- gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, overlay);
- gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, surfaces);
- gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, vbox);
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_comma, GDK_CONTROL_MASK, "app.preferences",
NULL);
}
static void
@@ -626,26 +608,35 @@ ide_workspace_init (IdeWorkspace *self)
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
g_autofree gchar *app_id = NULL;
- priv->mru_link.data = self;
-
- gtk_widget_init_template (GTK_WIDGET (self));
+#ifdef DEVELOPMENT_BUILD
+ gtk_widget_add_css_class (GTK_WIDGET (self), "devel");
+#endif
- g_signal_connect_object (priv->surfaces,
- "notify::visible-child",
- G_CALLBACK (ide_workspace_notify_surface_cb),
- self,
- G_CONNECT_SWAPPED);
+ priv->mru_link.data = self;
/* Add org-gnome-Builder style CSS identifier */
app_id = g_strdelimit (g_strdup (ide_get_application_id ()), ".", '-');
- dzl_gtk_widget_add_style_class (GTK_WIDGET (self), app_id);
- dzl_gtk_widget_add_style_class (GTK_WIDGET (self), "workspace");
+ gtk_widget_add_css_class (GTK_WIDGET (self), app_id);
+ gtk_widget_add_css_class (GTK_WIDGET (self), "workspace");
+
+ /* Setup container for children widgetry */
+ priv->box = g_object_new (GTK_TYPE_BOX,
+ "orientation", GTK_ORIENTATION_VERTICAL,
+ NULL);
+ adw_application_window_set_content (ADW_APPLICATION_WINDOW (self),
+ GTK_WIDGET (priv->box));
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->has_statusbar)
+ {
+ priv->statusbar = PANEL_STATUSBAR (panel_statusbar_new ());
+ gtk_box_append (priv->box, GTK_WIDGET (priv->statusbar));
+ }
- /* Add events for motion controller of fullscreen titlebar */
- gtk_widget_add_events (GTK_WIDGET (priv->event_box),
- (GDK_POINTER_MOTION_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK));
+ /* Track focus change to propagate to addins */
+ g_signal_connect (self,
+ "notify::focus-widget",
+ G_CALLBACK (ide_workspace_notify_focus_widget),
+ NULL);
/* Initialize GActions for workspace */
_ide_workspace_init_actions (self);
@@ -668,8 +659,6 @@ _ide_workspace_get_mru_link (IdeWorkspace *self)
* workspace joins an #IdeWorkbench.
*
* Returns: (transfer none) (nullable): an #IdeContext or %NULL
- *
- * Since: 3.32
*/
IdeContext *
ide_workspace_get_context (IdeWorkspace *self)
@@ -707,8 +696,6 @@ _ide_workspace_set_context (IdeWorkspace *self,
* to be cancelled if a window is closed.
*
* Returns: (transfer none): a #GCancellable
- *
- * Since: 3.32
*/
GCancellable *
ide_workspace_get_cancellable (IdeWorkspace *self)
@@ -731,13 +718,11 @@ ide_workspace_get_cancellable (IdeWorkspace *self)
* @user_data: closure data for @callback
*
* Calls @callback for each #IdePage found within the workspace.
- *
- * Since: 3.32
*/
void
-ide_workspace_foreach_page (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data)
+ide_workspace_foreach_page (IdeWorkspace *self,
+ IdePageCallback callback,
+ gpointer user_data)
{
g_return_if_fail (IDE_IS_WORKSPACE (self));
g_return_if_fail (callback != NULL);
@@ -754,338 +739,663 @@ ide_workspace_foreach_page (IdeWorkspace *self,
* Also works around Gtk giving back a GtkStack for the header bar.
*
* Returns: (nullable) (transfer none): an #IdeHeaderBar or %NULL
- *
- * Since: 3.32
*/
IdeHeaderBar *
ide_workspace_get_header_bar (IdeWorkspace *self)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- IdeHeaderBar *ret = NULL;
- GList *children;
-
g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
- children = gtk_container_get_children (GTK_CONTAINER (priv->vbox));
-
- for (const GList *iter = children; iter; iter = iter->next)
- {
- GtkWidget *widget = iter->data;
-
- if (GTK_IS_STACK (widget))
- widget = gtk_stack_get_visible_child (GTK_STACK (widget));
-
- if (IDE_IS_HEADER_BAR (widget))
- {
- ret = IDE_HEADER_BAR (widget);
- break;
- }
- }
-
- g_list_free (children);
-
- return ret;
+ return IDE_WORKSPACE_GET_CLASS (self)->get_header_bar (self);
}
/**
- * ide_workspace_add_surface:
+ * ide_workspace_get_most_recent_page:
* @self: a #IdeWorkspace
*
- * Adds a new #IdeSurface to the workspace.
+ * Gets the most recently focused #IdePage.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): an #IdePage or %NULL
*/
-void
-ide_workspace_add_surface (IdeWorkspace *self,
- IdeSurface *surface)
+IdePage *
+ide_workspace_get_most_recent_page (IdeWorkspace *self)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- g_autofree gchar *title = NULL;
- g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (IDE_IS_SURFACE (surface));
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
- if (DZL_IS_DOCK_ITEM (surface))
- title = dzl_dock_item_get_title (DZL_DOCK_ITEM (surface));
+ if (priv->page_mru.head != NULL)
+ return IDE_PAGE (priv->page_mru.head->data);
- gtk_container_add_with_properties (GTK_CONTAINER (priv->surfaces), GTK_WIDGET (surface),
- "name", gtk_widget_get_name (GTK_WIDGET (surface)),
- "title", title,
- NULL);
+ return NULL;
}
/**
- * ide_workspace_set_visible_surface_name:
+ * ide_workspace_get_most_recent_frame:
* @self: a #IdeWorkspace
- * @visible_surface_name: the name of the #IdeSurface
*
- * Sets the visible surface based on the name of the surface. The name of the
- * surface comes from gtk_widget_get_name(), which should be set when creating
- * the surface using gtk_widget_set_name().
+ * Gets the most recently selected frame.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): an #IdeFrame or %NULL
*/
+IdeFrame *
+ide_workspace_get_most_recent_frame (IdeWorkspace *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ return IDE_WORKSPACE_GET_CLASS (self)->get_most_recent_frame (self);
+}
+
void
-ide_workspace_set_visible_surface_name (IdeWorkspace *self,
- const gchar *visible_surface_name)
+_ide_workspace_add_page_mru (IdeWorkspace *self,
+ GList *mru_link)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (visible_surface_name != NULL);
-
- gtk_stack_set_visible_child_name (priv->surfaces, visible_surface_name);
-}
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (mru_link->prev == NULL);
+ g_return_if_fail (mru_link->next == NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ g_debug ("Adding %s to page MRU",
+ G_OBJECT_TYPE_NAME (mru_link->data));
+
+ g_queue_push_head_link (&priv->page_mru, mru_link);
+}
+
+void
+_ide_workspace_remove_page_mru (IdeWorkspace *self,
+ GList *mru_link)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ IdePage *mru_page;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ mru_page = mru_link->data;
+
+ g_debug ("Removing %s from page MRU",
+ G_OBJECT_TYPE_NAME (mru_page));
+
+ g_queue_unlink (&priv->page_mru, mru_link);
+
+ if ((gpointer)mru_page == priv->current_page_ptr)
+ {
+ g_clear_weak_pointer (&priv->current_page_ptr);
+ ide_extension_set_adapter_foreach (priv->addins,
+ ide_workspace_addin_page_changed_cb,
+ NULL);
+ }
+
+ IDE_EXIT;
+}
+
+void
+_ide_workspace_move_front_page_mru (IdeWorkspace *self,
+ GList *mru_link)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ if (mru_link == priv->page_mru.head)
+ return;
+
+ g_debug ("Moving %s to front of page MRU",
+ G_OBJECT_TYPE_NAME (mru_link->data));
+
+ g_queue_unlink (&priv->page_mru, mru_link);
+ g_queue_push_head_link (&priv->page_mru, mru_link);
+}
/**
- * ide_workspace_get_visible_surface:
- * @self: a #IdeWorkspace
- *
- * Gets the currently visible #IdeSurface, or %NULL
+ * ide_workspace_addin_find_by_module_name:
+ * @workspace: an #IdeWorkspace
+ * @module_name: the name of the addin module
*
- * Returns: (transfer none) (nullable): an #IdeSurface or %NULL
+ * Finds the addin (if any) matching the plugin's @module_name.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): an #IdeWorkspaceAddin or %NULL
*/
-IdeSurface *
-ide_workspace_get_visible_surface (IdeWorkspace *self)
+IdeWorkspaceAddin *
+ide_workspace_addin_find_by_module_name (IdeWorkspace *workspace,
+ const gchar *module_name)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- GtkWidget *child;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (workspace);
+ PeasPluginInfo *plugin_info;
+ PeasExtension *ret = NULL;
+ PeasEngine *engine;
- g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKSPACE (workspace), NULL);
+ g_return_val_if_fail (module_name != NULL, NULL);
- child = gtk_stack_get_visible_child (priv->surfaces);
- if (!IDE_IS_SURFACE (child))
- child = NULL;
+ if (priv->addins == NULL)
+ return NULL;
+
+ engine = peas_engine_get_default ();
- return IDE_SURFACE (child);
+ if ((plugin_info = peas_engine_get_plugin_info (engine, module_name)))
+ ret = ide_extension_set_adapter_get_extension (priv->addins, plugin_info);
+
+ return IDE_WORKSPACE_ADDIN (ret);
}
/**
- * ide_workspace_set_visible_surface:
+ * ide_workspace_add_page:
* @self: a #IdeWorkspace
- * @surface: an #IdeSurface
+ * @page: an #IdePage
+ * @position: the position for the page
*
- * Sets the #IdeWorkspace:visible-surface property which is the currently
- * visible #IdeSurface in the workspace.
+ * Adds @page to @workspace.
*
- * Since: 3.32
+ * In future versions, @position may be updated to reflect the
+ * position in which @page was added.
*/
void
-ide_workspace_set_visible_surface (IdeWorkspace *self,
- IdeSurface *surface)
+ide_workspace_add_page (IdeWorkspace *self,
+ IdePage *page,
+ IdePanelPosition *position)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
-
g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (IDE_IS_SURFACE (surface));
+ g_return_if_fail (IDE_IS_PAGE (page));
+ g_return_if_fail (position != NULL);
- gtk_stack_set_visible_child (priv->surfaces, GTK_WIDGET (surface));
+ if (IDE_WORKSPACE_GET_CLASS (self)->add_page)
+ IDE_WORKSPACE_GET_CLASS (self)->add_page (self, page, position);
+ else
+ g_critical ("%s does not support adding pages",
+ G_OBJECT_TYPE_NAME (self));
}
/**
- * ide_workspace_get_surface_by_name:
+ * ide_workspace_add_pane:
* @self: a #IdeWorkspace
- * @name: the name of the surface
- *
- * Locates an #IdeSurface that has been added to the workspace by the name
- * that was registered for the widget using gtk_widget_set_name().
+ * @pane: an #IdePane
+ * @position: the position for the pane
*
- * Returns: (transfer none) (nullable): an #IdeSurface or %NULL
+ * Adds @pane to @workspace.
*
- * Since: 3.32
+ * In future versions, @position may be updated to reflect the
+ * position in which @pane was added.
*/
-IdeSurface *
-ide_workspace_get_surface_by_name (IdeWorkspace *self,
- const gchar *name)
+void
+ide_workspace_add_pane (IdeWorkspace *self,
+ IdePane *pane,
+ IdePanelPosition *position)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- GtkWidget *child;
-
- g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- child = gtk_stack_get_child_by_name (priv->surfaces, name);
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (IDE_IS_PANE (pane));
+ g_return_if_fail (position != NULL);
- return IDE_IS_SURFACE (child) ? IDE_SURFACE (child) : NULL;
+ if (IDE_WORKSPACE_GET_CLASS (self)->add_pane)
+ IDE_WORKSPACE_GET_CLASS (self)->add_pane (self, pane, position);
+ else
+ g_critical ("%s does not support adding panels",
+ G_OBJECT_TYPE_NAME (self));
}
-static GObject *
-ide_workspace_get_internal_child (GtkBuildable *buildable,
- GtkBuilder *builder,
- const gchar *child_name)
+static void
+ide_workspace_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *object,
+ const char *type)
{
IdeWorkspace *self = (IdeWorkspace *)buildable;
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
- g_assert (GTK_IS_BUILDABLE (buildable));
+ g_assert (IDE_IS_WORKSPACE (self));
g_assert (GTK_IS_BUILDER (builder));
- g_assert (child_name != NULL);
+ g_assert (G_IS_OBJECT (object));
- if (ide_str_equal0 (child_name, "surfaces"))
- return G_OBJECT (priv->surfaces);
+ if (GTK_IS_WIDGET (object))
+ {
+ if (g_strcmp0 (type, "titlebar") == 0)
+ {
+ gtk_box_prepend (priv->box, GTK_WIDGET (object));
+ }
+ else
+ {
+ gtk_box_append (priv->box, GTK_WIDGET (object));
- return NULL;
+ if (priv->statusbar != NULL)
+ gtk_box_reorder_child_after (priv->box, GTK_WIDGET (priv->statusbar), GTK_WIDGET (object));
+ }
+ }
}
-static void
-ide_workspace_add_child (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *object,
- const char *type)
+static GObject *
+ide_workspace_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const char *id)
{
IdeWorkspace *self = (IdeWorkspace *)buildable;
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
g_assert (IDE_IS_WORKSPACE (self));
g_assert (GTK_IS_BUILDER (builder));
+ g_assert (id != NULL);
- if (g_strcmp0 (type, "titlebar") == 0 && GTK_IS_WIDGET (object))
- gtk_box_pack_start (priv->vbox, GTK_WIDGET (object), FALSE, FALSE, 0);
- else
- parent_builder->add_child (buildable, builder, object, type);
+ if (g_strcmp0 (id, "statusbar") == 0)
+ {
+ if (priv->statusbar == NULL)
+ {
+ priv->statusbar = PANEL_STATUSBAR (panel_statusbar_new ());
+ gtk_box_append (priv->box, GTK_WIDGET (priv->statusbar));
+ }
+
+ return G_OBJECT (priv->statusbar);
+ }
+
+ return NULL;
}
static void
buildable_iface_init (GtkBuildableIface *iface)
{
- parent_builder = g_type_interface_peek_parent (iface);
-
- iface->get_internal_child = ide_workspace_get_internal_child;
iface->add_child = ide_workspace_add_child;
+ iface->get_internal_child = ide_workspace_get_internal_child;
}
/**
- * ide_workspace_get_overlay:
+ * ide_workspace_get_statusbar:
* @self: a #IdeWorkspace
*
- * Gets a #GtkOverlay that contains all of the primary contents of the window
- * (everything except the headerbar). This can be used by plugins to draw
- * above the workspace contents.
- *
- * Returns: (transfer none): a #GtkOverlay
+ * Gets the statusbar if any.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): a #PanelStatusbar or %NULL
*/
-GtkOverlay *
-ide_workspace_get_overlay (IdeWorkspace *self)
+PanelStatusbar *
+ide_workspace_get_statusbar (IdeWorkspace *self)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
- return priv->overlay;
+ return priv->statusbar;
+}
+
+static void
+add_to_frame_with_depth (PanelFrame *frame,
+ PanelWidget *widget,
+ guint depth,
+ gboolean depth_set)
+{
+ PanelWidget *previous_page;
+ guint n_pages;
+
+ g_assert (PANEL_IS_FRAME (frame));
+ g_assert (PANEL_IS_WIDGET (widget));
+
+ previous_page = panel_frame_get_visible_child (frame);
+
+ if (!depth_set || depth > G_MAXINT)
+ depth = G_MAXINT;
+
+ SET_PRIORITY (widget, depth);
+
+ n_pages = panel_frame_get_n_pages (frame);
+
+ for (guint i = 0; i < n_pages; i++)
+ {
+ PanelWidget *child = panel_frame_get_page (frame, i);
+
+ if ((int)depth < GET_PRIORITY (child))
+ {
+ panel_frame_add_before (frame, widget, child);
+ goto reset_page;
+ }
+ }
+
+ panel_frame_add (frame, widget);
+
+reset_page:
+ if (previous_page != NULL)
+ panel_frame_set_visible_child (frame, previous_page);
+}
+
+static gboolean
+find_open_frame (IdeGrid *grid,
+ guint *column,
+ guint *row)
+{
+ guint n_columns;
+
+ g_assert (IDE_IS_GRID (grid));
+ g_assert (column != NULL);
+ g_assert (row != NULL);
+
+ n_columns = panel_grid_get_n_columns (PANEL_GRID (grid));
+
+ for (guint c = 0; c < n_columns; c++)
+ {
+ PanelGridColumn *grid_column = panel_grid_get_column (PANEL_GRID (grid), c);
+ guint n_rows = panel_grid_column_get_n_rows (grid_column);
+
+ for (guint r = 0; r < n_rows; r++)
+ {
+ PanelFrame *frame = panel_grid_column_get_row (grid_column, r);
+
+ if (panel_frame_get_empty (frame))
+ {
+ *column = c;
+ *row = r;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+find_most_recent_frame (IdeWorkspace *workspace,
+ IdeGrid *grid,
+ guint *column,
+ guint *row)
+{
+ GtkWidget *grid_column;
+ IdeFrame *frame;
+ guint n_columns;
+
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (IDE_IS_GRID (grid));
+ g_assert (column != NULL);
+ g_assert (row != NULL);
+
+ *column = 0;
+ *row = 0;
+
+ if (!(frame = ide_workspace_get_most_recent_frame (workspace)) ||
+ !(grid_column = gtk_widget_get_ancestor (GTK_WIDGET (frame), PANEL_TYPE_GRID_COLUMN)))
+ return;
+
+ n_columns = panel_grid_get_n_columns (PANEL_GRID (grid));
+
+ for (guint c = 0; c < n_columns; c++)
+ {
+ if (grid_column == (GtkWidget *)panel_grid_get_column (PANEL_GRID (grid), c))
+ {
+ guint n_rows = panel_grid_column_get_n_rows (PANEL_GRID_COLUMN (grid_column));
+
+ for (guint r = 0; r < n_rows; r++)
+ {
+ if ((PanelFrame *)frame == panel_grid_column_get_row (PANEL_GRID_COLUMN (grid_column), r))
+ {
+ *column = c;
+ *row = r;
+ return;
+ }
+ }
+ }
+ }
+}
+
+static gboolean
+dummy_cb (gpointer data)
+{
+ return G_SOURCE_REMOVE;
+}
+
+void
+_ide_workspace_add_widget (IdeWorkspace *self,
+ PanelWidget *widget,
+ IdePanelPosition *position,
+ PanelPaned *dock_start,
+ PanelPaned *dock_end,
+ PanelPaned *dock_bottom,
+ IdeGrid *grid)
+{
+ PanelFrame *frame;
+ gboolean depth_set;
+ guint depth;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (PANEL_IS_WIDGET (widget));
+ g_return_if_fail (position != NULL);
+ g_return_if_fail (!dock_start || PANEL_IS_PANED (dock_start));
+ g_return_if_fail (!dock_end || PANEL_IS_PANED (dock_end));
+ g_return_if_fail (!dock_bottom || PANEL_IS_PANED (dock_bottom));
+ g_return_if_fail (IDE_IS_GRID (grid));
+
+ if (!(frame = _ide_workspace_find_frame (self, position, dock_start, dock_end, dock_bottom, grid)))
+ {
+ /* Extreme failure case, try to be nice and wait until
+ * end of the main loop to destroy
+ */
+ g_idle_add_full (G_PRIORITY_LOW,
+ dummy_cb,
+ g_object_ref_sink (widget),
+ g_object_unref);
+ IDE_EXIT;
+ }
+
+ depth_set = ide_panel_position_get_depth (position, &depth);
+ add_to_frame_with_depth (frame, widget, depth, depth_set);
+
+ IDE_EXIT;
+}
+
+PanelFrame *
+_ide_workspace_find_frame (IdeWorkspace *self,
+ IdePanelPosition *position,
+ PanelPaned *dock_start,
+ PanelPaned *dock_end,
+ PanelPaned *dock_bottom,
+ IdeGrid *grid)
+{
+ PanelDockPosition edge;
+ PanelPaned *paned = NULL;
+ PanelFrame *ret;
+ GtkWidget *parent;
+ guint column = 0;
+ guint row = 0;
+ guint nth = 0;
+
+ IDE_ENTRY;
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+ g_return_val_if_fail (position != NULL, NULL);
+ g_return_val_if_fail (!dock_start || PANEL_IS_PANED (dock_start), NULL);
+ g_return_val_if_fail (!dock_end || PANEL_IS_PANED (dock_end), NULL);
+ g_return_val_if_fail (!dock_bottom || PANEL_IS_PANED (dock_bottom), NULL);
+
+ if (!ide_panel_position_get_edge (position, &edge))
+ edge = PANEL_DOCK_POSITION_CENTER;
+
+ if (edge == PANEL_DOCK_POSITION_CENTER)
+ {
+ gboolean has_column = ide_panel_position_get_column (position, &column);
+ gboolean has_row = ide_panel_position_get_row (position, &row);
+
+ /* If we are adding a page, and no row or column is set, then the next
+ * best thing to do is to try to find an open frame. If we can't do that
+ * then we'll try to find the most recent frame.
+ */
+ if (!has_column && !has_row)
+ {
+ if (!find_open_frame (grid, &column, &row))
+ find_most_recent_frame (self, grid, &column, &row);
+ }
+
+ ret = panel_grid_column_get_row (panel_grid_get_column (PANEL_GRID (grid), column), row);
+
+ IDE_RETURN (ret);
+ }
+
+ switch (edge)
+ {
+ case PANEL_DOCK_POSITION_START:
+ paned = dock_start;
+ ide_panel_position_get_row (position, &nth);
+ break;
+
+ case PANEL_DOCK_POSITION_END:
+ paned = dock_end;
+ ide_panel_position_get_row (position, &nth);
+ break;
+
+ case PANEL_DOCK_POSITION_BOTTOM:
+ paned = dock_bottom;
+ ide_panel_position_get_column (position, &nth);
+ break;
+
+ case PANEL_DOCK_POSITION_TOP:
+ g_warning ("Top panel is not supported");
+ return NULL;
+
+ case PANEL_DOCK_POSITION_CENTER:
+ default:
+ return NULL;
+ }
+
+ while (!(parent = panel_paned_get_nth_child (paned, nth)))
+ {
+ parent = panel_frame_new ();
+
+ if (edge == PANEL_DOCK_POSITION_START ||
+ edge == PANEL_DOCK_POSITION_END)
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (parent), GTK_ORIENTATION_VERTICAL);
+ else
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (parent), GTK_ORIENTATION_HORIZONTAL);
+
+ panel_paned_append (paned, parent);
+ }
+
+ IDE_RETURN (PANEL_FRAME (parent));
}
/**
- * ide_workspace_get_most_recent_page:
- * @self: a #IdeWorkspace
+ * ide_workspace_get_frame_at_position:
+ * @self: an #IdeWorkspace
+ * @position: an #IdePanelPosition
*
- * Gets the most recently focused #IdePage.
- *
- * Returns: (transfer none) (nullable): an #IdePage or %NULL
+ * Attempts to locate the #PanelFrame at a given position.
*
- * Since: 3.32
+ * Returns: (transfer none) (nullable): a #PaneFrame or %NULL
*/
-IdePage *
-ide_workspace_get_most_recent_page (IdeWorkspace *self)
+PanelFrame *
+ide_workspace_get_frame_at_position (IdeWorkspace *self,
+ IdePanelPosition *position)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
-
g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+ g_return_val_if_fail (position != NULL, NULL);
- if (priv->page_mru.head != NULL)
- return IDE_PAGE (priv->page_mru.head->data);
+ if (IDE_WORKSPACE_GET_CLASS (self)->get_frame_at_position)
+ return IDE_WORKSPACE_GET_CLASS (self)->get_frame_at_position (self, position);
return NULL;
}
+gboolean
+_ide_workspace_can_search (IdeWorkspace *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), FALSE);
+
+ return IDE_WORKSPACE_GET_CLASS (self)->can_search (self);
+}
+
void
-_ide_workspace_add_page_mru (IdeWorkspace *self,
- GList *mru_link)
+_ide_workspace_begin_global_search (IdeWorkspace *self)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (mru_link != NULL);
- g_return_if_fail (mru_link->prev == NULL);
- g_return_if_fail (mru_link->next == NULL);
- g_return_if_fail (IDE_IS_PAGE (mru_link->data));
- g_debug ("Adding %s to page MRU",
- G_OBJECT_TYPE_NAME (mru_link->data));
+ if (priv->search_popover == NULL)
+ {
+ IdeWorkbench *workbench = ide_workspace_get_workbench (self);
+ IdeSearchEngine *search_engine = ide_workbench_get_search_engine (workbench);
- g_queue_push_head_link (&priv->page_mru, mru_link);
+ priv->search_popover = IDE_SEARCH_POPOVER (ide_search_popover_new (search_engine));
+ gtk_widget_set_parent (GTK_WIDGET (priv->search_popover), GTK_WIDGET (self));
+
+ /* Popovers don't appear (as of GTK 4.7) to capture/bubble from the GtkRoot
+ * when running controllers. So we need to manually attach them for the popovers
+ * that are important enough to care about.
+ */
+ ide_workspace_attach_shortcuts (self, GTK_WIDGET (priv->search_popover));
+ }
+
+ if (!gtk_widget_get_visible (GTK_WIDGET (priv->search_popover)))
+ gtk_popover_popup (GTK_POPOVER (priv->search_popover));
}
void
-_ide_workspace_remove_page_mru (IdeWorkspace *self,
- GList *mru_link)
+ide_workspace_add_overlay (IdeWorkspace *self,
+ GtkWidget *overlay)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (GTK_IS_WIDGET (overlay));
+ g_return_if_fail (gtk_widget_get_parent (overlay) == NULL);
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->add_overlay == NULL)
+ g_critical ("Attempt to add overlay of type %s to workspace of type %s which does not support overlays",
+ G_OBJECT_TYPE_NAME (overlay), G_OBJECT_TYPE_NAME (self));
+ else
+ IDE_WORKSPACE_GET_CLASS (self)->add_overlay (self, overlay);
+}
+void
+ide_workspace_remove_overlay (IdeWorkspace *self,
+ GtkWidget *overlay)
+{
g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (mru_link != NULL);
- g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+ g_return_if_fail (GTK_IS_WIDGET (overlay));
- g_debug ("Removing %s from page MRU",
- G_OBJECT_TYPE_NAME (mru_link->data));
+ if (IDE_WORKSPACE_GET_CLASS (self)->remove_overlay == NULL)
+ g_critical ("Attempt to remove overlay of type %s to workspace of type %s which does not support
overlays",
+ G_OBJECT_TYPE_NAME (overlay), G_OBJECT_TYPE_NAME (self));
+ else
+ IDE_WORKSPACE_GET_CLASS (self)->remove_overlay (self, overlay);
+}
- g_queue_unlink (&priv->page_mru, mru_link);
+static gboolean
+shortcut_phase_filter (gpointer item,
+ gpointer user_data)
+{
+ return ide_shortcut_is_phase (item, user_data);
}
void
-_ide_workspace_move_front_page_mru (IdeWorkspace *self,
- GList *mru_link)
+_ide_workspace_set_shortcut_model (IdeWorkspace *self,
+ GListModel *model)
{
IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ static GtkCustomFilter *bubble_filter;
+ static GtkCustomFilter *capture_filter;
g_return_if_fail (IDE_IS_WORKSPACE (self));
- g_return_if_fail (mru_link != NULL);
- g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+ g_return_if_fail (G_IS_LIST_MODEL (model));
- if (mru_link == priv->page_mru.head)
- return;
+ if (bubble_filter == NULL)
+ bubble_filter = gtk_custom_filter_new (shortcut_phase_filter, GINT_TO_POINTER (GTK_PHASE_BUBBLE), NULL);
- g_debug ("Moving %s to front of page MRU",
- G_OBJECT_TYPE_NAME (mru_link->data));
+ if (capture_filter == NULL)
+ capture_filter = gtk_custom_filter_new (shortcut_phase_filter, GINT_TO_POINTER (GTK_PHASE_CAPTURE),
NULL);
- g_queue_unlink (&priv->page_mru, mru_link);
- g_queue_push_head_link (&priv->page_mru, mru_link);
+ priv->shortcut_model_capture = gtk_filter_list_model_new (g_object_ref (model),
+ g_object_ref (GTK_FILTER (capture_filter)));
+ priv->shortcut_model_bubble = gtk_filter_list_model_new (g_object_ref (model),
+ g_object_ref (GTK_FILTER (bubble_filter)));
+
+ ide_workspace_attach_shortcuts (self, GTK_WIDGET (self));
}
-/**
- * ide_workspace_addin_find_by_module_name:
- * @workspace: an #IdeWorkspace
- * @module_name: the name of the addin module
- *
- * Finds the addin (if any) matching the plugin's @module_name.
- *
- * Returns: (transfer none) (nullable): an #IdeWorkspaceAddin or %NULL
- *
- * Since: 3.40
- */
-IdeWorkspaceAddin *
-ide_workspace_addin_find_by_module_name (IdeWorkspace *workspace,
- const gchar *module_name)
+void
+ide_workspace_add_grid_column (IdeWorkspace *self,
+ guint position)
{
- IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (workspace);
- PeasPluginInfo *plugin_info;
- PeasExtension *ret = NULL;
- PeasEngine *engine;
-
- g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
- g_return_val_if_fail (IDE_IS_WORKSPACE (workspace), NULL);
- g_return_val_if_fail (module_name != NULL, NULL);
-
- if (priv->addins == NULL)
- return NULL;
-
- engine = peas_engine_get_default ();
-
- if ((plugin_info = peas_engine_get_plugin_info (engine, module_name)))
- ret = ide_extension_set_adapter_get_extension (priv->addins, plugin_info);
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (IDE_WORKSPACE_GET_CLASS (self)->add_grid_column);
- return IDE_WORKSPACE_ADDIN (ret);
+ IDE_WORKSPACE_GET_CLASS (self)->add_grid_column (self, position);
}
diff --git a/src/libide/gui/ide-workspace.h b/src/libide/gui/ide-workspace.h
index e9a28ac7a..8c27a2d4a 100644
--- a/src/libide/gui/ide-workspace.h
+++ b/src/libide/gui/ide-workspace.h
@@ -24,74 +24,112 @@
# error "Only <libide-gui.h> can be included directly."
#endif
-#include <dazzle.h>
-#include <handy.h>
+#include <adwaita.h>
+
#include <libide-core.h>
#include <libide-projects.h>
+#include "ide-frame.h"
#include "ide-header-bar.h"
#include "ide-page.h"
-#include "ide-surface.h"
+#include "ide-pane.h"
+#include "ide-panel-position.h"
G_BEGIN_DECLS
#define IDE_TYPE_WORKSPACE (ide_workspace_get_type())
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeWorkspace, ide_workspace, IDE, WORKSPACE, HdyApplicationWindow)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdeWorkspace, ide_workspace, IDE, WORKSPACE, AdwApplicationWindow)
+
+typedef void (*IdeWorkspaceCallback) (IdeWorkspace *workspace,
+ gpointer user_data);
struct _IdeWorkspaceClass
{
- HdyApplicationWindowClass parent_class;
+ AdwApplicationWindowClass parent_class;
const gchar *kind;
- void (*context_set) (IdeWorkspace *self,
- IdeContext *context);
- void (*foreach_page) (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data);
- void (*surface_set) (IdeWorkspace *self,
- IdeSurface *surface);
+ guint has_statusbar : 1;
+ guint _unused_flags : 31;
- /*< private >*/
- gpointer _reserved[32];
+ void (*context_set) (IdeWorkspace *self,
+ IdeContext *context);
+ void (*foreach_page) (IdeWorkspace *self,
+ IdePageCallback callback,
+ gpointer user_data);
+ IdeHeaderBar *(*get_header_bar) (IdeWorkspace *self);
+ IdePage *(*get_most_recent_page) (IdeWorkspace *self);
+ IdeFrame *(*get_most_recent_frame) (IdeWorkspace *self);
+ void (*agree_to_close_async) (IdeWorkspace *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*agree_to_close_finish) (IdeWorkspace *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*add_pane) (IdeWorkspace *self,
+ IdePane *pane,
+ IdePanelPosition *position);
+ void (*add_page) (IdeWorkspace *self,
+ IdePage *page,
+ IdePanelPosition *position);
+ void (*add_grid_column) (IdeWorkspace *self,
+ guint column);
+ void (*add_overlay) (IdeWorkspace *self,
+ GtkWidget *overlay);
+ void (*remove_overlay) (IdeWorkspace *self,
+ GtkWidget *overlay);
+ PanelFrame *(*get_frame_at_position) (IdeWorkspace *self,
+ IdePanelPosition *position);
+ void (*restore_size) (IdeWorkspace *self,
+ int width,
+ int height);
+ gboolean (*save_size) (IdeWorkspace *self,
+ int *width,
+ int *height);
+ gboolean (*can_search) (IdeWorkspace *self);
};
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
const gchar *kind);
-IDE_AVAILABLE_IN_3_32
-IdeHeaderBar *ide_workspace_get_header_bar (IdeWorkspace *self);
-IDE_AVAILABLE_IN_3_32
-IdeContext *ide_workspace_get_context (IdeWorkspace *self);
-IDE_AVAILABLE_IN_3_32
-GCancellable *ide_workspace_get_cancellable (IdeWorkspace *self);
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_foreach_page (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_foreach_surface (IdeWorkspace *self,
- GtkCallback callback,
- gpointer user_data);
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_add_surface (IdeWorkspace *self,
- IdeSurface *surface);
-IDE_AVAILABLE_IN_3_32
-IdeSurface *ide_workspace_get_surface_by_name (IdeWorkspace *self,
- const gchar *name);
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_set_visible_surface_name (IdeWorkspace *self,
- const gchar *visible_surface_name);
-IDE_AVAILABLE_IN_3_32
-IdeSurface *ide_workspace_get_visible_surface (IdeWorkspace *self);
-IDE_AVAILABLE_IN_3_32
-void ide_workspace_set_visible_surface (IdeWorkspace *self,
- IdeSurface *surface);
-IDE_AVAILABLE_IN_3_32
-GtkOverlay *ide_workspace_get_overlay (IdeWorkspace *self);
-IDE_AVAILABLE_IN_3_32
-IdePage *ide_workspace_get_most_recent_page (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+IdeHeaderBar *ide_workspace_get_header_bar (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+IdeContext *ide_workspace_get_context (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+GCancellable *ide_workspace_get_cancellable (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_foreach_page (IdeWorkspace *self,
+ IdePageCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_ALL
+IdePage *ide_workspace_get_most_recent_page (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+IdeFrame *ide_workspace_get_most_recent_frame (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+PanelFrame *ide_workspace_get_frame_at_position (IdeWorkspace *self,
+ IdePanelPosition *position);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_add_pane (IdeWorkspace *self,
+ IdePane *pane,
+ IdePanelPosition *position);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_add_page (IdeWorkspace *self,
+ IdePage *page,
+ IdePanelPosition *position);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_add_grid_column (IdeWorkspace *self,
+ guint position);
+IDE_AVAILABLE_IN_ALL
+PanelStatusbar *ide_workspace_get_statusbar (IdeWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_add_overlay (IdeWorkspace *self,
+ GtkWidget *widget);
+IDE_AVAILABLE_IN_ALL
+void ide_workspace_remove_overlay (IdeWorkspace *self,
+ GtkWidget *widget);
G_END_DECLS
diff --git a/src/libide/gui/libide-gui.gresource.xml b/src/libide/gui/libide-gui.gresource.xml
index 2c7fcb2b9..faa3a83bf 100644
--- a/src/libide/gui/libide-gui.gresource.xml
+++ b/src/libide/gui/libide-gui.gresource.xml
@@ -10,6 +10,7 @@
<file>images/style-preview-dark.png</file>
<file>images/style-preview-default.png</file>
<file>images/style-preview-light.png</file>
+ <file>style.css</file>
</gresource>
<gresource prefix="/org/gnome/libide-gui/ui">
<file preprocess="xml-stripblanks">ide-environment-editor-row.ui</file>
@@ -19,9 +20,11 @@
<file preprocess="xml-stripblanks">ide-notification-view.ui</file>
<file preprocess="xml-stripblanks">ide-notifications-button.ui</file>
<file preprocess="xml-stripblanks">ide-omni-bar.ui</file>
+ <file preprocess="xml-stripblanks">ide-page.ui</file>
<file preprocess="xml-stripblanks">ide-preferences-window.ui</file>
<file preprocess="xml-stripblanks">ide-primary-workspace.ui</file>
<file preprocess="xml-stripblanks">ide-run-button.ui</file>
+ <file preprocess="xml-stripblanks">ide-search-popover.ui</file>
<file preprocess="xml-stripblanks">ide-style-variant-preview.ui</file>
</gresource>
</gresources>
diff --git a/src/libide/gui/meson.build b/src/libide/gui/meson.build
index 6364db022..ed9ee4f76 100644
--- a/src/libide/gui/meson.build
+++ b/src/libide/gui/meson.build
@@ -1,3 +1,4 @@
+libide_gui_header_dir = join_paths(libide_header_dir, 'gui')
libide_gui_header_subdir = join_paths(libide_header_subdir, 'gui')
libide_include_directories += include_directories('.')
@@ -23,9 +24,13 @@ libide_gui_public_headers = [
'ide-omni-bar.h',
'ide-page.h',
'ide-pane.h',
+ 'ide-panel-position.h',
'ide-preferences-addin.h',
+ 'ide-preferences-choice-row.h',
'ide-preferences-window.h',
'ide-primary-workspace.h',
+ 'ide-run-button.h',
+ 'ide-search-popover.h',
'ide-session-addin.h',
'ide-shortcut-provider.h',
'ide-workbench.h',
@@ -47,8 +52,11 @@ libide_gui_private_headers = [
'ide-notification-list-box-row-private.h',
'ide-notification-stack-private.h',
'ide-notification-view-private.h',
+ 'ide-page-private.h',
'ide-preferences-builtin-private.h',
+ 'ide-primary-workspace-private.h',
'ide-recoloring-private.h',
+ 'ide-search-popover-private.h',
'ide-session-private.h',
'ide-shortcut-bundle-private.h',
'ide-shortcut-manager-private.h',
@@ -59,6 +67,7 @@ libide_gui_private_sources = [
'ide-application-actions.c',
'ide-application-color.c',
'ide-application-plugins.c',
+ 'ide-application-settings.c',
'ide-environment-editor-row.c',
'ide-notification-list-box-row.c',
'ide-notification-stack.c',
@@ -66,6 +75,7 @@ libide_gui_private_sources = [
'ide-preferences-builtin.c',
'ide-primary-workspace-actions.c',
'ide-recoloring.c',
+ 'ide-search-popover.c',
'ide-session.c',
'ide-shortcut-bundle.c',
'ide-shortcut-manager.c',
@@ -91,9 +101,12 @@ libide_gui_public_sources = [
'ide-omni-bar.c',
'ide-page.c',
'ide-pane.c',
+ 'ide-panel-position.c',
'ide-primary-workspace.c',
'ide-preferences-addin.c',
+ 'ide-preferences-choice-row.c',
'ide-preferences-window.c',
+ 'ide-run-button.c',
'ide-session-addin.c',
'ide-shortcut-provider.c',
'ide-workbench.c',
diff --git a/src/libide/gui/style.css b/src/libide/gui/style.css
new file mode 100644
index 000000000..0105575d8
--- /dev/null
+++ b/src/libide/gui/style.css
@@ -0,0 +1,98 @@
+/* Preferences */
+window.preferences list.boxed-list.style-variant button {
+ padding: 0;
+ margin: 0;
+ border: 3px solid transparent;
+ border-radius: 9px;
+ background: transparent;
+}
+window.preferences list.boxed-list.style-variant button:checked {
+ border-color: @theme_selected_bg_color;
+}
+.checkimage radio,
+.checkimage check {
+ background: none;
+ outline: none;
+ box-shadow: none;
+ border: none;
+ color: @window_fg_color;
+ -gtk-icon-source: none;
+ -gtk-icon-size: 18px;
+}
+.checkimage radio:checked,
+.checkimage check:checked {
+ -gtk-icon-source: -gtk-icontheme('object-select-symbolic');
+}
+stylevariantpreview widget.wallpaper {
+ border-radius: 6px;
+ box-shadow: 0 0 9px 1px rgba(0,0,0,.2);
+}
+stylevariantpreview widget.window {
+ box-shadow: 0 0 9px 2px rgba(0,0,0,.25);
+ border-radius: 7px;
+ border: 1px solid alpha(white, .075);
+}
+stylevariantpreview widget.window.dark {
+ background: #242424;
+}
+stylevariantpreview widget.window.light {
+ background: #fafafa;
+}
+stylevariantpreview widget.header {
+ border-radius: 7px 7px 0 0;
+ border-bottom: 1px solid alpha(white, 0.075);
+}
+stylevariantpreview widget.header.dark {
+ background: #484848;
+}
+stylevariantpreview widget.header.light {
+ background: #cccccc;
+}
+
+window popover.global-search contents {
+ padding: 0;
+ margin: 0;
+}
+window popover.global-search .navigation-sidebar {
+ border-right: 1px solid @borders;
+}
+
+notificationstack button {
+ margin: 0;
+ padding: 0;
+ background: none;
+}
+
+scrolledwindow.shadow-when-scroll undershoot.top {
+ box-shadow: inset 0px 5px 14px -14px rgba(0,0,0,.9);
+}
+
+button.pill.small {
+ font-size: .83333em;
+ border-radius: 99px;
+ margin: 0;
+ padding: 1px 12px;
+}
+
+/* Styling for search bars */
+.searchbar {
+ background-color: @popover_bg_color;
+ background-clip: padding-box;
+ color: @popover_fg_color;
+ padding: 6px;
+ border-style: solid;
+ border-color: alpha(black, .14);
+ border-left-width: 1px;
+ border-right-width: 1px;
+ border-bottom-width: 1px;
+ border-radius: 0 0 12px 12px;
+ margin: 0 10px 20px 10px;
+ box-shadow: 0 1px 5px 1px alpha(black, .09),
+ 0 2px 14px 3px alpha(black, .05);
+}
+.searchbar button.flat.circular {
+ min-width: 24px;
+ min-height: 24px;
+ margin: 0px;
+ padding: 3px;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]