[utilities/konsole] /: Add ability to load/save session layouts(split views) and add default layouts

Kurt Hindenburg null at kde.org
Sat Apr 24 22:30:13 BST 2021


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();


More information about the kde-doc-english mailing list