[gnome-shell/overview-relayout: 8/20] Use the old dash code to implement the view selector
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/overview-relayout: 8/20] Use the old dash code to implement the view selector
- Date: Mon, 15 Nov 2010 22:39:18 +0000 (UTC)
commit 17cc2539a8ac2454048b379f55d416246339916f
Author: Florian Müllner <fmuellner gnome org>
Date: Fri Nov 12 18:45:29 2010 +0100
Use the old dash code to implement the view selector
The view selector is a tabbed interface with a search entry. Starting
a search switches focus to the results' tab, ending a search moves the
focus back to the previously selected tab. Activating a normal tab
while a search is active cancels the search.
https://bugzilla.gnome.org/show_bug.cgi?id=634948
data/theme/gnome-shell.css | 85 ++++-
js/Makefile.am | 1 +
js/ui/dash.js | 903 +-------------------------------------------
js/ui/viewSelector.js | 810 +++++++++++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
5 files changed, 877 insertions(+), 923 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 7c70f69..4fdee54 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -375,35 +375,65 @@ StTooltip {
spacing: 12px;
}
+#ViewSelector {
+ spacing: 16px;
+}
+
+#SearchArea {
+ padding: 0px 12px;
+}
+
#searchEntry {
- padding: 4px;
- border-radius: 4px;
- color: #a8a8a8;
- border: 1px solid #565656;
- background-color: #404040;
- caret-color: #fff;
+ padding: 4px 8px;
+ border-radius: 12px;
+ color: rgb(128, 128, 128);
+ border: 2px solid rgba(128, 128, 128, 0.4);
+ background-gradient-start: rgba(0, 0, 0, 0.2);
+ background-gradient-end: rgba(128, 128, 128, 0.2);
+ background-gradient-direction: vertical;
+ caret-color: rgb(128, 128, 128);
caret-size: 1px;
height: 16px;
+ width: 250px;
transition-duration: 300;
}
#searchEntry:focus {
- color: #545454;
- border: 1px solid #3a3a3a;
- background-color: #e8e8e8;
- caret-color: #545454;
+ border: 2px solid #ffffff;
+ background-gradient-start: rgba(0, 0, 0, 0.2);
+ background-gradient-end: #ffffff;
+ background-gradient-direction: vertical;
+ color: rgb(64, 64, 64);
+ font-weight: bold;
-st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
transition-duration: 0;
}
#searchEntry:hover {
- color: #a8a8a8;
- border: 1px solid #4d4d4d;
- background-color: #e8e8e8;
+ border: 2px solid #e8e8e8;
caret-color: #545454;
transition-duration: 500;
}
+.view-tab-title {
+ color: #888a85;
+ font-weight: bold;
+ padding: 0px 12px;
+}
+
+.view-tab-title:selected {
+ color: white;
+}
+
+.view-tab-boxpointer {
+ -arrow-border-radius: 9px;
+ -arrow-background-color: rgba(0,0,0,0.5);
+ -arrow-border-width: 2px;
+ -arrow-border-color: rgba(255,255,255,0.5);
+ -arrow-base: 30px;
+ -arrow-rise: 15px;
+}
+
.dash-section {
spacing: 8px;
}
@@ -447,18 +477,33 @@ StTooltip {
padding: 30px 10px 10px 20px;
}
-#dashAppSearchResults {
- padding: 8px 0px;
+#SearchResults {
+ padding: 20px 10px 10px 10px;
+}
+
+#SearchResultsContent {
+ padding: 0 10px;
+ spacing: 8px;
}
.dash-search-statustext,
.dash-search-section-header {
- padding: 4px 0px;
+ padding: 4px 12px;
spacing: 4px;
+ color: #6f6f6f;
+}
+
+.dash-search-section {
+ background-color: rgba(128, 128, 128, .1);
+ border: 1px solid rgba(50, 50, 50, .4);
+ border-radius: 10px;
}
.dash-search-section-results {
color: #ffffff;
+ border-radius: 10px;
+ border: 1px solid rgba(50, 50, 50, .4);
+ padding: 6px;
}
.dash-search-section-list-results {
@@ -466,14 +511,12 @@ StTooltip {
}
.dash-search-result-content {
- padding: 3px;
+ padding: 4px;
}
.dash-search-result-content:selected {
- padding: 2px;
- border: 1px solid #5c5c5c;
- border-radius: 2px;
- background-color: #1e1e1e;
+ border-radius: 4px;
+ background: rgba(255,255,255,0.33);
}
.dash-results-container {
diff --git a/js/Makefile.am b/js/Makefile.am
index eca1726..fcca708 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -46,6 +46,7 @@ nobase_dist_js_DATA = \
ui/status/volume.js \
ui/telepathyClient.js \
ui/tweener.js \
+ ui/viewSelector.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \
diff --git a/js/ui/dash.js b/js/ui/dash.js
index 7d61838..cdeb770 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -1,6 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
-const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Lang = imports.lang;
@@ -11,911 +10,11 @@ const _ = Gettext.gettext;
const AppDisplay = imports.ui.appDisplay;
const AppFavorites = imports.ui.appFavorites;
+
const DND = imports.ui.dnd;
-const DocDisplay = imports.ui.docDisplay;
-const PlaceDisplay = imports.ui.placeDisplay;
const Main = imports.ui.main;
-const Overview = imports.ui.overview;
-const Search = imports.ui.search;
-const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
-// 25 search results (per result type) should be enough for everyone
-const MAX_RENDERED_SEARCH_RESULTS = 25;
-
-/*
- * Returns the index in an array of a given length that is obtained
- * if the provided index is incremented by an increment and the array
- * is wrapped in if necessary.
- *
- * index: prior index, expects 0 <= index < length
- * increment: the change in index, expects abs(increment) <= length
- * length: the length of the array
- */
-function _getIndexWrapped(index, increment, length) {
- return (index + increment + length) % length;
-}
-
-function Pane() {
- this._init();
-}
-
-Pane.prototype = {
- _init: function () {
- this._open = false;
-
- this.actor = new St.BoxLayout({ style_class: 'dash-pane',
- vertical: true,
- reactive: true });
- this.actor.connect('button-press-event', Lang.bind(this, function (a, e) {
- // Eat button press events so they don't go through and close the pane
- return true;
- }));
-
- // Hidden by default
- this.actor.hide();
- },
-
- open: function () {
- if (this._open)
- return;
- this._open = true;
- this.emit('open-state-changed', this._open);
- this.actor.opacity = 0;
- this.actor.show();
- Tweener.addTween(this.actor,
- { opacity: 255,
- time: Overview.PANE_FADE_TIME,
- transition: 'easeOutQuad'
- });
- },
-
- close: function () {
- if (!this._open)
- return;
- this._open = false;
- Tweener.addTween(this.actor,
- { opacity: 0,
- time: Overview.PANE_FADE_TIME,
- transition: 'easeOutQuad',
- onComplete: Lang.bind(this, function() {
- this.actor.hide();
- this.emit('open-state-changed', this._open);
- })
- });
- },
-
- destroyContent: function() {
- let children = this.actor.get_children();
- for (let i = 0; i < children.length; i++) {
- children[i].destroy();
- }
- },
-
- toggle: function () {
- if (this._open)
- this.close();
- else
- this.open();
- }
-};
-Signals.addSignalMethods(Pane.prototype);
-
-function ResultArea() {
- this._init();
-}
-
-ResultArea.prototype = {
- _init : function() {
- this.actor = new St.BoxLayout({ vertical: true });
- this.resultsContainer = new St.BoxLayout({ style_class: 'dash-results-container' });
- this.actor.add(this.resultsContainer, { expand: true });
-
- this.display = new DocDisplay.DocDisplay();
- this.resultsContainer.add(this.display.actor, { expand: true });
- this.display.load();
- }
-};
-
-function ResultPane(dash) {
- this._init(dash);
-}
-
-ResultPane.prototype = {
- __proto__: Pane.prototype,
-
- _init: function(dash) {
- Pane.prototype._init.call(this);
-
- let resultArea = new ResultArea();
- this.actor.add(resultArea.actor, { expand: true });
- this.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
- resultArea.display.resetState();
- }));
- }
-};
-
-function SearchEntry() {
- this._init();
-}
-
-SearchEntry.prototype = {
- _init : function() {
- this.actor = new St.Entry({ name: 'searchEntry',
- hint_text: _("Find") });
- this.entry = this.actor.clutter_text;
-
- this.actor.clutter_text.connect('text-changed', Lang.bind(this,
- function() {
- if (this.isActive())
- this.actor.set_secondary_icon_from_file(global.imagedir +
- 'close-black.svg');
- else
- this.actor.set_secondary_icon_from_file(null);
- }));
- this.actor.connect('secondary-icon-clicked', Lang.bind(this,
- function() {
- this.reset();
- }));
- this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
-
- global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
-
- this.pane = null;
-
- this._capturedEventId = 0;
- },
-
- _updateCursorVisibility: function() {
- let focus = global.stage.get_key_focus();
- if (focus == global.stage || focus == this.entry)
- this.entry.set_cursor_visible(true);
- else
- this.entry.set_cursor_visible(false);
- },
-
- show: function() {
- if (this._capturedEventId == 0)
- this._capturedEventId = global.stage.connect('captured-event',
- Lang.bind(this, this._onCapturedEvent));
- this.entry.set_cursor_visible(true);
- this.entry.set_selection(0, 0);
- },
-
- hide: function() {
- if (this.isActive())
- this.reset();
- if (this._capturedEventId > 0) {
- global.stage.disconnect(this._capturedEventId);
- this._capturedEventId = 0;
- }
- },
-
- reset: function () {
- let [x, y, mask] = global.get_pointer();
- let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE,
- x, y);
- // this.actor is never hovered directly, only its clutter_text and icon
- let hovered = this.actor == actor.get_parent();
-
- this.actor.set_hover(hovered);
-
- this.entry.text = '';
- global.stage.set_key_focus(null);
- this.entry.set_cursor_visible(true);
- this.entry.set_selection(0, 0);
- },
-
- getText: function () {
- return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
- },
-
- // some search term has been entered
- isActive: function() {
- return this.actor.get_text() != '';
- },
-
- // the entry does not show the hint
- _isActivated: function() {
- return this.entry.text == this.actor.get_text();
- },
-
- _onCapturedEvent: function(actor, event) {
- let source = event.get_source();
- let panelEvent = source && Main.panel.actor.contains(source);
-
- switch (event.type()) {
- case Clutter.EventType.BUTTON_PRESS:
- // the user clicked outside after activating the entry, but
- // with no search term entered - cancel the search
- if (source != this.entry && this.entry.text == '') {
- this.reset();
- // allow only panel events to continue
- return !panelEvent;
- }
- return false;
- case Clutter.EventType.KEY_PRESS:
- // If neither the stage nor our entry have key focus, some
- // "special" actor grabbed the focus (run dialog, looking
- // glass); we don't want to interfere with that
- let focus = global.stage.get_key_focus();
- if (focus != global.stage && focus != this.entry)
- return false;
-
- let sym = event.get_key_symbol();
-
- // If we have an active search, Escape cancels it - if we
- // haven't, the key is ignored
- if (sym == Clutter.Escape)
- if (this._isActivated()) {
- this.reset();
- return true;
- } else {
- return false;
- }
-
- // Ignore non-printable keys
- if (!Clutter.keysym_to_unicode(sym))
- return false;
-
- // Search started - move the key focus to the entry and
- // "repeat" the event
- if (!this._isActivated()) {
- global.stage.set_key_focus(this.entry);
- this.entry.event(event, false);
- }
-
- return false;
- default:
- // Suppress all other events outside the panel while the entry
- // is activated and no search has been entered - any click
- // outside the entry will cancel the search
- return (this.entry.text == '' && !panelEvent);
- }
- },
-
- _onDestroy: function() {
- if (this._capturedEventId > 0) {
- global.stage.disconnect(this._capturedEventId);
- this._capturedEventId = 0;
- }
- }
-};
-Signals.addSignalMethods(SearchEntry.prototype);
-
-function SearchResult(provider, metaInfo, terms) {
- this._init(provider, metaInfo, terms);
-}
-
-SearchResult.prototype = {
- _init: function(provider, metaInfo, terms) {
- this.provider = provider;
- this.metaInfo = metaInfo;
- this.actor = new St.Clickable({ style_class: 'dash-search-result',
- reactive: true,
- x_align: St.Align.START,
- x_fill: true,
- y_fill: true });
- this.actor._delegate = this;
-
- let content = provider.createResultActor(metaInfo, terms);
- if (content == null) {
- content = new St.BoxLayout({ style_class: 'dash-search-result-content' });
- let title = new St.Label({ text: this.metaInfo['name'] });
- let icon = this.metaInfo['icon'];
- content.add(icon, { y_fill: false });
- content.add(title, { expand: true, y_fill: false });
- }
- this._content = content;
- this.actor.set_child(content);
-
- this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
-
- 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);
- }));
- },
-
- setSelected: function(selected) {
- if (selected)
- this._content.add_style_pseudo_class('selected');
- else
- this._content.remove_style_pseudo_class('selected');
- },
-
- activate: function() {
- this.provider.activateResult(this.metaInfo.id);
- Main.overview.toggle();
- },
-
- _onResultClicked: function(actor, event) {
- this.activate();
- },
-
- getDragActorSource: function() {
- return this.metaInfo['icon'];
- },
-
- getDragActor: function(stageX, stageY) {
- return new Clutter.Clone({ source: this.metaInfo['icon'] });
- },
-
- shellWorkspaceLaunch: function() {
- if (this.provider.dragActivateResult)
- this.provider.dragActivateResult(this.metaInfo.id);
- else
- this.provider.activateResult(this.metaInfo.id);
- }
-};
-
-function OverflowSearchResults(provider) {
- this._init(provider);
-}
-
-OverflowSearchResults.prototype = {
- __proto__: Search.SearchResultDisplay.prototype,
-
- _init: function(provider) {
- Search.SearchResultDisplay.prototype._init.call(this, provider);
- this.actor = new St.OverflowBox({ style_class: 'dash-search-section-list-results' });
- },
-
- getVisibleResultCount: function() {
- return this.actor.get_n_visible();
- },
-
- renderResults: function(results, terms) {
- for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) {
- let result = results[i];
- let meta = this.provider.getResultMeta(result);
- let display = new SearchResult(this.provider, meta, terms);
- this.actor.add_actor(display.actor);
- }
- },
-
- selectIndex: function(index) {
- let nVisible = this.actor.get_n_visible();
- let children = this.actor.get_children();
- if (this.selectionIndex >= 0) {
- let prevActor = children[this.selectionIndex];
- prevActor._delegate.setSelected(false);
- }
- this.selectionIndex = -1;
- if (index >= nVisible)
- return false;
- else if (index < 0)
- return false;
- let targetActor = children[index];
- targetActor._delegate.setSelected(true);
- this.selectionIndex = index;
- return true;
- },
-
- activateSelected: function() {
- let children = this.actor.get_children();
- let targetActor = children[this.selectionIndex];
- targetActor._delegate.activate();
- }
-};
-
-function SearchResults(searchSystem) {
- this._init(searchSystem);
-}
-
-SearchResults.prototype = {
- _init: function(searchSystem) {
- this._searchSystem = searchSystem;
-
- this.actor = new St.BoxLayout({ name: 'dashSearchResults',
- vertical: true });
- this._statusText = new St.Label({ style_class: 'dash-search-statustext' });
- this.actor.add(this._statusText);
- this._selectedProvider = -1;
- this._providers = this._searchSystem.getProviders();
- this._providerMeta = [];
- for (let i = 0; i < this._providers.length; i++)
- this.createProviderMeta(this._providers[i]);
- },
-
- createProviderMeta: function(provider) {
- let providerBox = new St.BoxLayout({ style_class: 'dash-search-section',
- vertical: true });
- let titleButton = new St.Button({ style_class: 'dash-search-section-header',
- reactive: true,
- x_fill: true,
- y_fill: true });
- titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
- providerBox.add(titleButton);
- let titleBox = new St.BoxLayout();
- titleButton.set_child(titleBox);
- let title = new St.Label({ text: provider.title });
- let count = new St.Label();
- titleBox.add(title, { expand: true });
- titleBox.add(count);
-
- let resultDisplayBin = new St.Bin({ style_class: 'dash-search-section-results',
- x_fill: true,
- y_fill: true });
- providerBox.add(resultDisplayBin, { expand: true });
- let resultDisplay = provider.createResultContainerActor();
- if (resultDisplay == null) {
- resultDisplay = new OverflowSearchResults(provider);
- }
- resultDisplayBin.set_child(resultDisplay.actor);
-
- this._providerMeta.push({ actor: providerBox,
- resultDisplay: resultDisplay,
- count: count });
- this.actor.add(providerBox);
- },
-
- _clearDisplay: function() {
- this._selectedProvider = -1;
- this._visibleResultsCount = 0;
- for (let i = 0; i < this._providerMeta.length; i++) {
- let meta = this._providerMeta[i];
- meta.resultDisplay.clear();
- meta.actor.hide();
- }
- },
-
- reset: function() {
- this._searchSystem.reset();
- this._statusText.hide();
- this._clearDisplay();
- },
-
- startingSearch: function() {
- this.reset();
- this._statusText.set_text(_("Searching..."));
- this._statusText.show();
- },
-
- _metaForProvider: function(provider) {
- return this._providerMeta[this._providers.indexOf(provider)];
- },
-
- updateSearch: function (searchString) {
- let results = this._searchSystem.updateSearch(searchString);
-
- this._clearDisplay();
-
- if (results.length == 0) {
- this._statusText.set_text(_("No matching results."));
- this._statusText.show();
- return true;
- } else {
- this._statusText.hide();
- }
-
- let terms = this._searchSystem.getTerms();
-
- for (let i = 0; i < results.length; i++) {
- let [provider, providerResults] = results[i];
- let meta = this._metaForProvider(provider);
- meta.actor.show();
- meta.resultDisplay.renderResults(providerResults, terms);
- meta.count.set_text('' + providerResults.length);
- }
-
- this.selectDown(false);
-
- return true;
- },
-
- _onHeaderClicked: function(provider) {
- provider.expandSearch(this._searchSystem.getTerms());
- },
-
- _modifyActorSelection: function(resultDisplay, up) {
- let success;
- let index = resultDisplay.getSelectionIndex();
- if (up && index == -1)
- index = resultDisplay.getVisibleResultCount() - 1;
- else if (up)
- index = index - 1;
- else
- index = index + 1;
- return resultDisplay.selectIndex(index);
- },
-
- selectUp: function(recursing) {
- for (let i = this._selectedProvider; i >= 0; i--) {
- let meta = this._providerMeta[i];
- if (!meta.actor.visible)
- continue;
- let success = this._modifyActorSelection(meta.resultDisplay, true);
- if (success) {
- this._selectedProvider = i;
- return;
- }
- }
- if (this._providerMeta.length > 0 && !recursing) {
- this._selectedProvider = this._providerMeta.length - 1;
- this.selectUp(true);
- }
- },
-
- selectDown: function(recursing) {
- let current = this._selectedProvider;
- if (current == -1)
- current = 0;
- for (let i = current; i < this._providerMeta.length; i++) {
- let meta = this._providerMeta[i];
- if (!meta.actor.visible)
- continue;
- let success = this._modifyActorSelection(meta.resultDisplay, false);
- if (success) {
- this._selectedProvider = i;
- return;
- }
- }
- if (this._providerMeta.length > 0 && !recursing) {
- this._selectedProvider = 0;
- this.selectDown(true);
- }
- },
-
- activateSelected: function() {
- let current = this._selectedProvider;
- if (current < 0)
- return;
- let meta = this._providerMeta[current];
- let resultDisplay = meta.resultDisplay;
- resultDisplay.activateSelected();
- Main.overview.hide();
- }
-};
-
-function MoreLink() {
- this._init();
-}
-
-MoreLink.prototype = {
- _init : function () {
- this.actor = new St.BoxLayout({ style_class: 'more-link',
- reactive: true });
- this.pane = null;
-
- this._expander = new St.Bin({ style_class: 'more-link-expander' });
- this.actor.add(this._expander, { expand: true, y_fill: false });
- },
-
- activate: function() {
- if (!this.actor.visible)
- return true; // If the link isn't visible we don't want the header to react
- // to clicks
- if (this.pane == null) {
- // Ensure the pane is created; the activated handler will call setPane
- this.emit('activated');
- }
- this._pane.toggle();
- return true;
- },
-
- setPane: function (pane) {
- this._pane = pane;
- this._pane.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) {
- if (isOpen)
- this._expander.add_style_class_name('open');
- else
- this._expander.remove_style_class_name('open');
- }));
- }
-};
-
-Signals.addSignalMethods(MoreLink.prototype);
-
-function SectionHeader(title, suppressBrowse) {
- this._init(title, suppressBrowse);
-}
-
-SectionHeader.prototype = {
- _init : function (title, suppressBrowse) {
- this.actor = new St.Bin({ style_class: 'section-header',
- x_align: St.Align.START,
- x_fill: true,
- y_fill: true,
- reactive: !suppressBrowse });
- this._innerBox = new St.BoxLayout({ style_class: 'section-header-inner' });
- this.actor.set_child(this._innerBox);
-
- let textBox = new St.BoxLayout({ style_class: 'section-text-content' });
- this.text = new St.Label({ style_class: 'section-title',
- text: title });
- textBox.add(this.text, { x_align: St.Align.START });
-
- this._innerBox.add(textBox, { expand: true });
-
- if (!suppressBrowse) {
- this.moreLink = new MoreLink();
- this._innerBox.add(this.moreLink.actor, { x_align: St.Align.END });
- this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
- }
- },
-
- _onButtonPress: function() {
- this.moreLink.activate();
- },
-
- setMoreLinkVisible : function(visible) {
- if (visible)
- this.moreLink.actor.show();
- else
- this.moreLink.actor.hide();
- }
-};
-
-Signals.addSignalMethods(SectionHeader.prototype);
-
-function SearchSectionHeader(title, onClick) {
- this._init(title, onClick);
-}
-
-SearchSectionHeader.prototype = {
- _init : function(title, onClick) {
- this.actor = new St.Button({ style_class: 'dash-search-section-header',
- x_fill: true,
- y_fill: true });
- let box = new St.BoxLayout();
- this.actor.set_child(box);
- let titleText = new St.Label({ style_class: 'dash-search-section-title',
- text: title });
- this.countText = new St.Label({ style_class: 'dash-search-section-count' });
-
- box.add(titleText);
- box.add(this.countText, { expand: true, x_fill: false, x_align: St.Align.END });
-
- this.actor.connect('clicked', onClick);
- }
-};
-
-function Section(titleString, suppressBrowse) {
- this._init(titleString, suppressBrowse);
-}
-
-Section.prototype = {
- _init: function(titleString, suppressBrowse) {
- this.actor = new St.BoxLayout({ style_class: 'dash-section',
- vertical: true });
- this.header = new SectionHeader(titleString, suppressBrowse);
- this.actor.add(this.header.actor);
- this.content = new St.BoxLayout({ style_class: 'dash-section-content',
- vertical: true });
- this.actor.add(this.content);
- }
-};
-
-function OldDash() {
- this._init();
-}
-
-OldDash.prototype = {
- _init : function() {
- // dash and the popup panes need to be reactive so that the clicks in unoccupied places on them
- // are not passed to the transparent background underneath them. This background is used for the workspaces area when
- // the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user
- // can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane.
- //
- // We have to make the individual panes reactive instead of making the whole dash actor reactive because the width
- // of the Group actor ends up including the width of its hidden children, so we were getting a reactive object as
- // wide as the details pane that was blocking the clicks to the workspaces underneath it even when the details pane
- // was actually hidden.
- this.actor = new St.BoxLayout({ name: 'dash',
- vertical: true,
- reactive: true });
-
- // The searchArea just holds the entry
- this.searchArea = new St.BoxLayout({ name: 'dashSearchArea',
- vertical: true });
- this.sectionArea = new St.BoxLayout({ name: 'dashSections',
- vertical: true });
-
- this.actor.add(this.searchArea);
- this.actor.add(this.sectionArea);
-
- // The currently active popup display
- this._activePane = null;
-
- /***** Search *****/
-
- this._searchActive = false;
- this._searchPending = false;
- this._searchEntry = new SearchEntry();
- this.searchArea.add(this._searchEntry.actor, { y_fill: false, expand: true });
-
- this._searchSystem = new Search.SearchSystem();
- this._searchSystem.registerProvider(new AppDisplay.AppSearchProvider());
- this._searchSystem.registerProvider(new AppDisplay.PrefsSearchProvider());
- this._searchSystem.registerProvider(new PlaceDisplay.PlaceSearchProvider());
- this._searchSystem.registerProvider(new DocDisplay.DocSearchProvider());
-
- this.searchResults = new SearchResults(this._searchSystem);
- this.actor.add(this.searchResults.actor);
- this.searchResults.actor.hide();
-
- this._keyPressId = 0;
- this._searchTimeoutId = 0;
- this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) {
- let searchPreviouslyActive = this._searchActive;
- this._searchActive = this._searchEntry.isActive();
- this._searchPending = this._searchActive && !searchPreviouslyActive;
- if (this._searchPending) {
- this.searchResults.startingSearch();
- }
- if (this._searchActive) {
- this.searchResults.actor.show();
- this.sectionArea.hide();
- } else {
- this.searchResults.actor.hide();
- this.sectionArea.show();
- }
- if (!this._searchActive) {
- if (this._searchTimeoutId > 0) {
- Mainloop.source_remove(this._searchTimeoutId);
- this._searchTimeoutId = 0;
- }
- return;
- }
- if (this._searchTimeoutId > 0)
- return;
- this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
- }));
- this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
- if (this._searchTimeoutId > 0) {
- Mainloop.source_remove(this._searchTimeoutId);
- this._doSearch();
- }
- this.searchResults.activateSelected();
- return true;
- }));
-
- /***** Applications *****/
-
- this._appsSection = new Section(_("APPLICATIONS"));
- let appWell = new AppDisplay.AppWell();
- this._appsSection.content.add(appWell.actor, { expand: true });
-
- this._allApps = null;
- this._appsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) {
- if (this._allApps == null) {
- this._allApps = new AppDisplay.AllAppDisplay();
- this._addPane(this._allApps, St.Align.START);
- link.setPane(this._allApps);
- }
- }));
-
- this.sectionArea.add(this._appsSection.actor);
-
- /***** Places *****/
-
- /* Translators: This is in the sense of locations for documents,
- network locations, etc. */
- this._placesSection = new Section(_("PLACES & DEVICES"), true);
- let placesDisplay = new PlaceDisplay.DashPlaceDisplay();
- this._placesSection.content.add(placesDisplay.actor, { expand: true });
- this.sectionArea.add(this._placesSection.actor);
-
- /***** Documents *****/
-
- this._docsSection = new Section(_("RECENT ITEMS"));
-
- this._docDisplay = new DocDisplay.DashDocDisplay();
- this._docsSection.content.add(this._docDisplay.actor, { expand: true });
-
- this._moreDocsPane = null;
- this._docsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) {
- if (this._moreDocsPane == null) {
- this._moreDocsPane = new ResultPane(this);
- this._addPane(this._moreDocsPane, St.Align.END);
- link.setPane(this._moreDocsPane);
- }
- }));
-
- this._docDisplay.connect('changed', Lang.bind(this, function () {
- this._docsSection.header.setMoreLinkVisible(
- this._docDisplay.actor.get_children().length > 0);
- }));
- this._docDisplay.emit('changed');
-
- this.sectionArea.add(this._docsSection.actor, { expand: true });
- },
-
- _onKeyPress: function(stage, event) {
- // If neither the stage nor the search entry have key focus, some
- // "special" actor grabbed the focus (run dialog, looking glass);
- // we don't want to interfere with that
- let focus = stage.get_key_focus();
- if (focus != stage && focus != this._searchEntry.entry)
- return false;
-
- let symbol = event.get_key_symbol();
- if (symbol == Clutter.Escape) {
- // If we're in one of the "more" modes or showing the
- // details pane, close them
- if (this._activePane != null)
- this._activePane.close();
- // Otherwise, just close the Overview entirely
- else
- Main.overview.hide();
- return true;
- } else if (symbol == Clutter.Up) {
- if (!this._searchActive)
- return true;
- this.searchResults.selectUp(false);
-
- return true;
- } else if (symbol == Clutter.Down) {
- if (!this._searchActive)
- return true;
-
- this.searchResults.selectDown(false);
- return true;
- }
- return false;
- },
-
- _doSearch: function () {
- this._searchTimeoutId = 0;
- let text = this._searchEntry.getText();
- this.searchResults.updateSearch(text);
-
- return false;
- },
-
- addSearchProvider: function(provider) {
- //Add a new search provider to the dash.
-
- this._searchSystem.registerProvider(provider);
- this.searchResults.createProviderMeta(provider);
- },
-
- show: function() {
- this._searchEntry.show();
- if (this._keyPressId == 0)
- this._keyPressId = global.stage.connect('key-press-event',
- Lang.bind(this, this._onKeyPress));
- },
-
- hide: function() {
- this._firstSelectAfterOverlayShow = true;
- this._searchEntry.hide();
- if (this._activePane != null)
- this._activePane.close();
- if (this._keyPressId > 0) {
- global.stage.disconnect(this._keyPressId);
- this._keyPressId = 0;
- }
- },
-
- closePanes: function () {
- if (this._activePane != null)
- this._activePane.close();
- },
-
- _addPane: function(pane, align) {
- pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) {
- if (isOpen) {
- if (pane != this._activePane && this._activePane != null) {
- this._activePane.close();
- }
- this._activePane = pane;
- } else if (pane == this._activePane) {
- this._activePane = null;
- }
- }));
- Main.overview.addPane(pane, align);
- }
-};
-Signals.addSignalMethods(Dash.prototype);
-
-
function Dash() {
this._init();
}
diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js
new file mode 100644
index 0000000..74fc368
--- /dev/null
+++ b/js/ui/viewSelector.js
@@ -0,0 +1,810 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const Gtk = imports.gi.Gtk;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
+const Lang = imports.lang;
+const Shell = imports.gi.Shell;
+const St = imports.gi.St;
+const Gettext = imports.gettext.domain('gnome-shell');
+const _ = Gettext.gettext;
+
+const DND = imports.ui.dnd;
+const Main = imports.ui.main;
+const Overview = imports.ui.overview;
+const Search = imports.ui.search;
+const Tweener = imports.ui.tweener;
+
+// 25 search results (per result type) should be enough for everyone
+const MAX_RENDERED_SEARCH_RESULTS = 25;
+
+function SearchEntry() {
+ this._init();
+}
+
+SearchEntry.prototype = {
+ _init : function() {
+ this.actor = new St.Entry({ name: 'searchEntry',
+ hint_text: _("Search your computer") });
+ this.entry = this.actor.clutter_text;
+
+ this.actor.clutter_text.connect('text-changed', Lang.bind(this,
+ function() {
+ if (this.isActive())
+ this.actor.set_secondary_icon_from_file(global.imagedir +
+ 'close-black.svg');
+ else
+ this.actor.set_secondary_icon_from_file(null);
+ }));
+ this.actor.connect('secondary-icon-clicked', Lang.bind(this,
+ function() {
+ this.reset();
+ }));
+ this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+
+ global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility));
+
+ this.pane = null;
+
+ this._capturedEventId = 0;
+ },
+
+ _updateCursorVisibility: function() {
+ let focus = global.stage.get_key_focus();
+ if (focus == global.stage || focus == this.entry)
+ this.entry.set_cursor_visible(true);
+ else
+ this.entry.set_cursor_visible(false);
+ },
+
+ show: function() {
+ if (this._capturedEventId == 0)
+ this._capturedEventId = global.stage.connect('captured-event',
+ Lang.bind(this, this._onCapturedEvent));
+ this.entry.set_cursor_visible(true);
+ this.entry.set_selection(0, 0);
+ },
+
+ hide: function() {
+ if (this.isActive())
+ this.reset();
+ if (this._capturedEventId > 0) {
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ }
+ },
+
+ reset: function () {
+ let [x, y, mask] = global.get_pointer();
+ let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE,
+ x, y);
+ // this.actor is never hovered directly, only its clutter_text and icon
+ let hovered = this.actor == actor.get_parent();
+
+ this.actor.set_hover(hovered);
+
+ this.entry.text = '';
+ // make sure the entry gets a key-focus-out signal and sets the hint
+ global.stage.set_key_focus(this.entry);
+ global.stage.set_key_focus(null);
+ this.entry.set_cursor_visible(true);
+ this.entry.set_selection(0, 0);
+ },
+
+ getText: function () {
+ return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
+ },
+
+ // some search term has been entered
+ isActive: function() {
+ return this.actor.get_text() != '';
+ },
+
+ // the entry does not show the hint
+ _isActivated: function() {
+ return this.entry.text == this.actor.get_text();
+ },
+
+ _onCapturedEvent: function(actor, event) {
+ let source = event.get_source();
+ let panelEvent = source && Main.panel.actor.contains(source);
+
+ switch (event.type()) {
+ case Clutter.EventType.BUTTON_PRESS:
+ // the user clicked outside after activating the entry, but
+ // with no search term entered - cancel the search
+ if (source != this.entry && this.entry.text == '') {
+ this.reset();
+ // allow only panel events to continue
+ return !panelEvent;
+ }
+ return false;
+ case Clutter.EventType.KEY_PRESS:
+ // If neither the stage nor our entry have key focus, some
+ // "special" actor grabbed the focus (run dialog, looking
+ // glass); we don't want to interfere with that
+ let focus = global.stage.get_key_focus();
+ if (focus != global.stage && focus != this.entry)
+ return false;
+
+ let sym = event.get_key_symbol();
+
+ // If we have an active search, Escape cancels it - if we
+ // haven't, the key is ignored
+ if (sym == Clutter.Escape)
+ if (this._isActivated()) {
+ this.reset();
+ return true;
+ } else {
+ return false;
+ }
+
+ // Ignore non-printable keys
+ if (!Clutter.keysym_to_unicode(sym))
+ return false;
+
+ // Search started - move the key focus to the entry and
+ // "repeat" the event
+ if (!this._isActivated()) {
+ global.stage.set_key_focus(this.entry);
+ this.entry.event(event, false);
+ }
+
+ return false;
+ default:
+ // Suppress all other events outside the panel while the entry
+ // is activated and no search has been entered - any click
+ // outside the entry will cancel the search
+ return (this.entry.text == '' && !panelEvent);
+ }
+ },
+
+ _onDestroy: function() {
+ if (this._capturedEventId > 0) {
+ global.stage.disconnect(this._capturedEventId);
+ this._capturedEventId = 0;
+ }
+ }
+};
+Signals.addSignalMethods(SearchEntry.prototype);
+
+function SearchResult(provider, metaInfo, terms) {
+ this._init(provider, metaInfo, terms);
+}
+
+SearchResult.prototype = {
+ _init: function(provider, metaInfo, terms) {
+ this.provider = provider;
+ this.metaInfo = metaInfo;
+ this.actor = new St.Clickable({ style_class: 'dash-search-result',
+ reactive: true,
+ x_align: St.Align.START,
+ x_fill: true,
+ y_fill: true });
+ this.actor._delegate = this;
+
+ let content = provider.createResultActor(metaInfo, terms);
+ if (content == null) {
+ content = new St.BoxLayout({ style_class: 'dash-search-result-content' });
+ let title = new St.Label({ text: this.metaInfo['name'] });
+ let icon = this.metaInfo['icon'];
+ content.add(icon, { y_fill: false });
+ content.add(title, { expand: true, y_fill: false });
+ }
+ this._content = content;
+ this.actor.set_child(content);
+
+ this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
+
+ 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);
+ }));
+ },
+
+ setSelected: function(selected) {
+ if (selected)
+ this._content.add_style_pseudo_class('selected');
+ else
+ this._content.remove_style_pseudo_class('selected');
+ },
+
+ activate: function() {
+ this.provider.activateResult(this.metaInfo.id);
+ Main.overview.toggle();
+ },
+
+ _onResultClicked: function(actor, event) {
+ this.activate();
+ },
+
+ getDragActorSource: function() {
+ return this.metaInfo['icon'];
+ },
+
+ getDragActor: function(stageX, stageY) {
+ return new Clutter.Clone({ source: this.metaInfo['icon'] });
+ },
+
+ shellWorkspaceLaunch: function() {
+ if (this.provider.dragActivateResult)
+ this.provider.dragActivateResult(this.metaInfo.id);
+ else
+ this.provider.activateResult(this.metaInfo.id);
+ }
+};
+
+function OverflowSearchResults(provider) {
+ this._init(provider);
+}
+
+OverflowSearchResults.prototype = {
+ __proto__: Search.SearchResultDisplay.prototype,
+
+ _init: function(provider) {
+ Search.SearchResultDisplay.prototype._init.call(this, provider);
+ this.actor = new St.OverflowBox({ style_class: 'dash-search-section-list-results' });
+ },
+
+ getVisibleResultCount: function() {
+ return this.actor.get_n_visible();
+ },
+
+ renderResults: function(results, terms) {
+ for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) {
+ let result = results[i];
+ let meta = this.provider.getResultMeta(result);
+ let display = new SearchResult(this.provider, meta, terms);
+ this.actor.add_actor(display.actor);
+ }
+ },
+
+ selectIndex: function(index) {
+ let nVisible = this.actor.get_n_visible();
+ let children = this.actor.get_children();
+ if (this.selectionIndex >= 0) {
+ let prevActor = children[this.selectionIndex];
+ prevActor._delegate.setSelected(false);
+ }
+ this.selectionIndex = -1;
+ if (index >= nVisible)
+ return false;
+ else if (index < 0)
+ return false;
+ let targetActor = children[index];
+ targetActor._delegate.setSelected(true);
+ this.selectionIndex = index;
+ return true;
+ },
+
+ activateSelected: function() {
+ let children = this.actor.get_children();
+ let targetActor = children[this.selectionIndex];
+ targetActor._delegate.activate();
+ }
+};
+
+function SearchResults(searchSystem) {
+ this._init(searchSystem);
+}
+
+SearchResults.prototype = {
+ _init: function(searchSystem) {
+ this._searchSystem = searchSystem;
+
+ this.actor = new St.Bin({ name: 'SearchResults',
+ y_align: St.Align.START,
+ x_align: St.Align.START,
+ x_fill: true });
+ this._content = new St.BoxLayout({ name: 'SearchResultsContent',
+ vertical: true });
+
+ let scrollView = new St.ScrollView({ x_fill: true,
+ y_fill: false,
+ vshadows: true });
+ scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+ scrollView.add_actor(this._content);
+
+ this.actor.set_child(scrollView);
+
+ this._statusText = new St.Label({ style_class: 'dash-search-statustext' });
+ this._content.add(this._statusText);
+ this._selectedProvider = -1;
+ this._providers = this._searchSystem.getProviders();
+ this._providerMeta = [];
+ for (let i = 0; i < this._providers.length; i++)
+ this.createProviderMeta(this._providers[i]);
+ },
+
+ createProviderMeta: function(provider) {
+ let providerBox = new St.BoxLayout({ style_class: 'dash-search-section',
+ vertical: true });
+ let titleButton = new St.Button({ style_class: 'dash-search-section-header',
+ reactive: true,
+ x_fill: true,
+ y_fill: true });
+ titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
+ providerBox.add(titleButton);
+ let titleBox = new St.BoxLayout();
+ titleButton.set_child(titleBox);
+ let title = new St.Label({ text: provider.title });
+ let count = new St.Label();
+ titleBox.add(title, { expand: true });
+ titleBox.add(count);
+
+ let resultDisplayBin = new St.Bin({ style_class: 'dash-search-section-results',
+ x_fill: true,
+ y_fill: true });
+ providerBox.add(resultDisplayBin, { expand: true });
+ let resultDisplay = provider.createResultContainerActor();
+ if (resultDisplay == null) {
+ resultDisplay = new OverflowSearchResults(provider);
+ }
+ resultDisplayBin.set_child(resultDisplay.actor);
+
+ this._providerMeta.push({ actor: providerBox,
+ resultDisplay: resultDisplay,
+ count: count });
+ this._content.add(providerBox);
+ },
+
+ _clearDisplay: function() {
+ this._selectedProvider = -1;
+ this._visibleResultsCount = 0;
+ for (let i = 0; i < this._providerMeta.length; i++) {
+ let meta = this._providerMeta[i];
+ meta.resultDisplay.clear();
+ meta.actor.hide();
+ }
+ },
+
+ reset: function() {
+ this._searchSystem.reset();
+ this._statusText.hide();
+ this._clearDisplay();
+ },
+
+ startingSearch: function() {
+ this.reset();
+ this._statusText.set_text(_("Searching..."));
+ this._statusText.show();
+ },
+
+ _metaForProvider: function(provider) {
+ return this._providerMeta[this._providers.indexOf(provider)];
+ },
+
+ updateSearch: function (searchString) {
+ let results = this._searchSystem.updateSearch(searchString);
+
+ this._clearDisplay();
+
+ if (results.length == 0) {
+ this._statusText.set_text(_("No matching results."));
+ this._statusText.show();
+ return true;
+ } else {
+ this._statusText.hide();
+ }
+
+ let terms = this._searchSystem.getTerms();
+
+ for (let i = 0; i < results.length; i++) {
+ let [provider, providerResults] = results[i];
+ let meta = this._metaForProvider(provider);
+ meta.actor.show();
+ meta.resultDisplay.renderResults(providerResults, terms);
+ meta.count.set_text('' + providerResults.length);
+ }
+
+ this.selectDown(false);
+
+ return true;
+ },
+
+ _onHeaderClicked: function(provider) {
+ provider.expandSearch(this._searchSystem.getTerms());
+ },
+
+ _modifyActorSelection: function(resultDisplay, up) {
+ let success;
+ let index = resultDisplay.getSelectionIndex();
+ if (up && index == -1)
+ index = resultDisplay.getVisibleResultCount() - 1;
+ else if (up)
+ index = index - 1;
+ else
+ index = index + 1;
+ return resultDisplay.selectIndex(index);
+ },
+
+ selectUp: function(recursing) {
+ for (let i = this._selectedProvider; i >= 0; i--) {
+ let meta = this._providerMeta[i];
+ if (!meta.actor.visible)
+ continue;
+ let success = this._modifyActorSelection(meta.resultDisplay, true);
+ if (success) {
+ this._selectedProvider = i;
+ return;
+ }
+ }
+ if (this._providerMeta.length > 0 && !recursing) {
+ this._selectedProvider = this._providerMeta.length - 1;
+ this.selectUp(true);
+ }
+ },
+
+ selectDown: function(recursing) {
+ let current = this._selectedProvider;
+ if (current == -1)
+ current = 0;
+ for (let i = current; i < this._providerMeta.length; i++) {
+ let meta = this._providerMeta[i];
+ if (!meta.actor.visible)
+ continue;
+ let success = this._modifyActorSelection(meta.resultDisplay, false);
+ if (success) {
+ this._selectedProvider = i;
+ return;
+ }
+ }
+ if (this._providerMeta.length > 0 && !recursing) {
+ this._selectedProvider = 0;
+ this.selectDown(true);
+ }
+ },
+
+ activateSelected: function() {
+ let current = this._selectedProvider;
+ if (current < 0)
+ return;
+ let meta = this._providerMeta[current];
+ let resultDisplay = meta.resultDisplay;
+ resultDisplay.activateSelected();
+ Main.overview.hide();
+ }
+};
+
+
+function ViewTab(label, pageActor) {
+ this._init(label, pageActor);
+}
+
+ViewTab.prototype = {
+ _init: function(label, pageActor) {
+ this.title = new St.Button({ label: label,
+ style_class: 'view-tab-title' });
+ this.page = new St.Bin({ child: pageActor,
+ x_align: St.Align.START,
+ y_align: St.Align.START,
+ x_fill: true,
+ y_fill: true,
+ style_class: 'view-tab-page' });
+
+ this.title.connect('clicked', Lang.bind(this, this._activate));
+
+ this.visible = false;
+ },
+
+ show: function() {
+ this.visible = true;
+ this.page.opacity = 0;
+ this.page.show();
+
+ Tweener.addTween(this.page,
+ { opacity: 255,
+ time: 0.1,
+ transition: 'easeOutQuad' });
+ },
+
+ hide: function() {
+ this.visible = false;
+ Tweener.addTween(this.page,
+ { opacity: 0,
+ time: 0.1,
+ transition: 'easeOutQuad',
+ onComplete: Lang.bind(this,
+ function() {
+ this.page.hide();
+ })
+ });
+ },
+
+ _activate: function() {
+ this.emit('activated');
+ }
+};
+Signals.addSignalMethods(ViewTab.prototype);
+
+
+function SearchTab(entryActor, pageActor) {
+ this._init(entryActor, pageActor);
+}
+
+SearchTab.prototype = {
+ __proto__: ViewTab.prototype,
+
+ _init: function(entryActor, pageActor) {
+ ViewTab.prototype._init.call(this, 'search', pageActor);
+ this.title.destroy();
+ this.title = entryActor;
+ }
+};
+
+function ViewSelector() {
+ this._init();
+}
+
+ViewSelector.prototype = {
+ _init : function() {
+ this.actor = new St.BoxLayout({ name: 'ViewSelector',
+ vertical: true });
+
+ this._boxContainer = new Shell.GenericContainer();
+ this.actor.add(this._boxContainer);
+
+ this._boxContainer.connect('get-preferred-width',
+ Lang.bind(this, this._getPreferredWidth));
+ this._boxContainer.connect('get-preferred-height',
+ Lang.bind(this, this._getPreferredHeight));
+ this._boxContainer.connect('allocate',
+ Lang.bind(this, this._allocate));
+
+ this._tabBar = new St.BoxLayout({ name: 'ViewSelectorTabBar' });
+ this._boxContainer.add_actor(this._tabBar);
+
+ // The searchArea just holds the entry
+ this.searchArea = new St.Bin({ name: 'SearchArea' });
+ this._boxContainer.add_actor(this.searchArea);
+
+ this._contentArea = new Shell.GenericContainer();
+ this._contentArea.connect('allocate', Lang.bind(this,
+ function(container, box, flags) {
+ let children = container.get_children();
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = childBox.y1 = 0;
+ childBox.x2 = box.x2 - box.x1;
+ childBox.y2 = box.y2 - box.y1;
+ for (let i = 0; i < children.length; i++)
+ children[i].allocate(childBox, flags);
+ }));
+ this.actor.add(this._contentArea, { x_fill: true,
+ y_fill: true,
+ expand: true });
+
+ this._tabs = [];
+ this._activeTab = null;
+
+ let workId = Main.initializeDeferredWork(this.actor,
+ Lang.bind(this, function() {
+ Main.overview.connect('item-drag-begin',
+ Lang.bind(this, this._switchDefaultTab));
+ Main.overview.connect('hiding',
+ Lang.bind(this, this._switchDefaultTab));
+ }));
+
+ this.constraintY = new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.Y });
+ this.constraintHeight = new Clutter.BindConstraint({ coordinate: Clutter.BindCoordinate.HEIGHT });
+ this._constraintOffsetId = Main.initializeDeferredWork(this.actor,
+ Lang.bind(this, function() {
+ this.constraintY.offset = this._constraintOffset;
+ }));
+ this._constraintSourceId = Main.initializeDeferredWork(this.actor,
+ Lang.bind(this, function() {
+ if (this._activeTab) {
+ this.constraintY.set_source(this._activeTab.page);
+ this.constraintHeight.set_source(this._activeTab.page);
+ }
+ }));
+
+ /***** Search *****/
+ this._searchActive = false;
+ this._searchPending = false;
+ this._searchEntry = new SearchEntry();
+ this.searchArea.set_child(this._searchEntry.actor);
+
+ this._searchSystem = new Search.SearchSystem();
+
+ this.searchResults = new SearchResults(this._searchSystem);
+ this._searchTab = new SearchTab(this.searchArea,
+ this.searchResults.actor);
+ this._contentArea.add_actor(this._searchTab.page);
+ this._searchTab.hide();
+
+ this._keyPressId = 0;
+ this._searchTimeoutId = 0;
+ this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) {
+ let searchPreviouslyActive = this._searchActive;
+ this._searchActive = this._searchEntry.isActive();
+ this._searchPending = this._searchActive && !searchPreviouslyActive;
+ if (this._searchPending) {
+ this.searchResults.startingSearch();
+ }
+ if (this._searchActive) {
+ this._switchTab(this._searchTab);
+ } else {
+ this._switchTab(this._activeTab);
+ }
+ if (!this._searchActive) {
+ if (this._searchTimeoutId > 0) {
+ Mainloop.source_remove(this._searchTimeoutId);
+ this._searchTimeoutId = 0;
+ }
+ return;
+ }
+ if (this._searchTimeoutId > 0)
+ return;
+ this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
+ }));
+ this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) {
+ if (this._searchTimeoutId > 0) {
+ Mainloop.source_remove(this._searchTimeoutId);
+ this._doSearch();
+ }
+ this.searchResults.activateSelected();
+ return true;
+ }));
+ },
+
+ addViewTab: function(title, pageActor) {
+ let viewTab = new ViewTab(title, pageActor);
+ this._tabs.push(viewTab);
+ this._tabBar.add(viewTab.title);
+ this._contentArea.add_actor(viewTab.page);
+ viewTab.page.hide();
+
+ viewTab.connect('activated', Lang.bind(this,
+ function(tab) {
+ this._switchTab(tab);
+ }));
+ },
+
+ _switchTab: function(tab) {
+ if (this._activeTab && this._activeTab.visible) {
+ if (this._activeTab == tab)
+ return;
+ this._activeTab.title.remove_style_pseudo_class('selected');
+ this._activeTab.hide();
+ }
+
+ Main.queueDeferredWork(this._constraintSourceId);
+
+ if (tab != this._searchTab) {
+ tab.title.add_style_pseudo_class('selected');
+ this._activeTab = tab;
+ if (this._searchTab.visible) {
+ this._searchTab.hide();
+ this._searchEntry.reset();
+ }
+ }
+
+ if (!tab.visible)
+ tab.show();
+ },
+
+ _switchDefaultTab: function() {
+ if (this._tabs.length > 0)
+ this._switchTab(this._tabs[0]);
+ },
+
+ _getPreferredWidth: function(box, forHeight, alloc) {
+ let children = box.get_children();
+ for (let i = 0; i < children.length; i++) {
+ let [childMin, childNat] = children[i].get_preferred_width(forHeight);
+ alloc.min_size += childMin;
+ alloc.natural_size += childNat;
+ }
+ },
+
+ _getPreferredHeight: function(box, forWidth, alloc) {
+ let children = box.get_children();
+ for (let i = 0; i < children.length; i++) {
+ let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
+ if (childMin > alloc.min_size)
+ alloc.min_size = childMin;
+ if (childNatural > alloc.natural_size)
+ alloc.natural_size = childNatural;
+ }
+ },
+
+ _allocate: function(container, box, flags) {
+ let allocWidth = box.x2 - box.x1;
+ let allocHeight = box.y2 - box.y1;
+
+ let [searchMinWidth, searchNatWidth] = this.searchArea.get_preferred_width(-1);
+ let [barMinWidth, barNatWidth] = this._tabBar.get_preferred_width(-1);
+ let childBox = new Clutter.ActorBox();
+ childBox.y1 = 0;
+ childBox.y2 = allocHeight;
+ if (this.actor.get_direction() == St.TextDirection.RTL) {
+ childBox.x1 = allocWidth - barNatWidth;
+ childBox.x2 = allocWidth;
+ } else {
+ childBox.x1 = 0;
+ childBox.x2 = barNatWidth;
+ }
+ this._tabBar.allocate(childBox, flags);
+
+ if (this.actor.get_direction() == St.TextDirection.RTL) {
+ childBox.x1 = 0;
+ childBox.x2 = searchNatWidth;
+ } else {
+ childBox.x1 = allocWidth - searchNatWidth;
+ childBox.x2 = allocWidth;
+ }
+ this.searchArea.allocate(childBox, flags);
+
+ let spacing = this.actor.get_theme_node().get_length('spacing');
+ this._constraintOffset = this.actor.y + box.y2 + spacing;
+ Main.queueDeferredWork(this._constraintOffsetId);
+ },
+
+ _onKeyPress: function(stage, event) {
+ // If neither the stage nor the search entry have key focus, some
+ // "special" actor grabbed the focus (run dialog, looking glass);
+ // we don't want to interfere with that
+ let focus = stage.get_key_focus();
+ if (focus != stage && focus != this._searchEntry.entry)
+ return false;
+
+ let symbol = event.get_key_symbol();
+ if (symbol == Clutter.Escape) {
+ Main.overview.hide();
+ return true;
+ } else if (symbol == Clutter.Up) {
+ if (!this._searchActive)
+ return true;
+ this.searchResults.selectUp(false);
+
+ return true;
+ } else if (symbol == Clutter.Down) {
+ if (!this._searchActive)
+ return true;
+
+ this.searchResults.selectDown(false);
+ return true;
+ }
+ return false;
+ },
+
+ _doSearch: function () {
+ this._searchTimeoutId = 0;
+ let text = this._searchEntry.getText();
+ this.searchResults.updateSearch(text);
+
+ return false;
+ },
+
+ addSearchProvider: function(provider) {
+ //Add a new search provider to the dash.
+
+ this._searchSystem.registerProvider(provider);
+ this.searchResults.createProviderMeta(provider);
+ },
+
+ show: function() {
+ this._switchDefaultTab();
+
+ this._searchEntry.show();
+ if (this._keyPressId == 0)
+ this._keyPressId = global.stage.connect('key-press-event',
+ Lang.bind(this, this._onKeyPress));
+ },
+
+ hide: function() {
+ this._firstSelectAfterOverlayShow = true;
+ this._searchEntry.hide();
+ if (this._keyPressId > 0) {
+ global.stage.disconnect(this._keyPressId);
+ this._keyPressId = 0;
+ }
+ }
+};
+Signals.addSignalMethods(ViewSelector.prototype);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a23f9bf..b438a10 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,7 @@ js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/statusMenu.js
js/ui/status/accessibility.js
+js/ui/viewSelector.js
js/ui/windowAttentionHandler.js
js/ui/workspacesView.js
src/gvc/gvc-mixer-control.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]