[gnome-shell] appDisplay: Reimplement well layout to be width-independent
- From: Colin Walters <walters src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gnome-shell] appDisplay: Reimplement well layout to be width-independent
- Date: Sat, 8 Aug 2009 19:48:02 +0000 (UTC)
commit ebd6f4bc8fa0539cbe7520e8cee586a512e73fc9
Author: Colin Walters <walters verbum org>
Date: Thu Aug 6 15:39:09 2009 -0400
appDisplay: Reimplement well layout to be width-independent
Use ShellGenericContainer to implement a fully dynamic layout
for the application well. It's still fixed to 4 columns by default,
but no longer requires a fixed width to be passed in on start.
With another chunk of work, it could likely try to adjust to
the case where we can only fit fewer than 4 items in the well.
Remove the border highlighting on mouseover, since that caused
reallocations, and the grid layout isn't trivial.
Delete the unused shell_global_get_word_with function.
js/ui/appDisplay.js | 363 +++++++++++++++++++++++++++++++++------------------
js/ui/dash.js | 2 +-
src/shell-global.c | 40 ------
src/shell-global.h | 2 -
4 files changed, 239 insertions(+), 168 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 3406ee4..f992404 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -24,8 +24,11 @@ const GLOW_COLOR = new Clutter.Color();
GLOW_COLOR.from_pixel(0x4f6ba4ff);
const GLOW_PADDING = 5;
+
const APP_ICON_SIZE = 48;
-const APP_PADDING = 18;
+const WELL_DEFAULT_COLUMNS = 4;
+const WELL_ITEM_HSPACING = 0;
+const WELL_ITEM_VSPACING = 4;
const MENU_ICON_SIZE = 24;
const MENU_SPACING = 15;
@@ -452,18 +455,6 @@ WellDisplayItem.prototype = {
padding: 1,
border_color: GenericDisplay.ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR,
reactive: true });
- this.actor.connect('enter-event', Lang.bind(this,
- function(o, event) {
- this.actor.border = 1;
- this.actor.padding = 0;
- return false;
- }));
- this.actor.connect('leave-event', Lang.bind(this,
- function(o, event) {
- this.actor.border = 0;
- this.actor.padding = 1;
- return false;
- }));
this.actor._delegate = this;
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
this._handleActivate();
@@ -559,10 +550,6 @@ WellDisplayItem.prototype = {
return this._icon;
},
- getWordWidth: function() {
- return this._wordWidth;
- },
-
setWidth: function(width) {
this._nameBox.width = width + GLOW_PADDING * 2;
}
@@ -570,107 +557,200 @@ WellDisplayItem.prototype = {
Signals.addSignalMethods(WellDisplayItem.prototype);
-function WellArea(width, isFavorite) {
- this._init(width, isFavorite);
+function WellGrid() {
+ this._init();
}
-WellArea.prototype = {
- _init : function(width, isFavorite) {
- this.isFavorite = isFavorite;
+WellGrid.prototype = {
+ _init: function() {
+ this.actor = new Shell.GenericContainer();
- this._grid = new Tidy.Grid({ width: width, row_gap: 4 });
- this._grid._delegate = this;
+ this._separator = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+ border_top: 1,
+ height: 1 });
+ this.actor.add_actor(this._separator);
+ this._separatorIndex = 0;
+ this._cachedSeparatorY = 0;
- this.actor = this._grid;
+ 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));
},
- redisplay: function (infos) {
- let children;
+ _getPreferredWidth: function (grid, forHeight, alloc) {
+ let [itemMin, itemNatural] = this._getItemPreferredWidth();
+ let children = this._getItemChildren();
+ let nColumns;
+ if (children.length < WELL_DEFAULT_COLUMNS)
+ nColumns = children.length;
+ else
+ nColumns = WELL_DEFAULT_COLUMNS;
+ let spacing = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
+ alloc.min_size = itemMin * nColumns + spacing;
+ alloc.natural_size = itemNatural * nColumns + spacing;
+ },
- children = this._grid.get_children();
- children.forEach(Lang.bind(this, function (v) {
- v.destroy();
- }));
+ _getPreferredHeight: function (grid, forWidth, alloc) {
+ let [rows, columns, itemWidth, itemHeight] = this._computeLayout(forWidth);
+ let totalVerticalSpacing = Math.max(rows - 1, 0) * WELL_ITEM_VSPACING;
- this._maxWordWidth = 0;
- let displays = []
- for (let i = 0; i < infos.length; i++) {
- let app = infos[i];
- let display = new WellDisplayItem(app, this.isFavorite);
- displays.push(display);
- let width = display.getWordWidth();
- if (width > this._maxWordWidth)
- this._maxWordWidth = width;
- display.connect('activated', Lang.bind(this, function (display) {
- this.emit('activated', display);
- }));
- this.actor.add_actor(display.actor);
+ let [separatorMin, separatorNatural] = this._separator.get_preferred_height(forWidth);
+ alloc.min_size = alloc.natural_size = rows * itemHeight + totalVerticalSpacing + separatorNatural;
+ },
+
+ _allocate: function (grid, box, flags) {
+ let children = this._getItemChildren();
+ let availWidth = box.x2 - box.x1;
+ let availHeight = box.y2 - box.y1;
+
+ let [rows, columns, itemWidth, itemHeight] = this._computeLayout(availWidth);
+
+ let [separatorMin, separatorNatural] = this._separator.get_preferred_height(-1);
+
+ let x = box.x1;
+ let y = box.y1;
+ let columnIndex = 0;
+ for (let i = 0; i < children.length; i++) {
+ let [childMinWidth, childMinHeight,
+ childNaturalWidth, childNaturalHeight] = children[i].get_preferred_size();
+
+ /* Center the item in its allocation */
+ let width = Math.min(itemWidth, childNaturalWidth);
+ let height = Math.min(itemHeight, childNaturalHeight);
+ let horizSpacing = (itemWidth - width) / 2;
+ let vertSpacing = (itemHeight - height) / 2;
+
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = Math.floor(x + horizSpacing);
+ childBox.y1 = Math.floor(y + vertSpacing);
+ childBox.x2 = childBox.x1 + width;
+ childBox.y2 = childBox.y1 + height;
+ children[i].allocate(childBox, flags);
+
+ let atSeparator = (i == this._separatorIndex - 1);
+
+ columnIndex++;
+ if (columnIndex == columns || atSeparator) {
+ columnIndex = 0;
+ }
+
+ if (columnIndex == 0) {
+ y += itemHeight + WELL_ITEM_VSPACING;
+ x = box.x1;
+ } else {
+ x += itemWidth + WELL_ITEM_HSPACING;
+ }
+
+ if (atSeparator) {
+ y += separatorNatural + WELL_ITEM_VSPACING;
+ }
}
- this._displays = displays;
+
+ let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
+
+ /* Allocate the separator */
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = box.x1;
+ childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
+ this._cachedSeparatorY = childBox.y1;
+ childBox.x2 = box.x2;
+ childBox.y2 = childBox.y1+separatorNatural;
+ this._separator.allocate(childBox, flags);
},
- getWordWidth: function() {
- return this._maxWordWidth;
+ setSeparatorIndex: function (index) {
+ this._separatorIndex = index;
+ this.actor.queue_relayout();
},
- setItemWidth: function(width) {
- for (let i = 0; i < this._displays.length; i++) {
- let display = this._displays[i];
- display.setWidth(width);
+ removeAll: function () {
+ let itemChildren = this._getItemChildren();
+ for (let i = 0; i < itemChildren.length; i++) {
+ itemChildren[i].destroy();
}
+ this._separatorIndex = 0;
},
- // Draggable target interface
- acceptDrop : function(source, actor, x, y, time) {
- let global = Shell.Global.get();
+ isBeforeSeparator: function(x, y) {
+ return y < this._cachedSeparatorY;
+ },
- let id = null;
- if (source instanceof WellDisplayItem) {
- id = source.appInfo.get_id();
- } else if (source instanceof AppDisplayItem) {
- id = source.getId();
- } else if (source instanceof Workspaces.WindowClone) {
- let appMonitor = Shell.AppMonitor.get_default();
- let app = appMonitor.get_window_app(source.metaWindow);
- id = app.get_id();
- if (id === null)
- return false;
- } else {
- return false;
+ _getItemChildren: function () {
+ let children = this.actor.get_children();
+ children.shift();
+ return children;
+ },
+
+ _computeLayout: function (forWidth) {
+ let [itemMinWidth, itemNaturalWidth] = this._getItemPreferredWidth();
+ let columnsNatural;
+ let i;
+ let children = this._getItemChildren();
+ let nColumns;
+ if (children.length < WELL_DEFAULT_COLUMNS)
+ nColumns = children.length;
+ else
+ nColumns = WELL_DEFAULT_COLUMNS;
+
+ if (forWidth >= 0 && forWidth < minWidth) {
+ log("WellGrid: trying to allocate for width " + forWidth + " but min is " + minWidth);
+ /* FIXME - we should fall back to fewer than WELL_DEFAULT_COLUMNS here */
}
- let appSystem = Shell.AppSystem.get_default();
+ let horizSpacingTotal = Math.max(nColumns - 1, 0) * WELL_ITEM_HSPACING;
+ let minWidth = itemMinWidth * nColumns + horizSpacingTotal;
- if (source.isFavorite && (!this.isFavorite)) {
- Mainloop.idle_add(function () {
- appSystem.remove_favorite(id);
- });
- } else if ((!source.isFavorite) && this.isFavorite) {
- Mainloop.idle_add(function () {
- appSystem.add_favorite(id);
- });
+ let lastColumnIndex = nColumns - 1;
+ let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
+ let rows = Math.ceil((children.length + separatorColumns) / nColumns);
+
+ let itemWidth;
+ if (forWidth < 0) {
+ itemWidth = itemNaturalWidth;
} else {
- return false;
+ itemWidth = Math.max(forWidth - horizSpacingTotal, 0) / nColumns;
}
- return true;
+ let itemNaturalHeight = 0;
+ for (let i = 0; i < children.length; i++) {
+ let [childMin, childNatural] = children[i].get_preferred_height(itemWidth);
+ if (childNatural > itemNaturalHeight)
+ itemNaturalHeight = childNatural;
+ }
+
+ return [rows, WELL_DEFAULT_COLUMNS, itemWidth, itemNaturalHeight];
+ },
+
+ _getItemPreferredWidth: function () {
+ let children = this._getItemChildren();
+ let minWidth = 0;
+ let naturalWidth = 0;
+ for (let i = 0; i < children.length; i++) {
+ let [childMin, childNatural] = children[i].get_preferred_width(-1);
+ if (childMin > minWidth)
+ minWidth = childMin;
+ if (childNatural > naturalWidth)
+ naturalWidth = childNatural;
+ }
+ return [minWidth, naturalWidth];
}
}
-Signals.addSignalMethods(WellArea.prototype);
-
-function AppWell(width) {
- this._init(width);
+function AppWell() {
+ this._init();
}
AppWell.prototype = {
- _init : function(width) {
+ _init : function() {
this._menus = [];
this._menuDisplays = [];
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
- spacing: 4,
- width: width });
+ x_align: Big.BoxAlignment.CENTER });
+ this.actor._delegate = this;
+
+ this._grid = new WellGrid();
+ this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
this._appSystem = Shell.AppSystem.get_default();
this._appMonitor = Shell.AppMonitor.get_default();
@@ -685,23 +765,6 @@ AppWell.prototype = {
this._redisplay();
}));
- this._favoritesArea = new WellArea(width, true);
- this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
- Main.overlay.hide();
- }));
- this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
-
- this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
- border_top: 1,
- corner_radius: 3,
- padding_top: GenericDisplay.PREVIEW_BOX_PADDING });
- this._runningArea = new WellArea(width, false);
- this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
- Main.overlay.hide();
- }));
- this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
- this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
-
this._redisplay();
},
@@ -717,36 +780,86 @@ AppWell.prototype = {
return result;
},
- _redisplay: function() {
- /* hardcode here pending some design about how exactly activities behave */
- let contextId = "";
+ _arrayValues: function(array) {
+ return array.reduce(function (values, id, index) {
+ values[id] = index; return values; }, {});
+ },
+
+ _redisplay: function () {
+ this._grid.removeAll();
- let arrayToObject = function(a) {
- let o = {};
- for (let i = 0; i < a.length; i++)
- o[a[i]] = 1;
- return o;
- };
let favoriteIds = this._appSystem.get_favorites();
- let favoriteIdsObject = arrayToObject(favoriteIds);
+ let favoriteIdsHash = this._arrayValues(favoriteIds);
+
+ /* hardcode here pending some design about how exactly desktop contexts behave */
+ let contextId = "";
let runningIds = this._appMonitor.get_running_app_ids(contextId).filter(function (e) {
- return !(e in favoriteIdsObject);
+ return !(e in favoriteIdsHash);
});
let favorites = this._lookupApps(favoriteIds);
let running = this._lookupApps(runningIds);
- this._favoritesArea.redisplay(favorites);
- this._runningArea.redisplay(running);
- let maxWidth = this._favoritesArea.getWordWidth();
- if (this._runningArea.getWordWidth() > maxWidth)
- maxWidth = this._runningArea.getWordWidth();
- this._favoritesArea.setItemWidth(maxWidth);
- this._runningArea.setItemWidth(maxWidth);
- // If it's empty, we hide it so the top border doesn't show up
- if (running.length == 0)
- this._runningBox.hide();
- else
- this._runningBox.show();
+
+ let displays = []
+ this._addApps(favorites, true);
+ this._grid.setSeparatorIndex(favorites.length);
+ this._addApps(running, false);
+ this._displays = displays;
+ },
+
+ _addApps: function(apps) {
+ for (let i = 0; i < apps.length; i++) {
+ let app = apps[i];
+ let display = new WellDisplayItem(app, this.isFavorite);
+ display.connect('activated', Lang.bind(this, function (display) {
+ Main.overlay.hide();
+ }));
+ this._grid.actor.add_actor(display.actor);
+ }
+ },
+
+ // Draggable target interface
+ acceptDrop : function(source, actor, x, y, time) {
+ let global = Shell.Global.get();
+
+ let id = null;
+ if (source instanceof WellDisplayItem) {
+ id = source.appInfo.get_id();
+ } else if (source instanceof AppDisplayItem) {
+ id = source.getId();
+ } else if (source instanceof Workspaces.WindowClone) {
+ let appMonitor = Shell.AppMonitor.get_default();
+ let app = appMonitor.get_window_app(source.metaWindow);
+ id = app.get_id();
+ }
+
+ if (id == null) {
+ return false;
+ }
+
+ let appSystem = Shell.AppSystem.get_default();
+ let favoriteIds = this._appSystem.get_favorites();
+ let favoriteIdsObject = this._arrayValues(favoriteIds);
+
+ let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
+ y - this._grid.actor.y);
+ let srcIsFavorite = (id in favoriteIdsObject);
+
+ if (srcIsFavorite && (!dropIsFavorite)) {
+ Mainloop.idle_add(function () {
+ appSystem.remove_favorite(id);
+ return false;
+ });
+ } else if ((!srcIsFavorite) && dropIsFavorite) {
+ Mainloop.idle_add(function () {
+ appSystem.add_favorite(id);
+ return false;
+ });
+ } else {
+ return false;
+ }
+
+ return true;
}
};
diff --git a/js/ui/dash.js b/js/ui/dash.js
index c09307c..9e01eab 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -428,7 +428,7 @@ Dash.prototype = {
this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
- this._appWell = new AppDisplay.AppWell(this._width);
+ this._appWell = new AppDisplay.AppWell();
this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
this._moreAppsPane = null;
diff --git a/src/shell-global.c b/src/shell-global.c
index 648d1c1..7dd5c44 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -911,43 +911,3 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
return clutter_clone_new (global->root_pixmap);
}
-
-/**
- * shell_global_get_max_word_width:
- * @global:
- * @ref: Actor to use for font information
- * @text: Text to analyze
- * @font: Given font size to use
- *
- * Compute the largest pixel size among the individual words in text, when
- * rendered in the given font.
- */
-guint
-shell_global_get_max_word_width (ShellGlobal *global, ClutterActor *ref, const char *text, const char *font)
-{
- PangoLayout *layout;
- PangoFontDescription *fontdesc;
- char **components;
- char **iter;
- gint max;
-
- layout = clutter_actor_create_pango_layout (ref, NULL);
- fontdesc = pango_font_description_from_string (font);
- pango_layout_set_font_description (layout, fontdesc);
- pango_font_description_free (fontdesc);
-
- max = 0;
- components = g_strsplit (text, " ", 0);
- for (iter = components; *iter; iter++)
- {
- char *component = *iter;
- gint width, height;
- pango_layout_set_text (layout, component, -1);
- pango_layout_get_pixel_size (layout, &width, &height);
- if (width > max)
- max = width;
- }
- g_object_unref (layout);
- g_strfreev (components);
- return (guint)max;
-}
diff --git a/src/shell-global.h b/src/shell-global.h
index 386afbb..52d818d 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -73,8 +73,6 @@ void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta,
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
-guint shell_global_get_max_word_width (ShellGlobal *global, ClutterActor *ref, const char *text, const char *font);
-
G_END_DECLS
#endif /* __SHELL_GLOBAL_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]