mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
ViewArea: add Ctrl+G, H/J/K/L to navigate through ViewSplits
This commit is contained in:
parent
c13d1803ce
commit
361bbc50b8
@ -5,7 +5,7 @@
|
||||
|
||||
A pleasant note-taking platform.
|
||||
|
||||
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote).
|
||||
For more information, please visit [**VNote's Home Page**](https://vnotex.github.io/vnote) or [Home Page on Gitee](https://tamlok.gitee.io/vnote).
|
||||
|
||||

|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
一个舒适的笔记平台!
|
||||
|
||||
更多信息,请访问 [VNote 主页](https://vnotex.github.io/vnote) 。
|
||||
更多信息,请访问 [VNote 主页](https://tamlok.gitee.io/vnote) 或者 [Gitee 托管主页](https://tamlok.gitee.io/vnote) 。
|
||||
|
||||

|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 83f131edfa70ffce125f4f7ac4f9f75bf5f03078
|
||||
Subproject commit 34ad7467eb42b5d1d228228d875a7675814f222b
|
@ -53,6 +53,10 @@ namespace vnotex
|
||||
ActivatePreviousTab,
|
||||
FocusContentArea,
|
||||
OpenWithDefaultProgram,
|
||||
OneSplitLeft,
|
||||
OneSplitDown,
|
||||
OneSplitUp,
|
||||
OneSplitRight,
|
||||
MaxShortcut
|
||||
};
|
||||
Q_ENUM(Shortcut)
|
||||
|
@ -117,5 +117,5 @@ QString MainConfig::getVersion(const QJsonObject &p_jobj)
|
||||
void MainConfig::doVersionSpecificOverride()
|
||||
{
|
||||
// In a new version, we may want to change one value by force.
|
||||
m_coreConfig->m_shortcuts[CoreConfig::Shortcut::SearchDock].clear();
|
||||
m_coreConfig->m_shortcuts[CoreConfig::Shortcut::LocationListDock] = "Ctrl+G, C";
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
"OutlineDock" : "Ctrl+G, U",
|
||||
"SearchDock" : "",
|
||||
"SnippetDock" : "Ctrl+G, S",
|
||||
"LocationListDock" : "Ctrl+G, L",
|
||||
"LocationListDock" : "Ctrl+G, C",
|
||||
"Search" : "Ctrl+Alt+F",
|
||||
"NavigationMode" : "Ctrl+G, W",
|
||||
"LocateNode" : "Ctrl+G, D",
|
||||
@ -46,7 +46,11 @@
|
||||
"ActivateNextTab" : "Ctrl+G, N",
|
||||
"ActivatePreviousTab" : "Ctrl+G, P",
|
||||
"FocusContentArea" : "Ctrl+G, Y",
|
||||
"OpenWithDefaultProgram" : "F9"
|
||||
"OpenWithDefaultProgram" : "F9",
|
||||
"OneSplitLeft" : "Ctrl+G, H",
|
||||
"OneSplitDown" : "Ctrl+G, J",
|
||||
"OneSplitUp" : "Ctrl+G, K",
|
||||
"OneSplitRight" : "Ctrl+G, L"
|
||||
},
|
||||
"toolbar_icon_size" : 16,
|
||||
"note_management" : {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<p>
|
||||
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3 and continue to be open source.
|
||||
VNoteX is designed to be a pleasant note-taking platform, refactored from VNote, which is an open source note-taking application for Markdown since 2016. VNote shares most of the code base with VNoteX since version 3 and continues to be open source.
|
||||
<br/><br/>
|
||||
Source code of VNote could be found at <a href="https://github.com/vnotex/vnote">GitHub</a>.
|
||||
<br/><br/>
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Shortcuts
|
||||
1. All the keys without special notice are **case insensitive**;
|
||||
2. On macOS, `Ctrl` corresponds to `Command` except in Vi mode.
|
||||
2. On macOS, `Ctrl` corresponds to `Command` except in Vi mode;
|
||||
3. For a complete shortcuts list, please view the `vnotex.json` configuration file.
|
||||
|
||||
## General
|
||||
- `Ctrl+G E`
|
||||
|
@ -1,6 +1,7 @@
|
||||
# 快捷键
|
||||
1. 以下按键除特别说明外,都不区分大小写;
|
||||
2. 在 macOS 下,`Ctrl`对应于`Command`,在 Vi 模式下除外。
|
||||
2. 在 macOS 下,`Ctrl`对应于`Command`,在 Vi 模式下除外;
|
||||
3. 可以通过查看配置文件 `vnotex.json` 来获取一个完整的快捷键列表。
|
||||
|
||||
## 通用
|
||||
- `Ctrl+G E`
|
||||
|
@ -1126,6 +1126,10 @@ vnotex--ViewSplit QTabBar::tab:selected {
|
||||
background-color: @widgets#viewsplit#tabbar#tab#selected#bg;
|
||||
}
|
||||
|
||||
vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected {
|
||||
background-color: @widgets#viewsplit#flash#bg;
|
||||
}
|
||||
|
||||
vte--VTextEdit {
|
||||
border: none;
|
||||
}
|
||||
|
@ -252,6 +252,9 @@
|
||||
"bg" : "@base#content#bg"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flash" : {
|
||||
"bg" : "@base#master#alt"
|
||||
}
|
||||
},
|
||||
"qmainwindow" : {
|
||||
|
@ -123,3 +123,7 @@ vnotex--MainWindow QLabel#MainWindowTipsLabel {
|
||||
font-size: 18pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected {
|
||||
background-color: @widgets#viewsplit#flash#bg;
|
||||
}
|
||||
|
@ -103,6 +103,9 @@
|
||||
"active" : {
|
||||
"fg" : "@base#icon#fg"
|
||||
}
|
||||
},
|
||||
"flash" : {
|
||||
"bg" : "@base#lighter#fg"
|
||||
}
|
||||
},
|
||||
"qmainwindow" : {
|
||||
|
@ -1126,6 +1126,10 @@ vnotex--ViewSplit QTabBar::tab:selected {
|
||||
background-color: @widgets#viewsplit#tabbar#tab#selected#bg;
|
||||
}
|
||||
|
||||
vnotex--ViewSplit QTabBar[ViewSplitFlash="true"]::tab:selected {
|
||||
background-color: @widgets#viewsplit#flash#bg;
|
||||
}
|
||||
|
||||
vte--VTextEdit {
|
||||
border: none;
|
||||
}
|
||||
|
@ -248,6 +248,9 @@
|
||||
"bg" : "@base#content#bg"
|
||||
}
|
||||
}
|
||||
},
|
||||
"flash" : {
|
||||
"bg" : "@base#master#alt"
|
||||
}
|
||||
},
|
||||
"qmainwindow" : {
|
||||
|
@ -581,6 +581,11 @@ void NotebookNodeExplorer::updateNode(Node *p_node)
|
||||
|
||||
item->setExpanded(expanded);
|
||||
} else {
|
||||
if (m_notebook->isRecycleBinNode(p_node) && !m_recycleBinNodeVisible) {
|
||||
// No need to update.
|
||||
return;
|
||||
}
|
||||
|
||||
saveNotebookTreeState(false);
|
||||
|
||||
generateNodeTree();
|
||||
|
@ -12,6 +12,8 @@ const char *PropertyDefs::c_dialogCentralWidget = "DialogCentralWidget";
|
||||
|
||||
const char *PropertyDefs::c_viewSplitCornerWidget = "ViewSplitCornerWidget";
|
||||
|
||||
const char *PropertyDefs::c_viewSplitFlash = "ViewSplitFlash";
|
||||
|
||||
const char *PropertyDefs::c_state = "State";
|
||||
|
||||
const char *PropertyDefs::c_viewWindowToolBar = "ViewWindowToolBar";
|
||||
|
@ -19,6 +19,8 @@ namespace vnotex
|
||||
|
||||
static const char *c_viewSplitCornerWidget;
|
||||
|
||||
static const char *c_viewSplitFlash;
|
||||
|
||||
static const char *c_viewWindowToolBar;
|
||||
|
||||
static const char *c_consoleTextEdit;
|
||||
|
@ -83,9 +83,9 @@ QToolBar *ToolBarHelper::setupFileToolBar(MainWindow *p_win, QToolBar *p_toolBar
|
||||
emit VNoteX::getInst().importNotebookRequested();
|
||||
});
|
||||
|
||||
// Import notebook of VNote 2.0.
|
||||
// Import notebook of VNote 2.
|
||||
btnMenu->addAction(generateIcon("import_notebook_of_vnote2.svg"),
|
||||
MainWindow::tr("Import Legacy Notebook Of VNote 2.0"),
|
||||
MainWindow::tr("Import Legacy Notebook Of VNote 2"),
|
||||
btnMenu,
|
||||
[]() {
|
||||
emit VNoteX::getInst().importLegacyNotebookRequested();
|
||||
|
@ -12,13 +12,15 @@
|
||||
#include <QApplication>
|
||||
#include <QSet>
|
||||
#include <QHash>
|
||||
#include <QTabBar>
|
||||
|
||||
#include "viewwindow.h"
|
||||
#include "mainwindow.h"
|
||||
#include "events.h"
|
||||
#include "propertydefs.h"
|
||||
#include <utils/widgetutils.h>
|
||||
#include <utils/docsutils.h>
|
||||
#include <utils/urldragdroputils.h>
|
||||
#include <core/events.h>
|
||||
#include <core/vnotex.h>
|
||||
#include <core/configmgr.h>
|
||||
#include <core/coreconfig.h>
|
||||
@ -314,7 +316,18 @@ void ViewArea::addFirstViewSplit()
|
||||
void ViewArea::postFirstViewSplit()
|
||||
{
|
||||
Q_ASSERT(!m_splits.isEmpty());
|
||||
setCurrentViewSplit(m_splits.first(), false);
|
||||
auto currentSplit = m_splits.first();
|
||||
// Check if any split has focus. If there is any, then set it as current split.
|
||||
auto focusWidget = QApplication::focusWidget();
|
||||
if (focusWidget) {
|
||||
for (const auto &split : m_splits) {
|
||||
if (split == focusWidget || split->isAncestorOf(focusWidget)) {
|
||||
currentSplit = split;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setCurrentViewSplit(currentSplit, false);
|
||||
|
||||
emit viewSplitsCountChanged();
|
||||
checkCurrentViewWindowChange();
|
||||
@ -372,7 +385,6 @@ void ViewArea::removeViewSplit(ViewSplit *p_split, bool p_removeWorkspace)
|
||||
unwrapSplitter(splitter);
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT(m_splits.isEmpty());
|
||||
m_mainLayout->removeWidget(p_split);
|
||||
if (!m_splits.isEmpty()) {
|
||||
newCurrentSplit = m_splits.first();
|
||||
@ -427,6 +439,9 @@ void ViewArea::setCurrentViewSplit(ViewSplit *p_split, bool p_focus)
|
||||
{
|
||||
Q_ASSERT(!p_split || m_splits.contains(p_split));
|
||||
if (p_split == m_currentSplit) {
|
||||
if (p_split && p_focus) {
|
||||
p_split->focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -829,6 +844,50 @@ void ViewArea::setupShortcuts()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// OneSplitLeft.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitLeft), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
focusSplitByDirection(Direction::Left);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// OneSplitDown.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitDown), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
focusSplitByDirection(Direction::Down);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// OneSplitUp.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitUp), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
focusSplitByDirection(Direction::Up);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// OneSplitRight.
|
||||
{
|
||||
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::OneSplitRight), this);
|
||||
if (shortcut) {
|
||||
connect(shortcut, &QShortcut::activated,
|
||||
this, [this]() {
|
||||
focusSplitByDirection(Direction::Right);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewArea::close(Node *p_node, bool p_force)
|
||||
@ -1232,3 +1291,95 @@ void ViewArea::openViewWindowFromSession(const ViewWindowSession &p_session)
|
||||
|
||||
emit VNoteX::getInst().openFileRequested(p_session.m_bufferPath, paras);
|
||||
}
|
||||
|
||||
void ViewArea::focusSplitByDirection(Direction p_direction)
|
||||
{
|
||||
if (!m_currentSplit) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget *widget = m_currentSplit;
|
||||
auto targetSplitType = SplitType::Vertical;
|
||||
if (p_direction == Direction::Up || p_direction == Direction::Down) {
|
||||
targetSplitType = SplitType::Horizontal;
|
||||
}
|
||||
int splitIdx = 0;
|
||||
|
||||
QSplitter *targetSplitter = nullptr;
|
||||
while (true) {
|
||||
auto splitter = tryGetParentSplitter(widget);
|
||||
if (!splitter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkSplitType(splitter) == targetSplitType) {
|
||||
targetSplitter = splitter;
|
||||
splitIdx = splitter->indexOf(widget);
|
||||
break;
|
||||
} else {
|
||||
widget = splitter;
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(targetSplitter);
|
||||
switch (p_direction) {
|
||||
case Direction::Left:
|
||||
--splitIdx;
|
||||
break;
|
||||
|
||||
case Direction::Right:
|
||||
++splitIdx;
|
||||
break;
|
||||
|
||||
case Direction::Up:
|
||||
--splitIdx;
|
||||
break;
|
||||
|
||||
case Direction::Down:
|
||||
++splitIdx;
|
||||
break;
|
||||
}
|
||||
|
||||
if (splitIdx < 0 || splitIdx >= targetSplitter->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto targetWidget = targetSplitter->widget(splitIdx);
|
||||
// Find first split from targetWidget.
|
||||
while (true) {
|
||||
auto splitter = dynamic_cast<QSplitter *>(targetWidget);
|
||||
if (splitter) {
|
||||
if (splitter->count() == 0) {
|
||||
// Should not be an empty splitter.
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
targetWidget = splitter->widget(0);
|
||||
} else {
|
||||
auto viewSplit = dynamic_cast<ViewSplit *>(targetWidget);
|
||||
Q_ASSERT(viewSplit);
|
||||
setCurrentViewSplit(viewSplit, true);
|
||||
flashViewSplit(viewSplit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ViewArea::SplitType ViewArea::checkSplitType(const QSplitter *p_splitter) const
|
||||
{
|
||||
return p_splitter->orientation() == Qt::Horizontal ? SplitType::Vertical : SplitType::Horizontal;
|
||||
}
|
||||
|
||||
void ViewArea::flashViewSplit(ViewSplit *p_split)
|
||||
{
|
||||
auto tabBar = p_split->tabBar();
|
||||
if (!tabBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Directly set the property of ViewSplit won't work.
|
||||
WidgetUtils::setPropertyDynamically(tabBar, PropertyDefs::c_viewSplitFlash, true);
|
||||
QTimer::singleShot(1000, tabBar, [tabBar]() {
|
||||
WidgetUtils::setPropertyDynamically(tabBar, PropertyDefs::c_viewSplitFlash, false);
|
||||
});
|
||||
}
|
||||
|
@ -144,6 +144,14 @@ namespace vnotex
|
||||
Horizontal
|
||||
};
|
||||
|
||||
enum class Direction
|
||||
{
|
||||
Left,
|
||||
Down,
|
||||
Up,
|
||||
Right
|
||||
};
|
||||
|
||||
void setupUI();
|
||||
|
||||
// Find given @p_buffer among all view splits.
|
||||
@ -218,6 +226,12 @@ namespace vnotex
|
||||
|
||||
void openViewWindowFromSession(const ViewWindowSession &p_session);
|
||||
|
||||
void focusSplitByDirection(Direction p_direction);
|
||||
|
||||
SplitType checkSplitType(const QSplitter *p_splitter) const;
|
||||
|
||||
void flashViewSplit(ViewSplit *p_split);
|
||||
|
||||
QLayout *m_mainLayout = nullptr;
|
||||
|
||||
QWidget *m_sceneWidget = nullptr;
|
||||
|
Loading…
x
Reference in New Issue
Block a user