ViewArea: support ViewWindow movement across ViewSplits by Ctrl+G,Shift+H/J/K/L

This commit is contained in:
Le Tan 2021-07-15 20:02:16 +08:00
parent 361bbc50b8
commit 2e9c727248
12 changed files with 271 additions and 39 deletions

View File

@ -57,6 +57,10 @@ namespace vnotex
OneSplitDown,
OneSplitUp,
OneSplitRight,
MoveOneSplitLeft,
MoveOneSplitDown,
MoveOneSplitUp,
MoveOneSplitRight,
MaxShortcut
};
Q_ENUM(Shortcut)

View File

@ -101,6 +101,14 @@ namespace vnotex
enum { InvalidViewSplitId = 0 };
enum class Direction
{
Left,
Down,
Up,
Right
};
} // ns vnotex
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);

View File

@ -31,6 +31,7 @@ void WidgetConfig::init(const QJsonObject &p_app,
m_nodeExplorerAutoImportExternalFilesEnabled = READBOOL(QStringLiteral("node_explorer_auto_import_external_files_enabled"));
m_searchPanelAdvancedSettingsVisible = READBOOL(QStringLiteral("search_panel_advanced_settings_visible"));
m_mainWindowKeepDocksExpandingContentArea = READSTRLIST(QStringLiteral("main_window_keep_docks_expanding_content_area"));
m_snippetPanelBuiltInSnippetsVisible = READBOOL(QStringLiteral("snippet_panel_builtin_snippets_visible"));
}
QJsonObject WidgetConfig::toJson() const
@ -43,6 +44,7 @@ QJsonObject WidgetConfig::toJson() const
obj[QStringLiteral("node_explorer_external_files_visible")] = m_nodeExplorerExternalFilesVisible;
obj[QStringLiteral("node_explorer_auto_import_external_files_enabled")] = m_nodeExplorerAutoImportExternalFilesEnabled;
obj[QStringLiteral("search_panel_advanced_settings_visible")] = m_searchPanelAdvancedSettingsVisible;
obj[QStringLiteral("snippet_panel_builtin_snippets_visible")] = m_snippetPanelBuiltInSnippetsVisible;
writeStringList(obj,
QStringLiteral("main_window_keep_docks_expanding_content_area"),
m_mainWindowKeepDocksExpandingContentArea);
@ -128,3 +130,13 @@ void WidgetConfig::setMainWindowKeepDocksExpandingContentArea(const QStringList
{
updateConfig(m_mainWindowKeepDocksExpandingContentArea, p_docks, this);
}
bool WidgetConfig::isSnippetPanelBuiltInSnippetsVisible() const
{
return m_snippetPanelBuiltInSnippetsVisible;
}
void WidgetConfig::setSnippetPanelBuiltInSnippetsVisible(bool p_visible)
{
updateConfig(m_snippetPanelBuiltInSnippetsVisible, p_visible, this);
}

View File

@ -42,6 +42,9 @@ namespace vnotex
const QStringList &getMainWindowKeepDocksExpandingContentArea() const;
void setMainWindowKeepDocksExpandingContentArea(const QStringList &p_docks);
bool isSnippetPanelBuiltInSnippetsVisible() const;
void setSnippetPanelBuiltInSnippetsVisible(bool p_visible);
private:
int m_outlineAutoExpandedLevel = 6;
@ -59,6 +62,8 @@ namespace vnotex
// Object name of those docks that should be kept when expanding content area.
QStringList m_mainWindowKeepDocksExpandingContentArea;
bool m_snippetPanelBuiltInSnippetsVisible = true;
};
}

View File

@ -50,7 +50,11 @@
"OneSplitLeft" : "Ctrl+G, H",
"OneSplitDown" : "Ctrl+G, J",
"OneSplitUp" : "Ctrl+G, K",
"OneSplitRight" : "Ctrl+G, L"
"OneSplitRight" : "Ctrl+G, L",
"MoveOneSplitLeft" : "Ctrl+G, Shift+H",
"MoveOneSplitDown" : "Ctrl+G, Shift+J",
"MoveOneSplitUp" : "Ctrl+G, Shift+K",
"MoveOneSplitRight" : "Ctrl+G, Shift+L"
},
"toolbar_icon_size" : 16,
"note_management" : {
@ -341,6 +345,7 @@
"node_explorer_auto_import_external_files_enabled" : true,
"search_panel_advanced_settings_visible" : true,
"//comment" : "Docks to ignore when expanding content area of main window",
"main_window_keep_docks_expanding_content_area": []
"main_window_keep_docks_expanding_content_area": [],
"snippet_panel_builtin_snippets_visible" : true
}
}

View File

@ -431,7 +431,9 @@ void MarkdownEditor::insertImageLink(const QString &p_title,
void MarkdownEditor::handleCanInsertFromMimeData(const QMimeData *p_source, bool *p_handled, bool *p_allowed)
{
if (p_source->hasImage() || p_source->hasUrls()) {
if (p_source->hasImage() || (!p_source->hasText() && !p_source->hasHtml())) {
if (p_source->hasImage()
|| (!p_source->hasText() && !p_source->hasHtml())
|| QGuiApplication::keyboardModifiers() == Qt::ShiftModifier) {
// Change to Rich Paste.
QClipboard *clipboard = QApplication::clipboard();
clipboard->setProperty(c_clipboardPropertyMark, true);

View File

@ -8,6 +8,8 @@
#include <snippet/snippetmgr.h>
#include <core/vnotex.h>
#include <core/exception.h>
#include <core/configmgr.h>
#include <core/widgetconfig.h>
#include "titlebar.h"
#include "listwidget.h"
@ -22,6 +24,7 @@ using namespace vnotex;
SnippetPanel::SnippetPanel(QWidget *p_parent)
: QFrame(p_parent)
{
m_builtInSnippetsVisible = ConfigMgr::getInst().getWidgetConfig().isSnippetPanelBuiltInSnippetsVisible();
setupUI();
}
@ -49,7 +52,7 @@ void SnippetPanel::setupUI()
void SnippetPanel::setupTitleBar(const QString &p_title, QWidget *p_parent)
{
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::None, p_parent);
m_titleBar = new TitleBar(p_title, true, TitleBar::Action::Menu, p_parent);
m_titleBar->setActionButtonsAlwaysShown(true);
{
@ -65,6 +68,15 @@ void SnippetPanel::setupTitleBar(const QString &p_title, QWidget *p_parent)
WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(SnippetMgr::getInst().getSnippetFolder()));
});
}
auto showAct = m_titleBar->addMenuAction(tr("Show Built-In Snippets"),
m_titleBar,
[this](bool p_checked) {
ConfigMgr::getInst().getWidgetConfig().setSnippetPanelBuiltInSnippetsVisible(p_checked);
setBuiltInSnippetsVisible(p_checked);
});
showAct->setCheckable(true);
showAct->setChecked(m_builtInSnippetsVisible);
}
void SnippetPanel::newSnippet()
@ -91,6 +103,10 @@ void SnippetPanel::updateSnippetList()
const auto &snippets = SnippetMgr::getInst().getSnippets();
for (const auto &snippet : snippets) {
if (snippet->isReadOnly() && !m_builtInSnippetsVisible) {
continue;
}
auto item = new QListWidgetItem(m_snippetList);
QString suffix;
if (snippet->isReadOnly()) {
@ -231,3 +247,14 @@ void SnippetPanel::applySnippet(const QListWidgetItem *p_item)
emit applySnippetRequested(name);
}
}
void SnippetPanel::setBuiltInSnippetsVisible(bool p_visible)
{
if (m_builtInSnippetsVisible == p_visible) {
return;
}
m_builtInSnippetsVisible = p_visible;
updateSnippetList();
}

View File

@ -42,11 +42,15 @@ namespace vnotex
QString getSnippetName(const QListWidgetItem *p_item);
void setBuiltInSnippetsVisible(bool p_visible);
TitleBar *m_titleBar = nullptr;
QListWidget *m_snippetList = nullptr;
bool m_listInitialized = false;
bool m_builtInSnippetsVisible = true;
};
}

View File

@ -218,15 +218,13 @@ ViewSplit *ViewArea::createViewSplit(QWidget *p_parent, ID p_viewSplitId)
auto workspace = createWorkspace();
m_workspaces.push_back(workspace);
ID id = p_viewSplitId;
if (id == InvalidViewSplitId) {
id = m_nextViewSplitId++;
} else {
Q_ASSERT(p_viewSplitId >= m_nextViewSplitId);
m_nextViewSplitId = id + 1;
if (p_viewSplitId == InvalidViewSplitId) {
p_viewSplitId = m_nextViewSplitId++;
} else if (p_viewSplitId >= m_nextViewSplitId) {
m_nextViewSplitId = p_viewSplitId + 1;
}
auto split = new ViewSplit(m_workspaces, workspace, id, p_parent);
auto split = new ViewSplit(m_workspaces, workspace, p_viewSplitId, p_parent);
connect(split, &ViewSplit::viewWindowCloseRequested,
this, [this](ViewWindow *p_win) {
closeViewWindow(p_win, false, true);
@ -278,6 +276,8 @@ ViewSplit *ViewArea::createViewSplit(QWidget *p_parent, ID p_viewSplitId)
}
}
});
connect(split, &ViewSplit::moveViewWindowOneSplitRequested,
this, &ViewArea::moveViewWindowOneSplit);
return split;
}
@ -507,14 +507,14 @@ QSharedPointer<ViewWorkspace> ViewArea::createWorkspace()
return QSharedPointer<ViewWorkspace>::create(id);
}
void ViewArea::splitViewSplit(ViewSplit *p_split, SplitType p_type)
ViewSplit *ViewArea::splitViewSplit(ViewSplit *p_split, SplitType p_type, bool p_cloneViewWindow)
{
Q_ASSERT(p_split);
// Create the new split.
auto newSplit = createViewSplit(this);
// Clone a ViewWindow for the same buffer to display in the new split.
{
if (p_cloneViewWindow) {
auto win = p_split->getCurrentViewWindow();
if (win) {
auto buffer = win->getBuffer();
@ -560,6 +560,8 @@ void ViewArea::splitViewSplit(ViewSplit *p_split, SplitType p_type)
emit viewSplitsCountChanged();
checkCurrentViewWindowChange();
return newSplit;
}
QSplitter *ViewArea::createSplitter(Qt::Orientation p_orientation, QWidget *p_parent) const
@ -1294,22 +1296,31 @@ void ViewArea::openViewWindowFromSession(const ViewWindowSession &p_session)
void ViewArea::focusSplitByDirection(Direction p_direction)
{
if (!m_currentSplit) {
auto split = findSplitByDirection(m_currentSplit, p_direction);
if (!split) {
qWarning() << "failed to focus split by direction";
return;
}
QWidget *widget = m_currentSplit;
auto targetSplitType = SplitType::Vertical;
if (p_direction == Direction::Up || p_direction == Direction::Down) {
targetSplitType = SplitType::Horizontal;
setCurrentViewSplit(split, true);
flashViewSplit(split);
}
ViewSplit *ViewArea::findSplitByDirection(ViewSplit *p_split, Direction p_direction)
{
if (!p_split) {
return nullptr;
}
QWidget *widget = p_split;
const auto targetSplitType = splitTypeOfDirection(p_direction);
int splitIdx = 0;
QSplitter *targetSplitter = nullptr;
while (true) {
auto splitter = tryGetParentSplitter(widget);
if (!splitter) {
return;
return nullptr;
}
if (checkSplitType(splitter) == targetSplitType) {
@ -1341,7 +1352,7 @@ void ViewArea::focusSplitByDirection(Direction p_direction)
}
if (splitIdx < 0 || splitIdx >= targetSplitter->count()) {
return;
return nullptr;
}
auto targetWidget = targetSplitter->widget(splitIdx);
@ -1352,15 +1363,13 @@ void ViewArea::focusSplitByDirection(Direction p_direction)
if (splitter->count() == 0) {
// Should not be an empty splitter.
Q_ASSERT(false);
return;
return nullptr;
}
targetWidget = splitter->widget(0);
} else {
auto viewSplit = dynamic_cast<ViewSplit *>(targetWidget);
Q_ASSERT(viewSplit);
setCurrentViewSplit(viewSplit, true);
flashViewSplit(viewSplit);
break;
return viewSplit;
}
}
}
@ -1383,3 +1392,52 @@ void ViewArea::flashViewSplit(ViewSplit *p_split)
WidgetUtils::setPropertyDynamically(tabBar, PropertyDefs::c_viewSplitFlash, false);
});
}
void ViewArea::moveViewWindowOneSplit(ViewSplit *p_split, ViewWindow *p_win, Direction p_direction)
{
bool splitCountChanged = false;
auto targetSplit = findSplitByDirection(p_split, p_direction);
if (!targetSplit) {
if (p_split->getViewWindowCount() == 1) {
// Only one ViewWindow left. Skip it.
return;
}
// Create a new split.
targetSplit = splitViewSplit(p_split, splitTypeOfDirection(p_direction), false);
if (p_direction == Direction::Left || p_direction == Direction::Up) {
// Need to swap the position of the two splits.
auto splitter = tryGetParentSplitter(targetSplit);
Q_ASSERT(splitter);
Q_ASSERT(splitter->indexOf(targetSplit) == 1);
splitter->insertWidget(0, targetSplit);
}
splitCountChanged = true;
}
// Take ViewWindow out of @p_split.
p_split->takeViewWindow(p_win);
if (p_split->getViewWindowCount() == 0) {
removeViewSplit(p_split, true);
splitCountChanged = true;
}
targetSplit->addViewWindow(p_win);
setCurrentViewWindow(p_win);
flashViewSplit(targetSplit);
if (splitCountChanged) {
emit viewSplitsCountChanged();
}
}
ViewArea::SplitType ViewArea::splitTypeOfDirection(Direction p_direction)
{
if (p_direction == Direction::Up || p_direction == Direction::Down) {
return SplitType::Horizontal;
} else {
return SplitType::Vertical;
}
}

View File

@ -7,7 +7,7 @@
#include <functional>
#include <buffer/buffer.h>
#include "global.h"
#include <core/global.h>
#include "navigationmode.h"
#include "viewsplit.h"
#include "viewareasession.h"
@ -137,6 +137,8 @@ namespace vnotex
void saveSession() const;
void moveViewWindowOneSplit(ViewSplit *p_split, ViewWindow *p_win, Direction p_direction);
private:
enum class SplitType
{
@ -144,14 +146,6 @@ namespace vnotex
Horizontal
};
enum class Direction
{
Left,
Down,
Up,
Right
};
void setupUI();
// Find given @p_buffer among all view splits.
@ -179,7 +173,7 @@ namespace vnotex
QSharedPointer<ViewWorkspace> createWorkspace();
void splitViewSplit(ViewSplit *p_split, SplitType p_type);
ViewSplit *splitViewSplit(ViewSplit *p_split, SplitType p_type, bool p_cloneViewWindow = true);
QSplitter *tryGetParentSplitter(const QWidget *p_widget) const;
@ -228,10 +222,14 @@ namespace vnotex
void focusSplitByDirection(Direction p_direction);
ViewSplit *findSplitByDirection(ViewSplit *p_split, Direction p_direction);
SplitType checkSplitType(const QSplitter *p_splitter) const;
void flashViewSplit(ViewSplit *p_split);
static SplitType splitTypeOfDirection(Direction p_direction);
QLayout *m_mainLayout = nullptr;
QWidget *m_sceneWidget = nullptr;

View File

@ -530,14 +530,14 @@ void ViewSplit::updateMenu(QMenu *p_menu)
}
}
void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx)
{
Q_ASSERT(p_tabIdx > -1);
// Close Tab.
auto closeTabAct = p_menu->addAction(tr("Close Tab"),
[this, p_tabIdx]() {
const_cast<ViewSplit *>(this)->closeTab(p_tabIdx);
closeTab(p_tabIdx);
});
WidgetUtils::addActionShortcutText(closeTabAct,
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::CloseTab));
@ -554,7 +554,7 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
}
for (auto win : windowsNeedToClose) {
emit const_cast<ViewSplit *>(this)->viewWindowCloseRequested(win);
emit viewWindowCloseRequested(win);
}
});
@ -563,7 +563,7 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
[this, p_tabIdx]() {
int cnt = getViewWindowCount();
for (int i = cnt - 1; i > p_tabIdx; --i) {
const_cast<ViewSplit *>(this)->closeTab(i);
closeTab(i);
}
});
@ -633,6 +633,56 @@ void ViewSplit::createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const
emit VNoteX::getInst().pinToQuickAccessRequested(files);
}
});
p_menu->addSeparator();
{
auto splitAct = p_menu->addAction(tr("Move One Split Left"),
[this, p_tabIdx]() {
auto win = getViewWindow(p_tabIdx);
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Left);
}
});
WidgetUtils::addActionShortcutText(splitAct,
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::MoveOneSplitLeft));
}
{
auto splitAct = p_menu->addAction(tr("Move One Split Right"),
[this, p_tabIdx]() {
auto win = getViewWindow(p_tabIdx);
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Right);
}
});
WidgetUtils::addActionShortcutText(splitAct,
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::MoveOneSplitRight));
}
{
auto splitAct = p_menu->addAction(tr("Move One Split Up"),
[this, p_tabIdx]() {
auto win = getViewWindow(p_tabIdx);
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Up);
}
});
WidgetUtils::addActionShortcutText(splitAct,
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::MoveOneSplitUp));
}
{
auto splitAct = p_menu->addAction(tr("Move One Split Down"),
[this, p_tabIdx]() {
auto win = getViewWindow(p_tabIdx);
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Down);
}
});
WidgetUtils::addActionShortcutText(splitAct,
ConfigMgr::getInst().getCoreConfig().getShortcut(CoreConfig::Shortcut::MoveOneSplitDown));
}
}
void ViewSplit::closeTab(int p_idx)
@ -904,6 +954,62 @@ void ViewSplit::setupShortcuts()
});
}
}
// MoveOneSplitLeft.
{
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::MoveOneSplitLeft), this, Qt::WidgetWithChildrenShortcut);
if (shortcut) {
connect(shortcut, &QShortcut::activated,
this, [this]() {
auto win = getCurrentViewWindow();
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Left);
}
});
}
}
// MoveOneSplitDown.
{
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::MoveOneSplitDown), this, Qt::WidgetWithChildrenShortcut);
if (shortcut) {
connect(shortcut, &QShortcut::activated,
this, [this]() {
auto win = getCurrentViewWindow();
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Down);
}
});
}
}
// MoveOneSplitUp.
{
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::MoveOneSplitUp), this, Qt::WidgetWithChildrenShortcut);
if (shortcut) {
connect(shortcut, &QShortcut::activated,
this, [this]() {
auto win = getCurrentViewWindow();
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Up);
}
});
}
}
// MoveOneSplitRight.
{
auto shortcut = WidgetUtils::createShortcut(coreConfig.getShortcut(CoreConfig::MoveOneSplitRight), this, Qt::WidgetWithChildrenShortcut);
if (shortcut) {
connect(shortcut, &QShortcut::activated,
this, [this]() {
auto win = getCurrentViewWindow();
if (win) {
emit moveViewWindowOneSplitRequested(this, win, Direction::Right);
}
});
}
}
}
void ViewSplit::focus()

View File

@ -6,6 +6,7 @@
#include <functional>
#include <buffer/buffer.h>
#include <core/global.h>
class QToolButton;
class QMenu;
@ -92,6 +93,8 @@ namespace vnotex
void currentViewWindowChanged(ViewWindow *p_win);
void moveViewWindowOneSplitRequested(ViewSplit *p_split, ViewWindow *p_win, Direction p_direction);
protected:
bool eventFilter(QObject *p_object, QEvent *p_event) Q_DECL_OVERRIDE;
@ -125,7 +128,7 @@ namespace vnotex
void updateMenu(QMenu *p_menu);
void createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx) const;
void createContextMenuOnTabBar(QMenu *p_menu, int p_tabIdx);
void focusCurrentViewWindow();