Browse Source

support compact mode in main window

Add enable_compact_mode config.
Le Tan 8 years ago
parent
commit
ace96c7ce2
10 changed files with 311 additions and 93 deletions
  1. 3 2
      README.md
  2. 3 2
      README_zh.md
  3. 10 0
      src/resources/icons/compact_mode.svg
  4. 3 0
      src/resources/vnote.ini
  5. 2 2
      src/vcaptain.cpp
  6. 4 0
      src/vconfigmanager.cpp
  7. 38 0
      src/vconfigmanager.h
  8. 217 80
      src/vmainwindow.cpp
  9. 30 7
      src/vmainwindow.h
  10. 1 0
      src/vnote.qrc

+ 3 - 2
README.md

@@ -54,7 +54,8 @@ Utilizing Qt, VNote could run on **Linux**, **Windows**, and **macOS** (due to t
 - Supports infinite levels of folders;
 - Supports multiple tabs and splitting windows;
 - Supports [Mermaid](http://knsv.github.io/mermaid/), [Flowchart.js](http://flowchart.js.org/), and [MathJax](https://www.mathjax.org/);
-- Supports HiDPI.
+- Supports HiDPI;
+- Supports attachments of notes.
 
 ![VNote Edit](screenshots/vnote_edit.gif)
 
@@ -199,7 +200,7 @@ If you prefer command line on macOS, you could follow these steps.
 5. Now you got the bundle `path/to/project/build/src/VNote.app`. Enjoy yourself!
 
 # Dependencies
-- [Qt 5.7](http://qt-project.org) (L-GPL v3)
+- [Qt 5.9](http://qt-project.org) (L-GPL v3)
 - [PEG Markdown Highlight](http://hasseg.org/peg-markdown-highlight/) (MIT License)
 - [Hoedown 3.0.7](https://github.com/hoedown/hoedown/) (ISC License)
 - [Marked](https://github.com/chjj/marked) (MIT License)

+ 3 - 2
README_zh.md

@@ -54,8 +54,9 @@ VNote不是一个简单的Markdown编辑器。通过提供笔记管理功能,V
 - 支持Vim模式以及一系列强大的快捷键;
 - 支持无限层级的文件夹;
 - 支持多个标签页和窗口分割;
-- 支持[Mermaid](http://knsv.github.io/mermaid/), [Flowchart.js](http://flowchart.js.org/) 和 [MathJax](https://www.mathjax.org/);
+- 支持[Mermaid](http://knsv.github.io/mermaid/), [Flowchart.js](http://flowchart.js.org/) 和 [MathJax](https://www.mathjax.org/)
 - 支持高分辨率;
+- 支持笔记附件。
 
 ![VNote Edit](screenshots/vnote_edit.gif)
 
@@ -203,7 +204,7 @@ sudo make install
 5. 此时得到VNote的Bundle `path/to/project/build/src/VNote.app`,打开即可。
 
 # 依赖
-- [Qt 5.7](http://qt-project.org) (L-GPL v3)
+- [Qt 5.9](http://qt-project.org) (L-GPL v3)
 - [PEG Markdown Highlight](http://hasseg.org/peg-markdown-highlight/) (MIT License)
 - [Hoedown 3.0.7](https://github.com/hoedown/hoedown/) (ISC License)
 - [Marked](https://github.com/chjj/marked) (MIT License)

+ 10 - 0
src/resources/icons/compact_mode.svg

@@ -0,0 +1,10 @@
+<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <title>one_panel</title>
+ <g>
+  <title>Layer 1</title>
+  <rect fill="none" stroke="#000000" stroke-width="40" x="58" y="57" width="395" height="395" id="svg_1"/>
+  <line fill="none" stroke="#000000" stroke-width="40" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x1="193" y1="61" x2="193" y2="455" id="svg_4"/>
+  <line stroke="#000000" transform="rotate(-90 125.49999237060548,220.1999969482422) " id="svg_2" fill="none" stroke-width="40" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x1="125.49999" y1="169.7" x2="125.49999" y2="270.7"/>
+ </g>
+</svg>

+ 3 - 0
src/resources/vnote.ini

@@ -135,6 +135,9 @@ confirm_reload_folder=true
 ; Whether double click on a tab to close it
 double_click_close_tab=true
 
+; Whether put folder and note panel in one vertical column
+enable_compact_mode=false
+
 [web]
 ; Location and configuration for Mathjax
 mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML

+ 2 - 2
src/vcaptain.cpp

@@ -276,7 +276,7 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers)
         m_mainWindow->editArea->removeCurrentWindow();
 
         QWidget *nextFocus = m_mainWindow->editArea->currentEditTab();
-        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->fileList;
+        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList();
         break;
     }
 
@@ -305,7 +305,7 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers)
 
         // m_widgetBeforeCaptain may be the closed tab which will cause crash.
         QWidget *nextFocus = m_mainWindow->editArea->currentEditTab();
-        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->fileList;
+        m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList();
         break;
     }
 

+ 4 - 0
src/vconfigmanager.cpp

@@ -95,6 +95,7 @@ void VConfigManager::initialize()
     m_mainWindowGeometry = getConfigFromSettings("session", "main_window_geometry").toByteArray();
     m_mainWindowState = getConfigFromSettings("session", "main_window_state").toByteArray();
     m_mainSplitterState = getConfigFromSettings("session", "main_splitter_state").toByteArray();
+    m_naviSplitterState = getConfigFromSettings("session", "navi_splitter_state").toByteArray();
 
     m_findCaseSensitive = getConfigFromSettings("global",
                                                 "find_case_sensitive").toBool();
@@ -227,6 +228,9 @@ void VConfigManager::initialize()
 
     m_doubleClickCloseTab = getConfigFromSettings("global",
                                                   "double_click_close_tab").toBool();
+
+    m_enableCompactMode = getConfigFromSettings("global",
+                                                "enable_compact_mode").toBool();
 }
 
 void VConfigManager::readPredefinedColorsFromSettings()

+ 38 - 0
src/vconfigmanager.h

@@ -153,6 +153,9 @@ public:
     const QByteArray &getMainSplitterState() const;
     void setMainSplitterState(const QByteArray &p_state);
 
+    const QByteArray &getNaviSplitterState() const;
+    void setNaviSplitterState(const QByteArray &p_state);
+
     bool getFindCaseSensitive() const;
     void setFindCaseSensitive(bool p_enabled);
 
@@ -291,6 +294,9 @@ public:
     // Whether user specify template_code_block_css_url directly.
     bool getUserSpecifyTemplateCodeBlockCssUrl() const;
 
+    bool getEnableCompactMode() const;
+    void setEnableCompactMode(bool p_enabled);
+
     // Return the configured key sequence of @p_operation.
     // Return empty if there is no corresponding config.
     QString getShortcutKeySequence(const QString &p_operation) const;
@@ -429,6 +435,7 @@ private:
     QByteArray m_mainWindowGeometry;
     QByteArray m_mainWindowState;
     QByteArray m_mainSplitterState;
+    QByteArray m_naviSplitterState;
 
     // Find/Replace dialog options
     bool m_findCaseSensitive;
@@ -600,6 +607,9 @@ private:
     // Whether double click on a tab to close it.
     bool m_doubleClickCloseTab;
 
+    // Whether put folder and note panel in one single column.
+    bool m_enableCompactMode;
+
     // The name of the config file in each directory, obsolete.
     // Use c_dirConfigFile instead.
     static const QString c_obsoleteDirConfigFile;
@@ -609,8 +619,10 @@ private:
 
     // The name of the default configuration file
     static const QString defaultConfigFilePath;
+
     // QSettings for the user configuration
     QSettings *userSettings;
+
     // Qsettings for @defaultConfigFileName
     QSettings *defaultSettings;
 
@@ -899,6 +911,17 @@ inline void VConfigManager::setMainSplitterState(const QByteArray &p_state)
     setConfigToSettings("session", "main_splitter_state", m_mainSplitterState);
 }
 
+inline const QByteArray& VConfigManager::getNaviSplitterState() const
+{
+    return m_naviSplitterState;
+}
+
+inline void VConfigManager::setNaviSplitterState(const QByteArray &p_state)
+{
+    m_naviSplitterState = p_state;
+    setConfigToSettings("session", "navi_splitter_state", m_naviSplitterState);
+}
+
 inline bool VConfigManager::getFindCaseSensitive() const
 {
     return m_findCaseSensitive;
@@ -1570,4 +1593,19 @@ inline bool VConfigManager::getUserSpecifyTemplateCodeBlockCssUrl() const
     return !m_templateCodeBlockCssUrl.isEmpty();
 }
 
+inline bool VConfigManager::getEnableCompactMode() const
+{
+    return m_enableCompactMode;
+}
+
+inline void VConfigManager::setEnableCompactMode(bool p_enabled)
+{
+    if (m_enableCompactMode == p_enabled) {
+        return;
+    }
+
+    m_enableCompactMode = p_enabled;
+    setConfigToSettings("global", "enable_compact_mode", m_enableCompactMode);
+}
+
 #endif // VCONFIGMANAGER_H

+ 217 - 80
src/vmainwindow.cpp

@@ -41,7 +41,7 @@ extern QFile g_logFile;
 #endif
 
 VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent)
-    : QMainWindow(p_parent), m_onePanel(false), m_guard(p_guard),
+    : QMainWindow(p_parent), m_guard(p_guard),
       m_windowOldState(Qt::WindowNoState), m_requestQuit(false)
 {
     setWindowIcon(QIcon(":/resources/icons/vnote.ico"));
@@ -50,6 +50,12 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent)
     vnote->initPalette(palette());
     initPredefinedColorPixmaps();
 
+    if (g_config->getEnableCompactMode()) {
+        m_panelViewState = PanelViewState::CompactMode;
+    } else {
+        m_panelViewState = PanelViewState::TwoPanels;
+    }
+
     setupUI();
 
     initMenuBar();
@@ -58,6 +64,9 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent)
     initAvatar();
 
     restoreStateAndGeometry();
+
+    changePanelView(m_panelViewState);
+
     setContextMenuPolicy(Qt::NoContextMenu);
 
     notebookSelector->update();
@@ -88,7 +97,7 @@ void VMainWindow::initCaptain()
 
     m_captain->registerNavigationTarget(notebookSelector);
     m_captain->registerNavigationTarget(directoryTree);
-    m_captain->registerNavigationTarget(fileList);
+    m_captain->registerNavigationTarget(m_fileList);
     m_captain->registerNavigationTarget(editArea);
     m_captain->registerNavigationTarget(outline);
 }
@@ -97,29 +106,29 @@ void VMainWindow::setupUI()
 {
     QWidget *directoryPanel = setupDirectoryPanel();
 
-    fileList = new VFileList();
-    fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+    m_fileList = new VFileList();
+    m_fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
 
     editArea = new VEditArea(vnote);
     editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     m_findReplaceDialog = editArea->getFindReplaceDialog();
-    fileList->setEditArea(editArea);
+    m_fileList->setEditArea(editArea);
     directoryTree->setEditArea(editArea);
     notebookSelector->setEditArea(editArea);
 
     // Main Splitter
-    mainSplitter = new QSplitter();
-    mainSplitter->setObjectName("MainSplitter");
-    mainSplitter->addWidget(directoryPanel);
-    mainSplitter->addWidget(fileList);
-    mainSplitter->addWidget(editArea);
-    mainSplitter->setStretchFactor(0, 0);
-    mainSplitter->setStretchFactor(1, 0);
-    mainSplitter->setStretchFactor(2, 1);
+    m_mainSplitter = new QSplitter();
+    m_mainSplitter->setObjectName("MainSplitter");
+    m_mainSplitter->addWidget(directoryPanel);
+    m_mainSplitter->addWidget(m_fileList);
+    m_mainSplitter->addWidget(editArea);
+    m_mainSplitter->setStretchFactor(0, 0);
+    m_mainSplitter->setStretchFactor(1, 0);
+    m_mainSplitter->setStretchFactor(2, 1);
 
     // Signals
     connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
-            fileList, &VFileList::setDirectory);
+            m_fileList, &VFileList::setDirectory);
     connect(directoryTree, &VDirectoryTree::directoryUpdated,
             editArea, &VEditArea::handleDirectoryUpdated);
 
@@ -133,11 +142,11 @@ void VMainWindow::setupUI()
                 }
             });
 
-    connect(fileList, &VFileList::fileClicked,
+    connect(m_fileList, &VFileList::fileClicked,
             editArea, &VEditArea::openFile);
-    connect(fileList, &VFileList::fileCreated,
+    connect(m_fileList, &VFileList::fileCreated,
             editArea, &VEditArea::openFile);
-    connect(fileList, &VFileList::fileUpdated,
+    connect(m_fileList, &VFileList::fileUpdated,
             editArea, &VEditArea::handleFileUpdated);
     connect(editArea, &VEditArea::tabStatusUpdated,
             this, &VMainWindow::handleAreaTabStatusUpdated);
@@ -148,7 +157,7 @@ void VMainWindow::setupUI()
     connect(m_findReplaceDialog, &VFindReplaceDialog::findTextChanged,
             this, &VMainWindow::handleFindDialogTextChanged);
 
-    setCentralWidget(mainSplitter);
+    setCentralWidget(m_mainSplitter);
 
     m_vimIndicator = new VVimIndicator(this);
     m_vimIndicator->hide();
@@ -165,26 +174,49 @@ void VMainWindow::setupUI()
 
 QWidget *VMainWindow::setupDirectoryPanel()
 {
+    // Notebook selector.
     notebookLabel = new QLabel(tr("Notebooks"));
     notebookLabel->setProperty("TitleLabel", true);
     notebookLabel->setProperty("NotebookPanel", true);
-    directoryLabel = new QLabel(tr("Folders"));
-    directoryLabel->setProperty("TitleLabel", true);
-    directoryLabel->setProperty("NotebookPanel", true);
 
     notebookSelector = new VNotebookSelector(vnote);
     notebookSelector->setObjectName("NotebookSelector");
     notebookSelector->setProperty("NotebookPanel", true);
     notebookSelector->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
 
+    // Navigation panel.
+    directoryLabel = new QLabel(tr("Folders"));
+    directoryLabel->setProperty("TitleLabel", true);
+    directoryLabel->setProperty("NotebookPanel", true);
+
     directoryTree = new VDirectoryTree;
     directoryTree->setProperty("NotebookPanel", true);
 
+    QVBoxLayout *naviLayout = new QVBoxLayout;
+    naviLayout->addWidget(directoryLabel);
+    naviLayout->addWidget(directoryTree);
+    naviLayout->setContentsMargins(0, 0, 0, 0);
+    naviLayout->setSpacing(0);
+    QWidget *naviWidget = new QWidget();
+    naviWidget->setLayout(naviLayout);
+
+    QWidget *tmpWidget = new QWidget();
+
+    // Compact splitter.
+    m_naviSplitter = new QSplitter();
+    m_naviSplitter->setOrientation(Qt::Vertical);
+    m_naviSplitter->setObjectName("NaviSplitter");
+    m_naviSplitter->addWidget(naviWidget);
+    m_naviSplitter->addWidget(tmpWidget);
+    m_naviSplitter->setStretchFactor(0, 0);
+    m_naviSplitter->setStretchFactor(1, 1);
+
+    tmpWidget->hide();
+
     QVBoxLayout *nbLayout = new QVBoxLayout;
     nbLayout->addWidget(notebookLabel);
     nbLayout->addWidget(notebookSelector);
-    nbLayout->addWidget(directoryLabel);
-    nbLayout->addWidget(directoryTree);
+    nbLayout->addWidget(m_naviSplitter);
     nbLayout->setContentsMargins(0, 0, 0, 0);
     nbLayout->setSpacing(0);
     QWidget *nbContainer = new QWidget();
@@ -226,24 +258,62 @@ void VMainWindow::initViewToolBar(QSize p_iconSize)
         viewToolBar->setIconSize(p_iconSize);
     }
 
+    m_viewActGroup = new QActionGroup(this);
     QAction *onePanelViewAct = new QAction(QIcon(":/resources/icons/one_panel.svg"),
-                                           tr("&Single Panel"), this);
+                                           tr("&Single Panel"),
+                                           m_viewActGroup);
     onePanelViewAct->setStatusTip(tr("Display only the notes list panel"));
     onePanelViewAct->setToolTip(tr("Single Panel (Ctrl+E P)"));
-    connect(onePanelViewAct, &QAction::triggered,
-            this, &VMainWindow::onePanelView);
+    onePanelViewAct->setCheckable(true);
+    onePanelViewAct->setData((int)PanelViewState::SinglePanel);
 
     QAction *twoPanelViewAct = new QAction(QIcon(":/resources/icons/two_panels.svg"),
-                                           tr("&Two Panels"), this);
+                                           tr("&Two Panels"),
+                                           m_viewActGroup);
     twoPanelViewAct->setStatusTip(tr("Display both the folders and notes list panel"));
     twoPanelViewAct->setToolTip(tr("Two Panels (Ctrl+E P)"));
-    connect(twoPanelViewAct, &QAction::triggered,
-            this, &VMainWindow::twoPanelView);
+    twoPanelViewAct->setCheckable(true);
+    twoPanelViewAct->setData((int)PanelViewState::TwoPanels);
+
+    QAction *compactViewAct = new QAction(QIcon(":/resources/icons/compact_mode.svg"),
+                                           tr("&Compact Mode"),
+                                           m_viewActGroup);
+    compactViewAct->setStatusTip(tr("Integrate the folders and notes list panel in one column"));
+    compactViewAct->setCheckable(true);
+    compactViewAct->setData((int)PanelViewState::CompactMode);
+
+    connect(m_viewActGroup, &QActionGroup::triggered,
+            this, [this](QAction *p_action) {
+                if (!p_action) {
+                    return;
+                }
+
+                int act = p_action->data().toInt();
+                switch (act) {
+                case (int)PanelViewState::SinglePanel:
+                    onePanelView();
+                    break;
+
+                case (int)PanelViewState::TwoPanels:
+                    twoPanelView();
+                    break;
+
+                case (int)PanelViewState::CompactMode:
+                    m_panelViewState = PanelViewState::CompactMode;
+                    g_config->setEnableCompactMode(true);
+                    changePanelView(m_panelViewState);
+                    break;
+
+                default:
+                    break;
+                }
+            });
 
     QMenu *panelMenu = new QMenu(this);
     panelMenu->setToolTipsVisible(true);
     panelMenu->addAction(onePanelViewAct);
     panelMenu->addAction(twoPanelViewAct);
+    panelMenu->addAction(compactViewAct);
 
     expandViewAct = new QAction(QIcon(":/resources/icons/expand.svg"),
                                 tr("Expand (Ctrl+E E)"), this);
@@ -251,7 +321,11 @@ void VMainWindow::initViewToolBar(QSize p_iconSize)
     expandViewAct->setCheckable(true);
     expandViewAct->setMenu(panelMenu);
     connect(expandViewAct, &QAction::triggered,
-            this, &VMainWindow::expandPanelView);
+            this, [this](bool p_checked) {
+                // Recover m_panelViewState or change to expand mode.
+                changePanelView(p_checked ? PanelViewState::ExpandMode
+                                          : m_panelViewState);
+            });
 
     viewToolBar->addAction(expandViewAct);
 }
@@ -378,7 +452,7 @@ void VMainWindow::initFileToolBar(QSize p_iconSize)
     qDebug() << "set NewNote shortcut to" << keySeq;
     newNoteAct->setShortcut(QKeySequence(keySeq));
     connect(newNoteAct, &QAction::triggered,
-            fileList, &VFileList::newFile);
+            m_fileList, &VFileList::newFile);
 
     noteInfoAct = new QAction(QIcon(":/resources/icons/note_info_tb.svg"),
                               tr("Note &Info"), this);
@@ -1064,7 +1138,7 @@ void VMainWindow::importNoteFromFile()
     lastPath = QFileInfo(files[0]).path();
 
     QString msg;
-    if (!fileList->importFiles(files, &msg)) {
+    if (!m_fileList->importFiles(files, &msg)) {
         VUtils::showMessage(QMessageBox::Warning,
                             tr("Warning"),
                             tr("Fail to create notes for all the files."),
@@ -1694,65 +1768,119 @@ void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info)
 
 void VMainWindow::onePanelView()
 {
-    changeSplitterView(1);
-    expandViewAct->setChecked(false);
-    m_onePanel = true;
+    m_panelViewState = PanelViewState::SinglePanel;
+    g_config->setEnableCompactMode(false);
+    changePanelView(m_panelViewState);
 }
 
 void VMainWindow::twoPanelView()
 {
-    changeSplitterView(2);
-    expandViewAct->setChecked(false);
-    m_onePanel = false;
+    m_panelViewState = PanelViewState::TwoPanels;
+    g_config->setEnableCompactMode(false);
+    changePanelView(m_panelViewState);
 }
 
 void VMainWindow::toggleOnePanelView()
 {
-    if (m_onePanel) {
-        twoPanelView();
-    } else {
+    if (m_panelViewState == PanelViewState::TwoPanels) {
         onePanelView();
+    } else {
+        twoPanelView();
     }
 }
 
-void VMainWindow::expandPanelView(bool p_checked)
+void VMainWindow::enableCompactMode(bool p_enabled)
 {
-    int nrSplits = 0;
-    if (p_checked) {
-        nrSplits = 0;
+    const int fileListIdx = 1;
+    bool isCompactMode = m_naviSplitter->indexOf(m_fileList) != -1;
+    if (p_enabled) {
+        // Change to compact mode.
+        if (isCompactMode) {
+            return;
+        }
+
+        // Take m_fileList out of m_mainSplitter.
+        QWidget *tmpWidget = new QWidget(this);
+        Q_ASSERT(fileListIdx == m_mainSplitter->indexOf(m_fileList));
+        m_fileList->hide();
+        m_mainSplitter->replaceWidget(fileListIdx, tmpWidget);
+        tmpWidget->hide();
+
+        // Insert m_fileList into m_naviSplitter.
+        QWidget *wid = m_naviSplitter->replaceWidget(fileListIdx, m_fileList);
+        delete wid;
+
+        m_fileList->show();
     } else {
-        if (m_onePanel) {
-            nrSplits = 1;
-        } else {
-            nrSplits = 2;
+        // Disable compact mode and go back to two panels view.
+        if (!isCompactMode) {
+            return;
         }
+
+        // Take m_fileList out of m_naviSplitter.
+        Q_ASSERT(fileListIdx == m_naviSplitter->indexOf(m_fileList));
+        QWidget *tmpWidget = new QWidget(this);
+        m_fileList->hide();
+        m_naviSplitter->replaceWidget(fileListIdx, tmpWidget);
+        tmpWidget->hide();
+
+        // Insert m_fileList into m_mainSplitter.
+        QWidget *wid = m_mainSplitter->replaceWidget(fileListIdx, m_fileList);
+        delete wid;
+
+        m_fileList->show();
     }
-    changeSplitterView(nrSplits);
 }
 
-void VMainWindow::changeSplitterView(int nrPanel)
+void VMainWindow::changePanelView(PanelViewState p_state)
 {
-    switch (nrPanel) {
-    case 0:
-        // Expand
-        mainSplitter->widget(0)->hide();
-        mainSplitter->widget(1)->hide();
-        mainSplitter->widget(2)->show();
+    switch (p_state) {
+    case PanelViewState::ExpandMode:
+        m_mainSplitter->widget(0)->hide();
+        m_mainSplitter->widget(1)->hide();
+        m_mainSplitter->widget(2)->show();
+        break;
+
+    case PanelViewState::SinglePanel:
+        enableCompactMode(false);
+
+        m_mainSplitter->widget(0)->hide();
+        m_mainSplitter->widget(1)->show();
+        m_mainSplitter->widget(2)->show();
         break;
-    case 1:
-        // Single panel
-        mainSplitter->widget(0)->hide();
-        mainSplitter->widget(1)->show();
-        mainSplitter->widget(2)->show();
+
+    case PanelViewState::TwoPanels:
+        enableCompactMode(false);
+
+        m_mainSplitter->widget(0)->show();
+        m_mainSplitter->widget(1)->show();
+        m_mainSplitter->widget(2)->show();
         break;
-    case 2:
-        // Two panels
-        mainSplitter->widget(0)->show();
-        mainSplitter->widget(1)->show();
-        mainSplitter->widget(2)->show();
+
+    case PanelViewState::CompactMode:
+        m_mainSplitter->widget(0)->show();
+        m_mainSplitter->widget(1)->hide();
+        m_mainSplitter->widget(2)->show();
+
+        enableCompactMode(true);
         break;
+
     default:
-        qWarning() << "invalid panel number" << nrPanel;
+        break;
+    }
+
+    // Change the action state.
+    QList<QAction *> acts = m_viewActGroup->actions();
+    for (auto & act : acts) {
+        if (act->data().toInt() == (int)p_state) {
+            act->setChecked(true);
+        } else {
+            act->setChecked(false);
+        }
+    }
+
+    if (p_state != PanelViewState::ExpandMode) {
+        expandViewAct->setChecked(false);
     }
 }
 
@@ -1772,7 +1900,7 @@ void VMainWindow::curEditFileInfo()
     if (m_curFile->getType() == FileType::Note) {
         VNoteFile *file = dynamic_cast<VNoteFile *>((VFile *)m_curFile);
         Q_ASSERT(file);
-        fileList->fileInfo(file);
+        m_fileList->fileInfo(file);
     } else if (m_curFile->getType() == FileType::Orphan) {
         VOrphanFile *file = dynamic_cast<VOrphanFile *>((VFile *)m_curFile);
         Q_ASSERT(file);
@@ -1789,7 +1917,7 @@ void VMainWindow::deleteCurNote()
     }
 
     VNoteFile *file = dynamic_cast<VNoteFile *>((VFile *)m_curFile);
-    fileList->deleteFile(file);
+    m_fileList->deleteFile(file);
 }
 
 void VMainWindow::closeEvent(QCloseEvent *event)
@@ -1843,14 +1971,17 @@ void VMainWindow::closeEvent(QCloseEvent *event)
 
 void VMainWindow::saveStateAndGeometry()
 {
-    // In one panel view, it will save the wrong state that the directory tree
-    // panel has a width of zero.
-    twoPanelView();
-
     g_config->setMainWindowGeometry(saveGeometry());
     g_config->setMainWindowState(saveState());
     g_config->setToolsDockChecked(toolDock->isVisible());
-    g_config->setMainSplitterState(mainSplitter->saveState());
+
+    // In one panel view, it will save the wrong state that the directory tree
+    // panel has a width of zero.
+    changePanelView(PanelViewState::TwoPanels);
+    g_config->setMainSplitterState(m_mainSplitter->saveState());
+
+    changePanelView(PanelViewState::CompactMode);
+    g_config->setNaviSplitterState(m_naviSplitter->saveState());
 }
 
 void VMainWindow::restoreStateAndGeometry()
@@ -1864,9 +1995,15 @@ void VMainWindow::restoreStateAndGeometry()
         restoreState(state);
     }
     toolDock->setVisible(g_config->getToolsDockChecked());
+
     const QByteArray &splitterState = g_config->getMainSplitterState();
     if (!splitterState.isEmpty()) {
-        mainSplitter->restoreState(splitterState);
+        m_mainSplitter->restoreState(splitterState);
+    }
+
+    const QByteArray &naviSplitterState = g_config->getNaviSplitterState();
+    if (!naviSplitterState.isEmpty()) {
+        m_naviSplitter->restoreState(naviSplitterState);
     }
 }
 
@@ -1908,7 +2045,7 @@ void VMainWindow::keyPressEvent(QKeyEvent *event)
 
 void VMainWindow::repositionAvatar()
 {
-    int diameter = mainSplitter->pos().y();
+    int diameter = m_mainSplitter->pos().y();
     int x = width() - diameter - 5;
     int y = 0;
     qDebug() << "avatar:" << diameter << x << y;
@@ -1942,13 +2079,13 @@ bool VMainWindow::locateFile(VFile *p_file)
 
         VDirectory *dir = file->getDirectory();
         if (directoryTree->locateDirectory(dir)) {
-            while (fileList->currentDirectory() != dir) {
+            while (m_fileList->currentDirectory() != dir) {
                 QCoreApplication::sendPostedEvents();
             }
 
-            if (fileList->locateFile(file)) {
+            if (m_fileList->locateFile(file)) {
                 ret = true;
-                fileList->setFocus();
+                m_fileList->setFocus();
             }
         }
     }

+ 30 - 7
src/vmainwindow.h

@@ -38,6 +38,15 @@ class QShortcut;
 class VButtonWithWidget;
 class VAttachmentList;
 
+enum class PanelViewState
+{
+    ExpandMode,
+    SinglePanel,
+    TwoPanels,
+    CompactMode,
+    Invalid
+};
+
 class VMainWindow : public QMainWindow
 {
     Q_OBJECT
@@ -105,7 +114,6 @@ private slots:
     void changeHighlightTrailingSapce(bool p_checked);
     void onePanelView();
     void twoPanelView();
-    void expandPanelView(bool p_checked);
     void curEditFileInfo();
     void deleteCurNote();
     void handleCurrentDirectoryChanged(const VDirectory *p_dir);
@@ -127,6 +135,9 @@ private slots:
     void printNote();
     void exportAsPDF();
 
+    // Set the panel view properly.
+    void enableCompactMode(bool p_enabled);
+
     // Handle Vim status updated.
     void handleVimStatusUpdated(const VVim *p_vim);
 
@@ -186,7 +197,6 @@ private:
     void initEditorLineNumberMenu(QMenu *p_menu);
 
     void initEditorStyleMenu(QMenu *p_emnu);
-    void changeSplitterView(int nrPanel);
     void updateWindowTitle(const QString &str);
     void updateActionStateFromTabStatusChange(const VFile *p_file,
                                               bool p_editMode);
@@ -213,6 +223,10 @@ private:
     // Init system tray icon and correspondign context menu.
     void initTrayIcon();
 
+    // Change the panel view according to @p_state.
+    // Will not change m_panelViewState.
+    void changePanelView(PanelViewState p_state);
+
     VNote *vnote;
     QPointer<VFile> m_curFile;
     QPointer<VEditTab> m_curTab;
@@ -222,9 +236,16 @@ private:
     QLabel *notebookLabel;
     QLabel *directoryLabel;
     VNotebookSelector *notebookSelector;
-    VFileList *fileList;
+    VFileList *m_fileList;
     VDirectoryTree *directoryTree;
-    QSplitter *mainSplitter;
+
+    // Splitter for directory | files | edit.
+    QSplitter *m_mainSplitter;
+
+    // Splitter for directory | files.
+    // Move directory and file panel in one compact vertical split.
+    QSplitter *m_naviSplitter;
+
     VEditArea *editArea;
     QDockWidget *toolDock;
     QToolBox *toolBox;
@@ -234,8 +255,7 @@ private:
     VVimIndicator *m_vimIndicator;
     VTabIndicator *m_tabIndicator;
 
-    // Whether it is one panel or two panles.
-    bool m_onePanel;
+    PanelViewState m_panelViewState;
 
     // Actions
     QAction *newRootDirAct;
@@ -269,6 +289,9 @@ private:
     // Act group for code block render styles.
     QActionGroup *m_codeBlockStyleActs;
 
+    // Act group for panel view actions.
+    QActionGroup *m_viewActGroup;
+
     QShortcut *m_closeNoteShortcut;
 
     // Menus
@@ -306,7 +329,7 @@ private:
 
 inline VFileList *VMainWindow::getFileList() const
 {
-    return fileList;
+    return m_fileList;
 }
 
 #endif // VMAINWINDOW_H

+ 1 - 0
src/vnote.qrc

@@ -130,5 +130,6 @@
         <file>resources/icons/delete_attachment.svg</file>
         <file>resources/icons/sort.svg</file>
         <file>resources/icons/create_subdir.svg</file>
+        <file>resources/icons/compact_mode.svg</file>
     </qresource>
 </RCC>