#pragma once #include #include #include #include #include #include #include #include #include #include class QAbstractButton; class QKeyEvent; class QLabel; class QLineEdit; class QShortcut; class QWidget; // One key with its modifiers, stored as Swift stores it. struct ShortcutChord { // One character; Backspace is 0x7f, the arrows Cocoa's F700s. QString key; // Bits: Ctrl 1, Alt 2, Meta 4, Shift 8. int modifiers = 0; ShortcutChord() = default; ShortcutChord(QString key, int modifiers = 0) : key(std::move(key)), modifiers(modifiers) {} explicit ShortcutChord(const QKeyEvent &event); explicit ShortcutChord(QKeyCombination combination); QKeyCombination combination() const; QString label() const; // The same press, as though this chord were typed. std::unique_ptr event(const QKeyEvent &like) const; friend bool operator==(const ShortcutChord &, const ShortcutChord &) = default; }; struct ShortcutDefinition { QString title; QString group; ShortcutChord original; QString id() const { return group + QLatin1Char(':') + title; } bool isMenu() const { return group == QLatin1String("Menus"); } static const std::vector &all(); }; // Swift's ShortcutSettings: overrides, kept in QSettings. class ShortcutSettings : public QObject { Q_OBJECT public: static ShortcutSettings &shared(); static constexpr const char *storageKey = "keyboardShortcuts.v1"; const QHash &overrides() const { return m_overrides; } ShortcutChord chord(const ShortcutDefinition &definition) const; // A menu entry's key, or a sheet's, as remapped. QKeySequence menu(const QKeySequence &original) const; ShortcutChord native(const ShortcutChord &original) const; // Saves valid overrides; answers whether it did. bool save(const QHash &values); static std::optional problem(const QHash &values); // Null swallows the press; else the press to handle. std::unique_ptr canvasEvent(const QKeyEvent &event) const; std::unique_ptr textEvent(const QKeyEvent &event) const; // Reads the stored overrides again, as at launch. void reload(); signals: void changed(); private: ShortcutSettings(); QHash m_overrides; }; // Swift's configuredNativeShortcut on a sheet's button. class NativeShortcut : public QObject { Q_OBJECT public: NativeShortcut(const ShortcutChord &original, QWidget &scope, QAbstractButton &target); // A sheet's default (Return) and cancel (Escape) buttons. static void bind(QWidget &scope, QAbstractButton *apply, QAbstractButton *cancel); protected: bool eventFilter(QObject *watched, QEvent *event) override; private: void synchronize(); const ShortcutChord m_original; QWidget &m_scope; QPointer m_target; QShortcut *const m_shortcut; }; // A shortcut's button: click, then press the new keys. class ShortcutRecorder : public QPushButton { Q_OBJECT public: ShortcutRecorder(std::function start, std::function finish, QWidget *parent); void display(const ShortcutChord &chord, bool recording); protected: bool event(QEvent *event) override; void keyPressEvent(QKeyEvent *event) override; private: const std::function m_start; const std::function m_finish; bool m_recording = false; }; // Swift's KeyboardShortcutsSheet, in the "keyboardShortcuts" panel. class KeyboardShortcutsSheet : public QWidget { Q_OBJECT public: KeyboardShortcutsSheet(std::function close, QWidget *parent = nullptr); private: void synchronize(); const std::function m_close; QHash m_draft; std::optional m_recording; QLineEdit *const m_search; QLabel *const m_problem; QPushButton *const m_save; struct Row { QWidget *row; ShortcutRecorder *recorder; const ShortcutDefinition *definition; }; std::vector m_rows; };