From 2dcd0511c45d1e45978a145d28b4047fdee47051 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 16 Aug 2009 22:23:44 -0400 Subject: [PATCH] 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 23ebb89b7..ff83d6366 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 dd4e4bbf5..e22237850 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 605d7f8c9..dcb29ea36 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 534d3829e..0ed3446d6 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); - g_hash_table_remove (cache->priv->keyed_cache, &key); + 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_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 a6ab8cb95..940e1010f 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,