register VEditArea and VOutline for Navigation Mode

1. Register VEditArea and VOutline for Navigation mode;
2. Support Ctrl+J and Ctrl+K navigation in VOutline;
This commit is contained in:
Le Tan 2017-04-06 19:39:52 +08:00
parent 611a695cb1
commit ab824946e8
11 changed files with 353 additions and 44 deletions

View File

@ -281,6 +281,7 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers)
case Qt::Key_W:
// Enter navigation mode.
triggerNavigationMode();
m_ignoreFocusChange = false;
return ret;
case Qt::Key_X:

View File

@ -11,7 +11,8 @@
extern VNote *g_vnote;
VDirectoryTree::VDirectoryTree(VNote *vnote, QWidget *parent)
: QTreeWidget(parent), vnote(vnote), m_editArea(NULL)
: QTreeWidget(parent), VNavigationMode(),
vnote(vnote), m_editArea(NULL)
{
setColumnCount(1);
setHeaderHidden(true);
@ -720,6 +721,10 @@ void VDirectoryTree::showNavigation()
}
m_naviLabels.clear();
if (!isVisible()) {
return;
}
// Generate labels for visible items.
auto items = getVisibleItems();
for (int i = 0; i < 26 && i < items.size(); ++i) {
@ -752,17 +757,21 @@ bool VDirectoryTree::handleKeyNavigation(int p_key, bool &p_succeed)
QChar keyChar = VUtils::keyToChar(p_key);
if (secondKey && !keyChar.isNull()) {
secondKey = false;
p_succeed = true;
ret = true;
auto it = m_keyMap.find(keyChar);
if (it != m_keyMap.end()) {
setCurrentItem(it.value());
setFocus();
p_succeed = true;
ret = true;
}
} else if (keyChar == m_majorKey) {
// Major key pressed.
// Need second key.
// Need second key if m_keyMap is not empty.
if (m_keyMap.isEmpty()) {
p_succeed = true;
} else {
secondKey = true;
}
ret = true;
}
return ret;

View File

@ -6,11 +6,14 @@
#include "vconfigmanager.h"
#include "vfile.h"
#include "dialog/vfindreplacedialog.h"
#include "utils/vutils.h"
extern VConfigManager vconfig;
extern VNote *g_vnote;
VEditArea::VEditArea(VNote *vnote, QWidget *parent)
: QWidget(parent), vnote(vnote), curWindowIndex(-1)
: QWidget(parent), VNavigationMode(),
vnote(vnote), curWindowIndex(-1)
{
setupUI();
@ -570,3 +573,74 @@ VEditWindow *VEditArea::getCurrentWindow() const
}
return getWindow(curWindowIndex);
}
void VEditArea::registerNavigation(QChar p_majorKey)
{
m_majorKey = p_majorKey;
V_ASSERT(m_keyMap.empty());
V_ASSERT(m_naviLabels.empty());
}
void VEditArea::showNavigation()
{
// Clean up.
m_keyMap.clear();
for (auto label : m_naviLabels) {
delete label;
}
m_naviLabels.clear();
if (!isVisible()) {
return;
}
// Generate labels for VEditWindow.
for (int i = 0; i < 26 && i < splitter->count(); ++i) {
QChar key('a' + i);
m_keyMap[key] = getWindow(i);
QString str = QString(m_majorKey) + key;
QLabel *label = new QLabel(str, this);
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
label->move(getWindow(i)->geometry().topLeft());
label->show();
m_naviLabels.append(label);
}
}
void VEditArea::hideNavigation()
{
m_keyMap.clear();
for (auto label : m_naviLabels) {
delete label;
}
m_naviLabels.clear();
}
bool VEditArea::handleKeyNavigation(int p_key, bool &p_succeed)
{
static bool secondKey = false;
bool ret = false;
p_succeed = false;
QChar keyChar = VUtils::keyToChar(p_key);
if (secondKey && !keyChar.isNull()) {
secondKey = false;
p_succeed = true;
ret = true;
auto it = m_keyMap.find(keyChar);
if (it != m_keyMap.end()) {
setCurrentWindow(splitter->indexOf(it.value()), true);
}
} else if (keyChar == m_majorKey) {
// Major key pressed.
// Need second key if m_keyMap is not empty.
if (m_keyMap.isEmpty()) {
p_succeed = true;
} else {
secondKey = true;
}
ret = true;
}
return ret;
}

View File

@ -13,13 +13,14 @@
#include "vnotebook.h"
#include "veditwindow.h"
#include "vtoc.h"
#include "vnavigationmode.h"
class VNote;
class VFile;
class VDirectory;
class VFindReplaceDialog;
class VEditArea : public QWidget
class VEditArea : public QWidget, public VNavigationMode
{
Q_OBJECT
public:
@ -50,6 +51,12 @@ public:
void moveCurrentTabOneSplit(bool p_right);
VEditWindow *getCurrentWindow() const;
// Implementations for VNavigationMode.
void registerNavigation(QChar p_majorKey);
void showNavigation();
void hideNavigation();
bool handleKeyNavigation(int p_key, bool &p_succeed);
signals:
void curTabStatusChanged(const VFile *p_file, const VEditTab *p_editTab, bool p_editMode);
void outlineChanged(const VToc &toc);
@ -101,6 +108,11 @@ private:
// Splitter holding multiple split windows
QSplitter *splitter;
VFindReplaceDialog *m_findReplace;
// Navigation Mode.
// Map second key to VEditWindow.
QMap<QChar, VEditWindow *> m_keyMap;
QVector<QLabel *> m_naviLabels;
};
inline VEditWindow* VEditArea::getWindow(int windowIndex) const

View File

@ -12,7 +12,7 @@
extern VNote *g_vnote;
VFileList::VFileList(QWidget *parent)
: QWidget(parent)
: QWidget(parent), VNavigationMode()
{
setupUI();
initActions();
@ -565,6 +565,10 @@ void VFileList::showNavigation()
}
m_naviLabels.clear();
if (!isVisible()) {
return;
}
// Generate labels for visible items.
auto items = getVisibleItems();
for (int i = 0; i < 26 && i < items.size(); ++i) {
@ -597,17 +601,21 @@ bool VFileList::handleKeyNavigation(int p_key, bool &p_succeed)
QChar keyChar = VUtils::keyToChar(p_key);
if (secondKey && !keyChar.isNull()) {
secondKey = false;
p_succeed = true;
ret = true;
auto it = m_keyMap.find(keyChar);
if (it != m_keyMap.end()) {
fileList->setCurrentItem(it.value());
fileList->setFocus();
p_succeed = true;
ret = true;
}
} else if (keyChar == m_majorKey) {
// Major key pressed.
// Need second key.
// Need second key if m_keyMap is not empty.
if (m_keyMap.isEmpty()) {
p_succeed = true;
} else {
secondKey = true;
}
ret = true;
}
return ret;

View File

@ -53,6 +53,8 @@ void VMainWindow::initCaptain()
m_captain->registerNavigationTarget(notebookSelector);
m_captain->registerNavigationTarget(directoryTree);
m_captain->registerNavigationTarget(fileList);
m_captain->registerNavigationTarget(editArea);
m_captain->registerNavigationTarget(outline);
}
void VMainWindow::setupUI()

View File

@ -5,6 +5,8 @@
#include <QDir>
#include <QFont>
#include <QFontMetrics>
#include <QStringList>
#include <QFontDatabase>
#include "vnote.h"
#include "utils/vutils.h"
#include "vconfigmanager.h"
@ -176,14 +178,20 @@ QVector<VNotebook *> &VNote::getNotebooks()
QString VNote::getNavigationLabelStyle(const QString &p_str) const
{
static int lastLen = -1;
static int pxWidth = 24;
int fontPt = 15;
QString fontFamily("Monospace");
QString fontFamily = getMonospacedFont();
if (p_str.size() != lastLen) {
QFont font(fontFamily, fontPt);
font.setBold(true);
QFontMetrics fm(font);
int pxWidth = fm.width(p_str);
pxWidth = fm.width(p_str);
lastLen = p_str.size();
}
QString stylesheet = QString("background-color: %1;"
return QString("background-color: %1;"
"color: %2;"
"font-size: %3pt;"
"font: bold;"
@ -196,5 +204,30 @@ QString VNote::getNavigationLabelStyle(const QString &p_str) const
.arg(fontPt)
.arg(fontFamily)
.arg(pxWidth);
return stylesheet;
}
const QString &VNote::getMonospacedFont() const
{
static QString font;
if (font.isNull()) {
QStringList candidates;
candidates << "Consolas" << "Monaco" << "Andale Mono" << "Monospace" << "Courier New";
QStringList availFamilies = QFontDatabase().families();
for (int i = 0; i < candidates.size(); ++i) {
QString family = candidates[i].trimmed().toLower();
for (int j = 0; j < availFamilies.size(); ++j) {
QString availFamily = availFamilies[j];
availFamily.remove(QRegExp("\\[.*\\]"));
if (family == availFamily.trimmed().toLower()) {
font = availFamily;
return font;
}
}
}
// Fallback to current font.
font = QFont().family();
}
return font;
}

View File

@ -60,6 +60,8 @@ public slots:
void updateTemplate();
private:
const QString &getMonospacedFont() const;
// Maintain all the notebooks. Other holder should use QPointer.
QVector<VNotebook *> m_notebooks;
QVector<QPair<QString, QString> > m_palette;

View File

@ -399,37 +399,41 @@ void VNotebookSelector::resizeListWidgetToContent()
void VNotebookSelector::registerNavigation(QChar p_majorKey)
{
Q_ASSERT(!m_naviLabel);
qDebug() << "VNotebookSelector register for navigation key" << p_majorKey;
m_majorKey = p_majorKey;
m_naviLabel = new QLabel(m_majorKey, this);
m_naviLabel->setStyleSheet(g_vnote->getNavigationLabelStyle(m_majorKey));
m_naviLabel->hide();
}
void VNotebookSelector::showNavigation()
{
qDebug() << "VNotebookSelector show navigation";
if (!isVisible()) {
return;
}
V_ASSERT(!m_naviLabel);
m_naviLabel = new QLabel(m_majorKey, this);
m_naviLabel->setStyleSheet(g_vnote->getNavigationLabelStyle(m_majorKey));
m_naviLabel->show();
}
void VNotebookSelector::hideNavigation()
{
qDebug() << "VNotebookSelector hide navigation";
m_naviLabel->hide();
if (m_naviLabel) {
delete m_naviLabel;
m_naviLabel = NULL;
}
}
bool VNotebookSelector::handleKeyNavigation(int p_key, bool &p_succeed)
{
qDebug() << "VNotebookSelector handle key navigation" << p_key;
bool ret = false;
p_succeed = false;
QChar keyChar = VUtils::keyToChar(p_key);
if (keyChar == m_majorKey) {
// Hit.
p_succeed = true;
showPopup();
ret = true;
if (m_naviLabel) {
showPopup();
}
}
return ret;
}

View File

@ -3,11 +3,17 @@
#include <QString>
#include <QJsonObject>
#include <QKeyEvent>
#include <QLabel>
#include <QCoreApplication>
#include "voutline.h"
#include "vtoc.h"
#include "utils/vutils.h"
#include "vnote.h"
extern VNote *g_vnote;
VOutline::VOutline(QWidget *parent)
: QTreeWidget(parent)
: QTreeWidget(parent), VNavigationMode()
{
setColumnCount(1);
setHeaderHidden(true);
@ -178,11 +184,150 @@ bool VOutline::selectLineNumberOne(QTreeWidgetItem *item, int lineNumber)
void VOutline::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Return) {
int key = event->key();
int modifiers = event->modifiers();
switch (key) {
case Qt::Key_Return:
{
QTreeWidgetItem *item = currentItem();
if (item) {
item->setExpanded(!item->isExpanded());
}
break;
}
case Qt::Key_J:
{
if (modifiers == Qt::ControlModifier) {
event->accept();
QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
Qt::NoModifier);
QCoreApplication::postEvent(this, downEvent);
return;
}
break;
}
case Qt::Key_K:
{
if (modifiers == Qt::ControlModifier) {
event->accept();
QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
Qt::NoModifier);
QCoreApplication::postEvent(this, upEvent);
return;
}
break;
}
default:
break;
}
QTreeWidget::keyPressEvent(event);
}
void VOutline::registerNavigation(QChar p_majorKey)
{
m_majorKey = p_majorKey;
V_ASSERT(m_keyMap.empty());
V_ASSERT(m_naviLabels.empty());
}
void VOutline::showNavigation()
{
// Clean up.
m_keyMap.clear();
for (auto label : m_naviLabels) {
delete label;
}
m_naviLabels.clear();
if (!isVisible()) {
return;
}
// Generate labels for visible items.
auto items = getVisibleItems();
for (int i = 0; i < 26 && i < items.size(); ++i) {
QChar key('a' + i);
m_keyMap[key] = items[i];
QString str = QString(m_majorKey) + key;
QLabel *label = new QLabel(str, this);
label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
label->move(visualItemRect(items[i]).topLeft());
label->show();
m_naviLabels.append(label);
}
}
void VOutline::hideNavigation()
{
m_keyMap.clear();
for (auto label : m_naviLabels) {
delete label;
}
m_naviLabels.clear();
}
bool VOutline::handleKeyNavigation(int p_key, bool &p_succeed)
{
static bool secondKey = false;
bool ret = false;
p_succeed = false;
QChar keyChar = VUtils::keyToChar(p_key);
if (secondKey && !keyChar.isNull()) {
secondKey = false;
p_succeed = true;
ret = true;
auto it = m_keyMap.find(keyChar);
if (it != m_keyMap.end()) {
setCurrentItem(it.value());
setFocus();
}
} else if (keyChar == m_majorKey) {
// Major key pressed.
// Need second key if m_keyMap is not empty.
if (m_keyMap.isEmpty()) {
p_succeed = true;
} else {
secondKey = true;
}
ret = true;
}
return ret;
}
QList<QTreeWidgetItem *> VOutline::getVisibleItems() const
{
QList<QTreeWidgetItem *> items;
for (int i = 0; i < topLevelItemCount(); ++i) {
QTreeWidgetItem *item = topLevelItem(i);
if (!item->isHidden()) {
items.append(item);
if (item->isExpanded()) {
items.append(getVisibleChildItems(item));
}
}
}
return items;
}
QList<QTreeWidgetItem *> VOutline::getVisibleChildItems(const QTreeWidgetItem *p_item) const
{
QList<QTreeWidgetItem *> items;
if (p_item && !p_item->isHidden() && p_item->isExpanded()) {
for (int i = 0; i < p_item->childCount(); ++i) {
QTreeWidgetItem *child = p_item->child(i);
if (!child->isHidden()) {
items.append(child);
if (child->isExpanded()) {
items.append(getVisibleChildItems(child));
}
}
}
}
return items;
}

View File

@ -2,14 +2,26 @@
#define VOUTLINE_H
#include <QTreeWidget>
#include <QVector>
#include <QMap>
#include <QChar>
#include "vtoc.h"
#include "vnavigationmode.h"
class VOutline : public QTreeWidget
class QLabel;
class VOutline : public QTreeWidget, public VNavigationMode
{
Q_OBJECT
public:
VOutline(QWidget *parent = 0);
// Implementations for VNavigationMode.
void registerNavigation(QChar p_majorKey);
void showNavigation();
void hideNavigation();
bool handleKeyNavigation(int p_key, bool &p_succeed);
signals:
void outlineItemActivated(const VAnchor &anchor);
@ -32,9 +44,16 @@ private:
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
void selectLineNumber(int lineNumber);
bool selectLineNumberOne(QTreeWidgetItem *item, int lineNumber);
QList<QTreeWidgetItem *> getVisibleItems() const;
QList<QTreeWidgetItem *> getVisibleChildItems(const QTreeWidgetItem *p_item) const;
VToc outline;
VAnchor curHeader;
// Navigation Mode.
// Map second key to QTreeWidgetItem.
QMap<QChar, QTreeWidgetItem *> m_keyMap;
QVector<QLabel *> m_naviLabels;
};
#endif // VOUTLINE_H