[gnome-shell] quickSettings: Add menu support to items



commit e0c61f53f1dd91541d0e4e6271f9627dd13c088b
Author: Florian Müllner <fmuellner gnome org>
Date:   Fri Jul 29 15:22:37 2022 +0200

    quickSettings: Add menu support to items
    
    Now that the QuickSettingsMenu supports child menus, it is time
    to add menu support to quick toggles.
    
    Do that by introducing a QuickSettingsItem parent class with a
    construct-only :has-menu property, as that will allow including
    menus in items that aren't following the standard icon+label
    pattern of QuickToggle (yes, we'll have some of those).
    
    A common parent class also allows us to control the type of
    menu that is created. That's important, as we need another
    custom menu type to
    
    - constrain the menu's y position to place it underneath
      the source actor
    
    - use open/close animations taylored for this use case
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>

 .../gnome-shell-sass/widgets/_quick-settings.scss  |   9 ++
 js/ui/quickSettings.js                             | 118 ++++++++++++++++++++-
 2 files changed, 125 insertions(+), 2 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_quick-settings.scss 
b/data/theme/gnome-shell-sass/widgets/_quick-settings.scss
index 8dc5ff8508..05d8495144 100644
--- a/data/theme/gnome-shell-sass/widgets/_quick-settings.scss
+++ b/data/theme/gnome-shell-sass/widgets/_quick-settings.scss
@@ -20,3 +20,12 @@
   .quick-toggle-label { font-weight: bold; }
   .quick-toggle-icon { icon-size: $base_icon_size; }
 }
+
+.quick-toggle-menu {
+  @include card();
+  padding: 1.5 * $base_padding;
+}
+
+.quick-toggle-menu-container {
+  padding: 2 * $base_padding $base_padding 0;
+ }
diff --git a/js/ui/quickSettings.js b/js/ui/quickSettings.js
index 0e1b83cef1..ae097ca646 100644
--- a/js/ui/quickSettings.js
+++ b/js/ui/quickSettings.js
@@ -4,8 +4,32 @@ const {Atk, Clutter, Gio, GLib, GObject, Graphene, Pango, St} = imports.gi;
 const Main = imports.ui.main;
 const PopupMenu = imports.ui.popupMenu;
 
-const {POPUP_ANIMATION_TIME} = imports.ui.boxpointer;
+const {PopupAnimation} = imports.ui.boxpointer;
+
 const DIM_BRIGHTNESS = -0.4;
+const POPUP_ANIMATION_TIME = 400;
+
+var QuickSettingsItem = GObject.registerClass({
+    Properties: {
+        'has-menu': GObject.ParamSpec.boolean(
+            'has-menu', 'has-menu', 'has-menu',
+            GObject.ParamFlags.READWRITE |
+            GObject.ParamFlags.CONSTRUCT_ONLY,
+            false),
+    },
+}, class QuickSettingsItem extends St.Button {
+    _init(params) {
+        super._init(params);
+
+        if (this.hasMenu) {
+            this.menu = new QuickToggleMenu(this);
+            this.menu.actor.hide();
+
+            this._menuManager = new PopupMenu.PopupMenuManager(this);
+            this._menuManager.addMenu(this.menu);
+        }
+    }
+});
 
 var QuickToggle = GObject.registerClass({
     Properties: {
@@ -17,7 +41,7 @@ var QuickToggle = GObject.registerClass({
             GObject.ParamFlags.READWRITE,
             ''),
     },
-}, class QuickToggle extends St.Button {
+}, class QuickToggle extends QuickSettingsItem {
     _init(params) {
         super._init({
             style_class: 'quick-toggle button',
@@ -70,6 +94,96 @@ var QuickToggle = GObject.registerClass({
     }
 });
 
+class QuickToggleMenu extends PopupMenu.PopupMenuBase {
+    constructor(sourceActor) {
+        super(sourceActor, 'quick-toggle-menu');
+
+        const constraints = new Clutter.BindConstraint({
+            coordinate: Clutter.BindCoordinate.Y,
+            source: sourceActor,
+        });
+        sourceActor.bind_property('height',
+            constraints, 'offset',
+            GObject.BindingFlags.DEFAULT);
+
+        this.actor = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            style_class: 'quick-toggle-menu-container',
+            reactive: true,
+            x_expand: true,
+            y_expand: false,
+            constraints,
+        });
+        this.actor._delegate = this;
+        this.actor.add_child(this.box);
+
+        global.focus_manager.add_group(this.actor);
+    }
+
+    open(animate) {
+        if (this.isOpen)
+            return;
+
+        this.actor.show();
+        this.isOpen = true;
+
+        this.actor.height = -1;
+        const [targetHeight] = this.actor.get_preferred_height(-1);
+
+        const duration = animate !== PopupAnimation.NONE
+            ? POPUP_ANIMATION_TIME / 2
+            : 0;
+
+        this.actor.height = 0;
+        this.box.opacity = 0;
+        this.actor.ease({
+            duration,
+            height: targetHeight,
+            onComplete: () => {
+                this.box.ease({
+                    duration,
+                    opacity: 255,
+                });
+                this.actor.height = -1;
+            },
+        });
+        this.emit('open-state-changed', true);
+    }
+
+    close(animate) {
+        if (!this.isOpen)
+            return;
+
+        const duration = animate !== PopupAnimation.NONE
+            ? POPUP_ANIMATION_TIME / 2
+            : 0;
+
+        this.box.ease({
+            duration,
+            opacity: 0,
+            onComplete: () => {
+                this.actor.ease({
+                    duration,
+                    height: 0,
+                    onComplete: () => {
+                        this.actor.hide();
+                        this.emit('menu-closed');
+                    },
+                });
+            },
+        });
+
+        this.isOpen = false;
+        this.emit('open-state-changed', false);
+    }
+
+    // expected on toplevel menus
+    _setOpenedSubMenu(submenu) {
+        this._openedSubMenu?.close(true);
+        this._openedSubMenu = submenu;
+    }
+}
+
 const QuickSettingsLayoutMeta = GObject.registerClass({
     Properties: {
         'column-span': GObject.ParamSpec.int(


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