feat: able to cancel loading at any time & add tons of multithreading bugs
This commit is contained in:
+5
-5
@@ -53,11 +53,11 @@ target_link_libraries(wallpaper_chooser PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
|
|||||||
|
|
||||||
target_include_directories(wallpaper_chooser PRIVATE src)
|
target_include_directories(wallpaper_chooser PRIVATE src)
|
||||||
|
|
||||||
if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
# if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||||
target_compile_definitions(wallpaper_chooser PRIVATE
|
# target_compile_definitions(wallpaper_chooser PRIVATE
|
||||||
GENERAL_LOGGER_DISABLED
|
# GENERAL_LOGGER_DISABLED
|
||||||
)
|
# )
|
||||||
endif()
|
# endif()
|
||||||
|
|
||||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
# If you are developing for iOS or macOS you should consider setting an
|
# If you are developing for iOS or macOS you should consider setting an
|
||||||
|
|||||||
+49
-24
@@ -1,17 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 01:22:53
|
* @Date: 2025-08-05 01:22:53
|
||||||
* @LastEditTime: 2025-08-08 00:43:47
|
* @LastEditTime: 2025-08-08 02:34:24
|
||||||
* @Description: Animated carousel widget for displaying and selecting images.
|
* @Description: Animated carousel widget for displaying and selecting images.
|
||||||
*/
|
*/
|
||||||
#include "images_carousel.h"
|
#include "images_carousel.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <qboxlayout.h>
|
|
||||||
#include <qdebug.h>
|
|
||||||
#include <qnamespace.h>
|
|
||||||
#include <qobject.h>
|
|
||||||
#include <qpixmap.h>
|
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
@@ -76,7 +72,7 @@ void ImagesCarousel::_onInitImagesLoaded() {
|
|||||||
if (m_loadedImages.isEmpty()) {
|
if (m_loadedImages.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_focusCurrImage();
|
focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImagesCarousel::~ImagesCarousel() {
|
ImagesCarousel::~ImagesCarousel() {
|
||||||
@@ -91,8 +87,8 @@ ImagesCarousel::~ImagesCarousel() {
|
|||||||
|
|
||||||
void ImagesCarousel::appendImages(const QStringList& paths) {
|
void ImagesCarousel::appendImages(const QStringList& paths) {
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_imageCountMutex);
|
QMutexLocker locker(&m_countMutex);
|
||||||
m_imageCount += paths.size();
|
m_addedImagesCount += paths.size();
|
||||||
}
|
}
|
||||||
emit loadingStarted(paths.size());
|
emit loadingStarted(paths.size());
|
||||||
for (const QString& path : paths) {
|
for (const QString& path : paths) {
|
||||||
@@ -152,14 +148,31 @@ void ImagesCarousel::_insertImage(const ImageData* data) {
|
|||||||
|
|
||||||
emit imageLoaded(m_loadedImages.size());
|
emit imageLoaded(m_loadedImages.size());
|
||||||
{
|
{
|
||||||
QMutexLocker countLocker(&m_imageCountMutex);
|
QMutexLocker countLocker(&m_countMutex);
|
||||||
if (m_loadedImages.size() >= m_imageCount) {
|
if (++m_loadedImagesCount >= m_addedImagesCount) {
|
||||||
emit loadingCompleted(m_loadedImages.size());
|
QMutexLocker stopSignLocker(&m_stopSignMutex);
|
||||||
|
if (m_stopSign) {
|
||||||
|
// if all stopped
|
||||||
|
emit stopped();
|
||||||
|
} else {
|
||||||
|
emit loadingCompleted(m_loadedImagesCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageLoader::run() {
|
void ImageLoader::run() {
|
||||||
|
{
|
||||||
|
QMutexLocker countLocker(&m_carousel->m_countMutex);
|
||||||
|
QMutexLocker stopSignLocker(&m_carousel->m_stopSignMutex);
|
||||||
|
if (m_carousel->m_stopSign) {
|
||||||
|
// if all stopped
|
||||||
|
if (++m_carousel->m_loadedImagesCount == m_carousel->m_addedImagesCount) {
|
||||||
|
emit m_carousel->stopped();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto data = new ImageData(m_path, m_initWidth, m_initHeight);
|
auto data = new ImageData(m_path, m_initWidth, m_initHeight);
|
||||||
QMetaObject::invokeMethod(m_carousel,
|
QMetaObject::invokeMethod(m_carousel,
|
||||||
"_insertImage",
|
"_insertImage",
|
||||||
@@ -183,32 +196,38 @@ ImageData::ImageData(const QString& p, const int initWidth, const int initHeight
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::focusNextImage() {
|
void ImagesCarousel::focusNextImage() {
|
||||||
_unfocusCurrImage();
|
unfocusCurrImage();
|
||||||
if (m_loadedImages.size() <= 1) return;
|
if (m_loadedImages.size() <= 1) return;
|
||||||
m_currentIndex++;
|
m_currentIndex++;
|
||||||
if (m_currentIndex >= m_loadedImages.size()) {
|
if (m_currentIndex >= m_loadedImages.size()) {
|
||||||
m_currentIndex = 0;
|
m_currentIndex = 0;
|
||||||
}
|
}
|
||||||
_focusCurrImage();
|
focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::focusPrevImage() {
|
void ImagesCarousel::focusPrevImage() {
|
||||||
if (m_loadedImages.size() <= 1) return;
|
if (m_loadedImages.size() <= 1) return;
|
||||||
_unfocusCurrImage();
|
unfocusCurrImage();
|
||||||
m_currentIndex--;
|
m_currentIndex--;
|
||||||
if (m_currentIndex < 0) {
|
if (m_currentIndex < 0) {
|
||||||
m_currentIndex = m_loadedImages.size() - 1;
|
m_currentIndex = m_loadedImages.size() - 1;
|
||||||
}
|
}
|
||||||
_focusCurrImage();
|
focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::_unfocusCurrImage() {
|
void ImagesCarousel::unfocusCurrImage() {
|
||||||
// bound check was (or should) done by caller
|
if (m_currentIndex < 0 || m_currentIndex >= m_loadedImages.size()) {
|
||||||
|
error(QString("Invalid index to unfocus: %1").arg(m_currentIndex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_loadedImages[m_currentIndex]->setFocus(false);
|
m_loadedImages[m_currentIndex]->setFocus(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::_focusCurrImage() {
|
void ImagesCarousel::focusCurrImage() {
|
||||||
// bound check was (or should) done by caller
|
if (m_currentIndex < 0 || m_currentIndex >= m_loadedImages.size()) {
|
||||||
|
error(QString("Invalid index to focus: %1").arg(m_currentIndex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_loadedImages[m_currentIndex]->setFocus(true);
|
m_loadedImages[m_currentIndex]->setFocus(true);
|
||||||
emit imageFocused(m_loadedImages[m_currentIndex]->getFileFullPath(),
|
emit imageFocused(m_loadedImages[m_currentIndex]->getFileFullPath(),
|
||||||
m_currentIndex,
|
m_currentIndex,
|
||||||
@@ -263,19 +282,19 @@ void ImagesCarousel::_onScrollBarValueChanged(int value) {
|
|||||||
if (index == m_currentIndex) {
|
if (index == m_currentIndex) {
|
||||||
return; // Already focused
|
return; // Already focused
|
||||||
}
|
}
|
||||||
_unfocusCurrImage();
|
unfocusCurrImage();
|
||||||
m_currentIndex = index;
|
m_currentIndex = index;
|
||||||
_focusCurrImage();
|
focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImagesCarousel::_onItemClicked(int index) {
|
void ImagesCarousel::_onItemClicked(int index) {
|
||||||
// if (m_suppressAutoFocus) return;
|
// if (m_suppressAutoFocus) return;
|
||||||
_unfocusCurrImage();
|
unfocusCurrImage();
|
||||||
m_currentIndex = index;
|
m_currentIndex = index;
|
||||||
if (index < 0 || index >= m_loadedImages.size()) {
|
if (index < 0 || index >= m_loadedImages.size()) {
|
||||||
return; // Out of bounds
|
return; // Out of bounds
|
||||||
}
|
}
|
||||||
_focusCurrImage();
|
focusCurrImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageItem::ImageItem(const ImageData* data,
|
ImageItem::ImageItem(const ImageData* data,
|
||||||
@@ -288,6 +307,7 @@ ImageItem::ImageItem(const ImageData* data,
|
|||||||
m_data(data),
|
m_data(data),
|
||||||
m_itemSize(itemWidth, itemHeight),
|
m_itemSize(itemWidth, itemHeight),
|
||||||
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
|
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
|
||||||
|
assert(data != nullptr);
|
||||||
setScaledContents(true);
|
setScaledContents(true);
|
||||||
if (data->image.isNull()) {
|
if (data->image.isNull()) {
|
||||||
setText(":(");
|
setText(":(");
|
||||||
@@ -326,3 +346,8 @@ void ImageItem::setFocus(bool focus) {
|
|||||||
});
|
});
|
||||||
m_scaleAnimation->start();
|
m_scaleAnimation->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImagesCarousel::onStop() {
|
||||||
|
QMutexLocker locker(&m_stopSignMutex);
|
||||||
|
m_stopSign = true;
|
||||||
|
}
|
||||||
+49
-19
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 01:22:53
|
* @Date: 2025-08-05 01:22:53
|
||||||
* @LastEditTime: 2025-08-07 22:16:37
|
* @LastEditTime: 2025-08-08 02:41:05
|
||||||
* @Description: Animated carousel widget for displaying and selecting images.
|
* @Description: Animated carousel widget for displaying and selecting images.
|
||||||
*/
|
*/
|
||||||
#ifndef IMAGES_CAROUSEL_H
|
#ifndef IMAGES_CAROUSEL_H
|
||||||
@@ -44,7 +44,8 @@ struct ImageData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Image label that displays an image
|
* @brief Image label that displays an image,
|
||||||
|
* which should always be created in the main thread.
|
||||||
*/
|
*/
|
||||||
class ImageItem : public QLabel {
|
class ImageItem : public QLabel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -89,10 +90,13 @@ class ImageItem : public QLabel {
|
|||||||
void clicked(int index);
|
void clicked(int index);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Worker class for loading images in a separate thread.
|
||||||
|
*/
|
||||||
class ImageLoader : public QRunnable {
|
class ImageLoader : public QRunnable {
|
||||||
public:
|
public:
|
||||||
ImageLoader(const QString& path, ImagesCarousel* carousel);
|
ImageLoader(const QString& path, ImagesCarousel* carousel);
|
||||||
void run() override;
|
void run() override; // friend to ImagesCarousel
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_path;
|
QString m_path;
|
||||||
@@ -126,49 +130,75 @@ class ImagesCarousel : public QWidget {
|
|||||||
return m_loadedImages[m_currentIndex]->getFileFullPath();
|
return m_loadedImages[m_currentIndex]->getFileFullPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
const int m_itemWidth = 320;
|
// Should always be called in the main thread
|
||||||
const int m_itemHeight = 180;
|
[[nodiscard]] qsizetype getLoadedImagesCount() {
|
||||||
const int m_itemFocusWidth = 480;
|
return m_loadedImages.size();
|
||||||
const int m_itemFocusHeight = 270;
|
}
|
||||||
const Config::SortType m_sortType = Config::SortType::None;
|
|
||||||
const bool m_sortReverse = false;
|
[[nodiscard]] qsizetype getAddedImagesCount() {
|
||||||
|
QMutexLocker locker(&m_countMutex);
|
||||||
|
return m_addedImagesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// config items
|
||||||
|
const int m_itemWidth;
|
||||||
|
const int m_itemHeight;
|
||||||
|
const int m_itemFocusWidth;
|
||||||
|
const int m_itemFocusHeight;
|
||||||
|
const Config::SortType m_sortType;
|
||||||
|
const bool m_sortReverse;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void focusNextImage();
|
void focusNextImage();
|
||||||
void focusPrevImage();
|
void focusPrevImage();
|
||||||
|
void focusCurrImage();
|
||||||
|
void unfocusCurrImage();
|
||||||
|
void onStop();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void _unfocusCurrImage();
|
|
||||||
void _onScrollBarValueChanged(int value);
|
void _onScrollBarValueChanged(int value);
|
||||||
void _onItemClicked(int index);
|
void _onItemClicked(int index);
|
||||||
void _onInitImagesLoaded();
|
void _onInitImagesLoaded();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void appendImages(const QStringList& paths);
|
||||||
appendImages(const QStringList& paths);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _focusCurrImage();
|
|
||||||
Q_INVOKABLE void _insertImage(const ImageData* item);
|
Q_INVOKABLE void _insertImage(const ImageData* item);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// UI elements
|
||||||
Ui::ImagesCarousel* ui;
|
Ui::ImagesCarousel* ui;
|
||||||
QList<ImageItem*> m_loadedImages;
|
|
||||||
int m_currentIndex = 0;
|
|
||||||
QPropertyAnimation* m_scrollAnimation = nullptr;
|
|
||||||
QHBoxLayout* m_imagesLayout = nullptr;
|
QHBoxLayout* m_imagesLayout = nullptr;
|
||||||
QMutex m_imageCountMutex;
|
ImagesCarouselScrollArea* m_scrollArea = nullptr;
|
||||||
int m_imageCount = 0;
|
|
||||||
|
// Items and counters
|
||||||
|
QList<ImageItem*> m_loadedImages; // m_loadedImages.size() may != m_loadedImagesCount
|
||||||
|
int m_loadedImagesCount = 0; // increase when _insertImage is called OR ImageLoader::run() is called with m_stopSign as true
|
||||||
|
int m_addedImagesCount = 0; // increase when appendImages called
|
||||||
|
QMutex m_countMutex; // for m_loadedImagesCount and m_addedImagesCount
|
||||||
|
int m_currentIndex = 0;
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
QPropertyAnimation* m_scrollAnimation = nullptr;
|
||||||
|
|
||||||
|
// Auto focusing
|
||||||
bool m_suppressAutoFocus = false;
|
bool m_suppressAutoFocus = false;
|
||||||
int m_pendingScrollValue = 0;
|
int m_pendingScrollValue = 0;
|
||||||
QTimer* m_scrollDebounceTimer = nullptr;
|
QTimer* m_scrollDebounceTimer = nullptr;
|
||||||
ImagesCarouselScrollArea* m_scrollArea = nullptr;
|
|
||||||
|
// Loading stopped by user
|
||||||
|
QMutex m_stopSignMutex;
|
||||||
|
bool m_stopSign = false;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void imageFocused(const QString& path, const int index, const int count);
|
void imageFocused(const QString& path, const int index, const int count);
|
||||||
|
|
||||||
void loadingStarted(const qsizetype amount);
|
void loadingStarted(const qsizetype amount);
|
||||||
void loadingCompleted(const qsizetype amount);
|
void loadingCompleted(const qsizetype amount);
|
||||||
void imageLoaded(const qsizetype count);
|
void imageLoaded(const qsizetype count);
|
||||||
|
|
||||||
|
void stopped();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImagesCarouselScrollArea : public QScrollArea {
|
class ImagesCarouselScrollArea : public QScrollArea {
|
||||||
|
|||||||
+25
-2
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 00:37:58
|
* @Date: 2025-08-05 00:37:58
|
||||||
* @LastEditTime: 2025-08-07 22:15:47
|
* @LastEditTime: 2025-08-08 02:25:20
|
||||||
* @Description: MainWindow implementation.
|
* @Description: MainWindow implementation.
|
||||||
*/
|
*/
|
||||||
#include "main_window.h"
|
#include "main_window.h"
|
||||||
@@ -33,7 +33,6 @@ MainWindow::~MainWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::_setupUI() {
|
void MainWindow::_setupUI() {
|
||||||
|
|
||||||
// create images carousel
|
// create images carousel
|
||||||
m_carousel = new ImagesCarousel(
|
m_carousel = new ImagesCarousel(
|
||||||
m_config.getStyleConfig(),
|
m_config.getStyleConfig(),
|
||||||
@@ -44,6 +43,14 @@ void MainWindow::_setupUI() {
|
|||||||
&ImagesCarousel::imageFocused,
|
&ImagesCarousel::imageFocused,
|
||||||
this,
|
this,
|
||||||
&MainWindow::_onImageFocused);
|
&MainWindow::_onImageFocused);
|
||||||
|
connect(this, &MainWindow::stop, m_carousel, &ImagesCarousel::onStop);
|
||||||
|
connect(m_carousel, &ImagesCarousel::stopped, this,
|
||||||
|
// &MainWindow::close); // instead of closing, we just stop the loading
|
||||||
|
[this]() {
|
||||||
|
_onLoadingCompleted(m_carousel->getLoadedImagesCount());
|
||||||
|
m_carousel->focusCurrImage(); },
|
||||||
|
// ensure this is called in the main thread
|
||||||
|
Qt::QueuedConnection);
|
||||||
m_carouselIndex = ui->stackedWidget->addWidget(m_carousel);
|
m_carouselIndex = ui->stackedWidget->addWidget(m_carousel);
|
||||||
|
|
||||||
// create loading indicator
|
// create loading indicator
|
||||||
@@ -85,6 +92,10 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
|
|||||||
onCancel();
|
onCancel();
|
||||||
} else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
} else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
|
||||||
onConfirm();
|
onConfirm();
|
||||||
|
}
|
||||||
|
// if loadingScreen is enabled and loading is in progress, ignore other keys
|
||||||
|
else if (!m_config.getStyleConfig().noLoadingScreen && m_isLoading) {
|
||||||
|
event->ignore();
|
||||||
} else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Tab || event->key() == Qt::Key_Right) {
|
} else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Tab || event->key() == Qt::Key_Right) {
|
||||||
m_carousel->focusNextImage();
|
m_carousel->focusNextImage();
|
||||||
} else if (event->key() == Qt::Key_Left) {
|
} else if (event->key() == Qt::Key_Left) {
|
||||||
@@ -105,6 +116,10 @@ void MainWindow::wheelEvent(QWheelEvent *event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onConfirm() {
|
void MainWindow::onConfirm() {
|
||||||
|
if (m_isLoading) {
|
||||||
|
warn("Loading is still in progress, please wait until it finishes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
close();
|
close();
|
||||||
const auto path = m_carousel->getCurrentImagePath();
|
const auto path = m_carousel->getCurrentImagePath();
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
@@ -129,7 +144,12 @@ void MainWindow::onConfirm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onCancel() {
|
void MainWindow::onCancel() {
|
||||||
|
if (m_isLoading) {
|
||||||
|
warn("Loading stopped by user, waiting all threads to finish...");
|
||||||
|
emit stop();
|
||||||
|
} else {
|
||||||
close();
|
close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::_onImageFocused(const QString &path, const int index, const int count) {
|
void MainWindow::_onImageFocused(const QString &path, const int index, const int count) {
|
||||||
@@ -137,6 +157,7 @@ void MainWindow::_onImageFocused(const QString &path, const int index, const int
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::_onLoadingStarted(const qsizetype amount) {
|
void MainWindow::_onLoadingStarted(const qsizetype amount) {
|
||||||
|
m_isLoading = true;
|
||||||
if (m_config.getStyleConfig().noLoadingScreen) {
|
if (m_config.getStyleConfig().noLoadingScreen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -145,5 +166,7 @@ void MainWindow::_onLoadingStarted(const qsizetype amount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::_onLoadingCompleted(const qsizetype amount) {
|
void MainWindow::_onLoadingCompleted(const qsizetype amount) {
|
||||||
|
info(QString("Loading completed, loaded %1 images").arg(amount));
|
||||||
ui->stackedWidget->setCurrentIndex(m_carouselIndex);
|
ui->stackedWidget->setCurrentIndex(m_carouselIndex);
|
||||||
|
m_isLoading = false;
|
||||||
}
|
}
|
||||||
+5
-1
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: Uyanide pywang0608@foxmail.com
|
* @Author: Uyanide pywang0608@foxmail.com
|
||||||
* @Date: 2025-08-05 00:37:58
|
* @Date: 2025-08-05 00:37:58
|
||||||
* @LastEditTime: 2025-08-07 01:08:12
|
* @LastEditTime: 2025-08-08 02:11:57
|
||||||
* @Description: MainWindow implementation.
|
* @Description: MainWindow implementation.
|
||||||
*/
|
*/
|
||||||
#ifndef MAINWINDOW_H
|
#ifndef MAINWINDOW_H
|
||||||
@@ -50,5 +50,9 @@ class MainWindow : public QMainWindow {
|
|||||||
LoadingIndicator *m_loadingIndicator = nullptr;
|
LoadingIndicator *m_loadingIndicator = nullptr;
|
||||||
int m_carouselIndex, m_loadingIndicatorIndex;
|
int m_carouselIndex, m_loadingIndicatorIndex;
|
||||||
const Config &m_config;
|
const Config &m_config;
|
||||||
|
bool m_isLoading = false;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stop();
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
Reference in New Issue
Block a user