Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions content_scripts/link_hints.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ class LinkHintsMode {
}

this.setIndicator();
// Ensure overlapping markers don't visually stack on top of each other.
if (Settings.get("suppressOverlappingHintMarkers")) {
this.suppressOverlappingMarkers();
}
}

setOpenLinkMode(mode, shouldPropagateToOtherFrames) {
Expand Down Expand Up @@ -607,6 +611,10 @@ class LinkHintsMode {
for (const matched of linksMatched) {
this.showMarker(matched, this.markerMatcher.hintKeystrokeQueue.length);
}
// After visibility changes, suppress overlaps so only one marker per overlapping region shows.
if (Settings.get("suppressOverlappingHintMarkers")) {
this.suppressOverlappingMarkers();
}
}

return this.setIndicator();
Expand Down Expand Up @@ -690,6 +698,54 @@ class LinkHintsMode {
this.renderHints();
}

// Hide all but one visible marker in any overlapping group of markers.
suppressOverlappingMarkers() {
if (!this.containerEl) return;
// Consider only local, visible markers.
const visibleMarkers = this.hintMarkers.filter((m) => m.isLocalMarker() && (m.element.style.display !== "none"));
if (visibleMarkers.length <= 1) return;

// Refresh marker rects.
for (const m of visibleMarkers) {
const rect = m.element.getClientRects()[0];
m.markerRect = rect;
}

// Build stacks of overlapping markers (O(n^2)), merging stacks if necessary.
let stacks = [];
for (const m of visibleMarkers) {
if (!m.markerRect) continue;
let stackForThisMarker = null;
const results = [];
for (const stack of stacks) {
const overlaps = this.markerOverlapsStack(m, stack);
if (overlaps && (stackForThisMarker == null)) {
stack.push(m);
stackForThisMarker = stack;
results.push(stack);
} else if (overlaps && (stackForThisMarker != null)) {
// Merge overlapping stacks.
stackForThisMarker.push(...stack);
// Do not push this stack into results to effectively discard it.
} else {
results.push(stack);
}
}
stacks = results;
if (stackForThisMarker == null) stacks.push([m]);
}

// For each overlapping stack, hide all but the last marker (topmost after current ordering).
for (const stack of stacks) {
if (stack.length <= 1) continue;
for (let i = 0; i < stack.length - 1; i++) {
stack[i].element.style.display = "none";
}
// Ensure one remains visible.
stack[stack.length - 1].element.style.display = "";
}
}

// When only one hint remains, activate it in the appropriate way. The current frame may or may
// not contain the matched link, and may or may not have the focus. The resulting four cases are
// accounted for here by selectively pushing the appropriate HintCoordinator.onExit handlers.
Expand Down
2 changes: 2 additions & 0 deletions lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ w: https://www.wikipedia.org/w/index.php?title=Special:Search&search=%s Wikipedi
waitForEnterForFilteredHints: true,
helpDialog_showAdvancedCommands: false,
ignoreKeyboardLayout: false,
// When true, hide overlapping link-hint markers so only one shows per overlapping region.
suppressOverlappingHintMarkers: true,
};

/*
Expand Down
9 changes: 9 additions & 0 deletions pages/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ <h2></h2>
keyboards.
</div>

<h2></h2>
<label>
<input name="suppressOverlappingHintMarkers" type="checkbox" />
Hide overlapping link-hint markers
</label>
<div class="example">
When hints overlap visually, show only one marker per overlapping region.
</div>

<h2>Previous patterns</h2>
<div>
<input name="previousPatterns" type="text" />
Expand Down
1 change: 1 addition & 0 deletions pages/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const options = {
previousPatterns: "string",
regexFindMode: "boolean",
ignoreKeyboardLayout: "boolean",
suppressOverlappingHintMarkers: "boolean",
scrollStepSize: "number",
smoothScroll: "boolean",
grabBackFocus: "boolean",
Expand Down