presentation/KeyHint.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "KeyHint.h" | ||
| 2 | |||
| 3 | #include "KeyOverlayScrim.h" | ||
| 4 | |||
| 5 | #include <QMetaMethod> | ||
| 6 | #include <QQmlEngine> | ||
| 7 | #include <QQuickWindow> | ||
| 8 | #include <QSet> | ||
| 9 | #include <mutex> | ||
| 10 | |||
| 11 | namespace { | ||
| 12 | // Live entries; the GUI thread only (QML items live there). | ||
| 13 | 511378 | QList<KeyHint *> ®istry() { | |
| 14 |
3/4✓ Branch 2 → 3 taken 26 times.
✓ Branch 2 → 7 taken 511352 times.
✓ Branch 4 → 5 taken 26 times.
✗ Branch 4 → 7 not taken.
|
511378 | static QList<KeyHint *> entries; |
| 15 | 511378 | return entries; | |
| 16 | } | ||
| 17 | } // namespace | ||
| 18 | |||
| 19 |
1/1✓ Branch 15 → 16 taken 255292 times.
|
510584 | KeyHint::KeyHint(QObject *parent) : QObject(parent) { registry().append(this); } |
| 20 | |||
| 21 | 510580 | KeyHint::~KeyHint() { registry().removeOne(this); } | |
| 22 | |||
| 23 |
2/5✓ Branch 3 → 4 taken 255292 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 255292 times.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
|
510584 | KeyHint *KeyHint::qmlAttachedProperties(QObject *object) { return new KeyHint(object); } |
| 24 | |||
| 25 | 121735 | KeyHint *KeyHint::of(const QObject *item) { | |
| 26 |
1/2✓ Branch 2 → 3 taken 121735 times.
✗ Branch 2 → 6 not taken.
|
121735 | return item ? qobject_cast<KeyHint *>(qmlAttachedPropertiesObject<KeyHint>(item, false)) : nullptr; |
| 27 | } | ||
| 28 | |||
| 29 | 796 | QList<KeyHint *> KeyHint::all() { return registry(); } | |
| 30 | |||
| 31 | 891 | void KeyHint::registerTypes() { | |
| 32 | static std::once_flag once; | ||
| 33 |
1/1✓ Branch 2 → 3 taken 891 times.
|
919 | std::call_once(once, [] { |
| 34 |
1/1✓ Branch 5 → 6 taken 28 times.
|
28 | qmlRegisterUncreatableType<KeyHint>("MailJD.Keys", 1, 0, "KeyHint", |
| 35 | 56 | QStringLiteral("KeyHint is an attached property")); | |
| 36 | 28 | qmlRegisterType<KeyOverlayScrim>("MailJD.Keys", 1, 0, "KeyOverlayScrim"); | |
| 37 | 28 | }); | |
| 38 | 891 | } | |
| 39 | |||
| 40 | 1093 | void KeyHint::setFocusTarget(QQuickItem *value) { | |
| 41 |
2/2✓ Branch 3 → 4 taken 81 times.
✓ Branch 3 → 5 taken 1012 times.
|
1093 | if (m_focusTarget == value) return; |
| 42 | 1012 | m_focusTarget = value; | |
| 43 | 1012 | emit changed(); | |
| 44 | } | ||
| 45 | |||
| 46 | 1588 | QString KeyHint::effectiveLabel() const { | |
| 47 |
2/2✓ Branch 3 → 4 taken 1315 times.
✓ Branch 3 → 5 taken 273 times.
|
1588 | if (!m_label.isEmpty()) return m_label; |
| 48 |
2/2✓ Branch 6 → 7 taken 172 times.
✓ Branch 6 → 8 taken 101 times.
|
273 | if (!m_pane.isEmpty()) return m_pane; |
| 49 | 101 | const QQuickItem *element = item(); | |
| 50 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 101 times.
|
101 | if (!element) return {}; |
| 51 |
2/2✓ Branch 11 → 12 taken 101 times.
✓ Branch 12 → 13 taken 101 times.
|
101 | const QString text = element->property("text").toString(); |
| 52 | // Icon buttons carry a glyph as text; their name is the tooltip/accessible name. | ||
| 53 |
6/7✓ Branch 15 → 16 taken 25 times.
✓ Branch 15 → 20 taken 76 times.
✓ Branch 16 → 17 taken 25 times.
✓ Branch 18 → 19 taken 25 times.
✗ Branch 18 → 20 not taken.
✓ Branch 21 → 22 taken 25 times.
✓ Branch 21 → 23 taken 76 times.
|
101 | if (!text.isEmpty() && text.front().isLetterOrNumber()) return text; |
| 54 |
2/2✓ Branch 23 → 24 taken 76 times.
✓ Branch 24 → 25 taken 76 times.
|
76 | const QString tip = element->property("tip").toString(); |
| 55 |
4/8✓ Branch 27 → 28 taken 76 times.
✗ Branch 27 → 30 not taken.
✓ Branch 28 → 29 taken 76 times.
✓ Branch 29 → 31 taken 76 times.
✓ Branch 32 → 33 taken 76 times.
✗ Branch 32 → 34 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 47 not taken.
|
76 | return tip.isEmpty() ? element->property("name").toString() : tip; |
| 56 | 101 | } | |
| 57 | |||
| 58 | 9 | void KeyHint::activate() { | |
| 59 |
4/7✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 7 taken 7 times.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 33 not taken.
|
9 | static const QMetaMethod signal = QMetaMethod::fromSignal(&KeyHint::activated); |
| 60 |
2/2✓ Branch 8 → 9 taken 1 time.
✓ Branch 8 → 11 taken 8 times.
|
9 | if (isSignalConnected(signal)) { |
| 61 | 1 | emit activated(); | |
| 62 | 1 | return; | |
| 63 | } | ||
| 64 | 8 | QQuickItem *element = item(); | |
| 65 |
1/2✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 8 times.
|
8 | if (!element) return; |
| 66 | // A Controls button: click() runs its onClicked handler and the checked | ||
| 67 | // logic exactly like a pointer click. | ||
| 68 |
1/2✓ Branch 16 → 17 taken 8 times.
✗ Branch 16 → 19 not taken.
|
8 | if (element->metaObject()->indexOfMethod("click()") >= 0) { |
| 69 | 8 | QMetaObject::invokeMethod(element, "click"); | |
| 70 | 8 | return; | |
| 71 | } | ||
| 72 | // Older Controls have no click(): emitting clicked() runs onClicked. | ||
| 73 | ✗ | if (element->metaObject()->indexOfSignal("clicked()") >= 0) { | |
| 74 | ✗ | QMetaObject::invokeMethod(element, "clicked"); | |
| 75 | ✗ | return; | |
| 76 | } | ||
| 77 | ✗ | if (element->metaObject()->indexOfMethod("toggle()") >= 0) { | |
| 78 | ✗ | QMetaObject::invokeMethod(element, "toggle"); | |
| 79 | ✗ | return; | |
| 80 | } | ||
| 81 | ✗ | focusPane(); | |
| 82 | } | ||
| 83 | |||
| 84 | 1583 | QString KeyHint::paneOf(const QQuickItem *item) { | |
| 85 |
2/2✓ Branch 16 → 3 taken 10553 times.
✓ Branch 16 → 17 taken 908 times.
|
11461 | for (const QQuickItem *current = item; current; current = current->parentItem()) |
| 86 |
8/8✓ Branch 4 → 5 taken 2091 times.
✓ Branch 4 → 9 taken 8462 times.
✓ Branch 7 → 8 taken 675 times.
✓ Branch 7 → 9 taken 1416 times.
✓ Branch 10 → 11 taken 2091 times.
✓ Branch 10 → 12 taken 8462 times.
✓ Branch 12 → 13 taken 675 times.
✓ Branch 12 → 14 taken 9878 times.
|
10553 | if (const KeyHint *hint = of(current); hint && !hint->pane().isEmpty()) return hint->pane(); |
| 87 | 908 | return {}; | |
| 88 | } | ||
| 89 | |||
| 90 | 491 | void KeyHint::focusPane() { | |
| 91 |
2/2✓ Branch 3 → 4 taken 45 times.
✓ Branch 3 → 5 taken 446 times.
|
491 | QQuickItem *target = m_focusTarget ? m_focusTarget.data() : item(); |
| 92 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 491 times.
|
491 | if (!target) return; |
| 93 | 491 | target->forceActiveFocus(Qt::ShortcutFocusReason); | |
| 94 | // S105.9.10: a pane that is only a container (no Tab stop of its own, | ||
| 95 | // e.g. a sidebar's ScrollView) draws no focus ring; its first Tab stop | ||
| 96 | // takes the focus instead. A pane that moved the focus on by itself | ||
| 97 | // (the folder tree to the open folder) is left alone. | ||
| 98 | 491 | QQuickWindow *window = target->window(); | |
| 99 |
7/8✓ Branch 11 → 12 taken 448 times.
✓ Branch 11 → 15 taken 43 times.
✓ Branch 12 → 13 taken 448 times.
✗ Branch 12 → 15 not taken.
✓ Branch 14 → 15 taken 180 times.
✓ Branch 14 → 16 taken 268 times.
✓ Branch 17 → 18 taken 223 times.
✓ Branch 17 → 19 taken 268 times.
|
491 | if (target->activeFocusOnTab() || !window || window->activeFocusItem() != target) return; |
| 100 | 268 | QQuickItem *next = target->nextItemInFocusChain(true); | |
| 101 |
7/8✓ Branch 20 → 21 taken 268 times.
✗ Branch 20 → 25 not taken.
✓ Branch 21 → 22 taken 263 times.
✓ Branch 21 → 25 taken 5 times.
✓ Branch 23 → 24 taken 221 times.
✓ Branch 23 → 25 taken 42 times.
✓ Branch 26 → 27 taken 221 times.
✓ Branch 26 → 28 taken 47 times.
|
268 | if (next && next != target && target->isAncestorOf(next)) next->forceActiveFocus(Qt::ShortcutFocusReason); |
| 102 | } | ||
| 103 |