From be25636bc61a87ff9daaa78d9236e6b6e4d7df0d Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:49:57 +0200 Subject: [PATCH 1/5] Only flash sidebar when mouse leaves the browser, refactor flashSidebar --- src/ZenCompactMode.mjs | 111 ++++++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/src/ZenCompactMode.mjs b/src/ZenCompactMode.mjs index 767c64e..97fe7d1 100644 --- a/src/ZenCompactMode.mjs +++ b/src/ZenCompactMode.mjs @@ -1,5 +1,5 @@ var gZenCompactModeManager = { - _flashSidebarTimeout: null, + _flashTimeouts: {}, init() { Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this)); @@ -8,6 +8,7 @@ var gZenCompactModeManager = { gZenUIManager.addPopupTrackingAttribute(this.sidebar); gZenUIManager.addPopupTrackingAttribute(document.getElementById('zen-appcontent-navbar-container')); + Services.prefs.addObserver('zen.tabs.vertical.right-side', this._updateSidebarIsOnRight.bind(this)); this.addMouseActions(); }, @@ -19,6 +20,13 @@ var gZenCompactModeManager = { Services.prefs.setBoolPref('zen.view.compact', value); }, + get sidebarIsOnRight() { + if (this._sidebarIsOnRight) { + return this._sidebarIsOnRight; + } + return Services.prefs.getBoolPref('zen.tabs.vertical.right-side'); + }, + get sidebar() { if (!this._sidebar) { this._sidebar= document.getElementById('navigator-toolbox'); @@ -38,6 +46,10 @@ var gZenCompactModeManager = { this._flashSidebarDuration = Services.prefs.getIntPref('zen.view.compact.toolbar-flash-popup.duration'); }, + _updateSidebarIsOnRight() { + this._sidebarIsOnRight = Services.prefs.getBoolPref('zen.tabs.vertical.right-side'); + }, + toggleSidebar() { this.sidebar.toggleAttribute('zen-user-show'); }, @@ -58,47 +70,100 @@ var gZenCompactModeManager = { get hoverableElements() { return [ - this.sidebar, - document.getElementById('zen-appcontent-navbar-container'), + { + element: this.sidebar, + getScreenEdge: () => this.sidebarIsOnRight ? "right" : "left", + }, + { + element: document.getElementById('zen-appcontent-navbar-container'), + getScreenEdge: () => "top", + } ]; }, - flashSidebar(element = null, duration = null) { - if (!element) { - element = this.sidebar; - } - if (!duration) { - duration = this.flashSidebarDuration; - } + flashSidebar(duration = this.flashSidebarDuration) { let tabPanels = document.getElementById('tabbrowser-tabpanels'); - if (element.matches(':hover') || tabPanels.matches("[zen-split-view='true']")) { + if (!tabPanels.matches("[zen-split-view='true']")) { + this.flashElement(this.sidebar, duration, this.sidebar.id); + } + }, + + flashElement(element, duration, id, attrName = 'flash-popup') { + if (element.matches(':hover')) { return; } - if (this._flashSidebarTimeout) { - clearTimeout(this._flashSidebarTimeout); + if (this._flashTimeouts[id]) { + clearTimeout(this._flashTimeouts[id]); } else { - window.requestAnimationFrame(() => element.setAttribute('flash-popup', '')); + requestAnimationFrame(() => element.setAttribute(attrName, '')); } - this._flashSidebarTimeout = setTimeout(() => { + this._flashTimeouts[id] = setTimeout(() => { window.requestAnimationFrame(() => { - element.removeAttribute('flash-popup'); - this._flashSidebarTimeout = null; + element.removeAttribute(attrName); + this._flashTimeouts[id] = null; }); }, duration); }, + clearFlashTimeout(id) { + clearTimeout(this._flashTimeouts[id]); + this._flashTimeouts[id] = null; + }, + addMouseActions() { for (let i = 0; i < this.hoverableElements.length; i++) { - this.hoverableElements[i].addEventListener('mouseenter', (event) => { - let target = this.hoverableElements[i]; + const target = this.hoverableElements[i].element; + target.addEventListener('mouseenter', (event) => { target.setAttribute('zen-user-hover', 'true'); }); - this.hoverableElements[i].addEventListener('mouseleave', (event) => { - let target = this.hoverableElements[i]; - this.flashSidebar(target, this.hideAfterHoverDuration); - }); + if (this.hoverableElements[i].keepHoverDuration) { + target.addEventListener('mouseleave', (event) => { + this.flashSidebar(target, keepHoverDuration, target.id, 'hover-timeout'); + }); + } } + + document.body.addEventListener('mouseleave', (event) => { + for (let i = 0; i < this.hoverableElements.length; i++) { + const target = this.hoverableElements[i].element; + const edge = this.hoverableElements[i].getScreenEdge(); + if (!edge) return; + const orient = (edge === "left" || edge === "right" ? "vertical" : "horizontal"); + if ( + this._getNearestEdge(document.body, event.pageX, event.pageY) === edge + && this._positionIsAligned(orient, target, event.pageX, event.pageY, 7) + ) { + this.flashElement(target, this.hideAfterHoverDuration, target.id); + + document.addEventListener('mousemove', () => { + target.removeAttribute('flash-popup'); + this.clearFlashTimeout(target.id); + }, {once: true}); + } + } + }); + }, + + _getNearestEdge(element, posX, posY) { + const targetBox = element.getBoundingClientRect(); + let smallestDistance = Infinity; + let closestEdge = ""; + for (let edge of ["top", "bottom", "left", "right"]) { + const onXAxis = edge === "left" || edge === "right"; + const distance = Math.abs( (onXAxis ? posX : posY) - targetBox[edge]); + if (smallestDistance > distance) { + smallestDistance = distance; + closestEdge = edge; + } + } + return closestEdge; + }, + + _positionIsAligned(axis = "x", element, x, y, error = 0) { + const bBox = element.getBoundingClientRect(); + if (axis === "x") return bBox.top - error < y && y < bBox.bottom + error; + else return bBox.left - error < x && x < bBox.right + error; }, toggleToolbar() { From 5a9d14b2c2278c12cb5398efb70cb198ca8a4e3a Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:31:09 +0200 Subject: [PATCH 2/5] Slight refactor and fixes to mouseActions --- src/ZenCompactMode.mjs | 45 ++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/ZenCompactMode.mjs b/src/ZenCompactMode.mjs index 01a5521..59fd3ba 100644 --- a/src/ZenCompactMode.mjs +++ b/src/ZenCompactMode.mjs @@ -78,11 +78,11 @@ var gZenCompactModeManager = { return [ { element: this.sidebar, - getScreenEdge: () => this.sidebarIsOnRight ? "right" : "left", + screenEdge: this.sidebarIsOnRight ? "right" : "left", }, { element: document.getElementById('zen-appcontent-navbar-container'), - getScreenEdge: () => "top", + screenEdge:"top", } ]; }, @@ -125,28 +125,25 @@ var gZenCompactModeManager = { if (this.hoverableElements[i].keepHoverDuration) { target.addEventListener('mouseleave', (event) => { - this.flashSidebar(target, keepHoverDuration, target.id, 'hover-timeout'); + this.flashElement(target, keepHoverDuration, target.id, 'hover-timeout'); }); } } document.body.addEventListener('mouseleave', (event) => { - for (let i = 0; i < this.hoverableElements.length; i++) { - const target = this.hoverableElements[i].element; - const edge = this.hoverableElements[i].getScreenEdge(); - if (!edge) return; - const orient = (edge === "left" || edge === "right" ? "vertical" : "horizontal"); - if ( - this._getNearestEdge(document.body, event.pageX, event.pageY) === edge - && this._positionIsAligned(orient, target, event.pageX, event.pageY, 7) - ) { - this.flashElement(target, this.hideAfterHoverDuration, target.id); - - document.addEventListener('mousemove', () => { - target.removeAttribute('flash-popup'); - this.clearFlashTimeout(target.id); - }, {once: true}); + for (let entry of this.hoverableElements) { + if (!entry.screenEdge) return; + const target = entry.element; + const notAxis = (edge === "left" || edge === "right" ? "y" : "x"); + if (this._getNearestEdge(document.body, event.pageX, event.pageY) !== entry.screenEdge + || this._positionInBounds(notAxis, target, event.pageX, event.pageY, 7)) { + return; } + this.flashElement(target, this.hideAfterHoverDuration, target.id); + document.addEventListener('mousemove', () => { + target.removeAttribute('flash-popup'); + this.clearFlashTimeout(target.id); + }, {once: true}); } }); }, @@ -155,20 +152,20 @@ var gZenCompactModeManager = { const targetBox = element.getBoundingClientRect(); let smallestDistance = Infinity; let closestEdge = ""; - for (let edge of ["top", "bottom", "left", "right"]) { - const onXAxis = edge === "left" || edge === "right"; - const distance = Math.abs( (onXAxis ? posX : posY) - targetBox[edge]); + const edges = ["top", "bottom", "left", "right"]; + for (let i = 0; i < edges.length; i++) { + const distance = Math.abs( (i < 2 ? posY : posX) - targetBox[edges[i]]); if (smallestDistance > distance) { smallestDistance = distance; - closestEdge = edge; + closestEdge = edges[i]; } } return closestEdge; }, - _positionIsAligned(axis = "x", element, x, y, error = 0) { + _positionInBounds(axis = "x", element, x, y, error = 0) { const bBox = element.getBoundingClientRect(); - if (axis === "x") return bBox.top - error < y && y < bBox.bottom + error; + if (axis === "y") return bBox.top - error < y && y < bBox.bottom + error; else return bBox.left - error < x && x < bBox.right + error; }, From 10a9fe513f06e9e133223da9e2e1538e12ed2bfc Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 22 Sep 2024 11:02:34 +0200 Subject: [PATCH 3/5] Refactor mouse actions, fixes for hover, screenLeave --- src/ZenCompactMode.mjs | 56 ++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/ZenCompactMode.mjs b/src/ZenCompactMode.mjs index 59fd3ba..9729e60 100644 --- a/src/ZenCompactMode.mjs +++ b/src/ZenCompactMode.mjs @@ -5,11 +5,11 @@ var gZenCompactModeManager = { init() { Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this)); Services.prefs.addObserver('zen.view.compact.toolbar-flash-popup.duration', this._updatedSidebarFlashDuration.bind(this)); + Services.prefs.addObserver('zen.tabs.vertical.right-side', this._updateSidebarIsOnRight.bind(this)); gZenUIManager.addPopupTrackingAttribute(this.sidebar); gZenUIManager.addPopupTrackingAttribute(document.getElementById('zen-appcontent-navbar-container')); - Services.prefs.addObserver('zen.tabs.vertical.right-side', this._updateSidebarIsOnRight.bind(this)); this.addMouseActions(); }, @@ -101,7 +101,7 @@ var gZenCompactModeManager = { if (this._flashTimeouts[id]) { clearTimeout(this._flashTimeouts[id]); } else { - requestAnimationFrame(() => element.setAttribute(attrName, '')); + requestAnimationFrame(() => element.setAttribute(attrName, 'true')); } this._flashTimeouts[id] = setTimeout(() => { window.requestAnimationFrame(() => { @@ -118,49 +118,45 @@ var gZenCompactModeManager = { addMouseActions() { for (let i = 0; i < this.hoverableElements.length; i++) { - this.hoverableElements[i].addEventListener('mouseenter', (event) => { - let target = this.hoverableElements[i]; + let target = this.hoverableElements[i].element; + target.addEventListener('mouseenter', (event) => { + this.clearFlashTimeout('has-hover' + target.id); target.setAttribute('zen-has-hover', 'true'); }); - if (this.hoverableElements[i].keepHoverDuration) { - target.addEventListener('mouseleave', (event) => { - this.flashElement(target, keepHoverDuration, target.id, 'hover-timeout'); - }); - } + target.addEventListener('mouseleave', (event) => { + if (this.hoverableElements[i].keepHoverDuration) { + this.flashElement(target, keepHoverDuration, "has-hover" + target.id, 'zen-has-hover'); + } else { + target.removeAttribute('zen-has-hover'); + } + }); } - document.body.addEventListener('mouseleave', (event) => { + document.documentElement.addEventListener('mouseleave', (event) => { for (let entry of this.hoverableElements) { - if (!entry.screenEdge) return; + if (!entry.screenEdge) continue; const target = entry.element; - const notAxis = (edge === "left" || edge === "right" ? "y" : "x"); - if (this._getNearestEdge(document.body, event.pageX, event.pageY) !== entry.screenEdge - || this._positionInBounds(notAxis, target, event.pageX, event.pageY, 7)) { - return; + const boundAxis = (entry.screenEdge === "right" || entry.screenEdge === "left" ? "y" : "x"); + if (!this._crossedEdge(entry.screenEdge, event.pageX, event.pageY) || !this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) { + continue; } - this.flashElement(target, this.hideAfterHoverDuration, target.id); + this.flashElement(target, this.hideAfterHoverDuration, "has-hover" + target.id, 'zen-has-hover'); document.addEventListener('mousemove', () => { - target.removeAttribute('flash-popup'); - this.clearFlashTimeout(target.id); + if (target.matches(':hover')) return; + target.removeAttribute('zen-has-hover'); + this.clearFlashTimeout('has-hover' + target.id); }, {once: true}); } }); }, - _getNearestEdge(element, posX, posY) { + _crossedEdge(edge, posX, posY, element = document.documentElement, maxDistance = 10) { + posX = Math.max(0, posX); + posY = Math.max(0, posY); const targetBox = element.getBoundingClientRect(); - let smallestDistance = Infinity; - let closestEdge = ""; - const edges = ["top", "bottom", "left", "right"]; - for (let i = 0; i < edges.length; i++) { - const distance = Math.abs( (i < 2 ? posY : posX) - targetBox[edges[i]]); - if (smallestDistance > distance) { - smallestDistance = distance; - closestEdge = edges[i]; - } - } - return closestEdge; + const distance = Math.abs( ((edge === "right" || edge === "left") ? posX : posY) - targetBox[edge]); + return distance <= maxDistance; }, _positionInBounds(axis = "x", element, x, y, error = 0) { From 481725c67deb1759ad84439c8c2e5341bc9747af Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:11:14 +0200 Subject: [PATCH 4/5] Refactor ZenCompactMode._getCrossedEdge --- src/ZenCompactMode.mjs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ZenCompactMode.mjs b/src/ZenCompactMode.mjs index 9729e60..a3c62cc 100644 --- a/src/ZenCompactMode.mjs +++ b/src/ZenCompactMode.mjs @@ -134,11 +134,13 @@ var gZenCompactModeManager = { } document.documentElement.addEventListener('mouseleave', (event) => { + const screenEdgeCrossed = this._getCrossedEdge(event.pageX, event.pageY); + if (!screenEdgeCrossed) return; for (let entry of this.hoverableElements) { - if (!entry.screenEdge) continue; + if (screenEdgeCrossed !== entry.screenEdge) continue; const target = entry.element; const boundAxis = (entry.screenEdge === "right" || entry.screenEdge === "left" ? "y" : "x"); - if (!this._crossedEdge(entry.screenEdge, event.pageX, event.pageY) || !this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) { + if (!this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) { continue; } this.flashElement(target, this.hideAfterHoverDuration, "has-hover" + target.id, 'zen-has-hover'); @@ -151,12 +153,14 @@ var gZenCompactModeManager = { }); }, - _crossedEdge(edge, posX, posY, element = document.documentElement, maxDistance = 10) { + _getCrossedEdge(posX, posY, element = document.documentElement, maxDistance = 10) { posX = Math.max(0, posX); posY = Math.max(0, posY); const targetBox = element.getBoundingClientRect(); - const distance = Math.abs( ((edge === "right" || edge === "left") ? posX : posY) - targetBox[edge]); - return distance <= maxDistance; + return ["top", "bottom", "left", "right"].find((edge, i) => { + const distance = Math.abs((i < 2 ? posY : posX) - targetBox[edge]); + return distance <= maxDistance; + }); }, _positionInBounds(axis = "x", element, x, y, error = 0) { From c8d641a01b2f9128ef14d6a4497d85104c36c4e3 Mon Sep 17 00:00:00 2001 From: brahim <92426196+BrhmDev@users.noreply.github.com> Date: Sun, 22 Sep 2024 13:26:53 +0200 Subject: [PATCH 5/5] Fix flashBar duration after merge --- src/ZenCompactMode.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZenCompactMode.mjs b/src/ZenCompactMode.mjs index 7215967..9a94ffa 100644 --- a/src/ZenCompactMode.mjs +++ b/src/ZenCompactMode.mjs @@ -144,7 +144,7 @@ var gZenCompactModeManager = { ]; }, - flashSidebar(duration = this.flashSidebarDuration) { + flashSidebar(duration = lazyCompactMode.COMPACT_MODE_FLASH_DURATION) { let tabPanels = document.getElementById('tabbrowser-tabpanels'); if (!tabPanels.matches("[zen-split-view='true']")) { this.flashElement(this.sidebar, duration, this.sidebar.id);