diff --git a/.github/workflows/qt-build-test.yml b/.github/workflows/qt-build-test.yml index 1161300..b214483 100644 --- a/.github/workflows/qt-build-test.yml +++ b/.github/workflows/qt-build-test.yml @@ -11,9 +11,14 @@ jobs: strategy: matrix: - os: [ubuntu-latest] + os: [windows-latest, ubuntu-latest] qt_version: [5.15.2, 6.6.2] - arch: [gcc_64] + arch: [win64_msvc2019_64, gcc_64] + exclude: + - os: windows-latest + arch: gcc_64 + - os: ubuntu-latest + arch: win64_msvc2019_64 steps: - uses: actions/checkout@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 02db5d5..b0a66e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,8 +200,15 @@ else() endif() if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) - qt_add_executable(WingGifEditor2 MANUAL_FINALIZATION ${PROJECT_SOURCES} - ${QM_FILES}) + if(WIN32) + set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/favicon.rc") + qt_add_executable(WingGifEditor2 MANUAL_FINALIZATION ${PROJECT_SOURCES} + ${QM_FILES} ${app_icon_resource_windows}) + else() + qt_add_executable(WingGifEditor2 MANUAL_FINALIZATION ${PROJECT_SOURCES} + ${QM_FILES}) + endif() + # Define target properties for Android with Qt 6 as: set_property(TARGET # WingGifEditor2 APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # ${CMAKE_CURRENT_SOURCE_DIR}/android) For more information, see @@ -210,7 +217,14 @@ else() if(ANDROID) message(FATAL_ERROR "Not supported for Android!") else() - add_executable(WingGifEditor2 ${PROJECT_SOURCES} ${QM_FILES}) + if(WIN32) + set(app_icon_resource_windows + "${CMAKE_CURRENT_SOURCE_DIR}/favicon.rc") + add_executable(WingGifEditor2 ${PROJECT_SOURCES} ${QM_FILES} + ${app_icon_resource_windows}) + else() + add_executable(WingGifEditor2 ${PROJECT_SOURCES} ${QM_FILES}) + endif() endif() endif() diff --git a/appicon.ico b/appicon.ico new file mode 100644 index 0000000..1e0d150 Binary files /dev/null and b/appicon.ico differ diff --git a/favicon.rc b/favicon.rc new file mode 100644 index 0000000..17cc6d7 --- /dev/null +++ b/favicon.rc @@ -0,0 +1,3 @@ +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "appicon.ico" \ No newline at end of file diff --git a/lang/WingGifEditor2_zh_CN.ts b/lang/WingGifEditor2_zh_CN.ts index 350d3b3..3d7815c 100644 --- a/lang/WingGifEditor2_zh_CN.ts +++ b/lang/WingGifEditor2_zh_CN.ts @@ -5,6 +5,7 @@ AboutSoftwareDialog + About 关于 @@ -233,7 +234,7 @@ LanguageManager - + Chinese(Simplified) 中文(简体) @@ -299,48 +300,48 @@ MainWindow - + InfoSave 是否保存 - + WingGifEditor 羽云 GIF 编辑器 - + File 文件 - + Edit 编辑 - + View 视图 - + Plugin 插件 - + Setting 设置 - + About 关于 - - + + @@ -349,81 +350,81 @@ 选择文件 - + Choose to open 选择文件以打开 - + NewFromPicsGif 从图像序列创建动图 - + InvalidGif 无效 GIF 文件 - + SaveGif 保存 GIF - - + + CaneledByUser 被用户取消操作 - + SaveSuccess 保存成功 - + SaveFail 保存失败 - + ChooseSaveFile 选择保存文件路径 - + SaveAsGif 另存为 GIF - + SaveAsSuccess 另存为成功 - + SaveAsFail 另存为失败 - + ExportFrames 导出帧 - + ExportSuccess 导出成功 - + ExportFail 导出失败 - + Goto 跳转 @@ -465,7 +466,7 @@ - + InsertPics 插入图片序列 @@ -495,325 +496,355 @@ 无效模型 - + Basic 基础 - + NewFromPics 从图序新建 - + NewFromGifs 从动图创建 - + Open 打开 - + + Close + 关闭 + + + RecentFiles 最近文件 - - + + NewFromGifsGif + 从动图创建中 + + + + Save 保存 - + SaveAs 另存为 - + Export 导出 - + FileInfo 文件信息 - - + + General 基本 - + Undo 撤销 - + Redo 重做 - + Cut 剪切 - + Copy 复制 - + Paste 粘贴 - + Delete 删除 - + Frame - + ReduceFrame 降低帧率 - + DeleteBefore 删除之前帧 - + DeleteAfter 删除之后帧 - + MoveLeft 左移帧 - + MoveRight 右移帧 - + Reverse 反转 - + SetDelay 设置延时 - + Image 图像 - + ScaleGif 缩放 GIF - + CutGif 裁切 GIF - + FilpH 水平反转 - + FlipV 垂直翻转 - + RotateLeft 逆时针旋转 - + RotateR 顺时针旋转 - + Effect 效果 - + ExportBlank 导出模板 - + ApplyModel 应用模型 - + CreateReverse 创建逆向帧动画 - + ScaleDelay 缩放延时 - + Merge 合并 - + MergeGIfs 合并动图 - + Player 播放 - + FirstFrame 第一帧 - + LastFrame 上一帧 - + Play 播放 - + Stop 停止 - + NextFrame 下一帧 - + EndFrame 最后帧 - + LoopUp 查找 - + + FitInView + 适合编辑区 + + + SelectAll 全选 - + Deselect 取消选择 - + ReverseSelection 反选 - + Log 日志 - + Info 信息 - + Scale 缩放 - + PlgInfo 插件信息 - + Settings 设置 - + Software 软件 - + Sponsor 赞助 - + Wiki WIKI - + AboutQT 关于 QT - + + ReadingGif + 读取 GIF 中 + + + + WritingGif + 写入 GIF 中 + + + + ExportingGif + 导出 GIF 中 + + + ConfirmClose 确认退出 - + ConfirmSave 你确认未保存就退出软件吗? - + Untitled 未命名 - + Frame: %1/%2 帧:%1/%2 - + NewFrame 新帧 - + InputNewFrameInterval(keep10ms) 请设置新帧延时(时间只保留十位) @@ -1088,7 +1119,7 @@ WaitingLoop - + Waiting 请等待 diff --git a/src/class/gifcontentmodel.cpp b/src/class/gifcontentmodel.cpp index 7055680..3f6d19e 100644 --- a/src/class/gifcontentmodel.cpp +++ b/src/class/gifcontentmodel.cpp @@ -1,5 +1,9 @@ #include "gifcontentmodel.h" +#ifdef Q_OS_WIN +#include +#endif + GifContentModel::GifContentModel(QObject *parent) : QAbstractListModel(parent) {} @@ -209,10 +213,16 @@ void GifContentModel::cropFrames(const QRect &rect) { } Q_ASSERT(_data.first().rect().contains(rect)); +#ifdef Q_OS_WIN + concurrency::parallel_for_each( + _data.begin(), _data.end(), + [rect](QImage &img) { img = img.copy(rect); }); +#else #pragma omp parallel for for (auto &img : _data) { img = img.copy(rect); } +#endif emit dataChanged(this->index(0), this->index(_data.size() - 1)); updateLinkedListViewCurrent(); @@ -261,11 +271,19 @@ void GifContentModel::scaleFrames(int width, int height) { return; } +#ifdef Q_OS_WIN + concurrency::parallel_for_each( + _data.begin(), _data.end(), [width, height](QImage &img) { + img = img.scaled(width, height, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + }); +#else #pragma omp parallel for for (auto &img : _data) { img = img.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } +#endif emit dataChanged(this->index(0), this->index(_data.size() - 1)); updateLinkedListViewCurrent(); @@ -275,17 +293,29 @@ void GifContentModel::flipFrames(Qt::Orientation dir, int index, qsizetype count) { switch (dir) { case Qt::Horizontal: { +#ifdef Q_OS_WIN + concurrency::parallel_for_each( + _data.begin(), _data.end(), + [](QImage &img) { img = img.mirrored(true, false); }); +#else #pragma omp parallel for for (auto &img : _data) { img = img.mirrored(true, false); } +#endif break; } case Qt::Vertical: { +#ifdef Q_OS_WIN + concurrency::parallel_for_each( + _data.begin(), _data.end(), + [](QImage &img) { img = img.mirrored(); }); +#else #pragma omp parallel for for (auto &img : _data) { img = img.mirrored(); } +#endif break; } } @@ -313,10 +343,19 @@ void GifContentModel::reverseFrames(qsizetype begin, qsizetype end) { void GifContentModel::rotateFrames(bool clockwise) { QTransform trans; trans.rotate(clockwise ? -90 : 90); + +#ifdef Q_OS_WIN + concurrency::parallel_for_each( + _data.begin(), _data.end(), [&trans](QImage &img) { + img = img.transformed(trans, Qt::SmoothTransformation); + }); +#else #pragma omp parallel for for (auto &img : _data) { img = img.transformed(trans, Qt::SmoothTransformation); } +#endif + emit dataChanged(this->index(0), this->index(_data.size() - 1)); updateLinkedListViewCurrent(); } diff --git a/src/class/gifreader.cpp b/src/class/gifreader.cpp index 0af7d89..5ac043f 100644 --- a/src/class/gifreader.cpp +++ b/src/class/gifreader.cpp @@ -3,7 +3,8 @@ #include #include -GifReader::GifReader(const QString &filename) { +GifReader::GifReader(const QString &filename, QObject *parent) + : QObject(parent) { if (QFile::exists(filename)) { load(filename); } @@ -161,6 +162,8 @@ bool GifReader::load(const QString &filename) { default: break; } + + emit sigUpdateUIProcess(); } while (recordType != TERMINATE_RECORD_TYPE); return closeHandle(handle); diff --git a/src/class/gifreader.h b/src/class/gifreader.h index 0ed1a5c..a61ebff 100644 --- a/src/class/gifreader.h +++ b/src/class/gifreader.h @@ -4,12 +4,16 @@ #include "giflib/gif_lib.h" #include +#include #include #include -class GifReader { +class GifReader : public QObject { + Q_OBJECT + public: - explicit GifReader(const QString &filename = QString()); + explicit GifReader(const QString &filename = QString(), + QObject *parent = nullptr); virtual ~GifReader(); @@ -33,6 +37,9 @@ class GifReader { QString comment() const; +signals: + void sigUpdateUIProcess(); + private: bool closeHandleWithError(GifFileType *handle); diff --git a/src/class/gifwriter.cpp b/src/class/gifwriter.cpp index c8ec574..370597b 100644 --- a/src/class/gifwriter.cpp +++ b/src/class/gifwriter.cpp @@ -2,8 +2,9 @@ #include -GifWriter::GifWriter(int width, int height, const QString &filename) - : m_width(width), m_height(height), m_filename(filename) { +GifWriter::GifWriter(int width, int height, const QString &filename, + QObject *parent) + : QObject(parent), m_width(width), m_height(height), m_filename(filename) { Q_ASSERT(width > 0 && height > 0); } @@ -98,6 +99,8 @@ bool GifWriter::save(const QString &filename, unsigned int loopCount) { if (!addFrame(handle, key, key.rect(), m_delays.at(0), resources)) return closeEHandleWithError(handle); + emit sigUpdateUIProcess(); + int delta = 0; for (qsizetype i = 1; i < m_data.size(); ++i) { @@ -108,6 +111,8 @@ bool GifWriter::save(const QString &filename, unsigned int loopCount) { if (!result) return closeEHandleWithError(handle); + + emit sigUpdateUIProcess(); } closeEHandle(handle); diff --git a/src/class/gifwriter.h b/src/class/gifwriter.h index 10b4538..0dbacd5 100644 --- a/src/class/gifwriter.h +++ b/src/class/gifwriter.h @@ -5,11 +5,13 @@ #include #include +#include #include #include -class GifWriter { +class GifWriter : public QObject { + Q_OBJECT private: struct Color { unsigned char red = 0; @@ -280,7 +282,8 @@ class GifWriter { public: explicit GifWriter(int width, int height, - const QString &filename = QString()); + const QString &filename = QString(), + QObject *parent = nullptr); void push(const QImage &img, int delay); @@ -306,6 +309,9 @@ class GifWriter { void setExtString(const QString &newExtString); +signals: + void sigUpdateUIProcess(); + private: bool closeEHandle(GifFileType *handle); diff --git a/src/class/languagemanager.cpp b/src/class/languagemanager.cpp index 1d2930a..e037a14 100644 --- a/src/class/languagemanager.cpp +++ b/src/class/languagemanager.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include LanguageManager &LanguageManager::instance() { @@ -33,6 +34,28 @@ LanguageManager::LanguageManager() { } _defaultLocale = QLocale::system(); + + if (m_langs.isEmpty()) { +#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) + if (QLocale::China == _defaultLocale.territory() +#else + if (QLocale::China == _defaultLocale.country() +#endif + ) { + QMessageBox::critical( + nullptr, QStringLiteral("程序损坏"), + QStringLiteral( + "语言文件已损坏,请尝试重装软件以解决这个问题。")); + } else { + QMessageBox::critical( + nullptr, QStringLiteral("Corruption"), + QStringLiteral("The language file has been damaged. " + "Please try reinstalling the software to " + "solve the problem.")); + } + qApp->exit(-1); + } + bool found = false; for (auto p = m_localeMap.begin(); p != m_localeMap.end(); ++p) { #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) diff --git a/src/class/waitingloop.cpp b/src/class/waitingloop.cpp index f2b719c..9bf5aa0 100644 --- a/src/class/waitingloop.cpp +++ b/src/class/waitingloop.cpp @@ -1,5 +1,7 @@ #include "waitingloop.h" +#include + WaitingLoop::WaitingLoop(const QString &tip) { dw.dialog()->setCancelButton(nullptr); dw.dialog()->setLabelText(tip); diff --git a/src/class/waitingloop.h b/src/class/waitingloop.h index aa7d058..55500aa 100644 --- a/src/class/waitingloop.h +++ b/src/class/waitingloop.h @@ -5,6 +5,7 @@ #include #include +#include class WaitingLoop : public QObject { Q_OBJECT diff --git a/src/control/gifeditor.cpp b/src/control/gifeditor.cpp index 5e4ad75..c2c1ed7 100644 --- a/src/control/gifeditor.cpp +++ b/src/control/gifeditor.cpp @@ -13,6 +13,22 @@ GifEditor::GifEditor(const QImage &img, QWidget *parent) void GifEditor::setImage(const QImage &img) { scene->setFrameImg(img); } +void GifEditor::fitOpenSize() { + auto imgSize = scene->frameImageSize(); + auto wSize = this->size(); + if (imgSize.width() > wSize.width() || imgSize.height() > wSize.height()) { + fitInEditorView(); + } else { + resetTransform(); + } +} + +void GifEditor::fitInEditorView() { + auto r = scene->contentBounding(); + setSceneRect(r); + fitInView(r, Qt::KeepAspectRatio); +} + void GifEditor::setCropMode(bool b) { scene->setCuttingMode(b); } void GifEditor::zoomIn() { scale(1.1, 1.1); } diff --git a/src/control/gifeditor.h b/src/control/gifeditor.h index 71a2b2f..395aec8 100644 --- a/src/control/gifeditor.h +++ b/src/control/gifeditor.h @@ -12,17 +12,20 @@ class GifEditor : public QGraphicsView { GifEditor(const QImage &img, QWidget *parent = nullptr); void setImage(const QImage &img); + QRectF selRect() const; + +public slots: + void setSelRect(int x, int y, int w, int h); + void fitOpenSize(); + + void fitInEditorView(); + void setCropMode(bool b); void zoomIn(); void zoomOut(); void setZoom(int value); - QRectF selRect() const; - -public slots: - void setSelRect(int x, int y, int w, int h); - signals: void selRectChanged(QRectF rect); diff --git a/src/control/gifeditorscene.cpp b/src/control/gifeditorscene.cpp index 07b4af5..eb128b2 100644 --- a/src/control/gifeditorscene.cpp +++ b/src/control/gifeditorscene.cpp @@ -9,14 +9,14 @@ GifEditorScene::GifEditorScene(const QImage &img, QObject *parent) sel->setBackgroundColor(Qt::transparent); sel->setCropperVisible(false); sel->setImage(img); - auto w = addWidget(sel); - connect(sel, &ImageCropper::sizeChanged, w, [=] { + gw = addWidget(sel); + connect(sel, &ImageCropper::sizeChanged, gw, [=] { // call the internal updateProxyGeometryFromWidget - w->resetTransform(); - w->setWidget(nullptr); - w->setWidget(sel); + gw->resetTransform(); + gw->setWidget(nullptr); + gw->setWidget(sel); // center the image - setSceneRect(QRectF(QPointF(), w->size())); + setSceneRect(QRectF(QPointF(), gw->size())); }); connect(sel, &ImageCropper::selRectChanged, this, &GifEditorScene::selRectChanged); @@ -32,6 +32,10 @@ void GifEditorScene::setCuttingMode(bool value) { void GifEditorScene::setFrameImg(const QImage &img) { sel->setImage(img); } +QRectF GifEditorScene::contentBounding() const { return gw->boundingRect(); } + +QSize GifEditorScene::frameImageSize() const { return sel->imageSize(); } + QRectF GifEditorScene::selRect() const { return sel->selRect(); } void GifEditorScene::setSelRect(int x, int y, int w, int h) { diff --git a/src/control/gifeditorscene.h b/src/control/gifeditorscene.h index e59c80e..a47df2a 100644 --- a/src/control/gifeditorscene.h +++ b/src/control/gifeditorscene.h @@ -15,6 +15,10 @@ class GifEditorScene : public QGraphicsScene { void setFrameImg(const QImage &img); + QRectF contentBounding() const; + + QSize frameImageSize() const; + QRectF selRect() const; public slots: @@ -26,6 +30,7 @@ public slots: private: ImageCropper *sel; QPointF oldpos; + QGraphicsProxyWidget *gw; }; #endif // GIFEDITORSCENE_H diff --git a/src/control/imagecropper/imagecropper.cpp b/src/control/imagecropper/imagecropper.cpp index 819f626..f96e98f 100644 --- a/src/control/imagecropper/imagecropper.cpp +++ b/src/control/imagecropper/imagecropper.cpp @@ -26,6 +26,8 @@ bool ImageCropper::proportionFixed() const { return pimpl->isProportionFixed; } QRectF ImageCropper::selRect() const { return pimpl->croppingRect; } +QSize ImageCropper::imageSize() const { return pimpl->imageForCropping.size(); } + void ImageCropper::setSelRect(int x, int y, int w, int h) { if (pimpl->isProportionFixed) { pimpl->croppingRect = QRectF( diff --git a/src/control/imagecropper/imagecropper.h b/src/control/imagecropper/imagecropper.h index 2f20c93..8f39909 100644 --- a/src/control/imagecropper/imagecropper.h +++ b/src/control/imagecropper/imagecropper.h @@ -20,6 +20,8 @@ class ImageCropper : public QWidget { QRectF selRect() const; + QSize imageSize() const; + void setSelRect(int x, int y, int w, int h); public slots: diff --git a/src/dialog/aboutsoftwaredialog.cpp b/src/dialog/aboutsoftwaredialog.cpp index 759d574..04b6f23 100644 --- a/src/dialog/aboutsoftwaredialog.cpp +++ b/src/dialog/aboutsoftwaredialog.cpp @@ -13,6 +13,8 @@ AboutSoftwareDialog::AboutSoftwareDialog(QWidget *parent) ui->lblVersion->setText(WINGGIF_VERSION); + _dialog->setWindowTitle(tr("About")); + Utilities::moveToCenter(this); } diff --git a/src/dialog/mainwindow.cpp b/src/dialog/mainwindow.cpp index 4aa9205..a798030 100644 --- a/src/dialog/mainwindow.cpp +++ b/src/dialog/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" +#include #include #include @@ -121,6 +122,19 @@ MainWindow::MainWindow(QWidget *parent) : FramelessMainWindow(parent) { connect(_player, &PlayGifManager::playStateChanged, this, &MainWindow::updatePlayState); + auto psc = new QShortcut(QKeySequence(Qt::Key_Space), this); + psc->setContext(Qt::ShortcutContext::WindowShortcut); + connect(psc, &QShortcut::activated, this, [=] { + if (_curfilename.isEmpty()) { + return; + } + if (_player->isPlaying()) { + on_stop(); + } else { + on_play(); + } + }); + connect(&undo, &QUndoStack::canUndoChanged, this, [=](bool b) { m_toolBtneditors.value(ToolButtonIndex::UNDO_ACTION)->setEnabled(b); }); @@ -223,10 +237,7 @@ void MainWindow::on_new_frompics() { if (ensureSafeClose()) { NewDialog d(NewType::FromPics, this); if (d.exec()) { - WingProgressDialog dw; - dw.dialog()->setCancelButton(nullptr); - dw.dialog()->setLabelText(tr("NewFromPicsGif")); - dw.dialog()->setRange(0, 0); + WaitingLoop dw(tr("NewFromPicsGif")); if (loadfromImages(d.getResult(), getNewFrameInterval())) { _curfilename = QStringLiteral(":"); // 表示新建 setSaved(false); @@ -235,7 +246,6 @@ void MainWindow::on_new_frompics() { setEditModeEnabled(true); } else { } - dw.pdialog()->close(); } } } @@ -245,8 +255,7 @@ void MainWindow::on_new_fromgifs() { if (ensureSafeClose()) { NewDialog d(NewType::FromGifs, this); if (d.exec()) { - // WaitingDialog dw; - // dw.start(tr("NewFromGifsGif")); + WaitingLoop dw(tr("NewFromGifsGif")); if (loadfromGifs(d.getResult())) { _curfilename = QStringLiteral(":"); // 表示新建 setSaved(false); @@ -255,7 +264,6 @@ void MainWindow::on_new_fromgifs() { setEditModeEnabled(true); } else { } - // dw.close(); } } } @@ -372,16 +380,8 @@ void MainWindow::on_export() { void MainWindow::on_close() { _player->stop(); if (ensureSafeClose()) { - - // editor->setBackgroudPix(QPixmap(":/images/icon.png")); - // editor->fitPicEditor(); - // editor->scale(0.5, 0.5); - // iSaved->setPixmap(infoSaveg); - // iReadWrite->setPixmap(inforwg); - // status->showMessage(""); - + _editor->setImage(QImage(NAMEICONRES(QStringLiteral("icon")))); _model->clearData(); - _curfilename.clear(); undo.clear(); setEditModeEnabled(false); @@ -834,9 +834,16 @@ void MainWindow::on_fullscreen() { this->showFullScreen(); } void MainWindow::on_about() { AboutSoftwareDialog().exec(); } -void MainWindow::on_sponsor() {} +void MainWindow::on_sponsor() { + QDesktopServices::openUrl( + QUrl(QStringLiteral("https://github.com/Wing-summer/" + "WingGifEditor2#%E6%8D%90%E5%8A%A9"))); +} -void MainWindow::on_wiki() {} +void MainWindow::on_wiki() { + QDesktopServices::openUrl(QUrl( + QStringLiteral("https://github.com/Wing-summer/WingGifEditor2/wiki"))); +} RibbonTabContent *MainWindow::buildFilePage(RibbonTabContent *tab) { auto shortcuts = QKeySequences::instance(); @@ -851,6 +858,7 @@ RibbonTabContent *MainWindow::buildFilePage(RibbonTabContent *tab) { addPannelAction(pannel, QStringLiteral("open"), tr("Open"), &MainWindow::on_open, QKeySequence::Open); + addPannelAction(pannel, QStringLiteral("recent"), tr("RecentFiles"), EMPTY_FUNC, {}, m_recentMenu); } @@ -858,25 +866,26 @@ RibbonTabContent *MainWindow::buildFilePage(RibbonTabContent *tab) { { auto pannel = tab->addGroup(tr("Save")); - auto a = addPannelAction(pannel, QStringLiteral("save"), tr("Save"), - &MainWindow::on_save, QKeySequence::Save); - m_editStateWidgets << a; - - a = addPannelAction(pannel, QStringLiteral("saveas"), tr("SaveAs"), - &MainWindow::on_saveas, - shortcuts.keySequence(QKeySequences::Key::SAVE_AS)); - m_editStateWidgets << a; - - a = addPannelAction(pannel, QStringLiteral("export"), tr("Export"), - &MainWindow::on_export, - shortcuts.keySequence(QKeySequences::Key::EXPORT)); - m_editStateWidgets << a; - a = addPannelAction( + m_editStateWidgets << addPannelAction(pannel, QStringLiteral("save"), + tr("Save"), &MainWindow::on_save, + QKeySequence::Save); + m_editStateWidgets << addPannelAction( + pannel, QStringLiteral("saveas"), tr("SaveAs"), + &MainWindow::on_saveas, + shortcuts.keySequence(QKeySequences::Key::SAVE_AS)); + + m_editStateWidgets << addPannelAction( + pannel, QStringLiteral("export"), tr("Export"), + &MainWindow::on_export, + shortcuts.keySequence(QKeySequences::Key::EXPORT)); + m_editStateWidgets << addPannelAction( pannel, QStringLiteral("info"), tr("FileInfo"), [=] { FileInfoDialog(_curfilename, _model->frameSize(), _comment) .exec(); }); - m_editStateWidgets << a; + m_editStateWidgets << addPannelAction(pannel, QStringLiteral("close"), + tr("Close"), + &MainWindow::on_close); } return tab; @@ -988,11 +997,10 @@ RibbonTabContent *MainWindow::buildViewPage(RibbonTabContent *tab) { &MainWindow::on_last, QKeySequence(Qt::Key_Left)); _playDisWidgets << addPannelAction(pannel, QStringLiteral("gifplay"), - tr("Play"), &MainWindow::on_play, - QKeySequence(Qt::Key_F5)); - _btnPlayerStop = - addPannelAction(pannel, QStringLiteral("pause"), tr("Stop"), - &MainWindow::on_stop, QKeySequence(Qt::Key_F10)); + tr("Play"), &MainWindow::on_play); + _btnPlayerStop = addPannelAction(pannel, QStringLiteral("pause"), + tr("Stop"), &MainWindow::on_stop); + _playDisWidgets << addPannelAction( pannel, QStringLiteral("foreword"), tr("NextFrame"), &MainWindow::on_next, QKeySequence(Qt::Key_Right)); @@ -1022,6 +1030,12 @@ RibbonTabContent *MainWindow::buildViewPage(RibbonTabContent *tab) { [this] { _editor->setZoom(250); })); menu->addAction(newAction(QStringLiteral("300%"), [this] { _editor->setZoom(300); })); + menu->addSeparator(); + auto a = + newAction(tr("FitInView"), [this] { _editor->fitInEditorView(); }); + a->setIcon(ICONRES("fitinview")); + menu->addAction(a); + addPannelAction(pannel, QStringLiteral("scaleview"), tr("Scale"), EMPTY_FUNC, {}, menu); @@ -1096,19 +1110,27 @@ void MainWindow::openGif(const QString &filename) { bool MainWindow::readGif(const QString &gif) { GifReader reader; + connect(&reader, &GifReader::sigUpdateUIProcess, this, + [] { qApp->processEvents(); }); + + WaitingLoop dw(tr("ReadingGif")); if (!reader.load(gif)) { return false; } _comment = reader.comment(); _model->readGifReader(&reader); _gallery->setCurrentIndex(_model->index(0)); + _editor->fitOpenSize(); return true; } bool MainWindow::writeGif(const QString &gif, unsigned int loopCount, const QString &comment) { GifWriter writer(_model->width(), _model->height()); + connect(&writer, &GifWriter::sigUpdateUIProcess, this, + [] { qApp->processEvents(); }); + WaitingLoop dw(tr("WritingGif")); writer.setExtString(comment); writer.pushRange(_model->images(), _model->delays()); auto ret = writer.save(gif, loopCount); @@ -1128,8 +1150,14 @@ bool MainWindow::exportGifFrames(const QString &dirPath, const char *ext) { return false; } - for (int i = 0; i < _model->frameCount(); ++i) { - _model->image(i).save(dir.absoluteFilePath(QString::number(i)), ext); + { + WaitingLoop dw(tr("ExportingGif")); + +#pragma omp parallel for + for (int i = 0; i < _model->frameCount(); ++i) { + _model->image(i).save(dir.absoluteFilePath(QString::number(i)), + ext); + } } return true;