Bug 574117 - Allow paging through the application and document results in the overlay
Add a display control that contains page numbers for the result pages and is shown in the expanded results view. All of the page selection and switching is handled by the GenericDisplay which exposes a displayControl actor, which is then displayed by the Sideshow.
This commit is contained in:
parent
8b1f732228
commit
5af37f140e
@ -166,16 +166,15 @@ AppDisplay.prototype = {
|
||||
|
||||
// Sets the list of the displayed items based on the list of DEFAULT_APPLICATIONS.
|
||||
_setDefaultList : function() {
|
||||
this._removeAllDisplayItems();
|
||||
let added = 0;
|
||||
for (let i = 0; i < DEFAULT_APPLICATIONS.length && added < this._maxItems; i++) {
|
||||
this._matchedItems = [];
|
||||
for (let i = 0; i < DEFAULT_APPLICATIONS.length; i++) {
|
||||
let appId = DEFAULT_APPLICATIONS[i];
|
||||
let appInfo = this._allItems[appId];
|
||||
if (appInfo) {
|
||||
this._addDisplayItem(appId);
|
||||
added += 1;
|
||||
this._matchedItems.push(appId);
|
||||
}
|
||||
}
|
||||
this._displayMatchedItems(true);
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on the alphabetical order
|
||||
|
@ -134,8 +134,6 @@ DocDisplay.prototype = {
|
||||
|
||||
// Sets the list of the displayed items based on how recently they were last visited.
|
||||
_setDefaultList : function() {
|
||||
this._removeAllDisplayItems();
|
||||
|
||||
// It seems to be an implementation detail of the Mozilla JavaScript that object
|
||||
// properties are returned during the iteration in the same order in which they were
|
||||
// defined, but it is not a guarantee according to this
|
||||
@ -150,21 +148,23 @@ DocDisplay.prototype = {
|
||||
// them once when they are returned by this._recentManager.get_items() to avoid having to do
|
||||
// this sorting each time, but the sorting seems to be very fast anyway, so there is no need
|
||||
// to introduce an additional class variable.
|
||||
let docIds = [];
|
||||
this._matchedItems = [];
|
||||
let docIdsToRemove = [];
|
||||
for (docId in this._allItems) {
|
||||
docIds.push(docId);
|
||||
// this._allItems[docId].exists() checks if the resource still exists
|
||||
if (this._allItems[docId].exists())
|
||||
this._matchedItems.push(docId);
|
||||
else
|
||||
docIdsToRemove.push(docId);
|
||||
}
|
||||
docIds.sort(Lang.bind(this, function (a,b) { return this._compareItems(a,b); }));
|
||||
|
||||
let added = 0;
|
||||
for (let i = 0; i < docIds.length && added < this._maxItems; i++) {
|
||||
let docInfo = this._allItems[docIds[i]];
|
||||
// docInfo.exists() checks if the resource still exists
|
||||
if (docInfo.exists()) {
|
||||
this._addDisplayItem(docIds[i]);
|
||||
added += 1;
|
||||
}
|
||||
for (docId in docIdsToRemove) {
|
||||
delete this._allItems[docId];
|
||||
}
|
||||
|
||||
this._matchedItems.sort(Lang.bind(this, function (a,b) { return this._compareItems(a,b); }));
|
||||
|
||||
this._displayMatchedItems(true);
|
||||
},
|
||||
|
||||
// Compares items associated with the item ids based on how recently the items
|
||||
|
@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
|
||||
const Tidy = imports.gi.Tidy;
|
||||
|
||||
const DND = imports.ui.dnd;
|
||||
const Link = imports.ui.link;
|
||||
|
||||
const ITEM_DISPLAY_NAME_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_NAME_COLOR.from_pixel(0xffffffff);
|
||||
@ -20,11 +21,14 @@ const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000);
|
||||
const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color();
|
||||
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x00ff0055);
|
||||
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
|
||||
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
|
||||
|
||||
const ITEM_DISPLAY_HEIGHT = 50;
|
||||
const ITEM_DISPLAY_ICON_SIZE = 48;
|
||||
const ITEM_DISPLAY_PADDING = 1;
|
||||
const DEFAULT_COLUMN_GAP = 6;
|
||||
const LABEL_HEIGHT = 16;
|
||||
|
||||
/* This is a virtual class that represents a single display item containing
|
||||
* a name, a description, and an icon. It allows selecting an item and represents
|
||||
@ -180,21 +184,30 @@ GenericDisplay.prototype = {
|
||||
if (this._columnGap == null)
|
||||
this._columnGap = DEFAULT_COLUMN_GAP;
|
||||
|
||||
this._maxItems = null;
|
||||
this._maxItemsPerPage = null;
|
||||
this._setDimensionsAndMaxItems(width, height);
|
||||
this._grid = new Tidy.Grid({width: this._width, height: this._height});
|
||||
this._grid.column_major = true;
|
||||
this._grid.column_gap = this._columnGap;
|
||||
// map<itemId, Object> where Object represents the item info
|
||||
this._allItems = {};
|
||||
// an array of itemIds of items that match the current request
|
||||
// in the order in which the items should be displayed
|
||||
this._matchedItems = [];
|
||||
// map<itemId, GenericDisplayItem>
|
||||
this._displayedItems = {};
|
||||
this._displayedItemsCount = 0;
|
||||
this._pageDisplayed = 0;
|
||||
// GenericDisplayItem
|
||||
this._activatedItem = null;
|
||||
this._selectedIndex = -1;
|
||||
this._keepDisplayCurrent = false;
|
||||
this.actor = this._grid;
|
||||
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
|
||||
corner_radius: 4,
|
||||
height: 24,
|
||||
spacing: 12,
|
||||
orientation: Big.BoxOrientation.HORIZONTAL});
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
@ -291,7 +304,7 @@ GenericDisplay.prototype = {
|
||||
this._setDimensionsAndMaxItems(width, height);
|
||||
this._grid.width = this._width;
|
||||
this._grid.height = this._height;
|
||||
this._redisplay();
|
||||
this._displayMatchedItems(true);
|
||||
},
|
||||
|
||||
// Updates the displayed items and makes the display actor visible.
|
||||
@ -310,6 +323,38 @@ GenericDisplay.prototype = {
|
||||
|
||||
//// Protected methods ////
|
||||
|
||||
/*
|
||||
* Displays items that match the current request and should show up on the current page.
|
||||
* Updates the display control to reflect the matched items set and the page selected.
|
||||
*
|
||||
* matchedItemsChanged - indicates if the set of the matched items changed prior to the
|
||||
* request. If it did, the current page is reset to 0 and the display
|
||||
* control is updated.
|
||||
*/
|
||||
_displayMatchedItems: function(matchedItemsChanged) {
|
||||
// When generating a new list to display, we first remove all the old
|
||||
// displayed items which will unset the selection. So we need
|
||||
// to keep a flag which indicates if this display had the selection.
|
||||
let hadSelected = this.hasSelected();
|
||||
|
||||
if (matchedItemsChanged)
|
||||
this._pageDisplayed = 0;
|
||||
|
||||
this._removeAllDisplayItems();
|
||||
|
||||
for (let i = this._maxItemsPerPage * this._pageDisplayed; i < this._matchedItems.length && i < this._maxItemsPerPage * (this._pageDisplayed + 1); i++) {
|
||||
|
||||
this._addDisplayItem(this._matchedItems[i]);
|
||||
}
|
||||
|
||||
if (hadSelected) {
|
||||
this._selectedIndex = -1;
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
this._updateDisplayControl(matchedItemsChanged);
|
||||
},
|
||||
|
||||
// Creates a display item based on the information associated with itemId
|
||||
// and adds it to the displayed items.
|
||||
_addDisplayItem : function(itemId) {
|
||||
@ -359,22 +404,12 @@ GenericDisplay.prototype = {
|
||||
if (!this._keepDisplayCurrent)
|
||||
return;
|
||||
|
||||
// When generating a new list to display, we first remove all the old
|
||||
// displayed items which will unset the selection. So we need
|
||||
// to keep a flag which indicates if this display had the selection.
|
||||
let hadSelected = this.hasSelected();
|
||||
|
||||
this._refreshCache();
|
||||
if (!this._search)
|
||||
this._setDefaultList();
|
||||
else
|
||||
this._doSearchFilter();
|
||||
|
||||
if (hadSelected) {
|
||||
this._selectedIndex = -1;
|
||||
this.selectFirstItem();
|
||||
}
|
||||
|
||||
this.emit('redisplayed');
|
||||
},
|
||||
|
||||
@ -412,21 +447,20 @@ GenericDisplay.prototype = {
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
// Sets this._width, this._height, this._columnWidth, and this._maxItems based on the
|
||||
// Sets this._width, this._height, this._columnWidth, and this._maxItemsPerPage based on the
|
||||
// space available for the display, number of columns, and the number of items it can fit.
|
||||
_setDimensionsAndMaxItems: function(width, height) {
|
||||
this._width = width;
|
||||
this._columnWidth = (this._width - this._columnGap * (this._numberOfColumns - 1)) / this._numberOfColumns;
|
||||
let maxItemsInColumn = Math.floor(height / ITEM_DISPLAY_HEIGHT);
|
||||
this._maxItems = maxItemsInColumn * this._numberOfColumns;
|
||||
this._maxItemsPerPage = maxItemsInColumn * this._numberOfColumns;
|
||||
this._height = maxItemsInColumn * ITEM_DISPLAY_HEIGHT;
|
||||
},
|
||||
|
||||
// Applies the search string to the list of items to find matches,
|
||||
// and displays up to this._maxItems that matched.
|
||||
// and displays the matching items.
|
||||
_doSearchFilter: function() {
|
||||
this._removeAllDisplayItems();
|
||||
let matchedItems = {};
|
||||
let matchedItemsForSearch = {};
|
||||
|
||||
// Break the search up into terms, and search for each
|
||||
// individual term. Keep track of the number of terms
|
||||
@ -437,22 +471,22 @@ GenericDisplay.prototype = {
|
||||
for (itemId in this._allItems) {
|
||||
let item = this._allItems[itemId];
|
||||
if (this._isInfoMatching(item, term)) {
|
||||
let count = matchedItems[itemId];
|
||||
let count = matchedItemsForSearch[itemId];
|
||||
if (!count)
|
||||
count = 0;
|
||||
count += 1;
|
||||
matchedItems[itemId] = count;
|
||||
matchedItemsForSearch[itemId] = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let matchedList = [];
|
||||
for (itemId in matchedItems) {
|
||||
matchedList.push(itemId);
|
||||
this._matchedItems = [];
|
||||
for (itemId in matchedItemsForSearch) {
|
||||
this._matchedItems.push(itemId);
|
||||
}
|
||||
matchedList.sort(Lang.bind(this, function (a, b) {
|
||||
let countA = matchedItems[a];
|
||||
let countB = matchedItems[b];
|
||||
this._matchedItems.sort(Lang.bind(this, function (a, b) {
|
||||
let countA = matchedItemsForSearch[a];
|
||||
let countB = matchedItemsForSearch[b];
|
||||
if (countA > countB)
|
||||
return -1;
|
||||
else if (countA < countB)
|
||||
@ -461,8 +495,56 @@ GenericDisplay.prototype = {
|
||||
return this._compareItems(a, b);
|
||||
}));
|
||||
|
||||
for (var i = 0; i < matchedList.length && i < this._maxItems; i++) {
|
||||
this._addDisplayItem(matchedList[i]);
|
||||
this._displayMatchedItems(true);
|
||||
},
|
||||
|
||||
// Displays the page specified by the pageNumber argument. The pageNumber is 0-based.
|
||||
_displayPage: function(pageNumber) {
|
||||
this._pageDisplayed = pageNumber;
|
||||
this._displayMatchedItems(false);
|
||||
},
|
||||
|
||||
/*
|
||||
* Updates the display control to reflect the matched items set and the page selected.
|
||||
*
|
||||
* matchedItemsChanged - indicates if the set of the matched items changed prior to the
|
||||
* request. If it did, the display control is updated to reflect the
|
||||
* new set of pages. Otherwise, the page links are updated for the
|
||||
* current set of pages.
|
||||
*/
|
||||
_updateDisplayControl: function(matchedItemsChanged) {
|
||||
if (matchedItemsChanged) {
|
||||
this.displayControl.remove_all();
|
||||
let pageNumber = 0;
|
||||
for (let i = 0; i < this._matchedItems.length; i = i + this._maxItemsPerPage) {
|
||||
let pageControl = new Link.Link({ color: (pageNumber == this._pageDisplayed) ? DISPLAY_CONTROL_SELECTED_COLOR : ITEM_DISPLAY_DESCRIPTION_COLOR,
|
||||
font_name: "Sans Bold 16px",
|
||||
text: (pageNumber + 1) + "",
|
||||
height: LABEL_HEIGHT,
|
||||
reactive: (pageNumber == this._pageDisplayed) ? false : true});
|
||||
this.displayControl.append(pageControl.actor, Big.BoxPackFlags.NONE);
|
||||
|
||||
// we use pageNumberLocalScope to get the page number right in the callback function
|
||||
let pageNumberLocalScope = pageNumber;
|
||||
pageControl.connect('clicked',
|
||||
Lang.bind(this,
|
||||
function(o, event) {
|
||||
this._displayPage(pageNumberLocalScope);
|
||||
}));
|
||||
pageNumber ++;
|
||||
}
|
||||
} else {
|
||||
let pageControlActors = this.displayControl.get_children();
|
||||
for (let i = 0; i < pageControlActors.length; i++) {
|
||||
let pageControlActor = pageControlActors[i];
|
||||
if (i == this._pageDisplayed) {
|
||||
pageControlActor.color = DISPLAY_CONTROL_SELECTED_COLOR;
|
||||
pageControlActor.reactive = false;
|
||||
} else {
|
||||
pageControlActor.color = ITEM_DISPLAY_DESCRIPTION_COLOR;
|
||||
pageControlActor.reactive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -217,6 +217,9 @@ Sideshow.prototype = {
|
||||
|
||||
this._appsSectionDefaultHeight = this._appsSection.height;
|
||||
|
||||
this._appsDisplayControlBox = new Big.Box({x_align: Big.BoxAlignment.CENTER});
|
||||
this._appsDisplayControlBox.append(this._appDisplay.displayControl, Big.BoxPackFlags.NONE);
|
||||
|
||||
this._docsSection = new Big.Box({ background_color: OVERLAY_BACKGROUND_COLOR,
|
||||
x: SIDESHOW_PAD,
|
||||
y: this._appsSection.y + this._appsSection.height,
|
||||
@ -244,6 +247,9 @@ Sideshow.prototype = {
|
||||
|
||||
this._docsSectionDefaultHeight = this._docsSection.height;
|
||||
|
||||
this._docsDisplayControlBox = new Big.Box({x_align: Big.BoxAlignment.CENTER});
|
||||
this._docsDisplayControlBox.append(this._docDisplay.displayControl, Big.BoxPackFlags.NONE);
|
||||
|
||||
/* Proxy the activated signals */
|
||||
this._appDisplay.connect('activated', function(appDisplay) {
|
||||
// we allow clicking on an item to launch it, and this unsets the selection
|
||||
@ -415,9 +421,11 @@ Sideshow.prototype = {
|
||||
this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
|
||||
EXPANDED_SIDESHOW_COLUMNS);
|
||||
this._moreAppsLink.setText("Less...");
|
||||
this._appsSection.insert_after(this._appsDisplayControlBox, this._appDisplay.actor, Big.BoxPackFlags.NONE);
|
||||
} else {
|
||||
this._appDisplay.updateDimensions(this._width, this._appsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS);
|
||||
this._moreAppsLink.setText("More...");
|
||||
this._appsSection.remove_actor(this._appsDisplayControlBox);
|
||||
}
|
||||
this._moreAppsLink.actor.show();
|
||||
},
|
||||
@ -525,9 +533,11 @@ Sideshow.prototype = {
|
||||
this._itemDisplayHeight + SIDESHOW_SECTION_MISC_HEIGHT,
|
||||
EXPANDED_SIDESHOW_COLUMNS);
|
||||
this._moreDocsLink.setText("Less...");
|
||||
this._docsSection.insert_after(this._docsDisplayControlBox, this._docDisplay.actor, Big.BoxPackFlags.NONE);
|
||||
} else {
|
||||
this._docDisplay.updateDimensions(this._width, this._docsSectionDefaultHeight - SIDESHOW_SECTION_MISC_HEIGHT, SIDESHOW_COLUMNS);
|
||||
this._moreDocsLink.setText("More...");
|
||||
this._docsSection.remove_actor(this._docsDisplayControlBox);
|
||||
}
|
||||
this._moreDocsLink.actor.show();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user