add multiple windows support with shared session, add shortcuts

This commit is contained in:
2026-10-15 23:57:13 +02:00
parent b23e838957
commit b6020a4354
7 changed files with 194 additions and 41 deletions
+75 -1
View File
@@ -2,7 +2,7 @@
namespace {
constexpr auto kChars = "asdfghjkl";
constexpr auto kChars = "1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik9ol0p";
// Installs window.__attHints and shows the labels. "@CHARS@" is substituted
// with kChars before running.
@@ -178,6 +178,75 @@ constexpr auto kShowScript = R"JS(
})();
)JS";
constexpr auto kFocusInputScript = R"JS(
(function () {
'use strict';
const TEXT_INPUTS = new Set(['', 'text', 'search', 'email', 'url', 'tel',
'password', 'number']);
function isTextField(el) {
if (el.disabled || el.readOnly)
return false;
if (el.tagName === 'TEXTAREA')
return true;
// Only the root of an editable region, not every element inside it.
if (el.isContentEditable)
return !el.parentElement?.isContentEditable;
return el.tagName === 'INPUT' && TEXT_INPUTS.has((el.getAttribute('type') || '').toLowerCase());
}
function isVisible(el) {
const style = getComputedStyle(el);
if (style.visibility !== 'visible' || style.display === 'none')
return false;
const r = el.getBoundingClientRect();
return r.width >= 1 && r.height >= 1;
}
function inViewport(el) {
const r = el.getBoundingClientRect();
return r.bottom > 0 && r.right > 0 && r.top < innerHeight && r.left < innerWidth;
}
// Document order, descending into each open shadow root right after its host.
function* elements(root) {
for (const el of root.querySelectorAll('*')) {
yield el;
if (el.shadowRoot)
yield* elements(el.shadowRoot);
}
}
let fallback = null;
let target = null;
for (const el of elements(document)) {
if (!isTextField(el) || !isVisible(el))
continue;
if (inViewport(el)) {
target = el;
break;
}
fallback = fallback || el;
}
target = target || fallback;
if (!target)
return false;
target.scrollIntoView({ block: 'nearest', inline: 'nearest' });
target.focus();
// Put the cursor after any existing text, ready to type or edit.
if (typeof target.setSelectionRange === 'function') {
try {
target.setSelectionRange(target.value.length, target.value.length);
} catch (e) {
// type=email/number don't support selection ranges.
}
}
// From outside a shadow root, document.activeElement is only its host.
return target.getRootNode().activeElement === target;
})();
)JS";
QString callScript(const QString &call)
{
return QStringLiteral("window.__attHints ? window.__attHints.%1 : { state: 'none' };")
@@ -214,4 +283,9 @@ QString clearScript()
return callScript(QStringLiteral("clear()"));
}
QString focusInputScript()
{
return QString::fromUtf8(kFocusInputScript);
}
} // namespace hints