diff --git a/src/resources/icons/current_tab.svg b/src/resources/icons/arrow_dropup.svg
similarity index 50%
rename from src/resources/icons/current_tab.svg
rename to src/resources/icons/arrow_dropup.svg
index b51f8169..5cdfca0c 100644
--- a/src/resources/icons/current_tab.svg
+++ b/src/resources/icons/arrow_dropup.svg
@@ -2,7 +2,8 @@
diff --git a/src/resources/vnote.qss b/src/resources/vnote.qss
index 75cb0a16..1a26ad5e 100644
--- a/src/resources/vnote.qss
+++ b/src/resources/vnote.qss
@@ -17,6 +17,22 @@ QPushButton[CornerBtn="true"]::focus {
background-color: @focus-color;
}
+QPushButton[StatusBtn="true"] {
+ font: bold;
+ padding: 0px 2px 0px 2px;
+ margin: 0px;
+ border: none;
+ background-color: transparent;
+}
+
+QPushButton[StatusBtn="true"]::hover {
+ background-color: @hover-color;
+}
+
+QPushButton[StatusBtn="true"]::focus {
+ background-color: @focus-color;
+}
+
QPushButton[FlatBtn="true"] {
padding: 4px;
margin: 0px;
diff --git a/src/src.pro b/src/src.pro
index f1bae6f3..db15b44e 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -66,7 +66,9 @@ SOURCES += main.cpp\
vmdtab.cpp \
vhtmltab.cpp \
utils/vvim.cpp \
- utils/veditutils.cpp
+ utils/veditutils.cpp \
+ vvimindicator.cpp \
+ vbuttonwithwidget.cpp
HEADERS += vmainwindow.h \
vdirectorytree.h \
@@ -119,7 +121,9 @@ HEADERS += vmainwindow.h \
vmdtab.h \
vhtmltab.h \
utils/vvim.h \
- utils/veditutils.h
+ utils/veditutils.h \
+ vvimindicator.h \
+ vbuttonwithwidget.h
RESOURCES += \
vnote.qrc \
diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp
index a9c22998..3a7197ca 100644
--- a/src/utils/vvim.cpp
+++ b/src/utils/vvim.cpp
@@ -290,12 +290,16 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
goto accept;
}
+ m_pendingKeys.append(keyInfo);
+
if (expectingRegisterName()) {
// Expecting a register name.
QChar reg = keyToRegisterName(keyInfo);
if (!reg.isNull()) {
- resetState();
- m_regName = reg;
+ // We should keep m_pendingKeys.
+ m_keys.clear();
+ m_tokens.clear();
+ setRegister(reg);
if (m_registers[reg].isNamedRegister()) {
m_registers[reg].m_append = (modifiers == Qt::ShiftModifier);
} else {
@@ -1281,6 +1285,7 @@ accept:
exit:
m_resetPositionInBlock = resetPositionInBlock;
+ emit vimStatusUpdated(this);
return ret;
}
@@ -1288,7 +1293,8 @@ void VVim::resetState()
{
m_keys.clear();
m_tokens.clear();
- m_regName = c_unnamedRegister;
+ m_pendingKeys.clear();
+ setRegister(c_unnamedRegister);
m_resetPositionInBlock = true;
}
@@ -1305,6 +1311,7 @@ void VVim::setMode(VimMode p_mode)
resetState();
emit modeChanged(m_mode);
+ emit vimStatusUpdated(this);
}
}
@@ -3380,3 +3387,28 @@ void VVim::message(const QString &p_msg)
qDebug() << "vim msg:" << p_msg;
emit vimMessage(p_msg);
}
+
+const QMap &VVim::getRegisters() const
+{
+ return m_registers;
+}
+
+QChar VVim::getCurrentRegisterName() const
+{
+ return m_regName;
+}
+
+QString VVim::getPendingKeys() const
+{
+ QString str;
+ for (auto const & key : m_pendingKeys) {
+ str.append(keyToChar(key.m_key, key.m_modifiers));
+ }
+
+ return str;
+}
+
+void VVim::setRegister(QChar p_reg)
+{
+ m_regName = p_reg;
+}
diff --git a/src/utils/vvim.h b/src/utils/vvim.h
index 51537c0e..0ea24415 100644
--- a/src/utils/vvim.h
+++ b/src/utils/vvim.h
@@ -4,7 +4,7 @@
#include
#include
#include
-#include
+#include
#include
#include "vutils.h"
@@ -28,6 +28,69 @@ class VVim : public QObject
public:
explicit VVim(VEdit *p_editor);
+ struct Register
+ {
+ Register(QChar p_name, const QString &p_value)
+ : m_name(p_name), m_value(p_value), m_append(false)
+ {
+ }
+
+ Register(QChar p_name)
+ : m_name(p_name), m_append(false)
+ {
+ }
+
+ Register()
+ : m_append(false)
+ {
+ }
+
+ // Register a-z.
+ bool isNamedRegister() const
+ {
+ char ch = m_name.toLatin1();
+ return ch >= 'a' && ch <= 'z';
+ }
+
+ bool isUnnamedRegister() const
+ {
+ return m_name == c_unnamedRegister;
+ }
+
+ bool isBlackHoleRegister() const
+ {
+ return m_name == c_blackHoleRegister;
+ }
+
+ bool isSelectionRegister() const
+ {
+ return m_name == c_selectionRegister;
+ }
+
+ bool isBlock() const
+ {
+ return m_value.endsWith('\n');
+ }
+
+ // @p_value is the content to update.
+ // If @p_value ends with \n, then it is a block.
+ // When @p_value is a block, we need to add \n at the end if necessary.
+ // If @m_append is true and @p_value is a block, we need to add \n between
+ // them if necessary.
+ void update(const QString &p_value);
+
+ // Read the value of this register.
+ const QString &read();
+
+ QChar m_name;
+ QString m_value;
+
+ // This is not info of Register itself, but a hint to the handling logics
+ // whether we need to append the content to this register.
+ // Only valid for a-z registers.
+ bool m_append;
+ };
+
// Handle key press event.
// Returns true if the event is consumed and need no more handling.
bool handleKeyPressEvent(QKeyEvent *p_event);
@@ -38,6 +101,18 @@ public:
// Set current mode.
void setMode(VimMode p_mode);
+ // Set current register.
+ void setRegister(QChar p_reg);
+
+ // Get m_registers.
+ const QMap &getRegisters() const;
+
+ QChar getCurrentRegisterName() const;
+
+ // Get pending keys.
+ // Turn m_pendingKeys to a string.
+ QString getPendingKeys() const;
+
signals:
// Emit when current mode has been changed.
void modeChanged(VimMode p_mode);
@@ -45,6 +120,9 @@ signals:
// Emit when VVim want to display some messages.
void vimMessage(const QString &p_msg);
+ // Emit when current status updated.
+ void vimStatusUpdated(const VVim *p_vim);
+
private slots:
// When user use mouse to select texts in Normal mode, we should change to
// Visual mode.
@@ -265,69 +343,6 @@ private:
Key m_key;
};
- struct Register
- {
- Register(QChar p_name, const QString &p_value)
- : m_name(p_name), m_value(p_value), m_append(false)
- {
- }
-
- Register(QChar p_name)
- : m_name(p_name), m_append(false)
- {
- }
-
- Register()
- : m_append(false)
- {
- }
-
- // Register a-z.
- bool isNamedRegister() const
- {
- char ch = m_name.toLatin1();
- return ch >= 'a' && ch <= 'z';
- }
-
- bool isUnnamedRegister() const
- {
- return m_name == c_unnamedRegister;
- }
-
- bool isBlackHoleRegister() const
- {
- return m_name == c_blackHoleRegister;
- }
-
- bool isSelectionRegister() const
- {
- return m_name == c_selectionRegister;
- }
-
- bool isBlock() const
- {
- return m_value.endsWith('\n');
- }
-
- // @p_value is the content to update.
- // If @p_value ends with \n, then it is a block.
- // When @p_value is a block, we need to add \n at the end if necessary.
- // If @m_append is true and @p_value is a block, we need to add \n between
- // them if necessary.
- void update(const QString &p_value);
-
- // Read the value of this register.
- const QString &read();
-
- QChar m_name;
- QString m_value;
-
- // This is not info of Register itself, but a hint to the handling logics
- // whether we need to append the content to this register.
- // Only valid for a-z registers.
- bool m_append;
- };
-
// Reset all key state info.
void resetState();
@@ -468,10 +483,13 @@ private:
QList m_keys;
QList m_tokens;
+ // Keys for status indication.
+ QList m_pendingKeys;
+
// Whether reset the position in block when moving cursor.
bool m_resetPositionInBlock;
- QHash m_registers;
+ QMap m_registers;
// Currently used register.
QChar m_regName;
diff --git a/src/vbuttonwithwidget.cpp b/src/vbuttonwithwidget.cpp
new file mode 100644
index 00000000..30dfe558
--- /dev/null
+++ b/src/vbuttonwithwidget.cpp
@@ -0,0 +1,98 @@
+#include "vbuttonwithwidget.h"
+
+#include
+#include
+#include
+
+VButtonWithWidget::VButtonWithWidget(QWidget *p_parent)
+ : QPushButton(p_parent), m_popupWidget(NULL)
+{
+ init();
+}
+
+VButtonWithWidget::VButtonWithWidget(const QString &p_text, QWidget *p_parent)
+ : QPushButton(p_text, p_parent), m_popupWidget(NULL)
+{
+ init();
+}
+
+VButtonWithWidget::VButtonWithWidget(const QIcon &p_icon,
+ const QString &p_text,
+ QWidget *p_parent)
+ : QPushButton(p_icon, p_text, p_parent), m_popupWidget(NULL)
+{
+ init();
+}
+
+VButtonWithWidget::~VButtonWithWidget()
+{
+ if (m_popupWidget) {
+ delete m_popupWidget;
+ }
+}
+
+void VButtonWithWidget::init()
+{
+ connect(this, &QPushButton::clicked,
+ this, &VButtonWithWidget::showPopupWidget);
+}
+
+void VButtonWithWidget::setPopupWidget(QWidget *p_widget)
+{
+ if (m_popupWidget) {
+ delete m_popupWidget;
+ }
+
+ m_popupWidget = p_widget;
+ if (m_popupWidget) {
+ m_popupWidget->hide();
+ m_popupWidget->setParent(NULL);
+
+ Qt::WindowFlags flags = Qt::Popup;
+ m_popupWidget->setWindowFlags(flags);
+ m_popupWidget->setWindowModality(Qt::NonModal);
+
+ // Let popup widget to hide itself if focus lost.
+ m_popupWidget->installEventFilter(this);
+ }
+}
+
+QWidget *VButtonWithWidget::getPopupWidget() const
+{
+ return m_popupWidget;
+}
+
+void VButtonWithWidget::showPopupWidget()
+{
+ if (m_popupWidget->isVisible()) {
+ m_popupWidget->hide();
+ } else {
+ emit popupWidgetAboutToShow(m_popupWidget);
+
+ // Calculate the position of the popup widget.
+ QPoint btnPos = mapToGlobal(QPoint(0, 0));
+ int btnWidth = width();
+
+ int popupWidth = btnWidth * 10;
+ int popupHeight = height() * 10;
+ int popupX = btnPos.x() + btnWidth - popupWidth;
+ int popupY = btnPos.y() - popupHeight - 10;
+
+ m_popupWidget->setGeometry(popupX, popupY, popupWidth, popupHeight);
+ m_popupWidget->show();
+ }
+}
+
+bool VButtonWithWidget::eventFilter(QObject *p_obj, QEvent *p_event)
+{
+ if (p_event->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent *eve = dynamic_cast(p_event);
+ QPoint clickPos = eve->pos();
+ const QRect &rect = m_popupWidget->rect();
+ if (!rect.contains(clickPos)) {
+ m_popupWidget->hide();
+ }
+ }
+
+ return QPushButton::eventFilter(p_obj, p_event);
+}
diff --git a/src/vbuttonwithwidget.h b/src/vbuttonwithwidget.h
new file mode 100644
index 00000000..bd43b043
--- /dev/null
+++ b/src/vbuttonwithwidget.h
@@ -0,0 +1,42 @@
+#ifndef VBUTTONWITHWIDGET_H
+#define VBUTTONWITHWIDGET_H
+
+#include
+#include
+#include
+
+// A QPushButton with popup widget.
+class VButtonWithWidget : public QPushButton
+{
+ Q_OBJECT
+public:
+ VButtonWithWidget(QWidget *p_parent = Q_NULLPTR);
+ VButtonWithWidget(const QString &p_text, QWidget *p_parent = Q_NULLPTR);
+ VButtonWithWidget(const QIcon &p_icon,
+ const QString &p_text,
+ QWidget *p_parent = Q_NULLPTR);
+ ~VButtonWithWidget();
+
+ // Set the widget which will transfer the ownership to VButtonWithWidget.
+ void setPopupWidget(QWidget *p_widget);
+
+ QWidget *getPopupWidget() const;
+
+signals:
+ // Emit when popup widget is about to show.
+ void popupWidgetAboutToShow(QWidget *p_widget);
+
+protected:
+ bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE;
+
+private slots:
+ // Show the popup widget.
+ void showPopupWidget();
+
+private:
+ void init();
+
+ QWidget *m_popupWidget;
+};
+
+#endif // VBUTTONWITHWIDGET_H
diff --git a/src/vedit.cpp b/src/vedit.cpp
index e8f9c467..3077666c 100644
--- a/src/vedit.cpp
+++ b/src/vedit.cpp
@@ -781,3 +781,12 @@ void VEdit::mouseMoveEvent(QMouseEvent *p_event)
QTextEdit::mouseMoveEvent(p_event);
}
+
+void VEdit::requestUpdateVimStatus()
+{
+ if (m_editOps) {
+ m_editOps->requestUpdateVimStatus();
+ } else {
+ emit vimStatusUpdated(NULL);
+ }
+}
diff --git a/src/vedit.h b/src/vedit.h
index b6b8f333..d0d725cd 100644
--- a/src/vedit.h
+++ b/src/vedit.h
@@ -15,6 +15,7 @@
class VEditOperations;
class QLabel;
class QTimer;
+class VVim;
enum class SelectionId {
CurrentLine = 0,
@@ -81,6 +82,9 @@ public:
VEditConfig &getConfig();
+ // Request to update Vim status.
+ void requestUpdateVimStatus();
+
signals:
void saveAndRead();
void discardAndRead();
@@ -92,6 +96,9 @@ signals:
// Emit when want to show message in status bar.
void statusMessage(const QString &p_msg);
+ // Emit when Vim status updated.
+ void vimStatusUpdated(const VVim *p_vim);
+
public slots:
virtual void highlightCurrentLine();
diff --git a/src/veditarea.cpp b/src/veditarea.cpp
index 3dd432af..ec4518b5 100644
--- a/src/veditarea.cpp
+++ b/src/veditarea.cpp
@@ -81,6 +81,8 @@ void VEditArea::insertSplitWindow(int idx)
this, &VEditArea::handleCurHeaderChanged);
connect(win, &VEditWindow::statusMessage,
this, &VEditArea::handleWindowStatusMessage);
+ connect(win, &VEditWindow::vimStatusUpdated,
+ this, &VEditArea::handleWindowVimStatusUpdated);
}
void VEditArea::handleEditWindowStatusChanged(const VFile *p_file,
@@ -99,6 +101,13 @@ void VEditArea::handleWindowStatusMessage(const QString &p_msg)
}
}
+void VEditArea::handleWindowVimStatusUpdated(const VVim *p_vim)
+{
+ if (splitter->widget(curWindowIndex) == sender()) {
+ emit vimStatusUpdated(p_vim);
+ }
+}
+
void VEditArea::removeSplitWindow(VEditWindow *win)
{
if (!win) {
diff --git a/src/veditarea.h b/src/veditarea.h
index ffdedfd6..4a94f581 100644
--- a/src/veditarea.h
+++ b/src/veditarea.h
@@ -20,6 +20,7 @@ class VFile;
class VDirectory;
class VFindReplaceDialog;
class QLabel;
+class VVim;
class VEditArea : public QWidget, public VNavigationMode
{
@@ -66,6 +67,9 @@ signals:
// Emit when want to show message in status bar.
void statusMessage(const QString &p_msg);
+ // Emit when Vim status updated.
+ void vimStatusUpdated(const VVim *p_vim);
+
protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
@@ -101,6 +105,9 @@ private slots:
// Handle the statusMessage signal of VEditWindow.
void handleWindowStatusMessage(const QString &p_msg);
+ // Handle the vimStatusUpdated signal of VEditWindow.
+ void handleWindowVimStatusUpdated(const VVim *p_vim);
+
private:
void setupUI();
QVector > findTabsByFile(const VFile *p_file);
diff --git a/src/veditoperations.cpp b/src/veditoperations.cpp
index 0dc2a047..c5aa40b7 100644
--- a/src/veditoperations.cpp
+++ b/src/veditoperations.cpp
@@ -20,6 +20,8 @@ VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
this, &VEditOperations::handleVimModeChanged);
connect(m_vim, &VVim::vimMessage,
this, &VEditOperations::statusMessage);
+ connect(m_vim, &VVim::vimStatusUpdated,
+ this, &VEditOperations::vimStatusUpdated);
}
void VEditOperations::insertTextAtCurPos(const QString &p_text)
@@ -80,3 +82,12 @@ void VEditOperations::handleVimModeChanged(VimMode p_mode)
updateCursorLineBg();
}
+
+void VEditOperations::requestUpdateVimStatus()
+{
+ if (m_editConfig->m_enableVimMode) {
+ emit vimStatusUpdated(m_vim);
+ } else {
+ emit vimStatusUpdated(NULL);
+ }
+}
diff --git a/src/veditoperations.h b/src/veditoperations.h
index f01804fb..f349e5f0 100644
--- a/src/veditoperations.h
+++ b/src/veditoperations.h
@@ -27,10 +27,16 @@ public:
// processed.
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
+ // Request to propogate Vim status.
+ void requestUpdateVimStatus();
+
signals:
// Want to display a template message in status bar.
void statusMessage(const QString &p_msg);
+ // Propogate Vim status.
+ void vimStatusUpdated(const VVim *p_vim);
+
protected slots:
// Handle the update of VEditConfig of the editor.
virtual void handleEditConfigUpdated();
diff --git a/src/vedittab.h b/src/vedittab.h
index 9ab34f93..98af3a1e 100644
--- a/src/vedittab.h
+++ b/src/vedittab.h
@@ -6,6 +6,7 @@
#include
#include "vtoc.h"
#include "vfile.h"
+#include "utils/vvim.h"
class VEditArea;
@@ -63,6 +64,9 @@ public:
virtual void clearSearchedWordHighlight() = 0;
+ // Request current tab to propogate its status about Vim.
+ virtual void requestUpdateVimStatus() = 0;
+
public slots:
// Enter edit mode
virtual void editFile() = 0;
@@ -96,6 +100,8 @@ signals:
// Emit when want to show message in status bar.
void statusMessage(const QString &p_msg);
+ void vimStatusUpdated(const VVim *p_vim);
+
private slots:
// Called when app focus changed.
void handleFocusChanged(QWidget *p_old, QWidget *p_now);
diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp
index d8f8ac41..cad6a545 100644
--- a/src/veditwindow.cpp
+++ b/src/veditwindow.cpp
@@ -270,6 +270,8 @@ int VEditWindow::openFileInTab(VFile *p_file, OpenFileMode p_mode)
this, &VEditWindow::handleTabStatusChanged);
connect(editor, &VEditTab::statusMessage,
this, &VEditWindow::handleTabStatusMessage);
+ connect(editor, &VEditTab::vimStatusUpdated,
+ this, &VEditWindow::handleTabVimStatusUpdated);
int idx = appendEditTab(p_file, editor);
return idx;
@@ -586,6 +588,14 @@ void VEditWindow::handleTabStatusMessage(const QString &p_msg)
}
}
+void VEditWindow::handleTabVimStatusUpdated(const VVim *p_vim)
+{
+ int idx = indexOf(dynamic_cast(sender()));
+ if (idx == currentIndex()) {
+ emit vimStatusUpdated(p_vim);
+ }
+}
+
void VEditWindow::updateFileInfo(const VFile *p_file)
{
if (!p_file) {
diff --git a/src/veditwindow.h b/src/veditwindow.h
index db4ac331..91cb7b8f 100644
--- a/src/veditwindow.h
+++ b/src/veditwindow.h
@@ -71,6 +71,9 @@ signals:
// Emit when want to show message in status bar.
void statusMessage(const QString &p_msg);
+ // Emit when Vim mode status changed.
+ void vimStatusUpdated(const VVim *p_vim);
+
private slots:
bool handleTabCloseRequest(int index);
void splitWindow();
@@ -91,6 +94,9 @@ private slots:
// Handle the statusMessage signal of VEditTab.
void handleTabStatusMessage(const QString &p_msg);
+ // Handle the vimStatusUpdated() signal of VEditTab.
+ void handleTabVimStatusUpdated(const VVim *p_vim);
+
private:
void initTabActions();
void setupCornerWidget();
diff --git a/src/vhtmltab.cpp b/src/vhtmltab.cpp
index 797dcfac..c8396adf 100644
--- a/src/vhtmltab.cpp
+++ b/src/vhtmltab.cpp
@@ -41,6 +41,9 @@ void VHtmlTab::setupUI()
this, &VHtmlTab::editFile);
connect(m_editor, &VEdit::statusMessage,
this, &VEditTab::statusMessage);
+ connect(m_editor, &VEdit::vimStatusUpdated,
+ this, &VEditTab::vimStatusUpdated);
+
m_editor->reloadFile();
QVBoxLayout *mainLayout = new QVBoxLayout();
@@ -250,3 +253,8 @@ void VHtmlTab::focusChild()
{
m_editor->setFocus();
}
+
+void VHtmlTab::requestUpdateVimStatus()
+{
+ m_editor->requestUpdateVimStatus();
+}
diff --git a/src/vhtmltab.h b/src/vhtmltab.h
index 3e08b53c..ea0a237f 100644
--- a/src/vhtmltab.h
+++ b/src/vhtmltab.h
@@ -46,6 +46,8 @@ public:
void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
+ void requestUpdateVimStatus() Q_DECL_OVERRIDE;
+
public slots:
// Enter edit mode.
void editFile() Q_DECL_OVERRIDE;
diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp
index a01dd1a1..052b9739 100644
--- a/src/vmainwindow.cpp
+++ b/src/vmainwindow.cpp
@@ -20,6 +20,7 @@
#include "vwebview.h"
#include "vexporter.h"
#include "vmdtab.h"
+#include "vvimindicator.h"
extern VConfigManager vconfig;
@@ -111,12 +112,17 @@ void VMainWindow::setupUI()
this, &VMainWindow::handleCurTabStatusChanged);
connect(editArea, &VEditArea::statusMessage,
this, &VMainWindow::showStatusMessage);
+ connect(editArea, &VEditArea::vimStatusUpdated,
+ this, &VMainWindow::handleVimStatusUpdated);
connect(m_findReplaceDialog, &VFindReplaceDialog::findTextChanged,
this, &VMainWindow::handleFindDialogTextChanged);
setCentralWidget(mainSplitter);
+
// Create and show the status bar
- statusBar();
+ m_vimIndicator = new VVimIndicator(this);
+ m_vimIndicator->hide();
+ statusBar()->addPermanentWidget(m_vimIndicator);
}
QWidget *VMainWindow::setupDirectoryPanel()
@@ -1121,9 +1127,12 @@ void VMainWindow::handleCurTabStatusChanged(const VFile *p_file, const VEditTab
title.append('#');
}
}
+
updateWindowTitle(title);
m_curFile = const_cast(p_file);
m_curTab = const_cast(p_editTab);
+
+ updateStatusInfo(p_editMode);
}
void VMainWindow::onePanelView()
@@ -1496,3 +1505,22 @@ void VMainWindow::showStatusMessage(const QString &p_msg)
const int timeout = 3000;
statusBar()->showMessage(p_msg, timeout);
}
+
+void VMainWindow::updateStatusInfo(bool p_editMode)
+{
+ if (!p_editMode || !m_curTab) {
+ m_vimIndicator->hide();
+ } else {
+ m_curTab->requestUpdateVimStatus();
+ }
+}
+
+void VMainWindow::handleVimStatusUpdated(const VVim *p_vim)
+{
+ if (!p_vim || !m_curTab) {
+ m_vimIndicator->hide();
+ } else {
+ m_vimIndicator->update(p_vim);
+ m_vimIndicator->show();
+ }
+}
diff --git a/src/vmainwindow.h b/src/vmainwindow.h
index 1e0c68bb..332a0b51 100644
--- a/src/vmainwindow.h
+++ b/src/vmainwindow.h
@@ -29,6 +29,7 @@ class VNotebookSelector;
class VAvatar;
class VFindReplaceDialog;
class VCaptain;
+class VVimIndicator;
class VMainWindow : public QMainWindow
{
@@ -88,6 +89,9 @@ private slots:
// Show a temporary message in status bar.
void showStatusMessage(const QString &p_msg);
+ // Handle Vim status updated.
+ void handleVimStatusUpdated(const VVim *p_vim);
+
protected:
void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
@@ -127,6 +131,9 @@ private:
void toggleOnePanelView();
void closeCurrentFile();
+ // Update status bar information according to m_curTab and m_curFile.
+ void updateStatusInfo(bool p_editMode);
+
// Wrapper to create a QAction.
QAction *newAction(const QIcon &p_icon,
const QString &p_text,
@@ -150,6 +157,7 @@ private:
VOutline *outline;
VAvatar *m_avatar;
VFindReplaceDialog *m_findReplaceDialog;
+ VVimIndicator *m_vimIndicator;
// Whether it is one panel or two panles.
bool m_onePanel;
diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp
index 97c67360..42f5c23b 100644
--- a/src/vmdedit.cpp
+++ b/src/vmdedit.cpp
@@ -53,6 +53,8 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
connect(m_editOps, &VEditOperations::statusMessage,
this, &VEdit::statusMessage);
+ connect(m_editOps, &VEditOperations::vimStatusUpdated,
+ this, &VEdit::vimStatusUpdated);
connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::updateCurHeader);
diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp
index 5c24b6ef..adb09744 100644
--- a/src/vmdtab.cpp
+++ b/src/vmdtab.cpp
@@ -60,6 +60,8 @@ void VMdTab::setupUI()
this, &VMdTab::discardAndRead);
connect(m_editor, &VEdit::statusMessage,
this, &VEditTab::statusMessage);
+ connect(m_editor, &VEdit::vimStatusUpdated,
+ this, &VEditTab::vimStatusUpdated);
m_editor->reloadFile();
m_stacks->addWidget(m_editor);
@@ -643,3 +645,12 @@ void VMdTab::focusChild()
{
m_stacks->currentWidget()->setFocus();
}
+
+void VMdTab::requestUpdateVimStatus()
+{
+ if (m_editor) {
+ m_editor->requestUpdateVimStatus();
+ } else {
+ emit vimStatusUpdated(NULL);
+ }
+}
diff --git a/src/vmdtab.h b/src/vmdtab.h
index ec1e7d6a..b750f338 100644
--- a/src/vmdtab.h
+++ b/src/vmdtab.h
@@ -55,6 +55,8 @@ public:
MarkdownConverterType getMarkdownConverterType() const;
+ void requestUpdateVimStatus() Q_DECL_OVERRIDE;
+
public slots:
// Enter edit mode.
void editFile() Q_DECL_OVERRIDE;
diff --git a/src/vnote.qrc b/src/vnote.qrc
index 10147262..fd8a8357 100644
--- a/src/vnote.qrc
+++ b/src/vnote.qrc
@@ -66,7 +66,6 @@
resources/icons/dir_item.svg
resources/icons/notebook_item.svg
resources/icons/arrow_dropdown.svg
- resources/icons/current_tab.svg
resources/icons/vnote.png
resources/icons/insert_image.svg
resources/icons/import_note.svg
@@ -108,5 +107,6 @@
utils/markdown-it/markdown-it-sub.min.js
utils/markdown-it/markdown-it-sup.min.js
utils/markdown-it/markdown-it-footnote.min.js
+ resources/icons/arrow_dropup.svg
diff --git a/src/vvimindicator.cpp b/src/vvimindicator.cpp
new file mode 100644
index 00000000..0bcd8c3d
--- /dev/null
+++ b/src/vvimindicator.cpp
@@ -0,0 +1,174 @@
+#include "vvimindicator.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "vconfigmanager.h"
+#include "vbuttonwithwidget.h"
+
+extern VConfigManager vconfig;
+
+VVimIndicator::VVimIndicator(QWidget *parent)
+ : QWidget(parent), m_vim(NULL)
+{
+ setupUI();
+}
+
+void VVimIndicator::setupUI()
+{
+ m_modeLabel = new QLabel(this);
+
+ m_regBtn = new VButtonWithWidget(QIcon(":/resources/icons/arrow_dropup.svg"),
+ "\"",
+ this);
+ m_regBtn->setProperty("StatusBtn", true);
+ m_regBtn->setFocusPolicy(Qt::NoFocus);
+ QTreeWidget *regTree = new QTreeWidget(this);
+ regTree->setColumnCount(2);
+ regTree->header()->setStretchLastSection(true);
+ regTree->header()->setProperty("RegisterTree", true);
+ QStringList headers;
+ headers << tr("Register") << tr("Value");
+ regTree->setHeaderLabels(headers);
+ m_regBtn->setPopupWidget(regTree);
+ connect(m_regBtn, &VButtonWithWidget::popupWidgetAboutToShow,
+ this, &VVimIndicator::updateRegistersTree);
+
+ m_keyLabel = new QLabel(this);
+ QFontMetrics metric(font());
+ m_keyLabel->setMinimumWidth(metric.width('A') * 5);
+
+ QHBoxLayout *mainLayout = new QHBoxLayout(this);
+ mainLayout->addWidget(m_modeLabel);
+ mainLayout->addWidget(m_regBtn);
+ mainLayout->addWidget(m_keyLabel);
+ mainLayout->setContentsMargins(0, 0, 0, 0);
+
+ setLayout(mainLayout);
+}
+
+QString VVimIndicator::modeToString(VimMode p_mode) const
+{
+ QString str;
+
+ switch (p_mode) {
+ case VimMode::Normal:
+ str = tr("Normal");
+ break;
+
+ case VimMode::Insert:
+ str = tr("Insert");
+ break;
+
+ case VimMode::Visual:
+ str = tr("Visual");
+ break;
+
+ case VimMode::VisualLine:
+ str = tr("VisualLine");
+ break;
+
+ case VimMode::Replace:
+ str = tr("Replace");
+ break;
+
+ default:
+ str = tr("Unknown");
+ break;
+ }
+
+ return str;
+}
+
+static QString modeBackgroundColor(VimMode p_mode)
+{
+ QString color;
+
+ switch (p_mode) {
+ case VimMode::Normal:
+ color = vconfig.getEditorVimNormalBg();
+ break;
+
+ case VimMode::Insert:
+ color = vconfig.getEditorVimInsertBg();
+ break;
+
+ case VimMode::Visual:
+ color = vconfig.getEditorVimVisualBg();
+ break;
+
+ case VimMode::VisualLine:
+ color = vconfig.getEditorVimVisualBg();
+ break;
+
+ case VimMode::Replace:
+ color = vconfig.getEditorVimReplaceBg();
+ break;
+
+ default:
+ color = "red";
+ break;
+ }
+
+ return color;
+}
+
+static void fillTreeItemsWithRegisters(QTreeWidget *p_tree,
+ const QMap &p_regs)
+{
+ p_tree->clear();
+ for (auto const ® : p_regs) {
+ if (reg.m_value.isEmpty()) {
+ continue;
+ }
+
+ QStringList itemStr;
+ itemStr << reg.m_name << reg.m_value;
+ QTreeWidgetItem *item = new QTreeWidgetItem(p_tree, itemStr);
+ item->setFlags(item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ }
+
+ p_tree->resizeColumnToContents(0);
+ p_tree->resizeColumnToContents(1);
+}
+
+void VVimIndicator::update(const VVim *p_vim)
+{
+ if (!p_vim) {
+ m_vim = p_vim;
+ return;
+ }
+
+ m_vim = p_vim;
+
+ VimMode mode = p_vim->getMode();
+ QChar curRegName = p_vim->getCurrentRegisterName();
+
+ QString style = QString("QLabel { padding: 0px 2px 0px 2px; font: bold; background-color: %1; }")
+ .arg(modeBackgroundColor(mode));
+ m_modeLabel->setStyleSheet(style);
+ m_modeLabel->setText(modeToString(mode));
+
+ m_regBtn->setText(curRegName);
+
+ QString keyText = QString("%1")
+ .arg(p_vim->getPendingKeys());
+ m_keyLabel->setText(keyText);
+}
+
+void VVimIndicator::updateRegistersTree(QWidget *p_widget)
+{
+ QTreeWidget *regTree = dynamic_cast(p_widget);
+ if (!m_vim) {
+ regTree->clear();
+ return;
+ }
+
+ const QMap ®s = m_vim->getRegisters();
+ fillTreeItemsWithRegisters(regTree, regs);
+}
diff --git a/src/vvimindicator.h b/src/vvimindicator.h
new file mode 100644
index 00000000..5930ae66
--- /dev/null
+++ b/src/vvimindicator.h
@@ -0,0 +1,40 @@
+#ifndef VVIMINDICATOR_H
+#define VVIMINDICATOR_H
+
+#include
+#include "utils/vvim.h"
+
+class QLabel;
+class VButtonWithWidget;
+
+class VVimIndicator : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit VVimIndicator(QWidget *parent = 0);
+
+public slots:
+ void update(const VVim *p_vim);
+
+private slots:
+ void updateRegistersTree(QWidget *p_widget);
+
+private:
+ void setupUI();
+
+ QString modeToString(VimMode p_mode) const;
+
+ // Indicate the mode.
+ QLabel *m_modeLabel;
+
+ // Indicate the registers.
+ VButtonWithWidget *m_regBtn;
+
+ // Indicate the pending keys.
+ QLabel *m_keyLabel;
+
+ const VVim *m_vim;
+};
+
+#endif // VVIMINDICATOR_H