Browse Source

Captain mode

Le Tan 8 years ago
parent
commit
3406eab29c
18 changed files with 582 additions and 49 deletions
  1. 4 2
      src/src.pro
  2. 2 0
      src/utils/vutils.cpp
  3. 2 0
      src/utils/vutils.h
  4. 10 0
      src/vavatar.cpp
  5. 2 0
      src/vavatar.h
  6. 278 0
      src/vcaptain.cpp
  7. 53 0
      src/vcaptain.h
  8. 1 0
      src/vedit.cpp
  9. 52 7
      src/veditarea.cpp
  10. 7 0
      src/veditarea.h
  11. 4 0
      src/vedittab.cpp
  12. 62 31
      src/veditwindow.cpp
  13. 5 0
      src/veditwindow.h
  14. 9 3
      src/vfilelist.cpp
  15. 2 0
      src/vfilelist.h
  16. 55 5
      src/vmainwindow.cpp
  17. 11 0
      src/vmainwindow.h
  18. 23 1
      src/vnote.cpp

+ 4 - 2
src/src.pro

@@ -55,7 +55,8 @@ SOURCES += main.cpp\
     dialog/vfindreplacedialog.cpp \
     dialog/vsettingsdialog.cpp \
     dialog/vdeletenotebookdialog.cpp \
-    dialog/vselectdialog.cpp
+    dialog/vselectdialog.cpp \
+    vcaptain.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -96,7 +97,8 @@ HEADERS  += vmainwindow.h \
     dialog/vfindreplacedialog.h \
     dialog/vsettingsdialog.h \
     dialog/vdeletenotebookdialog.h \
-    dialog/vselectdialog.h
+    dialog/vselectdialog.h \
+    vcaptain.h
 
 RESOURCES += \
     vnote.qrc \

+ 2 - 0
src/utils/vutils.cpp

@@ -11,6 +11,7 @@
 #include <QDateTime>
 #include <QFileInfo>
 #include <QImageReader>
+#include <QKeyEvent>
 
 const QVector<QPair<QString, QString>> VUtils::c_availableLanguages = {QPair<QString, QString>("en_US", "Englisth(US)"),
                                                                        QPair<QString, QString>("zh_CN", "Chinese")};
@@ -340,3 +341,4 @@ bool VUtils::isImageURLText(const QString &p_url)
     QFileInfo info(p_url);
     return QImageReader::supportedImageFormats().contains(info.suffix().toLower().toLatin1());
 }
+

+ 2 - 0
src/utils/vutils.h

@@ -10,6 +10,8 @@
 #include "vconfigmanager.h"
 #include "vconstants.h"
 
+class QKeyEvent;
+
 class VUtils
 {
 public:

+ 10 - 0
src/vavatar.cpp

@@ -105,3 +105,13 @@ void VAvatar::setColor(const QString &p_baseColor, const QString &p_fgColor, con
     m_bgColor.setNamedColor(p_bgColor);
 }
 
+void VAvatar::updateBaseColor(const QString &p_baseColor)
+{
+    m_baseColor.setNamedColor(p_baseColor);
+    update();
+}
+
+QString VAvatar::getBaseColor() const
+{
+    return m_baseColor.name();
+}

+ 2 - 0
src/vavatar.h

@@ -17,7 +17,9 @@ public:
     void setAvatarPixmap(const QString &p_avatarPixmap);
     void setAvatarText(const QString &p_avatarText);
     void setColor(const QString &p_baseColor, const QString &p_fgColor, const QString &p_bgColor);
+    void updateBaseColor(const QString &p_baseColor);
     QSize sizeHint() const Q_DECL_OVERRIDE;
+    QString getBaseColor() const;
 
 protected:
     void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;

+ 278 - 0
src/vcaptain.cpp

@@ -0,0 +1,278 @@
+#include <QtWidgets>
+#include <QString>
+#include <QTimer>
+#include <QDebug>
+#include <QShortcut>
+#include "vcaptain.h"
+#include "vmainwindow.h"
+#include "veditarea.h"
+#include "vedittab.h"
+#include "vfilelist.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_pendingTimer = new QTimer(this);
+    m_pendingTimer->setSingleShot(true);
+    m_pendingTimer->setInterval(c_pendingTime);
+    connect(m_pendingTimer, &QTimer::timeout,
+            this, &VCaptain::pendingTimerTimeout);
+
+    connect(qApp, &QApplication::focusChanged,
+            this, &VCaptain::handleFocusChanged);
+
+    QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+E"), this,
+                                        Q_NULLPTR, Q_NULLPTR);
+    connect(shortcut, &QShortcut::activated,
+            this, &VCaptain::trigger);
+
+    qApp->installEventFilter(this);
+
+    setWindowFlags(Qt::FramelessWindowHint);
+    // Make it as small as possible. This widget will stay at the top-left corner
+    // of VMainWindow.
+    resize(1, 1);
+}
+
+// In pending mode, if user click other widgets, we need to exit Captain mode.
+void VCaptain::handleFocusChanged(QWidget *p_old, QWidget * /* p_now */)
+{
+    if (p_old == this) {
+        exitCaptainMode();
+    }
+}
+
+void VCaptain::pendingTimerTimeout()
+{
+    qDebug() << "Captain mode timeout";
+    exitCaptainMode();
+    restoreFocus();
+}
+
+void VCaptain::trigger()
+{
+    if (m_mode == VCaptain::Pending) {
+        return;
+    }
+    qDebug() << "trigger Captain mode";
+    // Focus to listen pending key press.
+    m_widgetBeforeCaptain = QApplication::focusWidget();
+    setFocus();
+    m_mode = VCaptain::Pending;
+    m_pendingTimer->stop();
+    m_pendingTimer->start();
+
+    emit captainModeChanged(true);
+}
+
+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.
+        QWidget::keyPressEvent(p_event);
+        m_mainWindow->focusNextChild();
+        return;
+    }
+
+    if (key == Qt::Key_Control || key == Qt::Key_Shift) {
+        qDebug() << "VCaptain ignore key event";
+        QWidget::keyPressEvent(p_event);
+        return;
+    }
+
+    if (handleKeyPress(key, modifiers)) {
+        p_event->accept();
+    } else {
+        QWidget::keyPressEvent(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
+        || (p_key == Qt::Key_BracketLeft
+            && p_modifiers == Qt::ControlModifier)) {
+        goto exit;
+    }
+
+    // In Captain mode, Ctrl key won't make a difference.
+    switch (p_key) {
+    case Qt::Key_D:
+        // Locate current tab.
+        m_mainWindow->locateCurrentFile();
+        break;
+
+    case Qt::Key_E:
+        // Toggle expand view.
+        m_mainWindow->expandViewAct->trigger();
+        break;
+
+    case Qt::Key_F:
+    {
+        // Show current window's opened file list.
+        VEditWindow *win = m_mainWindow->editArea->getCurrentWindow();
+        if (win) {
+            if (win->showOpenedFileList()) {
+                // showOpenedFileList() already focus the right widget.
+                m_widgetBeforeCaptain = NULL;
+            }
+        }
+        break;
+    }
+
+    case Qt::Key_H:
+    {
+        if (p_modifiers & Qt::ShiftModifier) {
+            // Move current tab one split left.
+            m_mainWindow->editArea->moveCurrentTabOneSplit(false);
+        } else {
+            // Focus previous window split.
+            int idx = m_mainWindow->editArea->focusNextWindow(-1);
+            if (idx > -1) {
+                m_widgetBeforeCaptain = NULL;
+            }
+        }
+        break;
+    }
+
+    case Qt::Key_J:
+    {
+        // Focus next tab.
+        VEditWindow *win = m_mainWindow->editArea->getCurrentWindow();
+        if (win) {
+            win->focusNextTab(true);
+            // focusNextTab() will focus the right widget.
+            m_widgetBeforeCaptain = NULL;
+        }
+        break;
+    }
+
+    case Qt::Key_K:
+    {
+        // Focus previous tab.
+        VEditWindow *win = m_mainWindow->editArea->getCurrentWindow();
+        if (win) {
+            win->focusNextTab(false);
+            // focusNextTab() will focus the right widget.
+            m_widgetBeforeCaptain = NULL;
+        }
+        break;
+    }
+
+    case Qt::Key_L:
+    {
+        if (p_modifiers & Qt::ShiftModifier) {
+            // Move current tab one split right.
+            m_mainWindow->editArea->moveCurrentTabOneSplit(true);
+        } else {
+            // Focus next window split.
+            int idx = m_mainWindow->editArea->focusNextWindow(1);
+            if (idx > -1) {
+                m_widgetBeforeCaptain = NULL;
+            }
+        }
+        break;
+    }
+
+    case Qt::Key_P:
+        // Toggle one/two panel view.
+        m_mainWindow->toggleOnePanelView();
+        break;
+
+    case Qt::Key_Q:
+        // Discard changes and exit edit mode.
+        m_mainWindow->discardExitAct->trigger();
+        break;
+
+    case Qt::Key_R:
+    {
+        // Remove current window split.
+        m_mainWindow->editArea->removeCurrentWindow();
+
+        QWidget *nextFocus = m_mainWindow->editArea->currentEditTab();
+        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->fileList;
+        break;
+    }
+
+    case Qt::Key_T:
+        // Toggle the Tools dock.
+        m_mainWindow->toolDock->setVisible(!m_mainWindow->toolDock->isVisible());
+        break;
+
+    case Qt::Key_V:
+        // Vertical split current window.
+        m_mainWindow->editArea->splitCurrentWindow();
+        // Do not restore focus.
+        m_widgetBeforeCaptain = NULL;
+
+        break;
+
+    case Qt::Key_X:
+    {
+        // Close current tab.
+        m_mainWindow->closeCurrentFile();
+
+        // m_widgetBeforeCaptain may be the closed tab which will cause crash.
+        QWidget *nextFocus = m_mainWindow->editArea->currentEditTab();
+        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->fileList;
+        break;
+    }
+
+    default:
+        // Not implemented yet. Just exit Captain mode.
+        break;
+    }
+
+exit:
+    exitCaptainMode();
+    restoreFocus();
+    return ret;
+}
+
+bool VCaptain::eventFilter(QObject *p_obj, QEvent *p_event)
+{
+    if (m_mode == VCaptain::Pending && p_event->type() == QEvent::Shortcut) {
+        qDebug() << "filter" << p_event;
+        QShortcutEvent *keyEve = dynamic_cast<QShortcutEvent *>(p_event);
+        Q_ASSERT(keyEve);
+        const QKeySequence &keys = keyEve->key();
+        if (keys.count() == 1) {
+            int key = keys[0];
+            Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(key & (~0x01FFFFFFU));
+            key &= 0x01FFFFFFUL;
+            if (handleKeyPress(key, modifiers)) {
+                return true;
+            }
+        }
+        exitCaptainMode();
+        restoreFocus();
+    }
+    return QWidget::eventFilter(p_obj, p_event);
+}
+
+void VCaptain::restoreFocus()
+{
+    if (m_widgetBeforeCaptain) {
+        m_widgetBeforeCaptain->setFocus();
+    }
+}
+
+void VCaptain::exitCaptainMode()
+{
+    m_mode = VCaptain::Normal;
+    m_pendingTimer->stop();
+
+    emit captainModeChanged(false);
+}
+

+ 53 - 0
src/vcaptain.h

@@ -0,0 +1,53 @@
+#ifndef VCAPTAIN_H
+#define VCAPTAIN_H
+
+#include <QWidget>
+
+class QTimer;
+class QKeyEvent;
+class VMainWindow;
+class QEvent;
+
+class VCaptain : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit VCaptain(VMainWindow *p_parent);
+
+    // Trigger Captain mode.
+    void trigger();
+
+signals:
+    void captainModeChanged(bool p_enabled);
+
+protected:
+    void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
+    bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
+
+public slots:
+
+private slots:
+    void pendingTimerTimeout();
+    void handleFocusChanged(QWidget *p_old, QWidget *p_new);
+
+private:
+    // Restore the focus to m_widgetBeforeCaptain.
+    void restoreFocus();
+    void exitCaptainMode();
+    // Return true if finish handling the event; otherwise, let the base widget
+    // to handle it.
+    bool handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers);
+
+    enum VCaptainMode {
+        Normal = 0,
+        Pending
+    };
+
+    VMainWindow *m_mainWindow;
+    QTimer *m_pendingTimer;
+    int m_mode;
+    // The widget which has the focus before entering Captain mode.
+    QWidget* m_widgetBeforeCaptain;
+};
+
+#endif // VCAPTAIN_H

+ 1 - 0
src/vedit.cpp

@@ -500,3 +500,4 @@ void VEdit::clearSearchedWordHighlight()
     selects.clear();
     highlightExtraSelections();
 }
+

+ 52 - 7
src/veditarea.cpp

@@ -167,20 +167,16 @@ void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
 void VEditArea::setCurrentWindow(int windowIndex, bool setFocus)
 {
     int nrWin = splitter->count();
-    if (curWindowIndex == windowIndex) {
-        goto out;
-    }
     curWindowIndex = windowIndex;
-    if (curWindowIndex > -1 && setFocus) {
-        getWindow(curWindowIndex)->focusWindow();
-    }
 
-out:
     for (int i = 0; i < nrWin; ++i) {
         getWindow(i)->setCurrentWindow(false);
     }
     if (curWindowIndex > -1) {
         getWindow(curWindowIndex)->setCurrentWindow(true);
+        if (setFocus) {
+            getWindow(curWindowIndex)->focusWindow();
+        }
     }
     // Update status
     updateWindowStatus();
@@ -323,6 +319,13 @@ void VEditArea::handleSplitWindowRequest(VEditWindow *curWindow)
     setCurrentWindow(idx, true);
 }
 
+void VEditArea::splitCurrentWindow()
+{
+    if (curWindowIndex > -1) {
+        handleSplitWindowRequest(getWindow(curWindowIndex));
+    }
+}
+
 void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
 {
     if (!curWindow || splitter->count() <= 1) {
@@ -339,6 +342,13 @@ void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
     setCurrentWindow(idx, true);
 }
 
+void VEditArea::removeCurrentWindow()
+{
+    if (curWindowIndex > -1) {
+        handleRemoveSplitRequest(getWindow(curWindowIndex));
+    }
+}
+
 void VEditArea::mousePressEvent(QMouseEvent *event)
 {
     QPoint pos = event->pos();
@@ -525,3 +535,38 @@ QString VEditArea::getSelectedText()
         return QString();
     }
 }
+
+int VEditArea::focusNextWindow(int p_biaIdx)
+{
+    if (p_biaIdx == 0) {
+        return curWindowIndex;
+    }
+    int newIdx = curWindowIndex + p_biaIdx;
+    if (newIdx < 0) {
+        newIdx = 0;
+    } else if (newIdx >= splitter->count()) {
+        newIdx = splitter->count() - 1;
+    }
+    if (newIdx >= 0 && newIdx < splitter->count()) {
+        setCurrentWindow(newIdx, true);
+    } else {
+        newIdx = -1;
+    }
+    return newIdx;
+}
+
+void VEditArea::moveCurrentTabOneSplit(bool p_right)
+{
+    if (splitter->count() < 2) {
+        return;
+    }
+    getWindow(curWindowIndex)->moveCurrentTabOneSplit(p_right);
+}
+
+VEditWindow *VEditArea::getCurrentWindow() const
+{
+    if (curWindowIndex < 0) {
+        return NULL;
+    }
+    return getWindow(curWindowIndex);
+}

+ 7 - 0
src/veditarea.h

@@ -42,6 +42,13 @@ public:
     inline VFindReplaceDialog *getFindReplaceDialog() const;
     // Return selected text of current edit tab.
     QString getSelectedText();
+    void splitCurrentWindow();
+    void removeCurrentWindow();
+    // Focus next window (curWindowIndex + p_biaIdx).
+    // Return the new current window index, otherwise, return -1.
+    int focusNextWindow(int p_biaIdx);
+    void moveCurrentTabOneSplit(bool p_right);
+    VEditWindow *getCurrentWindow() const;
 
 signals:
     void curTabStatusChanged(const VFile *p_file, const VEditTab *p_editTab, bool p_editMode);

+ 4 - 0
src/vedittab.cpp

@@ -333,6 +333,10 @@ void VEditTab::focusTab()
 void VEditTab::handleFocusChanged(QWidget * /* old */, QWidget *now)
 {
     if (isChild(now)) {
+        if (now == this) {
+            // When VEditTab get focus, it should focus to current widget.
+            currentWidget()->setFocus();
+        }
         emit getFocused();
     }
 }

+ 62 - 31
src/veditwindow.cpp

@@ -28,7 +28,6 @@ VEditWindow::VEditWindow(VNote *vnote, VEditArea *editArea, QWidget *parent)
     connect(bar, &QTabBar::customContextMenuRequested,
             this, &VEditWindow::tabbarContextMenuRequested);
 
-
     connect(this, &VEditWindow::tabCloseRequested,
             this, &VEditWindow::handleTabCloseRequest);
     connect(this, &VEditWindow::tabBarClicked,
@@ -416,17 +415,13 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos)
     menu.addAction(m_locateAct);
 
     int totalWin = m_editArea->windowCount();
-    int idx = m_editArea->windowIndex(this);
     if (totalWin > 1) {
         menu.addSeparator();
-        if (idx > 0) {
-            m_moveLeftAct->setData(tab);
-            menu.addAction(m_moveLeftAct);
-        }
-        if (idx < totalWin - 1) {
-            m_moveRightAct->setData(tab);
-            menu.addAction(m_moveRightAct);
-        }
+        m_moveLeftAct->setData(tab);
+        menu.addAction(m_moveLeftAct);
+
+        m_moveRightAct->setData(tab);
+        menu.addAction(m_moveRightAct);
     }
 
     menu.exec(bar->mapToGlobal(p_pos));
@@ -615,39 +610,41 @@ void VEditWindow::handleLocateAct()
 
 void VEditWindow::handleMoveLeftAct()
 {
-    int tab = m_locateAct->data().toInt();
+    int tab = m_moveLeftAct->data().toInt();
     Q_ASSERT(tab != -1);
-    VEditTab *editor = getTab(tab);
-    // Remove it from current window. This won't close the split even if it is
-    // the only tab.
-    removeTab(tab);
-
-    // Disconnect all the signals.
-    disconnect(editor, 0, this, 0);
-
-    int idx = m_editArea->windowIndex(this);
-    m_editArea->moveTab(editor, idx, idx - 1);
-
-    // If there is no tab, remove current split.
-    if (count() == 0) {
-        emit requestRemoveSplit(this);
-    }
+    moveTabOneSplit(tab, false);
 }
 
 void VEditWindow::handleMoveRightAct()
 {
-    int tab = m_locateAct->data().toInt();
+    int tab = m_moveRightAct->data().toInt();
     Q_ASSERT(tab != -1);
-    VEditTab *editor = getTab(tab);
+    moveTabOneSplit(tab, true);
+}
+
+void VEditWindow::moveTabOneSplit(int p_tabIdx, bool p_right)
+{
+    Q_ASSERT(p_tabIdx > -1 && p_tabIdx < count());
+    int totalWin = m_editArea->windowCount();
+    if (totalWin < 2) {
+        return;
+    }
+    int idx = m_editArea->windowIndex(this);
+    int newIdx = p_right ? idx + 1 : idx - 1;
+    if (newIdx >= totalWin) {
+        newIdx = 0;
+    } else if (newIdx < 0) {
+        newIdx = totalWin - 1;
+    }
+    VEditTab *editor = getTab(p_tabIdx);
     // Remove it from current window. This won't close the split even if it is
     // the only tab.
-    removeTab(tab);
+    removeTab(p_tabIdx);
 
     // Disconnect all the signals.
     disconnect(editor, 0, this, 0);
 
-    int idx = m_editArea->windowIndex(this);
-    m_editArea->moveTab(editor, idx, idx + 1);
+    m_editArea->moveTab(editor, idx, newIdx);
 
     // If there is no tab, remove current split.
     if (count() == 0) {
@@ -655,6 +652,15 @@ void VEditWindow::handleMoveRightAct()
     }
 }
 
+void VEditWindow::moveCurrentTabOneSplit(bool p_right)
+{
+    int idx = currentIndex();
+    if (idx == -1) {
+        return;
+    }
+    moveTabOneSplit(idx, p_right);
+}
+
 bool VEditWindow::addEditTab(QWidget *p_widget)
 {
     if (!p_widget) {
@@ -697,3 +703,28 @@ void VEditWindow::clearSearchedWordHighlight()
         getTab(i)->clearSearchedWordHighlight();
     }
 }
+
+void VEditWindow::focusNextTab(bool p_right)
+{
+    focusWindow();
+    if (count() < 2) {
+        return;
+    }
+    int idx = currentIndex();
+    idx = p_right ? idx + 1 : idx - 1;
+    if (idx < 0) {
+        idx = count() - 1;
+    } else if (idx >= count()) {
+        idx = 0;
+    }
+    setCurrentIndex(idx);
+}
+
+bool VEditWindow::showOpenedFileList()
+{
+    if (count() == 0) {
+        return false;
+    }
+    leftBtn->showMenu();
+    return true;
+}

+ 5 - 0
src/veditwindow.h

@@ -46,6 +46,10 @@ public:
     // Set whether it is the current window.
     void setCurrentWindow(bool p_current);
     void clearSearchedWordHighlight();
+    void moveCurrentTabOneSplit(bool p_right);
+    void focusNextTab(bool p_right);
+    // Return true if the file list is shown.
+    bool showOpenedFileList();
 
 protected:
     void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
@@ -90,6 +94,7 @@ private:
     inline QString generateTooltip(const VFile *p_file) const;
     inline QString generateTabText(const QString &p_name, bool p_modified) const;
     bool canRemoveSplit();
+    void moveTabOneSplit(int p_tabIdx, bool p_right);
 
     VNote *vnote;
     VEditArea *m_editArea;

+ 9 - 3
src/vfilelist.cpp

@@ -246,9 +246,10 @@ void VFileList::deleteFile(VFile *p_file)
     VDirectory *dir = p_file->getDirectory();
     QString fileName = p_file->getName();
     int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
-                       tr("Are you sure to delete note %1?").arg(fileName),
-                       tr("This may be unrecoverable!"),
-                       QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this);
+                                  tr("Are you sure to delete note %1?").arg(fileName),
+                                  tr("This may be unrecoverable!"),
+                                  QMessageBox::Ok | QMessageBox::Cancel,
+                                  QMessageBox::Ok, this);
     if (ret == QMessageBox::Ok) {
         editArea->closeFile(p_file, true);
 
@@ -469,6 +470,11 @@ void VFileList::keyPressEvent(QKeyEvent *event)
     QWidget::keyPressEvent(event);
 }
 
+void VFileList::focusInEvent(QFocusEvent * /* p_event */)
+{
+    fileList->setFocus();
+}
+
 bool VFileList::locateFile(const VFile *p_file)
 {
     if (p_file) {

+ 2 - 0
src/vfilelist.h

@@ -17,6 +17,7 @@ class VNote;
 class QListWidget;
 class QPushButton;
 class VEditArea;
+class QFocusEvent;
 
 class VFileList : public QWidget
 {
@@ -52,6 +53,7 @@ public slots:
 
 protected:
     void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
+    void focusInEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
 
 private:
     void setupUI();

+ 55 - 5
src/vmainwindow.cpp

@@ -12,6 +12,7 @@
 #include "vavatar.h"
 #include "dialog/vfindreplacedialog.h"
 #include "dialog/vsettingsdialog.h"
+#include "vcaptain.h"
 
 extern VConfigManager vconfig;
 
@@ -37,6 +38,17 @@ VMainWindow::VMainWindow(QWidget *parent)
     setContextMenuPolicy(Qt::NoContextMenu);
 
     notebookSelector->update();
+
+    initCaptain();
+}
+
+void VMainWindow::initCaptain()
+{
+    // VCaptain should be visible to accpet key focus. But VCaptain
+    // may hide other widgets.
+    m_captain = new VCaptain(this);
+    connect(m_captain, &VCaptain::captainModeChanged,
+            this, &VMainWindow::handleCaptainModeChanged);
 }
 
 void VMainWindow::setupUI()
@@ -156,10 +168,10 @@ void VMainWindow::initViewToolBar()
     panelMenu->addAction(twoPanelViewAct);
 
     expandViewAct = new QAction(QIcon(":/resources/icons/expand.svg"),
-                                         tr("Expand"), this);
+                                tr("Expand"), this);
     expandViewAct->setStatusTip(tr("Expand the edit area"));
     expandViewAct->setCheckable(true);
-    expandViewAct->setShortcut(QKeySequence("Ctrl+E"));
+    expandViewAct->setShortcut(QKeySequence("Ctrl+T"));
     expandViewAct->setMenu(panelMenu);
     connect(expandViewAct, &QAction::triggered,
             this, &VMainWindow::expandPanelView);
@@ -826,6 +838,15 @@ void VMainWindow::twoPanelView()
     m_onePanel = false;
 }
 
+void VMainWindow::toggleOnePanelView()
+{
+    if (m_onePanel) {
+        twoPanelView();
+    } else {
+        onePanelView();
+    }
+}
+
 void VMainWindow::expandPanelView(bool p_checked)
 {
     int nrSplits = 0;
@@ -948,9 +969,11 @@ void VMainWindow::resizeEvent(QResizeEvent *event)
 
 void VMainWindow::keyPressEvent(QKeyEvent *event)
 {
-    if (event->key() == Qt::Key_Escape
-        || (event->key() == Qt::Key_BracketLeft
-            && event->modifiers() == Qt::ControlModifier)) {
+    int key = event->key();
+    Qt::KeyboardModifiers modifiers = event->modifiers();
+    if (key == Qt::Key_Escape
+        || (key == Qt::Key_BracketLeft
+            && modifiers == Qt::ControlModifier)) {
         m_findReplaceDialog->closeDialog();
         event->accept();
         return;
@@ -1002,6 +1025,13 @@ void VMainWindow::locateFile(VFile *p_file)
     twoPanelView();
 }
 
+void VMainWindow::locateCurrentFile()
+{
+    if (m_curFile) {
+        locateFile(m_curFile);
+    }
+}
+
 void VMainWindow::handleFindDialogTextChanged(const QString &p_text, uint /* p_options */)
 {
     bool enabled = true;
@@ -1025,3 +1055,23 @@ void VMainWindow::viewSettings()
     VSettingsDialog settingsDialog(this);
     settingsDialog.exec();
 }
+
+void VMainWindow::handleCaptainModeChanged(bool p_enabled)
+{
+    static QString normalBaseColor = m_avatar->getBaseColor();
+    static QString captainModeColor = vnote->getColorFromPalette("Purple5");
+
+    if (p_enabled) {
+        m_avatar->updateBaseColor(captainModeColor);
+    } else {
+        m_avatar->updateBaseColor(normalBaseColor);
+    }
+}
+
+void VMainWindow::closeCurrentFile()
+{
+    if (m_curFile) {
+        editArea->closeFile(m_curFile, false);
+    }
+}
+

+ 11 - 0
src/vmainwindow.h

@@ -28,15 +28,19 @@ class VOutline;
 class VNotebookSelector;
 class VAvatar;
 class VFindReplaceDialog;
+class VCaptain;
 
 class VMainWindow : public QMainWindow
 {
     Q_OBJECT
 
 public:
+    friend class VCaptain;
+
     VMainWindow(QWidget *parent = 0);
     const QVector<QPair<QString, QString> > &getPalette() const;
     void locateFile(VFile *p_file);
+    void locateCurrentFile();
 
 private slots:
     void importNoteFromFile();
@@ -63,6 +67,7 @@ private slots:
     void openFindDialog();
     void enableMermaid(bool p_checked);
     void enableMathjax(bool p_checked);
+    void handleCaptainModeChanged(bool p_enabled);
 
 protected:
     void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
@@ -97,10 +102,16 @@ private:
     void restoreStateAndGeometry();
     void repositionAvatar();
 
+    void initCaptain();
+    void toggleOnePanelView();
+    void closeCurrentFile();
+
     VNote *vnote;
     QPointer<VFile> m_curFile;
     QPointer<VEditTab> m_curTab;
 
+    VCaptain *m_captain;
+
     QLabel *notebookLabel;
     QLabel *directoryLabel;
     VNotebookSelector *notebookSelector;

+ 23 - 1
src/vnote.cpp

@@ -41,7 +41,7 @@ void VNote::initPalette(QPalette palette)
                                              palette.background().color().name()));
     m_palette.append(QPair<QString, QString>("hover-color", "#42A5F5"));
     m_palette.append(QPair<QString, QString>("base-color", "#BDBDBD"));
-    m_palette.append(QPair<QString, QString>("focus-color", "#15AE67"));
+    m_palette.append(QPair<QString, QString>("focus-color", "#75C5B5"));
     m_palette.append(QPair<QString, QString>("logo-base", "#D6EACE"));
     m_palette.append(QPair<QString, QString>("logo-max", "#15AE67"));
     m_palette.append(QPair<QString, QString>("logo-min", "#75C5B5"));
@@ -79,6 +79,28 @@ void VNote::initPalette(QPalette palette)
     m_palette.append(QPair<QString, QString>("Green7", "#388E3C"));
     m_palette.append(QPair<QString, QString>("Green8", "#2E7D32"));
     m_palette.append(QPair<QString, QString>("Green9", "#1B5E20"));
+
+    m_palette.append(QPair<QString, QString>("DeepPurple0", "#EDE7F6"));
+    m_palette.append(QPair<QString, QString>("DeepPurple1", "#D1C4E9"));
+    m_palette.append(QPair<QString, QString>("DeepPurple2", "#B39DDB"));
+    m_palette.append(QPair<QString, QString>("DeepPurple3", "#9575CD"));
+    m_palette.append(QPair<QString, QString>("DeepPurple4", "#7E57C2"));
+    m_palette.append(QPair<QString, QString>("DeepPurple5", "#673AB7"));
+    m_palette.append(QPair<QString, QString>("DeepPurple6", "#5E35B1"));
+    m_palette.append(QPair<QString, QString>("DeepPurple7", "#512DA8"));
+    m_palette.append(QPair<QString, QString>("DeepPurple8", "#4527A0"));
+    m_palette.append(QPair<QString, QString>("DeepPurple9", "#311B92"));
+
+    m_palette.append(QPair<QString, QString>("Purple0", "#F3E5F5"));
+    m_palette.append(QPair<QString, QString>("Purple1", "#E1BEE7"));
+    m_palette.append(QPair<QString, QString>("Purple2", "#CE93D8"));
+    m_palette.append(QPair<QString, QString>("Purple3", "#BA68C8"));
+    m_palette.append(QPair<QString, QString>("Purple4", "#AB47BC"));
+    m_palette.append(QPair<QString, QString>("Purple5", "#9C27B0"));
+    m_palette.append(QPair<QString, QString>("Purple6", "#8E24AA"));
+    m_palette.append(QPair<QString, QString>("Purple7", "#7B1FA2"));
+    m_palette.append(QPair<QString, QString>("Purple8", "#6A1B9A"));
+    m_palette.append(QPair<QString, QString>("Purple9", "#4A148C"));
 }
 
 QString VNote::getColorFromPalette(const QString &p_name) const