Commit d8165a15 authored by Jan Blackquill's avatar Jan Blackquill 🌈 Committed by Carl Schwan
Browse files

Use QtSQL as backing data store; paginate loading and other goodies

parent 2c02045c
......@@ -18,7 +18,7 @@ include(ECMGenerateHeaders)
include(ECMPoQmTools)
################# Find dependencies #################
find_package(Qt5 REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg)
find_package(Qt5 REQUIRED COMPONENTS Core Gui Qml QuickControls2 Svg Sql)
find_package(KF5 REQUIRED COMPONENTS Kirigami2 I18n CoreAddons)
################# Enable C++11 features for clang and gcc #################
......
......@@ -12,6 +12,7 @@ target_link_libraries(kontrast
Qt5::Quick
Qt5::QuickControls2
Qt5::Svg
Qt5::Sql
KF5::I18n
KF5::CoreAddons
KF5::ConfigCore)
......
......@@ -17,11 +17,11 @@ Kirigami.ScrollablePage {
id: root
title: i18n("Saved colors")
ListView {
model: ColorStore
QtExtra.Clipboard {
id: clipboard
}
model: Kontrast.savedColors
spacing: Kirigami.Units.smallSpacing
delegate: Kirigami.AbstractListItem {
......@@ -92,8 +92,7 @@ Kirigami.ScrollablePage {
QQC2.Button {
text: i18n("Remove")
onClicked: {
console.log(model.index)
Kontrast.savedColors.removeColor(model.index)
ColorStore.removeColor(model.index)
}
}
}
......
......@@ -225,7 +225,9 @@ Kirigami.ScrollablePage {
QQC2.Button {
text: i18n("Save color")
icon.name: "favorite"
onClicked: Kontrast.savedColors.addColor(Kontrast.textColor, Kontrast.backgroundColor);
onClicked: if (!ColorStore.addColor("Lorem Ipsum", Kontrast.textColor, Kontrast.backgroundColor)) {
applicationWindow().showPassiveNotification(i18n("Failed to save color"))
}
}
}
}
......
......@@ -13,7 +13,6 @@
Kontrast::Kontrast(KAboutData about, QObject *parent)
: QObject(parent)
, m_about(about)
, m_savedColors(new SavedColorModel)
{
setObjectName("Kontrast");
}
......@@ -203,9 +202,4 @@ QColor Kontrast::displayTextColor() const
KAboutData Kontrast::about() const
{
return m_about;
}
SavedColorModel *Kontrast::savedColors() const
{
return m_savedColors;
}
}
\ No newline at end of file
......@@ -9,7 +9,6 @@
#include <QObject>
#include <QColor>
#include <KAboutData>
#include "savedcolormodel.h"
/**
* @brief Main class that expose all the value to the QML.
......@@ -39,8 +38,6 @@ class Kontrast : public QObject
Q_PROPERTY(QColor displayTextColor READ displayTextColor NOTIFY contrastChanged);
Q_PROPERTY(KAboutData about READ about);
Q_PROPERTY(SavedColorModel *savedColors READ savedColors);
public:
Kontrast(KAboutData about, QObject *parent = nullptr);
......@@ -76,8 +73,6 @@ public:
KAboutData about() const;
SavedColorModel *savedColors() const;
Q_INVOKABLE void random();
Q_INVOKABLE void reverse();
......@@ -90,5 +85,4 @@ private:
QColor m_textColor;
QColor m_backgroundColor;
KAboutData m_about;
SavedColorModel *m_savedColors;
};
......@@ -7,11 +7,13 @@
#include <KAboutData>
#include <KLocalizedString>
#include <kontrast.h>
#include "savedcolormodel.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
app.setApplicationName("kontrast");
KAboutData aboutData("kontrast", i18n("Kontrast"), "1.0",
i18n("A contrast checked application"),
......@@ -26,6 +28,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
QQmlApplicationEngine engine;
qmlRegisterSingletonInstance("org.kde.kontrast.private", 1, 0, "Kontrast", kontrast.get());
qmlRegisterSingletonInstance("org.kde.kontrast.private", 1, 0, "ColorStore", new SavedColorModel(qApp));
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
......
......@@ -6,25 +6,101 @@
#include "savedcolormodel.h"
#include <QCoreApplication>
#include <QColor>
#include <KSharedConfig>
#include <KConfigGroup>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QSqlError>
const QString DRIVER("QSQLITE");
SavedColorModel::SavedColorModel(QObject *parent)
: QAbstractListModel(parent)
{
m_textColor.append(QColor("black"));
m_backgroundColor.append(QColor("white"));
KSharedConfigPtr config = KSharedConfig::openConfig("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
KConfigGroup generalGroup(config, "General");
Q_ASSERT(QSqlDatabase::isDriverAvailable(DRIVER));
Q_ASSERT(QDir().mkpath(QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation))));
m_db = QSqlDatabase::addDatabase(DRIVER);
const auto path = QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + qApp->applicationName());
m_db.setDatabaseName(path);
if (!m_db.open()) {
qCritical() << m_db.lastError() << "while opening database at" << path;
}
const auto statement = QStringLiteral(R"RJIENRLWEY(
CREATE TABLE IF NOT EXISTS SavedColorModel (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
ForegroundColor BLOB NOT NULL,
BackgroundColor BLOB NOT NULL
)
)RJIENRLWEY");
auto query = QSqlQuery();
query.prepare(statement);
auto ok = query.exec();
if (!ok) {
qCritical() << query.lastError() << "while creating table";
}
m_query = QSqlQuery(m_db);
ok = m_query.prepare("SELECT * FROM SavedColorModel");
if (!ok) {
qCritical() << m_query.lastError() << "while preparing query";
}
ok = m_query.exec();
if (!ok) {
qCritical() << m_query.lastError() << "while executing query";
}
prefetch(fetch_size);
}
auto SavedColorModel::prefetch(int toRow, bool reset) -> void
{
if (m_atEnd || toRow <= m_bottom)
return;
int oldBottom = m_bottom;
int newBottom = 0;
if (generalGroup.exists()) {
m_textColor = generalGroup.readEntry("backgroundColors", QVariantList());
m_backgroundColor = generalGroup.readEntry("textColors", QVariantList());
if (m_query.seek(toRow)) {
newBottom = toRow;
} else {
int i = oldBottom;
if (m_query.seek(i)) {
while (m_query.next()) i++;
newBottom = i;
} else {
newBottom = -1;
}
m_atEnd = true;
}
if (newBottom >= 0 && newBottom >= oldBottom) {
if (!reset) beginInsertRows(QModelIndex(), oldBottom + 1, newBottom);
m_bottom = newBottom;
if (!reset) endInsertRows();
}
}
auto SavedColorModel::refresh() -> void
{
beginResetModel();
m_bottom = 0;
m_atEnd = false;
m_query.seek(0);
m_query.prepare(m_query.executedQuery());
m_query.exec();
prefetch(m_bottom + fetch_size, true);
endResetModel();
}
auto SavedColorModel::fetchMore(const QModelIndex &parent) -> void {
Q_UNUSED(parent)
prefetch(m_bottom + fetch_size);
}
auto SavedColorModel::canFetchMore(const QModelIndex &parent) const -> bool {
Q_UNUSED(parent)
return !m_atEnd;
}
QHash<int, QByteArray> SavedColorModel::roleNames() const
{
QHash<int, QByteArray> roles;
......@@ -35,48 +111,106 @@ QHash<int, QByteArray> SavedColorModel::roleNames() const
QVariant SavedColorModel::data(const QModelIndex &index, int role) const
{
if (role == TextColor) {
return m_textColor[index.row()];
} else if (role == BackgroundColor) {
return m_backgroundColor[index.row()];
if (!index.isValid()) return QVariant();
if (!m_query.seek(index.row())) {
qCritical() << m_query.lastError() << "when seeking data";
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return m_query.value("Name");
case TextColor:
return m_query.value("ForegroundColor");
case BackgroundColor:
return m_query.value("BackgroundColor");
}
return QVariant();
}
int SavedColorModel::rowCount(const QModelIndex& parent) const
auto SavedColorModel::setData(const QModelIndex &idx, const QVariant &value, int role) -> bool
{
Q_UNUSED(parent);
qDebug() << m_textColor.count();
return m_textColor.count();
if (!idx.isValid()) return false;
if (!m_query.seek(idx.row())) {
qCritical() << m_query.lastError() << "when seeking data";
return false;
}
const auto statement = QStringLiteral("UPDATE SavedColorModel SET %1 = :value WHERE ID = :id");
QString mutated;
switch (role) {
case Qt::DisplayRole:
mutated = statement.arg("Name"); break;
case TextColor:
mutated = statement.arg("ForegroundColor"); break;
case BackgroundColor:
mutated = statement.arg("BackgroundColor"); break;
default:
return false;
}
QSqlQuery query;
query.prepare(mutated);
query.bindValue(":value", value);
query.bindValue(":id", m_query.value("ID"));
auto ok = query.exec();
if (!ok) {
qCritical() << m_query.lastError() << "when updating data";
return false;
}
refresh();
return true;
}
void SavedColorModel::addColor(QColor textColor, QColor backgroundColor)
int SavedColorModel::rowCount(const QModelIndex& parent) const
{
beginInsertRows(QModelIndex(), 0, 0);
m_textColor.prepend(textColor);
m_backgroundColor.prepend(backgroundColor);
endInsertRows();
saveColors();
Q_UNUSED(parent);
return m_bottom;
}
void SavedColorModel::removeColor(int index)
bool SavedColorModel::addColor(const QString& name, const QColor& foreground, const QColor& background)
{
beginRemoveRows(QModelIndex(), index, index);
m_textColor.removeAt(index);
m_backgroundColor.removeAt(index);
endRemoveRows();
saveColors();
}
const auto statement = QStringLiteral(R"RJIENRLWEY(
INSERT INTO SavedColorModel
(Name, ForegroundColor, BackgroundColor)
VALUES
( :name, :foreground, :background )
)RJIENRLWEY");
QSqlQuery query;
query.prepare(statement);
query.bindValue(":name", name);
query.bindValue(":foreground", foreground);
query.bindValue(":background", background);
auto ok = query.exec();
if (!ok) {
qCritical() << m_query.lastError() << "when inserting data";
return false;
}
prefetch(m_bottom + fetch_size);
return true;
}
void SavedColorModel::saveColors()
bool SavedColorModel::removeColor(int index)
{
KSharedConfigPtr config = KSharedConfig::openConfig("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
KConfigGroup generalGroup(config, "General");
generalGroup.writeEntry("backgroundColors", m_textColor);
generalGroup.writeEntry("textColors", m_backgroundColor);
config->sync();
}
const auto statement = QStringLiteral(R"RJIENRLWEY(
DELETE FROM SavedColorModel
WHERE ID = :id
)RJIENRLWEY");
QSqlQuery query;
query.prepare(statement);
query.bindValue(":id", index);
auto ok = query.exec();
if (!ok) {
qCritical() << m_query.lastError() << "when deleting data";
return false;
}
refresh();
return true;
}
\ No newline at end of file
/*
* SPDX-FileCopyrightText: (C) 2020 Carl Schwan <carl@carlschwan.eu>
* SPDX-FileCopyrightText: (C) 2020 Carson Black <uhhadd@gmail.com>
*
* SPDX-LicenseRef: AGPL-3.0-or-later
*/
......@@ -8,6 +9,8 @@
#include <QAbstractListModel>
#include <QColor>
#include <QSqlDatabase>
#include <QSqlQuery>
struct ColorCombination {
QColor textColor;
......@@ -37,32 +40,28 @@ public:
SavedColorModel(QObject *parent = nullptr);
virtual ~SavedColorModel() override = default;
QHash<int, QByteArray> roleNames() const override;
/**
* Value of the saved colors.
*
* @param index The index of the data.
* @param role The item role, can be TextColor or BackgroundColor
* @return The color for the given role.
*/
virtual QVariant data(const QModelIndex &index, int role) const override;
/**
* @brief Length of the saved colors.
*
* @param parent
* @return The lenght of the saved colors.
*/
virtual int rowCount(const QModelIndex &parent) const override;
Q_INVOKABLE void addColor(QColor textColor, QColor backgroundColor);
Q_INVOKABLE void removeColor(int index);
Q_INVOKABLE bool addColor(const QString& name, const QColor& foreground, const QColor& background);
Q_INVOKABLE bool removeColor(int index);
void fetchMore(const QModelIndex &parent) override;
bool canFetchMore(const QModelIndex &parent) const override;
bool setData(const QModelIndex &item, const QVariant &value, int role = Qt::EditRole) override;
private:
void prefetch(int count, bool reset = false);
void saveColors();
QList<QVariant> m_textColor;
QList<QVariant> m_backgroundColor;
void refresh();
mutable QSqlQuery m_query;
static const int fetch_size = 255;
int m_rowCount = 0;
int m_bottom = 0;
bool m_atEnd = false;
QSqlDatabase m_db;
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment