添加设置UI

This commit is contained in:
RigoLigoRLC 2022-01-27 21:41:33 +08:00
parent 02530de7da
commit aa22cd7ff2
22 changed files with 943 additions and 43 deletions

@ -16,7 +16,7 @@ set(SPARK_FORCE_TELEMETRY OFF)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
#set(CMAKE_AUTOUIC ON)
set(QT_VERSION 5)
set(REQUIRED_LIBS
@ -49,6 +49,9 @@ set(SOURCE_FILES
resource/resource.qrc
gui/page/ui/settings.ui
${WRAPPED_UI_FILES}
inc/gitver.h
inc/deepinplatform.h
inc/dtk/spkdtkplugin.h
@ -79,12 +82,14 @@ set(SOURCE_FILES
inc/page/spkpageapplist.h gui/page/spkpageapplist.cpp
inc/page/spkpageappdetails.h gui/page/spkpageappdetails.cpp
inc/page/spkpagedownloads.h gui/page/spkpagedownloads.cpp
inc/page/spkpagesettings.h gui/page/spkpagesettings.cpp
inc/spkstore.h src/spkstore.cpp
inc/spkuimsg.h src/spkuimsg.cpp
inc/spklogging.h src/spklogging.cpp
inc/spkresource.h src/spkresource.cpp
inc/spkdownload.h src/spkdownload.cpp
inc/spkconfig.h src/spkconfig.cpp
)
include(cmake/FindLibNotify.cmake)
@ -107,6 +112,9 @@ add_link_options(-rdynamic)
# Find Qt before adding other build targets
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
QT5_WRAP_UI(WRAPPED_UI_FILES
gui/page/ui/settings.ui)
add_executable(${EXECUTABLE_NAME} ${SOURCE_FILES})
if(SPARK_BUILD_DTK_PLUGIN)

@ -0,0 +1,62 @@
#include "spkutils.h"
#include "page/spkpagesettings.h"
namespace SpkUi
{
SpkPageSettings::SpkPageSettings(QWidget *parent) :
SpkPageBase(parent)
{
mMainArea = new QScrollArea();
mMainLay = new QVBoxLayout(this);
mSettingsWidget = new QWidget(this);
mSettingsUi = new Ui::SpkUiSettings;
mSettingsUi->setupUi(mSettingsWidget);
mMainLay->addWidget(mMainArea);
mMainArea->setWidget(mSettingsWidget);
mMainArea->setWidgetResizable(true);
SetupUi();
}
SpkPageSettings::~SpkPageSettings()
{
delete mSettingsWidget;
}
void SpkPageSettings::SetupUi()
{
auto ui = mSettingsUi;
ui->lblSettingsTitle->setObjectName("styConfTitle");
ui->lblCleanup->setObjectName("styConfTitle");
ui->lblAdvanced->setObjectName("styConfTitle");
}
void SpkPageSettings::ReadConfiguration()
{
auto ui = mSettingsUi;
ui->spnConcurrentResDownloads->setValue(CFG->ReadField("resource/concurrent", 5).toInt());
ui->edtApiUrl->setText(CFG->ReadField("url/api", "").toString());
ui->edtResourceUrl->setText(CFG->ReadField("url/res", "").toString());
ui->edtResourceCachePath->setText(CFG->ReadField("dirs/cache", "").toString());
ui->edtDownloadPath->setText(CFG->ReadField("dirs/download", "").toString());
ui->edtDownloadServers->setPlainText(CFG->ReadField("download/servers", "").toString());
ui->edtQssPath->setText(CFG->ReadField("internal/qss_path", "").toString());
}
void SpkPageSettings::SaveConfiguration()
{
}
void SpkPageSettings::Activated()
{
ReadConfiguration();
}
}

430
gui/page/ui/settings.ui Normal file

@ -0,0 +1,430 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpkUiSettings</class>
<widget class="QWidget" name="SpkUiSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>501</width>
<height>699</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblSettingsTitle">
<property name="text">
<string>Spark Store Settings</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="lineTitle">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblRestartHint">
<property name="text">
<string>Configuration entries marked &quot;*&quot; will only take effect after restarting Spark Store.</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="laySettings">
<item row="3" column="1">
<widget class="QPlainTextEdit" name="edtDownloadServers">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>80</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>150</height>
</size>
</property>
<property name="baseSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="spkcfg_key" stdset="0">
<string notr="true">download/servers</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblLightDarkTheme">
<property name="text">
<string>Light/dark theme</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="edtDownloadPath">
<property name="spkcfg_key" stdset="0">
<string notr="true">dirs/download</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cmbLightDarkTheme">
<property name="frame">
<bool>true</bool>
</property>
<property name="spkcfg_key" stdset="0">
<string notr="true">gui/theme</string>
</property>
<item>
<property name="text">
<string>Auto</string>
</property>
</item>
<item>
<property name="text">
<string>Always Light</string>
</property>
</item>
<item>
<property name="text">
<string>Always Dark</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="lblApiUrl">
<property name="text">
<string>Store API URL</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblAptRepo">
<property name="text">
<string>APT Repository</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="edtApiUrl">
<property name="spkcfg_key" stdset="0">
<string>url/api</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="lblDownloadServers">
<property name="toolTip">
<string>Server addresses are separated with two semicolons (;;).</string>
</property>
<property name="toolTipDuration">
<number>1</number>
</property>
<property name="text">
<string>Download servers</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="edtResourceCachePath">
<property name="spkcfg_key" stdset="0">
<string>dirs/cache</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lblResourceCachePath">
<property name="text">
<string>Resource cache path*</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblDownloadPath">
<property name="toolTip">
<string/>
</property>
<property name="toolTipDuration">
<number>-1</number>
</property>
<property name="text">
<string>Download path</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="lblResourceUrl">
<property name="text">
<string>Store resource URL</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="edtResourceUrl">
<property name="spkcfg_key" stdset="0">
<string>url/res</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="layAptRepo">
<property name="spacing">
<number>4</number>
</property>
<item>
<widget class="QComboBox" name="cmbAptRepo">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="spkcfg_key" stdset="0">
<string notr="true">pkgmgr/apt_repo</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnFetchAptRepo">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Fetch all</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnApplyAptRepo">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblHomeNotice">
<property name="text">
<string>Note: character &quot;*&quot; in paths are replaced with the current user's home path.</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCleanup">
<property name="text">
<string>Cache and Downloads cleanup</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCleanupDescription">
<property name="text">
<string>Spark Store caches resources such as app icons and screenshots locally. If you want to free up space, you can clear them here or delete them manually.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="layCleanup">
<item row="1" column="3">
<widget class="QPushButton" name="btnCleanDownloadedContent">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnViewResourceCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>View</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnViewDownloadedContent">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>View</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblDownloadedContent">
<property name="text">
<string>Downloaded content</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblResourceCache">
<property name="text">
<string>Resource cache</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="btnCleanResourceCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblSizeResourceCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblSizeDownloadedContent">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblAdvanced">
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblAdvancedDescription">
<property name="text">
<string>Advanced settings are low-level configurations that can affect usability and are not meant to be modified by average users.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="layAdvanced">
<item row="0" column="0">
<widget class="QLabel" name="lblConcurrentResDownloads">
<property name="text">
<string>Concurrent resource downloads*</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spnConcurrentResDownloads">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="spkcfg_key" stdset="0">
<string>resource/concurrent</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblQssPath">
<property name="text">
<string>Default Qt Style Sheet template*</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="edtQssPath">
<property name="spkcfg_key" stdset="0">
<string>internal/qss_path</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

@ -425,7 +425,7 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
BtnSettings->setCheckable(true);
BtnSettings->setFixedSize({ 40, 40 });
BtnSettings->SetIcon(QIcon(":/icons/settings.svg"), QSize(20, 20));
BtnSettings->setProperty("spk_pageno", 0);
BtnSettings->setProperty("spk_pageno", PgSettings);
SidebarMgr->BindPageSwitcherButton(BtnSettings);
BtnDayNight = new SpkIconButton(this);
@ -562,6 +562,10 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
PageDownloads->setProperty("spk_pageid", SpkStackedPages::PgDownloads);
sorter[PgDownloads] = PageDownloads;
PageSettings = new SpkUi::SpkPageSettings(this);
PageSettings->setProperty("spk_pageid", SpkStackedPages::PgSettings);
sorter[PgSettings] = PageSettings;
#ifndef NDEBUG // If only in debug mode should we initialize QSS test page
PageQssTest = new SpkUi::SpkPageUiTest(this);
PageQssTest->setProperty("spk_pageid", SpkStackedPages::PgQssTest);

@ -21,6 +21,7 @@
#include "spkpopup.h"
#include "spklogging.h"
#include "spkstore.h"
#include "spkutils.h"
namespace SpkUi
{
@ -60,7 +61,9 @@ namespace SpkUi
{
// Obtain global stylesheets
QFile ObtainStylesheet;
ObtainStylesheet.setFileName(":/stylesheets/stylesheets/mainwindow_dark.css");
ObtainStylesheet.setFileName(CFG->ReadField("internal/qss_path",
":/stylesheet/stylesheet/default.css")
.toString());
ObtainStylesheet.open(QIODevice::ReadOnly);
StylesheetBase = ObtainStylesheet.readAll();
ObtainStylesheet.close();
@ -70,7 +73,7 @@ namespace SpkUi
#ifdef NDEBUG
SetGlobalStyle(Light, false);
#else
SetGlobalStyle(qgetenv("SPK_FORCE_DARK").toInt() ? Dark : Light, false);
SetGlobalStyle(qEnvironmentVariableIntValue("SPK_FORCE_DARK") ? Dark : Light, false);
#endif
// Initalize crash handler
@ -111,7 +114,7 @@ namespace SpkUi
qApp->addLibraryPath("/usr/local/lib");
qApp->addLibraryPath("/usr/lib");
#endif
if(!qgetenv("SPARK_NO_DTK_PLUGIN").toInt())
if(!qEnvironmentVariableIntValue("SPARK_NO_DTK_PLUGIN"))
{
QPluginLoader p("libspkdtkplugin");
if(p.load())
@ -121,24 +124,24 @@ namespace SpkUi
{
DtkPlugin = i;
States::IsUsingDtkPlugin = true;
i->Initialize();
SpkUiMetaObject.SetAccentColor(i->GetAccentColor()); // Match OS accent color
SpkUiMetaObject.SetDarkLightTheme(i->GetIsDarkTheme()); // Match OS dark theme type
QObject::connect(i, &SpkDtkPlugin::AccentColorChanged,
&SpkUiMetaObject, &UiMetaObject::SetAccentColor);
QObject::connect(i, &SpkDtkPlugin::DarkLightThemeChanged,
&SpkUiMetaObject, &UiMetaObject::SetDarkLightTheme);
}
i->Initialize();
SpkUiMetaObject.SetAccentColor(i->GetAccentColor()); // Match OS accent color
SpkUiMetaObject.SetDarkLightTheme(i->GetIsDarkTheme()); // Match OS dark theme type
QObject::connect(i, &SpkDtkPlugin::AccentColorChanged,
&SpkUiMetaObject, &UiMetaObject::SetAccentColor);
QObject::connect(i, &SpkDtkPlugin::DarkLightThemeChanged,
&SpkUiMetaObject, &UiMetaObject::SetDarkLightTheme);
}
}
// NOTE: Chameleon style kept adding unwanted blue focus indication border
// to widgets that shouldn't have borders.
// We need to eliminate this irritating problem.
if(qgetenv("SPARK_NO_QSTYLE_CHANGE").toInt())
if(qEnvironmentVariableIntValue("SPARK_NO_QSTYLE_CHANGE"))
return;
OldSystemStyle = QStyleFactory::create("chameleon"); // TreeWidget doesn't work well with Fusion
auto styles = QStyleFactory::keys();

@ -0,0 +1,32 @@
#pragma once
#include <QScrollArea>
#include <QVBoxLayout>
#include "page/spkpagebase.h"
#include "ui_settings.h"
namespace SpkUi
{
class SpkPageSettings : public SpkPageBase
{
Q_OBJECT
public:
SpkPageSettings(QWidget *parent = nullptr);
~SpkPageSettings();
void SetupUi();
void ReadConfiguration();
void SaveConfiguration();
virtual void Activated() override;
private:
QScrollArea *mMainArea;
QVBoxLayout *mMainLay;
QWidget *mSettingsWidget;
Ui::SpkUiSettings *mSettingsUi;
};
}

49
inc/spkconfig.h Normal file

@ -0,0 +1,49 @@
#ifndef SPKCONFIG_H
#define SPKCONFIG_H
#include <QSettings>
#include <QHash>
#include <QPair>
#include <functional>
class SpkConfig : public QObject
{
Q_OBJECT
public:
SpkConfig(QObject *parent, QString configPath);
~SpkConfig();
/**
* @brief BindField If a variable is bound to the specified key, then future chanegs via SetField
* will modify the provided variable. A callback can also be specified to make
* sure the chanegs are acceptable.
* @param key
* @param value A pointer to the variable to be bound
* @param defaultValue
* @param callback When SetField is called to modify this specific key associated with a callback,
* the callback is called. If the callback returned false then the original value
* is restored to the value target, and changes won't be saved in mSettings, and
* SetField will return false too. It is used to ensure if the target can accept
* the changes.
* @return false when the key is already bound.
*/
bool BindField(QString key, QString* value, QString defaultValue, std::function<bool(void)> callback = nullptr);
bool BindField(QString key, int* value, int defaultValue, std::function<bool(void)> callback = nullptr);
bool SetField(QString key, QString value);
bool SetField(QString key, int value);
// Wrapper of QSettings::value, used for "read once on startup" configurations
QVariant ReadField(QString key, QVariant defaultValue);
// Wrapper of QSettings::setValue, used for "set and restart to take effect" configurations
void SetSettings(QString key, QVariant value);
private:
QSettings mSettings;
QHash<QString, QPair<QString*, std::function<bool(void)>>> mStringBindMap;
QHash<QString, QPair<int*,std::function<bool(void)>>> mIntBindMap;
};
#endif // SPKCONFIG_H

@ -72,9 +72,11 @@ class SpkDownloadMgr : public QObject
QTimer mProgressEmitterTimer;
qint64 mDownloadedBytes;
int mCurrentDownloadId;
int mCurrentDownloadId; ///< Indicates download status. -1 means no download going on.
int mActiveWorkerCount;
QString mBulkServerPaths; ///< Config string, modification are taken care of by callback.
public slots:
void SetDestinationFolder(QString path);
@ -100,6 +102,8 @@ class SpkDownloadMgr : public QObject
void TryScheduleFailureRetries();
void TryScheduleFailureRetries(int i); ///< Try schedule on a specific task slot.
bool ServerAddressesChangedCallback(); ///< Called by SpkConfig upon address changing
signals:
void DownloadProgressed(qint64 bytes, qint64 total, int id);
void DownloadStopped(TaskResult status, int id);

4
inc/spkfillwidget.h Normal file

@ -0,0 +1,4 @@
#ifndef SPKFILLWIDGET_H
#define SPKFILLWIDGET_H
#endif // SPKFILLWIDGET_H

@ -20,6 +20,7 @@
#include "page/spkpageapplist.h"
#include "page/spkpageappdetails.h"
#include "page/spkpagedownloads.h"
#include "page/spkpagesettings.h"
class QNetworkReply;
@ -31,6 +32,7 @@ namespace SpkUi
PgAppList,
PgAppDetails,
PgDownloads,
PgSettings,
PgQssTest // Must be at last
};
@ -177,6 +179,7 @@ namespace SpkUi
SpkPageAppList *PageAppList;
SpkPageAppDetails *PageAppDetails;
SpkPageDownloads *PageDownloads;
SpkPageSettings *PageSettings;
};
}

@ -9,6 +9,7 @@
#include "spklogging.h"
#include "spkresource.h"
#include "spkconfig.h"
class SpkMainWindow;
@ -23,7 +24,7 @@ class SpkStore : public QObject
Q_OBJECT
public:
static SpkStore *Instance;
QSettings *mCfg;
SpkConfig *mCfg;
SpkStore(bool aCli, QString &aLogPath);
~SpkStore();

@ -28,4 +28,6 @@ namespace SpkUtils
QString CutPath(QString);
QString BytesToSize(size_t s, int prec = 2);
bool EnsureDirExists(QString path);
void FillWidget(QWidget* widget, QVariant val);
}

@ -4,11 +4,24 @@ api=https://store.deepinos.org/api/
res=http://img.store.deepinos.org.cn/
[dirs]
cache=%1/.cache/spark-store/res/
download="%1/.local/spark-store/downloads/"
cache="*/.cache/spark-store/res/"
download="*/.local/spark-store/downloads/"
[download]
servers=https://d1.store.deepinos.org.cn/;;https://d2.store.deepinos.org.cn/;;https://d3.store.deepinos.org.cn/;;https://d4.store.deepinos.org.cn/;;https://d5.store.deepinos.org.cn/
servers="https://d1.store.deepinos.org.cn/;;https://d2.store.deepinos.org.cn/;;https://d3.store.deepinos.org.cn/;;https://d4.store.deepinos.org.cn/;;https://d5.store.deepinos.org.cn/"
[resource]
concurrent=5
[misc]
privacy_warned=0
distro=none
[gui]
theme=light
[pkgmgr]
apt_repo=""
[internal]
qss_path=":/stylesheets/stylesheets/default.css"

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="15"
height="10"
viewBox="0 0 3.9687499 2.6458334"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
sodipodi:docname="down-small.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
units="px"
inkscape:zoom="13.001718"
inkscape:cx="2.268931"
inkscape:cy="17.036211"
inkscape:window-width="1440"
inkscape:window-height="830"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
width="8px">
<inkscape:grid
type="xygrid"
id="grid136" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:0.39;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 0.55647555,0.79375 2.0043655,2.0149264 3.4522556,0.79375"
id="path138"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

(image error) Size: 1.6 KiB

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="15"
height="10"
viewBox="0 0 3.9687499 2.6458334"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
sodipodi:docname="up-small.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
units="px"
inkscape:zoom="13.001718"
inkscape:cx="2.268931"
inkscape:cy="14.267346"
inkscape:window-width="1440"
inkscape:window-height="830"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
width="8px">
<inkscape:grid
type="xygrid"
id="grid136" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;fill-opacity:1;stroke:#808080;stroke-width:0.39;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 3.4522556,1.9581625 2.0043656,0.73698623 0.55647554,1.9581625"
id="path138"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

(image error) Size: 1.6 KiB

@ -1,6 +1,6 @@
<RCC>
<qresource prefix="/stylesheets">
<file>stylesheets/mainwindow_dark.css</file>
<file>stylesheets/default.css</file>
</qresource>
<qresource prefix="/info">
<file>lipsum.txt</file>
@ -17,5 +17,7 @@
<file>icons/daynight.svg</file>
<file>icons/loading-icon.svg</file>
<file>icons/back.svg</file>
<file>icons/down-small.svg</file>
<file>icons/up-small.svg</file>
</qresource>
</RCC>

@ -39,7 +39,7 @@ QScrollArea
border-radius: 5px;
}
QLineEdit, QTextEdit
QLineEdit, QTextEdit, QComboBox, QPlainTextEdit, QSpinBox
{
padding: 2px;
border-radius: 7px;
@ -48,9 +48,60 @@ QLineEdit, QTextEdit
selection-color: TXACC;
selection-background-color: ACC_;
}
QLineEdit /*We can't apply gradient to TextEdit cuz it messes with scroll bar*/
QLineEdit, QComboBox /*We can't apply gradient to TextEdit cuz it messes with scroll bar*/
{
background:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 CBG_, stop:1 GBG_);
/*background:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 DCTL1, stop:1 DCTL3);*/
/*Gradient doesn't work, LCTL/DCTL isn't adopting theme changes.
To see effects, try it yourself*/
}
QComboBox::drop-down
{
subcontrol-origin: padding;
subcontrol-position: top right;
width: 30px;
border: none;
}
QSpinBox::up-button
{
subcontrol-origin: border;
subcontrol-position: top right;
width: 25px;
height: 15px;
border: none;
}
QSpinBox::down-button
{
subcontrol-origin: border;
subcontrol-position: bottom right;
width: 25px;
height: 15px;
border: none;
}
QComboBox::down-arrow
{
image: url(:/icons/down-small.svg)
}
QComboBox QAbstractItemView
{
border: 1px solid SHD;
border-radius: 7px;
selection-background-color: ACCH;
}
QSpinBox::up-arrow
{
image: url(:/icons/up-small.svg);
}
QSpinBox::down-arrow
{
image: url(:/icons/down-small.svg);
}
QPushButton
@ -150,6 +201,12 @@ SpkAppItem::hover
font-size: 36px;
}
#styConfTitle
{
font-weight: 400;
font-size: 28px;
}
#styAppItmDesc
{
color: TXL1;

99
src/spkconfig.cpp Normal file

@ -0,0 +1,99 @@
#include "spkconfig.h"
SpkConfig::SpkConfig(QObject *parent, QString configPath) :
QObject(parent),
mSettings(configPath, QSettings::IniFormat, this)
{
}
SpkConfig::~SpkConfig()
{
// TODO
}
bool SpkConfig::BindField(QString key, QString *value, QString defaultValue, std::function<bool(void)> callback)
{
if(mStringBindMap.contains(key))
return false;
*value = mSettings.value(key, defaultValue).toString();
mStringBindMap[key] = QPair<QString*, std::function<bool(void)>>(value, callback);
return true;
}
bool SpkConfig::BindField(QString key, int *value, int defaultValue, std::function<bool(void)> callback)
{
if(mIntBindMap.contains(key))
return false;
*value = mSettings.value(key, defaultValue).toInt(); // Read inital value
mIntBindMap[key] = QPair<int*, std::function<bool(void)>>(value, callback);
return true;
}
bool SpkConfig::SetField(QString key, QString value)
{
auto it = mStringBindMap.find(key);
if(it == mStringBindMap.end())
return false;
else
{
if(it->second) // Has a valid callback?
{
QString originalValue = *it->first; // Backup first in case of a failure
*it->first = value; // Set the target
if(!it->second()) // Failure, restore and give up
{
*it->first = originalValue;
return false;
}
else // Success, set the mSettings and continue
{
mSettings.setValue(key, value);
return true;
}
}
// No valid callback, normal operations
*it->first = value;
mSettings.setValue(key, value);
return true;
}
}
bool SpkConfig::SetField(QString key, int value)
{
auto it = mIntBindMap.find(key);
if(it == mIntBindMap.end())
return false;
else
{
if(it->second) // Has a valid callback?
{
int originalValue = *it->first; // Backup first in case of a failure
*it->first = value; // Set the target
if(!it->second()) // Failure, restore and give up
{
*it->first = originalValue;
return false;
}
else // Success, set the mSettings and continue
{
mSettings.setValue(key, value);
return true;
}
}
// No valid callback, normal operations
*it->first = value;
mSettings.setValue(key, value);
return true;
}
}
QVariant SpkConfig::ReadField(QString key, QVariant defaultValue)
{
return mSettings.value(key, defaultValue);
}

@ -8,20 +8,25 @@
SpkDownloadMgr::SpkDownloadMgr(QObject *parent)
{
mDestFolder = CFG->value("dirs/download", "%1/.local/spark-store/downloads")
.toString().arg(QDir::homePath());
CFG->BindField("dirs/download", &mDestFolder,
QString("*/.local/spark-store/downloads"));
mDestFolder.replace('*', QDir::homePath());
QDir dest(mDestFolder);
if(!dest.exists())
QDir().mkdir(mDestFolder);
// Distribution servers
QString srvPaths = CFG->value("download/servers", "https://d1.store.deepinos.org.cn/;;"
"https://d2.store.deepinos.org.cn/;;"
"https://d3.store.deepinos.org.cn/;;"
"https://d4.store.deepinos.org.cn/;;"
"https://d5.store.deepinos.org.cn/").toString();
mServers = srvPaths.split(";;");
CFG->BindField("download/servers", &mBulkServerPaths,
"https://d1.store.deepinos.org.cn/;;"
"https://d2.store.deepinos.org.cn/;;"
"https://d3.store.deepinos.org.cn/;;"
"https://d4.store.deepinos.org.cn/;;"
"https://d5.store.deepinos.org.cn/",
std::bind(&SpkDownloadMgr::ServerAddressesChangedCallback, this));
mServers = mBulkServerPaths.split(";;");
mCurrentDownloadId = -1;
mActiveWorkerCount = 0;
@ -333,3 +338,13 @@ void SpkDownloadMgr::TryScheduleFailureRetries(int i)
LinkReplyWithMe(mScheduledWorkers[i].Reply);
}
}
bool SpkDownloadMgr::ServerAddressesChangedCallback()
{
if(mCurrentDownloadId != -1)
return false;
// URL format verification *is done in the GUI*, we jsut have to split it here
mServers = mBulkServerPaths.split(";;");
return true;
}

@ -12,14 +12,15 @@ SpkResource* SpkResource::Instance = nullptr;
// clazy:excludeall=container-anti-pattern
SpkResource::SpkResource(QObject *parent) : QObject(parent),
mMaximumConcurrent(CFG->value("resource/concurrent", 5).toInt()),
mCacheDirectory(CFG->value("dirs/cache", "%1/.cache/spark-store/res/")
mMaximumConcurrent(CFG->ReadField("resource/concurrent", 5).toInt()),
mCacheDirectory(CFG->ReadField("dirs/cache", "*/.cache/spark-store/res/")
.toString()
.arg(QDir::homePath()))
.replace('*', QDir::homePath()))
{
Q_ASSERT(!Instance);
qRegisterMetaType<ResourceResult>();
Instance = this;
mRequestSemaphore = new QSemaphore(mMaximumConcurrent);
QString path = mCacheDirectory.section('/', 1, -2, QString::SectionIncludeLeadingSep);

@ -26,12 +26,11 @@ SpkStore::SpkStore(bool aCli, QString &aLogPath)
// Finish all essential initialization after this.
mConfigPath = QDir::homePath() + "/.config/spark-store/config"; //TODO: flexible config via CLI
if(QFileInfo(mConfigPath).exists())
mCfg = new QSettings(QDir::homePath() + "/.config/spark-store/config", QSettings::IniFormat,
this);
if(QFileInfo::exists(mConfigPath))
mCfg = new SpkConfig(this, QDir::homePath() + "/.config/spark-store/config");
else
{
mCfg = new QSettings(":/info/default_config", QSettings::IniFormat, this);
mCfg = new SpkConfig(this, ":/info/default_config");
#if 0
bool cfgDirOk;
if(!qgetenv("SPARK_NO_INSTALL_CONFIG").toInt())
@ -59,9 +58,8 @@ SpkStore::SpkStore(bool aCli, QString &aLogPath)
mDistroName = SpkUtils::GetDistroName();
// Initialize URL
mApiRequestUrl = mCfg->value("url/api", "https://store.deepinos.org/api/").toString();
mResourceRequestUrl = mCfg->value("url/res", "http://img.store.deepinos.org.cn/").toString();
mCfg->BindField("url/api", &mApiRequestUrl, "https://store.deepinos.org/api/");
mCfg->BindField("url/res", &mResourceRequestUrl, "http://img.store.deepinos.org.cn/");
mUserAgentStr = QString("Spark-Store/%1 Distro/%2")
.arg(GitVer::DescribeTags())
@ -74,6 +72,7 @@ SpkStore::SpkStore(bool aCli, QString &aLogPath)
// UI Initialization
mResMgr = new SpkResource(this); // Resource manager must be created before the windows
SpkUi::Initialize();
SpkUi::SpkUiMetaObject.SetAccentColor(QColor(200,100,0));
mMainWindow = new SpkMainWindow;
SpkUi::Popup = new SpkUi::SpkPopup(mMainWindow);

@ -3,6 +3,7 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QDir>
#include <QWidget>
#include "spkutils.h"
void SpkUtils::VerifySingleRequest(QPointer<QNetworkReply> aReply)
@ -110,3 +111,8 @@ bool SpkUtils::EnsureDirExists(QString path)
return true;
}
void SpkUtils::FillWidget(QWidget *widget, QVariant val)
{
}