[gnome-shell/gbsneto/folders-as-dialogs: 6/9] appDisplay: Add folder title and entry to dialog



commit a612d087652e8beb25a2ed51ceb2af5523a16ca8
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Dec 17 16:39:24 2019 -0300

    appDisplay: Add folder title and entry to dialog
    
    This allows editing the folder name, and keeps the folder title visible
    at all times.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/896

 data/theme/gnome-shell-sass/widgets/_app-grid.scss |  27 ++++
 js/ui/appDisplay.js                                | 142 ++++++++++++++++++++-
 2 files changed, 166 insertions(+), 3 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss 
b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
index f5467bbd6d..42aeadc192 100644
--- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss
+++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
@@ -90,6 +90,33 @@ $app_grid_fg_color: #fff;
   border-radius: 8px;
   spacing: 24px;
   background-color: transparentize(darken($osd_bg_color,10%), 0.05);
+
+  & .folder-name-container {
+    padding: 12px 18px;
+    spacing: 12px;
+
+    & .folder-name-label,
+    & .folder-name-entry {
+      font-size: 18pt;
+      font-weight: bold;
+    }
+
+    & .folder-name-entry { width: 300px }
+
+    /* FIXME: this is to keep the label in sync with the entry */
+    & .folder-name-label { padding: 5px 7px }
+
+    & .edit-folder-button {
+      @extend %button;
+
+      padding: 0;
+      width: 36px;
+      height: 36px;
+      border-radius: 18px;
+
+      & > StIcon { icon-size: 16px }
+    }
+  }
 }
 .app-folder-dialog-container {
   padding: 12px;
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index e1be5abdd9..855e952244 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1630,7 +1630,7 @@ var FolderIcon = GObject.registerClass({
         if (this._dialog)
             return;
         if (!this._dialog) {
-            this._dialog = new AppFolderDialog(this);
+            this._dialog = new AppFolderDialog(this, this._folder);
             this._parentView.addFolderDialog(this._dialog);
             this._dialog.connect('open-state-changed', (popup, isOpen) => {
                 if (!isOpen)
@@ -1818,7 +1818,7 @@ var AppFolderDialog = GObject.registerClass({
         'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
     },
 }, class AppFolderDialog extends St.Widget {
-    _init(source) {
+    _init(source, folder) {
         super._init({
             layout_manager: new Clutter.BinLayout(),
             style_class: 'app-folder-dialog-container',
@@ -1834,6 +1834,7 @@ var AppFolderDialog = GObject.registerClass({
         }));
 
         this._source = source;
+        this._folder = folder;
         this._view = source.view;
 
         this._isOpen = false;
@@ -1845,8 +1846,11 @@ var AppFolderDialog = GObject.registerClass({
             y_expand: true,
             x_align: Clutter.ActorAlign.FILL,
             y_align: Clutter.ActorAlign.FILL,
+            vertical: true,
         });
         this.add_child(this._viewBox);
+
+        this._addFolderNameEntry();
         this._viewBox.add_child(this._view);
 
         global.focus_manager.add_group(this);
@@ -1861,6 +1865,131 @@ var AppFolderDialog = GObject.registerClass({
         this._needsZoomAndFade = false;
     }
 
+    _addFolderNameEntry() {
+        this._entryBox = new St.BoxLayout({
+            style_class: 'folder-name-container',
+        });
+        this._viewBox.add_child(this._entryBox);
+
+        // Empty actor to center the title
+        let ghostButton = new Clutter.Actor();
+        this._entryBox.add_child(ghostButton);
+
+        let stack = new Shell.Stack({
+            x_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+        });
+        this._entryBox.add_child(stack);
+
+        // Folder name label
+        this._folderNameLabel = new St.Label({
+            style_class: 'folder-name-label',
+            x_expand: true,
+            y_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+
+        stack.add_child(this._folderNameLabel);
+
+        // Folder name entry
+        this._entry = new St.Entry({
+            style_class: 'folder-name-entry',
+            opacity: 0,
+            reactive: false,
+        });
+        this._entry.clutter_text.set({
+            x_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+        });
+
+        this._entry.clutter_text.connect('activate', () => {
+            this._showFolderLabel();
+        });
+
+        stack.add_child(this._entry);
+
+        // Edit button
+        let button = new St.Button({
+            style_class: 'edit-folder-button',
+            button_mask: St.ButtonMask.ONE,
+            toggle_mode: true,
+            reactive: true,
+            can_focus: true,
+            x_align: Clutter.ActorAlign.END,
+            y_align: Clutter.ActorAlign.CENTER,
+            child: new St.Icon({
+                icon_name: 'document-edit-symbolic',
+                icon_size: 16,
+            }),
+        });
+
+        button.connect('notify::checked', () => {
+            if (button.checked)
+                this._showFolderEntry();
+            else
+                this._showFolderLabel();
+        });
+
+        this._entryBox.add_child(button);
+
+        ghostButton.add_constraint(new Clutter.BindConstraint({
+            source: button,
+            coordinate: Clutter.BindCoordinate.SIZE,
+        }));
+
+        this._folder.connect('changed::name', () => this._syncFolderName());
+        this._syncFolderName();
+    }
+
+    _syncFolderName() {
+        let newName = _getFolderName(this._folder);
+
+        this._folderNameLabel.text = newName;
+        this._entry.text = newName;
+    }
+
+    _switchActor(from, to) {
+        to.reactive = true;
+        to.ease({
+            opacity: 255,
+            duration: 300,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+        });
+
+        from.ease({
+            opacity: 0,
+            duration: 300,
+            mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+            onComplete: () => {
+                from.reactive = false;
+            },
+        });
+    }
+
+    _showFolderLabel() {
+        this._maybeUpdateFolderName();
+        this._switchActor(this._entry, this._folderNameLabel);
+    }
+
+    _showFolderEntry() {
+        this._switchActor(this._folderNameLabel, this._entry);
+
+        this._entry.clutter_text.set_selection(0, -1);
+        this._entry.clutter_text.grab_key_focus();
+    }
+
+    _maybeUpdateFolderName() {
+        let folderName = _getFolderName(this._folder);
+        let newFolderName = this._entry.text.trim();
+
+        if (newFolderName.length === 0 || newFolderName === folderName)
+            return;
+
+        this._folder.set_string('name', newFolderName);
+        this._folder.set_boolean('translate', false);
+    }
+
     _zoomAndFadeIn() {
         let [sourceX, sourceY] =
             this._source.get_transformed_position();
@@ -1945,7 +2074,13 @@ var AppFolderDialog = GObject.registerClass({
 
     vfunc_allocate(box, flags) {
         let contentBox = this.get_theme_node().get_content_box(box);
-        this._view.adaptToSize(contentBox.get_width(), contentBox.get_height());
+
+        let [, entryBoxHeight] = this._entryBox.get_size();
+        let spacing = this._viewBox.layout_manager.spacing;
+
+        this._view.adaptToSize(
+            contentBox.get_width(),
+            contentBox.get_height() - entryBoxHeight - spacing);
 
         super.vfunc_allocate(box, flags);
 
@@ -2028,6 +2163,7 @@ var AppFolderDialog = GObject.registerClass({
             return;
 
         this._zoomAndFadeOut();
+        this._showFolderLabel();
 
         this._grabHelper.ungrab({ actor: this });
         this._isOpen = false;


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