Git commit 29e86ddeb7b9bee1b73014a97439570dafb78f31 by Kurt Hindenburg, on behalf of Lucas Biaggi. Committed on 24/04/2021 at 21:30. Pushed by hindenburg into branch 'master'.
Add ability to load/save session layouts(split views) and add default layouts Add three layouts to the toolbar; add --layout <file> to the command line. The 3 defaults layouts are 2x2, 2x1, 1x2 GUI: M +1 -0 data/CMakeLists.txt A +24 -0 data/layouts/1x2-terminals.json A +24 -0 data/layouts/2x1-terminals.json A +27 -0 data/layouts/2x2-terminals.json A +6 -0 data/layouts/CMakeLists.txt M +6 -1 desktop/konsoleui.rc M +16 -7 src/Application.cpp M +14 -0 src/MainWindow.cpp M +75 -4 src/ViewManager.cpp M +5 -0 src/ViewManager.h https://invent.kde.org/utilities/konsole/commit/29e86ddeb7b9bee1b73014a97439570dafb78f31 diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index b364995e..a3f8d2d8 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory( color-schemes ) add_subdirectory( keyboard-layouts ) +add_subdirectory( layouts ) install( FILES konsole.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR}) diff --git a/data/layouts/1x2-terminals.json b/data/layouts/1x2-terminals.json new file mode 100644 index 00000000..4762ef8d --- /dev/null +++ b/data/layouts/1x2-terminals.json @@ -0,0 +1,24 @@ +{ + "Orientation": "Vertical", + "Widgets": [ + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + } + ] + }, + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + }, + { + "SessionRestoreId": 0 + } + ] + } + ] +} diff --git a/data/layouts/2x1-terminals.json b/data/layouts/2x1-terminals.json new file mode 100644 index 00000000..5de5e84e --- /dev/null +++ b/data/layouts/2x1-terminals.json @@ -0,0 +1,24 @@ +{ + "Orientation": "Vertical", + "Widgets": [ + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + }, + { + "SessionRestoreId": 0 + } + ] + }, + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + } + ] + } + ] +} diff --git a/data/layouts/2x2-terminals.json b/data/layouts/2x2-terminals.json new file mode 100644 index 00000000..fbf54f87 --- /dev/null +++ b/data/layouts/2x2-terminals.json @@ -0,0 +1,27 @@ +{ + "Orientation": "Vertical", + "Widgets": [ + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + }, + { + "SessionRestoreId": 0 + } + ] + }, + { + "Orientation": "Horizontal", + "Widgets": [ + { + "SessionRestoreId": 0 + }, + { + "SessionRestoreId": 0 + } + ] + } + ] +} diff --git a/data/layouts/CMakeLists.txt b/data/layouts/CMakeLists.txt new file mode 100644 index 00000000..3d912dd0 --- /dev/null +++ b/data/layouts/CMakeLists.txt @@ -0,0 +1,6 @@ +install (FILES + 1x2-terminals.json + 2x1-terminals.json + 2x2-terminals.json + DESTINATION + ${KDE_INSTALL_DATADIR}/konsole) diff --git a/desktop/konsoleui.rc b/desktop/konsoleui.rc index 8ff09c51..9bc62425 100644 --- a/desktop/konsoleui.rc +++ b/desktop/konsoleui.rc @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!DOCTYPE gui SYSTEM "kpartgui.dtd"> -<gui name="konsole" version="13"> +<gui name="konsole" version="14"> <MenuBar> <Menu name="file"><text>File</text> <Action name="new-window"/> @@ -29,6 +29,8 @@ <Separator/> <Action name="detach-tab" /> <Action name="detach-view"/> + <Action name="save-layout"/> + <Action name="load-layout"/> <Separator/> <DefineGroup name="session-view-operations"/> </Menu> @@ -53,5 +55,8 @@ <Action name="new-tab"/> <Action name="split-view-left-right"/> <Action name="split-view-top-bottom"/> + <Action name="load-terminals-layout-2x2"/> + <Action name="load-terminals-layout-2x1"/> + <Action name="load-terminals-layout-1x2"/> </ToolBar> </gui> diff --git a/src/Application.cpp b/src/Application.cpp index 9a353b38..c5a7b04a 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -51,7 +51,11 @@ void Application::populateCommandLineParser(QCommandLineParser *parser) i18nc("@info:shell", "Name of profile to use for new Konsole instance"), QStringLiteral("name") }, - { { QStringLiteral("fallback-profile") }, + { { QStringLiteral("layout") }, + i18nc("@info:shell", "json layoutfile to be loaded to use for new Konsole instance"), + QStringLiteral("file") + }, + { { QStringLiteral("fallback-profile") }, i18nc("@info:shell", "Use the internal FALLBACK profile") }, { { QStringLiteral("workdir") }, @@ -210,7 +214,6 @@ int Application::newInstance() return 0; } } - // select profile to use Profile::Ptr baseProfile = processProfileSelectArgs(); @@ -218,14 +221,20 @@ int Application::newInstance() // selected profile to be changed Profile::Ptr newProfile = processProfileChangeArgs(baseProfile); - // create new session - Session *session = window->createSession(newProfile, QString()); + // if layout file is enable load it and create session from definitions, + // else create new session + if (m_parser->isSet(QStringLiteral("layout"))) { + window->viewManager()->loadLayout(m_parser->value(QStringLiteral("layout"))); + } else { + Session *session = window->createSession(newProfile, QString()); - if (m_parser->isSet(QStringLiteral("noclose"))) { - session->setAutoClose(false); + if (m_parser->isSet(QStringLiteral("noclose"))) { + session->setAutoClose(false); + } } - // if the background-mode argument is supplied, start the background + + // if the background-mode argument is supplied, start the background // session ( or bring to the front if it already exists ) if (m_parser->isSet(QStringLiteral("background-mode"))) { startBackgroundMode(window); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 4e6a4671..6774acb0 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -377,6 +377,20 @@ void MainWindow::setupActions() menuAction->setText(i18nc("@item", "Activate Menu")); collection->setDefaultShortcut(menuAction, Konsole::ACCEL | Qt::SHIFT | Qt::Key_F10); connect(menuAction, &QAction::triggered, this, &Konsole::MainWindow::activateMenuBar); + + auto action = collection->addAction(QStringLiteral("save-layout")); + action->setEnabled(true); + action->setText(i18nc("@action:inmenu", "Save tab layout to file")); + connect(action, &QAction::triggered, this, [this]() { + if (viewManager()) { viewManager()->saveLayoutFile(); } + }); + + action = collection->addAction(QStringLiteral("load-layout")); + action->setEnabled(true); + action->setText(i18nc("@action:inmenu", "Load tab layout from file")); + connect(action, &QAction::triggered, this, [this]() { + if (viewManager()) { viewManager()->loadLayoutFile(); } + }); } void MainWindow::viewFullScreen(bool fullScreen) diff --git a/src/ViewManager.cpp b/src/ViewManager.cpp index 751684dc..856245cf 100644 --- a/src/ViewManager.cpp +++ b/src/ViewManager.cpp @@ -12,9 +12,17 @@ // Qt #include <QStringList> #include <QTabBar> +#include <QStandardPaths> +#include <QFile> +#include <QFileDialog> + + +#include <QJsonArray> +#include <QJsonDocument> // KDE #include <KLocalizedString> +#include <KMessageBox> #include <KActionCollection> #include <KConfigGroup> @@ -113,6 +121,30 @@ void ViewManager::setupActions() collection->setDefaultShortcut(action, Konsole::ACCEL | Qt::Key_ParenRight); collection->addAction(QStringLiteral("split-view-top-bottom"), action); + action = new QAction(this); + action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom"))); + action->setText(i18nc("@action:inmenu", "Load a new tab with layout 2x2 terminals")); + connect(action, &QAction::triggered, + this, [this](){ + this->loadLayout(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/2x2-terminals.json"))); }); + collection->addAction(QStringLiteral("load-terminals-layout-2x2"), action); + + action = new QAction(this); + action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); + action->setText(i18nc("@action:inmenu", "Load a new tab with layout 2x1 terminals")); + connect(action, &QAction::triggered, + this, [this](){ + this->loadLayout(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/2x1-terminals.json"))); }); + collection->addAction(QStringLiteral("load-terminals-layout-2x1"), action); + + action = new QAction(this); + action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom"))); + action->setText(i18nc("@action:inmenu", "Load a new tab with layout 2x1 terminals")); + connect(action, &QAction::triggered, + this, [this](){ + this->loadLayout(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("konsole/1x2-terminals.json"))); }); + collection->addAction(QStringLiteral("load-terminals-layout-1x2"), action); + action = new QAction(this); action->setText(i18nc("@action:inmenu", "Expand View")); action->setEnabled(false); @@ -134,6 +166,7 @@ void ViewManager::setupActions() action->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach"))); action->setText(i18nc("@action:inmenu", "Detach Current &View")); + connect(action, &QAction::triggered, this, &ViewManager::detachActiveView); _multiSplitterOnlyActions << action; @@ -895,6 +928,23 @@ QJsonObject saveSessionsRecurse(QSplitter *splitter) { } // namespace +void ViewManager::saveLayoutFile() { + QFile file(QFileDialog::getSaveFileName(this->widget(), i18n("Save File"), QStringLiteral("~/"), + i18n("Konsole View Layout (*.json)"))); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + KMessageBox::sorry(this->widget(), i18n("A problem occurred when saving the Layout.\n%1", file.fileName())); + } + + QJsonObject jsonSplit = saveSessionsRecurse(_viewContainer->activeViewSplitter()); + + if (!jsonSplit.isEmpty()){ + file.write(QJsonDocument(jsonSplit).toJson()); + qDebug() << "Maybe was saved"; + } +} + + void ViewManager::saveSessions(KConfigGroup &group) { QJsonArray rootArray; @@ -909,7 +959,7 @@ void ViewManager::saveSessions(KConfigGroup &group) namespace { -ViewSplitter *restoreSessionsSplitterRecurse(const QJsonObject& jsonSplitter, ViewManager *manager) +ViewSplitter *restoreSessionsSplitterRecurse(const QJsonObject& jsonSplitter, ViewManager *manager, bool useSessionId) { const QJsonArray splitterWidgets = jsonSplitter[QStringLiteral("Widgets")].toArray(); auto orientation = (jsonSplitter[QStringLiteral("Orientation")].toString() == QStringLiteral("Horizontal")) @@ -923,11 +973,14 @@ ViewSplitter *restoreSessionsSplitterRecurse(const QJsonObject& jsonSplitter, Vi const auto sessionIterator = widgetJsonObject.constFind(QStringLiteral("SessionRestoreId")); if (sessionIterator != widgetJsonObject.constEnd()) { - Session *session = SessionManager::instance()->idToSession(sessionIterator->toInt()); + Session *session = useSessionId + ? SessionManager::instance()->idToSession(sessionIterator->toInt()) + : SessionManager::instance()->createSession(); + auto newView = manager->createView(session); currentSplitter->addWidget(newView); } else { - auto nextSplitter = restoreSessionsSplitterRecurse(widgetJsonObject, manager); + auto nextSplitter = restoreSessionsSplitterRecurse(widgetJsonObject, manager, useSessionId); currentSplitter->addWidget(nextSplitter); } } @@ -935,12 +988,30 @@ ViewSplitter *restoreSessionsSplitterRecurse(const QJsonObject& jsonSplitter, Vi } } // namespace +void ViewManager::loadLayout(QString file) { + QFile jsonFile(file); + + if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + KMessageBox::sorry(this->widget(), i18n("A problem occurred when loading the Layout.\n%1", jsonFile.fileName())); + } + auto json = QJsonDocument::fromJson(jsonFile.readAll()); + if (!json.isEmpty()){ + auto splitter = restoreSessionsSplitterRecurse(json.object(), this, false); + _viewContainer->addSplitter(splitter, _viewContainer->count()); + } +} +void ViewManager::loadLayoutFile() { + + loadLayout(QFileDialog::getOpenFileName(this->widget(), i18n("Open File"), QStringLiteral("~/"), + i18n("Konsole View Layout (*.json)"))); +} + void ViewManager::restoreSessions(const KConfigGroup &group) { const auto tabList = group.readEntry("Tabs", QByteArray("[]")); const auto jsonTabs = QJsonDocument::fromJson(tabList).array(); for (const auto& jsonSplitter : jsonTabs) { - auto topLevelSplitter = restoreSessionsSplitterRecurse(jsonSplitter.toObject(), this); + auto topLevelSplitter = restoreSessionsSplitterRecurse(jsonSplitter.toObject(), this, true); _viewContainer->addSplitter(topLevelSplitter, _viewContainer->count()); } diff --git a/src/ViewManager.h b/src/ViewManager.h index cefa6eb2..f7cc3a09 100644 --- a/src/ViewManager.h +++ b/src/ViewManager.h @@ -307,6 +307,11 @@ public Q_SLOTS: /** DBus slot that sets ALL tabs' width to match their text */ Q_SCRIPTABLE void setTabWidthToText(bool); + // Creates json file with split config + Q_SCRIPTABLE void saveLayoutFile(); + Q_SCRIPTABLE void loadLayoutFile(); + Q_SCRIPTABLE void loadLayout(QString File); + private Q_SLOTS: // called when the "Split View Left/Right" menu item is selected void splitLeftRight();
