From 2e9c727248792294bd16dc86fd162e0a30e4ed20 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Thu, 15 Jul 2021 20:02:16 +0800 Subject: [PATCH] ViewArea: support ViewWindow movement across ViewSplits by Ctrl+G,Shift+H/J/K/L --- src/core/coreconfig.h | 4 + src/core/global.h | 8 ++ src/core/widgetconfig.cpp | 12 +++ src/core/widgetconfig.h | 5 ++ src/data/core/vnotex.json | 9 +- src/widgets/editors/markdowneditor.cpp | 4 +- src/widgets/snippetpanel.cpp | 29 ++++++- src/widgets/snippetpanel.h | 4 + src/widgets/viewarea.cpp | 98 ++++++++++++++++----- src/widgets/viewarea.h | 18 ++-- src/widgets/viewsplit.cpp | 114 ++++++++++++++++++++++++- src/widgets/viewsplit.h | 5 +- 12 files changed, 271 insertions(+), 39 deletions(-) diff --git a/src/core/coreconfig.h b/src/core/coreconfig.h index 6ada3757..85cbbca7 100644 --- a/src/core/coreconfig.h +++ b/src/core/coreconfig.h @@ -57,6 +57,10 @@ namespace vnotex OneSplitDown, OneSplitUp, OneSplitRight, + MoveOneSplitLeft, + MoveOneSplitDown, + MoveOneSplitUp, + MoveOneSplitRight, MaxShortcut }; Q_ENUM(Shortcut) diff --git a/src/core/global.h b/src/core/global.h index 37701c7d..306e3a91 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -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); diff --git a/src/core/widgetconfig.cpp b/src/core/widgetconfig.cpp index 92fed255..82d8d653 100644 --- a/src/core/widgetconfig.cpp +++ b/src/core/widgetconfig.cpp @@ -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); +} diff --git a/src/core/widgetconfig.h b/src/core/widgetconfig.h index a31fdbfe..0a13455d 100644 --- a/src/core/widgetconfig.h +++ b/src/core/widgetconfig.h @@ -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; }; } diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index 70cc8907..83dc8367 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -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 } } diff --git a/src/widgets/editors/markdowneditor.cpp b/src/widgets/editors/markdowneditor.cpp index 937ac42d..3f751acd 100644 --- a/src/widgets/editors/markdowneditor.cpp +++ b/src/widgets/editors/markdowneditor.cpp @@ -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); diff --git a/src/widgets/snippetpanel.cpp b/src/widgets/snippetpanel.cpp index 49e7a5bb..9c75272a 100644 --- a/src/widgets/snippetpanel.cpp +++ b/src/widgets/snippetpanel.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #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(); +} diff --git a/src/widgets/snippetpanel.h b/src/widgets/snippetpanel.h index 9c891a58..43d038e5 100644 --- a/src/widgets/snippetpanel.h +++ b/src/widgets/snippetpanel.h @@ -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; }; } diff --git a/src/widgets/viewarea.cpp b/src/widgets/viewarea.cpp index 914fe002..c23417b1 100644 --- a/src/widgets/viewarea.cpp +++ b/src/widgets/viewarea.cpp @@ -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 ViewArea::createWorkspace() return QSharedPointer::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(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; + } +} diff --git a/src/widgets/viewarea.h b/src/widgets/viewarea.h index 837e95bf..9a96d632 100644 --- a/src/widgets/viewarea.h +++ b/src/widgets/viewarea.h @@ -7,7 +7,7 @@ #include #include -#include "global.h" +#include #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 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; diff --git a/src/widgets/viewsplit.cpp b/src/widgets/viewsplit.cpp index 5883a5be..2fccfd72 100644 --- a/src/widgets/viewsplit.cpp +++ b/src/widgets/viewsplit.cpp @@ -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(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(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(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() diff --git a/src/widgets/viewsplit.h b/src/widgets/viewsplit.h index 0d64f57c..25c6ba3b 100644 --- a/src/widgets/viewsplit.h +++ b/src/widgets/viewsplit.h @@ -6,6 +6,7 @@ #include #include +#include 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();