[gnome-shell] Scale thumbnails to fit in the vertical space



commit 0ef3f999d242fb297b6dedb295cad2224ca47f8d
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Fri Feb 11 12:09:23 2011 -0500

    Scale thumbnails to fit in the vertical space
    
    When we have more thumbnails than can fit in the vertical space, scale
    them down. This is implemented by using a generic container so we can
    compute positions and sizes on the fly and do the appropriate
    width-for-height behavior.
    
    The usage of clutter constraints to position the indicator is droppped
    since it less complicated to just position the indicator in the right
    place ourselves.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641879

 js/ui/workspaceThumbnail.js |  130 +++++++++++++++++++++++++++++++++----------
 1 files changed, 101 insertions(+), 29 deletions(-)
---
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
index 1564720..8b17b4f 100644
--- a/js/ui/workspaceThumbnail.js
+++ b/js/ui/workspaceThumbnail.js
@@ -13,8 +13,8 @@ const Tweener = imports.ui.tweener;
 const Workspace = imports.ui.workspace;
 const WorkspacesView = imports.ui.workspacesView;
 
-// Fraction of original screen size for thumbnails
-let THUMBNAIL_SCALE = 1/8.;
+// The maximum size of a thumbnail is 1/8 the width and height of the screen
+let MAX_THUMBNAIL_SCALE = 1/8.;
 
 function WindowClone(realWindow) {
     this._init(realWindow);
@@ -153,9 +153,7 @@ WorkspaceThumbnail.prototype = {
 
         this._background = new Clutter.Clone({ source: global.background_actor });
         this._group.add_actor(this._background);
-
-        this._group.set_size(THUMBNAIL_SCALE * global.screen_width, THUMBNAIL_SCALE * global.screen_height);
-        this._group.set_scale(THUMBNAIL_SCALE, THUMBNAIL_SCALE);
+        this._group.set_size(global.screen_width, global.screen_height);
 
         let windows = global.get_window_actors().filter(this._isMyWindow, this);
 
@@ -329,8 +327,11 @@ function ThumbnailsBox() {
 
 ThumbnailsBox.prototype = {
     _init: function() {
-        this.actor = new St.BoxLayout({ vertical: true,
-                                        style_class: 'workspace-thumbnails' });
+        this.actor = new Shell.GenericContainer({ style_class: 'workspace-thumbnails',
+                                                  request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
+        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+        this.actor.connect('allocate', Lang.bind(this, this._allocate));
 
         let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator',
                                      fixed_position_set: true });
@@ -339,13 +340,8 @@ ThumbnailsBox.prototype = {
         Shell.util_set_hidden_from_pick(indicator, true);
 
         this._indicator = indicator;
-        this.actor.add(indicator);
-        this._indicatorConstraints = [];
-        this._indicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.POSITION }));
-        this._indicatorConstraints.push(new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.SIZE }));
-        this._indicatorConstraints.forEach(function(constraint) {
-                                               indicator.add_constraint(constraint);
-                                           });
+        this.actor.add_actor(indicator);
+        this._indicatorConstrained = false;
 
         this._thumbnails = [];
     },
@@ -377,13 +373,10 @@ ThumbnailsBox.prototype = {
             let metaWorkspace = global.screen.get_workspace_by_index(k);
             let thumbnail = new WorkspaceThumbnail(metaWorkspace);
             this._thumbnails[k] = thumbnail;
-            this.actor.add(thumbnail.actor);
+            this.actor.add_actor(thumbnail.actor);
         }
 
-        // The thumbnails indicator actually needs to be on top of the thumbnails, but
-        // there is also something more subtle going on as well - actors in a StBoxLayout
-        // are allocated from bottom to top (start to end), and we need the
-        // thumnail indicator to be allocated after the actors it is constrained to.
+        // The thumbnails indicator actually needs to be on top of the thumbnails
         this._indicator.raise_top();
     },
 
@@ -407,20 +400,99 @@ ThumbnailsBox.prototype = {
             this._thumbnails[i].syncStacking(stackIndices);
     },
 
-    _constrainThumbnailIndicator: function() {
-        let active = global.screen.get_active_workspace_index();
-        let thumbnail = this._thumbnails[active];
+    _getPreferredHeight: function(actor, forWidth, alloc) {
+        // Note that for getPreferredWidth/Height we cheat a bit and skip propagating
+        // the size request to our children because we know how big they are and know
+        // that the actors aren't depending on the virtual functions being called.
+
+        if (this._thumbnails.length == 0)
+            return;
+
+        let spacing = this.actor.get_theme_node().get_length('spacing');
+        let totalSpacing = (this._thumbnails.length - 1) * spacing;
+
+        alloc.min_size = totalSpacing;
+        alloc.natural_size = totalSpacing + this._thumbnails.length * global.screen_height * MAX_THUMBNAIL_SCALE;
+    },
+
+    _getPreferredWidth: function(actor, forHeight, alloc) {
+        if (this._thumbnails.length == 0)
+            return;
+
+        let spacing = this.actor.get_theme_node().get_length('spacing');
+        let totalSpacing = (this._thumbnails.length - 1) * spacing;
+        let avail = forHeight - totalSpacing;
+
+        let scale = (avail / this._thumbnails.length) / global.screen_height;
+        scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
+
+        alloc.min_size = alloc.natural_size = Math.round(global.screen_width * scale);
+    },
+
+    _allocate: function(actor, box, flags) {
+        let screenHeight = global.screen_height;
+
+        let spacing = this.actor.get_theme_node().get_length('spacing');
+        let totalSpacing = (this._thumbnails.length - 1) * spacing;
+        let avail = (box.y2 - box.y1) - totalSpacing;
 
-        this._indicatorConstraints.forEach(function(constraint) {
-                                               constraint.set_source(thumbnail.actor);
-                                               constraint.set_enabled(true);
-                                           });
+        let scale = (avail / this._thumbnails.length) / screenHeight;
+        scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
+
+        let thumbnailHeight = screenHeight * scale;
+
+        let childBox = new Clutter.ActorBox();
+
+        let indicatorWorkspace = this._indicatorConstrained ? global.screen.get_active_workspace() : null;
+        let indicatorBox;
+
+        // Allocating a scaled actor is funny - x1/y1 correspond to the origin
+        // of the actor, but x2/y2 are increased by the *unscaled* size.
+        childBox.x1 = box.x1;
+        childBox.x2 = childBox.x1 + global.screen_width;
+
+        let y = box.y1;
+
+        for (let i = 0; i < this._thumbnails.length; i++) {
+            if (i > 0)
+                y += spacing + thumbnailHeight;
+
+            // We might end up with thumbnailHeight being something like 99.33
+            // pixels. To make this work and not end up with a gap at the bottom,
+            // we need some thumbnails to be 99 pixels and some 100 pixels height;
+            // we compute an actual scale separately for each thumbnail.
+            let y1 = Math.round(y);
+            let y2 = Math.round(y + thumbnailHeight);
+            let roundedScale = (y2 - y1) / screenHeight;
+
+            if (this._thumbnails[i].metaWorkspace == indicatorWorkspace) {
+                let indicatorBox = new Clutter.ActorBox();
+                indicatorBox.x1 = box.x1;
+                indicatorBox.x2 = box.x2;
+                indicatorBox.y1 = y1;
+                indicatorBox.y2 = y2;
+
+                this._indicator.allocate(indicatorBox, flags);
+            }
+
+            childBox.y1 = y1;
+            childBox.y2 = childBox.y1 + screenHeight;
+
+            this._thumbnails[i].actor.set_scale(roundedScale, roundedScale);
+            this._thumbnails[i].actor.allocate(childBox, flags);
+        }
+
+        if (indicatorWorkspace == null)
+            this._indicator.allocate_preferred_size(flags);
+    },
+
+    _constrainThumbnailIndicator: function() {
+        this._indicatorConstrained = true;
+        this.actor.queue_relayout();
     },
 
     _unconstrainThumbnailIndicator: function() {
-        this._indicatorConstraints.forEach(function(constraint) {
-                                               constraint.set_enabled(false);
-                                           });
+        this._indicatorConstrained = false;
     },
 
     _activeWorkspaceChanged: function(wm, from, to, direction) {



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