Compare commits
9 Commits
1.4.0
...
bbe5e485f4
| Author | SHA1 | Date | |
|---|---|---|---|
| bbe5e485f4 | |||
|
|
6da53ced81 | ||
| 8f0d4da7ad | |||
| 9c8ffb6c1b | |||
| 4c5c5e6fc6 | |||
| b8cb10bb33 | |||
| 6e2f40633e | |||
|
|
a0ba7c1d17 | ||
|
|
b6d759d7c6 |
4
.github/workflows/deepin.yml
vendored
4
.github/workflows/deepin.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
run: |
|
||||
apt update
|
||||
apt install -y git ca-certificates curl file
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Get build dept.
|
||||
run: |
|
||||
apt install -y cmake qt6-base-dev qt6-svg-dev qt6-tools-dev libexiv2-dev
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
run: |
|
||||
cd build
|
||||
apt install -y ./*.deb
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: deepin-25-deb-package
|
||||
path: build/*.deb
|
||||
|
||||
6
.github/workflows/macos.yml
vendored
6
.github/workflows/macos.yml
vendored
@@ -8,11 +8,11 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: '6.10.2'
|
||||
version: '6.10.3'
|
||||
modules: 'qtimageformats'
|
||||
cache: true
|
||||
- name: Install Conan and Dependencies
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
run: |
|
||||
macdeployqt ./ppic.app -dmg
|
||||
ls
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "macos-bundle"
|
||||
path: "*.dmg"
|
||||
|
||||
4
.github/workflows/ubuntu.yml
vendored
4
.github/workflows/ubuntu.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-24-04-build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Get build dept.
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
run: |
|
||||
cd build
|
||||
sudo apt install ./*.deb
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ubuntu-24.04-deb-package
|
||||
path: build/*.deb
|
||||
|
||||
16
.github/workflows/windows.yml
vendored
16
.github/workflows/windows.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- qt_ver: '6.10.2'
|
||||
- qt_ver: '6.10.3'
|
||||
vs: '2022'
|
||||
aqt_arch: 'win64_msvc2022_64'
|
||||
msvc_arch: 'x64'
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
nmake
|
||||
nmake clean
|
||||
windeployqt --verbose=2 --no-quick-import --no-translations --no-opengl-sw --no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types tls,networkinformation release\ppic.exe
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-qmake-package"
|
||||
path: release/*
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- qt_ver: '6.10.2'
|
||||
- qt_ver: '6.10.3'
|
||||
vs: '2022'
|
||||
aqt_arch: 'win64_msvc2022_64'
|
||||
msvc_arch: 'x64'
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
@@ -79,9 +79,9 @@ jobs:
|
||||
ren .\exiv2-0.28.7-2022msvc-AMD64 dependencies_bin
|
||||
echo ::endgroup::
|
||||
echo ::group::===== zlib =====
|
||||
curl -fsSL -o zlib_src.zip https://zlib.net/zlib131.zip
|
||||
curl -fsSL -o zlib_src.zip https://zlib.net/zlib132.zip
|
||||
7z x zlib_src.zip -y -o"dependencies_src"
|
||||
ren .\dependencies_src\zlib-1.3.1 zlib || goto :error
|
||||
ren .\dependencies_src\zlib-1.3.2 zlib || goto :error
|
||||
cmake ./dependencies_src/zlib -Bbuild_dependencies/zlib -DCMAKE_INSTALL_PREFIX="dependencies_bin" || goto :error
|
||||
cmake --build build_dependencies/zlib --config Release --target=install || goto :error
|
||||
echo ::endgroup::
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
if ErrorLevel 8 (exit /B 1)
|
||||
copy LICENSE build\bin
|
||||
exit /B 0
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-cmake-package"
|
||||
path: build/bin/*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(pineapple-pictures VERSION 1.3.0) # don't forget to update NEWS file and AppStream metadata.
|
||||
project(pineapple-pictures VERSION 1.4.1) # don't forget to update NEWS file and AppStream metadata.
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(FeatureSummary)
|
||||
@@ -44,6 +44,7 @@ set (PPIC_CPP_FILES
|
||||
app/graphicsview.cpp
|
||||
app/graphicsscene.cpp
|
||||
app/bottombuttongroup.cpp
|
||||
app/titlebar.cpp
|
||||
app/navigatorview.cpp
|
||||
app/opacityhelper.cpp
|
||||
app/toolbutton.cpp
|
||||
@@ -65,6 +66,7 @@ set (PPIC_HEADER_FILES
|
||||
app/graphicsview.h
|
||||
app/graphicsscene.h
|
||||
app/bottombuttongroup.h
|
||||
app/titlebar.h
|
||||
app/navigatorview.h
|
||||
app/opacityhelper.h
|
||||
app/toolbutton.h
|
||||
|
||||
11
NEWS
11
NEWS
@@ -1,3 +1,14 @@
|
||||
Version 1.4.1
|
||||
~~~~~~~~~~~~~
|
||||
Released: 2026-04-05
|
||||
|
||||
Miscellaneous:
|
||||
* Update translations
|
||||
* Update Qt version to 6.10.3 for Windows and macOS pre-built binaries
|
||||
|
||||
Contributors:
|
||||
Heimen Stoffels (vistaus), Loc Huynh (hthienloc)
|
||||
|
||||
Version 1.4.0
|
||||
~~~~~~~~~~~~~
|
||||
Released: 2026-02-11
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "settings.h"
|
||||
#include "toolbutton.h"
|
||||
#include "bottombuttongroup.h"
|
||||
#include "titlebar.h"
|
||||
#include "graphicsview.h"
|
||||
#include "navigatorview.h"
|
||||
#include "graphicsscene.h"
|
||||
@@ -101,20 +102,20 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
connect(m_graphicsView, &GraphicsView::viewportRectChanged,
|
||||
m_gv, &NavigatorView::updateMainViewportRegion);
|
||||
|
||||
m_closeButton = new ToolButton(true, m_graphicsView);
|
||||
m_closeButton->setIconSize(QSize(32, 32));
|
||||
m_closeButton->setFixedSize(QSize(50, 50));
|
||||
m_closeButton->setIconResourcePath(":/icons/window-close.svg");
|
||||
m_titleBar = new TitleBar(this);
|
||||
m_titleBar->setCloseButtonOnly(!Settings::instance()->showTitleBar());
|
||||
|
||||
connect(m_closeButton, &QAbstractButton::clicked,
|
||||
connect(m_titleBar, &TitleBar::closeRequested,
|
||||
this, &MainWindow::closeWindow);
|
||||
connect(m_titleBar, &TitleBar::maximizeToggleRequested,
|
||||
this, &MainWindow::toggleMaximize);
|
||||
|
||||
m_prevButton = new ToolButton(false, m_graphicsView);
|
||||
m_prevButton = new ToolButton(m_graphicsView);
|
||||
m_prevButton->setIconSize(QSize(75, 75));
|
||||
m_prevButton->setIconResourcePath(":/icons/go-previous.svg");
|
||||
m_prevButton->setVisible(false);
|
||||
m_prevButton->setOpacity(0, false);
|
||||
m_nextButton = new ToolButton(false, m_graphicsView);
|
||||
m_nextButton = new ToolButton(m_graphicsView);
|
||||
m_nextButton->setIconSize(QSize(75, 75));
|
||||
m_nextButton->setIconResourcePath(":/icons/go-next.svg");
|
||||
m_nextButton->setVisible(false);
|
||||
@@ -138,7 +139,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
m_bottomButtonGroup->setOpacity(0, false);
|
||||
m_gv->setOpacity(0, false);
|
||||
m_closeButton->setOpacity(0, false);
|
||||
m_titleBar->setOpacity(0, false);
|
||||
|
||||
connect(m_pm, &PlaylistManager::totalCountChanged, this, &MainWindow::updateGalleryButtonsVisibility);
|
||||
|
||||
@@ -161,7 +162,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
});
|
||||
|
||||
// allow some mouse events can go through these widgets for resizing window.
|
||||
installResizeCapture(m_closeButton);
|
||||
installResizeCapture(m_titleBar);
|
||||
installResizeCapture(m_graphicsView);
|
||||
installResizeCapture(m_graphicsView->viewport());
|
||||
installResizeCapture(m_gv);
|
||||
@@ -323,7 +324,7 @@ void MainWindow::enterEvent(QEnterEvent *event)
|
||||
m_bottomButtonGroup->setOpacity(1);
|
||||
m_gv->setOpacity(1);
|
||||
|
||||
m_closeButton->setOpacity(1);
|
||||
m_titleBar->setOpacity(1);
|
||||
m_prevButton->setOpacity(1);
|
||||
m_nextButton->setOpacity(1);
|
||||
|
||||
@@ -335,7 +336,7 @@ void MainWindow::leaveEvent(QEvent *event)
|
||||
m_bottomButtonGroup->setOpacity(0);
|
||||
m_gv->setOpacity(0);
|
||||
|
||||
m_closeButton->setOpacity(0);
|
||||
m_titleBar->setOpacity(0);
|
||||
m_prevButton->setOpacity(0);
|
||||
m_nextButton->setOpacity(0);
|
||||
|
||||
@@ -612,7 +613,12 @@ void MainWindow::closeWindow()
|
||||
|
||||
void MainWindow::updateWidgetsPosition()
|
||||
{
|
||||
m_closeButton->move(width() - m_closeButton->width(), 0);
|
||||
if (m_titleBar->closeButtonOnly()) {
|
||||
const int bw = m_titleBar->closeButtonWidth();
|
||||
m_titleBar->setGeometry(width() - bw, 0, bw, m_titleBar->height());
|
||||
} else {
|
||||
m_titleBar->setGeometry(0, 0, width(), m_titleBar->height());
|
||||
}
|
||||
m_prevButton->move(25, (height() - m_prevButton->sizeHint().height()) / 2);
|
||||
m_nextButton->move(width() - m_nextButton->sizeHint().width() - 25,
|
||||
(height() - m_prevButton->sizeHint().height()) / 2);
|
||||
@@ -624,7 +630,7 @@ void MainWindow::updateWidgetsPosition()
|
||||
void MainWindow::toggleProtectedMode()
|
||||
{
|
||||
m_protectedMode = !m_protectedMode;
|
||||
m_closeButton->setVisible(!m_protectedMode);
|
||||
m_titleBar->setCloseButtonVisible(!m_protectedMode);
|
||||
updateGalleryButtonsVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ QT_END_NAMESPACE
|
||||
class ActionManager;
|
||||
class PlaylistManager;
|
||||
class ToolButton;
|
||||
class TitleBar;
|
||||
class GraphicsView;
|
||||
class NavigatorView;
|
||||
class BottomButtonGroup;
|
||||
@@ -123,9 +124,9 @@ private:
|
||||
QPropertyAnimation *m_floatUpAnimation;
|
||||
QParallelAnimationGroup *m_exitAnimationGroup;
|
||||
QFileSystemWatcher *m_fileSystemWatcher;
|
||||
ToolButton *m_closeButton;
|
||||
ToolButton *m_prevButton;
|
||||
ToolButton *m_nextButton;
|
||||
TitleBar *m_titleBar;
|
||||
GraphicsView *m_graphicsView;
|
||||
NavigatorView *m_gv;
|
||||
BottomButtonGroup *m_bottomButtonGroup;
|
||||
|
||||
@@ -55,6 +55,11 @@ bool Settings::useBuiltInCloseAnimation() const
|
||||
return m_qsettings->value("use_built_in_close_animation", true).toBool();
|
||||
}
|
||||
|
||||
bool Settings::showTitleBar() const
|
||||
{
|
||||
return m_qsettings->value("show_title_bar", true).toBool();
|
||||
}
|
||||
|
||||
bool Settings::useLightCheckerboard() const
|
||||
{
|
||||
return m_qsettings->value("use_light_checkerboard", false).toBool();
|
||||
@@ -123,6 +128,12 @@ void Settings::setUseBuiltInCloseAnimation(bool on)
|
||||
m_qsettings->sync();
|
||||
}
|
||||
|
||||
void Settings::setShowTitleBar(bool on)
|
||||
{
|
||||
m_qsettings->setValue("show_title_bar", on);
|
||||
m_qsettings->sync();
|
||||
}
|
||||
|
||||
void Settings::setUseLightCheckerboard(bool light)
|
||||
{
|
||||
m_qsettings->setValue("use_light_checkerboard", light);
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
|
||||
bool stayOnTop() const;
|
||||
bool useBuiltInCloseAnimation() const;
|
||||
bool showTitleBar() const;
|
||||
bool useLightCheckerboard() const;
|
||||
bool loopGallery() const;
|
||||
bool autoLongImageMode() const;
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
|
||||
void setStayOnTop(bool on);
|
||||
void setUseBuiltInCloseAnimation(bool on);
|
||||
void setShowTitleBar(bool on);
|
||||
void setUseLightCheckerboard(bool light);
|
||||
void setLoopGallery(bool on);
|
||||
void setAutoLongImageMode(bool on);
|
||||
|
||||
@@ -21,6 +21,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_stayOnTop(new QCheckBox)
|
||||
, m_useBuiltInCloseAnimation(new QCheckBox)
|
||||
, m_showTitleBar(new QCheckBox)
|
||||
, m_useLightCheckerboard(new QCheckBox)
|
||||
, m_loopGallery(new QCheckBox)
|
||||
, m_autoLongImageMode(new QCheckBox)
|
||||
@@ -123,6 +124,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
||||
|
||||
settingsForm->addRow(tr("Stay on top when start-up"), m_stayOnTop);
|
||||
settingsForm->addRow(tr("Use built-in close window animation"), m_useBuiltInCloseAnimation);
|
||||
settingsForm->addRow(tr("Show title bar"), m_showTitleBar);
|
||||
settingsForm->addRow(tr("Use light-color checkerboard"), m_useLightCheckerboard);
|
||||
settingsForm->addRow(tr("Loop the loaded gallery"), m_loopGallery);
|
||||
settingsForm->addRow(tr("Auto long image mode"), m_autoLongImageMode);
|
||||
@@ -134,6 +136,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
||||
|
||||
m_stayOnTop->setChecked(Settings::instance()->stayOnTop());
|
||||
m_useBuiltInCloseAnimation->setChecked(Settings::instance()->useBuiltInCloseAnimation());
|
||||
m_showTitleBar->setChecked(Settings::instance()->showTitleBar());
|
||||
m_useLightCheckerboard->setChecked(Settings::instance()->useLightCheckerboard());
|
||||
m_loopGallery->setChecked(Settings::instance()->loopGallery());
|
||||
m_autoLongImageMode->setChecked(Settings::instance()->autoLongImageMode());
|
||||
@@ -172,6 +175,10 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
||||
Settings::instance()->setUseBuiltInCloseAnimation(state == Qt::Checked);
|
||||
});
|
||||
|
||||
connect(m_showTitleBar, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
||||
Settings::instance()->setShowTitleBar(state == Qt::Checked);
|
||||
});
|
||||
|
||||
connect(m_useLightCheckerboard, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
||||
Settings::instance()->setUseLightCheckerboard(state == Qt::Checked);
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ public slots:
|
||||
private:
|
||||
QCheckBox * m_stayOnTop = nullptr;
|
||||
QCheckBox * m_useBuiltInCloseAnimation = nullptr;
|
||||
QCheckBox * m_showTitleBar = nullptr;
|
||||
QCheckBox * m_useLightCheckerboard = nullptr;
|
||||
QCheckBox * m_loopGallery = nullptr;
|
||||
QCheckBox * m_autoLongImageMode = nullptr;
|
||||
|
||||
218
app/titlebar.cpp
Normal file
218
app/titlebar.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gary Wang <git@blumia.net>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "titlebar.h"
|
||||
|
||||
#include "opacityhelper.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QMouseEvent>
|
||||
#include <QEvent>
|
||||
#include <QEnterEvent>
|
||||
#include <QWindow>
|
||||
#include <QCursor>
|
||||
|
||||
TitleBar::TitleBar(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_opacityHelper(new OpacityHelper(this))
|
||||
, m_closeIcon(QStringLiteral(":/icons/window-close.svg"))
|
||||
{
|
||||
setFixedHeight(32);
|
||||
setMouseTracking(true);
|
||||
setAttribute(Qt::WA_Hover, true);
|
||||
|
||||
if (QWidget *win = window())
|
||||
win->installEventFilter(this);
|
||||
}
|
||||
|
||||
void TitleBar::setOpacity(qreal opacity, bool animated)
|
||||
{
|
||||
m_opacityHelper->setOpacity(opacity, animated);
|
||||
}
|
||||
|
||||
void TitleBar::setCloseButtonVisible(bool visible)
|
||||
{
|
||||
if (m_closeButtonVisible == visible)
|
||||
return;
|
||||
m_closeButtonVisible = visible;
|
||||
if (!visible) {
|
||||
m_closeHovered = false;
|
||||
m_closePressed = false;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void TitleBar::setCloseButtonOnly(bool only)
|
||||
{
|
||||
if (m_closeButtonOnly == only)
|
||||
return;
|
||||
m_closeButtonOnly = only;
|
||||
update();
|
||||
}
|
||||
|
||||
QRect TitleBar::closeButtonRect() const
|
||||
{
|
||||
const int btnWidth = closeButtonWidth();
|
||||
return QRect(width() - btnWidth, 0, btnWidth, height());
|
||||
}
|
||||
|
||||
void TitleBar::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
QPainter painter(this);
|
||||
|
||||
// Subtle translucent backdrop so the title bar region is distinguishable
|
||||
// (similar to the bottom button group). Skipped in close-button-only mode.
|
||||
if (!m_closeButtonOnly) {
|
||||
painter.fillRect(rect(), QColor(0, 0, 0, 120));
|
||||
}
|
||||
|
||||
const QRect closeRect = closeButtonRect();
|
||||
|
||||
// Title text (leave room for the close button).
|
||||
QRect labelRect = rect().adjusted(8, 0, 0, 0);
|
||||
if (m_closeButtonVisible)
|
||||
labelRect.setRight(closeRect.left() - 2);
|
||||
|
||||
const QString title = window() ? window()->windowTitle() : QString();
|
||||
if (!m_closeButtonOnly && !title.isEmpty()) {
|
||||
const QString elided = painter.fontMetrics().elidedText(title, Qt::ElideRight, labelRect.width());
|
||||
const int flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine;
|
||||
painter.setPen(Qt::black);
|
||||
painter.drawText(labelRect.adjusted(1, 1, 1, 1), flags, elided);
|
||||
painter.setPen(Qt::white);
|
||||
painter.drawText(labelRect, flags, elided);
|
||||
}
|
||||
|
||||
if (m_closeButtonVisible) {
|
||||
if (m_closeHovered) {
|
||||
painter.fillRect(closeRect,
|
||||
m_closePressed ? QColor(0xC5, 0x0F, 0x1F)
|
||||
: QColor(0xE8, 0x11, 0x23));
|
||||
}
|
||||
const int sz = height() / 3 * 2;
|
||||
const QRect iconRect = QStyle::alignedRect(layoutDirection(), Qt::AlignCenter,
|
||||
QSize(sz, sz), closeRect);
|
||||
m_closeIcon.paint(&painter, iconRect);
|
||||
}
|
||||
}
|
||||
|
||||
void TitleBar::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton) {
|
||||
QWidget::mousePressEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_closeButtonVisible && closeButtonRect().contains(event->pos())) {
|
||||
m_closePressed = true;
|
||||
m_dragPending = false;
|
||||
update();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
m_dragPending = true;
|
||||
m_moveStartPos = event->pos();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void TitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (m_closeButtonVisible) {
|
||||
const bool hovered = closeButtonRect().contains(event->pos());
|
||||
if (hovered != m_closeHovered) {
|
||||
m_closeHovered = hovered;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
if (event->buttons() & Qt::LeftButton && m_dragPending
|
||||
&& !window()->isMaximized() && !window()->isFullScreen()) {
|
||||
if (QWindow *wh = window()->windowHandle()) {
|
||||
if (!wh->startSystemMove())
|
||||
window()->move(event->globalPosition().toPoint() - m_moveStartPos);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
QWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void TitleBar::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const bool wasClosePressed = m_closePressed;
|
||||
m_closePressed = false;
|
||||
m_dragPending = false;
|
||||
update();
|
||||
if (wasClosePressed && m_closeButtonVisible
|
||||
&& closeButtonRect().contains(event->pos())) {
|
||||
emit closeRequested();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QWidget::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton
|
||||
&& !(m_closeButtonVisible && closeButtonRect().contains(event->pos()))) {
|
||||
emit maximizeToggleRequested();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void TitleBar::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
if (m_closeButtonVisible) {
|
||||
const bool hovered = closeButtonRect().contains(mapFromGlobal(QCursor::pos()));
|
||||
if (hovered != m_closeHovered) {
|
||||
m_closeHovered = hovered;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget::enterEvent(event);
|
||||
}
|
||||
|
||||
void TitleBar::leaveEvent(QEvent *event)
|
||||
{
|
||||
if (m_closeHovered) {
|
||||
m_closeHovered = false;
|
||||
update();
|
||||
}
|
||||
|
||||
QWidget::leaveEvent(event);
|
||||
}
|
||||
|
||||
bool TitleBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == window()) {
|
||||
switch (event->type()) {
|
||||
case QEvent::WindowTitleChange:
|
||||
case QEvent::WindowStateChange:
|
||||
case QEvent::ActivationChange:
|
||||
update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
QSize TitleBar::sizeHint() const
|
||||
{
|
||||
return QSize(0, 32);
|
||||
}
|
||||
60
app/titlebar.h
Normal file
60
app/titlebar.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gary Wang <git@blumia.net>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef TITLEBAR_H
|
||||
#define TITLEBAR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QIcon>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPaintEvent;
|
||||
class QMouseEvent;
|
||||
class QEvent;
|
||||
class QEnterEvent;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class OpacityHelper;
|
||||
|
||||
class TitleBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TitleBar(QWidget *parent = nullptr);
|
||||
|
||||
void setOpacity(qreal opacity, bool animated = true);
|
||||
void setCloseButtonVisible(bool visible);
|
||||
bool closeButtonOnly() const { return m_closeButtonOnly; }
|
||||
void setCloseButtonOnly(bool only);
|
||||
int closeButtonWidth() const { return qMax(height(), 46); }
|
||||
|
||||
signals:
|
||||
void closeRequested();
|
||||
void maximizeToggleRequested();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
QSize sizeHint() const override;
|
||||
|
||||
private:
|
||||
QRect closeButtonRect() const;
|
||||
|
||||
OpacityHelper *m_opacityHelper;
|
||||
QIcon m_closeIcon;
|
||||
bool m_closeButtonVisible = true;
|
||||
bool m_closeButtonOnly = false;
|
||||
bool m_closeHovered = false;
|
||||
bool m_closePressed = false;
|
||||
bool m_dragPending = false;
|
||||
QPoint m_moveStartPos;
|
||||
};
|
||||
|
||||
#endif // TITLEBAR_H
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <QGraphicsOpacityEffect>
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
ToolButton::ToolButton(bool hoverColor, QWidget *parent)
|
||||
ToolButton::ToolButton(QWidget *parent)
|
||||
: QPushButton(parent)
|
||||
, m_opacityHelper(new OpacityHelper(this))
|
||||
{
|
||||
@@ -19,11 +19,6 @@ ToolButton::ToolButton(bool hoverColor, QWidget *parent)
|
||||
QString qss = "QPushButton {"
|
||||
"background: transparent;"
|
||||
"}";
|
||||
if (hoverColor) {
|
||||
qss += "QPushButton:hover {"
|
||||
"background: red;"
|
||||
"}";
|
||||
}
|
||||
setStyleSheet(qss);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class ToolButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToolButton(bool hoverColor = false, QWidget * parent = nullptr);
|
||||
ToolButton(QWidget * parent = nullptr);
|
||||
void setIconResourcePath(const QString &iconp);
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -236,27 +236,27 @@
|
||||
<location filename="../mainwindow.cpp" line="734"/>
|
||||
<location filename="../mainwindow.cpp" line="764"/>
|
||||
<source>Save As</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>다른 이름으로 저장</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="710"/>
|
||||
<source>No image is currently open.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>현재 열려 있는 이미지가 없습니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="722"/>
|
||||
<source>%1 Image (*.%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>%1 이미지 (*.%2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="728"/>
|
||||
<source>No supported image formats are available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>지원되는 이미지 형식을 사용할 수 없습니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="765"/>
|
||||
<source>Failed to save image: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>이미지 저장 실패: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="854"/>
|
||||
@@ -320,7 +320,7 @@
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="88"/>
|
||||
<source>Save &As...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>다른 이름으로 저장(&A)...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="93"/>
|
||||
|
||||
@@ -240,27 +240,27 @@
|
||||
<location filename="../mainwindow.cpp" line="734"/>
|
||||
<location filename="../mainwindow.cpp" line="764"/>
|
||||
<source>Save As</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Opslaan als</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="710"/>
|
||||
<source>No image is currently open.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Er is geen afbeelding geopend.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="722"/>
|
||||
<source>%1 Image (*.%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>%1-afbeelding (*.%2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="728"/>
|
||||
<source>No supported image formats are available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Er zijn geen ondersteunde formaten beschikbaar.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="765"/>
|
||||
<source>Failed to save image: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>De afbeelding kan niet worden opgeslagen: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="854"/>
|
||||
@@ -324,7 +324,7 @@
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="88"/>
|
||||
<source>Save &As...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Opslaan &als…</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="93"/>
|
||||
@@ -359,7 +359,7 @@
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="107"/>
|
||||
<source>Fit long image</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Lange afbeelding inpassen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="110"/>
|
||||
@@ -891,7 +891,7 @@
|
||||
<message>
|
||||
<location filename="../settingsdialog.cpp" line="129"/>
|
||||
<source>Limit SVG support to SVG Tiny 1.2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Svg-ondersteuning beperken tot SVG Tiny 1.2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsdialog.cpp" line="130"/>
|
||||
|
||||
@@ -225,27 +225,27 @@ Loc Huynh <https://github.com/hthienloc></translation>
|
||||
<location filename="../mainwindow.cpp" line="734"/>
|
||||
<location filename="../mainwindow.cpp" line="764"/>
|
||||
<source>Save As</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Lưu dưới dạng</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="710"/>
|
||||
<source>No image is currently open.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Hiện tại không có hình ảnh nào được mở.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="722"/>
|
||||
<source>%1 Image (*.%2)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Hình ảnh %1 (*.%2)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="728"/>
|
||||
<source>No supported image formats are available.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Không có định dạng hình ảnh được hỗ trợ.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="765"/>
|
||||
<source>Failed to save image: %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Không lưu được hình ảnh: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mainwindow.cpp" line="854"/>
|
||||
@@ -309,7 +309,7 @@ Loc Huynh <https://github.com/hthienloc></translation>
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="88"/>
|
||||
<source>Save &As...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Lưu &dưới dạng...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../actionmanager.cpp" line="93"/>
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -11,7 +11,7 @@ environment:
|
||||
- job_name: mingw_64_qt6_8
|
||||
QTDIR: C:\Qt\6.8\mingw_64
|
||||
MINGW64: C:\Qt\mingw_64\Tools\mingw1310_64
|
||||
KF_BRANCH: master
|
||||
KF_BRANCH: v6.24.0
|
||||
EXIV2_VERSION: "0.28.7"
|
||||
EXIV2_CMAKE_OPTIONS: "-DEXIV2_ENABLE_BROTLI=OFF -DEXIV2_ENABLE_INIH=OFF -DEXIV2_BUILD_EXIV2_COMMAND=OFF"
|
||||
PPIC_CMAKE_OPTIONS: "-DPREFER_QT_5=OFF"
|
||||
@@ -39,9 +39,9 @@ build_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
# download and install zlib for KArchive
|
||||
- cd %LIBZ%
|
||||
- curl -fsSL -o zlib131.zip https://zlib.net/zlib131.zip
|
||||
- 7z x zlib131.zip -y
|
||||
- cd zlib-1.3.1
|
||||
- curl -fsSL -o zlib132.zip https://zlib.net/zlib132.zip
|
||||
- 7z x zlib132.zip -y
|
||||
- cd zlib-1.3.2
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX%
|
||||
@@ -134,7 +134,7 @@ build_script:
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libexpat-1.dll
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libexiv2.dll
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libavif.dll
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libzlib.dll
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libz.dll
|
||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libKF?Archive.dll
|
||||
- windeployqt %WINDEPLOYQT_ARGS% .\ppic.exe
|
||||
# copy 3rdparty licenses for the libs we vendored for windows...
|
||||
|
||||
@@ -4,18 +4,26 @@
|
||||
<name>Pineapple Pictures</name>
|
||||
<name xml:lang="es">Pineapple Pictures</name>
|
||||
<name xml:lang="ja">Pineapple Pictures</name>
|
||||
<name xml:lang="ko">Pineapple Pictures</name>
|
||||
<name xml:lang="nl">Pineapple Afbeeldingen</name>
|
||||
<name xml:lang="ru">Pineapple Pictures</name>
|
||||
<name xml:lang="sl">Pineapple Slike</name>
|
||||
<name xml:lang="ta">அன்னாசி படங்கள்</name>
|
||||
<name xml:lang="tr">Pineapple Resimler</name>
|
||||
<name xml:lang="uk">Pineapple Pictures</name>
|
||||
<name xml:lang="vi">Pineapple Pictures</name>
|
||||
<name xml:lang="zh-CN">菠萝看图</name>
|
||||
<summary>Image Viewer</summary>
|
||||
<summary xml:lang="es">Visor de imágenes</summary>
|
||||
<summary xml:lang="ja">画像ビューアー</summary>
|
||||
<summary xml:lang="ko">이미지 뷰어</summary>
|
||||
<summary xml:lang="nl">Afbeeldingsweergave</summary>
|
||||
<summary xml:lang="ru">Просмотр изображений</summary>
|
||||
<summary xml:lang="sl">Pregledovalnik slik</summary>
|
||||
<summary xml:lang="ta">பட பார்வையாளர்</summary>
|
||||
<summary xml:lang="tr">Resim Görüntüleyici</summary>
|
||||
<summary xml:lang="uk">Переглядач зображень</summary>
|
||||
<summary xml:lang="vi">Trình xem ảnh</summary>
|
||||
<summary xml:lang="zh-CN">图像查看器</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
@@ -23,10 +31,13 @@
|
||||
<p>Pineapple Pictures is a lightweight and easy-to-use image viewer that comes with a handy navigation thumbnail when zoom-in, and doesn't contain any image management support.</p>
|
||||
<p xml:lang="es">Pineapple Pictures es un visor de imágenes ligero y fácil de usar que viene con una práctica miniatura de navegación al hacer zoom, y no contiene ningún soporte de gestión de imágenes.</p>
|
||||
<p xml:lang="ja">Pineapple Picturesは、ズームイン時に便利なナビゲーションサムネイルを備えた軽量で使いやすい画像ビューアです。画像管理のサポートは含まれていません。</p>
|
||||
<p xml:lang="ko">Pineapple Pictures는 가볍고 사용하기 쉬운 이미지 뷰어로, 확대 시 편리한 내비게이션 썸네일과 함께 제공되며 이미지 관리 지원이 포함되어 있지 않습니다.</p>
|
||||
<p xml:lang="nl">Pineapple Afbeeldingen is een licht en eenvoudig te gebruiken afbeeldingsweergaveprogramma met miniatuurnavigatie na inzoomen. Het programma heeft echter geen fotobeheermogelijkheid.</p>
|
||||
<p xml:lang="ru">Pineapple Pictures - это легкий и простой в использовании просмотрщик изображений, оснащенный удобной навигацией по миниатюрам при увеличении масштаба и не содержащий никакой поддержки управления изображениями.</p>
|
||||
<p xml:lang="sl">Pineapple Pictures je lahek in enostaven pregledovalnik slik, ki ima pri povečavi priročno sličico za navigacijo in ne vsebuje nobene podpore za upravljanje slik.</p>
|
||||
<p xml:lang="ta">அன்னாசி படங்கள் ஒரு இலகுரக மற்றும் பயன்படுத்த எளிதான பட பார்வையாளராகும், இது பெரிதாக்கும்போது ஒரு எளிமையான வழிசெலுத்தல் சிறுபடத்துடன் வருகிறது, மேலும் எந்த பட மேலாண்மை ஆதரவையும் கொண்டிருக்கவில்லை.</p>
|
||||
<p xml:lang="uk">Pineapple Pictures – це легкий і простий у використанні переглядач зображень, який постачається зі зручною навігаційною мініатюрою при збільшенні масштабу і не містить жодної підтримки керування зображеннями.</p>
|
||||
<p xml:lang="vi">Pineapple Pictures là trình xem hình ảnh nhẹ và dễ sử dụng, đi kèm với hình thu nhỏ điều hướng tiện dụng khi phóng to và không chứa bất kỳ hỗ trợ quản lý hình ảnh nào.</p>
|
||||
<p xml:lang="zh-CN">菠萝看图是一个轻量级易用的图像查看器,在图片放大时提供了方便的鸟瞰导航功能,且不包含任何图片管理功能。</p>
|
||||
</description>
|
||||
<developer id="net.blumia">
|
||||
@@ -50,10 +61,13 @@
|
||||
<caption>Main window when an image file is loaded</caption>
|
||||
<caption xml:lang="es">Ventana principal cuando se carga un archivo de imagen</caption>
|
||||
<caption xml:lang="ja">画像ファイル読み込み時のメインウィンドウ</caption>
|
||||
<caption xml:lang="ko">이미지 파일이 로드될 때 기본 창</caption>
|
||||
<caption xml:lang="nl">Hoofdvenster na het laden van een afbeelding</caption>
|
||||
<caption xml:lang="ru">Основное окно после загрузки файла изображения</caption>
|
||||
<caption xml:lang="sl">Glavno okno ob nalaganju slikovne datoteke</caption>
|
||||
<caption xml:lang="ta">ஒரு படக் கோப்பு ஏற்றப்படும் போது முதன்மையான சாளரம்</caption>
|
||||
<caption xml:lang="uk">Головне вікно після завантаження файлу зображення</caption>
|
||||
<caption xml:lang="vi">Cửa sổ chính khi tải tệp hình ảnh</caption>
|
||||
<caption xml:lang="zh-CN">加载图片后的主窗口</caption>
|
||||
<image type="source" width="1503" height="640">https://pineapple-pictures.sourceforge.io/ppic-gui-static.png</image>
|
||||
</screenshot>
|
||||
@@ -61,10 +75,13 @@
|
||||
<caption>Zooming in a raster image</caption>
|
||||
<caption xml:lang="es">Ampliar una imagen rasterizada</caption>
|
||||
<caption xml:lang="ja">ラスター画像の拡大</caption>
|
||||
<caption xml:lang="ko">래스터 이미지 확대하기</caption>
|
||||
<caption xml:lang="nl">Inzoomen op een roosterafbeelding</caption>
|
||||
<caption xml:lang="ru">Масштабирование растрового изображения</caption>
|
||||
<caption xml:lang="sl">Povečevanje rastrske slike</caption>
|
||||
<caption xml:lang="ta">ராச்டர் படத்தில் பெரிதாக்குதல்</caption>
|
||||
<caption xml:lang="uk">Масштабування растрового зображення</caption>
|
||||
<caption xml:lang="vi">Phóng to hình ảnh raster</caption>
|
||||
<caption xml:lang="zh-CN">放大浏览位图</caption>
|
||||
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-raster.png</image>
|
||||
</screenshot>
|
||||
@@ -72,15 +89,29 @@
|
||||
<caption>Zooming in a vector image</caption>
|
||||
<caption xml:lang="es">Ampliar una imagen vectorial</caption>
|
||||
<caption xml:lang="ja">ベクター画像の拡大</caption>
|
||||
<caption xml:lang="ko">벡터 이미지 확대하기</caption>
|
||||
<caption xml:lang="nl">Inzoomen op een vectorafbeelding</caption>
|
||||
<caption xml:lang="ru">Масштабирование векторного изображения</caption>
|
||||
<caption xml:lang="sl">Povečevanje vektorske slike</caption>
|
||||
<caption xml:lang="ta">ஒரு திசையன் படத்தில் பெரிதாக்குதல்</caption>
|
||||
<caption xml:lang="uk">Масштабування векторного зображення</caption>
|
||||
<caption xml:lang="vi">Phóng to hình ảnh vector</caption>
|
||||
<caption xml:lang="zh-CN">放大浏览矢量图</caption>
|
||||
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-svg.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release type="stable" version="1.4.1" date="2026-04-05T00:00:00Z">
|
||||
<description>
|
||||
<p>This release includes the following changes:</p>
|
||||
<ul>
|
||||
<li>Update translations</li>
|
||||
<li>Update Qt version to 6.10.3 for Windows and macOS pre-built binaries</li>
|
||||
</ul>
|
||||
<p>With contributions from:</p>
|
||||
<p>Heimen Stoffels (vistaus), Loc Huynh (hthienloc)</p>
|
||||
</description>
|
||||
</release>
|
||||
<release type="stable" version="1.4.0" date="2026-02-11T00:00:00Z">
|
||||
<description>
|
||||
<p>This release adds the following features:</p>
|
||||
|
||||
Reference in New Issue
Block a user