mirror of
https://gitee.com/spark-store-project/spark-store
synced 2025-08-15 00:42:24 +08:00
添加应用详情页面
This commit is contained in:
parent
d2ab7cfbf7
commit
2b12c38f50
CMakeLists.txt
docs
gui
inc
resource/stylesheets
src
@ -75,6 +75,7 @@ set(SOURCE_FILES
|
||||
inc/page/spkpagebase.h gui/page/spkpagebase.cpp
|
||||
inc/page/spkpageuitest.h gui/page/spkpageuitest.cpp
|
||||
inc/page/spkpageapplist.h gui/page/spkpageapplist.cpp
|
||||
inc/page/spkpageappdetails.h gui/page/spkpageappdetails.cpp
|
||||
|
||||
inc/spkstore.h src/spkstore.cpp
|
||||
inc/spkuimsg.h src/spkuimsg.cpp
|
||||
|
@ -40,4 +40,13 @@ C++ 规范要求的和第三方库有较大差异的除外。例如,`SpkUiMess
|
||||
如非第三方代码,一般以 `Spk` 前缀标明。
|
||||
实现 UI 的类都应归于 `SpkUi` 命名空间。
|
||||
|
||||
## 类定义
|
||||
|
||||
### 一般规则
|
||||
|
||||
Qt 类的 `Q_OBJECT` 应置于类定义最顶端。
|
||||
`public` ,`protected` 和 `private` 等标签中只应该包含一类成员,
|
||||
如单个 `public` 标签内只能包含方法,或成员。
|
||||
信号、槽等不得与普通方法混合。
|
||||
|
||||
<!--TODO-->
|
182
gui/page/spkpageappdetails.cpp
Normal file
182
gui/page/spkpageappdetails.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "page/spkpageappdetails.h"
|
||||
#include "spkutils.h"
|
||||
|
||||
|
||||
namespace SpkUi
|
||||
{
|
||||
constexpr QSize SpkPageAppDetails::IconSize;
|
||||
|
||||
void SpkPageAppDetails::LoadAppResources(QString aPkgName, QString aIcon, QStringList aScreenshots,
|
||||
QStringList aTags)
|
||||
{
|
||||
QPixmap pic;
|
||||
|
||||
// Load icon
|
||||
auto res = RES->RequestResource(0, aPkgName, SpkResource::ResourceType::AppIcon, aIcon);
|
||||
if(res.status == SpkResource::ResourceStatus::Ready)
|
||||
{
|
||||
if(pic.loadFromData(res.data))
|
||||
mAppIcon->setPixmap(pic.scaled(IconSize,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
else
|
||||
{
|
||||
mAppIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(SpkAppItem::IconSize_));
|
||||
RES->PurgeCachedResource(aPkgName, SpkResource::ResourceType::AppIcon, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Load screenshots
|
||||
if(aScreenshots.isEmpty())
|
||||
return;
|
||||
|
||||
int shotId = 0;
|
||||
for(auto &i : aScreenshots)
|
||||
{
|
||||
shotId++;
|
||||
res = RES->RequestResource(shotId, aPkgName, SpkResource::ResourceType::AppScreenshot, aIcon,
|
||||
shotId);
|
||||
if(res.status == SpkResource::ResourceStatus::Ready)
|
||||
{
|
||||
if(pic.loadFromData(res.data))
|
||||
;// TODO
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
// mAppIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(SpkAppItem::IconSize_));
|
||||
RES->PurgeCachedResource(aPkgName, SpkResource::ResourceType::AppScreenshot, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: tags
|
||||
}
|
||||
|
||||
SpkPageAppDetails::SpkPageAppDetails(QWidget *parent) : SpkPageBase(parent)
|
||||
{
|
||||
mMainArea = new QScrollArea;
|
||||
mMainArea->setWidgetResizable(true);
|
||||
mMainArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
mLay4MainArea = new QVBoxLayout(this);
|
||||
mLay4MainArea->addWidget(mMainArea);
|
||||
|
||||
mMainLay = new QVBoxLayout(mMainArea);
|
||||
mMainLay->setSizeConstraint(QLayout::SetMinAndMaxSize);
|
||||
|
||||
mAppIcon = new QLabel;
|
||||
|
||||
mAppTitle = new QLabel;
|
||||
mAppTitle->setObjectName("styDetTitle");
|
||||
|
||||
mAppDescription = new QLabel;
|
||||
mAppDescription->setObjectName("styDetDesc");
|
||||
mAppDescription->setWordWrap(true);
|
||||
mAppShortDesc = new QLabel;
|
||||
mAppShortDesc->setObjectName("styDetDesc");
|
||||
mAppShortDesc->setWordWrap(true);
|
||||
// NOTE: Seems Qt have trouble dealing with wrapped text here. Removing the following operations
|
||||
// to mAppShortDesc will result in broken layout. Very possible that it's a Qt bug.
|
||||
mAppShortDesc->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
mAppShortDesc->setMinimumWidth(100);
|
||||
mVersion = new QLabel;
|
||||
mPkgName = new QLabel;
|
||||
mPkgName->setObjectName("styDetPkg");
|
||||
|
||||
mTitleLay = new QVBoxLayout;
|
||||
mTitleLay->setAlignment(Qt::AlignTop);
|
||||
mTitleLay->addWidget(mAppTitle);
|
||||
mTitleLay->addWidget(mVersion);
|
||||
mTitleLay->addWidget(mAppShortDesc);
|
||||
mTitleLay->addWidget(mPkgName);
|
||||
mTitleLay->setSpacing(0);
|
||||
|
||||
mIconTitleLay = new QHBoxLayout;
|
||||
mIconTitleLay->setAlignment(Qt::AlignLeft);
|
||||
mIconTitleLay->addWidget(mAppIcon);
|
||||
mIconTitleLay->addSpacing(15);
|
||||
mIconTitleLay->addLayout(mTitleLay);
|
||||
|
||||
mIconTitleWidget = new QWidget;
|
||||
mIconTitleWidget->setLayout(mIconTitleLay);
|
||||
|
||||
mAuthor = new SpkDetailEntry;
|
||||
mAuthor->SetTitle(tr("Author"));
|
||||
mContributor = new SpkDetailEntry;
|
||||
mContributor->SetTitle(tr("Contributor"));
|
||||
mSite = new SpkDetailEntry;
|
||||
mSite->SetTitle(tr("Website"));
|
||||
mArch = new SpkDetailEntry;
|
||||
mArch->SetTitle(tr("Architecture"));
|
||||
mSize = new SpkDetailEntry;
|
||||
mSize->SetTitle(tr("Size"));
|
||||
|
||||
mDetailLay = new SpkStretchLayout;
|
||||
mDetailLay->setSpacing(12);
|
||||
mDetailLay->addWidget(mAuthor);
|
||||
mDetailLay->addWidget(mContributor);
|
||||
mDetailLay->addWidget(mSize);
|
||||
mDetailLay->addWidget(mArch);
|
||||
mDetailLay->addWidget(mSite);
|
||||
|
||||
// mDetailWidget = new QWidget;
|
||||
// mDetailWidget->setLayout(mDetailLay);
|
||||
// mDetailWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
|
||||
mMainLay->setAlignment(Qt::AlignTop);
|
||||
mMainLay->addWidget(mIconTitleWidget);
|
||||
mMainLay->addLayout(mDetailLay);
|
||||
mMainLay->addWidget(mAppDescription);
|
||||
// mMainLay->addStretch();
|
||||
mWid4MainArea = new QWidget;
|
||||
mWid4MainArea->setLayout(mMainLay);
|
||||
|
||||
mMainArea->setWidget(mWid4MainArea);
|
||||
}
|
||||
|
||||
void SpkPageAppDetails::ResourceAcquisitionFinished(int id, ResourceResult result)
|
||||
{
|
||||
QPixmap icon;
|
||||
if(!id)
|
||||
{
|
||||
// id == 0, icon
|
||||
if(result.status == SpkResource::ResourceStatus::Ready)
|
||||
{
|
||||
if(icon.loadFromData(result.data))
|
||||
mAppIcon->setPixmap(icon.scaled(SpkAppItem::IconSize_,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
else
|
||||
mAppIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(SpkAppItem::IconSize_));
|
||||
}
|
||||
else if(result.status == SpkResource::ResourceStatus::Failed)
|
||||
{
|
||||
mAppIcon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(SpkAppItem::IconSize_));
|
||||
RES->PurgeCachedResource(mPkgName->text(), SpkResource::ResourceType::AppIcon, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: screenshots
|
||||
}
|
||||
}
|
||||
|
||||
void SpkPageAppDetails::Activated()
|
||||
{
|
||||
RES->Acquire(this, false);
|
||||
}
|
||||
|
||||
SpkDetailEntry::SpkDetailEntry(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setLayout(&mLay);
|
||||
mLay.addWidget(&mTitle);
|
||||
mLay.addWidget(&mField);
|
||||
mTitle.setAlignment(Qt::AlignLeft);
|
||||
mField.setAlignment(Qt::AlignRight);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
setMinimumWidth(300);
|
||||
setAutoFillBackground(true);
|
||||
}
|
||||
}
|
@ -53,6 +53,7 @@ namespace SpkUi
|
||||
auto item = new SpkAppItem(appId, this);
|
||||
auto id = mAppItemList.size();
|
||||
|
||||
connect(item, &SpkAppItem::clicked, this, &SpkPageAppList::ApplicationClicked);
|
||||
item->SetTitle(name);
|
||||
item->SetDescription(description);
|
||||
item->setProperty("pkg_name", pkgName);
|
||||
|
@ -51,6 +51,18 @@ SpkUi::SpkPageUiTest::SpkPageUiTest(QWidget *parent) : QSplitter(parent)
|
||||
"Phasellus finibus risus id aliquam pulvinar.");
|
||||
AppItem->SetIcon(QIcon::fromTheme("dialog-information").pixmap(72, 72));
|
||||
|
||||
Detail1 = new SpkDetailEntry; Detail1->SetTitle("Foo"); Detail1->SetValue("1");
|
||||
Detail2 = new SpkDetailEntry; Detail2->SetTitle("Foo"); Detail2->SetValue("1");
|
||||
Detail3 = new SpkDetailEntry; Detail3->SetTitle("Foo"); Detail3->SetValue("1");
|
||||
|
||||
DetailsLay = new SpkStretchLayout;
|
||||
DetailsLay->addWidget(Detail1);
|
||||
DetailsLay->addWidget(Detail2);
|
||||
DetailsLay->addWidget(Detail3);
|
||||
|
||||
DetailsWidget = new QWidget;
|
||||
DetailsWidget->setLayout(DetailsLay);
|
||||
|
||||
PopupText = new QLineEdit(this);
|
||||
PopupText->setObjectName("spk_pg_qsstest_poptext");
|
||||
PopupText->setText("Hello, world");
|
||||
@ -85,6 +97,7 @@ SpkUi::SpkPageUiTest::SpkPageUiTest(QWidget *parent) : QSplitter(parent)
|
||||
VLayTestWidgets->addWidget(ShowPopup);
|
||||
VLayTestWidgets->addWidget(ShowAbout);
|
||||
VLayTestWidgets->addWidget(AppItem);
|
||||
VLayTestWidgets->addWidget(DetailsWidget);
|
||||
|
||||
Group = new QGroupBox(this);
|
||||
Group->setObjectName("spk_pg_qsstest_groupbox");
|
||||
|
@ -50,3 +50,15 @@ void SpkAppItem::paintEvent(QPaintEvent *e)
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
}
|
||||
|
||||
void SpkAppItem::mousePressEvent(QMouseEvent *e)
|
||||
{
|
||||
mPressCond = true;
|
||||
}
|
||||
|
||||
void SpkAppItem::mouseReleaseEvent(QMouseEvent *e)
|
||||
{
|
||||
if(mPressCond)
|
||||
emit clicked(mAppId);
|
||||
mPressCond = false;
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ void SpkMainWindow::PopulateAppList(QJsonObject appData, QString &&keyword)
|
||||
static auto err =
|
||||
[](){
|
||||
sErr("Received invalid application list data!");
|
||||
SpkUiMessage::SendStoreNotification(tr("An invalid response was received. Please try again!"));
|
||||
SpkUiMessage::SendStoreNotification(tr("Received an invalid response. Please try again!"));
|
||||
return;
|
||||
};
|
||||
int pgCurrent, pgTotal, totalApps;
|
||||
@ -178,7 +178,7 @@ void SpkMainWindow::PopulateAppList(QJsonObject appData, QString &&keyword)
|
||||
|
||||
auto apps = appData.value("data").toArray();
|
||||
|
||||
foreach(auto i, apps)
|
||||
for(auto &&i : apps)
|
||||
{
|
||||
if(i.isObject())
|
||||
{
|
||||
@ -206,10 +206,112 @@ void SpkMainWindow::PopulateAppList(QJsonObject appData, QString &&keyword)
|
||||
}
|
||||
}
|
||||
|
||||
void SpkMainWindow::EnterAppDetails(int aAppId)
|
||||
{
|
||||
using namespace SpkUtils;
|
||||
VerifySingleRequest(mAppDetailsGetReply);
|
||||
QJsonObject reqData;
|
||||
QJsonDocument reqDoc;
|
||||
reqData.insert("application_id", QJsonValue(aAppId));
|
||||
reqDoc.setObject(reqData);
|
||||
mAppDetailsGetReply = STORE->SendApiRequest("application/get_application_detail", reqDoc);
|
||||
DeleteReplyLater(mAppDetailsGetReply);
|
||||
connect(mAppDetailsGetReply, &QNetworkReply::finished,
|
||||
this, &SpkMainWindow::AppDetailsDataReceived);
|
||||
setCursor(Qt::BusyCursor);
|
||||
}
|
||||
|
||||
void SpkMainWindow::AppDetailsDataReceived()
|
||||
{
|
||||
QJsonValue retval;
|
||||
if(!SpkUtils::VerifyReplyJson(mAppDetailsGetReply, retval) || !retval.isObject())
|
||||
{
|
||||
sErrPop(tr("Failed to open app details page! Type of retval: %1.").arg(retval.type()));
|
||||
return;
|
||||
}
|
||||
SwitchToPage(SpkUi::PgAppList);
|
||||
PopulateAppDetails(retval.toObject());
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
void SpkMainWindow::PopulateAppDetails(QJsonObject appDetails)
|
||||
{
|
||||
QString pkgName, author, contributor, site, iconPath, arch, version, details, shortDesc, name;
|
||||
QStringList screenshots, tags;
|
||||
int packageSize;
|
||||
static auto err =
|
||||
[](){
|
||||
sErr("Received invalid application details!");
|
||||
SpkUiMessage::SendStoreNotification(tr("Received an invalid response. Please try again!"));
|
||||
return;
|
||||
};
|
||||
|
||||
if(appDetails.contains("package") && appDetails.value("package").isString())
|
||||
pkgName = appDetails.value("package").toString();
|
||||
else return err();
|
||||
if(appDetails.contains("application_name_zh") && appDetails.value("application_name_zh").isString())
|
||||
name = appDetails.value("application_name_zh").toString();
|
||||
else name = pkgName;
|
||||
if(appDetails.contains("version") && appDetails.value("version").isString())
|
||||
version = appDetails.value("version").toString();
|
||||
else return err();
|
||||
if(appDetails.contains("icons") && appDetails.value("icons").isString())
|
||||
iconPath= appDetails.value("icons").toString();
|
||||
if(appDetails.contains("author") && appDetails.value("author").isString())
|
||||
author = appDetails.value("author").toString();
|
||||
if(appDetails.contains("contributor") && appDetails.value("contributor").isString())
|
||||
contributor = appDetails.value("contributor").toString();
|
||||
if(appDetails.contains("website") && appDetails.value("website").isString())
|
||||
site = appDetails.value("website").toString();
|
||||
if(appDetails.contains("description") && appDetails.value("description").isString())
|
||||
shortDesc = appDetails.value("description").toString();
|
||||
if(appDetails.contains("more") && appDetails.value("more").isString())
|
||||
details = appDetails.value("more").toString();
|
||||
if(appDetails.contains("arch") && appDetails.value("arch").isString())
|
||||
arch = appDetails.value("arch").toString();
|
||||
if(appDetails.contains("size") && appDetails.value("size").isDouble())
|
||||
packageSize = appDetails.value("size").toInt();
|
||||
|
||||
QJsonArray imgs;
|
||||
if(appDetails.contains("img_urls") && appDetails.value("img_urls").isArray())
|
||||
imgs = appDetails.value("img_urls").toArray();
|
||||
if(!imgs.isEmpty())
|
||||
for(auto &&i : imgs)
|
||||
if(i.isString()) screenshots << i.toString();
|
||||
|
||||
QJsonArray tags_j;
|
||||
if(appDetails.contains("tags") && appDetails.value("tags").isArray())
|
||||
imgs = appDetails.value("tags").toArray();
|
||||
if(!tags_j.isEmpty())
|
||||
for(auto &&i : tags_j)
|
||||
if(i.isString()) tags << i.toString();
|
||||
|
||||
// Details string has a strangely appended LF. IDK but still should remove it.
|
||||
shortDesc = shortDesc.trimmed();
|
||||
|
||||
auto w = ui->PageAppDetails;
|
||||
w->mPkgName->setText(pkgName);
|
||||
w->mAppTitle->setText(name);
|
||||
w->mAppShortDesc->setText(shortDesc);
|
||||
w->mAppDescription->setText(details);
|
||||
w->mAuthor->SetValue(author);
|
||||
w->mContributor->SetValue(contributor);
|
||||
w->mSite->SetValue(site);
|
||||
w->mArch->SetValue(arch);
|
||||
w->mSize->SetValue(SpkUtils::BytesToSize(packageSize));
|
||||
w->mVersion->setText(version);
|
||||
SwitchToPage(SpkUi::PgAppDetails);
|
||||
ui->AppDetailsItem->setHidden(false);
|
||||
ui->CategoryWidget->setCurrentItem(ui->AppDetailsItem);
|
||||
w->LoadAppResources(pkgName, iconPath, screenshots, tags);
|
||||
}
|
||||
|
||||
// ==================== Main Window Initialization ====================
|
||||
|
||||
void SpkMainWindow::Initialize()
|
||||
{
|
||||
connect(ui->SidebarMgr, &SpkUi::SpkSidebarSelector::SwitchToPage,
|
||||
this, &SpkMainWindow::SwitchToPage);
|
||||
connect(ui->SidebarMgr, &SpkUi::SpkSidebarSelector::SwitchToCategory,
|
||||
this, &SpkMainWindow::EnterCategoryList);
|
||||
connect(ui->PageAppList, &SpkUi::SpkPageAppList::SwitchListPage,
|
||||
@ -218,6 +320,8 @@ void SpkMainWindow::Initialize()
|
||||
this, &SpkMainWindow::SearchKeyword);
|
||||
connect(ui->SearchEdit, &QLineEdit::returnPressed,
|
||||
[=](){ emit SearchKeyword(ui->SearchEdit->text(), 1); });
|
||||
connect(ui->PageAppList, &SpkUi::SpkPageAppList::ApplicationClicked,
|
||||
this, &SpkMainWindow::EnterAppDetails);
|
||||
}
|
||||
|
||||
// ==================== Main Widget Initialization ====================
|
||||
@ -292,11 +396,28 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
|
||||
CategoryWidget->setColumnCount(1);
|
||||
CategoryWidget->setHeaderHidden(true);
|
||||
CategoryWidget->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
|
||||
|
||||
//============ Sidebar entries BEGIN ============
|
||||
AppDetailsItem = new QTreeWidgetItem(QStringList(tr("App Details")));
|
||||
AppDetailsItem->setData(0, SpkSidebarSelector::RoleItemIsCategory, false);
|
||||
AppDetailsItem->setData(0, SpkSidebarSelector::RoleItemCategoryPageId, SpkStackedPages::PgAppDetails);
|
||||
CategoryParentItem = new QTreeWidgetItem(QStringList(tr("Categories")));
|
||||
CategoryParentItem->setFlags(CategoryParentItem->flags().setFlag(Qt::ItemIsSelectable, false));
|
||||
CategoryParentItem->setExpanded(true);
|
||||
#ifndef NDEBUG
|
||||
UiTestItem = new QTreeWidgetItem(QStringList(tr("UI TEST")));
|
||||
UiTestItem->setData(0, SpkSidebarSelector::RoleItemIsCategory, false);
|
||||
UiTestItem->setData(0, SpkSidebarSelector::RoleItemCategoryPageId, SpkStackedPages::PgQssTest);
|
||||
#endif
|
||||
//============ Sidebar entries END ============
|
||||
|
||||
SidebarMgr->AddUnusableItem(CategoryParentItem);
|
||||
CategoryWidget->addTopLevelItem(AppDetailsItem);
|
||||
CategoryWidget->addTopLevelItem(CategoryParentItem);
|
||||
CategoryWidget->addTopLevelItem(UiTestItem);
|
||||
|
||||
// Must be done after added into a view.
|
||||
AppDetailsItem->setHidden(true); // Hide until we actually open up a Details page
|
||||
CategoryParentItem->setExpanded(true);
|
||||
|
||||
// FIXMEIFPOSSIBLE: Fusion adds extra gradient.
|
||||
// Details: https://forum.qt.io/topic/128190/fusion-style-kept-adding-an-extra-
|
||||
@ -355,7 +476,7 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
|
||||
|
||||
//============ Pages =============
|
||||
|
||||
// Red-Black tree based map will be able to sort things
|
||||
// Red-Black tree based map will be able to sort things. Just for convenience of ordering pages.
|
||||
QMap<SpkStackedPages, QWidget*> sorter;
|
||||
|
||||
// Initialize pages
|
||||
@ -363,6 +484,10 @@ SpkUi::SpkMainWidget::SpkMainWidget(QWidget *parent) : QFrame(parent)
|
||||
PageAppList->setProperty("spk_pageid", SpkStackedPages::PgAppList);
|
||||
sorter[PgAppList] = PageAppList;
|
||||
|
||||
PageAppDetails = new SpkUi::SpkPageAppDetails(this);
|
||||
PageAppDetails->setProperty("spk_pageid", SpkStackedPages::PgAppDetails);
|
||||
sorter[PgAppDetails] = PageAppDetails;
|
||||
|
||||
#ifndef NDEBUG // If only in debug mode should we initialize QSS test page
|
||||
PageQssTest = new SpkUi::SpkPageUiTest(this);
|
||||
PageQssTest->setProperty("spk_pageid", SpkStackedPages::PgQssTest);
|
||||
|
@ -26,7 +26,8 @@ const std::map<SpkUi::Qss::ColorSetIndex, const char *> SpkUi::Qss::ColorSet2Tok
|
||||
{ TextOnSelection, "TXACC" },
|
||||
{ TextOnGlobalBgnd, "TXGBG" },
|
||||
{ TextOnControlsBgnd, "TXCBG" },
|
||||
{ TextLighter, "TXL" },
|
||||
{ TextLighter, "TXL1" },
|
||||
{ TextEvenLighter, "TXL2" },
|
||||
{ TextDisabled, "TXD" },
|
||||
{ GlossyEdge, "GLS" },
|
||||
{ ShadesEdge, "SHD" }
|
||||
@ -51,6 +52,7 @@ const std::map<SpkUi::Qss::ColorSetIndex, QColor> SpkUi::Qss::DarkColorSet
|
||||
{ TextOnGlobalBgnd, ColorTextOnBackground(0x282828) },
|
||||
{ TextOnControlsBgnd, ColorTextOnBackground(0x282828) },
|
||||
{ TextLighter, 0xd5d5d5 },
|
||||
{ TextEvenLighter, 0x505050 },
|
||||
{ TextDisabled, 0xbebebe },
|
||||
{ GlossyEdge, 0x656565 },
|
||||
{ ShadesEdge, 0x7b7b7b }
|
||||
@ -75,6 +77,7 @@ const std::map<SpkUi::Qss::ColorSetIndex, QColor> SpkUi::Qss::LightColorSet
|
||||
{ TextOnGlobalBgnd, ColorTextOnBackground(0xf8f8f8) },
|
||||
{ TextOnControlsBgnd, ColorTextOnBackground(0xf8f8f8) },
|
||||
{ TextLighter, 0x2a2a2a },
|
||||
{ TextEvenLighter, 0xa0a0a0 },
|
||||
{ TextDisabled, 0x8a8a8a },
|
||||
{ GlossyEdge, 0xc5c5c5 },
|
||||
{ ShadesEdge, 0x9d9d9d }
|
||||
|
@ -21,7 +21,7 @@ QSize SpkStretchLayout::sizeHint() const
|
||||
{
|
||||
if(mItems.isEmpty())
|
||||
return { 300, 300 };
|
||||
auto w = geometry().width();
|
||||
auto w = geometry().width() - spacing();
|
||||
auto it = mItems.first();
|
||||
int countPerLine = w / (it->minimumSize().width() + spacing());
|
||||
int lines = ceil((double)mItems.size() / countPerLine);
|
||||
@ -75,14 +75,16 @@ void SpkStretchLayout::setGeometry(const QRect &rect)
|
||||
else // Stretch items.
|
||||
size = QSize {(w / countPerLine - spc), itm->maximumSize().height() };
|
||||
|
||||
auto origin = geometry().topLeft();
|
||||
|
||||
QLayoutItem *o;
|
||||
for(int i = 0; i < mItems.size(); i++)
|
||||
{
|
||||
o = mItems.at(i);
|
||||
QRect geo;
|
||||
geo.setSize(size);
|
||||
geo.moveTo((i % countPerLine) * (size.width() + spc) + spc,
|
||||
(i / countPerLine) * (size.height() + spacing()) + spc);
|
||||
geo.moveTo((i % countPerLine) * (size.width() + spc) + spc + origin.x(),
|
||||
(i / countPerLine) * (size.height() + spacing()) + spc + origin.y());
|
||||
o->setGeometry(geo);
|
||||
}
|
||||
}
|
||||
|
55
inc/page/spkpageappdetails.h
Normal file
55
inc/page/spkpageappdetails.h
Normal file
@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include "page/spkpagebase.h"
|
||||
#include "spkstretchlayout.h"
|
||||
|
||||
namespace SpkUi
|
||||
{
|
||||
class SpkDetailEntry;
|
||||
|
||||
class SpkPageAppDetails : public SpkPageBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpkPageAppDetails(QWidget *parent = nullptr);
|
||||
|
||||
void LoadAppResources(QString pkgName, QString icon, QStringList screenshots, QStringList tags);
|
||||
|
||||
private:
|
||||
|
||||
public slots:
|
||||
void ResourceAcquisitionFinished(int id, ResourceResult result);
|
||||
void Activated();
|
||||
|
||||
public:
|
||||
static constexpr QSize IconSize { 144, 144 };
|
||||
|
||||
QScrollArea *mMainArea;
|
||||
QWidget *mDetailWidget, *mIconTitleWidget, *mWid4MainArea;
|
||||
QLabel *mAppTitle, *mAppIcon, *mAppDescription, *mAppShortDesc, *mPkgName, *mVersion;
|
||||
SpkDetailEntry *mAuthor, *mContributor, *mSite, *mArch, *mSize;
|
||||
SpkStretchLayout *mDetailLay;
|
||||
QVBoxLayout *mMainLay, *mTitleLay, *mLay4MainArea;
|
||||
QHBoxLayout *mIconTitleLay;
|
||||
|
||||
};
|
||||
|
||||
class SpkDetailEntry : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SpkDetailEntry(QWidget *parent = nullptr);
|
||||
void SetTitle(const QString &s) { mTitle.setText(s); }
|
||||
void SetValue(const QString &s) { mField.setText(s); }
|
||||
|
||||
private:
|
||||
QLabel mTitle, mField;
|
||||
QHBoxLayout mLay;
|
||||
};
|
||||
}
|
@ -43,7 +43,7 @@ namespace SpkUi
|
||||
QString mKeyword;
|
||||
|
||||
signals:
|
||||
void ApplicationClicked(QString name, QString pkgName);
|
||||
void ApplicationClicked(int appId);
|
||||
void SwitchListPage(int categoryId, int page);
|
||||
void SwitchSearchPage(QString keyword, int page);
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QSlider>
|
||||
#include "spkappitem.h"
|
||||
#include "spkstretchlayout.h"
|
||||
#include "page/spkpageappdetails.h"
|
||||
|
||||
#include "spkloading.h"
|
||||
|
||||
@ -40,6 +42,9 @@ namespace SpkUi
|
||||
QSlider *SlideV;
|
||||
SpkLoading *Loading;
|
||||
SpkAppItem *AppItem;
|
||||
SpkStretchLayout *DetailsLay;
|
||||
SpkDetailEntry *Detail1, *Detail2, *Detail3;
|
||||
QWidget *DetailsWidget;
|
||||
|
||||
QLineEdit *PopupText;
|
||||
QPushButton *ShowPopup,
|
||||
|
@ -18,6 +18,8 @@ class SpkAppItem : public QWidget
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
void mousePressEvent(QMouseEvent *e);
|
||||
void mouseReleaseEvent(QMouseEvent *e);
|
||||
|
||||
public:
|
||||
static constexpr int IconSize = 72;
|
||||
@ -29,7 +31,11 @@ class SpkAppItem : public QWidget
|
||||
ElidedLabel *mDescription;
|
||||
int mAppId;
|
||||
|
||||
bool mPressCond;
|
||||
|
||||
QVBoxLayout *mLayText;
|
||||
QHBoxLayout *mMainLay;
|
||||
|
||||
signals:
|
||||
void clicked(int);
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "spkfocuslineedit.h"
|
||||
#include "page/spkpageuitest.h"
|
||||
#include "page/spkpageapplist.h"
|
||||
#include "page/spkpageappdetails.h"
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
@ -25,6 +26,7 @@ namespace SpkUi
|
||||
{
|
||||
PgInvalid = -1,
|
||||
PgAppList,
|
||||
PgAppDetails,
|
||||
PgQssTest // Must be at last
|
||||
};
|
||||
|
||||
@ -86,7 +88,7 @@ namespace SpkUi
|
||||
mLastSelectedItem = nullptr;
|
||||
}
|
||||
mLastCheckedBtn = b;
|
||||
emit SwitchToPage(b->property("spk_pageno").toInt());
|
||||
emit SwitchToPage(static_cast<SpkStackedPages>(b->property("spk_pageno").toInt()));
|
||||
}
|
||||
void TreeItemSelected(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
@ -103,7 +105,8 @@ namespace SpkUi
|
||||
if(item->data(column, RoleItemIsCategory).toBool())
|
||||
emit SwitchToCategory(item->data(column, RoleItemCategoryPageId).toInt(), 0);
|
||||
else
|
||||
emit SwitchToPage(item->data(column, RoleItemCategoryPageId).toInt());
|
||||
emit SwitchToPage(static_cast<SpkStackedPages>(
|
||||
item->data(column, RoleItemCategoryPageId).toInt()));
|
||||
}
|
||||
void UnusableItemSelected(QTreeWidgetItem *i)
|
||||
{
|
||||
@ -120,7 +123,7 @@ namespace SpkUi
|
||||
|
||||
signals:
|
||||
void SwitchToCategory(int aCategoryId, int aPage);
|
||||
void SwitchToPage(int aPageId);
|
||||
void SwitchToPage(SpkStackedPages aPageId);
|
||||
};
|
||||
|
||||
class SpkMainWidget : public QFrame
|
||||
@ -146,7 +149,9 @@ namespace SpkUi
|
||||
QMap<int, QTreeWidgetItem> *CategoryItemMap;
|
||||
SpkSidebarSelector *SidebarMgr;
|
||||
|
||||
QTreeWidgetItem *CategoryParentItem;
|
||||
QTreeWidgetItem *CategoryParentItem,
|
||||
*AppDetailsItem,
|
||||
*UiTestItem;
|
||||
|
||||
// Title bar search bar
|
||||
SpkFocusLineEdit *SearchEdit;
|
||||
@ -156,6 +161,7 @@ namespace SpkUi
|
||||
//Pages
|
||||
SpkPageUiTest *PageQssTest;
|
||||
SpkPageAppList *PageAppList;
|
||||
SpkPageAppDetails *PageAppDetails;
|
||||
};
|
||||
}
|
||||
|
||||
@ -175,7 +181,8 @@ class SpkMainWindow : public SpkWindow
|
||||
|
||||
private:
|
||||
QPointer<QNetworkReply> mCategoryGetReply,
|
||||
mCategoryAppListGetReply;
|
||||
mCategoryAppListGetReply,
|
||||
mAppDetailsGetReply;
|
||||
SpkUi::SpkStackedPages mCurrentPage = SpkUi::PgInvalid;
|
||||
|
||||
public slots:
|
||||
@ -191,7 +198,11 @@ class SpkMainWindow : public SpkWindow
|
||||
// Search a keyword (and switch pages)
|
||||
void SearchKeyword(QString aKeyword, int aPage);
|
||||
void SearchDataReceived();
|
||||
// Enter the details page of an application (and switch pages)
|
||||
void EnterAppDetails(int aAppId);
|
||||
void AppDetailsDataReceived();
|
||||
|
||||
private:
|
||||
void PopulateAppList(QJsonObject appData, QString &&keyword);
|
||||
void PopulateAppDetails(QJsonObject appDetails);
|
||||
};
|
||||
|
@ -30,6 +30,7 @@ namespace SpkUi
|
||||
TextOnGlobalBgnd,
|
||||
TextOnControlsBgnd,
|
||||
TextLighter,
|
||||
TextEvenLighter,
|
||||
TextDisabled,
|
||||
GlossyEdge,
|
||||
ShadesEdge,
|
||||
|
@ -26,4 +26,5 @@ namespace SpkUtils
|
||||
|
||||
QString CutFileName(QString);
|
||||
QString CutPath(QString);
|
||||
QString BytesToSize(size_t s, int prec = 2);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@
|
||||
TXACC: Text on Selection/Activation
|
||||
TXGBG: Text on Global background
|
||||
TXCBG: Text on controls background
|
||||
TXL: Text slightly lighter
|
||||
TXL1: Text slightly lighter
|
||||
TXL2: Text lighter even more
|
||||
TXD: Text disabled
|
||||
GLS: Glossy edge on controls
|
||||
SHD: Shades edge on controls
|
||||
@ -106,7 +107,7 @@ ElidedLabel
|
||||
|
||||
#styAboutDesc
|
||||
{
|
||||
color: TXL
|
||||
color: TXL1
|
||||
}
|
||||
|
||||
SpkAppItem
|
||||
@ -126,12 +127,29 @@ SpkAppItem::hover
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#styDetTitle
|
||||
{
|
||||
font-weight: 600;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
#styAppItmDesc
|
||||
{
|
||||
color: TXL;
|
||||
color: TXL1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#styDetDesc
|
||||
{
|
||||
color: TXL1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#styDetPkg
|
||||
{
|
||||
color: TXL2
|
||||
}
|
||||
|
||||
#styChkBtn
|
||||
{
|
||||
border-width: 0px;
|
||||
|
@ -88,3 +88,14 @@ QString SpkUtils::CutPath(QString path)
|
||||
{
|
||||
return path.section('/', 1, -2, QString::SectionIncludeLeadingSep);
|
||||
}
|
||||
|
||||
QString SpkUtils::BytesToSize(size_t s, int prec)
|
||||
{
|
||||
if(s > (1 << 30))
|
||||
return QString::number(double (s) / (1 << 30), 'f', prec) + " GB";
|
||||
if(s > (1 << 20))
|
||||
return QString::number(double (s) / (1 << 20), 'f', prec) + " MB";
|
||||
if(s > (1 << 10))
|
||||
return QString::number(double (s) / (1 << 10), 'f', prec) + " KB";
|
||||
return QString::number(s) + " B";
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user