diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b01e35..9db7949 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,7 @@ set(SOURCE_FILES inc/spkstretchlayout.h gui/spkstretchlayout.cpp inc/spkfocuslineedit.h inc/spknotifydot.h gui/spknotifydot.cpp + inc/spkimgviewer.h gui/spkimgviewer.cpp inc/page/spkpagebase.h gui/page/spkpagebase.cpp inc/page/spkpagehome.h gui/page/spkpagehome.cpp diff --git a/gui/page/spkpageappdetails.cpp b/gui/page/spkpageappdetails.cpp index eba6e92..1559afd 100644 --- a/gui/page/spkpageappdetails.cpp +++ b/gui/page/spkpageappdetails.cpp @@ -23,31 +23,59 @@ namespace SpkUi Qt::SmoothTransformation)); else { - mAppIcon->setPixmap(QIcon(":/icons/broken-icon.svg").pixmap(IconSize)); + mAppIcon->setPixmap(mBrokenImg); RES->PurgeCachedResource(aPkgName, SpkResource::ResourceType::AppIcon, 0); } } - // Load screenshots + // Load screenshots. Screenshots have id starting with 1. if(aScreenshots.isEmpty()) return; + else + { + auto count = aScreenshots.size(); + mImgViewer->SetImageTotal(count); + if(count > mScreenshotPreviews.size()) + { + auto from = mScreenshotPreviews.size(), to = count - mScreenshotPreviews.size(); + for(int i = 0; i <= to; i++) + { + auto wid = new SpkClickLabel; + wid->setProperty("shotId", from + i + 1); + wid->setFixedHeight(200); + wid->setCursor(Qt::PointingHandCursor); + connect(wid, &SpkClickLabel::Pressed, this, &SpkPageAppDetails::ImageClicked); + mScreenshotPreviews.append(wid); + mScreenshotLay->addWidget(wid); + } + } + } int shotId = 0; for(auto &i : aScreenshots) { shotId++; - res = RES->RequestResource(shotId, aPkgName, SpkResource::ResourceType::AppScreenshot, aIcon, + res = RES->RequestResource(shotId, aPkgName, SpkResource::ResourceType::AppScreenshot, i, shotId); + auto preview = mScreenshotPreviews[shotId - 1]; + preview->setVisible(true); if(res.status == SpkResource::ResourceStatus::Ready) { if(pic.loadFromData(res.data)) - ;// TODO - else - { - // TODO - // mAppIcon->setPixmap(QIcon(":/icons/broken-icon.svg").pixmap(SpkAppItem::IconSize_)); - RES->PurgeCachedResource(aPkgName, SpkResource::ResourceType::AppScreenshot, 0); - } + { + mAppImages[shotId] = pic; + mImgViewer->SetPixmap(shotId, &mAppImages[shotId]); + preview->setPixmap(pic.scaledToHeight(200, Qt::SmoothTransformation)); + } + else + { + mAppImages[shotId] = mBrokenImg; + RES->PurgeCachedResource(aPkgName, SpkResource::ResourceType::AppScreenshot, 0); + } + } + else + { + preview->setPixmap(mIconLoading); } } @@ -64,7 +92,9 @@ namespace SpkUi mPkgPath = url; } - SpkPageAppDetails::SpkPageAppDetails(QWidget *parent) : SpkPageBase(parent) + SpkPageAppDetails::SpkPageAppDetails(QWidget *parent) : SpkPageBase(parent), + mBrokenImg(QIcon(":/icons/broken-icon.svg").pixmap(SpkAppItem::IconSize_)), + mIconLoading(QIcon(":/icons/loading-icon.svg").pixmap(SpkAppItem::IconSize_)) { mMainArea = new QScrollArea; mMainArea->setWidgetResizable(true); @@ -148,6 +178,18 @@ namespace SpkUi mDetailsLay->addLayout(mDetailLay); mDetailsLay->addWidget(mAppDescription); // mMainLay->addStretch(); + + mScreenshotLay = new QHBoxLayout; + mScreenshotArea = new QScrollArea; + mWid4ShotArea = new QWidget; + mWid4ShotArea->setLayout(mScreenshotLay); + mScreenshotArea->setWidget(mWid4ShotArea); + mScreenshotArea->setWidgetResizable(true); + mScreenshotArea->setFixedHeight(230); + mScreenshotArea->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + mDetailsLay->addWidget(mScreenshotArea); + mWid4MainArea = new QWidget; mWid4MainArea->setLayout(mDetailsLay); @@ -182,43 +224,80 @@ namespace SpkUi mBottomBarLay->addWidget(mBtnRequestUpdate); mBottomBarLay->addWidget(mBtnReport); + mImgViewer = new SpkImgViewer; + mImgViewer->setVisible(false); + connect(mBtnDownload, &QPushButton::clicked, [=](){ emit RequestDownload(mAppTitle->text(), mPkgName->text(), "/store/reading/youdao-dict/youdao-dict_6.0.0-0~ubuntu_amd64.deb"); - }); + }); + } + + SpkPageAppDetails::~SpkPageAppDetails() + { + delete mImgViewer; } void SpkPageAppDetails::ResourceAcquisitionFinished(int id, ResourceResult result) { - QPixmap icon; + QPixmap img; // qDebug() << "PageAppDetails: Resource" << id << "acquired"; if(!id) { // id == 0, icon if(result.status == SpkResource::ResourceStatus::Ready) { - if(icon.loadFromData(result.data)) - mAppIcon->setPixmap(icon.scaled(SpkAppItem::IconSize_, + if(img.loadFromData(result.data)) + mAppIcon->setPixmap(img.scaled(SpkAppItem::IconSize_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); else - mAppIcon->setPixmap(QIcon(":/icons/broken-icon.svg").pixmap(SpkAppItem::IconSize_)); + mAppIcon->setPixmap(mBrokenImg); } else if(result.status == SpkResource::ResourceStatus::Failed) { - mAppIcon->setPixmap(QIcon(":/icons/broken-icon.svg").pixmap(SpkAppItem::IconSize_)); + mAppIcon->setPixmap(mBrokenImg); RES->PurgeCachedResource(mPkgName->text(), SpkResource::ResourceType::AppIcon, 0); } } else { - // TODO: screenshots + auto preview = mScreenshotPreviews[id - 1]; + preview->setVisible(true); + if(result.status == SpkResource::ResourceStatus::Ready) + { + if(img.loadFromData(result.data)) + { + mAppImages[id] = img; + mImgViewer->SetPixmap(id, &mAppImages[id]); + mScreenshotPreviews[id - 1]->setPixmap(img.scaledToHeight(200, Qt::SmoothTransformation)); + } + else + { + mImgViewer->SetPixmap(id, &mBrokenImg); + mScreenshotPreviews[id - 1]->setPixmap(mBrokenImg); + } + } + else if(result.status == SpkResource::ResourceStatus::Failed) + { + mImgViewer->SetPixmap(id, &mBrokenImg); + mScreenshotPreviews[id - 1]->setPixmap(mBrokenImg); + RES->PurgeCachedResource(mPkgName->text(), SpkResource::ResourceType::AppIcon, 0); + } } } void SpkPageAppDetails::Activated() { RES->Acquire(this, false); + for(auto &i : mScreenshotPreviews) + i->setVisible(false); + mImgViewer->Clear(); + } + + void SpkPageAppDetails::ImageClicked() + { + mImgViewer->ShowWithImage(sender()->property("shotId").toInt()); } SpkDetailEntry::SpkDetailEntry(QWidget *parent) : QWidget(parent) diff --git a/gui/spkimgviewer.cpp b/gui/spkimgviewer.cpp new file mode 100644 index 0000000..db1c657 --- /dev/null +++ b/gui/spkimgviewer.cpp @@ -0,0 +1,120 @@ + +#include "spktitlebar.h" +#include "spkimgviewer.h" +#include "spkui_general.h" +#include <QDebug> +#include <QFocusEvent> +#include <QGuiApplication> +#include <QScreen> + +SpkImgViewer::SpkImgViewer(QWidget *parent) : + SpkWindow(parent), + mIconLoading(QIcon(":/icons/loading-icon.svg").pixmap({ 72, 72 })) +{ + mImgIndict = new QLabel; + mImgIndict->setText("%1/%2"); + mBtnPrev = new QPushButton; + mBtnPrev->setText("<"); + mBtnNext = new QPushButton; + mBtnNext->setText(">"); + + auto titleBar = GetTitleBar(); + titleBar->SetUseIcon(false); + titleBar->SetTitle(tr("Image Preview")); + titleBar->SetOperationButton(SpkTitleBar::OperationButton::Close); + + auto lay = titleBar->GetUserSpace(); + lay->setAlignment(Qt::AlignVCenter); + lay->addStretch(); + lay->addWidget(mBtnPrev); + lay->addWidget(mImgIndict); + lay->addWidget(mBtnNext); + lay->addStretch(); + + mImgArea = new QScrollArea; + mImgArea->setWidgetResizable(true); + mImgArea->setContentsMargins(10, 10, 10, 10); + + mImgShow = new ImgView; + + mImgArea->setWidget(mImgShow); + + auto w = new QWidget; + auto l = new QHBoxLayout; + l->setContentsMargins(10, 10, 10, 10); + l->addWidget(mImgArea); + w->setLayout(l); + SetCentralWidget(w); + + connect(mBtnPrev, &QPushButton::clicked, + [&](){ if(mCurrentImg > 0) { SwitchToImage(--mCurrentImg); } }); + connect(mBtnNext, &QPushButton::clicked, + [&](){ if(mCurrentImg < mTotalImg) { SwitchToImage(++mCurrentImg); } }); +} + +void SpkImgViewer::ShowWithImage(int idx) +{ + SwitchToImage(idx); + show(); +} + +void SpkImgViewer::Clear() +{ + mImgMap.clear(); + mImgShow->SetPixmap(nullptr); + mCurrentImg = 1; +} + +void SpkImgViewer::SetPixmap(int idx, QPixmap *img) +{ + mImgMap[idx] = img; + if(mCurrentImg == idx) + { + mImgShow->SetPixmap(img); + } + ResizeToFitImageSize(img->size()); +} + +void SpkImgViewer::SwitchToImage(int idx) +{ + auto img = mImgMap.value(idx, nullptr); + mCurrentImg = idx; + mImgShow->SetPixmap(img ? img : &mIconLoading); + if(img) + ResizeToFitImageSize(img->size()); + if(idx == 1) + { + mBtnPrev->setEnabled(false); + mBtnNext->setEnabled(true); + } + else if(idx == mTotalImg) + { + mBtnPrev->setEnabled(true); + mBtnNext->setEnabled(false); + } + else + { + mBtnPrev->setEnabled(true); + mBtnNext->setEnabled(true); + } + mImgIndict->setText(QString("%1/%2").arg(mCurrentImg).arg(mTotalImg)); +} + +bool SpkImgViewer::event(QEvent *e) +{ + if(e->type() == QEvent::WindowDeactivate) + close(); + return SpkWindow::event(e); +} + +void SpkImgViewer::ResizeToFitImageSize(QSize s) +{ + auto targetSize = s; + targetSize.rheight() += SpkTitleBar::Height; + targetSize = s.grownBy(QMargins(10, 10, 10, 10)); + targetSize = targetSize.boundedTo(SpkUi::PrimaryScreenSize * 0.8); + resize(targetSize); + targetSize /= 2; + auto targetPos = SpkUi::PrimaryScreenSize / 2 - targetSize; + move(targetPos.width(), targetPos.height()); +} diff --git a/gui/spktitlebar.cpp b/gui/spktitlebar.cpp index 7a72020..69dfd11 100644 --- a/gui/spktitlebar.cpp +++ b/gui/spktitlebar.cpp @@ -9,8 +9,7 @@ SpkTitleBar::SpkTitleBar(QWidget *parent) : QFrame(parent) { mLinkedWindow = nullptr; - setMinimumHeight(48); - setMaximumHeight(48); + setFixedHeight(Height); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); mIcon = new QLabel(this); diff --git a/inc/page/spkpageappdetails.h b/inc/page/spkpageappdetails.h index 76f7f18..948b1a6 100644 --- a/inc/page/spkpageappdetails.h +++ b/inc/page/spkpageappdetails.h @@ -8,16 +8,27 @@ #include <QFormLayout> #include "page/spkpagebase.h" #include "spkstretchlayout.h" +#include "spkimgviewer.h" namespace SpkUi { class SpkDetailEntry; + class SpkClickLabel : public QLabel + { + Q_OBJECT + protected: + virtual void mousePressEvent(QMouseEvent *e) override { emit Pressed(); } + signals: + void Pressed(); + }; + class SpkPageAppDetails : public SpkPageBase { Q_OBJECT public: SpkPageAppDetails(QWidget *parent = nullptr); + ~SpkPageAppDetails(); void LoadAppResources(QString pkgName, QString icon, QStringList screenshots, QStringList tags); void SetWebsiteLink(QString url); @@ -25,6 +36,7 @@ namespace SpkUi private: QString mPkgPath; + QPixmap mBrokenImg, mIconLoading; public slots: void ResourceAcquisitionFinished(int id, ResourceResult result); @@ -34,14 +46,18 @@ namespace SpkUi static constexpr QSize IconSize { 144, 144 }; // Main Area - QScrollArea *mMainArea; - QWidget *mDetailWidget, *mIconTitleWidget, *mWid4MainArea; + QScrollArea *mMainArea, *mScreenshotArea; + QWidget *mDetailWidget, *mIconTitleWidget, *mWid4MainArea, *mWid4ShotArea; QLabel *mAppTitle, *mAppIcon, *mAppDescription, *mAppShortDesc, *mPkgName, *mVersion, *mWebsite; SpkDetailEntry *mAuthor, *mContributor, *mSite, *mArch, *mSize; SpkStretchLayout *mDetailLay; QVBoxLayout *mDetailsLay, *mTitleLay, *mMainLay; - QHBoxLayout *mIconTitleLay; + QHBoxLayout *mIconTitleLay, *mScreenshotLay; + QList<SpkClickLabel*> mScreenshotPreviews; + + QMap<int, QPixmap> mAppImages; + SpkImgViewer *mImgViewer; // Bottom bar QWidget *mBottomBar; @@ -50,6 +66,9 @@ namespace SpkUi signals: void RequestDownload(QString name, QString pkgName, QString path); + + private slots: + void ImageClicked(); }; class SpkDetailEntry : public QWidget diff --git a/inc/spkimgviewer.h b/inc/spkimgviewer.h new file mode 100644 index 0000000..4d46756 --- /dev/null +++ b/inc/spkimgviewer.h @@ -0,0 +1,61 @@ + +#pragma once + +#include <QPainter> +#include <QScrollArea> +#include "spkwindow.h" + +class ImgView : public QWidget +{ + Q_OBJECT + + public: + ImgView(QWidget *parent = nullptr) : QWidget(parent) { mPixmap = nullptr; } + void SetPixmap(QPixmap *p) { mPixmap = p; if(p) setFixedSize(p->size()); update(); } + + protected: + void paintEvent(QPaintEvent *e) + { + QPainter p(this); + if(mPixmap) + p.drawPixmap(0, 0, *mPixmap); + p.end(); + e->accept(); + } + + private: + QPixmap *mPixmap; +}; + +class SpkImgViewer : public SpkWindow +{ + Q_OBJECT + + public: + SpkImgViewer(QWidget *parent = nullptr); + void ShowWithImage(int idx); + void SetImageTotal(int a) { mTotalImg = a; } + + public slots: + void Clear(); + void SetPixmap(int idx, QPixmap *img); + + private slots: + void SwitchToImage(int idx); + + protected: + bool event(QEvent*) override; + + private: + void ResizeToFitImageSize(QSize); + + private: + QPushButton *mBtnPrev, *mBtnNext; + QScrollArea *mImgArea; + QLabel *mImgIndict; + QMap<int, QPixmap*> mImgMap; + int mCurrentImg, mTotalImg; + QPixmap mIconLoading; + + ImgView *mImgShow; +}; diff --git a/inc/spktitlebar.h b/inc/spktitlebar.h index 39288a1..e290b9e 100644 --- a/inc/spktitlebar.h +++ b/inc/spktitlebar.h @@ -43,6 +43,7 @@ class SpkTitleBar : public QFrame public: SpkTitleBar(QWidget *parent = nullptr); ~SpkTitleBar(); + static constexpr int Height = 48; using OperationButton = SpkTitleBarDefaultButton::OperationButton; void SetOperationButton(OperationButton); diff --git a/lang/zh.ts b/lang/zh.ts index d606f6b..ca53d7c 100644 --- a/lang/zh.ts +++ b/lang/zh.ts @@ -403,6 +403,14 @@ Right now we are not just a Chinese group. We are discovering our way into more <translation>关于</translation> </message> </context> +<context> + <name>SpkImgViewer</name> + <message> + <location filename="../gui/spkimgviewer.cpp" line="23"/> + <source>Image Preview</source> + <translation type="unfinished"></translation> + </message> +</context> <context> <name>SpkMainWindow</name> <message> @@ -666,47 +674,47 @@ to use terminal for program output.</source> <context> <name>SpkUi::SpkPageAppDetails</name> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="59"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="87"/> <source>Website link</source> <translation>网站链接</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="124"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="154"/> <source>Author</source> <translation>作者</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="126"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="156"/> <source>Contributor</source> <translation>贡献者</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="130"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="160"/> <source>Architecture</source> <translation>架构</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="132"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="162"/> <source>Size</source> <translation>大小</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="164"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="206"/> <source>Download</source> <translation>下载</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="170"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="212"/> <source>Uninstall</source> <translation>卸载</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="173"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="215"/> <source>Request Update</source> <translation>请求更新</translation> </message> <message> - <location filename="../gui/page/spkpageappdetails.cpp" line="176"/> + <location filename="../gui/page/spkpageappdetails.cpp" line="218"/> <source>Report</source> <translation>举报</translation> </message> diff --git a/src/main.cpp b/src/main.cpp index efc890f..d87936f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,7 @@ #include "spkstore.h" #include "spkdownload.h" -#include "spkmsgbox.h" +#include "spkimgviewer.h" int main(int argc, char *argv[]) @@ -30,21 +30,5 @@ int main(int argc, char *argv[]) SpkStore store(false, LogPath); -// SpkDownloadMgr dl; -// dl.SetNewServers({ -// "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/" -// }); -// dl.SetDestinationFolder("/tmp/"); -// dl.StartNewDownload("store/office/cn.com.10jqka/cn.com.10jqka_1.6.1.2_amd64.deb", 0); -// QObject::connect(&dl, &SpkDownloadMgr::DownloadStopped, -// [&](SpkDownloadMgr::TaskResult, int) -// { -// SpkMsgBox::StaticExec("Finished", ""); -// }); - return QApplication::exec(); }