[gnome-shell] windowPreview: Subclass a C actor



commit afb56df55cc1889b5181df2dea4014ec85a45810
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Thu Feb 25 16:04:30 2021 +0100

    windowPreview: Subclass a C actor
    
    Move the get_preferred_width/height() and allocate() vfunc
    implementations of WindowPreview to C, subclassing the C GObject from
    JS.
    
    This gets us another significant performance gain, allocating a
    workspace with 20 windows now only takes 1.2 ms.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1743>

 js/ui/windowPreview.js     |  85 ++++++++--------------
 src/meson.build            |   2 +
 src/shell-window-preview.c | 175 +++++++++++++++++++++++++++++++++++++++++++++
 src/shell-window-preview.h |  14 ++++
 4 files changed, 221 insertions(+), 55 deletions(-)
---
diff --git a/js/ui/windowPreview.js b/js/ui/windowPreview.js
index 3bd213959f..84d80fe3ef 100644
--- a/js/ui/windowPreview.js
+++ b/js/ui/windowPreview.js
@@ -37,7 +37,7 @@ var WindowPreview = GObject.registerClass({
         'show-chrome': {},
         'size-changed': {},
     },
-}, class WindowPreview extends St.Widget {
+}, class WindowPreview extends Shell.WindowPreview {
     _init(metaWindow, workspace, overviewAdjustment) {
         this.metaWindow = metaWindow;
         this.metaWindow._delegate = this;
@@ -52,17 +52,19 @@ var WindowPreview = GObject.registerClass({
             offscreen_redirect: Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY,
         });
 
-        this._windowContainer = new Clutter.Actor({
+        const windowContainer = new Clutter.Actor({
             pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
         });
-        this._windowContainer.connect('notify::scale-x',
+        this.window_container = windowContainer;
+
+        windowContainer.connect('notify::scale-x',
             () => this._adjustOverlayOffsets());
         // gjs currently can't handle setting an actors layout manager during
         // the initialization of the actor if that layout manager keeps track
         // of its container, so set the layout manager after creating the
         // container
-        this._windowContainer.layout_manager = new Shell.WindowPreviewLayout();
-        this.add_child(this._windowContainer);
+        windowContainer.layout_manager = new Shell.WindowPreviewLayout();
+        this.add_child(windowContainer);
 
         this._addWindow(metaWindow);
 
@@ -71,13 +73,13 @@ var WindowPreview = GObject.registerClass({
         this._stackAbove = null;
 
         this._cachedBoundingBox = {
-            x: this._windowContainer.layout_manager.bounding_box.x1,
-            y: this._windowContainer.layout_manager.bounding_box.y1,
-            width: this._windowContainer.layout_manager.bounding_box.get_width(),
-            height: this._windowContainer.layout_manager.bounding_box.get_height(),
+            x: windowContainer.layout_manager.bounding_box.x1,
+            y: windowContainer.layout_manager.bounding_box.y1,
+            width: windowContainer.layout_manager.bounding_box.get_width(),
+            height: windowContainer.layout_manager.bounding_box.get_height(),
         };
 
-        this._windowContainer.layout_manager.connect(
+        windowContainer.layout_manager.connect(
             'notify::bounding-box', layout => {
                 this._cachedBoundingBox = {
                     x: layout.bounding_box.x1,
@@ -127,16 +129,16 @@ var WindowPreview = GObject.registerClass({
             pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
         });
         this._icon.add_constraint(new Clutter.BindConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             coordinate: Clutter.BindCoordinate.POSITION,
         }));
         this._icon.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.X_AXIS,
             factor: 0.5,
         }));
         this._icon.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.Y_AXIS,
             pivot_point: new Graphene.Point({ x: -1, y: ICON_OVERLAP }),
             factor: 1,
@@ -150,22 +152,22 @@ var WindowPreview = GObject.registerClass({
             reactive: true,
         });
         this._title.add_constraint(new Clutter.BindConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             coordinate: Clutter.BindCoordinate.X,
         }));
         const iconBottomOverlap = ICON_SIZE * (1 - ICON_OVERLAP);
         this._title.add_constraint(new Clutter.BindConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             coordinate: Clutter.BindCoordinate.Y,
             offset: scaleFactor * (iconBottomOverlap + ICON_TITLE_SPACING),
         }));
         this._title.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.X_AXIS,
             factor: 0.5,
         }));
         this._title.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.Y_AXIS,
             pivot_point: new Graphene.Point({ x: -1, y: 0 }),
             factor: 1,
@@ -187,17 +189,17 @@ var WindowPreview = GObject.registerClass({
             child: new St.Icon({ icon_name: 'preview-close-symbolic' }),
         });
         this._closeButton.add_constraint(new Clutter.BindConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             coordinate: Clutter.BindCoordinate.POSITION,
         }));
         this._closeButton.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.X_AXIS,
             pivot_point: new Graphene.Point({ x: 0.5, y: -1 }),
             factor: this._closeButtonSide === St.Side.LEFT ? 0 : 1,
         }));
         this._closeButton.add_constraint(new Clutter.AlignConstraint({
-            source: this._windowContainer,
+            source: windowContainer,
             align_axis: Clutter.AlignAxis.Y_AXIS,
             pivot_point: new Graphene.Point({ x: -1, y: 0.5 }),
             factor: 0,
@@ -223,33 +225,6 @@ var WindowPreview = GObject.registerClass({
         });
     }
 
-    vfunc_get_preferred_width(forHeight) {
-        const themeNode = this.get_theme_node();
-
-        // Only include window previews in size request, not chrome
-        const [minWidth, natWidth] =
-            this._windowContainer.get_preferred_width(
-                themeNode.adjust_for_height(forHeight));
-
-        return themeNode.adjust_preferred_width(minWidth, natWidth);
-    }
-
-    vfunc_get_preferred_height(forWidth) {
-        const themeNode = this.get_theme_node();
-        const [minHeight, natHeight] =
-            this._windowContainer.get_preferred_height(
-                themeNode.adjust_for_width(forWidth));
-
-        return themeNode.adjust_preferred_height(minHeight, natHeight);
-    }
-
-    vfunc_allocate(box) {
-        this.set_allocation(box);
-
-        for (const child of this)
-            child.allocate_available_size(0, 0, box.get_width(), box.get_height());
-    }
-
     _updateIconScale() {
         const { ControlsState } = OverviewControls;
         const { currentState, initialState, finalState } =
@@ -354,13 +329,13 @@ var WindowPreview = GObject.registerClass({
             });
         });
 
-        const [width, height] = this._windowContainer.get_size();
+        const [width, height] = this.window_container.get_size();
         const { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
         const activeExtraSize = WINDOW_ACTIVE_SIZE_INC * 2 * scaleFactor;
         const origSize = Math.max(width, height);
         const scale = (origSize + activeExtraSize) / origSize;
 
-        this._windowContainer.ease({
+        this.window_container.ease({
             scale_x: scale,
             scale_y: scale,
             duration: animate ? WINDOW_SCALE_TIME : 0,
@@ -395,7 +370,7 @@ var WindowPreview = GObject.registerClass({
             });
         });
 
-        this._windowContainer.ease({
+        this.window_container.ease({
             scale_x: 1,
             scale_y: 1,
             duration: animate ? WINDOW_SCALE_TIME : 0,
@@ -407,9 +382,9 @@ var WindowPreview = GObject.registerClass({
         // Assume that scale-x and scale-y update always set
         // in lock-step; that allows us to not use separate
         // handlers for horizontal and vertical offsets
-        const previewScale = this._windowContainer.scale_x;
+        const previewScale = this.window_container.scale_x;
         const [previewWidth, previewHeight] =
-            this._windowContainer.allocation.get_size();
+            this.window_container.allocation.get_size();
 
         const heightIncrease =
             Math.floor(previewHeight * (previewScale - 1) / 2);
@@ -427,7 +402,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _addWindow(metaWindow) {
-        const clone = this._windowContainer.layout_manager.add_window(metaWindow);
+        const clone = this.window_container.layout_manager.add_window(metaWindow);
         if (!clone)
             return;
 
@@ -446,7 +421,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _deleteAll() {
-        const windows = this._windowContainer.layout_manager.get_windows();
+        const windows = this.window_container.layout_manager.get_windows();
 
         // Delete all windows, starting from the bottom-most (most-modal) one
         for (const window of windows.reverse())
@@ -471,7 +446,7 @@ var WindowPreview = GObject.registerClass({
     }
 
     _hasAttachedDialogs() {
-        return this._windowContainer.layout_manager.get_windows().length > 1;
+        return this.window_container.layout_manager.get_windows().length > 1;
     }
 
     _updateAttachedDialogs() {
diff --git a/src/meson.build b/src/meson.build
index d20134fd20..b06f7cab1e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -109,6 +109,7 @@ libshell_public_headers = [
   'shell-tray-icon.h',
   'shell-tray-manager.h',
   'shell-util.h',
+  'shell-window-preview.h',
   'shell-window-preview-layout.h',
   'shell-window-tracker.h',
   'shell-wm.h'
@@ -152,6 +153,7 @@ libshell_sources = [
   'shell-tray-icon.c',
   'shell-tray-manager.c',
   'shell-util.c',
+  'shell-window-preview.c',
   'shell-window-preview-layout.c',
   'shell-window-tracker.c',
   'shell-wm.c'
diff --git a/src/shell-window-preview.c b/src/shell-window-preview.c
new file mode 100644
index 0000000000..2a4c6967cf
--- /dev/null
+++ b/src/shell-window-preview.c
@@ -0,0 +1,175 @@
+#include "config.h"
+
+#include "shell-window-preview.h"
+
+enum
+{
+  PROP_0,
+
+  PROP_WINDOW_CONTAINER,
+
+  PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+
+struct _ShellWindowPreview
+{
+  /*< private >*/
+  StWidget parent_instance;
+
+  ClutterActor *window_container;
+};
+
+G_DEFINE_TYPE (ShellWindowPreview, shell_window_preview, ST_TYPE_WIDGET);
+
+static void
+shell_window_preview_get_property (GObject      *gobject,
+                                   unsigned int  property_id,
+                                   GValue       *value,
+                                   GParamSpec   *pspec)
+{
+  ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+  switch (property_id)
+    {
+    case PROP_WINDOW_CONTAINER:
+      g_value_set_object (value, self->window_container);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+    }
+}
+
+static void
+shell_window_preview_set_property (GObject      *gobject,
+                                   unsigned int  property_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+  switch (property_id)
+    {
+    case PROP_WINDOW_CONTAINER:
+      g_set_object (&self->window_container, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+    }
+}
+
+static void
+shell_window_preview_get_preferred_width (ClutterActor *actor,
+                                          float         for_height,
+                                          float        *min_width_p,
+                                          float        *natural_width_p)
+{
+  ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (actor);
+  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+  float min_width, nat_width;
+
+  st_theme_node_adjust_for_height (theme_node, &for_height);
+
+  clutter_actor_get_preferred_width (self->window_container, for_height,
+                                     &min_width, &nat_width);
+
+  st_theme_node_adjust_preferred_width (theme_node, &min_width, &nat_width);
+
+  if (min_width_p)
+    *min_width_p = min_width;
+
+  if (natural_width_p)
+    *natural_width_p = nat_width;
+}
+
+static void
+shell_window_preview_get_preferred_height (ClutterActor *actor,
+                                           float         for_width,
+                                           float        *min_height_p,
+                                           float        *natural_height_p)
+{
+  ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (actor);
+  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+  float min_height, nat_height;
+
+  st_theme_node_adjust_for_width (theme_node, &for_width);
+
+  clutter_actor_get_preferred_height (self->window_container, for_width,
+                                      &min_height, &nat_height);
+
+  st_theme_node_adjust_preferred_height (theme_node, &min_height, &nat_height);
+
+  if (min_height_p)
+    *min_height_p = min_height;
+
+  if (natural_height_p)
+    *natural_height_p = nat_height;
+}
+
+static void
+shell_window_preview_allocate (ClutterActor          *actor,
+                               const ClutterActorBox *box)
+{
+  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+  ClutterActorBox content_box;
+  float x, y, max_width, max_height;
+  ClutterActorIter iter;
+  ClutterActor *child;
+
+  clutter_actor_set_allocation (actor, box);
+
+  st_theme_node_get_content_box (theme_node, box, &content_box);
+
+  clutter_actor_box_get_origin (&content_box, &x, &y);
+  clutter_actor_box_get_size (&content_box, &max_width, &max_height);
+
+  clutter_actor_iter_init (&iter, actor);
+  while (clutter_actor_iter_next (&iter, &child))
+    clutter_actor_allocate_available_size (child, x, y, max_width, max_height);
+}
+
+static void
+shell_window_preview_dispose (GObject *gobject)
+{
+  ShellWindowPreview *self = SHELL_WINDOW_PREVIEW (gobject);
+
+  g_clear_object (&self->window_container);
+
+  G_OBJECT_CLASS (shell_window_preview_parent_class)->dispose (gobject);
+}
+
+static void
+shell_window_preview_init (ShellWindowPreview *self)
+{
+}
+
+static void
+shell_window_preview_class_init (ShellWindowPreviewClass *klass)
+{
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  actor_class->get_preferred_width = shell_window_preview_get_preferred_width;
+  actor_class->get_preferred_height = shell_window_preview_get_preferred_height;
+  actor_class->allocate = shell_window_preview_allocate;
+
+  gobject_class->dispose = shell_window_preview_dispose;
+  gobject_class->get_property = shell_window_preview_get_property;
+  gobject_class->set_property = shell_window_preview_set_property;
+
+  /**
+   * ShellWindowPreview:window-container:
+   */
+  obj_props[PROP_WINDOW_CONTAINER] =
+    g_param_spec_object ("window-container",
+                         "window-container",
+                         "window-container",
+                         CLUTTER_TYPE_ACTOR,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
diff --git a/src/shell-window-preview.h b/src/shell-window-preview.h
new file mode 100644
index 0000000000..e50380018b
--- /dev/null
+++ b/src/shell-window-preview.h
@@ -0,0 +1,14 @@
+#ifndef __SHELL_WINDOW_PREVIEW_H__
+#define __SHELL_WINDOW_PREVIEW_H__
+
+#include <st/st.h>
+
+G_BEGIN_DECLS
+
+#define SHELL_TYPE_WINDOW_PREVIEW (shell_window_preview_get_type ())
+G_DECLARE_FINAL_TYPE (ShellWindowPreview, shell_window_preview,
+                      SHELL, WINDOW_PREVIEW, StWidget)
+
+G_END_DECLS
+
+#endif /* __SHELL_WINDOW_PREVIEW_H__ */


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