-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: Allow scrolling on edge of the window #8461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rstanuwijaya
wants to merge
44
commits into
zen-browser:dev
Choose a base branch
from
rstanuwijaya:fix-edge-scrolling
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 17 commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
87e491d
initial trial of edge scrolling
rstanuwijaya e5a30c1
Try to change implementation into fake mouse event but failed
rstanuwijaya cdb8608
Reverted into scroll
rstanuwijaya f932117
Activate adjacent browser
rstanuwijaya 73bde70
Try to synthesize mouse event again
rstanuwijaya a4ad405
Synthesize mouse click event
rstanuwijaya 916079b
Merge branch 'dev' into fix-edge-scrolling
mr-cheffy 2addbee
Merge branch 'dev' into fix-edge-scrolling
mr-cheffy 416db95
Working actor implementation
rstanuwijaya 67742d9
Refine code
rstanuwijaya 25bc7bd
Add prefs zen.edgescroll.enabled
rstanuwijaya ae91fc4
fix: Change the mousemove and mouseup listener to window
rstanuwijaya 84aa004
fix: Add restart prompt when updating edgescroll prefs
rstanuwijaya ee0a312
fix: Enable edgescroll on about:* pages
rstanuwijaya 892c6a8
fix: Only display zen-edge-scroll-trigger when the sidebar is on the …
rstanuwijaya 8868a4c
fix: Increase offset to 2 pixels
rstanuwijaya ebea8f6
chore: Remove logging
rstanuwijaya c100068
style: Lint code
rstanuwijaya 025e563
Merge branch 'dev' into fix-edge-scrolling
rstanuwijaya 5b44b34
Merge branch 'dev' into fix-edge-scrolling
mr-cheffy 95abc99
Merge branch 'dev' into fix-edge-scrolling
mr-cheffy ead77dc
fix: Allow content window zoom
rstanuwijaya 3c42ae3
Merge branch 'dev' into fix-edge-scrolling
mr-cheffy 33162bd
test: Add tests for ZenEdgeScroll
rstanuwijaya 83cd980
fix: Renamed var to gZenEdgeScrollManager and make _initialized as p…
rstanuwijaya 976c206
Merge remote-tracking branch 'origin/fix-edge-scrolling' into fix-edg…
rstanuwijaya e63ca21
Discard changes to src/browser/components/preferences/zen-settings.js
mr-cheffy 00145da
fix: Move edgescroll style to css
rstanuwijaya 426b1d7
fix: Move trigger to zen-appcontent-wrapper
rstanuwijaya ea33664
fix: Removed _initialized
rstanuwijaya f6448d0
style: Formatting
rstanuwijaya 83056ac
feat: Add observer for preference update
rstanuwijaya 564f7e3
fix: Rename id to specify vertical trigger
rstanuwijaya 460b979
fix: Modified logic for synthetic event
rstanuwijaya bc9cbab
fix: Specify edge scroll direction
rstanuwijaya b34da26
fix: Discard unused attribute
rstanuwijaya a7ac8a8
feat: Add horizontal trigger
rstanuwijaya 0816774
fix: Change css position to absolute + make vertical on top
rstanuwijaya 8ea7e05
fix: Correct Y offset
rstanuwijaya ee58617
Merge remote-tracking branch 'upstream/dev' into fix-edge-scrolling
rstanuwijaya f15fae0
Merge branch dev into fix-edge-scrolling
rstanuwijaya 12c39bf
fix: move edgescroll.inc
rstanuwijaya 0046eab
Merge branch 'dev' into fix-edge-scrolling
rstanuwijaya 1252fe9
Update scrollwheel sendWheelEvent
rstanuwijaya File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,4 +17,5 @@ scripts | |
workflows | ||
winsign | ||
flatpak | ||
configs | ||
configs | ||
edgescroll |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,4 +107,4 @@ document.addEventListener( | |
}); | ||
}, | ||
{ once: true } | ||
); | ||
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
{ | ||
const EDGE_INTERACTION_WIDTH_PX = Services.prefs.getIntPref("zen.theme.border-radius", 8); | ||
const SYNTHETIC_EVENT_X_OFFSET_FROM_RIGHT_EDGE = 2; | ||
const ACTOR_NAME = "ZenEdgeScroll"; // Name used for actor registration | ||
|
||
class ZenEdgeScrollManager extends ZenDOMOperatedFeature { | ||
init() { | ||
this.isSynthesizingDrag = false; | ||
this.dragInitialModel = { | ||
targetBrowserDuringDrag: null, | ||
targetBrowsingContextDuringDrag: null, | ||
}; | ||
this.edgeScrollTriggerDiv = null; // Added for the trigger div | ||
|
||
this._boundHandleMouseDown = this.handleMouseDown.bind(this); | ||
this._boundHandleSyntheticDrag = this.handleSyntheticDrag.bind(this); | ||
this._boundHandleSyntheticDragEnd = this.handleSyntheticDragEnd.bind(this); | ||
this._boundHandleWheel = this.handleWheel.bind(this); | ||
this._boundUpdateTriggerDivDisplay = this._updateTriggerDivDisplay.bind(this); // Added | ||
|
||
if (window.gZenEdgeScrollManagerInitialized) { | ||
return; | ||
} | ||
window.gZenEdgeScrollManagerInitialized = true; | ||
|
||
// Create and append the edge scroll trigger div | ||
this.edgeScrollTriggerDiv = window.document.createElement("div"); | ||
this.edgeScrollTriggerDiv.id = "zen-edge-scroll-trigger"; | ||
Object.assign(this.edgeScrollTriggerDiv.style, { | ||
position: "fixed", | ||
top: "0px", | ||
right: "0px", | ||
width: `${EDGE_INTERACTION_WIDTH_PX}px`, | ||
rstanuwijaya marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
height: "100%", | ||
zIndex: "2147483647", // Max z-index | ||
userSelect: "none", | ||
// backgroundColor: "rgba(255,0,0,0.1)", // For debugging visibility | ||
}); | ||
window.document.documentElement.appendChild(this.edgeScrollTriggerDiv); | ||
rstanuwijaya marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
this.edgeScrollTriggerDiv.addEventListener('mousedown', this._boundHandleMouseDown, true); | ||
this.edgeScrollTriggerDiv.addEventListener('wheel', this._boundHandleWheel, { capture: true, passive: false }); | ||
|
||
this._updateTriggerDivDisplay(); // Added: Set initial display state | ||
Services.prefs.addObserver("zen.tabs.vertical.right-side", this._boundUpdateTriggerDivDisplay); // Added: Observe preference | ||
} | ||
|
||
destroy() { | ||
if (this.edgeScrollTriggerDiv) { | ||
this.edgeScrollTriggerDiv.removeEventListener('mousedown', this._boundHandleMouseDown, true); | ||
this.edgeScrollTriggerDiv.removeEventListener('wheel', this._boundHandleWheel, true); | ||
if (this.edgeScrollTriggerDiv.parentNode) { | ||
this.edgeScrollTriggerDiv.parentNode.removeChild(this.edgeScrollTriggerDiv); // Corrected removeChild call | ||
} | ||
this.edgeScrollTriggerDiv = null; | ||
} | ||
// These listeners are added to window, not edgeScrollTriggerDiv in handleMouseDown | ||
window.removeEventListener('mousemove', this._boundHandleSyntheticDrag, true); | ||
window.removeEventListener('mouseup', this._boundHandleSyntheticDragEnd, true); | ||
Services.prefs.removeObserver("zen.tabs.vertical.right-side", this._boundUpdateTriggerDivDisplay); // Added: Remove observer | ||
window.gZenEdgeScrollManagerInitialized = false; | ||
} | ||
|
||
_updateTriggerDivDisplay() { // Added method | ||
if (!this.edgeScrollTriggerDiv) { | ||
return; | ||
} | ||
if (window.gZenCompactModeManager && gZenCompactModeManager.sidebarIsOnRight) { | ||
this.edgeScrollTriggerDiv.style.display = "none"; | ||
} else { | ||
this.edgeScrollTriggerDiv.style.display = "block"; | ||
} | ||
} | ||
|
||
_getParentActor() { | ||
if (!gBrowser.selectedBrowser.browsingContext.currentWindowGlobal) { | ||
return null; | ||
} | ||
try { | ||
const actor = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getActor(ACTOR_NAME); | ||
return actor; | ||
} catch (e) { | ||
console.error(`Error getting actor ${ACTOR_NAME}:`, e); | ||
return null; | ||
} | ||
} | ||
|
||
getGapZoneInfo(event) { | ||
const windowWidth = window.innerWidth; | ||
const eventClientY = event.clientY; | ||
|
||
let potentialTargetBrowser = null; | ||
let potentialTargetBrowserRect = null; | ||
|
||
const selectedBrowser = gBrowser.selectedBrowser; | ||
if (selectedBrowser && selectedBrowser.getAttribute("primary") === "true") { | ||
const selectedBrowserRect = selectedBrowser.getBoundingClientRect(); | ||
if (selectedBrowserRect.width > 0 && selectedBrowserRect.height > 0) { | ||
// Check if the browser's right edge is very close to the window's right edge | ||
const isBrowserAtRightEdge = (windowWidth - selectedBrowserRect.right) <= EDGE_INTERACTION_WIDTH_PX + 1; | ||
const isEventYWithinBrowser = eventClientY >= selectedBrowserRect.top && eventClientY <= selectedBrowserRect.bottom; | ||
if (isBrowserAtRightEdge && isEventYWithinBrowser) { | ||
potentialTargetBrowser = selectedBrowser; | ||
potentialTargetBrowserRect = selectedBrowserRect; | ||
} | ||
} | ||
} | ||
|
||
// The event is on the trigger div, so it is "in gap". We return the browser found. | ||
return { isInGap: true, targetBrowser: potentialTargetBrowser, browserRect: potentialTargetBrowserRect }; | ||
} | ||
|
||
createSyntheticEventData(originalEvent, targetBrowserRect, eventType) { | ||
const clientXInContent = Math.max(0, Math.floor(targetBrowserRect.width - SYNTHETIC_EVENT_X_OFFSET_FROM_RIGHT_EDGE)); | ||
const clientYInContent = Math.max(0, Math.min(Math.floor(originalEvent.clientY - targetBrowserRect.top), Math.floor(targetBrowserRect.height - 1))); | ||
const screenX = Math.floor(window.screenX + targetBrowserRect.left + clientXInContent); | ||
const screenY = Math.floor(window.screenY + targetBrowserRect.top + clientYInContent); | ||
|
||
return { | ||
type: eventType, clientX: clientXInContent, clientY: clientYInContent, | ||
screenX: screenX, screenY: screenY, button: originalEvent.button, | ||
buttons: (eventType === 'mousemove' || eventType === 'mousedown') ? 1 : 0, | ||
ctrlKey: originalEvent.ctrlKey, altKey: originalEvent.altKey, | ||
shiftKey: originalEvent.shiftKey, metaKey: originalEvent.metaKey, | ||
}; | ||
} | ||
|
||
handleMouseDown(event) { | ||
if (event.button !== 0) return; | ||
const gapInfo = this.getGapZoneInfo(event); // event is from edgeScrollTriggerDiv | ||
|
||
if (!gapInfo.targetBrowser) { | ||
return; | ||
} | ||
let targetBrowser = gapInfo.targetBrowser; | ||
let targetBrowserRect = gapInfo.browserRect; | ||
|
||
const parentActor = this._getParentActor(); | ||
if (!parentActor || !targetBrowser.browsingContext) { | ||
return; | ||
} | ||
|
||
event.preventDefault(); | ||
this.isSynthesizingDrag = true; | ||
this.dragInitialModel.targetBrowserDuringDrag = targetBrowser; | ||
this.dragInitialModel.targetBrowsingContextDuringDrag = targetBrowser.browsingContext; | ||
|
||
const eventData = this.createSyntheticEventData(event, targetBrowserRect, 'mousedown'); | ||
parentActor.sendEventToChild(targetBrowser.browsingContext, "ZenEdgeScroll:SynthesizeMouseEvent", eventData); | ||
|
||
window.addEventListener('mousemove', this._boundHandleSyntheticDrag, true); | ||
window.addEventListener('mouseup', this._boundHandleSyntheticDragEnd, true); | ||
} | ||
|
||
handleSyntheticDrag(event) { | ||
if (!this.isSynthesizingDrag || !this.dragInitialModel.targetBrowsingContextDuringDrag) return; | ||
|
||
const targetBrowser = this.dragInitialModel.targetBrowserDuringDrag; | ||
const targetBrowsingContext = this.dragInitialModel.targetBrowsingContextDuringDrag; | ||
|
||
if (gBrowser.selectedBrowser !== targetBrowser) { | ||
this.handleSyntheticDragEnd(event); return; | ||
} | ||
|
||
const parentActor = this._getParentActor(); | ||
if (!parentActor) { | ||
this.handleSyntheticDragEnd(event); return; | ||
} | ||
|
||
event.preventDefault(); event.stopPropagation(); | ||
const currentTargetBrowserRect = targetBrowser.getBoundingClientRect(); | ||
if (currentTargetBrowserRect.width === 0 || currentTargetBrowserRect.height === 0) { | ||
this.handleSyntheticDragEnd(event); return; | ||
} | ||
const eventData = this.createSyntheticEventData(event, currentTargetBrowserRect, 'mousemove'); | ||
parentActor.sendEventToChild(targetBrowsingContext, "ZenEdgeScroll:SynthesizeMouseEvent", eventData); | ||
} | ||
|
||
handleSyntheticDragEnd(event) { | ||
if (this.isSynthesizingDrag && this.dragInitialModel.targetBrowsingContextDuringDrag) { | ||
const targetBrowser = this.dragInitialModel.targetBrowserDuringDrag; | ||
const targetBrowsingContext = this.dragInitialModel.targetBrowsingContextDuringDrag; | ||
const parentActor = this._getParentActor(); | ||
|
||
if (parentActor && event) { // If called by an event | ||
event.preventDefault(); event.stopPropagation(); | ||
const currentTargetBrowserRect = targetBrowser.getBoundingClientRect(); | ||
if (currentTargetBrowserRect.width > 0 && currentTargetBrowserRect.height > 0) { | ||
const eventData = this.createSyntheticEventData(event, currentTargetBrowserRect, 'mouseup'); | ||
parentActor.sendEventToChild(targetBrowsingContext, "ZenEdgeScroll:SynthesizeMouseEvent", eventData); | ||
} else { | ||
} | ||
} else if (parentActor && !event) { // Called without event (e.g. drag cancelled) | ||
// Optionally send a generic mouseup if needed, or just clean up. | ||
} | ||
} | ||
this.isSynthesizingDrag = false; | ||
this.dragInitialModel.targetBrowserDuringDrag = null; | ||
this.dragInitialModel.targetBrowsingContextDuringDrag = null; | ||
window.removeEventListener('mousemove', this._boundHandleSyntheticDrag, true); | ||
window.removeEventListener('mouseup', this._boundHandleSyntheticDragEnd, true); | ||
} | ||
|
||
handleWheel(event) { | ||
const gapInfo = this.getGapZoneInfo(event); // event is from edgeScrollTriggerDiv | ||
|
||
if (!gapInfo.targetBrowser) { | ||
return; | ||
} | ||
|
||
const targetBrowser = gapInfo.targetBrowser; | ||
const targetBrowserRect = gapInfo.browserRect; | ||
const parentActor = this._getParentActor(); | ||
|
||
event.preventDefault(); event.stopPropagation(); | ||
const wheelData = { | ||
deltaX: event.deltaX, deltaY: event.deltaY, deltaZ: event.deltaZ, deltaMode: event.deltaMode, | ||
ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey, metaKey: event.metaKey, | ||
clientX: Math.max(0, Math.floor(targetBrowserRect.width - SYNTHETIC_EVENT_X_OFFSET_FROM_RIGHT_EDGE)), | ||
clientY: Math.max(0, Math.min(Math.floor(event.clientY - targetBrowserRect.top), Math.floor(targetBrowserRect.height - 1))) | ||
}; | ||
parentActor.sendEventToChild(targetBrowser.browsingContext, "ZenEdgeScroll:DispatchWheel", { wheelData }); | ||
} | ||
} | ||
|
||
// Actor Registration (must happen before manager instantiation if manager relies on actors being ready) | ||
// This is modeled after ZenGlanceManager's registerWindowActors | ||
function registerEdgeScrollActors() { | ||
if (Services.prefs.getBoolPref('zen.edgescroll.enabled', true)) { | ||
window.gZenEdgeScrollManagerInstance = new ZenEdgeScrollManager(); | ||
|
||
const actorConfig = { | ||
parent: { | ||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenEdgeScrollParent.sys.mjs', | ||
}, | ||
child: { | ||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenEdgeScrollChild.sys.mjs', | ||
}, | ||
allFrames: true, | ||
matches: [ | ||
'*://*/*', | ||
'about:*', // For about: pages | ||
], | ||
includeChrome: true, // <--- ENSURE THIS LINE IS PRESENT AND SET TO TRUE | ||
}; | ||
|
||
if (window.gZenActorsManager && typeof window.gZenActorsManager.addJSWindowActor === 'function') { | ||
window.gZenActorsManager.addJSWindowActor(ACTOR_NAME, actorConfig); | ||
} else { | ||
console.error(`Failed to register ${ACTOR_NAME} actors:`, e); | ||
} | ||
} | ||
} | ||
|
||
registerEdgeScrollActors(); | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.