vim-mode: add an indicator for Vim status in status bar

This commit is contained in:
Le Tan 2017-06-18 15:03:31 +08:00
parent ffce4b9611
commit d909091f46
26 changed files with 632 additions and 75 deletions

View File

@ -2,7 +2,8 @@
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path d="M184.7,413.1l2.1-1.8l156.5-136c5.3-4.6,8.6-11.5,8.6-19.2c0-7.7-3.4-14.6-8.6-19.2L187.1,101l-2.6-2.3 <g>
C182,97,179,96,175.8,96c-8.7,0-15.8,7.4-15.8,16.6h0v286.8h0c0,9.2,7.1,16.6,15.8,16.6C179.1,416,182.2,414.9,184.7,413.1z"/> <polygon points="128,320 256,192 384,320 "/>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 725 B

After

Width:  |  Height:  |  Size: 542 B

View File

@ -17,6 +17,22 @@ QPushButton[CornerBtn="true"]::focus {
background-color: @focus-color; 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"] { QPushButton[FlatBtn="true"] {
padding: 4px; padding: 4px;
margin: 0px; margin: 0px;

View File

@ -66,7 +66,9 @@ SOURCES += main.cpp\
vmdtab.cpp \ vmdtab.cpp \
vhtmltab.cpp \ vhtmltab.cpp \
utils/vvim.cpp \ utils/vvim.cpp \
utils/veditutils.cpp utils/veditutils.cpp \
vvimindicator.cpp \
vbuttonwithwidget.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -119,7 +121,9 @@ HEADERS += vmainwindow.h \
vmdtab.h \ vmdtab.h \
vhtmltab.h \ vhtmltab.h \
utils/vvim.h \ utils/vvim.h \
utils/veditutils.h utils/veditutils.h \
vvimindicator.h \
vbuttonwithwidget.h
RESOURCES += \ RESOURCES += \
vnote.qrc \ vnote.qrc \

View File

@ -290,12 +290,16 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
goto accept; goto accept;
} }
m_pendingKeys.append(keyInfo);
if (expectingRegisterName()) { if (expectingRegisterName()) {
// Expecting a register name. // Expecting a register name.
QChar reg = keyToRegisterName(keyInfo); QChar reg = keyToRegisterName(keyInfo);
if (!reg.isNull()) { if (!reg.isNull()) {
resetState(); // We should keep m_pendingKeys.
m_regName = reg; m_keys.clear();
m_tokens.clear();
setRegister(reg);
if (m_registers[reg].isNamedRegister()) { if (m_registers[reg].isNamedRegister()) {
m_registers[reg].m_append = (modifiers == Qt::ShiftModifier); m_registers[reg].m_append = (modifiers == Qt::ShiftModifier);
} else { } else {
@ -1281,6 +1285,7 @@ accept:
exit: exit:
m_resetPositionInBlock = resetPositionInBlock; m_resetPositionInBlock = resetPositionInBlock;
emit vimStatusUpdated(this);
return ret; return ret;
} }
@ -1288,7 +1293,8 @@ void VVim::resetState()
{ {
m_keys.clear(); m_keys.clear();
m_tokens.clear(); m_tokens.clear();
m_regName = c_unnamedRegister; m_pendingKeys.clear();
setRegister(c_unnamedRegister);
m_resetPositionInBlock = true; m_resetPositionInBlock = true;
} }
@ -1305,6 +1311,7 @@ void VVim::setMode(VimMode p_mode)
resetState(); resetState();
emit modeChanged(m_mode); emit modeChanged(m_mode);
emit vimStatusUpdated(this);
} }
} }
@ -3380,3 +3387,28 @@ void VVim::message(const QString &p_msg)
qDebug() << "vim msg:" << p_msg; qDebug() << "vim msg:" << p_msg;
emit vimMessage(p_msg); emit vimMessage(p_msg);
} }
const QMap<QChar, VVim::Register> &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;
}

View File

@ -4,7 +4,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QTextCursor> #include <QTextCursor>
#include <QHash> #include <QMap>
#include <QDebug> #include <QDebug>
#include "vutils.h" #include "vutils.h"
@ -28,6 +28,69 @@ class VVim : public QObject
public: public:
explicit VVim(VEdit *p_editor); 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. // Handle key press event.
// Returns true if the event is consumed and need no more handling. // Returns true if the event is consumed and need no more handling.
bool handleKeyPressEvent(QKeyEvent *p_event); bool handleKeyPressEvent(QKeyEvent *p_event);
@ -38,6 +101,18 @@ public:
// Set current mode. // Set current mode.
void setMode(VimMode p_mode); void setMode(VimMode p_mode);
// Set current register.
void setRegister(QChar p_reg);
// Get m_registers.
const QMap<QChar, Register> &getRegisters() const;
QChar getCurrentRegisterName() const;
// Get pending keys.
// Turn m_pendingKeys to a string.
QString getPendingKeys() const;
signals: signals:
// Emit when current mode has been changed. // Emit when current mode has been changed.
void modeChanged(VimMode p_mode); void modeChanged(VimMode p_mode);
@ -45,6 +120,9 @@ signals:
// Emit when VVim want to display some messages. // Emit when VVim want to display some messages.
void vimMessage(const QString &p_msg); void vimMessage(const QString &p_msg);
// Emit when current status updated.
void vimStatusUpdated(const VVim *p_vim);
private slots: private slots:
// When user use mouse to select texts in Normal mode, we should change to // When user use mouse to select texts in Normal mode, we should change to
// Visual mode. // Visual mode.
@ -265,69 +343,6 @@ private:
Key m_key; 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. // Reset all key state info.
void resetState(); void resetState();
@ -468,10 +483,13 @@ private:
QList<Key> m_keys; QList<Key> m_keys;
QList<Token> m_tokens; QList<Token> m_tokens;
// Keys for status indication.
QList<Key> m_pendingKeys;
// Whether reset the position in block when moving cursor. // Whether reset the position in block when moving cursor.
bool m_resetPositionInBlock; bool m_resetPositionInBlock;
QHash<QChar, Register> m_registers; QMap<QChar, Register> m_registers;
// Currently used register. // Currently used register.
QChar m_regName; QChar m_regName;

98
src/vbuttonwithwidget.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "vbuttonwithwidget.h"
#include <QEvent>
#include <QMouseEvent>
#include <QRect>
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<QMouseEvent *>(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);
}

42
src/vbuttonwithwidget.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef VBUTTONWITHWIDGET_H
#define VBUTTONWITHWIDGET_H
#include <QPushButton>
#include <QString>
#include <QIcon>
// 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

View File

@ -781,3 +781,12 @@ void VEdit::mouseMoveEvent(QMouseEvent *p_event)
QTextEdit::mouseMoveEvent(p_event); QTextEdit::mouseMoveEvent(p_event);
} }
void VEdit::requestUpdateVimStatus()
{
if (m_editOps) {
m_editOps->requestUpdateVimStatus();
} else {
emit vimStatusUpdated(NULL);
}
}

View File

@ -15,6 +15,7 @@
class VEditOperations; class VEditOperations;
class QLabel; class QLabel;
class QTimer; class QTimer;
class VVim;
enum class SelectionId { enum class SelectionId {
CurrentLine = 0, CurrentLine = 0,
@ -81,6 +82,9 @@ public:
VEditConfig &getConfig(); VEditConfig &getConfig();
// Request to update Vim status.
void requestUpdateVimStatus();
signals: signals:
void saveAndRead(); void saveAndRead();
void discardAndRead(); void discardAndRead();
@ -92,6 +96,9 @@ signals:
// Emit when want to show message in status bar. // Emit when want to show message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);
// Emit when Vim status updated.
void vimStatusUpdated(const VVim *p_vim);
public slots: public slots:
virtual void highlightCurrentLine(); virtual void highlightCurrentLine();

View File

@ -81,6 +81,8 @@ void VEditArea::insertSplitWindow(int idx)
this, &VEditArea::handleCurHeaderChanged); this, &VEditArea::handleCurHeaderChanged);
connect(win, &VEditWindow::statusMessage, connect(win, &VEditWindow::statusMessage,
this, &VEditArea::handleWindowStatusMessage); this, &VEditArea::handleWindowStatusMessage);
connect(win, &VEditWindow::vimStatusUpdated,
this, &VEditArea::handleWindowVimStatusUpdated);
} }
void VEditArea::handleEditWindowStatusChanged(const VFile *p_file, 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) void VEditArea::removeSplitWindow(VEditWindow *win)
{ {
if (!win) { if (!win) {

View File

@ -20,6 +20,7 @@ class VFile;
class VDirectory; class VDirectory;
class VFindReplaceDialog; class VFindReplaceDialog;
class QLabel; class QLabel;
class VVim;
class VEditArea : public QWidget, public VNavigationMode class VEditArea : public QWidget, public VNavigationMode
{ {
@ -66,6 +67,9 @@ signals:
// Emit when want to show message in status bar. // Emit when want to show message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);
// Emit when Vim status updated.
void vimStatusUpdated(const VVim *p_vim);
protected: protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
@ -101,6 +105,9 @@ private slots:
// Handle the statusMessage signal of VEditWindow. // Handle the statusMessage signal of VEditWindow.
void handleWindowStatusMessage(const QString &p_msg); void handleWindowStatusMessage(const QString &p_msg);
// Handle the vimStatusUpdated signal of VEditWindow.
void handleWindowVimStatusUpdated(const VVim *p_vim);
private: private:
void setupUI(); void setupUI();
QVector<QPair<int, int> > findTabsByFile(const VFile *p_file); QVector<QPair<int, int> > findTabsByFile(const VFile *p_file);

View File

@ -20,6 +20,8 @@ VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
this, &VEditOperations::handleVimModeChanged); this, &VEditOperations::handleVimModeChanged);
connect(m_vim, &VVim::vimMessage, connect(m_vim, &VVim::vimMessage,
this, &VEditOperations::statusMessage); this, &VEditOperations::statusMessage);
connect(m_vim, &VVim::vimStatusUpdated,
this, &VEditOperations::vimStatusUpdated);
} }
void VEditOperations::insertTextAtCurPos(const QString &p_text) void VEditOperations::insertTextAtCurPos(const QString &p_text)
@ -80,3 +82,12 @@ void VEditOperations::handleVimModeChanged(VimMode p_mode)
updateCursorLineBg(); updateCursorLineBg();
} }
void VEditOperations::requestUpdateVimStatus()
{
if (m_editConfig->m_enableVimMode) {
emit vimStatusUpdated(m_vim);
} else {
emit vimStatusUpdated(NULL);
}
}

View File

@ -27,10 +27,16 @@ public:
// processed. // processed.
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0; virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
// Request to propogate Vim status.
void requestUpdateVimStatus();
signals: signals:
// Want to display a template message in status bar. // Want to display a template message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);
// Propogate Vim status.
void vimStatusUpdated(const VVim *p_vim);
protected slots: protected slots:
// Handle the update of VEditConfig of the editor. // Handle the update of VEditConfig of the editor.
virtual void handleEditConfigUpdated(); virtual void handleEditConfigUpdated();

View File

@ -6,6 +6,7 @@
#include <QPointer> #include <QPointer>
#include "vtoc.h" #include "vtoc.h"
#include "vfile.h" #include "vfile.h"
#include "utils/vvim.h"
class VEditArea; class VEditArea;
@ -63,6 +64,9 @@ public:
virtual void clearSearchedWordHighlight() = 0; virtual void clearSearchedWordHighlight() = 0;
// Request current tab to propogate its status about Vim.
virtual void requestUpdateVimStatus() = 0;
public slots: public slots:
// Enter edit mode // Enter edit mode
virtual void editFile() = 0; virtual void editFile() = 0;
@ -96,6 +100,8 @@ signals:
// Emit when want to show message in status bar. // Emit when want to show message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);
void vimStatusUpdated(const VVim *p_vim);
private slots: private slots:
// Called when app focus changed. // Called when app focus changed.
void handleFocusChanged(QWidget *p_old, QWidget *p_now); void handleFocusChanged(QWidget *p_old, QWidget *p_now);

View File

@ -270,6 +270,8 @@ int VEditWindow::openFileInTab(VFile *p_file, OpenFileMode p_mode)
this, &VEditWindow::handleTabStatusChanged); this, &VEditWindow::handleTabStatusChanged);
connect(editor, &VEditTab::statusMessage, connect(editor, &VEditTab::statusMessage,
this, &VEditWindow::handleTabStatusMessage); this, &VEditWindow::handleTabStatusMessage);
connect(editor, &VEditTab::vimStatusUpdated,
this, &VEditWindow::handleTabVimStatusUpdated);
int idx = appendEditTab(p_file, editor); int idx = appendEditTab(p_file, editor);
return idx; 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<QWidget *>(sender()));
if (idx == currentIndex()) {
emit vimStatusUpdated(p_vim);
}
}
void VEditWindow::updateFileInfo(const VFile *p_file) void VEditWindow::updateFileInfo(const VFile *p_file)
{ {
if (!p_file) { if (!p_file) {

View File

@ -71,6 +71,9 @@ signals:
// Emit when want to show message in status bar. // Emit when want to show message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);
// Emit when Vim mode status changed.
void vimStatusUpdated(const VVim *p_vim);
private slots: private slots:
bool handleTabCloseRequest(int index); bool handleTabCloseRequest(int index);
void splitWindow(); void splitWindow();
@ -91,6 +94,9 @@ private slots:
// Handle the statusMessage signal of VEditTab. // Handle the statusMessage signal of VEditTab.
void handleTabStatusMessage(const QString &p_msg); void handleTabStatusMessage(const QString &p_msg);
// Handle the vimStatusUpdated() signal of VEditTab.
void handleTabVimStatusUpdated(const VVim *p_vim);
private: private:
void initTabActions(); void initTabActions();
void setupCornerWidget(); void setupCornerWidget();

View File

@ -41,6 +41,9 @@ void VHtmlTab::setupUI()
this, &VHtmlTab::editFile); this, &VHtmlTab::editFile);
connect(m_editor, &VEdit::statusMessage, connect(m_editor, &VEdit::statusMessage,
this, &VEditTab::statusMessage); this, &VEditTab::statusMessage);
connect(m_editor, &VEdit::vimStatusUpdated,
this, &VEditTab::vimStatusUpdated);
m_editor->reloadFile(); m_editor->reloadFile();
QVBoxLayout *mainLayout = new QVBoxLayout(); QVBoxLayout *mainLayout = new QVBoxLayout();
@ -250,3 +253,8 @@ void VHtmlTab::focusChild()
{ {
m_editor->setFocus(); m_editor->setFocus();
} }
void VHtmlTab::requestUpdateVimStatus()
{
m_editor->requestUpdateVimStatus();
}

View File

@ -46,6 +46,8 @@ public:
void clearSearchedWordHighlight() Q_DECL_OVERRIDE; void clearSearchedWordHighlight() Q_DECL_OVERRIDE;
void requestUpdateVimStatus() Q_DECL_OVERRIDE;
public slots: public slots:
// Enter edit mode. // Enter edit mode.
void editFile() Q_DECL_OVERRIDE; void editFile() Q_DECL_OVERRIDE;

View File

@ -20,6 +20,7 @@
#include "vwebview.h" #include "vwebview.h"
#include "vexporter.h" #include "vexporter.h"
#include "vmdtab.h" #include "vmdtab.h"
#include "vvimindicator.h"
extern VConfigManager vconfig; extern VConfigManager vconfig;
@ -111,12 +112,17 @@ void VMainWindow::setupUI()
this, &VMainWindow::handleCurTabStatusChanged); this, &VMainWindow::handleCurTabStatusChanged);
connect(editArea, &VEditArea::statusMessage, connect(editArea, &VEditArea::statusMessage,
this, &VMainWindow::showStatusMessage); this, &VMainWindow::showStatusMessage);
connect(editArea, &VEditArea::vimStatusUpdated,
this, &VMainWindow::handleVimStatusUpdated);
connect(m_findReplaceDialog, &VFindReplaceDialog::findTextChanged, connect(m_findReplaceDialog, &VFindReplaceDialog::findTextChanged,
this, &VMainWindow::handleFindDialogTextChanged); this, &VMainWindow::handleFindDialogTextChanged);
setCentralWidget(mainSplitter); setCentralWidget(mainSplitter);
// Create and show the status bar // Create and show the status bar
statusBar(); m_vimIndicator = new VVimIndicator(this);
m_vimIndicator->hide();
statusBar()->addPermanentWidget(m_vimIndicator);
} }
QWidget *VMainWindow::setupDirectoryPanel() QWidget *VMainWindow::setupDirectoryPanel()
@ -1121,9 +1127,12 @@ void VMainWindow::handleCurTabStatusChanged(const VFile *p_file, const VEditTab
title.append('#'); title.append('#');
} }
} }
updateWindowTitle(title); updateWindowTitle(title);
m_curFile = const_cast<VFile *>(p_file); m_curFile = const_cast<VFile *>(p_file);
m_curTab = const_cast<VEditTab *>(p_editTab); m_curTab = const_cast<VEditTab *>(p_editTab);
updateStatusInfo(p_editMode);
} }
void VMainWindow::onePanelView() void VMainWindow::onePanelView()
@ -1496,3 +1505,22 @@ void VMainWindow::showStatusMessage(const QString &p_msg)
const int timeout = 3000; const int timeout = 3000;
statusBar()->showMessage(p_msg, timeout); 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();
}
}

View File

@ -29,6 +29,7 @@ class VNotebookSelector;
class VAvatar; class VAvatar;
class VFindReplaceDialog; class VFindReplaceDialog;
class VCaptain; class VCaptain;
class VVimIndicator;
class VMainWindow : public QMainWindow class VMainWindow : public QMainWindow
{ {
@ -88,6 +89,9 @@ private slots:
// Show a temporary message in status bar. // Show a temporary message in status bar.
void showStatusMessage(const QString &p_msg); void showStatusMessage(const QString &p_msg);
// Handle Vim status updated.
void handleVimStatusUpdated(const VVim *p_vim);
protected: protected:
void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
@ -127,6 +131,9 @@ private:
void toggleOnePanelView(); void toggleOnePanelView();
void closeCurrentFile(); void closeCurrentFile();
// Update status bar information according to m_curTab and m_curFile.
void updateStatusInfo(bool p_editMode);
// Wrapper to create a QAction. // Wrapper to create a QAction.
QAction *newAction(const QIcon &p_icon, QAction *newAction(const QIcon &p_icon,
const QString &p_text, const QString &p_text,
@ -150,6 +157,7 @@ private:
VOutline *outline; VOutline *outline;
VAvatar *m_avatar; VAvatar *m_avatar;
VFindReplaceDialog *m_findReplaceDialog; VFindReplaceDialog *m_findReplaceDialog;
VVimIndicator *m_vimIndicator;
// Whether it is one panel or two panles. // Whether it is one panel or two panles.
bool m_onePanel; bool m_onePanel;

View File

@ -53,6 +53,8 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
connect(m_editOps, &VEditOperations::statusMessage, connect(m_editOps, &VEditOperations::statusMessage,
this, &VEdit::statusMessage); this, &VEdit::statusMessage);
connect(m_editOps, &VEditOperations::vimStatusUpdated,
this, &VEdit::vimStatusUpdated);
connect(this, &VMdEdit::cursorPositionChanged, connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::updateCurHeader); this, &VMdEdit::updateCurHeader);

View File

@ -60,6 +60,8 @@ void VMdTab::setupUI()
this, &VMdTab::discardAndRead); this, &VMdTab::discardAndRead);
connect(m_editor, &VEdit::statusMessage, connect(m_editor, &VEdit::statusMessage,
this, &VEditTab::statusMessage); this, &VEditTab::statusMessage);
connect(m_editor, &VEdit::vimStatusUpdated,
this, &VEditTab::vimStatusUpdated);
m_editor->reloadFile(); m_editor->reloadFile();
m_stacks->addWidget(m_editor); m_stacks->addWidget(m_editor);
@ -643,3 +645,12 @@ void VMdTab::focusChild()
{ {
m_stacks->currentWidget()->setFocus(); m_stacks->currentWidget()->setFocus();
} }
void VMdTab::requestUpdateVimStatus()
{
if (m_editor) {
m_editor->requestUpdateVimStatus();
} else {
emit vimStatusUpdated(NULL);
}
}

View File

@ -55,6 +55,8 @@ public:
MarkdownConverterType getMarkdownConverterType() const; MarkdownConverterType getMarkdownConverterType() const;
void requestUpdateVimStatus() Q_DECL_OVERRIDE;
public slots: public slots:
// Enter edit mode. // Enter edit mode.
void editFile() Q_DECL_OVERRIDE; void editFile() Q_DECL_OVERRIDE;

View File

@ -66,7 +66,6 @@
<file>resources/icons/dir_item.svg</file> <file>resources/icons/dir_item.svg</file>
<file>resources/icons/notebook_item.svg</file> <file>resources/icons/notebook_item.svg</file>
<file>resources/icons/arrow_dropdown.svg</file> <file>resources/icons/arrow_dropdown.svg</file>
<file>resources/icons/current_tab.svg</file>
<file>resources/icons/vnote.png</file> <file>resources/icons/vnote.png</file>
<file>resources/icons/insert_image.svg</file> <file>resources/icons/insert_image.svg</file>
<file>resources/icons/import_note.svg</file> <file>resources/icons/import_note.svg</file>
@ -108,5 +107,6 @@
<file>utils/markdown-it/markdown-it-sub.min.js</file> <file>utils/markdown-it/markdown-it-sub.min.js</file>
<file>utils/markdown-it/markdown-it-sup.min.js</file> <file>utils/markdown-it/markdown-it-sup.min.js</file>
<file>utils/markdown-it/markdown-it-footnote.min.js</file> <file>utils/markdown-it/markdown-it-footnote.min.js</file>
<file>resources/icons/arrow_dropup.svg</file>
</qresource> </qresource>
</RCC> </RCC>

174
src/vvimindicator.cpp Normal file
View File

@ -0,0 +1,174 @@
#include "vvimindicator.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QTreeWidget>
#include <QStringList>
#include <QFontMetrics>
#include <QFont>
#include <QHeaderView>
#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<QChar, VVim::Register> &p_regs)
{
p_tree->clear();
for (auto const &reg : 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("<span style=\"font-weight:bold;\">%1</span>")
.arg(p_vim->getPendingKeys());
m_keyLabel->setText(keyText);
}
void VVimIndicator::updateRegistersTree(QWidget *p_widget)
{
QTreeWidget *regTree = dynamic_cast<QTreeWidget *>(p_widget);
if (!m_vim) {
regTree->clear();
return;
}
const QMap<QChar, VVim::Register> &regs = m_vim->getRegisters();
fillTreeItemsWithRegisters(regTree, regs);
}

40
src/vvimindicator.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef VVIMINDICATOR_H
#define VVIMINDICATOR_H
#include <QWidget>
#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