Browse Source

refactor: VNotebookSelector

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 years ago
parent
commit
e32ee1fa90

+ 15 - 18
src/resources/vnote.qss

@@ -36,18 +36,16 @@ QDockWidget::close-button:hover, QDockWidget::float-button:hover {
     background-color: @hover-color;
 }
 
-/* QComboBox */
-QComboBox[OnMainWindow="true"] {
+/* QComboBox#NotebookSelector */
+QComboBox#NotebookSelector {
     border: 1px solid grey;
     background-color: @base-background;
-}
-
-QComboBox[OnMainWindow="true"]:on { /* shift the text when the popup opens */
+    font-size: 14px;
     padding-top: 3px;
-    padding-left: 4px;
+    padding-bottom: 3px;
 }
 
-QComboBox[OnMainWindow="true"]::drop-down {
+QComboBox#NotebookSelector::drop-down {
     subcontrol-origin: padding;
     subcontrol-position: top right;
     width: 20px;
@@ -55,24 +53,23 @@ QComboBox[OnMainWindow="true"]::drop-down {
     background: transparent;
 }
 
-QComboBox[OnMainWindow="true"]::down-arrow {
+QComboBox#NotebookSelector::down-arrow {
     image: url(:/resources/icons/arrow_dropdown.svg);
     width: 20px;
 }
 
-QComboBox[OnMainWindow="true"]::down-arrow:on { /* shift the arrow when popup is open */
-    top: 1px;
-    left: 1px;
+QComboBox#NotebookSelector QListWidget {
+    border: 1px solid grey;
+    background-color: @base-background;
+    font-size: 14px;
 }
 
-QComboBox[OnMainWindow="true"]::item {
-    padding-left: 20px;
-    border: none;
-    height: 20px;
+QComboBox#NotebookSelector QListWidget::item {
+    padding-top: 5px;
+    padding-bottom: 5px;
 }
 
-QComboBox[OnMainWindow="true"]::item:selected {
-    padding-left: 20px;
-    border: none;
+QComboBox#NotebookSelector QListWidget::item:hover {
     background-color: @hover-color;
 }
+/* End QComboBox#NotebookSelector */

+ 4 - 2
src/src.pro

@@ -45,7 +45,8 @@ SOURCES += main.cpp\
     vsingleinstanceguard.cpp \
     vdirectory.cpp \
     vfile.cpp \
-    vnotebookselector.cpp
+    vnotebookselector.cpp \
+    vnofocusitemdelegate.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -79,7 +80,8 @@ HEADERS  += vmainwindow.h \
     vsingleinstanceguard.h \
     vdirectory.h \
     vfile.h \
-    vnotebookselector.h
+    vnotebookselector.h \
+    vnofocusitemdelegate.h
 
 RESOURCES += \
     vnote.qrc

+ 4 - 27
src/vmainwindow.cpp

@@ -80,30 +80,14 @@ QWidget *VMainWindow::setupDirectoryPanel()
     notebookLabel = new QLabel(tr("Notebook"));
     directoryLabel = new QLabel(tr("Directory"));
 
-    newNotebookBtn = new QPushButton(QIcon(":/resources/icons/create_notebook.svg"), "");
-    newNotebookBtn->setToolTip(tr("Create a new notebook"));
-    newNotebookBtn->setProperty("OnMainWindow", true);
-    deleteNotebookBtn = new QPushButton(QIcon(":/resources/icons/delete_notebook.svg"), "");
-    deleteNotebookBtn->setToolTip(tr("Delete current notebook"));
-    deleteNotebookBtn->setProperty("OnMainWindow", true);
-    notebookInfoBtn = new QPushButton(QIcon(":/resources/icons/notebook_info.svg"), "");
-    notebookInfoBtn->setToolTip(tr("View and edit current notebook's information"));
-    notebookInfoBtn->setProperty("OnMainWindow", true);
-
     notebookSelector = new VNotebookSelector(vnote);
-    notebookSelector->setProperty("OnMainWindow", true);
-    notebookSelector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+    notebookSelector->setObjectName("NotebookSelector");
+    notebookSelector->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
+
     directoryTree = new VDirectoryTree(vnote);
 
-    QHBoxLayout *nbBtnLayout = new QHBoxLayout;
-    nbBtnLayout->addWidget(notebookLabel);
-    nbBtnLayout->addStretch();
-    nbBtnLayout->addWidget(newNotebookBtn);
-    nbBtnLayout->addWidget(deleteNotebookBtn);
-    nbBtnLayout->addWidget(notebookInfoBtn);
-    nbBtnLayout->setContentsMargins(0, 0, 0, 0);
     QVBoxLayout *nbLayout = new QVBoxLayout;
-    nbLayout->addLayout(nbBtnLayout);
+    nbLayout->addWidget(notebookLabel);
     nbLayout->addWidget(notebookSelector);
     nbLayout->addWidget(directoryLabel);
     nbLayout->addWidget(directoryTree);
@@ -117,13 +101,6 @@ QWidget *VMainWindow::setupDirectoryPanel()
     connect(notebookSelector, &VNotebookSelector::curNotebookChanged,
             this, &VMainWindow::handleCurrentNotebookChanged);
 
-    connect(newNotebookBtn, &QPushButton::clicked,
-            notebookSelector, &VNotebookSelector::newNotebook);
-    connect(deleteNotebookBtn, SIGNAL(clicked(bool)),
-            notebookSelector, SLOT(deleteNotebook()));
-    connect(notebookInfoBtn, SIGNAL(clicked(bool)),
-            notebookSelector, SLOT(editNotebookInfo()));
-
     connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
             this, &VMainWindow::handleCurrentDirectoryChanged);
     return nbContainer;

+ 0 - 3
src/vmainwindow.h

@@ -74,9 +74,6 @@ private:
     QLabel *notebookLabel;
     QLabel *directoryLabel;
     VNotebookSelector *notebookSelector;
-    QPushButton *newNotebookBtn;
-    QPushButton *deleteNotebookBtn;
-    QPushButton *notebookInfoBtn;
     VFileList *fileList;
     VDirectoryTree *directoryTree;
     QSplitter *mainSplitter;

+ 17 - 0
src/vnofocusitemdelegate.cpp

@@ -0,0 +1,17 @@
+#include "vnofocusitemdelegate.h"
+
+VNoFocusItemDelegate::VNoFocusItemDelegate(QWidget *parent)
+    : QStyledItemDelegate(parent)
+{
+
+}
+
+void VNoFocusItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+                                 const QModelIndex &index) const
+{
+    QStyleOptionViewItem itemOp(option);
+    if (itemOp.state & QStyle::State_HasFocus) {
+        itemOp.state ^= QStyle::State_HasFocus;
+    }
+    QStyledItemDelegate::paint(painter, itemOp, index);
+}

+ 15 - 0
src/vnofocusitemdelegate.h

@@ -0,0 +1,15 @@
+#ifndef VNOFOCUSITEMDELEGATE_H
+#define VNOFOCUSITEMDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class VNoFocusItemDelegate : public QStyledItemDelegate
+{
+    Q_OBJECT
+public:
+    explicit VNoFocusItemDelegate(QWidget *parent = 0);
+    void paint(QPainter *painter, const QStyleOptionViewItem &option,
+               const QModelIndex &index) const Q_DECL_OVERRIDE;
+};
+
+#endif // VNOFOCUSITEMDELEGATE_H

+ 2 - 1
src/vnotebook.cpp

@@ -59,10 +59,11 @@ void VNotebook::deleteNotebook(VNotebook *p_notebook)
     if (!p_notebook) {
         return;
     }
+    QString path = p_notebook->getPath();
+
     p_notebook->close();
     delete p_notebook;
 
-    QString path = p_notebook->getPath();
     QDir dir(path);
     if (!dir.removeRecursively()) {
         qWarning() << "failed to delete" << path;

+ 190 - 18
src/vnotebookselector.cpp

@@ -1,6 +1,9 @@
 #include "vnotebookselector.h"
 #include <QDebug>
 #include <QJsonObject>
+#include <QListWidget>
+#include <QAction>
+#include <QMenu>
 #include "vnotebook.h"
 #include "vconfigmanager.h"
 #include "dialog/vnewnotebookdialog.h"
@@ -10,15 +13,48 @@
 #include "utils/vutils.h"
 #include "vnote.h"
 #include "veditarea.h"
+#include "vnofocusitemdelegate.h"
 
 extern VConfigManager vconfig;
 
+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_editArea(NULL), m_lastValidIndex(-1)
 {
+    m_listWidget = new QListWidget(this);
+    m_listWidget->setItemDelegate(new VNoFocusItemDelegate(this));
+    m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(m_listWidget, &QListWidget::customContextMenuRequested,
+            this, &VNotebookSelector::requestPopupListContextMenu);
+
+    setModel(m_listWidget->model());
+    setView(m_listWidget);
+
+    m_listWidget->viewport()->installEventFilter(this);
+
+    initActions();
+
     connect(this, SIGNAL(currentIndexChanged(int)),
             this, SLOT(handleCurIndexChanged(int)));
+    connect(this, SIGNAL(activated(int)),
+            this, SLOT(handleItemActivated(int)));
+}
+
+void VNotebookSelector::initActions()
+{
+    m_deleteNotebookAct = new QAction(QIcon(":/resources/icons/delete_notebook.svg"),
+                                      tr("&Delete"), this);
+    m_deleteNotebookAct->setStatusTip(tr("Delete current notebook"));
+    connect(m_deleteNotebookAct, SIGNAL(triggered(bool)),
+            this, SLOT(deleteNotebook()));
+
+    m_notebookInfoAct = new QAction(QIcon(":/resources/icons/notebook_info.svg"),
+                                    tr("&Info"));
+    m_notebookInfoAct->setStatusTip(tr("View and edit current notebook's information"));
+    connect(m_notebookInfoAct, SIGNAL(triggered(bool)),
+            this, SLOT(editNotebookInfo()));
 }
 
 void VNotebookSelector::updateComboBox()
@@ -28,42 +64,99 @@ void VNotebookSelector::updateComboBox()
     disconnect(this, SIGNAL(currentIndexChanged(int)),
                this, SLOT(handleCurIndexChanged(int)));
     clear();
+    m_listWidget->clear();
+
+    insertAddNotebookItem();
+
     for (int i = 0; i < m_notebooks.size(); ++i) {
-        addItem(QIcon(":/resources/icons/notebook_item.svg"), m_notebooks[i]->getName());
+        addNotebookItem(m_notebooks[i]->getName());
     }
     setCurrentIndex(-1);
     connect(this, SIGNAL(currentIndexChanged(int)),
             this, SLOT(handleCurIndexChanged(int)));
 
-    if (m_notebooks.isEmpty() && index != -1) {
-        index = -1;
-        vconfig.setCurNotebookIndex(index);
+    if (m_notebooks.isEmpty()) {
+        vconfig.setCurNotebookIndex(-1);
+        setCurrentIndex(0);
+    } else {
+        setCurrentIndexNotebook(index);
     }
-    setCurrentIndex(index);
     qDebug() << "notebooks" << m_notebooks.size() << "current index" << index;
 }
 
+void VNotebookSelector::setCurrentIndexNotebook(int p_index)
+{
+    if (p_index > -1) {
+        p_index += c_notebookStartIdx;
+    }
+    setCurrentIndex(p_index);
+}
+
+void VNotebookSelector::insertAddNotebookItem()
+{
+    QListWidgetItem *item = new QListWidgetItem();
+    item->setIcon(QIcon(":/resources/icons/create_notebook.svg"));
+    item->setText("Add Notebook");
+    QFont font;
+    font.setItalic(true);
+    item->setData(Qt::FontRole, font);
+    item->setToolTip(tr("Create or import a notebook."));
+    m_listWidget->insertItem(0, item);
+}
+
 void VNotebookSelector::handleCurIndexChanged(int p_index)
 {
-    qDebug() << "current index changed" << p_index;
+    qDebug() << "current index changed" << p_index << "startIdx" << c_notebookStartIdx;
     VNotebook *nb = NULL;
     if (p_index > -1) {
-        nb = m_notebooks[p_index];
+        if (p_index < c_notebookStartIdx) {
+            // Click a special action item.
+            if (m_listWidget->count() == c_notebookStartIdx) {
+                // There is no regular notebook item. Just let it be selected.
+                p_index = -1;
+            } else {
+                // handleItemActivated() will handle the logics.
+                return;
+            }
+        } else {
+            int nbIdx = p_index - c_notebookStartIdx;
+            Q_ASSERT(nbIdx >= 0);
+            nb = m_notebooks[nbIdx];
+        }
+    }
+    m_lastValidIndex = p_index;
+    QString tooltip;
+    if (p_index > -1) {
+        p_index -= c_notebookStartIdx;
+        tooltip = nb->getName();
     }
+    setToolTip(tooltip);
     vconfig.setCurNotebookIndex(p_index);
     emit curNotebookChanged(nb);
 }
 
+void VNotebookSelector::handleItemActivated(int p_index)
+{
+    if (p_index > -1 && p_index < c_notebookStartIdx) {
+        // Click a special action item
+        if (m_lastValidIndex > -1) {
+            setCurrentIndex(m_lastValidIndex);
+        }
+        newNotebook();
+    }
+}
+
 void VNotebookSelector::update()
 {
     updateComboBox();
 }
 
-void VNotebookSelector::newNotebook()
+bool VNotebookSelector::newNotebook()
 {
     QString info;
     QString defaultName("new_notebook");
     QString defaultPath;
+
     do {
         VNewNotebookDialog dialog(tr("Create notebook"), info, defaultName,
                                   defaultPath, this);
@@ -77,9 +170,11 @@ void VNotebookSelector::newNotebook()
                 continue;
             }
             createNotebook(name, path);
+            return true;
         }
         break;
     } while (true);
+    return false;
 }
 
 VNotebook *VNotebookSelector::findNotebook(const QString &p_name)
@@ -98,14 +193,22 @@ void VNotebookSelector::createNotebook(const QString &p_name, const QString &p_p
     m_notebooks.append(nb);
     vconfig.setNotebooks(m_notebooks);
 
-    addItem(QIcon(":/resources/icons/notebook_item.svg"), nb->getName());
-    setCurrentIndex(m_notebooks.size() - 1);
+    addNotebookItem(nb->getName());
+    setCurrentIndexNotebook(m_notebooks.size() - 1);
 }
 
 void VNotebookSelector::deleteNotebook()
 {
-    int curIndex = currentIndex();
-    VNotebook *notebook = m_notebooks[curIndex];
+    QList<QListWidgetItem *> items = m_listWidget->selectedItems();
+    if (items.isEmpty()) {
+        return;
+    }
+    Q_ASSERT(items.size() == 1);
+    QListWidgetItem *item = items[0];
+    int index = indexOfListItem(item);
+
+    VNotebook *notebook = getNotebookFromComboIndex(index);
+    Q_ASSERT(notebook);
     QString curName = notebook->getName();
     QString curPath = notebook->getPath();
 
@@ -126,7 +229,7 @@ void VNotebookSelector::deleteNotebook(VNotebook *p_notebook)
     m_notebooks.remove(idx);
     vconfig.setNotebooks(m_notebooks);
 
-    removeItem(idx);
+    removeNotebookItem(idx);
 
     VNotebook::deleteNotebook(p_notebook);
 }
@@ -141,11 +244,18 @@ int VNotebookSelector::indexOfNotebook(const VNotebook *p_notebook)
     return -1;
 }
 
+
 void VNotebookSelector::editNotebookInfo()
 {
-    int curIndex = currentIndex();
-    Q_ASSERT(curIndex > -1);
-    VNotebook *notebook = m_notebooks[curIndex];
+    QList<QListWidgetItem *> items = m_listWidget->selectedItems();
+    if (items.isEmpty()) {
+        return;
+    }
+    Q_ASSERT(items.size() == 1);
+    QListWidgetItem *item = items[0];
+    int index = indexOfListItem(item);
+
+    VNotebook *notebook = getNotebookFromComboIndex(index);
     QString info;
     QString curName = notebook->getName();
     QString defaultPath = notebook->getPath();
@@ -164,10 +274,72 @@ void VNotebookSelector::editNotebookInfo()
                 continue;
             }
             notebook->rename(name);
-            setItemText(curIndex, name);
+            updateComboBoxItem(index, name);
             vconfig.setNotebooks(m_notebooks);
             emit notebookUpdated(notebook);
         }
         break;
     } while (true);
 }
+
+void VNotebookSelector::addNotebookItem(const QString &p_name)
+{
+    QListWidgetItem *item = new QListWidgetItem(m_listWidget);
+    item->setText(p_name);
+    item->setToolTip(p_name);
+    item->setIcon(QIcon(":/resources/icons/notebook_item.svg"));
+}
+
+void VNotebookSelector::removeNotebookItem(int p_index)
+{
+    QListWidgetItem *item = m_listWidget->item(p_index + c_notebookStartIdx);
+    m_listWidget->removeItemWidget(item);
+    delete item;
+}
+
+void VNotebookSelector::updateComboBoxItem(int p_index, const QString &p_name)
+{
+    QListWidgetItem *item = m_listWidget->item(p_index);
+    item->setText(p_name);
+    item->setToolTip(p_name);
+}
+
+void VNotebookSelector::requestPopupListContextMenu(QPoint p_pos)
+{
+    QModelIndex index = m_listWidget->indexAt(p_pos);
+    if (!index.isValid() || index.row() < c_notebookStartIdx) {
+        return;
+    }
+
+    QListWidgetItem *item = m_listWidget->itemAt(p_pos);
+    Q_ASSERT(item);
+    m_listWidget->clearSelection();
+    item->setSelected(true);
+
+    QMenu menu(this);
+    menu.addAction(m_deleteNotebookAct);
+    menu.addAction(m_notebookInfoAct);
+
+    menu.exec(m_listWidget->mapToGlobal(p_pos));
+}
+
+bool VNotebookSelector::eventFilter(QObject *watched, QEvent *event)
+{
+    if (event->type() == QEvent::MouseButtonRelease) {
+        if (static_cast<QMouseEvent *>(event)->button() == Qt::RightButton) {
+            return true;
+        }
+    }
+    return QComboBox::eventFilter(watched, event);
+}
+
+int VNotebookSelector::indexOfListItem(const QListWidgetItem *p_item)
+{
+    int nrItems = m_listWidget->count();
+    for (int i = 0; i < nrItems; ++i) {
+        if (m_listWidget->item(i) == p_item) {
+            return i;
+        }
+    }
+    return -1;
+}

+ 44 - 1
src/vnotebookselector.h

@@ -8,6 +8,9 @@
 class VNotebook;
 class VNote;
 class VEditArea;
+class QListWidget;
+class QAction;
+class QListWidgetItem;
 
 class VNotebookSelector : public QComboBox
 {
@@ -23,23 +26,51 @@ signals:
     void notebookUpdated(const VNotebook *p_notebook);
 
 public slots:
-    void newNotebook();
+    bool newNotebook();
     void deleteNotebook();
     void editNotebookInfo();
 
+protected:
+    bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
+
 private slots:
     void handleCurIndexChanged(int p_index);
+    void handleItemActivated(int p_index);
+    void requestPopupListContextMenu(QPoint p_pos);
 
 private:
+    void initActions();
     void updateComboBox();
     VNotebook *findNotebook(const QString &p_name);
+    // Return the index of @p_notebook in m_noteboks.
     int indexOfNotebook(const VNotebook *p_notebook);
     void createNotebook(const QString &p_name, const QString &p_path);
     void deleteNotebook(VNotebook *p_notebook);
+    void addNotebookItem(const QString &p_name);
+    // @p_index is the index of m_notebooks, NOT of QComboBox.
+    void removeNotebookItem(int p_index);
+    // @p_index is the index of QComboBox.
+    void updateComboBoxItem(int p_index, const QString &p_name);
+    void insertAddNotebookItem();
+    // @p_index is the index of m_notebooks.
+    void setCurrentIndexNotebook(int p_index);
+    int indexOfListItem(const QListWidgetItem *p_item);
+    // @p_index is the idnex of QComboBox.
+    inline VNotebook *getNotebookFromComboIndex(int p_index);
 
     VNote *m_vnote;
     QVector<VNotebook *> &m_notebooks;
     VEditArea *m_editArea;
+    QListWidget *m_listWidget;
+    int m_lastValidIndex;
+
+    // Actions
+    QAction *m_deleteNotebookAct;
+    QAction *m_notebookInfoAct;
+
+    // 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;
 };
 
 inline void VNotebookSelector::setEditArea(VEditArea *p_editArea)
@@ -47,4 +78,16 @@ inline void VNotebookSelector::setEditArea(VEditArea *p_editArea)
     m_editArea = p_editArea;
 }
 
+inline VNotebook *VNotebookSelector::getNotebookFromComboIndex(int p_index)
+{
+    if (p_index < c_notebookStartIdx) {
+        return NULL;
+    }
+    int nbIdx = p_index - c_notebookStartIdx;
+    if (nbIdx >= m_notebooks.size()) {
+        return NULL;
+    }
+    return m_notebooks[nbIdx];
+}
+
 #endif // VNOTEBOOKSELECTOR_H