[gnome-shell] dash: Make recent docs display two columns



commit 2dcd0511c45d1e45978a145d28b4047fdee47051
Author: Colin Walters <walters verbum org>
Date:   Sun Aug 16 22:23:44 2009 -0400

    dash: Make recent docs display two columns
    
    The design has smaller icons in two columns.  Add a new
    custom display to docDisplay for it.
    
    Clean up some of the texture cache handling for recent URIs so
    it's not size-dependent, since the dash size is now different
    from the default GenericDisplay size.

 js/misc/docInfo.js        |   16 ++--
 js/ui/dash.js             |    5 +-
 js/ui/docDisplay.js       |  189 ++++++++++++++++++++++++++++++++++++++++++++-
 src/shell-texture-cache.c |   37 ++++-----
 src/shell-texture-cache.h |    2 -
 5 files changed, 215 insertions(+), 34 deletions(-)
---
diff --git a/js/misc/docInfo.js b/js/misc/docInfo.js
index 23ebb89..ff83d63 100644
--- a/js/misc/docInfo.js
+++ b/js/misc/docInfo.js
@@ -85,19 +85,18 @@ DocInfo.prototype = {
 
 var docManagerInstance = null;
 
-function getDocManager(size) {
+function getDocManager() {
     if (docManagerInstance == null)
-        docManagerInstance = new DocManager(size);
+        docManagerInstance = new DocManager();
     return docManagerInstance;
 }
 
-function DocManager(size) {
-    this._init(size);
+function DocManager() {
+    this._init();
 }
 
 DocManager.prototype = {
-    _init: function(iconSize) {
-        this._iconSize = iconSize;
+    _init: function() {
         this._recentManager = Gtk.RecentManager.get_default();
         this._items = {};
         this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
@@ -112,6 +111,9 @@ DocManager.prototype = {
         let newItems = {};
         for (let i = 0; i < docs.length; i++) {
             let recentInfo = docs[i];
+            if (!recentInfo.exists())
+                continue;
+
             let docInfo = new DocInfo(recentInfo);
 
             // we use GtkRecentInfo URI as an item Id
@@ -126,7 +128,7 @@ DocManager.prototype = {
            dump them here */
         let texCache = Shell.TextureCache.get_default();
         for (var uri in deleted) {
-            texCache.evict_recent_thumbnail(this._iconSize, this._items[uri]);
+            texCache.evict_recent_thumbnail(this._items[uri]);
         }
         this._items = newItems;
     },
diff --git a/js/ui/dash.js b/js/ui/dash.js
index dd4e4bb..e222378 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -64,7 +64,6 @@ const PANE_BORDER_WIDTH = 2;
 const PANE_BACKGROUND_COLOR = new Clutter.Color();
 PANE_BACKGROUND_COLOR.from_pixel(0x000000f4);
 
-
 function Pane() {
     this._init();
 }
@@ -604,10 +603,8 @@ Dash.prototype = {
 
         let docsSection = new Section(_("RECENT DOCUMENTS"));
 
-        let docDisplay = new DocDisplay.DocDisplay();
-        docDisplay.load();
+        let docDisplay = new DocDisplay.DashDocDisplay();
         docsSection.content.append(docDisplay.actor, Big.BoxPackFlags.EXPAND);
-        createPaneForDetails(this, docDisplay);
 
         this._moreDocsPane = null;
         docsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) {
diff --git a/js/ui/docDisplay.js b/js/ui/docDisplay.js
index 605d7f8..dcb29ea 100644
--- a/js/ui/docDisplay.js
+++ b/js/ui/docDisplay.js
@@ -1,17 +1,24 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
+const Big = imports.gi.Big;
 const Clutter = imports.gi.Clutter;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
 const Lang = imports.lang;
+const Pango = imports.gi.Pango;
 const Shell = imports.gi.Shell;
 const Signals = imports.signals;
 const Mainloop = imports.mainloop;
 
 const DocInfo = imports.misc.docInfo;
+const DND = imports.ui.dnd;
 const GenericDisplay = imports.ui.genericDisplay;
 const Main = imports.ui.main;
 
+const DASH_DOCS_ICON_SIZE = 16;
+
+const DEFAULT_SPACING = 4;
+
 /* This class represents a single display item containing information about a document.
  * We take the current number of seconds in the constructor to avoid looking up the current
  * time for every item when they are created in a batch.
@@ -123,7 +130,7 @@ DocDisplay.prototype = {
         this._updateTimeoutTargetTime = -1;
         this._updateTimeoutId = 0;
 
-        this._docManager = DocInfo.getDocManager(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
+        this._docManager = DocInfo.getDocManager();
         this._docsStale = true;
         this._docManager.connect('changed', function(mgr, userData) {
             me._docsStale = true;
@@ -249,3 +256,183 @@ DocDisplay.prototype = {
 };
 
 Signals.addSignalMethods(DocDisplay.prototype);
+
+function DashDocDisplayItem(docInfo) {
+    this._init(docInfo);
+}
+
+DashDocDisplayItem.prototype = {
+    _init: function(docInfo) {
+        this._info = docInfo;
+        this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+                                   spacing: DEFAULT_SPACING,
+                                   reactive: true });
+        this.actor.connect('button-release-event', Lang.bind(this, function () {
+            docInfo.launch();
+            Main.overview.hide();
+        }));
+
+        this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
+        let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
+        iconBox.append(this._icon, Big.BoxPackFlags.NONE);
+        this.actor.append(iconBox, Big.BoxPackFlags.NONE);
+        let name = new Clutter.Text({ font_name: "Sans 14px",
+                                      color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+                                      ellipsize: Pango.EllipsizeMode.END,
+                                      text: docInfo.name });
+        this.actor.append(name, Big.BoxPackFlags.EXPAND);
+
+        let draggable = DND.makeDraggable(this.actor);
+        this.actor._delegate = this;
+    },
+
+    getDragActorSource: function() {
+        return this._icon;
+    },
+
+    getDragActor: function(stageX, stageY) {
+        this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
+        return this.dragActor;
+    },
+
+    //// Drag and drop functions ////
+
+    shellWorkspaceLaunch: function () {
+        this._info.launch();
+    }
+}
+
+/**
+ * Class used to display two column recent documents in the dash
+ */
+function DashDocDisplay() {
+    this._init();
+}
+
+DashDocDisplay.prototype = {
+    _init: function() {
+        this.actor = new Shell.GenericContainer();
+        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));
+
+        this._docManager = DocInfo.getDocManager();
+        this._docManager.connect('changed', Lang.bind(this, function(mgr) {
+            this._redisplay();
+        }));
+        this._redisplay();
+    },
+
+    _getPreferredWidth: function(actor, forHeight, alloc) {
+        let children = actor.get_children();
+
+        // We use two columns maximum.  Just take the min and natural size of the
+        // first two items, even though strictly speaking it's not correct; we'd
+        // need to calculate how many items we could fit for the height, then
+        // take the biggest preferred width for each column.
+        // In practice the dash gets a fixed width anyways.
+
+        // If we have one child, add its minimum and natural size
+        if (children.length > 0) {
+            let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
+            alloc.min_size += minSize;
+            alloc.natural_size += naturalSize;
+        }
+        // If we have two, add its size, plus DEFAULT_SPACING
+        if (children.length > 1) {
+            let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
+            alloc.min_size += DEFAULT_SPACING + minSize;
+            alloc.natural_size += DEFAULT_SPACING + naturalSize;
+        }
+    },
+
+    _getPreferredHeight: function(actor, forWidth, alloc) {
+        let children = actor.get_children();
+
+        // Two columns, where we go vertically down first.  So just take
+        // the height of half of the children as our preferred height.
+
+        let firstColumnChildren = children.length / 2;
+
+        alloc.min_size = 0;
+        for (let i = 0; i < firstColumnChildren; i++) {
+            let child = children[i];
+            let [minSize, naturalSize] = child.get_preferred_height(forWidth);
+            alloc.natural_size += naturalSize;
+
+            if (i > 0 && i < children.length - 1) {
+                alloc.min_size += DEFAULT_SPACING;
+                alloc.natural_size += DEFAULT_SPACING;
+            }
+        }
+    },
+
+    _allocate: function(actor, box, flags) {
+        let width = box.x2 - box.x1;
+        let height = box.y2 - box.y1;
+
+        let children = actor.get_children();
+
+        // The width of an item is our allocated width, minus spacing, divided in half.
+        let itemWidth = Math.floor((width - DEFAULT_SPACING) / 2);
+        let x = box.x1;
+        let y = box.y1;
+        let columnIndex = 0;
+        let i = 0;
+        // Loop over the children, going vertically down first.  When we run
+        // out of vertical space (our y variable is bigger than box.y2), switch
+        // to the second column.
+        for (; i < children.length; i++) {
+            let child = children[i];
+
+            let [minSize, naturalSize] = child.get_preferred_height(-1);
+
+            if (y + naturalSize > box.y2) {
+                // Is this the second column?  Ok, break.
+                if (columnIndex == 1) {
+                    break;
+                }
+                // Set x to the halfway point.
+                columnIndex += 1;
+                x = x + itemWidth + DEFAULT_SPACING;
+                // And y is back to the top.
+                y = box.y1;
+            }
+
+            let childBox = new Clutter.ActorBox();
+            childBox.x1 = x;
+            childBox.y1 = y;
+            childBox.x2 = childBox.x1 + itemWidth;
+            childBox.y2 = y + naturalSize;
+
+            y = childBox.y2 + DEFAULT_SPACING;
+
+            child.show();
+            child.allocate(childBox, flags);
+        }
+
+        // Everything else didn't fit, just hide it.
+        for (; i < children.length; i++) {
+            children[i].hide();
+        }
+    },
+
+    _redisplay: function() {
+        this.actor.remove_all();
+
+        let docs = this._docManager.getItems();
+        let docUrls = [];
+        for (let url in docs) {
+            docUrls.push(url);
+        }
+        docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
+        let textureCache = Shell.TextureCache.get_default();
+
+        for (let i = 0; i < docUrls.length; i++) {
+            let url = docUrls[i];
+            let docInfo = docs[url];
+            let display = new DashDocDisplayItem(docInfo);
+            this.actor.add_actor(display.actor);
+        }
+    }
+}
diff --git a/src/shell-texture-cache.c b/src/shell-texture-cache.c
index 534d382..0ed3446 100644
--- a/src/shell-texture-cache.c
+++ b/src/shell-texture-cache.c
@@ -1196,47 +1196,44 @@ shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
 /**
  * shell_texture_cache_evict_thumbnail:
  * @cache:
- * @size: Size in pixels
  * @uri: Source URI
  *
- * Removes the reference the shell_texture_cache_load_thumbnail() function
- * created for a thumbnail.
+ * Removes all references added by shell_texture_cache_load_thumbnail() function
+ * created for the given URI.
  */
 void
 shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
-                                     int                size,
                                      const char        *uri)
 {
-  CacheKey key;
+  GHashTableIter iter;
+  gpointer key, value;
 
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.thumbnail_uri = (char*)uri;
+  g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      CacheKey *cachekey = key;
+
+      if (cachekey->thumbnail_uri == NULL || strcmp (cachekey->thumbnail_uri, uri) != 0)
+        continue;
 
-  g_hash_table_remove (cache->priv->keyed_cache, &key);
+      g_hash_table_iter_remove (&iter);
+    }
 }
 
 /**
  * shell_texture_cache_evict_recent_thumbnail:
  * @cache:
- * @size: Size in pixels
  * @info: A recent info
  *
- * Removes the reference the shell_texture_cache_load_recent_thumbnail() function
- * created for a thumbnail.
+ * Removes all references added by shell_texture_cache_load_recent_thumbnail() function
+ * for the URI associated with the given @info.
  */
 void
 shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
-                                            int                size,
                                             GtkRecentInfo     *info)
 {
-  CacheKey key;
-
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
-
-  g_hash_table_remove (cache->priv->keyed_cache, &key);
+  shell_texture_cache_evict_thumbnail (cache, gtk_recent_info_get_uri (info));
 }
 
 static ShellTextureCache *instance = NULL;
diff --git a/src/shell-texture-cache.h b/src/shell-texture-cache.h
index a6ab8cb..940e101 100644
--- a/src/shell-texture-cache.h
+++ b/src/shell-texture-cache.h
@@ -62,11 +62,9 @@ ClutterActor *shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cach
                                                          GtkRecentInfo     *info);
 
 void shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
-                                          int                size,
                                           const char        *uri);
 
 void shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
-                                                 int                size,
                                                  GtkRecentInfo     *info);
 
 ClutterActor *shell_texture_cache_load_uri_async (ShellTextureCache *cache,



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