iconGrid: Also adapt icon size to available space

Similar to adapting the spacing dynamically to the available
space we already do, scale down icon sizes if the grid is too
small to fit the requested minimum number of rows/columns.

https://bugzilla.gnome.org/show_bug.cgi?id=706081
This commit is contained in:
Carlos Soriano 2013-08-15 10:38:17 +02:00
parent 792b963bda
commit d58f0646cf
2 changed files with 90 additions and 37 deletions

View File

@ -542,10 +542,9 @@ const AllView = new Lang.Class({
let oldNPages = this._grid.nPages(); let oldNPages = this._grid.nPages();
this._updateAdjustment(availHeight); this._updateAdjustment(availHeight);
this._grid.updateSpacingForSize(availWidth, availHeight);
this._grid.computePages(availWidth, availHeight); this._grid.adaptToSize(availWidth, availHeight);
// Make sure the view doesn't have a bad adjustment value after screen size changes
// and therefore the pages computation.
if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) { if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) {
this._verticalAdjustment.value = 0; this._verticalAdjustment.value = 0;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
@ -597,7 +596,7 @@ const FrequentView = new Lang.Class({
box = this._grid.actor.get_theme_node().get_content_box(box); box = this._grid.actor.get_theme_node().get_content_box(box);
let availWidth = box.x2 - box.x1; let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1; let availHeight = box.y2 - box.y1;
this._grid.updateSpacingForSize(availWidth, availHeight); this._grid.adaptToSize(availWidth, availHeight);
} }
}); });
@ -935,7 +934,7 @@ const FolderView = new Lang.Class({
this._parentAvailableWidth = width; this._parentAvailableWidth = width;
this._parentAvailableHeight = height; this._parentAvailableHeight = height;
this._grid.updateSpacingForSize(width, height); this._grid.adaptToSize(width, height);
// Set extra padding to avoid popup or close button being cut off // Set extra padding to avoid popup or close button being cut off
this._grid.topPadding = Math.max(this._grid.topPadding - this._offsetForEachSide, 0); this._grid.topPadding = Math.max(this._grid.topPadding - this._offsetForEachSide, 0);
@ -998,7 +997,7 @@ const FolderIcon = new Lang.Class({
let label = this._dir.get_name(); let label = this._dir.get_name();
this.icon = new IconGrid.BaseIcon(label, this.icon = new IconGrid.BaseIcon(label,
{ createIcon: Lang.bind(this, this._createIcon) }); { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.actor.set_child(this.icon.actor); this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label; this.actor.label_actor = this.icon.label;
@ -1019,8 +1018,8 @@ const FolderIcon = new Lang.Class({
})); }));
}, },
_createIcon: function(size) { _createIcon: function(iconSize) {
return this.view.createFolderIcon(size, this); return this.view.createFolderIcon(iconSize, this);
}, },
_popupHeight: function() { _popupHeight: function() {
@ -1225,6 +1224,7 @@ const AppIcon = new Lang.Class({
iconParams = {}; iconParams = {};
iconParams['createIcon'] = Lang.bind(this, this._createIcon); iconParams['createIcon'] = Lang.bind(this, this._createIcon);
iconParams['setSizeManually'] = true;
this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams); this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
this.actor.set_child(this.icon.actor); this.actor.set_child(this.icon.actor);

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
@ -10,7 +11,8 @@ const Lang = imports.lang;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ICON_SIZE = 48; const ICON_SIZE = 96;
const MIN_ICON_SIZE = 16;
const EXTRA_SPACE_ANIMATION_TIME = 0.25; const EXTRA_SPACE_ANIMATION_TIME = 0.25;
@ -209,6 +211,7 @@ const IconGrid = new Lang.Class({
// Pulled from CSS, but hardcode some defaults here // Pulled from CSS, but hardcode some defaults here
this._spacing = 0; this._spacing = 0;
this._hItemSize = this._vItemSize = ICON_SIZE; this._hItemSize = this._vItemSize = ICON_SIZE;
this._fixedHItemSize = this._fixedVItemSize = undefined;
this._grid = new Shell.GenericContainer(); this._grid = new Shell.GenericContainer();
this.actor.add(this._grid, { expand: true, y_align: St.Align.START }); this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
@ -232,8 +235,8 @@ const IconGrid = new Lang.Class({
// Kind of a lie, but not really an issue right now. If // Kind of a lie, but not really an issue right now. If
// we wanted to support some sort of hidden/overflow that would // we wanted to support some sort of hidden/overflow that would
// need higher level design // need higher level design
alloc.min_size = this._hItemSize + this.leftPadding + this.rightPadding; alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding;
alloc.natural_size = nColumns * this._hItemSize + totalSpacing + this.leftPadding + this.rightPadding; alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
}, },
_getVisibleChildren: function() { _getVisibleChildren: function() {
@ -265,7 +268,7 @@ const IconGrid = new Lang.Class({
if (this._rowLimit) if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit); nRows = Math.min(nRows, this._rowLimit);
let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing(); let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing();
let height = nRows * this._vItemSize + totalSpacing + this.topPadding + this.bottomPadding; let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding;
alloc.min_size = height; alloc.min_size = height;
alloc.natural_size = height; alloc.natural_size = height;
}, },
@ -318,10 +321,10 @@ const IconGrid = new Lang.Class({
} }
if (columnIndex == 0) { if (columnIndex == 0) {
y += this._vItemSize + spacing; y += this._getVItemSize() + spacing;
x = box.x1 + leftEmptySpace + this.leftPadding; x = box.x1 + leftEmptySpace + this.leftPadding;
} else { } else {
x += this._hItemSize + spacing; x += this._getHItemSize() + spacing;
} }
} }
}, },
@ -331,9 +334,9 @@ const IconGrid = new Lang.Class({
child.get_preferred_size(); child.get_preferred_size();
/* Center the item in its allocation horizontally */ /* Center the item in its allocation horizontally */
let width = Math.min(this._hItemSize, childNaturalWidth); let width = Math.min(this._getHItemSize(), childNaturalWidth);
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2; let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
let height = Math.min(this._vItemSize, childNaturalHeight); let height = Math.min(this._getVItemSize(), childNaturalHeight);
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2; let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
let childBox = new Clutter.ActorBox(); let childBox = new Clutter.ActorBox();
@ -363,8 +366,8 @@ const IconGrid = new Lang.Class({
let spacing = this._getSpacing(); let spacing = this._getSpacing();
while ((this._colLimit == null || nColumns < this._colLimit) && while ((this._colLimit == null || nColumns < this._colLimit) &&
(usedWidth + this._hItemSize <= forWidth)) { (usedWidth + this._getHItemSize() <= forWidth)) {
usedWidth += this._hItemSize + spacing; usedWidth += this._getHItemSize() + spacing;
nColumns += 1; nColumns += 1;
} }
@ -392,15 +395,19 @@ const IconGrid = new Lang.Class({
}, },
rowsForHeight: function(forHeight) { rowsForHeight: function(forHeight) {
return Math.floor((forHeight -(this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._vItemSize + this._getSpacing())); return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
}, },
usedHeightForNRows: function(nRows) { usedHeightForNRows: function(nRows) {
return (this._vItemSize + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding; return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
}, },
usedWidth: function(forWidth) { usedWidth: function(forWidth) {
let usedWidth = this.columnsForWidth(forWidth) * (this._hItemSize + this._getSpacing()); return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
},
usedWidthForNColumns: function(columns) {
let usedWidth = columns * (this._getHItemSize() + this._getSpacing());
usedWidth -= this._getSpacing(); usedWidth -= this._getSpacing();
return usedWidth + this.leftPadding + this.rightPadding; return usedWidth + this.leftPadding + this.rightPadding;
}, },
@ -439,13 +446,17 @@ const IconGrid = new Lang.Class({
return this._fixedSpacing ? this._fixedSpacing : this._spacing; return this._fixedSpacing ? this._fixedSpacing : this._spacing;
}, },
/** _getHItemSize: function() {
* This function must to be called before iconGrid allocation, return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
* to know how much spacing can the grid has },
*/
updateSpacingForSize: function(availWidth, availHeight) { _getVItemSize: function() {
let maxEmptyVArea = availHeight - this._minRows * this._vItemSize; return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
let maxEmptyHArea = availWidth - this._minColumns * this._hItemSize; },
_updateSpacingForSize: function(availWidth, availHeight) {
let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
let maxHSpacing, maxVSpacing; let maxHSpacing, maxVSpacing;
if (this._padWithSpacing) { if (this._padWithSpacing) {
@ -467,13 +478,51 @@ const IconGrid = new Lang.Class({
let maxSpacing = Math.min(maxHSpacing, maxVSpacing); let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
// Limit spacing to the item size // Limit spacing to the item size
maxSpacing = Math.min(maxSpacing, Math.min(this._vItemSize, this._hItemSize)); maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
// The minimum spacing, regardless of whether it satisfies the row/column minima, // The minimum spacing, regardless of whether it satisfies the row/columng minima,
// is the spacing we get from CSS. // is the spacing we get from CSS.
let spacing = Math.max(this._spacing, maxSpacing); let spacing = Math.max(this._spacing, maxSpacing);
this.setSpacing(spacing); this.setSpacing(spacing);
if (this._padWithSpacing) if (this._padWithSpacing)
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing; this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
},
/**
* This function must to be called before iconGrid allocation,
* to know how much spacing can the grid has
*/
adaptToSize: function(availWidth, availHeight) {
this._fixedHItemSize = this._hItemSize;
this._fixedVItemSize = this._vItemSize;
this._updateSpacingForSize(availWidth, availHeight);
let spacing = this._getSpacing();
if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
: Math.ceil(neededHeight / this._minRows);
this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
if (this._fixedHItemSize < MIN_ICON_SIZE)
this._fixedHItemSize = MIN_ICON_SIZE;
if (this._fixedVItemSize < MIN_ICON_SIZE)
this._fixedVItemSize = MIN_ICON_SIZE;
this._updateSpacingForSize(availWidth, availHeight);
}
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
},
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
_updateChildrenScale: function(scale) {
for (let i in this._items) {
let newIconSize = Math.floor(ICON_SIZE * scale);
this._items[i].icon.setIconSize(newIconSize);
}
} }
}); });
@ -535,17 +584,16 @@ const PaginatedIconGrid = new Lang.Class({
rowIndex++; rowIndex++;
} }
if (columnIndex == 0) { if (columnIndex == 0) {
y += this._vItemSize + spacing; y += this._getVItemSize() + spacing;
if ((i + 1) % this._childrenPerPage == 0) if ((i + 1) % this._childrenPerPage == 0)
y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding; y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
x = box.x1 + leftEmptySpace + this.leftPadding; x = box.x1 + leftEmptySpace + this.leftPadding;
} else { } else
x += this._hItemSize + spacing; x += this._getHItemSize() + spacing;
}
} }
}, },
computePages: function (availWidthPerPage, availHeightPerPage) { _computePages: function (availWidthPerPage, availHeightPerPage) {
let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage); let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
let nRows; let nRows;
let children = this._getVisibleChildren(); let children = this._getVisibleChildren();
@ -564,6 +612,11 @@ const PaginatedIconGrid = new Lang.Class({
this._childrenPerPage = nColumns * this._rowsPerPage; this._childrenPerPage = nColumns * this._rowsPerPage;
}, },
adaptToSize: function(availWidth, availHeight) {
this.parent(availWidth, availHeight);
this._computePages(availWidth, availHeight);
},
_availableHeightPerPageForItems: function() { _availableHeightPerPageForItems: function() {
return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding); return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
}, },