add multiple windows support with shared session, add shortcuts
This commit is contained in:
+75
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user