From 6a5fecf8ef91c3253777f17ab57ee71367219a89 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Tue, 4 Apr 2017 18:02:47 +0800 Subject: [PATCH] Add Navigation Mode to Captain Mode In Captain Mode, W will trigger the Navigation Mode. In this mode, registered widgets will display some characters for hints and after user pressing the the keys, corresponding widget will take action as response. For now, only VNotebookSelector registers in the Navigation Mode. --- src/src.pro | 3 +- src/utils/vutils.cpp | 7 +++ src/utils/vutils.h | 1 + src/vcaptain.cpp | 92 ++++++++++++++++++++++++++++++++++++--- src/vcaptain.h | 23 +++++++++- src/vmainwindow.cpp | 2 + src/vnavigationmode.h | 24 ++++++++++ src/vnote.cpp | 20 +++++++++ src/vnote.h | 2 + src/vnotebookselector.cpp | 45 ++++++++++++++++++- src/vnotebookselector.h | 12 ++++- 11 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 src/vnavigationmode.h diff --git a/src/src.pro b/src/src.pro index 552d193e..8f4c7325 100644 --- a/src/src.pro +++ b/src/src.pro @@ -100,7 +100,8 @@ HEADERS += vmainwindow.h \ dialog/vdeletenotebookdialog.h \ dialog/vselectdialog.h \ vcaptain.h \ - vopenedlistmenu.h + vopenedlistmenu.h \ + vnavigationmode.h RESOURCES += \ vnote.qrc \ diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index a0c380b1..7247cc55 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -360,3 +360,10 @@ bool VUtils::realEqual(qreal p_a, qreal p_b) return std::abs(p_a - p_b) < 1e-8; } +QChar VUtils::keyToChar(int p_key) +{ + if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) { + return QChar('a' + p_key - Qt::Key_A); + } + return QChar(); +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index 65cd9f47..547550dd 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -48,6 +48,7 @@ public: static bool isImageURLText(const QString &p_url); static qreal calculateScaleFactor(); static bool realEqual(qreal p_a, qreal p_b); + static QChar keyToChar(int p_key); private: // diff --git a/src/vcaptain.cpp b/src/vcaptain.cpp index 217c70d3..7baca2fe 100644 --- a/src/vcaptain.cpp +++ b/src/vcaptain.cpp @@ -8,13 +8,14 @@ #include "veditarea.h" #include "vedittab.h" #include "vfilelist.h" +#include "vnavigationmode.h" // 3s pending time after the leader keys. const int c_pendingTime = 3 * 1000; VCaptain::VCaptain(VMainWindow *p_parent) : QWidget(p_parent), m_mainWindow(p_parent), m_mode(VCaptain::Normal), - m_widgetBeforeCaptain(NULL) + m_widgetBeforeCaptain(NULL), m_nextMajorKey('a') { m_pendingTimer = new QTimer(this); m_pendingTimer->setSingleShot(true); @@ -38,6 +39,26 @@ VCaptain::VCaptain(VMainWindow *p_parent) resize(1, 1); } +QChar VCaptain::getNextMajorKey() +{ + QChar ret = m_nextMajorKey; + if (m_nextMajorKey == 'z') { + m_nextMajorKey = QChar(); + } else if (!m_nextMajorKey.isNull()) { + m_nextMajorKey = QChar(m_nextMajorKey.toLatin1() + 1); + } + return ret; +} + +void VCaptain::registerNavigationTarget(VNavigationMode *p_target) +{ + QChar key = getNextMajorKey(); + if (!key.isNull()) { + p_target->registerNavigation(key); + m_targets.append(NaviModeTarget(p_target, true)); + } +} + // In pending mode, if user click other widgets, we need to exit Captain mode. void VCaptain::handleFocusChanged(QWidget *p_old, QWidget * /* p_now */) { @@ -55,7 +76,7 @@ void VCaptain::pendingTimerTimeout() void VCaptain::trigger() { - if (m_mode == VCaptain::Pending) { + if (m_mode != VCaptain::Normal) { return; } qDebug() << "trigger Captain mode"; @@ -73,7 +94,6 @@ void VCaptain::keyPressEvent(QKeyEvent *p_event) { int key = p_event->key(); Qt::KeyboardModifiers modifiers = p_event->modifiers(); - qDebug() << "VCaptain key pressed" << key << modifiers; if (m_mode == VCaptain::Normal) { // Should not in focus while in Normal mode. @@ -83,7 +103,6 @@ void VCaptain::keyPressEvent(QKeyEvent *p_event) } if (key == Qt::Key_Control || key == Qt::Key_Shift) { - qDebug() << "VCaptain ignore key event"; QWidget::keyPressEvent(p_event); return; } @@ -97,7 +116,6 @@ void VCaptain::keyPressEvent(QKeyEvent *p_event) bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) { - qDebug() << "handleKeyPress" << p_key << p_modifiers; bool ret = true; if (p_key == Qt::Key_Escape @@ -106,6 +124,10 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) goto exit; } + if (m_mode == VCaptainMode::Navigation) { + return handleKeyPressNavigationMode(p_key, p_modifiers); + } + // In Captain mode, Ctrl key won't make a difference. switch (p_key) { case Qt::Key_1: @@ -248,9 +270,13 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) m_mainWindow->editArea->splitCurrentWindow(); // Do not restore focus. m_widgetBeforeCaptain = NULL; - break; + case Qt::Key_W: + // Enter navigation mode. + triggerNavigationMode(); + return ret; + case Qt::Key_X: { // Close current tab. @@ -273,9 +299,58 @@ exit: return ret; } +bool VCaptain::handleKeyPressNavigationMode(int p_key, + Qt::KeyboardModifiers /* p_modifiers */) +{ + Q_ASSERT(m_mode == VCaptainMode::Navigation); + for (auto target : m_targets) { + if (target.m_available) { + bool succeed = false; + bool consumed = target.m_target->handleKeyNavigation(p_key, succeed); + if (consumed) { + if (succeed) { + // Exit. + m_widgetBeforeCaptain = NULL; + break; + } else { + // Consumed but not succeed. Need more keys. + return true; + } + } else { + // Do not ask this target any more. + target.m_available = false; + } + } + } + exitCaptainMode(); + restoreFocus(); + return true; +} + +void VCaptain::triggerNavigationMode() +{ + m_pendingTimer->stop(); + m_mode = VCaptainMode::Navigation; + + for (auto target : m_targets) { + target.m_available = true; + target.m_target->showNavigation(); + } +} + +void VCaptain::exitNavigationMode() +{ + m_mode = VCaptainMode::Normal; + + for (auto target : m_targets) { + target.m_available = true; + target.m_target->hideNavigation(); + } +} + bool VCaptain::eventFilter(QObject *p_obj, QEvent *p_event) { - if (m_mode == VCaptain::Pending && p_event->type() == QEvent::Shortcut) { + if (m_mode != VCaptain::Normal && p_event->type() == QEvent::Shortcut) { qDebug() << "filter" << p_event; QShortcutEvent *keyEve = dynamic_cast(p_event); Q_ASSERT(keyEve); @@ -303,6 +378,9 @@ void VCaptain::restoreFocus() void VCaptain::exitCaptainMode() { + if (m_mode == VCaptainMode::Navigation) { + exitNavigationMode(); + } m_mode = VCaptain::Normal; m_pendingTimer->stop(); diff --git a/src/vcaptain.h b/src/vcaptain.h index 2493d7d8..d1511292 100644 --- a/src/vcaptain.h +++ b/src/vcaptain.h @@ -2,11 +2,13 @@ #define VCAPTAIN_H #include +#include class QTimer; class QKeyEvent; class VMainWindow; class QEvent; +class VNavigationMode; class VCaptain : public QWidget { @@ -17,6 +19,9 @@ public: // Trigger Captain mode. void trigger(); + // Register a target for Navigation Mode. + void registerNavigationTarget(VNavigationMode *p_target); + signals: void captainModeChanged(bool p_enabled); @@ -37,10 +42,16 @@ private: // Return true if finish handling the event; otherwise, let the base widget // to handle it. bool handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers); + bool handleKeyPressNavigationMode(int p_key, + Qt::KeyboardModifiers p_modifiers); + QChar getNextMajorKey(); + void triggerNavigationMode(); + void exitNavigationMode(); enum VCaptainMode { Normal = 0, - Pending + Pending, + Navigation }; VMainWindow *m_mainWindow; @@ -48,6 +59,16 @@ private: int m_mode; // The widget which has the focus before entering Captain mode. QWidget* m_widgetBeforeCaptain; + + struct NaviModeTarget { + VNavigationMode *m_target; + bool m_available; + + NaviModeTarget(VNavigationMode *p_target, bool p_available) + : m_target(p_target), m_available(p_available) {} + }; + QList m_targets; + QChar m_nextMajorKey; }; #endif // VCAPTAIN_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index ab10c0f8..493a2a13 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -49,6 +49,8 @@ void VMainWindow::initCaptain() m_captain = new VCaptain(this); connect(m_captain, &VCaptain::captainModeChanged, this, &VMainWindow::handleCaptainModeChanged); + + m_captain->registerNavigationTarget(notebookSelector); } void VMainWindow::setupUI() diff --git a/src/vnavigationmode.h b/src/vnavigationmode.h new file mode 100644 index 00000000..a1af73a3 --- /dev/null +++ b/src/vnavigationmode.h @@ -0,0 +1,24 @@ +#ifndef VNAVIGATIONMODE_H +#define VNAVIGATIONMODE_H + +#include + +// Interface class for Navigation Mode in Captain Mode. +class VNavigationMode +{ +public: + VNavigationMode() {}; + virtual ~VNavigationMode() {}; + + virtual void registerNavigation(QChar p_majorKey) = 0; + virtual void showNavigation() = 0; + virtual void hideNavigation() = 0; + // Return true if this object could consume p_key. + // p_succeed indicates whether the keys hit a target successfully. + virtual bool handleKeyNavigation(int p_key, bool &p_succeed) = 0; + +protected: + QChar m_majorKey; +}; + +#endif // VNAVIGATIONMODE_H diff --git a/src/vnote.cpp b/src/vnote.cpp index 0eb36276..e535fc07 100644 --- a/src/vnote.cpp +++ b/src/vnote.cpp @@ -102,6 +102,17 @@ void VNote::initPalette(QPalette palette) m_palette.append(QPair("Purple7", "#7B1FA2")); m_palette.append(QPair("Purple8", "#6A1B9A")); m_palette.append(QPair("Purple9", "#4A148C")); + + m_palette.append(QPair("Red0", "#FFEBEE")); + m_palette.append(QPair("Red1", "#FFCDD2")); + m_palette.append(QPair("Red2", "#EF9A9A")); + m_palette.append(QPair("Red3", "#E57373")); + m_palette.append(QPair("Red4", "#EF5350")); + m_palette.append(QPair("Red5", "#F44336")); + m_palette.append(QPair("Red6", "#E53935")); + m_palette.append(QPair("Red7", "#D32F2F")); + m_palette.append(QPair("Red8", "#C62828")); + m_palette.append(QPair("Red9", "#B71C1C")); } QString VNote::getColorFromPalette(const QString &p_name) const @@ -160,3 +171,12 @@ QVector &VNote::getNotebooks() { return m_notebooks; } + +const QString &VNote::getNavigationLabelStyle() const +{ + static const QString stylesheet = QString("color: %1;" + "font-size: %2;" + "font: bold;").arg(getColorFromPalette("Red5")) + .arg("18pt"); + return stylesheet; +} diff --git a/src/vnote.h b/src/vnote.h index de488596..19ccafa0 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -54,6 +54,8 @@ public: QString getColorFromPalette(const QString &p_name) const; inline VMainWindow *getMainWindow() const; + const QString &getNavigationLabelStyle() const; + public slots: void updateTemplate(); diff --git a/src/vnotebookselector.cpp b/src/vnotebookselector.cpp index cf467a02..e9a8d44b 100644 --- a/src/vnotebookselector.cpp +++ b/src/vnotebookselector.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "vnotebook.h" #include "vconfigmanager.h" #include "dialog/vnewnotebookdialog.h" @@ -19,12 +20,14 @@ #include "vnofocusitemdelegate.h" extern VConfigManager vconfig; +extern VNote *g_vnote; const int VNotebookSelector::c_notebookStartIdx = 1; VNotebookSelector::VNotebookSelector(VNote *vnote, QWidget *p_parent) - : QComboBox(p_parent), m_vnote(vnote), m_notebooks(m_vnote->getNotebooks()), - m_editArea(NULL), m_lastValidIndex(-1) + : QComboBox(p_parent), VNavigationMode(), + m_vnote(vnote), m_notebooks(m_vnote->getNotebooks()), + m_editArea(NULL), m_lastValidIndex(-1), m_naviLabel(NULL) { m_listWidget = new QListWidget(this); m_listWidget->setItemDelegate(new VNoFocusItemDelegate(this)); @@ -386,3 +389,41 @@ void VNotebookSelector::resizeListWidgetToContent() } m_listWidget->setMinimumSize(minWidth, minHeight); } + +void VNotebookSelector::registerNavigation(QChar p_majorKey) +{ + Q_ASSERT(!m_naviLabel); + qDebug() << "VNotebookSelector register for navigation key" << p_majorKey; + m_majorKey = p_majorKey; + + m_naviLabel = new QLabel(m_majorKey, this); + m_naviLabel->setStyleSheet(g_vnote->getNavigationLabelStyle()); + m_naviLabel->hide(); +} + +void VNotebookSelector::showNavigation() +{ + qDebug() << "VNotebookSelector show navigation"; + m_naviLabel->show(); +} + +void VNotebookSelector::hideNavigation() +{ + qDebug() << "VNotebookSelector hide navigation"; + m_naviLabel->hide(); +} + +bool VNotebookSelector::handleKeyNavigation(int p_key, bool &p_succeed) +{ + qDebug() << "VNotebookSelector handle key navigation" << p_key; + bool ret = false; + p_succeed = false; + QChar keyChar = VUtils::keyToChar(p_key); + if (keyChar == m_majorKey) { + // Hit. + p_succeed = true; + showPopup(); + ret = true; + } + return ret; +} diff --git a/src/vnotebookselector.h b/src/vnotebookselector.h index a3b9e9c4..43d7acee 100644 --- a/src/vnotebookselector.h +++ b/src/vnotebookselector.h @@ -4,6 +4,7 @@ #include #include #include +#include "vnavigationmode.h" class VNotebook; class VNote; @@ -11,8 +12,9 @@ class VEditArea; class QListWidget; class QAction; class QListWidgetItem; +class QLabel; -class VNotebookSelector : public QComboBox +class VNotebookSelector : public QComboBox, public VNavigationMode { Q_OBJECT public: @@ -23,6 +25,12 @@ public: bool locateNotebook(const VNotebook *p_notebook); void showPopup() Q_DECL_OVERRIDE; + // Implementations for VNavigationMode. + void registerNavigation(QChar p_majorKey); + void showNavigation(); + void hideNavigation(); + bool handleKeyNavigation(int p_key, bool &p_succeed); + signals: void curNotebookChanged(VNotebook *p_notebook); // Info of current notebook was changed. @@ -76,6 +84,8 @@ private: // We will add several special action item in the combobox. This is the start index // of the real notebook items related to m_notebooks. static const int c_notebookStartIdx; + + QLabel *m_naviLabel; }; inline void VNotebookSelector::setEditArea(VEditArea *p_editArea)