[gnome-builder] libide-gui: port IdeApplication to GTK 4



commit 71608e8d2e423f2df3f0132b5fa38cbd570422f5
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 21:45:36 2022 -0700

    libide-gui: port IdeApplication to GTK 4
    
    This bridges in the support for recoloring, fixes plugin loading, handles
    a number of actions that needed to be redone, improves open support, and
    cleans up availability macros.

 src/libide/gui/ide-application-color.c        |  83 ++-
 src/libide/gui/ide-application-command-line.c |   6 -
 src/libide/gui/ide-application-open.c         |   2 -
 src/libide/gui/ide-application-plugins.c      |  78 +--
 src/libide/gui/ide-application-private.h      |  56 +-
 src/libide/gui/ide-application-settings.c     | 209 ++++++++
 src/libide/gui/ide-application.c              | 715 ++++++++++++++++----------
 src/libide/gui/ide-application.h              |  68 ++-
 8 files changed, 826 insertions(+), 391 deletions(-)
---
diff --git a/src/libide/gui/ide-application-color.c b/src/libide/gui/ide-application-color.c
index ff5e8c1d5..de2e8a487 100644
--- a/src/libide/gui/ide-application-color.c
+++ b/src/libide/gui/ide-application-color.c
@@ -23,10 +23,10 @@
 #include "config.h"
 
 #include <gtksourceview/gtksource.h>
-#include <handy.h>
 
 #include "ide-application.h"
 #include "ide-application-private.h"
+#include "ide-recoloring-private.h"
 
 static void
 add_style_name (GPtrArray   *ar,
@@ -93,7 +93,7 @@ static void
 _ide_application_update_color (IdeApplication *self)
 {
   static gboolean ignore_reentrant = FALSE;
-  HdyStyleManager *manager;
+  AdwStyleManager *manager;
   g_autofree char *style_variant = NULL;
 
   g_assert (IDE_IS_APPLICATION (self));
@@ -109,14 +109,16 @@ _ide_application_update_color (IdeApplication *self)
   g_assert (G_IS_SETTINGS (self->settings));
 
   style_variant = g_settings_get_string (self->settings, "style-variant");
-  manager = hdy_style_manager_get_default ();
+  manager = adw_style_manager_get_default ();
 
-  if (!g_strcmp0 (style_variant, "follow"))
-    hdy_style_manager_set_color_scheme (manager, HDY_COLOR_SCHEME_PREFER_LIGHT);
-  else if (!g_strcmp0 (style_variant, "dark"))
-    hdy_style_manager_set_color_scheme (manager, HDY_COLOR_SCHEME_FORCE_DARK);
+  g_debug ("Style variant changed to %s", style_variant);
+
+  if (g_strcmp0 (style_variant, "follow") == 0)
+    adw_style_manager_set_color_scheme (manager, ADW_COLOR_SCHEME_PREFER_LIGHT);
+  else if (g_strcmp0 (style_variant, "dark") == 0)
+    adw_style_manager_set_color_scheme (manager, ADW_COLOR_SCHEME_FORCE_DARK);
   else
-    hdy_style_manager_set_color_scheme (manager, HDY_COLOR_SCHEME_FORCE_LIGHT);
+    adw_style_manager_set_color_scheme (manager, ADW_COLOR_SCHEME_FORCE_LIGHT);
 
   ignore_reentrant = FALSE;
 }
@@ -124,12 +126,14 @@ _ide_application_update_color (IdeApplication *self)
 static void
 _ide_application_update_style_scheme (IdeApplication *self)
 {
-  HdyStyleManager *manager;
+  AdwStyleManager *manager;
   g_autoptr(GSettings) editor_settings = NULL;
   g_autofree gchar *old_name = NULL;
   g_autofree gchar *new_name = NULL;
 
-  manager = hdy_style_manager_get_default ();
+  g_assert (IDE_IS_APPLICATION (self));
+
+  manager = adw_style_manager_get_default ();
 
   /*
    * Now that we have our color up to date, we need to possibly update the
@@ -143,32 +147,87 @@ _ide_application_update_style_scheme (IdeApplication *self)
 
   editor_settings = g_settings_new ("org.gnome.builder.editor");
   old_name = g_settings_get_string (editor_settings, "style-scheme-name");
-  new_name = find_similar_style_scheme (old_name, hdy_style_manager_get_dark (manager));
+  new_name = find_similar_style_scheme (old_name, adw_style_manager_get_dark (manager));
 
   if (new_name != NULL)
     g_settings_set_string (editor_settings, "style-scheme-name", new_name);
 }
 
+static void
+ide_application_color_style_scheme_changed_cb (IdeApplication *self,
+                                               const char     *key,
+                                               GSettings      *editor_settings)
+{
+  GtkSourceStyleSchemeManager *manager;
+  GtkSourceStyleScheme *scheme;
+  g_autofree char *style_scheme_name = NULL;
+  g_autofree char *css = NULL;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (g_strcmp0 (key, "style-scheme-name") == 0);
+  g_assert (G_IS_SETTINGS (editor_settings));
+
+  style_scheme_name = g_settings_get_string (editor_settings, key);
+  g_debug ("Style scheme changed to %s", style_scheme_name);
+
+  manager = gtk_source_style_scheme_manager_get_default ();
+  scheme = gtk_source_style_scheme_manager_get_scheme (manager, style_scheme_name);
+
+  if (scheme == NULL)
+    return;
+
+  css = _ide_recoloring_generate_css (scheme);
+  gtk_css_provider_load_from_data (self->recoloring, css ? css : "", -1);
+}
+
 void
 _ide_application_init_color (IdeApplication *self)
 {
+  g_autofree char *style_scheme_name = NULL;
+
   g_return_if_fail (IDE_IS_APPLICATION (self));
   g_return_if_fail (G_IS_SETTINGS (self->settings));
 
   if (g_getenv ("GTK_THEME") == NULL)
     {
+      g_autofree char *style_variant = NULL;
+
+      /* We must read "style-variant" to get changed notifications */
+      style_variant = g_settings_get_string (self->settings, "style-variant");
+      g_debug ("Initialized with style-variant %s", style_variant);
+
       g_signal_connect_object (self->settings,
                                "changed::style-variant",
                                G_CALLBACK (_ide_application_update_color),
                                self,
                                G_CONNECT_SWAPPED);
-      g_signal_connect_object (hdy_style_manager_get_default (),
+      g_signal_connect_object (adw_style_manager_get_default (),
                                "notify::dark",
                                G_CALLBACK (_ide_application_update_style_scheme),
                                self,
                                G_CONNECT_SWAPPED);
     }
 
+  style_scheme_name = g_settings_get_string (self->editor_settings, "style-scheme-name");
+  g_debug ("Initialized with style scheme %s", style_scheme_name);
+  g_signal_connect_object (self->editor_settings,
+                           "changed::style-scheme-name",
+                           G_CALLBACK (ide_application_color_style_scheme_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+  gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                              GTK_STYLE_PROVIDER (self->recoloring),
+                                              GTK_STYLE_PROVIDER_PRIORITY_THEME+1);
+
   _ide_application_update_color (self);
   _ide_application_update_style_scheme (self);
+  ide_application_color_style_scheme_changed_cb (self, "style-scheme-name", self->editor_settings);
+}
+
+gboolean
+ide_application_get_dark (IdeApplication *self)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), FALSE);
+
+  return adw_style_manager_get_dark (adw_style_manager_get_default ());
 }
diff --git a/src/libide/gui/ide-application-command-line.c b/src/libide/gui/ide-application-command-line.c
index 9899137fd..155762904 100644
--- a/src/libide/gui/ide-application-command-line.c
+++ b/src/libide/gui/ide-application-command-line.c
@@ -54,8 +54,6 @@ add_option_entries_foreach_cb (PeasExtensionSet *set,
  *
  * Inflate all early stage plugins asking them to let us know about what
  * command-line options they support.
- *
- * Since: 3.32
  */
 void
 _ide_application_add_option_entries (IdeApplication *self)
@@ -130,8 +128,6 @@ ide_application_command_line_open_project_cb (GObject      *object,
  * plugins who have elected to handle command-line options. Some
  * of them, like the greeter, may create an initial workbench
  * and workspace window in response.
- *
- * Since: 3.32
  */
 void
 _ide_application_command_line (IdeApplication          *self,
@@ -230,8 +226,6 @@ _ide_application_command_line (IdeApplication          *self,
  *
  * Returns: (transfer full) (nullable) (array zero-terminated=1): an
  *   array of strings or %NULL
- *
- * Since: 3.32
  */
 gchar **
 ide_application_get_argv (IdeApplication          *self,
diff --git a/src/libide/gui/ide-application-open.c b/src/libide/gui/ide-application-open.c
index 721bb0664..4624089b5 100644
--- a/src/libide/gui/ide-application-open.c
+++ b/src/libide/gui/ide-application-open.c
@@ -147,8 +147,6 @@ ide_application_open_project_async (IdeApplication      *self,
  *
  * Returns: (transfer full): an #IdeWorkbench or %NULL on failure and @error
  *   is set.
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_application_open_project_finish (IdeApplication  *self,
diff --git a/src/libide/gui/ide-application-plugins.c b/src/libide/gui/ide-application-plugins.c
index ff27b1aa7..7c066d584 100644
--- a/src/libide/gui/ide-application-plugins.c
+++ b/src/libide/gui/ide-application-plugins.c
@@ -106,10 +106,6 @@ ide_application_can_load_plugin (IdeApplication *self,
   module_dir = peas_plugin_info_get_module_dir (plugin_info);
   module_name = peas_plugin_info_get_module_name (plugin_info);
 
-  /* Short-circuit for single-plugin mode */
-  if (self->plugin != NULL)
-    return ide_str_equal0 (module_name, self->plugin);
-
   if (g_hash_table_contains (circular, module_name))
     {
       g_warning ("Circular dependency found in module %s", module_name);
@@ -123,14 +119,6 @@ ide_application_can_load_plugin (IdeApplication *self,
   if (!g_settings_get_boolean (settings, "enabled"))
     return FALSE;
 
-#if 0
-  if (self->mode == IDE_APPLICATION_MODE_WORKER)
-    {
-      if (self->worker != plugin_info)
-        return FALSE;
-    }
-#endif
-
   /*
    * If the plugin is not bundled within the Builder executable, then we
    * require that an X-Builder-ABI=major.minor style extended data be
@@ -216,7 +204,7 @@ ide_application_load_plugin_resources (IdeApplication *self,
       g_resources_register (resource);
 
       resource_path = g_strdup_printf ("resource:///plugins/%s", module_name);
-      dzl_application_add_resources (DZL_APPLICATION (self), resource_path);
+      _ide_application_add_resources (self, resource_path);
     }
 }
 
@@ -238,9 +226,9 @@ _ide_application_load_plugin (IdeApplication *self,
 }
 
 static void
-ide_application_plugins_load_plugin_cb (IdeApplication *self,
-                                        PeasPluginInfo *plugin_info,
-                                        PeasEngine     *engine)
+ide_application_plugins_load_plugin_after_cb (IdeApplication *self,
+                                              PeasPluginInfo *plugin_info,
+                                              PeasEngine     *engine)
 {
   const gchar *data_dir;
   const gchar *module_dir;
@@ -273,19 +261,29 @@ ide_application_plugins_load_plugin_cb (IdeApplication *self,
    */
   if (g_str_has_prefix (data_dir, "resource://") ||
       !peas_plugin_info_is_builtin (plugin_info))
-    dzl_application_add_resources (DZL_APPLICATION (self), data_dir);
+    _ide_application_add_resources (self, data_dir);
 }
 
 static void
-ide_application_plugins_unload_plugin_cb (IdeApplication *self,
-                                          PeasPluginInfo *plugin_info,
-                                          PeasEngine     *engine)
+ide_application_plugins_unload_plugin_after_cb (IdeApplication *self,
+                                                PeasPluginInfo *plugin_info,
+                                                PeasEngine     *engine)
 {
+  const gchar *module_dir;
+  const gchar *module_name;
+
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_APPLICATION (self));
   g_assert (plugin_info != NULL);
   g_assert (PEAS_IS_ENGINE (engine));
 
+  module_dir = peas_plugin_info_get_module_dir (plugin_info);
+  module_name = peas_plugin_info_get_module_name (plugin_info);
+
+  _ide_application_remove_resources (self, module_dir);
+
+  g_debug ("Unloaded plugin \"%s\" with module-dir \"%s\"",
+           module_name, module_dir);
 }
 
 /**
@@ -295,8 +293,6 @@ ide_application_plugins_unload_plugin_cb (IdeApplication *self,
  * early-stage initialization. Usually, that is any plugin that has a
  * command-line handler and uses "X-At-Startup=true" in their .plugin
  * manifest.
- *
- * Since: 3.32
  */
 void
 _ide_application_load_plugins_for_startup (IdeApplication *self)
@@ -308,15 +304,15 @@ _ide_application_load_plugins_for_startup (IdeApplication *self)
 
   g_signal_connect_object (engine,
                            "load-plugin",
-                           G_CALLBACK (ide_application_plugins_load_plugin_cb),
+                           G_CALLBACK (ide_application_plugins_load_plugin_after_cb),
                            self,
-                           G_CONNECT_SWAPPED);
+                           G_CONNECT_SWAPPED | G_CONNECT_AFTER);
 
   g_signal_connect_object (engine,
                            "unload-plugin",
-                           G_CALLBACK (ide_application_plugins_unload_plugin_cb),
+                           G_CALLBACK (ide_application_plugins_unload_plugin_after_cb),
                            self,
-                           G_CONNECT_SWAPPED);
+                           G_CONNECT_SWAPPED | G_CONNECT_AFTER);
 
   /* Ensure that our embedded plugins are allowed early access to
    * start loading (before we ever look at anything on disk). This
@@ -355,14 +351,11 @@ _ide_application_load_plugins_for_startup (IdeApplication *self)
  *
  * This function loads any additional plugins that have not yet been
  * loaded during early startup.
- *
- * Since: 3.32
  */
 void
 _ide_application_load_plugins (IdeApplication *self)
 {
   g_autofree gchar *user_plugins_dir = NULL;
-  g_autoptr(GError) error = NULL;
   const GList *plugins;
   PeasEngine *engine;
 
@@ -406,28 +399,7 @@ _ide_application_load_plugins (IdeApplication *self)
                                        NULL);
   peas_engine_prepend_search_path (engine, user_plugins_dir, NULL);
 
-  /* Ensure that we have all our required GObject Introspection packages
-   * loaded so that plugins don't need to require_version() as that is
-   * tedious and annoying to keep up to date.
-   *
-   * If we can't load any of our dependent packages, then fail to load
-   * python3 plugins altogether to avoid loading anything improper into
-   * the process space.
-   */
-  g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0");
-  if (!g_irepository_require (NULL, "GtkSource", "4", 0, &error) ||
-      !g_irepository_require (NULL, "Gio", "2.0", 0, &error) ||
-      !g_irepository_require (NULL, "GLib", "2.0", 0, &error) ||
-      !g_irepository_require (NULL, "Gtk", "3.0", 0, &error) ||
-      !g_irepository_require (NULL, "Dazzle", "1.0", 0, &error) ||
-      !g_irepository_require (NULL, "Jsonrpc", "1.0", 0, &error) ||
-      !g_irepository_require (NULL, "Template", "1.0", 0, &error) ||
-#ifdef HAVE_WEBKIT
-      !g_irepository_require (NULL, "WebKit2", "4.0", 0, &error) ||
-#endif
-      !g_irepository_require (NULL, "Ide", PACKAGE_ABI_S, 0, &error))
-    g_critical ("Cannot enable Python 3 plugins: %s", error->message);
-  else
+  if (self->loaded_typelibs)
     peas_engine_enable_loader (engine, "python3");
 
   peas_engine_rescan_plugins (engine);
@@ -484,8 +456,6 @@ ide_application_addin_removed_cb (PeasExtensionSet *set,
  * @self: a #IdeApplication
  *
  * Loads the #IdeApplicationAddin's for this application.
- *
- * Since: 3.32
  */
 void
 _ide_application_load_addins (IdeApplication *self)
@@ -518,8 +488,6 @@ _ide_application_load_addins (IdeApplication *self)
  * @self: a #IdeApplication
  *
  * Unloads all of the previously loaded #IdeApplicationAddin.
- *
- * Since: 3.32
  */
 void
 _ide_application_unload_addins (IdeApplication *self)
diff --git a/src/libide/gui/ide-application-private.h b/src/libide/gui/ide-application-private.h
index c710549ad..51c0e5908 100644
--- a/src/libide/gui/ide-application-private.h
+++ b/src/libide/gui/ide-application-private.h
@@ -20,18 +20,22 @@
 
 #pragma once
 
-#include <dazzle.h>
+#include <libide-core.h>
+#include <libide-gtk.h>
+
 #include <libpeas/peas.h>
 
 #include "ide-application.h"
-#include "ide-keybindings.h"
-#include "ide-worker-manager.h"
 
 G_BEGIN_DECLS
 
 struct _IdeApplication
 {
-  DzlApplication parent_instance;
+  AdwApplication parent_instance;
+
+  /* Our helper to merge menus together */
+  IdeMenuManager *menu_manager;
+  GHashTable *menu_merge_ids;
 
   /* Array of all of our IdeWorkebench instances (loaded projects and
    * their application windows).
@@ -55,11 +59,7 @@ struct _IdeApplication
    * for various keys.
    */
   GSettings *settings;
-
-  /* Tracks changes to plugins and updates the available keybindings
-   * to ensure they are loaded correctly (including .css files).
-   */
-  IdeKeybindings *keybindings;
+  GSettings *editor_settings;
 
   /* We need to track the GResource files that were manually loaded for
    * plugins on disk (generally Python plugins that need resources). That
@@ -67,6 +67,18 @@ struct _IdeApplication
    */
   GHashTable *plugin_gresources;
 
+  /* CSS providers for each plugin that is loaded, indexed by the resource
+   * path for the plugin/internal library.
+   */
+  GHashTable *css_providers;
+
+  /* The CSS provider to recolor all of the widgetry based on style schemes */
+  GtkCssProvider *recoloring;
+
+  /* A D-Bus proxy to settings portal */
+  GDBusProxy *settings_portal;
+  char *system_font_name;
+
   /* We need to stash the unmodified argv for the application somewhere
    * so that we can pass it to a remote instance. Otherwise we lose
    * the ability by cmdline-addins to determine if any options were
@@ -77,18 +89,6 @@ struct _IdeApplication
   /* The time the application was started */
   GDateTime *started_at;
 
-  /* Multi-process worker manager */
-  IdeWorkerManager *worker_manager;
-
-  /* Our type of process (optionally set to "worker" */
-  gchar *type;
-
-  /* The single plugin to load within a worker */
-  gchar *plugin;
-
-  /* The dbus-address for worker mode */
-  gchar *dbus_address;
-
   /* Sets the type of workspace to create when creating the next workspace
    * (such as when processing command line arguments).
    */
@@ -97,15 +97,15 @@ struct _IdeApplication
   /* If we've detected we lost network access */
   GNetworkMonitor *network_monitor;
   guint has_network : 1;
+
+  /* If all our typelibs were loaded successfully */
+  guint loaded_typelibs : 1;
 };
 
-IdeApplication *_ide_application_new                      (gboolean                 standalone,
-                                                           const gchar             *type,
-                                                           const gchar             *plugin,
-                                                           const gchar             *dbus_address);
+IdeApplication *_ide_application_new                      (gboolean                 standalone);
 void            _ide_application_init_color               (IdeApplication          *self);
 void            _ide_application_init_actions             (IdeApplication          *self);
-void            _ide_application_init_shortcuts           (IdeApplication          *self);
+void            _ide_application_init_settings            (IdeApplication          *self);
 void            _ide_application_load_addins              (IdeApplication          *self);
 void            _ide_application_unload_addins            (IdeApplication          *self);
 void            _ide_application_load_plugin              (IdeApplication          *self,
@@ -115,5 +115,9 @@ void            _ide_application_load_plugins_for_startup (IdeApplication
 void            _ide_application_load_plugins             (IdeApplication          *self);
 void            _ide_application_command_line             (IdeApplication          *self,
                                                            GApplicationCommandLine *cmdline);
+void            _ide_application_add_resources            (IdeApplication          *self,
+                                                           const char              *path);
+void            _ide_application_remove_resources         (IdeApplication          *self,
+                                                           const char              *path);
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-application-settings.c b/src/libide/gui/ide-application-settings.c
new file mode 100644
index 000000000..d24a71698
--- /dev/null
+++ b/src/libide/gui/ide-application-settings.c
@@ -0,0 +1,209 @@
+/* ide-application-settings.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-application-settings"
+
+#include "config.h"
+
+#include <libide-sourceview.h>
+
+#include "ide-application-private.h"
+#include "ide-recoloring-private.h"
+
+#define PORTAL_BUS_NAME "org.freedesktop.portal.Desktop"
+#define PORTAL_OBJECT_PATH "/org/freedesktop/portal/desktop"
+#define PORTAL_SETTINGS_INTERFACE "org.freedesktop.portal.Settings"
+
+static void
+on_portal_settings_changed_cb (IdeApplication *self,
+                               const char     *sender_name,
+                               const char     *signal_name,
+                               GVariant       *parameters,
+                               gpointer        user_data)
+{
+  g_autoptr(GVariant) value = NULL;
+  const char *schema_id;
+  const char *key;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (sender_name != NULL);
+  g_assert (signal_name != NULL);
+
+  if (g_strcmp0 (signal_name, "SettingChanged") != 0)
+    return;
+
+  g_variant_get (parameters, "(&s&sv)", &schema_id, &key, &value);
+
+  if (g_strcmp0 (schema_id, "org.gnome.desktop.interface") == 0 &&
+      g_strcmp0 (key, "monospace-font-name") == 0 &&
+      g_strcmp0 (g_variant_get_string (value, NULL), "") != 0)
+    {
+      g_free (self->system_font_name);
+      self->system_font_name = g_strdup (g_variant_get_string (value, NULL));
+      g_object_notify (G_OBJECT (self), "system-font-name");
+      g_object_notify (G_OBJECT (self), "system-font");
+    }
+}
+
+static void
+parse_portal_settings (IdeApplication *self,
+                       GVariant       *parameters)
+{
+  GVariantIter *iter = NULL;
+  const char *schema_str;
+  GVariant *val;
+
+  g_assert (IDE_IS_APPLICATION (self));
+
+  if (parameters == NULL)
+    return;
+
+  g_variant_get (parameters, "(a{sa{sv}})", &iter);
+
+  while (g_variant_iter_loop (iter, "{s@a{sv}}", &schema_str, &val))
+    {
+      GVariantIter *iter2 = g_variant_iter_new (val);
+      const char *key;
+      GVariant *v;
+
+      while (g_variant_iter_loop (iter2, "{sv}", &key, &v))
+        {
+          if (g_strcmp0 (schema_str, "org.gnome.desktop.interface") == 0 &&
+              g_strcmp0 (key, "monospace-font-name") == 0 &&
+              g_strcmp0 (g_variant_get_string (v, NULL), "") != 0)
+            {
+              g_free (self->system_font_name);
+              self->system_font_name = g_strdup (g_variant_get_string (v, NULL));
+            }
+        }
+
+      g_variant_iter_free (iter2);
+    }
+
+  g_variant_iter_free (iter);
+}
+
+static void
+ide_application_settings_style_scheme_changed_cb (IdeApplication *self,
+                                                  const char     *key,
+                                                  GSettings      *settings)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (G_IS_SETTINGS (settings));
+
+  g_object_notify (G_OBJECT (self), "style-scheme");
+
+  IDE_EXIT;
+}
+
+void
+_ide_application_init_settings (IdeApplication *self)
+{
+  static const char *patterns[] = { "org.gnome.*", NULL };
+  g_autoptr(GVariant) all = NULL;
+  g_autofree char *style_scheme_name = NULL;
+
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+  g_return_if_fail (self->settings_portal == NULL);
+
+  /* We must query the key to get changed notifications */
+  style_scheme_name = g_settings_get_string (self->editor_settings, "style-scheme-name");
+  g_debug ("Initial style scheme set to %s", style_scheme_name);
+  g_signal_connect_object (self->editor_settings,
+                           "changed::style-scheme-name",
+                           G_CALLBACK (ide_application_settings_style_scheme_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  self->settings_portal = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                                         G_DBUS_PROXY_FLAGS_NONE,
+                                                         NULL,
+                                                         PORTAL_BUS_NAME,
+                                                         PORTAL_OBJECT_PATH,
+                                                         PORTAL_SETTINGS_INTERFACE,
+                                                         NULL,
+                                                         NULL);
+
+  if (self->settings_portal != NULL)
+    {
+      g_signal_connect_object (self->settings_portal,
+                               "g-signal",
+                               G_CALLBACK (on_portal_settings_changed_cb),
+                               self,
+                               G_CONNECT_SWAPPED);
+      all = g_dbus_proxy_call_sync (self->settings_portal,
+                                    "ReadAll",
+                                    g_variant_new ("(^as)", patterns),
+                                    G_DBUS_CALL_FLAGS_NONE,
+                                    G_MAXINT,
+                                    NULL,
+                                    NULL);
+      parse_portal_settings (self, all);
+    }
+}
+
+void
+ide_application_set_style_scheme (IdeApplication *self,
+                                  const char     *style_scheme)
+{
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+
+  if (style_scheme == NULL)
+    style_scheme = "Adwaita";
+
+  g_object_freeze_notify (G_OBJECT (self));
+  g_settings_set_string (self->editor_settings, "style-scheme-name", style_scheme);
+  g_object_thaw_notify (G_OBJECT (self));
+}
+
+const char *
+ide_application_get_style_scheme (IdeApplication *self)
+{
+  GtkSourceStyleSchemeManager *style_scheme_manager;
+  GtkSourceStyleScheme *style_scheme;
+  AdwStyleManager *style_manager;
+  g_autofree char *style_scheme_id = NULL;
+  const char *variant;
+
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+  style_manager = adw_style_manager_get_default ();
+  style_scheme_manager = gtk_source_style_scheme_manager_get_default ();
+  style_scheme_id = g_settings_get_string (self->editor_settings, "style-scheme-name");
+
+  /* Fallback to Adwaita if we don't find a match */
+  if (gtk_source_style_scheme_manager_get_scheme (style_scheme_manager, style_scheme_id) == NULL)
+    {
+      g_free (style_scheme_id);
+      style_scheme_id = g_strdup ("Adwaita");
+    }
+
+  if (adw_style_manager_get_dark (style_manager))
+    variant = "dark";
+  else
+    variant = "light";
+
+  style_scheme = gtk_source_style_scheme_manager_get_scheme (style_scheme_manager, style_scheme_id);
+  style_scheme = ide_source_style_scheme_get_variant (style_scheme, variant);
+
+  return gtk_source_style_scheme_get_id (style_scheme);
+}
diff --git a/src/libide/gui/ide-application.c b/src/libide/gui/ide-application.c
index ab0b6c390..90e8f277f 100644
--- a/src/libide/gui/ide-application.c
+++ b/src/libide/gui/ide-application.c
@@ -27,9 +27,8 @@
 #endif
 
 #include <glib/gi18n.h>
-#include <handy.h>
+
 #include <libpeas/peas-autocleanups.h>
-#include <libide-themes.h>
 
 #include "ide-language-defaults.h"
 
@@ -38,11 +37,7 @@
 #include "ide-application-private.h"
 #include "ide-gui-global.h"
 #include "ide-primary-workspace.h"
-#include "ide-worker.h"
-
-G_DEFINE_FINAL_TYPE (IdeApplication, ide_application, DZL_TYPE_APPLICATION)
-
-#define IS_UI_PROCESS(app) ((app)->type == NULL)
+#include "ide-shortcut-manager-private.h"
 
 typedef struct
 {
@@ -52,6 +47,18 @@ typedef struct
   const gchar     *hint;
 } OpenData;
 
+G_DEFINE_FINAL_TYPE (IdeApplication, ide_application, ADW_TYPE_APPLICATION)
+
+enum {
+  PROP_0,
+  PROP_STYLE_SCHEME,
+  PROP_SYSTEM_FONT,
+  PROP_SYSTEM_FONT_NAME,
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
 static void
 ide_application_add_platform_data (GApplication    *app,
                                    GVariantBuilder *builder)
@@ -106,113 +113,122 @@ ide_application_local_command_line (GApplication   *app,
   return G_APPLICATION_CLASS (ide_application_parent_class)->local_command_line (app, arguments, 
exit_status);
 }
 
-static void
-ide_application_register_keybindings (IdeApplication *self)
+G_GNUC_NULL_TERMINATED
+static gboolean
+ide_application_load_all_typelibs (GError **error, ...)
 {
-  g_autoptr(GSettings) settings = NULL;
-  g_autofree gchar *name = NULL;
+  g_autoptr(GString) msg = g_string_new (NULL);
+  const char *typelib;
+  gboolean had_failure = FALSE;
+  va_list args;
 
-  g_assert (IDE_IS_APPLICATION (self));
+  va_start (args, error);
+  while ((typelib = va_arg (args, const char *)))
+    {
+      const char *version = va_arg (args, const char *);
+      g_autoptr(GError) local_error = NULL;
+
+      if (!g_irepository_require (NULL, typelib, version, 0, &local_error))
+        {
+          if (msg->len)
+            g_string_append (msg, "; ");
+          g_string_append (msg, local_error->message);
+          had_failure = TRUE;
+        }
+    }
+  va_end (args);
 
-  settings = g_settings_new ("org.gnome.builder.editor");
-  name = g_settings_get_string (settings, "keybindings");
-  self->keybindings = ide_keybindings_new (name);
-  g_settings_bind (settings, "keybindings", self->keybindings, "mode", G_SETTINGS_BIND_GET);
+  if (had_failure)
+    {
+      g_set_error_literal (error,
+                           G_IREPOSITORY_ERROR,
+                           G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
+                           msg->str);
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
-static int
-keybinding_key_snooper (GtkWidget   *grab_widget,
-                        GdkEventKey *key,
-                        gpointer     func_data)
+static void
+ide_application_load_typelibs (IdeApplication *self)
 {
-  IdeApplication *self = func_data;
-
-  g_assert (IDE_IS_APPLICATION (self));
+  g_autoptr(GError) error = NULL;
 
-  /* We need to hijack <Ctrl>period because ibus is messing it up. However,
-   * we only get a release event since it gets hijacked from the compositor
-   * so we never see the event. Instead, we catch the release and then change
-   * the focus to what we want (clearlying the state created in the compositor
-   * ibus bits).
-   */
-  if (key->type == GDK_KEY_RELEASE &&
-      key->keyval == GDK_KEY_period &&
-      (key->state & GDK_CONTROL_MASK) != 0)
-    {
-      if (IDE_IS_WORKSPACE (grab_widget))
-        {
-          DzlShortcutManager *shortcuts = dzl_shortcut_manager_get_default ();
+  IDE_ENTRY;
 
-          g_clear_object (&key->window);
-          key->window = g_object_ref (gtk_widget_get_window (grab_widget));
-          key->type = GDK_KEY_PRESS;
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_APPLICATION (self));
 
-          dzl_shortcut_manager_handle_event (shortcuts, key, grab_widget);
+  g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0");
 
-          return TRUE;
-        }
-    }
+  /* Ensure that we have all our required GObject Introspection packages
+   * loaded so that plugins don't need to require_version() as that is
+   * tedious and annoying to keep up to date.
+   *
+   * If we can't load any of our dependent packages, then fail to load
+   * python3 plugins altogether to avoid loading anything improper into
+   * the process space.
+   */
+  if (!ide_application_load_all_typelibs (&error,
+                                          "Gio", "2.0",
+                                          "GLib", "2.0",
+                                          "Gtk", "4.0",
+                                          "GtkSource", "5",
+                                          "Jsonrpc", "1.0",
+                                          "Template", "1.0",
+                                          "Vte", "3.91",
+#ifdef HAVE_WEBKIT
+                                          "WebKit2", "5.0",
+#endif
+                                          "Ide", PACKAGE_ABI_S,
+                                          NULL))
+    g_critical ("Cannot enable Python 3 plugins: %s", error->message);
+  else
+    self->loaded_typelibs = TRUE;
 
-  return FALSE;
+  IDE_EXIT;
 }
 
 static void
 ide_application_startup (GApplication *app)
 {
   IdeApplication *self = (IdeApplication *)app;
+  g_autofree gchar *style_path = NULL;
+  GtkSourceStyleSchemeManager *styles;
+  GtkSourceLanguageManager *langs;
+  GtkIconTheme *icon_theme;
 
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_APPLICATION (self));
 
-  /*
-   * We require a desktop session that provides a properly working
-   * D-Bus environment. Bail if for some reason that is not the case.
-   */
-  if (g_getenv ("DBUS_SESSION_BUS_ADDRESS") == NULL)
-    g_warning ("%s",
-               _("GNOME Builder requires a session with D-Bus which was not found. Please set 
DBUS_SESSION_BUS_ADDRESS. Some features may not be available."));
-
   G_APPLICATION_CLASS (ide_application_parent_class)->startup (app);
 
-  if (IS_UI_PROCESS (self))
-    {
-      g_autofree gchar *style_path = NULL;
-      GtkSourceStyleSchemeManager *styles;
-
-      /* Setup key snoopers */
-      G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-      gtk_key_snooper_install (keybinding_key_snooper, self);
-      G_GNUC_END_IGNORE_DEPRECATIONS
+  /* Setup access to private icons dir */
+  icon_theme = gtk_icon_theme_get_for_display (gdk_display_get_default ());
+  gtk_icon_theme_add_search_path (icon_theme, PACKAGE_ICONDIR);
 
-      /* Setup access to private icons dir */
-      gtk_icon_theme_prepend_search_path (gtk_icon_theme_get_default (), PACKAGE_ICONDIR);
+  /* Add custom style locations for gtksourceview schemes */
+  styles = gtk_source_style_scheme_manager_get_default ();
+  style_path = g_build_filename (g_get_home_dir (), ".local", "share", "gtksourceview-5", "styles", NULL);
+  gtk_source_style_scheme_manager_append_search_path (styles, style_path);
+  gtk_source_style_scheme_manager_append_search_path (styles, PACKAGE_DATADIR"/gnome-builder/styles/");
 
-      /* Add custom style locations for gtksourceview schemes */
-      styles = gtk_source_style_scheme_manager_get_default ();
-      style_path = g_build_filename (g_get_home_dir (), ".local", "share", "gtksourceview-4", "styles", 
NULL);
-      gtk_source_style_scheme_manager_append_search_path (styles, style_path);
-      gtk_source_style_scheme_manager_append_search_path (styles, PACKAGE_DATADIR"/gtksourceview-4/styles/");
+  /* Add custom locations for language specs */
+  langs = gtk_source_language_manager_get_default ();
+  gtk_source_language_manager_append_search_path (langs, 
"resource:///org/gnome/builder/gtksourceview/language-specs");
 
-      hdy_init ();
+  /* Setup access to portal settings */
+  _ide_application_init_settings (self);
 
-      /* Load color settings (Night Light, Dark Mode, etc) */
-      _ide_application_init_color (self);
-    }
+  /* Load color settings (Night Light, Dark Mode, etc) */
+  _ide_application_init_color (self);
 
   /* And now we can load the rest of our plugins for startup. */
   _ide_application_load_plugins (self);
 
-  if (IS_UI_PROCESS (self))
-    {
-      /* Make sure our shorcuts are registered */
-      _ide_application_init_shortcuts (self);
-
-      /* Load keybindings from plugins and what not */
-      ide_application_register_keybindings (self);
-
-      /* Load language defaults into gsettings */
-      ide_language_defaults_init_async (NULL, NULL, NULL);
-    }
+  /* Load language defaults into gsettings */
+  ide_language_defaults_init_async (NULL, NULL, NULL);
 }
 
 static void
@@ -228,66 +244,10 @@ ide_application_shutdown (GApplication *app)
   g_clear_pointer (&self->plugin_settings, g_hash_table_unref);
   g_clear_object (&self->addins);
   g_clear_object (&self->settings);
-  g_clear_object (&self->keybindings);
 
   G_APPLICATION_CLASS (ide_application_parent_class)->shutdown (app);
 }
 
-static void
-ide_application_activate_worker (IdeApplication *self)
-{
-  g_autoptr(GDBusConnection) connection = NULL;
-  g_autoptr(GError) error = NULL;
-  PeasPluginInfo *plugin_info;
-  PeasExtension *extension;
-  PeasEngine *engine;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (IDE_IS_APPLICATION (self));
-  g_assert (ide_str_equal0 (self->type, "worker"));
-  g_assert (self->dbus_address != NULL);
-  g_assert (self->plugin != NULL);
-
-#ifdef __linux__
-  prctl (PR_SET_PDEATHSIG, SIGKILL);
-#endif
-
-  IDE_TRACE_MSG ("Connecting to %s", self->dbus_address);
-
-  connection = g_dbus_connection_new_for_address_sync (self->dbus_address,
-                                                       (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
-                                                        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
-                                                       NULL, NULL, &error);
-
-  if (error != NULL)
-    {
-      g_error ("D-Bus failure: %s", error->message);
-      IDE_EXIT;
-    }
-
-  engine = peas_engine_get_default ();
-
-  if (!(plugin_info = peas_engine_get_plugin_info (engine, self->plugin)))
-    {
-      g_error ("No such plugin \"%s\"", self->plugin);
-      IDE_EXIT;
-    }
-
-  if (!(extension = peas_engine_create_extension (engine, plugin_info, IDE_TYPE_WORKER, NULL)))
-    {
-      g_error ("Failed to create \"%s\" worker", self->plugin);
-      IDE_EXIT;
-    }
-
-  ide_worker_register_service (IDE_WORKER (extension), connection);
-  g_application_hold (G_APPLICATION (self));
-  g_dbus_connection_start_message_processing (connection);
-
-  IDE_EXIT;
-}
-
 static void
 ide_application_activate_cb (PeasExtensionSet *set,
                              PeasPluginInfo   *plugin_info,
@@ -313,14 +273,8 @@ ide_application_activate (GApplication *app)
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_APPLICATION (self));
 
-  if (ide_str_equal0 (self->type, "worker"))
-    {
-      ide_application_activate_worker (self);
-      IDE_EXIT;
-    }
-
   if ((window = gtk_application_get_active_window (GTK_APPLICATION (self))))
-    ide_gtk_window_present (window);
+    gtk_window_present (GTK_WINDOW (window));
 
   if (self->addins != NULL)
     peas_extension_set_foreach (self->addins,
@@ -377,6 +331,148 @@ ide_application_open (GApplication  *app,
   IDE_EXIT;
 }
 
+static GtkCssProvider *
+get_css_provider (IdeApplication *self,
+                  const char     *key)
+{
+  GtkCssProvider *ret;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (key != NULL);
+
+  if (!(ret = g_hash_table_lookup (self->css_providers, key)))
+    {
+      ret = gtk_css_provider_new ();
+      gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                                  GTK_STYLE_PROVIDER (ret),
+                                                  GTK_STYLE_PROVIDER_PRIORITY_USER-1);
+      g_hash_table_insert (self->css_providers, g_strdup (key), ret);
+    }
+
+  return ret;
+}
+
+void
+_ide_application_add_resources (IdeApplication *self,
+                                const char     *resource_path)
+{
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *menu_path = NULL;
+  g_autofree gchar *css_path = NULL;
+  guint merge_id;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (resource_path != NULL);
+
+  /* We use interned strings for hash table keys */
+  resource_path = g_intern_string (resource_path);
+
+  /*
+   * If the resource path has a gtk/menus.ui file, we want to auto-load and
+   * merge the menus.
+   */
+  menu_path = g_build_filename (resource_path, "gtk", "menus.ui", NULL);
+
+  if (g_str_has_prefix (menu_path, "resource://"))
+    merge_id = ide_menu_manager_add_resource (self->menu_manager, menu_path, &error);
+  else
+    merge_id = ide_menu_manager_add_filename (self->menu_manager, menu_path, &error);
+
+  if (merge_id != 0)
+    g_hash_table_insert (self->menu_merge_ids, (gchar *)resource_path, GUINT_TO_POINTER (merge_id));
+
+  if (error != NULL &&
+      !(g_error_matches (error, G_RESOURCE_ERROR, G_RESOURCE_ERROR_NOT_FOUND) ||
+        g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)))
+    g_warning ("%s", error->message);
+  g_clear_error (&error);
+
+  if (g_str_has_prefix (resource_path, "resource://"))
+    {
+      g_autoptr(GBytes) bytes = NULL;
+
+      css_path = g_build_filename (resource_path + strlen ("resource://"), "style.css", NULL);
+      bytes = g_resources_lookup_data (css_path, 0, NULL);
+
+      if (bytes != NULL)
+        {
+          GtkCssProvider *provider = get_css_provider (self, resource_path);
+          gtk_css_provider_load_from_resource (provider, css_path);
+        }
+    }
+  else
+    {
+      css_path = g_build_filename (resource_path, "style.css", NULL);
+
+      if (g_file_test (css_path, G_FILE_TEST_IS_REGULAR))
+        {
+          GtkCssProvider *provider = get_css_provider (self, resource_path);
+          gtk_css_provider_load_from_path (provider, css_path);
+        }
+    }
+
+  ide_shortcut_manager_add_resources (resource_path);
+}
+
+void
+_ide_application_remove_resources (IdeApplication *self,
+                                   const char     *resource_path)
+{
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+  g_return_if_fail (resource_path != NULL);
+
+  /* Unmerge menus, keybindings, etc */
+  g_warning ("TODO: implement resource unloading for plugins: %s", resource_path);
+}
+
+static void
+ide_application_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  IdeApplication *self = IDE_APPLICATION (object);
+
+  switch (prop_id)
+    {
+    case PROP_STYLE_SCHEME:
+      g_value_set_string (value, ide_application_get_style_scheme (self));
+      break;
+
+    case PROP_SYSTEM_FONT_NAME:
+      g_value_set_string (value, ide_application_get_system_font_name (self));
+      break;
+
+    case PROP_SYSTEM_FONT: {
+      const char *system_font_name = ide_application_get_system_font_name (self);
+      g_value_take_boxed (value, pango_font_description_from_string (system_font_name));
+      break;
+    }
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_application_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  IdeApplication *self = IDE_APPLICATION (object);
+
+  switch (prop_id)
+    {
+    case PROP_STYLE_SCHEME:
+      ide_application_set_style_scheme (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
 static void
 ide_application_dispose (GObject *object)
 {
@@ -390,14 +486,16 @@ ide_application_dispose (GObject *object)
   g_clear_pointer (&self->workbenches, g_ptr_array_unref);
   g_clear_pointer (&self->plugin_settings, g_hash_table_unref);
   g_clear_pointer (&self->plugin_gresources, g_hash_table_unref);
+  g_clear_pointer (&self->css_providers, g_hash_table_unref);
   g_clear_pointer (&self->argv, g_strfreev);
-  g_clear_pointer (&self->plugin, g_free);
-  g_clear_pointer (&self->type, g_free);
-  g_clear_pointer (&self->dbus_address, g_free);
+  g_clear_pointer (&self->menu_merge_ids, g_hash_table_unref);
+  g_clear_pointer (&self->system_font_name, g_free);
+  g_clear_object (&self->recoloring);
   g_clear_object (&self->addins);
+  g_clear_object (&self->editor_settings);
   g_clear_object (&self->settings);
   g_clear_object (&self->network_monitor);
-  g_clear_object (&self->worker_manager);
+  g_clear_object (&self->menu_manager);
 
   G_OBJECT_CLASS (ide_application_parent_class)->dispose (object);
 }
@@ -409,6 +507,8 @@ ide_application_class_init (IdeApplicationClass *klass)
   GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
 
   object_class->dispose = ide_application_dispose;
+  object_class->get_property = ide_application_get_property;
+  object_class->set_property = ide_application_set_property;
 
   app_class->activate = ide_application_activate;
   app_class->open = ide_application_open;
@@ -417,41 +517,71 @@ ide_application_class_init (IdeApplicationClass *klass)
   app_class->local_command_line = ide_application_local_command_line;
   app_class->startup = ide_application_startup;
   app_class->shutdown = ide_application_shutdown;
+
+  properties[PROP_STYLE_SCHEME] =
+    g_param_spec_string ("style-scheme",
+                         "Style Scheme",
+                         "The style scheme for the editor",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_SYSTEM_FONT] =
+    g_param_spec_boxed ("system-font",
+                        "System Font",
+                        "System Font",
+                        PANGO_TYPE_FONT_DESCRIPTION,
+                        (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  properties[PROP_SYSTEM_FONT_NAME] =
+    g_param_spec_string ("system-font-name",
+                         "System Font Name",
+                         "System Font Name",
+                         "Monospace 11",
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
 static void
 ide_application_init (IdeApplication *self)
 {
+  self->system_font_name = g_strdup ("Monospace 11");
+  self->menu_merge_ids = g_hash_table_new (g_str_hash, g_str_equal);
+  self->menu_manager = ide_menu_manager_new ();
   self->started_at = g_date_time_new_now_local ();
   self->workspace_type = IDE_TYPE_PRIMARY_WORKSPACE;
   self->workbenches = g_ptr_array_new_with_free_func (g_object_unref);
   self->settings = g_settings_new ("org.gnome.builder");
+  self->editor_settings = g_settings_new ("org.gnome.builder.editor");
   self->plugin_gresources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
                                                    (GDestroyNotify)g_resource_unref);
+  self->css_providers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref );
+  self->recoloring = gtk_css_provider_new ();
 
   g_application_set_default (G_APPLICATION (self));
   gtk_window_set_default_icon_name (ide_get_application_id ());
-  ide_themes_init ();
+
+  /* Make sure we've loaded typelibs into process for early access */
+  ide_application_load_typelibs (self);
 
   /* Ensure our core data is loaded early. */
-  dzl_application_add_resources (DZL_APPLICATION (self), "resource:///org/gnome/libide-sourceview/");
-  dzl_application_add_resources (DZL_APPLICATION (self), "resource:///org/gnome/libide-gui/");
-  dzl_application_add_resources (DZL_APPLICATION (self), "resource:///org/gnome/libide-terminal/");
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-sourceview/");
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-gui/");
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-greeter/");
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-editor/");
+  _ide_application_add_resources (self, "resource:///org/gnome/libide-terminal/");
 
   /* Make sure our GAction are available */
   _ide_application_init_actions (self);
 }
 
 IdeApplication *
-_ide_application_new (gboolean     standalone,
-                      const gchar *type,
-                      const gchar *plugin,
-                      const gchar *dbus_address)
+_ide_application_new (gboolean standalone)
 {
   GApplicationFlags flags = G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN;
   IdeApplication *self;
 
-  if (standalone || ide_str_equal0 (type, "worker"))
+  if (standalone)
     flags |= G_APPLICATION_NON_UNIQUE;
 
   self = g_object_new (IDE_TYPE_APPLICATION,
@@ -460,10 +590,6 @@ _ide_application_new (gboolean     standalone,
                        "resource-base-path", "/org/gnome/builder",
                        NULL);
 
-  self->type = g_strdup (type);
-  self->plugin = g_strdup (plugin);
-  self->dbus_address = g_strdup (dbus_address);
-
   /* Load plugins indicating they support startup features */
   _ide_application_load_plugins_for_startup (self);
 
@@ -541,8 +667,6 @@ ide_application_remove_workbench (IdeApplication *self,
  * @user_data: user data for @callback
  *
  * Calls @callback for each of the registered workbenches.
- *
- * Since: 3.32
  */
 void
 ide_application_foreach_workbench (IdeApplication *self,
@@ -568,8 +692,6 @@ ide_application_foreach_workbench (IdeApplication *self,
  * next workspace upon handling files from command-line arguments. This is
  * reset after the files are opened and is generally only useful from
  * #IdeApplicationAddin's who need to alter the default workspace.
- *
- * Since: 3.32
  */
 void
 ide_application_set_workspace_type (IdeApplication *self,
@@ -601,8 +723,6 @@ ide_application_network_changed_cb (IdeApplication  *self,
  * the wild that make determining if we have network access difficult.
  *
  * Returns: %TRUE if we think there is network access.
- *
- * Since: 3.32
  */
 gboolean
 ide_application_has_network (IdeApplication *self)
@@ -646,8 +766,6 @@ ide_application_has_network (IdeApplication *self)
  * Gets the time the application was started.
  *
  * Returns: (transfer none): a #GDateTime
- *
- * Since: 3.32
  */
 GDateTime *
 ide_application_get_started_at (IdeApplication *self)
@@ -657,94 +775,6 @@ ide_application_get_started_at (IdeApplication *self)
   return self->started_at;
 }
 
-static void
-ide_application_get_worker_cb (GObject      *object,
-                               GAsyncResult *result,
-                               gpointer      user_data)
-{
-  IdeWorkerManager *worker_manager = (IdeWorkerManager *)object;
-  g_autoptr(IdeTask) task = user_data;
-  g_autoptr(GError) error = NULL;
-  GDBusProxy *proxy;
-
-  g_assert (IDE_IS_WORKER_MANAGER (worker_manager));
-
-  if (!(proxy = ide_worker_manager_get_worker_finish (worker_manager, result, &error)))
-    ide_task_return_error (task, g_steal_pointer (&error));
-  else
-    ide_task_return_pointer (task, g_steal_pointer (&proxy), g_object_unref);
-}
-
-/**
- * ide_application_get_worker_async:
- * @self: an #IdeApplication
- * @plugin_name: The name of the plugin.
- * @cancellable: (allow-none): a #GCancellable or %NULL.
- * @callback: a #GAsyncReadyCallback or %NULL.
- * @user_data: user data for @callback.
- *
- * Asynchronously requests a #GDBusProxy to a service provided in a worker
- * process. The worker should be an #IdeWorker implemented by the plugin named
- * @plugin_name. The #IdeWorker is responsible for created both the service
- * registered on the bus and the proxy to it.
- *
- * The #IdeApplication is responsible for spawning a subprocess for the worker.
- *
- * @callback should call ide_application_get_worker_finish() with the result
- * provided to retrieve the result.
- *
- * Since: 3.32
- */
-void
-ide_application_get_worker_async (IdeApplication      *self,
-                                  const gchar         *plugin_name,
-                                  GCancellable        *cancellable,
-                                  GAsyncReadyCallback  callback,
-                                  gpointer             user_data)
-{
-  g_autoptr(IdeTask) task = NULL;
-
-  g_return_if_fail (IDE_IS_APPLICATION (self));
-  g_return_if_fail (plugin_name != NULL);
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  if (self->worker_manager == NULL)
-    self->worker_manager = ide_worker_manager_new ();
-
-  task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_application_get_worker_async);
-
-  ide_worker_manager_get_worker_async (self->worker_manager,
-                                       plugin_name,
-                                       cancellable,
-                                       ide_application_get_worker_cb,
-                                       g_steal_pointer (&task));
-}
-
-/**
- * ide_application_get_worker_finish:
- * @self: an #IdeApplication.
- * @result: a #GAsyncResult
- * @error: a location for a #GError, or %NULL.
- *
- * Completes an asynchronous request to get a proxy to a worker process.
- *
- * Returns: (transfer full): a #GDBusProxy or %NULL.
- *
- * Since: 3.32
- */
-GDBusProxy *
-ide_application_get_worker_finish (IdeApplication  *self,
-                                   GAsyncResult    *result,
-                                   GError         **error)
-{
-  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
-  g_return_val_if_fail (IDE_IS_TASK (result), NULL);
-
-  return ide_task_propagate_pointer (IDE_TASK (result), error);
-}
-
 /**
  * ide_application_find_workbench_for_file:
  * @self: a #IdeApplication
@@ -755,8 +785,6 @@ ide_application_get_worker_finish (IdeApplication  *self,
  * If no workbench is the root of @file, then %NULL is returned.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkbench or %NULL
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_application_find_workbench_for_file (IdeApplication *self,
@@ -833,8 +861,6 @@ ide_application_get_command_line_handled (IdeApplication          *self,
  *
  * Returns: (transfer none) (type IdeApplicationAddin) (nullable): an
  *   #IdeApplicationAddin or %NULL.
- *
- * Since: 3.34
  */
 gpointer
 ide_application_find_addin_by_module_name (IdeApplication *self,
@@ -860,3 +886,166 @@ ide_application_find_addin_by_module_name (IdeApplication *self,
 
   return peas_extension_set_get_extension (self->addins, plugin_info);
 }
+
+/**
+ * ide_application_get_menu_by_id:
+ * @self: a #IdeApplication
+ * @menu_id: (nullable): the menu identifier
+ *
+ * Gets the merged menu by it's identifier.
+ *
+ * Returns: (transfer none) (nullable): a #GMenu or %NULL if @menu_id is %NULL
+ */
+GMenu *
+ide_application_get_menu_by_id (IdeApplication *self,
+                                const char     *menu_id)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+  if (menu_id == NULL)
+    return NULL;
+
+  return ide_menu_manager_get_menu_by_id (self->menu_manager, menu_id);
+}
+
+const char *
+ide_application_get_system_font_name (IdeApplication *self)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+  return self->system_font_name;
+}
+
+static GFile *
+get_user_style_file (GFile *file)
+{
+  static GFile *style_dir;
+  g_autofree char *basename = NULL;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if G_UNLIKELY (style_dir == NULL)
+    {
+      if (ide_is_flatpak ())
+        style_dir = g_file_new_build_filename (g_get_home_dir (),
+                                               ".local",
+                                               "share",
+                                               "gtksourceview-5",
+                                               "styles",
+                                               NULL);
+      else
+        style_dir = g_file_new_build_filename (g_get_user_data_dir (),
+                                               "gtksourceview-5",
+                                               "styles",
+                                               NULL);
+    }
+
+  basename = g_file_get_basename (file);
+
+  return g_file_get_child (style_dir, basename);
+}
+
+static void
+ide_application_install_schemes_cb (GObject      *object,
+                                    GAsyncResult *result,
+                                    gpointer      user_data)
+{
+  GFile *file = (GFile *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GFile) dst = NULL;
+  GPtrArray *ar;
+  GFile *src;
+
+  g_assert (G_IS_FILE (file));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  ar = g_task_get_task_data (task);
+
+  g_assert (ar != NULL);
+  g_assert (ar->len > 0);
+  g_assert (G_IS_FILE (g_ptr_array_index (ar, ar->len-1)));
+
+  g_ptr_array_remove_index (ar, ar->len-1);
+
+  if (!g_file_copy_finish (file, result, &error))
+    g_warning ("Failed to copy file: %s", error->message);
+
+  if (ar->len == 0)
+    {
+      g_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  src = g_ptr_array_index (ar, ar->len-1);
+  dst = get_user_style_file (src);
+
+  g_file_copy_async (src, dst,
+                     G_FILE_COPY_OVERWRITE | G_FILE_COPY_BACKUP,
+                     G_PRIORITY_LOW,
+                     g_task_get_cancellable (task),
+                     NULL, NULL,
+                     ide_application_install_schemes_cb,
+                     g_object_ref (task));
+}
+
+void
+ide_application_install_schemes_async (IdeApplication       *self,
+                                       GFile               **files,
+                                       guint                 n_files,
+                                       GCancellable         *cancellable,
+                                       GAsyncReadyCallback   callback,
+                                       gpointer              user_data)
+{
+  g_autoptr(GPtrArray) ar = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) dst = NULL;
+  g_autoptr(GFile) dir = NULL;
+  GFile *src;
+
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+  g_return_if_fail (files != NULL);
+  g_return_if_fail (n_files > 0);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ar = g_ptr_array_new_with_free_func (g_object_unref);
+  for (guint i = 0; i < n_files; i++)
+    g_ptr_array_add (ar, g_object_ref (files[i]));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_application_install_schemes_async);
+  g_task_set_task_data (task, g_ptr_array_ref (ar), (GDestroyNotify) g_ptr_array_unref);
+
+  src = g_ptr_array_index (ar, ar->len-1);
+  dst = get_user_style_file (src);
+  dir = g_file_get_parent (dst);
+
+  if (!g_file_query_exists (dir, NULL) &&
+      !g_file_make_directory_with_parents (dir, cancellable, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  g_file_copy_async (src, dst,
+                     G_FILE_COPY_OVERWRITE | G_FILE_COPY_BACKUP,
+                     G_PRIORITY_LOW,
+                     cancellable,
+                     NULL, NULL,
+                     ide_application_install_schemes_cb,
+                     g_steal_pointer (&task));
+}
+
+gboolean
+ide_application_install_schemes_finish (IdeApplication  *self,
+                                        GAsyncResult    *result,
+                                        GError         **error)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/src/libide/gui/ide-application.h b/src/libide/gui/ide-application.h
index 446f9aa7d..69c18a9f4 100644
--- a/src/libide/gui/ide-application.h
+++ b/src/libide/gui/ide-application.h
@@ -24,7 +24,8 @@
 # error "Only <libide-gui.h> can be included directly."
 #endif
 
-#include <dazzle.h>
+#include <adwaita.h>
+
 #include <libide-core.h>
 #include <libide-projects.h>
 
@@ -35,65 +36,78 @@ G_BEGIN_DECLS
 #define IDE_TYPE_APPLICATION    (ide_application_get_type())
 #define IDE_APPLICATION_DEFAULT IDE_APPLICATION(g_application_get_default())
 
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeApplication, ide_application, IDE, APPLICATION, DzlApplication)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeApplication, ide_application, IDE, APPLICATION, AdwApplication)
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_application_has_network              (IdeApplication           *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gchar        **ide_application_get_argv                 (IdeApplication           *self,
                                                          GApplicationCommandLine  *cmdline);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GDateTime     *ide_application_get_started_at           (IdeApplication           *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean       ide_application_get_command_line_handled (IdeApplication           *self,
                                                          GApplicationCommandLine  *cmdline);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_set_command_line_handled (IdeApplication           *self,
                                                          GApplicationCommandLine  *cmdline,
                                                          gboolean                  handled);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_open_project_async       (IdeApplication           *self,
                                                          IdeProjectInfo           *project_info,
                                                          GType                     workspace_type,
                                                          GCancellable             *cancellable,
                                                          GAsyncReadyCallback       callback,
                                                          gpointer                  user_data);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeWorkbench  *ide_application_open_project_finish      (IdeApplication           *self,
                                                          GAsyncResult             *result,
                                                          GError                  **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_set_workspace_type       (IdeApplication           *self,
                                                          GType                     workspace_type);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_add_workbench            (IdeApplication           *self,
                                                          IdeWorkbench             *workbench);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_remove_workbench         (IdeApplication           *self,
                                                          IdeWorkbench             *workbench);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_application_foreach_workbench        (IdeApplication           *self,
                                                          GFunc                     callback,
                                                          gpointer                  user_data);
-IDE_AVAILABLE_IN_3_32
-void           ide_application_get_worker_async         (IdeApplication           *self,
-                                                         const gchar              *plugin_name,
-                                                         GCancellable             *cancellable,
-                                                         GAsyncReadyCallback       callback,
-                                                         gpointer                  user_data);
-IDE_AVAILABLE_IN_3_32
-GDBusProxy    *ide_application_get_worker_finish        (IdeApplication           *self,
-                                                         GAsyncResult             *result,
-                                                         GError                  **error);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeWorkbench  *ide_application_find_workbench_for_file  (IdeApplication           *self,
                                                          GFile                    *file);
-IDE_AVAILABLE_IN_3_34
+IDE_AVAILABLE_IN_ALL
 gpointer       ide_application_find_addin_by_module_name (IdeApplication           *self,
                                                           const gchar              *module_name);
-IDE_AVAILABLE_IN_41
+IDE_AVAILABLE_IN_ALL
 char          *ide_application_create_cancel_action      (IdeApplication           *self,
                                                           GCancellable             *cancellable);
+IDE_AVAILABLE_IN_ALL
+GMenu         *ide_application_get_menu_by_id            (IdeApplication           *self,
+                                                          const char               *menu_id);
+IDE_AVAILABLE_IN_ALL
+const char    *ide_application_get_system_font_name      (IdeApplication           *self);
+IDE_AVAILABLE_IN_ALL
+const char    *ide_application_get_style_scheme          (IdeApplication           *self);
+IDE_AVAILABLE_IN_ALL
+void           ide_application_set_style_scheme          (IdeApplication           *self,
+                                                          const char               *style_scheme);
+IDE_AVAILABLE_IN_ALL
+gboolean       ide_application_get_dark                  (IdeApplication           *self);
+IDE_AVAILABLE_IN_ALL
+void           ide_application_install_schemes_async     (IdeApplication           *self,
+                                                          GFile                   **files,
+                                                          guint                     n_files,
+                                                          GCancellable             *cancellable,
+                                                          GAsyncReadyCallback       callback,
+                                                          gpointer                  user_data);
+IDE_AVAILABLE_IN_ALL
+gboolean       ide_application_install_schemes_finish    (IdeApplication           *self,
+                                                          GAsyncResult             *result,
+                                                          GError                  **error);
 
 G_END_DECLS


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