homescreens/folio: Add import/export homescreen layout setting

This commit is contained in:
Devin Lin 2023-10-22 10:17:09 -07:00
parent df80fb65fc
commit f1aff0b13e
10 changed files with 163 additions and 18 deletions

View file

@ -212,12 +212,8 @@ void FavouritesModel::deleteGhostEntry()
} }
} }
void FavouritesModel::save() QJsonArray FavouritesModel::exportToJson()
{ {
if (!m_applet) {
return;
}
QJsonArray arr; QJsonArray arr;
for (int i = 0; i < m_delegates.size(); i++) { for (int i = 0; i < m_delegates.size(); i++) {
FolioDelegate *delegate = m_delegates[i].delegate; FolioDelegate *delegate = m_delegates[i].delegate;
@ -229,6 +225,16 @@ void FavouritesModel::save()
arr.append(delegate->toJson()); arr.append(delegate->toJson());
} }
return arr;
}
void FavouritesModel::save()
{
if (!m_applet) {
return;
}
QJsonArray arr = exportToJson();
QByteArray data = QJsonDocument(arr).toJson(QJsonDocument::Compact); QByteArray data = QJsonDocument(arr).toJson(QJsonDocument::Compact);
m_applet->config().writeEntry("Favourites", QString::fromStdString(data.toStdString())); m_applet->config().writeEntry("Favourites", QString::fromStdString(data.toStdString()));
@ -242,12 +248,16 @@ void FavouritesModel::load()
} }
QJsonDocument doc = QJsonDocument::fromJson(m_applet->config().readEntry("Favourites", "{}").toUtf8()); QJsonDocument doc = QJsonDocument::fromJson(m_applet->config().readEntry("Favourites", "{}").toUtf8());
loadFromJson(doc.array());
}
void FavouritesModel::loadFromJson(QJsonArray arr)
{
beginResetModel(); beginResetModel();
m_delegates.clear(); m_delegates.clear();
for (QJsonValueRef r : doc.array()) { for (QJsonValueRef r : arr) {
QJsonObject obj = r.toObject(); QJsonObject obj = r.toObject();
FolioDelegate *delegate = FolioDelegate::fromJson(obj, this); FolioDelegate *delegate = FolioDelegate::fromJson(obj, this);

View file

@ -59,12 +59,14 @@ public:
QPointF getDelegateScreenPosition(int position) const; QPointF getDelegateScreenPosition(int position) const;
QJsonArray exportToJson();
void save();
Q_INVOKABLE void load(); Q_INVOKABLE void load();
void loadFromJson(QJsonArray arr);
void setApplet(Plasma::Applet *applet); void setApplet(Plasma::Applet *applet);
private: private:
void save();
void evaluateDelegatePositions(bool emitSignal = true); void evaluateDelegatePositions(bool emitSignal = true);
// get the x (or y) position where delegates start being placed // get the x (or y) position where delegates start being placed

View file

@ -265,6 +265,9 @@ void ApplicationFolderModel::removeDelegate(int index)
QPointF ApplicationFolderModel::getDelegatePosition(int index) QPointF ApplicationFolderModel::getDelegatePosition(int index)
{ {
if (index < 0 || index >= m_folder->m_delegates.size()) {
return {0, 0};
}
auto delegate = m_folder->m_delegates[index]; auto delegate = m_folder->m_delegates[index];
return {delegate.xPosition, delegate.yPosition}; return {delegate.xPosition, delegate.yPosition};
} }

View file

@ -2,6 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "foliosettings.h" #include "foliosettings.h"
#include "favouritesmodel.h"
#include "pagelistmodel.h"
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QTextStream>
FolioSettings::FolioSettings(QObject *parent) FolioSettings::FolioSettings(QObject *parent)
: QObject{parent} : QObject{parent}
@ -139,3 +146,56 @@ void FolioSettings::load()
Q_EMIT showFavouritesAppLabelsChanged(); Q_EMIT showFavouritesAppLabelsChanged();
Q_EMIT delegateIconSizeChanged(); Q_EMIT delegateIconSizeChanged();
} }
bool FolioSettings::saveLayoutToFile(QString path)
{
if (path.startsWith(QStringLiteral("file://"))) {
path = path.replace(QStringLiteral("file://"), QString());
}
QJsonArray favourites = FavouritesModel::self()->exportToJson();
QJsonArray pages = PageListModel::self()->exportToJson();
QJsonObject obj;
obj[QStringLiteral("Favourites")] = favourites;
obj[QStringLiteral("Pages")] = pages;
QByteArray data = QJsonDocument(obj).toJson(QJsonDocument::Compact);
QFile file{path};
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
file.write(data);
} else {
qDebug() << "failed to write to file:" << file.errorString();
return false;
}
file.close();
return true;
}
bool FolioSettings::loadLayoutFromFile(QString path)
{
if (path.startsWith(QStringLiteral("file://"))) {
path = path.replace(QStringLiteral("file://"), QString());
}
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "failed to open file:" << file.errorString();
return false;
}
QTextStream in(&file);
QString contents = in.readAll();
file.close();
QJsonDocument doc = QJsonDocument::fromJson(contents.toUtf8());
QJsonObject obj = doc.object();
// TODO error checking
FavouritesModel::self()->loadFromJson(obj[QStringLiteral("Favourites")].toArray());
PageListModel::self()->loadFromJson(obj[QStringLiteral("Pages")].toArray());
return true;
}

View file

@ -45,6 +45,9 @@ public:
Q_INVOKABLE void load(); Q_INVOKABLE void load();
Q_INVOKABLE bool saveLayoutToFile(QString path);
Q_INVOKABLE bool loadLayoutFromFile(QString path);
Q_INVOKABLE void setApplet(Plasma::Applet *applet); Q_INVOKABLE void setApplet(Plasma::Applet *applet);
Q_SIGNALS: Q_SIGNALS:

View file

@ -644,7 +644,7 @@ QPointF HomeScreenState::getFavouritesDelegateScreenPosition(int position)
QPointF HomeScreenState::getFolderDelegateScreenPosition(int position) QPointF HomeScreenState::getFolderDelegateScreenPosition(int position)
{ {
if (!m_currentFolder) { if (!m_currentFolder || position < 0 || position >= m_currentFolder->applications()->rowCount()) {
return {0, 0}; return {0, 0};
} }
auto pos = m_currentFolder->applications()->getDelegatePosition(position); auto pos = m_currentFolder->applications()->getDelegatePosition(position);

View file

@ -34,7 +34,7 @@ MobileShell.GridView {
cellWidth: effectiveContentWidth / Math.min(Math.floor(effectiveContentWidth / (Folio.FolioSettings.delegateIconSize + Kirigami.Units.largeSpacing * 3)), 8) cellWidth: effectiveContentWidth / Math.min(Math.floor(effectiveContentWidth / (Folio.FolioSettings.delegateIconSize + Kirigami.Units.largeSpacing * 3)), 8)
cellHeight: cellWidth + reservedSpaceForLabel cellHeight: cellWidth + reservedSpaceForLabel
boundsBehavior: Flickable.OvershootBounds boundsBehavior: Flickable.StopAtBounds
readonly property int columns: Math.floor(effectiveContentWidth / cellWidth) readonly property int columns: Math.floor(effectiveContentWidth / cellWidth)
readonly property int rows: Math.ceil(root.count / columns) readonly property int rows: Math.ceil(root.count / columns)

View file

@ -4,6 +4,7 @@
import QtQuick import QtQuick
import QtQuick.Window import QtQuick.Window
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Dialogs
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import org.kde.kirigami 2.20 as Kirigami import org.kde.kirigami 2.20 as Kirigami
@ -178,6 +179,7 @@ Kirigami.ApplicationWindow {
} }
FormCard.FormCard { FormCard.FormCard {
Layout.bottomMargin: Kirigami.Units.gridUnit
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
id: containmentSettings id: containmentSettings
text: i18n('Switch Homescreen') text: i18n('Switch Homescreen')
@ -189,20 +191,73 @@ Kirigami.ApplicationWindow {
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
id: exportSettings id: exportSettings
enabled: false text: i18n('Export layout')
text: 'Export layout (in development)'
icon.name: 'document-export' icon.name: 'document-export'
onClicked: exportFileDialog.open()
} }
FormCard.FormDelegateSeparator { above: exportSettings; below: importSettings } FormCard.FormDelegateSeparator { above: exportSettings; below: importSettings }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
id: importSettings id: importSettings
enabled: false text: i18n('Import layout')
text: 'Import layout (in development)'
icon.name: 'document-import' icon.name: 'document-import'
onClicked: importFileDialog.open()
}
}
}
FileDialog {
id: exportFileDialog
title: i18n("Export layout to")
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadsLocation)
fileMode: FileDialog.SaveFile
defaultSuffix: 'json'
nameFilters: ["JSON files (*.json)"]
onAccepted: {
console.log('saving layout to ' + selectedFile);
if (selectedFile) {
let status = Folio.FolioSettings.saveLayoutToFile(selectedFile);
if (status) {
exportedSuccessfullyPrompt.open();
} else {
exportFailedPrompt.open();
} }
} }
} }
} }
FileDialog {
id: importFileDialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadsLocation)
fileMode: FileDialog.OpenFile
nameFilters: ["JSON files (*.json)"]
onAccepted: {
console.log('about to load layout from ' + selectedFile);
confirmImportPrompt.open();
}
}
Kirigami.PromptDialog {
id: exportFailedPrompt
title: i18n("Export Status")
subtitle: i18n("Failed to export to %1", String(exportFileDialog.selectedFile).substring('file://'.length))
standardButtons: Kirigami.Dialog.Close
}
Kirigami.PromptDialog {
id: exportedSuccessfullyPrompt
title: i18n("Export Status")
subtitle: i18n("Homescreen layout exported successfully to %1", String(exportFileDialog.selectedFile).substring('file://'.length))
standardButtons: Kirigami.Dialog.Close
}
Kirigami.PromptDialog {
id: confirmImportPrompt
title: i18n("Confirm Import")
subtitle: i18n("This will overwrite your existing homescreen layout!")
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: Folio.FolioSettings.loadLayoutFromFile(importFileDialog.selectedFile);
}
}
} }

View file

@ -94,16 +94,22 @@ bool PageListModel::isLastPageEmpty()
return m_pages.size() == 0 ? true : m_pages[m_pages.size() - 1]->isPageEmpty(); return m_pages.size() == 0 ? true : m_pages[m_pages.size() - 1]->isPageEmpty();
} }
QJsonArray PageListModel::exportToJson()
{
QJsonArray arr;
for (auto &page : m_pages) {
arr.push_back(page->toJson());
}
return arr;
}
void PageListModel::save() void PageListModel::save()
{ {
if (!m_applet) { if (!m_applet) {
return; return;
} }
QJsonArray arr; QJsonArray arr = exportToJson();
for (auto &page : m_pages) {
arr.push_back(page->toJson());
}
QByteArray data = QJsonDocument(arr).toJson(QJsonDocument::Compact); QByteArray data = QJsonDocument(arr).toJson(QJsonDocument::Compact);
m_applet->config().writeEntry("Pages", QString::fromStdString(data.toStdString())); m_applet->config().writeEntry("Pages", QString::fromStdString(data.toStdString()));
@ -117,12 +123,16 @@ void PageListModel::load()
} }
QJsonDocument doc = QJsonDocument::fromJson(m_applet->config().readEntry("Pages", "{}").toUtf8()); QJsonDocument doc = QJsonDocument::fromJson(m_applet->config().readEntry("Pages", "{}").toUtf8());
loadFromJson(doc.array());
}
void PageListModel::loadFromJson(QJsonArray arr)
{
beginResetModel(); beginResetModel();
m_pages.clear(); m_pages.clear();
for (QJsonValueRef r : doc.array()) { for (QJsonValueRef r : arr) {
QJsonArray obj = r.toArray(); QJsonArray obj = r.toArray();
PageModel *page = PageModel::fromJson(obj, this); PageModel *page = PageModel::fromJson(obj, this);

View file

@ -33,8 +33,10 @@ public:
Q_INVOKABLE void addPageAtEnd(); Q_INVOKABLE void addPageAtEnd();
bool isLastPageEmpty(); bool isLastPageEmpty();
QJsonArray exportToJson();
void save(); void save();
Q_INVOKABLE void load(); Q_INVOKABLE void load();
void loadFromJson(QJsonArray arr);
void setApplet(Plasma::Applet *applet); void setApplet(Plasma::Applet *applet);