Add UI classes with empty event listeners

This is mostly boilerplate, but hopefully it's consistent boilerplate
and gets out of the way of wiring up the actual meat of the app.
This commit is contained in:
Jared Hirsch 2016-02-23 15:33:53 -08:00
Родитель 0fa934a8e6
Коммит 5aa5fac72e
6 изменённых файлов: 207 добавлений и 5 удалений

Просмотреть файл

@ -1,3 +1,4 @@
content universalsearch chrome/content/
content universalsearch-lib lib/
content universalsearch-ui lib/ui/
content universalsearch-skin chrome/skin/

Просмотреть файл

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ['HighlightManager'];
function HighlightManager(opts) {
this.win = opts.win;
this.events = opts.events;
// this is a recommendation view, because the highlight
// class manages passing focus between it and the list of results
this.recommendationView = opts.recommendationView;
this.adjustHighlight = this.adjustHighlight.bind(this);
}
HighlightManager.prototype = {
init: function() {
this.events.subscribe('navigational-key', this.adjustHighlight);
this.events.subscribe('recommendation-shown', this.adjustHighlight);
this.popup = this.win.document.getElementById('PopupAutoCompleteRichResult');
// TODO: maybe we can hook into the XBL mousemove handler and steal focus
// that way instead of manually setting a mousemove listener
this.popup.addEventListener('mousemove', this.adjustHighlight);
},
destroy: function() {
this.popup.removeEventListener('mousemove', this.adjustHighlight);
this.events.unsubscribe('recommendation-shown', this.adjustHighlight);
this.events.unsubscribe('navigational-key', this.adjustHighlight);
delete this.popup;
delete this.recommendationView;
delete this.win;
},
adjustHighlight: function(evt) {
// if the recommendation isn't shown, do nothing: let the popup manage focus
// otherwise,
// check if the recommendation is shown + highlighted
// check the selectedIndex of the popup
// if it's a down key, move the focus downward, wrapping around if needed
// if it's an up key, move the focus upward, wrapping around if needed
}
};

22
lib/ui/popup.js Normal file
Просмотреть файл

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ['Popup'];
function Popup(opts) {
this.win = opts.win;
this.events = opts.events;
this.beforePopupHide = this.beforePopupHide.bind(this);
}
Popup.prototype = {
init: function() {
this.el = this.win.document.getElementById('PopupAutoCompleteRichResult');
this.el.addEventListener('popuphiding', this.beforePopupHide);
},
beforePopupHide: function(evt) {
this.events.publish('before-popup-hide');
}
};

67
lib/ui/recommendation.js Normal file
Просмотреть файл

@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ['Recommendation'];
function Recommendation(opts) {
this.win = opts.win;
this.events = opts.events;
this.urlbar = opts.urlbar;
this.navigate = this.navigate.bind(this);
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
this._pollForElement = this._pollForElement.bind(this);
}
Recommendation.prototype = {
init: function() {
// Note: there is a race between XBL modifying the XUL DOM to insert our
// this.el, and the scripts being loaded and initialized. Rather than try
// to fire a 'ready' event / set app state from the XBL <constructor>,
// which is sometimes unreliable, we instead poll the XUL DOM for the el.
this._pollForElement();
this.events.subscribe('recommendation', this.show);
this.events.subscribe('enter-key', this.navigate);
this.events.subscribe('printable-key', this.hide);
this.events.subscribe('before-popup-hide', this.hide);
},
destroy: function() {
this.el.removeEventListener('click', this.navigate);
this.events.unsubscribe('recommendation', this.show);
this.events.unsubscribe('enter-key', this.navigate);
this.events.unsubscribe('printable-key', this.hide);
this.events.unsubscribe('before-popup-hide', this.hide);
delete this.el;
delete this.urlbar;
delete this.win;
},
_pollForElement: function() {
const el = this.win.document.getElementById('universal-search-recommendation');
if (el) {
this.el = el;
this.el.addEventListener('click', this.navigate);
} else {
this.win.setTimeout(this._pollForElement, 20);
}
},
show: function(data) {
// assign the data to this.data
// render using a simple inline template string
// unset this.el.collapsed to show the el
// fire 'recommendation-shown' event
},
hide: function() {
// collapse the node
// null out this.data
},
navigate: function(evt) {
// if it's a click, we'll have a MouseEvent; if it's a right-click, bail.
// else, we'll just have the (possibly null) data from the 'enter-key' event.
// call this.urlbar.navigate to trigger navigation, passing in the URL.
// TODO: or should we fire an event and keep all the modules maximally disconnected?
}
};

33
lib/ui/urlbar.js Normal file
Просмотреть файл

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ['Urlbar'];
function Urlbar(opts) {
this.win = opts.win;
this.events = opts.events;
this.onKeyDown = this.onKeyDown.bind(this);
}
Urlbar.prototype = {
init: function() {
this.el = this.win.document.getElementById('urlbar');
this.el.addEventListener('keydown', this.onKeyDown);
},
destroy: function() {
delete this.el;
delete this.win;
},
onKeyDown: function(evt) {
// check the key event:
// if it's a printable key, fire 'printable-key'
// if it's a navigational key, fire 'navigational-key'
// if we don't know what to do, return true or false
},
navigate: function(url) {
// set some urlbar state + navigate
}
};

Просмотреть файл

@ -94,19 +94,28 @@ Search.prototype = {
_loadScripts: function(win) {
// Note that we load the scripts into the app namespace, to ease cleanup.
// Each module's EXPORTED_SYMBOLS will be properties on win.universalSearch.
// Example: Cu.import('chrome://universalsearch-lib/content/something-in-lib.js', win.universalSearch);
Cu.import('resource://gre/modules/Console.jsm', win.universalSearch);
Cu.import('chrome://universalsearch-lib/content/events.js', win.universalSearch);
Cu.import('chrome://universalsearch-lib/content/recommendation-server.js', win.universalSearch);
Cu.import('chrome://universalsearch-ui/content/highlight-manager.js', win.universalSearch);
Cu.import('chrome://universalsearch-ui/content/popup.js', win.universalSearch);
Cu.import('chrome://universalsearch-ui/content/recommendation.js', win.universalSearch);
Cu.import('chrome://universalsearch-ui/content/urlbar.js', win.universalSearch);
},
_unloadScripts: function(win) {
// Unload scripts from the namespace. Not clear on whether this is necessary
// if we just delete win.universalSearch.
// Example: Cu.unload('chrome://universalsearch-lib/content/something-in-lib.js', win.universalSearch);
Cu.unload('chrome://universalsearch-ui/content/highlight-manager.js', win.universalSearch);
Cu.unload('chrome://universalsearch-ui/content/popup.js', win.universalSearch);
Cu.unload('chrome://universalsearch-ui/content/recommendation.js', win.universalSearch);
Cu.unload('chrome://universalsearch-ui/content/urlbar.js', win.universalSearch);
Cu.unload('chrome://universalsearch-lib/content/recommendation-server.js', win.universalSearch);
Cu.unload('chrome://universalsearch-lib/content/events.js', win.universalSearch);
Cu.unload('resource://gre/modules/Console.jsm', win.universalSearch);
},
@ -127,16 +136,42 @@ Search.prototype = {
});
app.recommendationServer.init();
app.urlbar = new app.Urlbar({
win: win,
events: app.events
});
app.urlbar.init();
app.highlightManager = new app.HighlightManager({
win: win,
events: app.events,
recommendation: app.recommendation
});
app.highlightManager.init();
app.recommendation = new app.Recommendation({
win: win,
events: app.events,
urlbar: app.urlbar
});
app.recommendation.init();
app.popup = new app.Popup({
win: win,
events: app.events
});
app.popup.init();
},
_stopApp: function(win) {
const app = win.universalSearch;
app.popup.destroy();
app.recommendationView.destroy();
app.highlightManager.destroy();
app.urlbar.destroy();
app.recommendationServer.destroy();
delete app.recommendationServer;
app.events.destroy();
delete app.events;
delete win.universalSearch;
},