mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
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:
parent
611a695cb1
commit
ab824946e8
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
149
src/voutline.cpp
149
src/voutline.cpp
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user