From 65d88dc7c4c5331e11b66ed2978ac474caf31a3e Mon Sep 17 00:00:00 2001 From: Jeremias Bosch Date: Fri, 27 Aug 2010 16:30:45 -0400 Subject: [PATCH] Bug 587492 - More consistent zooming into input elements [r=vingtetun, mfinkle] --- mobile/app/mobile.js | 4 + mobile/chrome/content/BrowserView.js | 1 + mobile/chrome/content/browser-ui.js | 162 +++++++++++++++++++++++---- mobile/chrome/content/browser.js | 1 - mobile/chrome/content/forms.js | 84 +++++++------- 5 files changed, 190 insertions(+), 62 deletions(-) diff --git a/mobile/app/mobile.js b/mobile/app/mobile.js index 5b09388ea33..0323a82516e 100644 --- a/mobile/app/mobile.js +++ b/mobile/app/mobile.js @@ -139,6 +139,10 @@ pref("signon.SignonFileName", "signons.txt"); pref("formhelper.enabled", true); pref("formhelper.autozoom", true); pref("formhelper.restore", false); +pref("formhelper.caretLines.portrait", 4); +pref("formhelper.caretLines.landscape", 1); +pref("formhelper.harmonizeValue", 10); +pref("formhelper.margin", 15); /* find helper */ pref("findhelper.autozoom", true); diff --git a/mobile/chrome/content/BrowserView.js b/mobile/chrome/content/BrowserView.js index b829bf666f6..86e43c4df5d 100644 --- a/mobile/chrome/content/BrowserView.js +++ b/mobile/chrome/content/BrowserView.js @@ -201,6 +201,7 @@ BrowserView.prototype = { this._browserViewportState = null; this._renderMode = 0; this._offscreenDepth = 0; + this._visibleScreenArea = new Rect(0, 0, 0, 0); let cacheSize = Services.prefs.getIntPref("tile.cache.size"); diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js index 767513e49b8..4a672396b6b 100644 --- a/mobile/chrome/content/browser-ui.js +++ b/mobile/chrome/content/browser-ui.js @@ -451,6 +451,7 @@ var BrowserUI = { uninit: function() { ExtensionsView.uninit(); ConsoleView.uninit(); + FormHelperUI.uninit(); }, update: function(aState) { @@ -1590,6 +1591,10 @@ var FormHelperUI = { close: "cmd_formClose" }, + //for resize/rotate case + _currentCaretRect: null, + _currentElementRect: null, + init: function formHelperInit() { this._container = document.getElementById("content-navigator"); this._autofillContainer = document.getElementById("form-helper-autofill"); @@ -1600,6 +1605,7 @@ var FormHelperUI = { messageManager.addMessageListener("FormAssist:Show", this); messageManager.addMessageListener("FormAssist:Hide", this); messageManager.addMessageListener("FormAssist:Update", this); + messageManager.addMessageListener("FormAssist:Resize", this); messageManager.addMessageListener("FormAssist:AutoComplete", this); // Listen for events where form assistant should be closed @@ -1609,6 +1615,12 @@ var FormHelperUI = { // Listen for modal dialog to show/hide the UI messageManager.addMessageListener("DOMWillOpenModalDialog", this); messageManager.addMessageListener("DOMModalDialogClosed", this); + + Services.obs.addObserver(this, "softkb-change", false); + }, + + uninit: function formHelperUninit() { + Services.obs.removeObserver(this, "softkb-change"); }, show: function formHelperShow(aElement, aHasPrevious, aHasNext) { @@ -1630,13 +1642,22 @@ var FormHelperUI = { } this._updateContainer(lastElement, this._currentElement); - this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect)); + //hide all sidebars, this will adjust the visible rect. + Browser.hideSidebars(); + + //save the element Rect and reuse it to avoid jumps in cases the element moves slighty on the website. + this._currentElementRect = Rect.fromRect(aElement.rect); + this._zoom(this._currentElementRect, Rect.fromRect(aElement.caretRect)); }, hide: function formHelperHide() { if (!this._open) return; + // reset current Element and Caret Rect + this._currentElementRect = null; + this._currentCaretRect = null; + this._updateContainerForSelect(this._currentElement, null); this._open = false; }, @@ -1667,8 +1688,19 @@ var FormHelperUI = { this._container.contentHasChanged(); break; - case "FormAssist:Update": - this._zoom(null, Rect.fromRect(json.caretRect)); + case "FormAssist:Resize": + // First hide all tool/sidebars they take to much space, this will adjust the visible rect. + Browser.hideSidebars(); + this._zoom(this._currentElementRect, this._currentCaretRect); + this._container.contentHasChanged(); + break; + + case "FormAssist:Update": + // Using currentElementRect here is maybe not 100% perfect since + // elements might change there position while typing + // out of screen movement is covered by simply following the caret + // as long as we see what we type, let the element move + this._zoom(this._currentElementRect, Rect.fromRect(json.caretRect)); break; case "DOMWillOpenModalDialog": @@ -1686,6 +1718,16 @@ var FormHelperUI = { break; } }, + + observe: function formHelperObserve(aSubject, aTopic, aData) { + let rect = Rect.fromRect(JSON.parse(aData)); + rect.height = rect.bottom - rect.top; + rect.width = rect.right - rect.left; + + Browser._browserView._visibleScreenArea = rect; + BrowserUI.sizeControls(rect.width, rect.height); + this._zoom(this._currentElementRect, this._currentCaretRect); + }, goToPrevious: function formHelperGoToPrevious() { Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { }); @@ -1712,6 +1754,7 @@ var FormHelperUI = { let bv = Browser._browserView; bv.ignorePageScroll(aVal); this._container.hidden = !aVal; + this._container.contentHasChanged(); if (aVal) { this._zoomStart(); @@ -1796,31 +1839,106 @@ var FormHelperUI = { /** Zoom and move viewport so that element is legible and touchable. */ _zoom: function _formHelperZoom(aElementRect, aCaretRect) { let bv = Browser._browserView; - let zoomRect = bv.getVisibleRect(); - // Zoom to a specified Rect - if (aElementRect && bv.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom")) { - // Zoom to an element by keeping the caret into view - let zoomLevel = Browser._getZoomLevelForRect(aElementRect); - zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax); + if (aElementRect && aCaretRect && this._open) { + this._currentCaretRect = aCaretRect; - zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel); - Browser.animatedZoomTo(zoomRect); - } + // might not always be set, if not - use the windowsize + let visibleScreenArea = !bv._visibleScreenArea.isEmpty() ? bv._visibleScreenArea : new Rect(0, 0, window.innerWidth, window.innerHeight); - // Move the view to show the caret if needed - if (aCaretRect) { - let caretRect = bv.browserToViewportRect(aCaretRect); - if (zoomRect.contains(caretRect)) - return; + // respect the helper container in setting the correct viewAreaHeight + let viewAreaHeight = visibleScreenArea.height - this._container.getBoundingClientRect().height; + let viewAreaWidth = visibleScreenArea.width; + let caretLines = Services.prefs.getIntPref("formhelper.caretLines.portrait"); + let harmonizeValue = Services.prefs.getIntPref("formhelper.harmonizeValue"); - let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect); - if (deltaX != 0 || deltaY != 0) { - Browser.contentScrollboxScroller.scrollBy(deltaX, deltaY); - bv.onAfterVisibleMove(); + if (!Util.isPortrait()) + caretLines = Services.prefs.getIntPref("formhelper.caretLines.landscape"); + + // hide titlebar if the remaining space would be smaller than the height of the titlebar itself + // if there is enough space left than adjust the height. Since this adjust the the visible rects + // there is no need to adjust the y later + let toolbar = document.getElementById("toolbar-main"); + if (viewAreaHeight - toolbar.boxObject.height <= toolbar.boxObject.height * 2) + Browser.hideTitlebar(); + else + viewAreaHeight -= toolbar.boxObject.height; + + // To ensure the correct calculation when the sidebars are visible - get the sidebar size and + // use them as margin + let [leftvis, rightvis, leftW, rightW] = Browser.computeSidebarVisibility(0, 0); + let marginLeft = leftvis ? leftW : 0; + let marginRight = rightvis ? rightW : 0; + + // The height and Y of the caret might change during writing - even in cases the + // fontsize keeps the same. To avoid unneeded zooming and scrolling + let harmonizedCaretHeight = 0; + let harmonizedCaretY = 0; + + // Start calculation here, the order is important + // All calculations are done in non_zoomed_coordinates => 1:1 to "screen-pixels" + + // for buttons and non input field elements a caretRect with the height of 0 gets reported + // cover this case. + if (!aCaretRect.isEmpty()) { + // the height and y position may vary from letter to letter + // adjust position and zooming only if a bigger step was done. + harmonizedCaretHeight = aCaretRect.height - aCaretRect.height % harmonizeValue; + harmonizedCaretY = aCaretRect.y - aCaretRect.y % harmonizeValue; + } else { + harmonizedCaretHeight = 30; // fallback height + + // use the element as position + harmonizedCaretY = aElementRect.y; + aCaretRect.x = aElementRect.x; } - Browser.animatedZoomTo(zoomRect); + let zoomLevel = bv.getZoomLevel(); + let enableZoom = bv.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom"); + if (enableZoom) { + zoomLevel = (viewAreaHeight / caretLines) / harmonizedCaretHeight; + zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax); + } + viewAreaWidth /= zoomLevel; + + const margin = Services.prefs.getIntPref("formhelper.margin"); + + // if the viewAreaWidth is smaller than the neutralized position + margins. + // [YES] use the x position of the element minus margins as x position for our visible rect. + // [NO] use the x position of the caret minus margins as the x position for our visible rect. + let x = (marginLeft + marginRight + margin + aCaretRect.x - aElementRect.x) < viewAreaWidth + ? aElementRect.x - margin - marginLeft + : aCaretRect.x - viewAreaWidth + margin + marginRight; + // use the adjustet Caret Y minus a margin four our visible rect + let y = harmonizedCaretY - margin; + + // from here on play with zoomed values + // if we want to have it animated, build up zoom rect and animate. + if (enableZoom && bv.getZoomLevel() != zoomLevel) { + let vis = bv.getVisibleRect(); + x = bv.browserToViewport(x); + y = bv.browserToViewport(y); + + //dont use browser functions they are bogus for this case + let zoomRatio = zoomLevel / bv.getZoomLevel(); + let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio; + let zoomRect = new Rect(x, y, newVisW, newVisH); + + Browser.animatedZoomTo(zoomRect); + } + else { // no zooming at all + let vis = bv.getVisibleRect(); + // get our x and y in viewport "zoomed" coordinates + x = bv.browserToViewport(x); + y = bv.browserToViewport(y); + + Browser.contentScrollboxScroller.scrollBy(x-vis.x, y-vis.y); + + // workaround for tilemanager bug, after scrolling one screen height, text gets not painted on typing + bv.invalidateEntireView(); + + bv.onAfterVisibleMove(); + } } }, diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index 18d0d034de5..451dd804f8f 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -2157,7 +2157,6 @@ const gSessionHistoryObserver = { } }; - var MemoryObserver = { observe: function mo_observe() { window.QueryInterface(Ci.nsIInterfaceRequestor) diff --git a/mobile/chrome/content/forms.js b/mobile/chrome/content/forms.js index 5dc1dd54a1a..7093d6bbdc4 100644 --- a/mobile/chrome/content/forms.js +++ b/mobile/chrome/content/forms.js @@ -65,6 +65,9 @@ function FormAssistant() { addMessageListener("FormAssist:AutoComplete", this); addEventListener("keyup", this, false); + + // change on rotation/resize + addEventListener("resize", this, false); }; FormAssistant.prototype = { @@ -186,51 +189,54 @@ FormAssistant.prototype = { if (!this._enabled || !this.currentElement) return; - let currentElement = this.currentElement; - switch (aEvent.keyCode) { - case aEvent.DOM_VK_DOWN: - if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) { - if (this._hasKeyListener(currentElement)) - return; - } - else if (currentElement instanceof HTMLTextAreaElement) { - let existSelection = currentElement.selectionEnd - currentElement.selectionStart; - let isEnd = (currentElement.textLength == currentElement.selectionEnd); - if (!isEnd || existSelection) - return; - } + // change zoom on resize/rotation + if (aEvent.type == "resize") { + sendAsyncMessage("FormAssist:Resize"); + } else { + let currentElement = this.currentElement; + switch (aEvent.keyCode) { + case aEvent.DOM_VK_DOWN: + if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) { + if (this._hasKeyListener(currentElement)) + return; + } + else if (currentElement instanceof HTMLTextAreaElement) { + let existSelection = currentElement.selectionEnd - currentElement.selectionStart; + let isEnd = (currentElement.textLength == currentElement.selectionEnd); + if (!isEnd || existSelection) + return; + } - this.currentIndex++; - break; + this.currentIndex++; + break; - case aEvent.DOM_VK_UP: - if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) { - if (this._hasKeyListener(currentElement)) - return; - } - else if (currentElement instanceof HTMLTextAreaElement) { - let existSelection = currentElement.selectionEnd - currentElement.selectionStart; - let isStart = (currentElement.selectionEnd == 0); - if (!isStart || existSelection) - return; - } + case aEvent.DOM_VK_UP: + if (currentElement instanceof HTMLInputElement && !this._isAutocomplete(currentElement)) { + if (this._hasKeyListener(currentElement)) + return; + } + else if (currentElement instanceof HTMLTextAreaElement) { + let existSelection = currentElement.selectionEnd - currentElement.selectionStart; + let isStart = (currentElement.selectionEnd == 0); + if (!isStart || existSelection) + return; + } - this.currentIndex--; - break; + this.currentIndex--; + break; - case aEvent.DOM_VK_RETURN: - break; + case aEvent.DOM_VK_RETURN: + break; - default: - if (this._isAutocomplete(aEvent.target)) { - sendAsyncMessage("FormAssist:AutoComplete", this._getJSON()); - } - break; - } + default: + if (this._isAutocomplete(aEvent.target)) + sendAsyncMessage("FormAssist:AutoComplete", this._getJSON()); + break; + } - let caretRect = this._getCaretRect(); - if (!caretRect.isEmpty()) { - sendAsyncMessage("FormAssist:Update", { caretRect: caretRect }); + let caretRect = this._getCaretRect(); + if (!caretRect.isEmpty()) + sendAsyncMessage("FormAssist:Update", { caretRect: caretRect }); } },