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)