diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 58e1c7354..05c9ce263 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -441,42 +441,6 @@ StTooltip StLabel { spacing: 4px; } -/* GenericDisplay */ - -.generic-display-container { - spacing: 4px; -} - -.generic-display-item { - height: 50px; - border-radius: 4px; - color: #ffffff; - font-size: 14px; - spacing: 4px; -} - -.generic-display-item:selected { - background-color: rgba(79,111,173,0.66); -} - -.generic-display-item-text { - spacing: 4px; -} - -.generic-display-item-description { - font-size: 12px; - color: #bababa; -} - -.generic-display-details { - font-size: 14px; - color: #ffffff; -} - -.generic-display-details-name { - font-weight: bold; -} - /* Apps */ .icon-grid { diff --git a/js/Makefile.am b/js/Makefile.am index 1b02f4968..3a7a2da06 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -24,7 +24,6 @@ nobase_dist_js_DATA = \ ui/endSessionDialog.js \ ui/environment.js \ ui/extensionSystem.js \ - ui/genericDisplay.js \ ui/iconGrid.js \ ui/lightbox.js \ ui/link.js \ diff --git a/js/misc/docInfo.js b/js/misc/docInfo.js index 6888ced50..a9332d91c 100644 --- a/js/misc/docInfo.js +++ b/js/misc/docInfo.js @@ -60,8 +60,7 @@ function getDocManager() { } /** - * DocManager wraps the DocSystem, primarily to expose DocInfo objects - * which conform to the GenericDisplay item API. + * DocManager wraps the DocSystem, primarily to expose DocInfo objects. */ function DocManager() { this._init(); diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js deleted file mode 100644 index 071599315..000000000 --- a/js/ui/genericDisplay.js +++ /dev/null @@ -1,693 +0,0 @@ -/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ - -const Gtk = imports.gi.Gtk; -const Lang = imports.lang; -const Signals = imports.signals; -const St = imports.gi.St; - -const DND = imports.ui.dnd; -const Main = imports.ui.main; - -const RedisplayFlags = { NONE: 0, - FULL: 1 << 1, - SUBSEARCH: 1 << 2, - IMMEDIATE: 1 << 3 }; - -// Used by subclasses -const ITEM_DISPLAY_ICON_SIZE = 48; -const PREVIEW_ICON_SIZE = 96; - -/* 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 - * it by highlighting it with a different background color than the default. - */ -function GenericDisplayItem() { - this._init(); -} - -GenericDisplayItem.prototype = { - _init: function() { - this.actor = new St.BoxLayout({ style_class: 'generic-display-item', - reactive: true }); - - this.actor._delegate = this; - this.actor.connect('button-release-event', - Lang.bind(this, - function() { - // Activates the item by launching it - this.emit('activate'); - return true; - })); - - let draggable = DND.makeDraggable(this.actor); - draggable.connect('drag-begin', - Lang.bind(this, function() { - Main.overview.beginItemDrag(this); - })); - draggable.connect('drag-end', - Lang.bind(this, function() { - Main.overview.endItemDrag(this); - })); - - this._iconBin = new St.Bin(); - this.actor.add(this._iconBin); - - this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text', - vertical: true }); - this.actor.add(this._infoText, { expand: true, y_fill: false }); - - this._name = null; - this._description = null; - this._icon = null; - - this._initialLoadComplete = false; - - // An array of details description actors that we create over time for the item. - // It is used for updating the description text inside the details actor when - // the description text for the item is updated. - this._detailsDescriptions = []; - }, - - //// Draggable object interface //// - - // Returns a cloned texture of the item's icon to represent the item as it - // is being dragged. - getDragActor: function(stageX, stageY) { - return this._createIcon(); - }, - - // Returns the item icon, a separate copy of which is used to - // represent the item as it is being dragged. This is used to - // determine a snap-back location for the drag icon if it does - // not get accepted by any drop target. - getDragActorSource: function() { - return this._icon; - }, - - //// Public methods //// - - // Highlights the item by setting a different background color than the default - // if isSelected is true, removes the highlighting otherwise. - markSelected: function(isSelected) { - if (isSelected) - this.actor.add_style_pseudo_class('selected'); - else - this.actor.remove_style_pseudo_class('selected'); - }, - - /* - * Returns an actor containing item details. In the future details can have more information than what - * the preview pop-up has and be item-type specific. - */ - createDetailsActor: function() { - - let details = new St.BoxLayout({ style_class: 'generic-display-container', - vertical: true }); - - let mainDetails = new St.BoxLayout({ style_class: 'generic-display-container' }); - - // Inner box with name and description - let textDetails = new St.BoxLayout({ style_class: 'generic-display-details', - vertical: true }); - let detailsName = new St.Label({ style_class: 'generic-display-details-name', - text: this._name.text }); - textDetails.add(detailsName); - - let detailsDescription = new St.Label({ text: this._description.text }); - textDetails.add(detailsDescription); - - this._detailsDescriptions.push(detailsDescription); - - mainDetails.add(textDetails, { expand: true }); - - let previewIcon = this._createPreviewIcon(); - let largePreviewIcon = this._createLargePreviewIcon(); - - if (previewIcon != null && largePreviewIcon == null) { - mainDetails.insert_actor(previewIcon, 0); - } - - details.add(mainDetails); - - if (largePreviewIcon != null) { - details.add(largePreviewIcon); - } - - return details; - }, - - // Destroys the item. - destroy: function() { - this.actor.destroy(); - }, - - //// Pure virtual public methods //// - - // Performes an action associated with launching this item, such as opening a file or an application. - launch: function() { - throw new Error('Not implemented'); - }, - - //// Protected methods //// - - /* - * Creates the graphical elements for the item based on the item information. - * - * nameText - name of the item - * descriptionText - short description of the item - */ - _setItemInfo: function(nameText, descriptionText) { - if (this._name != null) { - // this also removes this._name from the parent container, - // so we don't need to call this.actor.remove_actor(this._name) directly - this._name.destroy(); - this._name = null; - } - if (this._description != null) { - this._description.destroy(); - this._description = null; - } - if (this._icon != null) { - // though we get the icon from elsewhere, we assume its ownership here, - // and therefore should be responsible for distroying it - this._icon.destroy(); - this._icon = null; - } - - this._icon = this._createIcon(); - this._iconBin.set_child(this._icon); - - this._name = new St.Label({ style_class: 'generic-display-item-name', - text: nameText }); - this._infoText.add(this._name); - - this._description = new St.Label({ style_class: 'generic-display-item-description', - text: descriptionText ? descriptionText : '' }); - this._infoText.add(this._description); - }, - - // Sets the description text for the item, including the description text - // in the details actors that have been created for the item. - _setDescriptionText: function(text) { - this._description.text = text; - for (let i = 0; i < this._detailsDescriptions.length; i++) { - let detailsDescription = this._detailsDescriptions[i]; - if (detailsDescription != null) { - detailsDescription.text = text; - } - } - }, - - //// Virtual protected methods //// - - // Creates and returns a large preview icon, but only if we have a detailed image. - _createLargePreviewIcon : function() { - return null; - }, - - //// Pure virtual protected methods //// - - // Returns an icon for the item. - _createIcon: function() { - throw new Error('Not implemented'); - }, - - // Returns a preview icon for the item. - _createPreviewIcon: function() { - throw new Error('Not implemented'); - } - - //// Private methods //// -}; - -Signals.addSignalMethods(GenericDisplayItem.prototype); - -/* This is a virtual class that represents a display containing a collection of items - * that can be filtered with a search string. - */ -function GenericDisplay() { - this._init(); -} - -GenericDisplay.prototype = { - _init : function() { - this._search = ''; - this._expanded = false; - - this.actor = new St.ScrollView({ x_fill: true, - y_fill: false, - vshadows: true }); - this._list = new St.BoxLayout({ style_class: 'generic-display-container', - vertical: true }); - this.actor.add_actor(this._list); - this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); - - this._pendingRedisplay = RedisplayFlags.NONE; - this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify)); - - // map where Object represents the item info - this._allItems = {}; - // set - this._matchedItems = {}; - // sorted array of items matched by search - this._matchedItemKeys = []; - // map - this._displayedItems = {}; - this._openDetailIndex = -1; - this._selectedIndex = -1; - }, - - //// Public methods //// - - // Sets the search string and displays the matching items. - setSearch: function(text) { - let lowertext = text.toLowerCase(); - if (lowertext == this._search) { - return; - } - let flags = RedisplayFlags.IMMEDIATE; - if (this._search != '') { - // Because we combine search terms with OR, we have to be sure that no new term - // was introduced before deciding that the new search results will be a subset of - // the existing search results. - if (lowertext.indexOf(this._search) == 0 && - lowertext.split(/\s+/).length == this._search.split(/\s+/).length) { - flags |= RedisplayFlags.SUBSEARCH; - } - } - this._search = lowertext; - this._redisplay(flags); - }, - - // Launches the item that is currently selected, closing the Overview - activateSelected: function() { - if (this._selectedIndex != -1) { - let selected = this._findDisplayedByIndex(this._selectedIndex); - selected.launch(); - this.unsetSelected(); - Main.overview.hide(); - } - }, - - // Moves the selection one up. If the selection was already on the top item, it's moved - // to the bottom one. Returns true if the selection actually moved up, false if it wrapped - // around to the bottom. - selectUp: function() { - let count = this._getVisibleCount(); - let selectedUp = true; - let prev = this._selectedIndex - 1; - if (this._selectedIndex <= 0) { - prev = count - 1; - selectedUp = false; - } - this._selectIndex(prev); - return selectedUp; - }, - - // Moves the selection one down. If the selection was already on the bottom item, it's moved - // to the top one. Returns true if the selection actually moved down, false if it wrapped - // around to the top. - selectDown: function() { - let count = this._getVisibleCount(); - let selectedDown = true; - let next = this._selectedIndex + 1; - if (this._selectedIndex == count - 1) { - next = 0; - selectedDown = false; - } - this._selectIndex(next); - return selectedDown; - }, - - // Selects the first item among the displayed items. - selectFirstItem: function() { - if (this.hasItems()) - this._selectIndex(0); - }, - - // Selects the last item among the displayed items. - selectLastItem: function() { - let count = this._getVisibleCount(); - if (this.hasItems()) - this._selectIndex(count - 1); - }, - - // Returns true if the display has some item selected. - hasSelected: function() { - return this._selectedIndex != -1; - }, - - // Removes selection if some display item is selected. - unsetSelected: function() { - this._selectIndex(-1); - }, - - // Returns true if the display has any displayed items. - hasItems: function() { - // TODO: figure out why this._list.displayedCount is returning a - // positive number when this._mathedItems.length is 0 - // This can be triggered if a search string is entered for which there are no matches. - // log('this._mathedItems.length: ' + this._matchedItems.length + ' this._list.displayedCount ' + this._list.displayedCount); - return this._matchedItemKeys.length > 0; - }, - - getMatchedItemsCount: function() { - return this._matchedItemKeys.length; - }, - - // Load the initial state - load: function() { - this._redisplay(RedisplayFlags.FULL); - }, - - // Should be called when the display is closed - resetState: function() { - this._filterReset(); - this._openDetailIndex = -1; - this.actor.get_vscroll_bar().get_adjustment().value = 0; - }, - - // Returns an actor which acts as a sidebar; this is used for - // the applications category view - getNavigationArea: function () { - return null; - }, - - createDetailsForIndex: function(index) { - let item = this._findDisplayedByIndex(index); - return item.createDetailsActor(); - }, - - //// Protected methods //// - - _recreateDisplayItems: function() { - this._removeAllDisplayItems(); - this._setDefaultList(); - for (let itemId in this._allItems) { - this._addDisplayItem(itemId); - } - }, - - // Creates a display item based on the information associated with itemId - // and adds it to the list of displayed items, but does not yet display it. - _addDisplayItem : function(itemId) { - if (this._displayedItems.hasOwnProperty(itemId)) { - log('Tried adding a display item for ' + itemId + ', but an item with this item id is already among displayed items.'); - return; - } - - let itemInfo = this._allItems[itemId]; - let displayItem = this._createDisplayItem(itemInfo); - - displayItem.connect('activate', - Lang.bind(this, - function() { - // update the selection - this._selectIndex(this._list.get_children().indexOf(displayItem.actor)); - this.activateSelected(); - })); - - displayItem.connect('show-details', - Lang.bind(this, - function() { - let index = this._list.get_children().indexOf(displayItem.actor); - /* Close the details pane if already open */ - if (index == this._openDetailIndex) { - this._openDetailIndex = -1; - this.emit('show-details', -1); - } else { - this._openDetailIndex = index; - this.emit('show-details', index); - } - })); - this._displayedItems[itemId] = displayItem; - }, - - // Removes an item identifed by the itemId from the displayed items. - _removeDisplayItem: function(itemId) { - let children = this._list.get_children(); - let count = children.length; - let displayItem = this._displayedItems[itemId]; - let displayItemIndex = children.indexOf(displayItem.actor); - - if (this.hasSelected() && count == 1) { - this.unsetSelected(); - } else if (this.hasSelected() && displayItemIndex < this._selectedIndex) { - this.selectUp(); - } - - displayItem.destroy(); - - delete this._displayedItems[itemId]; - }, - - // Removes all displayed items. - _removeAllDisplayItems: function() { - this.unsetSelected(); - for (itemId in this._displayedItems) - this._removeDisplayItem(itemId); - }, - - // Return true if there's an active search or other constraint - // on the list - _filterActive: function() { - return this._search != ''; - }, - - // Called when we are resetting state - _filterReset: function() { - this.unsetSelected(); - }, - - _compareSearchMatch: function(a, b) { - let countA = this._matchedItems[a]; - let countB = this._matchedItems[b]; - if (countA > countB) - return -1; - else if (countA < countB) - return 1; - else - return this._compareItems(a, b); - }, - - _setMatches: function(matches) { - this._matchedItems = matches; - this._matchedItemKeys = []; - for (let itemId in this._matchedItems) { - this._matchedItemKeys.push(itemId); - } - this._matchedItemKeys.sort(Lang.bind(this, this._compareSearchMatch)); - }, - - /** - * _redisplaySubSearch: - * A somewhat more optimized function called when we know - * that we're going to be displaying a subset of the items - * we already had, in the same order. In that case, we can - * just hide the actors that shouldn't be shown. - */ - _redisplaySubSearch: function() { - let matches = this._getSearchMatchedItems(true); - - // Just hide all from the old set, - // we'll show the ones we want below - for (let itemId in this._displayedItems) { - let item = this._displayedItems[itemId]; - item.actor.hide(); - } - - this._setMatches(matches); - - for (let itemId in matches) { - let item = this._displayedItems[itemId]; - item.actor.show(); - } - this._list.queue_relayout(); - }, - - _redisplayReordering: function() { - if (!this._filterActive()) { - this._setDefaultList(); - } else { - this._setMatches(this._getSearchMatchedItems(false)); - } - this._list.remove_all(); - for (let i = 0; i < this._matchedItemKeys.length; i++) { - let itemId = this._matchedItemKeys[i]; - let item = this._displayedItems[itemId]; - item.actor.show(); - this._list.add_actor(item.actor); - } - }, - - /* - * Updates the displayed items, applying the search string if one exists. - * @flags: Flags controlling redisplay behavior as follows: - * SUBSEARCH - Indicates that the current _search is a superstring of the previous - * one, which implies we only need to re-search through previous results. - * FULL - Indicates that we need recreate all displayed items. - * IMMEDIATE - Do the full redisplay even if we're not mapped. This is useful - * if you want to get the number of matched items and show/hide a section based on - * that number. - */ - _redisplay: function(flags) { - let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0; - if (!immediate && !this.actor.mapped) { - this._pendingRedisplay |= flags; - return; - } - - let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0; - let fullReload = (flags & RedisplayFlags.FULL) != 0; - - let hadSelected = this.hasSelected(); - this.unsetSelected(); - - if (!this._initialLoadComplete) - fullReload = true; - - if (!this._refreshCache()) - fullReload = true; - - if (fullReload) { - this._recreateDisplayItems(); - this._initialLoadComplete = true; - } - - if (isSubSearch) { - this._redisplaySubSearch(); - } else { - this._redisplayReordering(); - } - - if (hadSelected) { - this._selectedIndex = -1; - this.selectFirstItem(); - } - - this.emit('redisplayed'); - }, - - //// Pure virtual protected methods //// - - // Performs the steps needed to have the latest information about the items. - // Implementation should return %true if we are up to date, and %false - // if a full reload occurred. - _refreshCache: function() { - throw new Error('Not implemented'); - }, - - // Sets the list of the displayed items based on the default sorting order. - // The default sorting order is specific to each implementing class. - _setDefaultList: function() { - throw new Error('Not implemented'); - }, - - // Compares items associated with the item ids based on the order in which the - // items should be displayed. - // Intended to be used as a compareFunction for array.sort(). - // Returns an integer value indicating the result of the comparison. - _compareItems: function(itemIdA, itemIdB) { - throw new Error('Not implemented'); - }, - - // Checks if the item info can be a match for the search string. - // Returns a boolean flag indicating if that's the case. - _isInfoMatching: function(itemInfo, search) { - throw new Error('Not implemented'); - }, - - // Creates a display item based on itemInfo. - _createDisplayItem: function(itemInfo) { - throw new Error('Not implemented'); - }, - - //// Private methods //// - - _getItemSearchScore: function(itemId, terms) { - let item = this._allItems[itemId]; - let score = 0; - for (let i = 0; i < terms.length; i++) { - let term = terms[i]; - if (this._isInfoMatching(item, term)) { - score++; - } - } - return score; - }, - - _getSearchMatchedItems: function(isSubSearch) { - // Break the search up into terms, and search for each - // individual term. Keep track of the number of terms - // each item matched. - let terms = this._search.split(/\s+/); - let matchScores = {}; - - if (isSubSearch) { - for (let i = 0; i < this._matchedItemKeys.length; i++) { - let itemId = this._matchedItemKeys[i]; - let score = this._getItemSearchScore(itemId, terms); - if (score > 0) - matchScores[itemId] = score; - } - } else { - for (let itemId in this._displayedItems) { - let score = this._getItemSearchScore(itemId, terms); - if (score > 0) - matchScores[itemId] = score; - } - } - return matchScores; - }, - - // Returns a display item based on its index in the ordering of the - // display children. - _findDisplayedByIndex: function(index) { - let actor = this._list.get_children()[index]; - return this._findDisplayedByActor(actor); - }, - - // Returns a display item based on the actor that represents it in - // the display. - _findDisplayedByActor: function(actor) { - for (itemId in this._displayedItems) { - let item = this._displayedItems[itemId]; - if (item.actor == actor) { - return item; - } - } - return null; - }, - - // Selects (e.g. highlights) a display item at the provided index, - // updates this.selectedItemDetails actor, and emits 'selected' signal. - _selectIndex: function(index) { - // Cleanup from the previous item - if (this.hasSelected()) { - this._findDisplayedByIndex(this._selectedIndex).markSelected(false); - } - - this._selectedIndex = index; - if (index < 0) - return; - - // Mark the new item as selected and create its details pane - let item = this._findDisplayedByIndex(index); - item.markSelected(true); - this.emit('selected'); - }, - - _getVisibleCount: function() { - return this._list.get_n_children(); - }, - - _onMappedNotify: function () { - let mapped = this.actor.mapped; - if (mapped && this._pendingRedisplay > RedisplayFlags.NONE) - this._redisplay(this._pendingRedisplay); - - this._pendingRedisplay = RedisplayFlags.NONE; - } -}; - -Signals.addSignalMethods(GenericDisplay.prototype); diff --git a/js/ui/overview.js b/js/ui/overview.js index 08641ae89..353d769e8 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -15,7 +15,6 @@ const AppDisplay = imports.ui.appDisplay; const Dash = imports.ui.dash; const DND = imports.ui.dnd; const DocDisplay = imports.ui.docDisplay; -const GenericDisplay = imports.ui.genericDisplay; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray;