[appDisplay] Factor out WellGrid/AppIcon
All mockups now use a representation for documents/places similar to the one used for applications. Rename AppIcon to BaseIcon and move its code together with WellGrid out of appDisplay to stress their general usefulness. https://bugzilla.gnome.org/show_bug.cgi?id=625887
This commit is contained in:
parent
8f0f159960
commit
8c1bf346a9
@ -547,7 +547,7 @@ StTooltip {
|
||||
width: 440px;
|
||||
}
|
||||
|
||||
#dashAppWell {
|
||||
.icon-grid {
|
||||
spacing: 6px;
|
||||
-shell-grid-item-size: 70px;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ nobase_dist_js_DATA = \
|
||||
ui/environment.js \
|
||||
ui/extensionSystem.js \
|
||||
ui/genericDisplay.js \
|
||||
ui/iconGrid.js \
|
||||
ui/lightbox.js \
|
||||
ui/link.js \
|
||||
ui/lookingGlass.js \
|
||||
@ -46,4 +47,4 @@ nobase_dist_js_DATA = \
|
||||
ui/windowManager.js \
|
||||
ui/workspace.js \
|
||||
ui/workspacesView.js \
|
||||
ui/workspaceSwitcherPopup.js
|
||||
ui/workspaceSwitcherPopup.js
|
||||
|
@ -12,6 +12,7 @@ const _ = Gettext.gettext;
|
||||
|
||||
const AppFavorites = imports.ui.appFavorites;
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -20,8 +21,7 @@ const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const APPICON_SIZE = 48;
|
||||
const WELL_MAX_COLUMNS = 8;
|
||||
const WELL_MAX_COLUMNS = 16;
|
||||
const WELL_MAX_SEARCH_ROWS = 1;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
|
||||
@ -32,7 +32,7 @@ function AlphabeticalView() {
|
||||
AlphabeticalView.prototype = {
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true });
|
||||
this._grid = new WellGrid();
|
||||
this._grid = new IconGrid.IconGrid();
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
},
|
||||
@ -222,7 +222,7 @@ AppSearchResultDisplay.prototype = {
|
||||
|
||||
_init: function (provider) {
|
||||
Search.SearchResultDisplay.prototype._init.call(this, provider);
|
||||
this._grid = new WellGrid({ rowLimit: WELL_MAX_SEARCH_ROWS });
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: WELL_MAX_SEARCH_ROWS });
|
||||
this.actor = new St.Bin({ name: 'dashAppSearchResults',
|
||||
x_align: St.Align.START });
|
||||
this.actor.set_child(this._grid.actor);
|
||||
@ -368,23 +368,18 @@ function AppIcon(app) {
|
||||
}
|
||||
|
||||
AppIcon.prototype = {
|
||||
__proto__: IconGrid.BaseIcon.prototype,
|
||||
|
||||
_init : function(app) {
|
||||
this.app = app;
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'app-icon',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
let label = this.app.get_name();
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this.actor.set_child(box);
|
||||
IconGrid.BaseIcon.prototype._init.call(this, label);
|
||||
},
|
||||
|
||||
this.icon = this.app.create_icon_texture(APPICON_SIZE);
|
||||
|
||||
box.add(this.icon, { expand: true, x_fill: false, y_fill: false });
|
||||
|
||||
this._name = new St.Label({ text: this.app.get_name() });
|
||||
box.add_actor(this._name);
|
||||
createIcon: function(iconSize) {
|
||||
return this.app.create_icon_texture(iconSize);
|
||||
}
|
||||
};
|
||||
|
||||
@ -587,7 +582,7 @@ AppWellIcon.prototype = {
|
||||
},
|
||||
|
||||
getDragActor: function() {
|
||||
return this.app.create_icon_texture(APPICON_SIZE);
|
||||
return this.app.create_icon_texture(this._icon.iconSize);
|
||||
},
|
||||
|
||||
// Returns the original actor that should align with the actor
|
||||
@ -763,157 +758,6 @@ AppIconMenu.prototype = {
|
||||
};
|
||||
Signals.addSignalMethods(AppIconMenu.prototype);
|
||||
|
||||
function WellGrid(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
WellGrid.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { rowLimit: null });
|
||||
this._rowLimit = params.rowLimit;
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'dashAppWell', vertical: true });
|
||||
// Pulled from CSS, but hardcode some defaults here
|
||||
this._spacing = 0;
|
||||
this._item_size = 48;
|
||||
this._grid = new Shell.GenericContainer();
|
||||
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
|
||||
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this._grid.connect('allocate', Lang.bind(this, this._allocate));
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = children.length;
|
||||
let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
|
||||
// Kind of a lie, but not really an issue right now. If
|
||||
// we wanted to support some sort of hidden/overflow that would
|
||||
// need higher level design
|
||||
alloc.min_size = this._item_size;
|
||||
alloc.natural_size = nColumns * this._item_size + totalSpacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._grid.get_children();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
else
|
||||
nRows = 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
||||
let height = nRows * this._item_size + totalSpacing;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
let children = this._grid.get_children();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||
|
||||
let overallPaddingX = Math.floor((availWidth - usedWidth) / 2);
|
||||
|
||||
let x = box.x1 + overallPaddingX;
|
||||
let y = box.y1;
|
||||
let columnIndex = 0;
|
||||
let rowIndex = 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 horizontally */
|
||||
let width = Math.min(this._item_size, childNaturalWidth);
|
||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||
let height = Math.min(this._item_size, childNaturalHeight);
|
||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
let _x = box.x2 - (x + width);
|
||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||
} else {
|
||||
childBox.x1 = Math.floor(x + childXSpacing);
|
||||
}
|
||||
childBox.y1 = Math.floor(y + childYSpacing);
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
|
||||
if (this._rowLimit && rowIndex >= this._rowLimit) {
|
||||
this._grid.set_skip_paint(children[i], true);
|
||||
} else {
|
||||
children[i].allocate(childBox, flags);
|
||||
this._grid.set_skip_paint(children[i], false);
|
||||
}
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == nColumns) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += this._item_size + this._spacing;
|
||||
x = box.x1 + overallPaddingX;
|
||||
} else {
|
||||
x += this._item_size + this._spacing;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
while (nColumns < WELL_MAX_COLUMNS &&
|
||||
(usedWidth + this._item_size <= forWidth)) {
|
||||
usedWidth += this._item_size + this._spacing;
|
||||
nColumns += 1;
|
||||
}
|
||||
|
||||
if (nColumns > 0)
|
||||
usedWidth -= this._spacing;
|
||||
|
||||
return [nColumns, usedWidth];
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let [success, len] = themeNode.get_length('spacing', false);
|
||||
if (success)
|
||||
this._spacing = len;
|
||||
[success, len] = themeNode.get_length('-shell-grid-item-size', false);
|
||||
if (success)
|
||||
this._item_size = len;
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
this._grid.get_children().forEach(Lang.bind(this, function (child) {
|
||||
child.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
addItem: function(actor) {
|
||||
this._grid.add_actor(actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this._grid.get_children()[index];
|
||||
},
|
||||
|
||||
visibleItemsCount: function() {
|
||||
return this._grid.get_children().length - this._grid.get_n_skip_paint();
|
||||
}
|
||||
};
|
||||
|
||||
function AppWell() {
|
||||
this._init();
|
||||
}
|
||||
@ -926,7 +770,7 @@ AppWell.prototype = {
|
||||
|
||||
this._favorites = [];
|
||||
|
||||
this._grid = new WellGrid();
|
||||
this._grid = new IconGrid.IconGrid();
|
||||
this.actor = this._grid.actor;
|
||||
this.actor._delegate = this;
|
||||
|
||||
|
219
js/ui/iconGrid.js
Normal file
219
js/ui/iconGrid.js
Normal file
@ -0,0 +1,219 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const ICON_SIZE = 48;
|
||||
|
||||
|
||||
function BaseIcon(label, createIcon) {
|
||||
this._init(label, createIcon);
|
||||
}
|
||||
|
||||
BaseIcon.prototype = {
|
||||
_init : function(label, createIcon) {
|
||||
this.actor = new St.Bin({ style_class: 'overview-icon',
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.actor.connect('style-changed',
|
||||
Lang.bind(this, this._onStyleChanged));
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this.actor.set_child(box);
|
||||
|
||||
this.iconSize = ICON_SIZE;
|
||||
this._iconBin = new St.Bin();
|
||||
|
||||
box.add(this._iconBin, { expand: true, x_fill: false, y_fill: false });
|
||||
|
||||
this._name = new St.Label({ text: label });
|
||||
box.add_actor(this._name);
|
||||
|
||||
if (createIcon)
|
||||
this.createIcon = createIcon;
|
||||
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
this._iconBin.set_child(this.icon);
|
||||
},
|
||||
|
||||
// This can be overridden by a subclass, or by the createIcon
|
||||
// parameter to _init()
|
||||
createIcon: function(size) {
|
||||
throw new Error('no implementation of createIcon in ' + this);
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
let [success, len] = node.get_length('icon-size', false);
|
||||
if (success) {
|
||||
if (len == this.iconSize)
|
||||
return;
|
||||
|
||||
this.icon.destroy();
|
||||
this.iconSize = len;
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
this._iconBin.set_child(this.icon);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function IconGrid(params) {
|
||||
this._init(params);
|
||||
}
|
||||
|
||||
IconGrid.prototype = {
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { rowLimit: null, columnLimit: null });
|
||||
this._rowLimit = params.rowLimit;
|
||||
this._colLimit = params.columnLimit;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
||||
vertical: true });
|
||||
// Pulled from CSS, but hardcode some defaults here
|
||||
this._spacing = 0;
|
||||
this._item_size = ICON_SIZE;
|
||||
this._grid = new Shell.GenericContainer();
|
||||
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||
|
||||
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this._grid.connect('allocate', Lang.bind(this, this._allocate));
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
||||
children.length)
|
||||
: children.length;
|
||||
let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
|
||||
// Kind of a lie, but not really an issue right now. If
|
||||
// we wanted to support some sort of hidden/overflow that would
|
||||
// need higher level design
|
||||
alloc.min_size = this._item_size;
|
||||
alloc.natural_size = nColumns * this._item_size + totalSpacing;
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
let children = this._grid.get_children();
|
||||
let [nColumns, usedWidth] = this._computeLayout(forWidth);
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
else
|
||||
nRows = 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
||||
let height = nRows * this._item_size + totalSpacing;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
let children = this._grid.get_children();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||
|
||||
let overallPaddingX = Math.floor((availWidth - usedWidth) / 2);
|
||||
|
||||
let x = box.x1 + overallPaddingX;
|
||||
let y = box.y1;
|
||||
let columnIndex = 0;
|
||||
let rowIndex = 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 horizontally */
|
||||
let width = Math.min(this._item_size, childNaturalWidth);
|
||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||
let height = Math.min(this._item_size, childNaturalHeight);
|
||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
|
||||
let _x = box.x2 - (x + width);
|
||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||
} else {
|
||||
childBox.x1 = Math.floor(x + childXSpacing);
|
||||
}
|
||||
childBox.y1 = Math.floor(y + childYSpacing);
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
|
||||
if (this._rowLimit && rowIndex >= this._rowLimit) {
|
||||
this._grid.set_skip_paint(children[i], true);
|
||||
} else {
|
||||
children[i].allocate(childBox, flags);
|
||||
this._grid.set_skip_paint(children[i], false);
|
||||
}
|
||||
|
||||
columnIndex++;
|
||||
if (columnIndex == nColumns) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += this._item_size + this._spacing;
|
||||
x = box.x1 + overallPaddingX;
|
||||
} else {
|
||||
x += this._item_size + this._spacing;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_computeLayout: function (forWidth) {
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||
(usedWidth + this._item_size <= forWidth)) {
|
||||
usedWidth += this._item_size + this._spacing;
|
||||
nColumns += 1;
|
||||
}
|
||||
|
||||
if (nColumns > 0)
|
||||
usedWidth -= this._spacing;
|
||||
|
||||
return [nColumns, usedWidth];
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
let [success, len] = themeNode.get_length('spacing', false);
|
||||
if (success)
|
||||
this._spacing = len;
|
||||
[success, len] = themeNode.get_length('-shell-grid-item-size', false);
|
||||
if (success)
|
||||
this._item_size = len;
|
||||
this._grid.queue_relayout();
|
||||
},
|
||||
|
||||
removeAll: function () {
|
||||
this._grid.get_children().forEach(Lang.bind(this, function (child) {
|
||||
child.destroy();
|
||||
}));
|
||||
},
|
||||
|
||||
addItem: function(actor) {
|
||||
this._grid.add_actor(actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this._grid.get_children()[index];
|
||||
},
|
||||
|
||||
visibleItemsCount: function() {
|
||||
return this._grid.get_children().length - this._grid.get_n_skip_paint();
|
||||
}
|
||||
};
|
@ -11,7 +11,6 @@ const Signals = imports.signals;
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const Calendar = imports.ui.calendar;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -219,10 +218,11 @@ AppMenuButton.prototype = {
|
||||
|
||||
this._updateId = 0;
|
||||
this._animationStep = 0;
|
||||
this._clipWidth = AppDisplay.APPICON_SIZE / 2;
|
||||
this._clipWidth = PANEL_ICON_SIZE;
|
||||
this._direction = SPINNER_SPEED;
|
||||
|
||||
this._spinner = new AnimatedIcon('process-working.png', 24);
|
||||
this._spinner = new AnimatedIcon('process-working.png',
|
||||
PANEL_ICON_SIZE);
|
||||
this._container.add_actor(this._spinner.actor);
|
||||
this._spinner.actor.lower_bottom();
|
||||
|
||||
@ -304,7 +304,7 @@ AppMenuButton.prototype = {
|
||||
}
|
||||
if (this._animationStep > 1)
|
||||
this._animationStep = 1;
|
||||
this._clipWidth = this._label.actor.width - (this._label.actor.width - AppDisplay.APPICON_SIZE / 2) * (1 - this._animationStep);
|
||||
this._clipWidth = this._label.actor.width - (this._label.actor.width - PANEL_ICON_SIZE) * (1 - this._animationStep);
|
||||
if (this.actor.get_direction() == St.TextDirection.LTR) {
|
||||
this._label.actor.set_clip(0, 0, this._clipWidth + this._shadow.width, this.actor.height);
|
||||
} else {
|
||||
@ -454,7 +454,7 @@ AppMenuButton.prototype = {
|
||||
|
||||
let targetApp = this._focusedApp != null ? this._focusedApp : this._lastStartedApp;
|
||||
if (targetApp != null) {
|
||||
let icon = targetApp.get_faded_icon(AppDisplay.APPICON_SIZE);
|
||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
||||
|
||||
this._label.setText(targetApp.get_name());
|
||||
// TODO - _quit() doesn't really work on apps in state STARTING yet
|
||||
|
Loading…
Reference in New Issue
Block a user