зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1492842 - Implement UrlbarInput up/down key event handling to navigate within the UrlbarView. r=Standard8
Differential Revision: https://phabricator.services.mozilla.com/D13794 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f4794a2bfb
Коммит
e54f0d9a0f
|
@ -107,6 +107,26 @@ class UrlbarController {
|
|||
this._listeners = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks up the controller with an input.
|
||||
*
|
||||
* @param {UrlbarInput} input
|
||||
* The UrlbarInput instance associated with this controller.
|
||||
*/
|
||||
setInput(input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks up the controller with a view.
|
||||
*
|
||||
* @param {UrlbarView} view
|
||||
* The UrlbarView instance associated with this controller.
|
||||
*/
|
||||
setView(view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a query context and starts the query based on the user input.
|
||||
*
|
||||
|
@ -173,6 +193,55 @@ class UrlbarController {
|
|||
// TODO: implementation needed (bug 1496685)
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives keyboard events from the input and handles those that should
|
||||
* navigate within the view or pick the currently selected item.
|
||||
*
|
||||
* @param {KeyboardEvent} event
|
||||
* The DOM KeyboardEvent.
|
||||
*/
|
||||
handleKeyNavigation(event) {
|
||||
// Handle readline/emacs-style navigation bindings on Mac.
|
||||
if (AppConstants.platform == "macosx" &&
|
||||
this.view.isOpen &&
|
||||
event.ctrlKey &&
|
||||
(event.key == "n" || event.key == "p")) {
|
||||
this.view.selectNextItem({ reverse: event.key == "p" });
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.keyCode) {
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
if (AppConstants.platform == "macosx" &&
|
||||
event.metaKey) {
|
||||
// Prevent beep on Mac.
|
||||
event.preventDefault();
|
||||
}
|
||||
// TODO: We may have an autoFill entry, so we should use that instead.
|
||||
// TODO: We should have an input bufferrer so that we can use search results
|
||||
// if appropriate.
|
||||
this.input.handleCommand(event);
|
||||
return;
|
||||
case KeyEvent.DOM_VK_TAB:
|
||||
this.view.selectNextItem({ reverse: event.shiftKey });
|
||||
event.preventDefault();
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
if (!event.ctrlKey && !event.altKey) {
|
||||
this.view.selectNextItem();
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
if (!event.ctrlKey && !event.altKey) {
|
||||
this.view.selectNextItem({ reverse: true });
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to notify listeners of results.
|
||||
*
|
||||
|
|
|
@ -50,6 +50,7 @@ class UrlbarInput {
|
|||
this.controller = options.controller || new UrlbarController({
|
||||
browserWindow: this.window,
|
||||
});
|
||||
this.controller.setInput(this);
|
||||
this.view = new UrlbarView(this);
|
||||
this.valueIsTyped = false;
|
||||
this.userInitiatedFocus = false;
|
||||
|
@ -104,7 +105,7 @@ class UrlbarInput {
|
|||
this.inputField.addEventListener("underflow", this);
|
||||
this.inputField.addEventListener("scrollend", this);
|
||||
this.inputField.addEventListener("select", this);
|
||||
this.inputField.addEventListener("keyup", this);
|
||||
this.inputField.addEventListener("keydown", this);
|
||||
|
||||
this.inputField.controllers.insertControllerAt(0, new CopyCutController(this));
|
||||
}
|
||||
|
@ -180,7 +181,7 @@ class UrlbarInput {
|
|||
* Handles an event which would cause a url or text to be opened.
|
||||
* XXX the name is currently handleCommand which is compatible with
|
||||
* urlbarBindings. However, it is no longer called automatically by autocomplete,
|
||||
* See _on_keyup.
|
||||
* See _on_keydown.
|
||||
*
|
||||
* @param {Event} event The event triggering the open.
|
||||
* @param {string} [openWhere] Where we expect the result to be opened.
|
||||
|
@ -255,12 +256,12 @@ class UrlbarInput {
|
|||
}
|
||||
|
||||
/**
|
||||
* Called by the view when a result is selected.
|
||||
* Called by the view when a result is picked.
|
||||
*
|
||||
* @param {Event} event The event that selected the result.
|
||||
* @param {UrlbarMatch} result The result that was selected.
|
||||
* @param {Event} event The event that picked the result.
|
||||
* @param {UrlbarMatch} result The result that was picked.
|
||||
*/
|
||||
resultSelected(event, result) {
|
||||
pickResult(event, result) {
|
||||
this.setValueFromResult(result);
|
||||
|
||||
// TODO: Work out how we get the user selection behavior, probably via passing
|
||||
|
@ -712,13 +713,8 @@ class UrlbarInput {
|
|||
this.controller.tabContextChanged();
|
||||
}
|
||||
|
||||
_on_keyup(event) {
|
||||
// TODO: We may have an autoFill entry, so we should use that instead.
|
||||
// TODO: We should have an input bufferrer so that we can use search results
|
||||
// if appropriate.
|
||||
if (event.key == "Enter") {
|
||||
this.handleCommand(event);
|
||||
}
|
||||
_on_keydown(event) {
|
||||
this.controller.handleKeyNavigation(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,24 +16,27 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
*/
|
||||
class UrlbarView {
|
||||
/**
|
||||
* @param {UrlbarInput} urlbar
|
||||
* @param {UrlbarInput} input
|
||||
* The UrlbarInput instance belonging to this UrlbarView instance.
|
||||
*/
|
||||
constructor(urlbar) {
|
||||
this.urlbar = urlbar;
|
||||
this.panel = urlbar.panel;
|
||||
this.controller = urlbar.controller;
|
||||
this.document = urlbar.panel.ownerDocument;
|
||||
constructor(input) {
|
||||
this.input = input;
|
||||
this.panel = input.panel;
|
||||
this.controller = input.controller;
|
||||
this.document = this.panel.ownerDocument;
|
||||
this.window = this.document.defaultView;
|
||||
|
||||
this._mainContainer = this.panel.querySelector(".urlbarView-body-inner");
|
||||
this._rows = this.panel.querySelector(".urlbarView-results");
|
||||
|
||||
this._rows.addEventListener("click", this);
|
||||
|
||||
// For the horizontal fade-out effect, set the overflow attribute on result
|
||||
// rows when they overflow.
|
||||
this._rows.addEventListener("overflow", this);
|
||||
this._rows.addEventListener("underflow", this);
|
||||
|
||||
this.controller.setView(this);
|
||||
this.controller.addQueryListener(this);
|
||||
}
|
||||
|
||||
|
@ -43,6 +46,50 @@ class UrlbarView {
|
|||
new this.window.SearchOneOffs(this.panel.querySelector(".search-one-offs")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
* Whether the panel is open.
|
||||
*/
|
||||
get isOpen() {
|
||||
return this.panel.state == "open" || this.panel.state == "showing";
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the next or previous view item. An item could be an autocomplete
|
||||
* result or a one-off search button.
|
||||
*
|
||||
* @param {boolean} options.reverse
|
||||
* Set to true to select the previous item. By default the next item
|
||||
* will be selected.
|
||||
*/
|
||||
selectNextItem({reverse = false} = {}) {
|
||||
if (!this.isOpen) {
|
||||
this.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle one-off search buttons
|
||||
|
||||
let row;
|
||||
if (reverse) {
|
||||
row = this._selected.previousElementSibling ||
|
||||
this._rows.lastElementChild;
|
||||
} else {
|
||||
row = this._selected.nextElementSibling ||
|
||||
this._rows.firstElementChild;
|
||||
}
|
||||
|
||||
this._selected.toggleAttribute("selected", false);
|
||||
this._selected = row;
|
||||
row.toggleAttribute("selected", true);
|
||||
|
||||
let resultIndex = row.getAttribute("resultIndex");
|
||||
let result = this._queryContext.results[resultIndex];
|
||||
if (result) {
|
||||
this.input.setValueFromResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the autocomplete results popup.
|
||||
*/
|
||||
|
@ -55,9 +102,10 @@ class UrlbarView {
|
|||
// We'll need to set them up properly.
|
||||
this.oneOffSearchButtons;
|
||||
|
||||
this.panel.openPopup(this.urlbar.textbox.closest("toolbar"), "after_end", 0, -1);
|
||||
this.panel.openPopup(this.input.textbox.closest("toolbar"), "after_end", 0, -1);
|
||||
|
||||
this._rows.firstElementChild.toggleAttribute("selected", true);
|
||||
this._selected = this._rows.firstElementChild;
|
||||
this._selected.toggleAttribute("selected", true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,12 +159,12 @@ class UrlbarView {
|
|||
// Subtract two pixels for left and right borders on the panel.
|
||||
this._mainContainer.style.maxWidth = (width - 2) + "px";
|
||||
|
||||
// Keep the popup items' site icons aligned with the urlbar's identity
|
||||
// Keep the popup items' site icons aligned with the input's identity
|
||||
// icon if it's not too far from the edge of the window. We define
|
||||
// "too far" as "more than 30% of the window's width AND more than
|
||||
// 250px".
|
||||
let boundToCheck = this.window.RTL_UI ? "right" : "left";
|
||||
let inputRect = this._getBoundsWithoutFlushing(this.urlbar.textbox);
|
||||
let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
|
||||
let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
|
||||
let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
|
||||
if (alignSiteIcons) {
|
||||
|
@ -148,7 +196,6 @@ class UrlbarView {
|
|||
let result = this._queryContext.results[resultIndex];
|
||||
let item = this._createElement("div");
|
||||
item.className = "urlbarView-row";
|
||||
item.addEventListener("click", this);
|
||||
item.setAttribute("resultIndex", resultIndex);
|
||||
if (result.type == UrlbarUtils.MATCH_TYPE.TAB_SWITCH) {
|
||||
item.setAttribute("action", "switch-to-tab");
|
||||
|
@ -208,7 +255,7 @@ class UrlbarView {
|
|||
let resultIndex = row.getAttribute("resultIndex");
|
||||
let result = this._queryContext.results[resultIndex];
|
||||
if (result) {
|
||||
this.urlbar.resultSelected(event, result);
|
||||
this.input.pickResult(event, result);
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче