Compare commits
41
Commits
1.4.0
...
31303a20c7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31303a20c7 | ||
|
|
319fd64fc6 | ||
|
|
cdd0b764a7 | ||
|
|
2a69482adb | ||
|
|
fc3ef1cd7f | ||
|
|
1de1517a81 | ||
|
|
04a4ab9bff | ||
|
|
011c1e858f | ||
|
|
4fe09757b3 | ||
|
|
82f1f10cca | ||
|
|
60eebea741 | ||
|
|
9b151e2fea | ||
|
|
9db9c3e9f7 | ||
|
|
9a18a41b8f | ||
|
|
c93ed94210 | ||
|
|
f4d272e014 | ||
|
|
d536e8c611 | ||
|
|
c8dfccb460 | ||
|
|
6fde9474ae | ||
|
|
c19fcaa560 | ||
|
|
602e2cccf8 | ||
|
|
3c1a3c54a7 | ||
|
|
032752faf9 | ||
|
|
457647c59f | ||
|
|
821e0f9fd6 | ||
|
|
26f51e418d | ||
|
|
cd2eac44f1 | ||
|
|
f0bb945c7d | ||
|
|
870e472f53 | ||
|
|
76677381de | ||
|
|
d2bd521ad0 | ||
|
|
265e894b3f | ||
|
|
6339a93c6c | ||
|
|
6da53ced81 | ||
|
|
8f0d4da7ad | ||
|
|
9c8ffb6c1b | ||
|
|
4c5c5e6fc6 | ||
|
|
b8cb10bb33 | ||
|
|
6e2f40633e | ||
|
|
a0ba7c1d17 | ||
|
|
b6d759d7c6 |
@@ -1,35 +0,0 @@
|
|||||||
name: deepin 25 CI
|
|
||||||
|
|
||||||
on: [push, pull_request, workflow_dispatch]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-deepin-25-deb:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: linuxdeepin/deepin:crimson
|
|
||||||
options: --user root
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Update package lists and install basic tools
|
|
||||||
run: |
|
|
||||||
apt update
|
|
||||||
apt install -y git ca-certificates curl file
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Get build dept.
|
|
||||||
run: |
|
|
||||||
apt install -y cmake qt6-base-dev qt6-svg-dev qt6-tools-dev libexiv2-dev
|
|
||||||
- name: Build it
|
|
||||||
run: |
|
|
||||||
git config --global --add safe.directory '*' # to avoid "fatal: detected dubious ownership in repository at xxx"
|
|
||||||
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr .
|
|
||||||
cmake --build build -j
|
|
||||||
cd build
|
|
||||||
cpack -G DEB
|
|
||||||
- name: Try install it
|
|
||||||
run: |
|
|
||||||
cd build
|
|
||||||
apt install -y ./*.deb
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: deepin-25-deb-package
|
|
||||||
path: build/*.deb
|
|
||||||
@@ -8,11 +8,11 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
version: '6.10.2'
|
version: '6.10.3'
|
||||||
modules: 'qtimageformats'
|
modules: 'qtimageformats'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Install Conan and Dependencies
|
- name: Install Conan and Dependencies
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
macdeployqt ./ppic.app -dmg
|
macdeployqt ./ppic.app -dmg
|
||||||
ls
|
ls
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "macos-bundle"
|
name: "macos-bundle"
|
||||||
path: "*.dmg"
|
path: "*.dmg"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ jobs:
|
|||||||
ubuntu-24-04-build:
|
ubuntu-24-04-build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Get build dept.
|
- name: Get build dept.
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
sudo apt install ./*.deb
|
sudo apt install ./*.deb
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ubuntu-24.04-deb-package
|
name: ubuntu-24.04-deb-package
|
||||||
path: build/*.deb
|
path: build/*.deb
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- qt_ver: '6.10.2'
|
- qt_ver: '6.10.3'
|
||||||
vs: '2022'
|
vs: '2022'
|
||||||
aqt_arch: 'win64_msvc2022_64'
|
aqt_arch: 'win64_msvc2022_64'
|
||||||
msvc_arch: 'x64'
|
msvc_arch: 'x64'
|
||||||
@@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
nmake
|
nmake
|
||||||
nmake clean
|
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
|
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:
|
with:
|
||||||
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-qmake-package"
|
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-qmake-package"
|
||||||
path: release/*
|
path: release/*
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- qt_ver: '6.10.2'
|
- qt_ver: '6.10.3'
|
||||||
vs: '2022'
|
vs: '2022'
|
||||||
aqt_arch: 'win64_msvc2022_64'
|
aqt_arch: 'win64_msvc2022_64'
|
||||||
msvc_arch: 'x64'
|
msvc_arch: 'x64'
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v4
|
uses: jurplel/install-qt-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -79,9 +79,9 @@ jobs:
|
|||||||
ren .\exiv2-0.28.7-2022msvc-AMD64 dependencies_bin
|
ren .\exiv2-0.28.7-2022msvc-AMD64 dependencies_bin
|
||||||
echo ::endgroup::
|
echo ::endgroup::
|
||||||
echo ::group::===== zlib =====
|
echo ::group::===== zlib =====
|
||||||
curl -fsSL -o zlib_src.zip https://zlib.net/zlib131.zip
|
curl -fsSL -o zlib_src.zip https://github.com/madler/zlib/releases/download/v1.3.2/zlib132.zip
|
||||||
7z x zlib_src.zip -y -o"dependencies_src"
|
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 ./dependencies_src/zlib -Bbuild_dependencies/zlib -DCMAKE_INSTALL_PREFIX="dependencies_bin" || goto :error
|
||||||
cmake --build build_dependencies/zlib --config Release --target=install || goto :error
|
cmake --build build_dependencies/zlib --config Release --target=install || goto :error
|
||||||
echo ::endgroup::
|
echo ::endgroup::
|
||||||
@@ -125,17 +125,13 @@ jobs:
|
|||||||
cmake --build build --config Release
|
cmake --build build --config Release
|
||||||
cmake --install build --config Release --prefix "%PWD%\build"
|
cmake --install build --config Release --prefix "%PWD%\build"
|
||||||
echo ::endgroup::
|
echo ::endgroup::
|
||||||
echo ::group::===== passoc stuff =====
|
|
||||||
curl -fsSL -o passoc-standalone.zip https://github.com/BLumia/pineapple-assoc-manager/releases/latest/download/pineapple-assoc-manager-qt${{ matrix.qt_ver }}-standalone.zip
|
|
||||||
7z x passoc-standalone.zip passoc.exe -y -o"build/bin"
|
|
||||||
echo ::endgroup::
|
|
||||||
:: ------ pkg ------
|
:: ------ pkg ------
|
||||||
windeployqt --verbose=2 --no-compiler-runtime --no-quick-import --no-translations --no-network --no-opengl-sw --no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types tls,networkinformation build\bin\ppic.exe
|
windeployqt --verbose=2 --no-compiler-runtime --no-quick-import --no-translations --no-network --no-opengl-sw --no-system-d3d-compiler --no-system-dxc-compiler --skip-plugin-types tls,networkinformation build\bin\ppic.exe
|
||||||
robocopy ./dependencies_bin/bin build/bin *.dll
|
robocopy ./dependencies_bin/bin build/bin *.dll
|
||||||
if ErrorLevel 8 (exit /B 1)
|
if ErrorLevel 8 (exit /B 1)
|
||||||
copy LICENSE build\bin
|
copy LICENSE build\bin
|
||||||
exit /B 0
|
exit /B 0
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-cmake-package"
|
name: "windows-msvc${{ matrix.vs }}-qt${{ matrix.qt_ver }}-cmake-package"
|
||||||
path: build/bin/*
|
path: build/bin/*
|
||||||
|
|||||||
+7
-13
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
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.5.0) # don't forget to update NEWS file and AppStream metadata.
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
@@ -16,6 +16,7 @@ set (CMAKE_CXX_STANDARD 17)
|
|||||||
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
set (CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set (CMAKE_AUTOMOC ON)
|
set (CMAKE_AUTOMOC ON)
|
||||||
set (CMAKE_AUTORCC ON)
|
set (CMAKE_AUTORCC ON)
|
||||||
|
set (CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
|
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core)
|
||||||
|
|
||||||
@@ -45,8 +46,6 @@ set (PPIC_CPP_FILES
|
|||||||
app/graphicsscene.cpp
|
app/graphicsscene.cpp
|
||||||
app/bottombuttongroup.cpp
|
app/bottombuttongroup.cpp
|
||||||
app/navigatorview.cpp
|
app/navigatorview.cpp
|
||||||
app/opacityhelper.cpp
|
|
||||||
app/toolbutton.cpp
|
|
||||||
app/settings.cpp
|
app/settings.cpp
|
||||||
app/settingsdialog.cpp
|
app/settingsdialog.cpp
|
||||||
app/aboutdialog.cpp
|
app/aboutdialog.cpp
|
||||||
@@ -54,7 +53,6 @@ set (PPIC_CPP_FILES
|
|||||||
app/metadatadialog.cpp
|
app/metadatadialog.cpp
|
||||||
app/exiv2wrapper.cpp
|
app/exiv2wrapper.cpp
|
||||||
app/playlistmanager.cpp
|
app/playlistmanager.cpp
|
||||||
app/shortcutedit.cpp
|
|
||||||
app/fileopeneventhandler.cpp
|
app/fileopeneventhandler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,8 +64,6 @@ set (PPIC_HEADER_FILES
|
|||||||
app/graphicsscene.h
|
app/graphicsscene.h
|
||||||
app/bottombuttongroup.h
|
app/bottombuttongroup.h
|
||||||
app/navigatorview.h
|
app/navigatorview.h
|
||||||
app/opacityhelper.h
|
|
||||||
app/toolbutton.h
|
|
||||||
app/settings.h
|
app/settings.h
|
||||||
app/settingsdialog.h
|
app/settingsdialog.h
|
||||||
app/aboutdialog.h
|
app/aboutdialog.h
|
||||||
@@ -75,10 +71,13 @@ set (PPIC_HEADER_FILES
|
|||||||
app/metadatadialog.h
|
app/metadatadialog.h
|
||||||
app/exiv2wrapper.h
|
app/exiv2wrapper.h
|
||||||
app/playlistmanager.h
|
app/playlistmanager.h
|
||||||
app/shortcutedit.h
|
|
||||||
app/fileopeneventhandler.h
|
app/fileopeneventhandler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set (PPIC_UI_FILES
|
||||||
|
app/settingsdialog.ui
|
||||||
|
)
|
||||||
|
|
||||||
set (PPIC_QRC_FILES
|
set (PPIC_QRC_FILES
|
||||||
assets/resources.qrc
|
assets/resources.qrc
|
||||||
)
|
)
|
||||||
@@ -101,6 +100,7 @@ endif ()
|
|||||||
add_executable (${EXE_NAME}
|
add_executable (${EXE_NAME}
|
||||||
${PPIC_HEADER_FILES}
|
${PPIC_HEADER_FILES}
|
||||||
${PPIC_CPP_FILES}
|
${PPIC_CPP_FILES}
|
||||||
|
${PPIC_UI_FILES}
|
||||||
${PPIC_QRC_FILES}
|
${PPIC_QRC_FILES}
|
||||||
${PPIC_RC_FILES}
|
${PPIC_RC_FILES}
|
||||||
)
|
)
|
||||||
@@ -271,12 +271,6 @@ if (DEFINED QM_FILE_INSTALL_DIR)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
install(
|
|
||||||
FILES dist/passoc/ppic.pacfg
|
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
RENAME default-assoc.pacfg
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
dist/passoc/generic.ico
|
dist/passoc/generic.ico
|
||||||
|
|||||||
@@ -1,3 +1,32 @@
|
|||||||
|
Version 1.5.0
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
Released: 2026-07-04
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Introduced an optional titlebar that display file name and allows drag and double-click to move and toggle maximize state
|
||||||
|
* Windows: Now the application will use the system's built-in animation for open window animation
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Show empty url list should also reset window title to blank
|
||||||
|
|
||||||
|
Miscellaneous:
|
||||||
|
* Update translations
|
||||||
|
* Add vcs-browser link to AppStream metadata
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Heimen Stoffels (vistaus), VenusGirl (VenusGirl)
|
||||||
|
|
||||||
|
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
|
Version 1.4.0
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
Released: 2026-02-11
|
Released: 2026-02-11
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
Yet another image viewer.
|
# Sarasas Chip Workshop Picture
|
||||||
|
|
||||||
|
Yet another legacy Windows Photo Viewer with cross-platform support.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> SarasaCW Picture are still in refactoring. Please do not use it until this message removes.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Sarasas Chip Workshop Picture (abbr. SarasaCW Picture) is a lightweight image viewer that has cross-platform support and similar user interface about legacy Windows Photo Viewer. It allows you view JPEG, PNG, GIF, SVG, PSD, KRA, XCF, TGA, HDR, AVIF and some other frequently used image formats files quickly and easily without changing your long-term Windows usage experience.
|
||||||
|
|
||||||
|
SarasaCW Picture is the **hard fork** of [Pineapple Pictures](https://github.com/BLumia/pineapple-pictures) since Pineapple Pictures 1.4.1 version.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|CI|Build Status|
|
|CI|Build Status|
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -8,10 +21,6 @@ Yet another image viewer.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Pineapple Pictures is a lightweight image viewer that allows you view JPEG, PNG, GIF, SVG, PSD, KRA, XCF, TGA, HDR, AVIF and some other frequently used image formats files quickly and easily, and also provide a Stay-on-Top window setting that allows you pin the window so you can use it to pin a reference image at the top and then you can work with other software.
|
|
||||||
|
|
||||||
## Get it!
|
## Get it!
|
||||||
|
|
||||||
### Maintained by the original author
|
### Maintained by the original author
|
||||||
@@ -71,3 +80,5 @@ It's possible to build it under Windows, Linux, macOS, and maybe other desktop p
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
Pineapple Pictures as a whole is licensed under MIT license. Individual files may have a different, but compatible license.
|
Pineapple Pictures as a whole is licensed under MIT license. Individual files may have a different, but compatible license.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|||||||
+17
-4
@@ -1,3 +1,18 @@
|
|||||||
|
# Sarasas Chip Workshop 照片查看器
|
||||||
|
|
||||||
|
简单轻量的、具有跨平台支持的、经典样式的 Windows 照片查看器。
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> SarasaCW 照片查看器还在重构中。在这条信息被移除之前请不要使用它。
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
Sarasas Chip Workshop 照片查看器(简称 SarasaCW 照片查看器)是一个轻量级的图片查看器。支持跨平台。界面风格类似于经典的 Windows 照片查看器。它可以让你快速轻松地浏览 JPEG、PNG、GIF、SVG、PSD、KRA、XCF、TGA、HDR、AVIF 以及其他一些常用的图片格式,且无需改变你长期的 Windows 使用习惯。
|
||||||
|
|
||||||
|
SarasaCW 照片查看器是[菠萝看图](https://github.com/BLumia/pineapple-pictures)的**硬分叉**,该分叉自菠萝看图 1.4.1 版本开始。
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
简单轻量的跨平台看图工具。
|
简单轻量的跨平台看图工具。
|
||||||
|
|
||||||
|CI|构建状态|
|
|CI|构建状态|
|
||||||
@@ -8,10 +23,6 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 简介
|
|
||||||
|
|
||||||
菠萝看图是一个轻量图像查看器,允许你简单快捷的查看 JPEG, PNG, GIF, SVG, PSD, KRA, XCF, TGA, HDR, AVIF 等常用格式的图像文件,并提供了置顶窗口的选项以便你在使用其它软件时也可以将参考图片固定在顶端。
|
|
||||||
|
|
||||||
## 立即获取!
|
## 立即获取!
|
||||||
|
|
||||||
### 由原作者维护
|
### 由原作者维护
|
||||||
@@ -72,3 +83,5 @@ $ cmake --build . # 如果你使用 Makefile 作为 CMake 生成器,也可以
|
|||||||
## 许可协议
|
## 许可协议
|
||||||
|
|
||||||
菠萝看图整体使用 MIT 协议进行发布。项目所随的部分源文件可能具备不同但与之兼容的许可协议。
|
菠萝看图整体使用 MIT 协议进行发布。项目所随的部分源文件可能具备不同但与之兼容的许可协议。
|
||||||
|
|
||||||
|
-->
|
||||||
|
|||||||
+60
-144
@@ -3,176 +3,92 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "aboutdialog.h"
|
#include "aboutdialog.h"
|
||||||
|
#include "ui_aboutdialog.h"
|
||||||
|
|
||||||
#include <QAbstractButton>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QTextBrowser>
|
|
||||||
#include <QTabWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
|
||||||
|
static QString loadEmbeddedHtml(QString&& path) {
|
||||||
|
QFile htmlFile(path);
|
||||||
|
bool canOpenFile = htmlFile.open(QIODevice::ReadOnly);
|
||||||
|
QByteArray htmlRawContent = canOpenFile ? htmlFile.readAll() : QByteArrayLiteral("");
|
||||||
|
return QString::fromUtf8(htmlRawContent);
|
||||||
|
}
|
||||||
|
|
||||||
AboutDialog::AboutDialog(QWidget *parent)
|
AboutDialog::AboutDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent), ui(new Ui::AboutDialog)
|
||||||
, m_tabWidget(new QTabWidget)
|
|
||||||
, m_buttonBox(new QDialogButtonBox)
|
|
||||||
, m_helpTextEdit(new QTextBrowser)
|
|
||||||
, m_aboutTextEdit(new QTextBrowser)
|
|
||||||
, m_specialThanksTextEdit(new QTextBrowser)
|
|
||||||
, m_licenseTextEdit(new QTextBrowser)
|
|
||||||
, m_3rdPartyLibsTextEdit(new QTextBrowser)
|
|
||||||
{
|
{
|
||||||
this->setWindowTitle(tr("About"));
|
ui->setupUi(this);
|
||||||
|
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||||
|
|
||||||
const QStringList helpStr {
|
// blumia: Chain two arg() here since it seems lupdate will remove one of them if we use
|
||||||
u"<p>%1</p>"_s.arg(tr("Launch application with image file path as argument to load the file.")),
|
// the old `arg(QCoreApp::translate(), tr())` way, but it's worth to mention
|
||||||
u"<p>%1</p>"_s.arg(tr("Drag and drop image file onto the window is also supported.")),
|
// `arg(QCoreApp::translate(), this->tr())` works, but lupdate will complain about the usage.
|
||||||
u"<p>%1</p>"_s.arg(tr("None of the operations in this application will alter the pictures on disk.")),
|
const QString HELP_HTML = loadEmbeddedHtml(u":/plain/help.html"_s)
|
||||||
u"<p>%1</p>"_s.arg(tr("Context menu option explanation:")),
|
.arg(tr("General Help"))
|
||||||
u"<ul>"_s,
|
.arg(tr("Launch application with image file path as argument to load the file."))
|
||||||
// blumia: Chain two arg() here since it seems lupdate will remove one of them if we use
|
.arg(tr("Passing in a single image file will load all the images in that directory and start viewing from that image."))
|
||||||
// the old `arg(QCoreApp::translate(), tr())` way, but it's worth to mention
|
.arg(tr("Passing in multiple image files lets you view them one by one."))
|
||||||
// `arg(QCoreApp::translate(), this->tr())` works, but lupdate will complain about the usage.
|
.arg(tr("Drag and drop image file onto the window is also supported."))
|
||||||
u"<li><b>%1</b>:<br/>%2</li>"_s
|
.arg(tr("None of the operations in this application will alter the pictures on disk."))
|
||||||
.arg(QCoreApplication::translate("MainWindow", "Stay on top"))
|
.arg(tr("Context Menu Help"))
|
||||||
.arg(tr("Make window stay on top of all other windows.")),
|
.arg(tr("Keep transformation", "The same meaning of MainWindow's Keep transformation."))
|
||||||
u"<li><b>%1</b>:<br/>%2</li>"_s
|
.arg(tr("Avoid resetting the zoom/rotation/flip state that was applied to the image view when switching between images."));
|
||||||
.arg(QCoreApplication::translate("MainWindow", "Protected mode"))
|
|
||||||
.arg(tr("Avoid close window accidentally. (eg. by double clicking the window)")),
|
|
||||||
u"<li><b>%1</b>:<br/>%2</li>"_s
|
|
||||||
.arg(QCoreApplication::translate("MainWindow", "Keep transformation", "The 'transformation' means the flip/rotation status that currently applied to the image view"))
|
|
||||||
.arg(tr("Avoid resetting the zoom/rotation/flip state that was applied to the image view when switching between images.")),
|
|
||||||
u"</ul>"_s
|
|
||||||
};
|
|
||||||
|
|
||||||
const QStringList aboutStr {
|
const QString APP_NAME_ARG = qApp->applicationDisplayName();
|
||||||
u"<center><img width='128' height='128' src=':/icons/app-icon.svg'/><br/>"_s,
|
const QString APP_VERSION_ARG =
|
||||||
qApp->applicationDisplayName(),
|
|
||||||
(u"<br/>"_s + tr("Version: %1").arg(
|
|
||||||
#ifdef GIT_DESCRIBE_VERSION_STRING
|
#ifdef GIT_DESCRIBE_VERSION_STRING
|
||||||
GIT_DESCRIBE_VERSION_STRING
|
GIT_DESCRIBE_VERSION_STRING;
|
||||||
#else
|
#else
|
||||||
qApp->applicationVersion()
|
qApp->applicationVersion();
|
||||||
#endif // GIT_DESCRIBE_VERSION_STRING
|
#endif // GIT_DESCRIBE_VERSION_STRING
|
||||||
)),
|
const QString QT_VERSION_ARG = QT_VERSION_STR;
|
||||||
u"<hr/>"_s,
|
const QString QT_ARCH_ARG = QSysInfo::buildCpuArchitecture();
|
||||||
tr("Copyright (c) %1 %2", "%1 is year, %2 is the name of copyright holder(s)")
|
const QString ABOUT_HTML = loadEmbeddedHtml(u":/plain/about.html"_s)
|
||||||
.arg(u"2026"_s, u"<a href='https://github.com/BLumia'>@BLumia</a>"_s),
|
.arg(APP_NAME_ARG)
|
||||||
u"<br/>"_s,
|
.arg(tr("Version: %1").arg(APP_VERSION_ARG))
|
||||||
tr("Logo designed by %1").arg(u"<a href='https://github.com/Lovelyblack'>@Lovelyblack</a>"_s),
|
.arg(tr("Upstream author"))
|
||||||
u"<hr/>"_s,
|
.arg(tr("Hard fork author"))
|
||||||
tr("Built with Qt %1 (%2)").arg(QT_VERSION_STR, QSysInfo::buildCpuArchitecture()),
|
.arg(tr("Logo designer"))
|
||||||
QStringLiteral("<br/><a href='%1'>%2</a>").arg("https://github.com/BLumia/pineapple-pictures", tr("Source code")),
|
.arg(tr("Built with Qt %1 (%2)").arg(QT_VERSION_ARG).arg(QT_ARCH_ARG))
|
||||||
u"</center>"_s
|
.arg(tr("Source code"));
|
||||||
};
|
|
||||||
|
|
||||||
QFile translaterHtml(u":/plain/translators.html"_s);
|
const QString SPECIAL_THANKS_HTML = loadEmbeddedHtml(u":/plain/special-thanks.html"_s)
|
||||||
bool canOpenFile = translaterHtml.open(QIODevice::ReadOnly);
|
.arg(tr("Contributors"))
|
||||||
const QByteArray & translatorList = canOpenFile ? translaterHtml.readAll() : QByteArrayLiteral("");
|
.arg(tr("List of contributors on GitHub"))
|
||||||
|
.arg(tr("Thanks to all people who contributed to this project."))
|
||||||
|
.arg(tr("Translators"))
|
||||||
|
.arg(tr("I would like to thank the following people who volunteered to translate this application."));
|
||||||
|
|
||||||
const QStringList specialThanksStr {
|
const QString LICENSE_HTML = loadEmbeddedHtml(u":/plain/license.html"_s)
|
||||||
u"<h1 align='center'>%1</h1><a href='%2'>%3</a><p>%4</p>"_s.arg(
|
.arg(tr("License"))
|
||||||
tr("Contributors"),
|
.arg(tr("This application is released under the MIT License."))
|
||||||
u"https://github.com/BLumia/pineapple-pictures/graphs/contributors"_s,
|
.arg(tr("License Verbatim Copy"));
|
||||||
tr("List of contributors on GitHub"),
|
|
||||||
tr("Thanks to all people who contributed to this project.")
|
|
||||||
),
|
|
||||||
|
|
||||||
u"<h1 align='center'>%1</h1><p>%2</p>%3"_s.arg(
|
const QString THIRD_PARTY_LIBS_HTML = loadEmbeddedHtml(u":/plain/third-party-libs.html"_s)
|
||||||
tr("Translators"),
|
.arg(tr("Third-party Libraries"))
|
||||||
tr("I would like to thank the following people who volunteered to translate this application."),
|
.arg(tr("This application is built on the following free software libraries.", "Free as in freedom"))
|
||||||
translatorList
|
.arg(tr("Some of them are optional and might not be included with this distribution depending on the build environment."));
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const QStringList licenseStr {
|
ui->m_helpTextEdit->setHtml(HELP_HTML);
|
||||||
u"<h1 align='center'><b>%1</b></h1>"_s.arg(tr("Your Rights")),
|
ui->m_aboutTextEdit->setHtml(ABOUT_HTML);
|
||||||
u"<p>%1</p><p>%2</p><ul><li>%3</li><li>%4</li><li>%5</li><li>%6</li></ul>"_s.arg(
|
ui->m_specialThanksTextEdit->setHtml(SPECIAL_THANKS_HTML);
|
||||||
tr("%1 is released under the MIT License."), // %1
|
ui->m_licenseTextEdit->setHtml(LICENSE_HTML);
|
||||||
tr("This license grants people a number of freedoms:"), // %2
|
ui->m_3rdPartyLibsTextEdit->setHtml(THIRD_PARTY_LIBS_HTML);
|
||||||
tr("You are free to use %1, for any purpose"), // %3
|
|
||||||
tr("You are free to distribute %1"), // %4
|
|
||||||
tr("You can study how %1 works and change it"), // %5
|
|
||||||
tr("You can distribute changed versions of %1") // %6
|
|
||||||
).arg(u"<i>%1</i>"_s),
|
|
||||||
u"<p>%1</p>"_s.arg(tr("The MIT license guarantees you this freedom. Nobody is ever permitted to take it away.")),
|
|
||||||
u"<hr/><pre>%2</pre>"_s
|
|
||||||
};
|
|
||||||
|
|
||||||
const QString mitLicense(QStringLiteral(R"(Expat/MIT License
|
connect(ui->m_buttonBox, QOverload<QAbstractButton *>::of(&QDialogButtonBox::clicked), this, [this](QAbstractButton * btn){
|
||||||
|
Q_UNUSED(btn)
|
||||||
Copyright © 2026 BLumia
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
)"));
|
|
||||||
|
|
||||||
const QStringList thirdPartyLibsStr {
|
|
||||||
u"<h1 align='center'><b>%1</b></h1>"_s.arg(tr("Third-party Libraries used by %1")),
|
|
||||||
tr("%1 is built on the following free software libraries:", "Free as in freedom"),
|
|
||||||
u"<ul>"_s,
|
|
||||||
#ifdef HAVE_EXIV2_VERSION
|
|
||||||
u"<li><a href='%1'>%2</a>: %3</li>"_s.arg("https://www.exiv2.org/", "Exiv2", "GPLv2"),
|
|
||||||
#endif // EXIV2_VERSION
|
|
||||||
u"<li><a href='%1'>%2</a>: %3</li>"_s.arg("https://www.qt.io/", "Qt", "GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3"),
|
|
||||||
u"</ul>"_s
|
|
||||||
};
|
|
||||||
|
|
||||||
m_helpTextEdit->setText(helpStr.join('\n'));
|
|
||||||
|
|
||||||
m_aboutTextEdit->setText(aboutStr.join('\n'));
|
|
||||||
m_aboutTextEdit->setOpenExternalLinks(true);
|
|
||||||
|
|
||||||
m_specialThanksTextEdit->setText(specialThanksStr.join('\n'));
|
|
||||||
m_specialThanksTextEdit->setOpenExternalLinks(true);
|
|
||||||
|
|
||||||
m_licenseTextEdit->setText(licenseStr.join('\n').arg(qApp->applicationDisplayName(), mitLicense));
|
|
||||||
|
|
||||||
m_3rdPartyLibsTextEdit->setText(thirdPartyLibsStr.join('\n').arg(u"<i>%1</i>"_s).arg(qApp->applicationDisplayName()));
|
|
||||||
m_3rdPartyLibsTextEdit->setOpenExternalLinks(true);
|
|
||||||
|
|
||||||
m_tabWidget->addTab(m_helpTextEdit, tr("&Help"));
|
|
||||||
m_tabWidget->addTab(m_aboutTextEdit, tr("&About"));
|
|
||||||
m_tabWidget->addTab(m_specialThanksTextEdit, tr("&Special Thanks"));
|
|
||||||
m_tabWidget->addTab(m_licenseTextEdit, tr("&License"));
|
|
||||||
m_tabWidget->addTab(m_3rdPartyLibsTextEdit, tr("&Third-party Libraries"));
|
|
||||||
|
|
||||||
m_buttonBox->setStandardButtons(QDialogButtonBox::Close);
|
|
||||||
connect(m_buttonBox, QOverload<QAbstractButton *>::of(&QDialogButtonBox::clicked), this, [this](){
|
|
||||||
this->close();
|
this->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
setLayout(new QVBoxLayout);
|
|
||||||
|
|
||||||
layout()->addWidget(m_tabWidget);
|
|
||||||
layout()->addWidget(m_buttonBox);
|
|
||||||
|
|
||||||
setMinimumSize(361, 161); // not sure why it complain "Unable to set geometry"
|
setMinimumSize(361, 161); // not sure why it complain "Unable to set geometry"
|
||||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AboutDialog::~AboutDialog()
|
AboutDialog::~AboutDialog()
|
||||||
{
|
{
|
||||||
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize AboutDialog::sizeHint() const
|
QSize AboutDialog::sizeHint() const
|
||||||
|
|||||||
+5
-11
@@ -7,10 +7,11 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QTextBrowser;
|
namespace Ui {
|
||||||
class QTabWidget;
|
class AboutDialog;
|
||||||
class QDialogButtonBox;
|
}
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class AboutDialog : public QDialog
|
class AboutDialog : public QDialog
|
||||||
@@ -23,14 +24,7 @@ public:
|
|||||||
QSize sizeHint() const override;
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTabWidget * m_tabWidget = nullptr;
|
QT_PREPEND_NAMESPACE(Ui::AboutDialog) *ui;
|
||||||
QDialogButtonBox * m_buttonBox = nullptr;
|
|
||||||
|
|
||||||
QTextBrowser * m_helpTextEdit = nullptr;
|
|
||||||
QTextBrowser * m_aboutTextEdit = nullptr;
|
|
||||||
QTextBrowser * m_specialThanksTextEdit = nullptr;
|
|
||||||
QTextBrowser * m_licenseTextEdit = nullptr;
|
|
||||||
QTextBrowser * m_3rdPartyLibsTextEdit = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ABOUTDIALOG_H
|
#endif // ABOUTDIALOG_H
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AboutDialog</class>
|
||||||
|
<widget class="QDialog" name="AboutDialog">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::NonModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>About</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>361</width>
|
||||||
|
<height>161</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="m_tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="helpTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&Help</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="helpTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="m_helpTextEdit">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="aboutTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&About</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="aboutTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="m_aboutTextEdit">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="specialThanksTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&Special Thanks</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="specialThanksTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="m_specialThanksTextEdit">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="licenseTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&License</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="licenseTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="m_licenseTextEdit">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="thirdPartyLibsTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&Third-party Libraries</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="thirdPartyLibsTabLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="m_3rdPartyLibsTextEdit">
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="m_buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
+30
-27
@@ -10,13 +10,7 @@
|
|||||||
#include <QSvgRenderer>
|
#include <QSvgRenderer>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#define ICON_NAME(name)\
|
static QIcon loadHidpiIcon(const QString &resp, QSize sz = QSize(32, 32))
|
||||||
QStringLiteral(":/icons/" #name ".svg")
|
|
||||||
|
|
||||||
#define ACTION_NAME(s) QStringLiteral(STRIFY(s))
|
|
||||||
#define STRIFY(s) #s
|
|
||||||
|
|
||||||
QIcon ActionManager::loadHidpiIcon(const QString &resp, QSize sz)
|
|
||||||
{
|
{
|
||||||
QSvgRenderer r(resp);
|
QSvgRenderer r(resp);
|
||||||
QPixmap pm = QPixmap(sz * qApp->devicePixelRatio());
|
QPixmap pm = QPixmap(sz * qApp->devicePixelRatio());
|
||||||
@@ -29,13 +23,22 @@ QIcon ActionManager::loadHidpiIcon(const QString &resp, QSize sz)
|
|||||||
|
|
||||||
void ActionManager::setupAction(MainWindow *mainWindow)
|
void ActionManager::setupAction(MainWindow *mainWindow)
|
||||||
{
|
{
|
||||||
|
// region: Action and Its Icon Creation
|
||||||
|
|
||||||
|
#define ICON_NAME(name)\
|
||||||
|
QStringLiteral(":/icons/" #name ".svg")
|
||||||
|
|
||||||
|
#define ACTION_NAME(s) QStringLiteral(STRIFY(s))
|
||||||
|
#define STRIFY(s) #s
|
||||||
|
|
||||||
auto create_action = [] (QWidget *w, QAction **a, QString i, QString an, bool iconFromTheme = false) {
|
auto create_action = [] (QWidget *w, QAction **a, QString i, QString an, bool iconFromTheme = false) {
|
||||||
*a = new QAction(w);
|
*a = new QAction(w);
|
||||||
if (!i.isNull())
|
if (!i.isNull())
|
||||||
(*a)->setIcon(iconFromTheme ? QIcon::fromTheme(i) : ActionManager::loadHidpiIcon(i));
|
(*a)->setIcon(iconFromTheme ? QIcon::fromTheme(i) : loadHidpiIcon(i));
|
||||||
(*a)->setObjectName(an);
|
(*a)->setObjectName(an);
|
||||||
w->addAction(*a);
|
w->addAction(*a);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CREATE_NEW_ICON_ACTION(w, a, i) create_action(w, &a, ICON_NAME(i), ACTION_NAME(a))
|
#define CREATE_NEW_ICON_ACTION(w, a, i) create_action(w, &a, ICON_NAME(i), ACTION_NAME(a))
|
||||||
CREATE_NEW_ICON_ACTION(mainWindow, actionActualSize, zoom-original);
|
CREATE_NEW_ICON_ACTION(mainWindow, actionActualSize, zoom-original);
|
||||||
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleMaximize, view-fullscreen);
|
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleMaximize, view-fullscreen);
|
||||||
@@ -43,29 +46,25 @@ void ActionManager::setupAction(MainWindow *mainWindow)
|
|||||||
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomOut, zoom-out);
|
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomOut, zoom-out);
|
||||||
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleCheckerboard, view-background-checkerboard);
|
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleCheckerboard, view-background-checkerboard);
|
||||||
CREATE_NEW_ICON_ACTION(mainWindow, actionRotateClockwise, object-rotate-right);
|
CREATE_NEW_ICON_ACTION(mainWindow, actionRotateClockwise, object-rotate-right);
|
||||||
|
CREATE_NEW_ICON_ACTION(mainWindow, actionPrevPicture, go-previous);
|
||||||
|
CREATE_NEW_ICON_ACTION(mainWindow, actionNextPicture, go-next);
|
||||||
#undef CREATE_NEW_ICON_ACTION
|
#undef CREATE_NEW_ICON_ACTION
|
||||||
|
|
||||||
#define CREATE_NEW_ACTION(w, a) create_action(w, &a, QString(), ACTION_NAME(a))
|
#define CREATE_NEW_ACTION(w, a) create_action(w, &a, QString(), ACTION_NAME(a))
|
||||||
#define CREATE_NEW_THEMEICON_ACTION(w, a, i) create_action(w, &a, QLatin1String(STRIFY(i)), ACTION_NAME(a), true)
|
#define CREATE_NEW_THEMEICON_ACTION(w, a, i) create_action(w, &a, QLatin1String(STRIFY(i)), ACTION_NAME(a), true)
|
||||||
CREATE_NEW_ACTION(mainWindow, actionRotateCounterClockwise);
|
CREATE_NEW_ACTION(mainWindow, actionRotateCounterClockwise);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionPrevPicture);
|
|
||||||
CREATE_NEW_ACTION(mainWindow, actionNextPicture);
|
|
||||||
|
|
||||||
CREATE_NEW_ACTION(mainWindow, actionTogglePauseAnimation);
|
CREATE_NEW_ACTION(mainWindow, actionTogglePauseAnimation);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionAnimationNextFrame);
|
CREATE_NEW_ACTION(mainWindow, actionAnimationNextFrame);
|
||||||
|
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionOpen, document-open);
|
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionOpen, document-open);
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionSaveAs, document-save-as);
|
|
||||||
CREATE_NEW_ACTION(mainWindow, actionHorizontalFlip);
|
CREATE_NEW_ACTION(mainWindow, actionHorizontalFlip);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionFitInView);
|
CREATE_NEW_ACTION(mainWindow, actionFitInView);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionFitByWidth);
|
CREATE_NEW_ACTION(mainWindow, actionFitByWidth);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionFitLongImage);
|
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionCopyPixmap, edit-copy);
|
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionCopyPixmap, edit-copy);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionCopyFilePath);
|
CREATE_NEW_ACTION(mainWindow, actionCopyFilePath);
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionPaste, edit-paste);
|
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionPaste, edit-paste);
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionTrash, edit-delete);
|
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionTrash, edit-delete);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionToggleStayOnTop);
|
|
||||||
CREATE_NEW_ACTION(mainWindow, actionToggleProtectMode);
|
|
||||||
CREATE_NEW_ACTION(mainWindow, actionToggleAvoidResetTransform);
|
CREATE_NEW_ACTION(mainWindow, actionToggleAvoidResetTransform);
|
||||||
CREATE_NEW_ACTION(mainWindow, actionSettings);
|
CREATE_NEW_ACTION(mainWindow, actionSettings);
|
||||||
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionHelp, system-help);
|
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionHelp, system-help);
|
||||||
@@ -75,17 +74,15 @@ void ActionManager::setupAction(MainWindow *mainWindow)
|
|||||||
#undef CREATE_NEW_ACTION
|
#undef CREATE_NEW_ACTION
|
||||||
#undef CREATE_NEW_THEMEICON_ACTION
|
#undef CREATE_NEW_THEMEICON_ACTION
|
||||||
|
|
||||||
retranslateUi(mainWindow);
|
#undef STRIFY
|
||||||
|
#undef ACTION_NAME
|
||||||
|
#undef ICON_NAME
|
||||||
|
|
||||||
QMetaObject::connectSlotsByName(mainWindow);
|
// endregion
|
||||||
}
|
|
||||||
|
|
||||||
void ActionManager::retranslateUi(MainWindow *mainWindow)
|
// region: Action Translatable Text Setup
|
||||||
{
|
|
||||||
Q_UNUSED(mainWindow);
|
|
||||||
|
|
||||||
actionOpen->setText(QCoreApplication::translate("MainWindow", "&Open...", nullptr));
|
actionOpen->setText(QCoreApplication::translate("MainWindow", "&Open...", nullptr));
|
||||||
actionSaveAs->setText(QCoreApplication::translate("MainWindow", "Save &As...", nullptr));
|
|
||||||
|
|
||||||
actionActualSize->setText(QCoreApplication::translate("MainWindow", "Actual size", nullptr));
|
actionActualSize->setText(QCoreApplication::translate("MainWindow", "Actual size", nullptr));
|
||||||
actionToggleMaximize->setText(QCoreApplication::translate("MainWindow", "Toggle maximize", nullptr));
|
actionToggleMaximize->setText(QCoreApplication::translate("MainWindow", "Toggle maximize", nullptr));
|
||||||
@@ -104,13 +101,10 @@ void ActionManager::retranslateUi(MainWindow *mainWindow)
|
|||||||
actionHorizontalFlip->setText(QCoreApplication::translate("MainWindow", "Flip &Horizontally", nullptr));
|
actionHorizontalFlip->setText(QCoreApplication::translate("MainWindow", "Flip &Horizontally", nullptr));
|
||||||
actionFitInView->setText(QCoreApplication::translate("MainWindow", "Fit to view", nullptr));
|
actionFitInView->setText(QCoreApplication::translate("MainWindow", "Fit to view", nullptr));
|
||||||
actionFitByWidth->setText(QCoreApplication::translate("MainWindow", "Fit to width", nullptr));
|
actionFitByWidth->setText(QCoreApplication::translate("MainWindow", "Fit to width", nullptr));
|
||||||
actionFitLongImage->setText(QCoreApplication::translate("MainWindow", "Fit long image", nullptr));
|
|
||||||
actionCopyPixmap->setText(QCoreApplication::translate("MainWindow", "Copy P&ixmap", nullptr));
|
actionCopyPixmap->setText(QCoreApplication::translate("MainWindow", "Copy P&ixmap", nullptr));
|
||||||
actionCopyFilePath->setText(QCoreApplication::translate("MainWindow", "Copy &File Path", nullptr));
|
actionCopyFilePath->setText(QCoreApplication::translate("MainWindow", "Copy &File Path", nullptr));
|
||||||
actionPaste->setText(QCoreApplication::translate("MainWindow", "&Paste", nullptr));
|
actionPaste->setText(QCoreApplication::translate("MainWindow", "&Paste", nullptr));
|
||||||
actionTrash->setText(QCoreApplication::translate("MainWindow", "Move to Trash", nullptr));
|
actionTrash->setText(QCoreApplication::translate("MainWindow", "Move to Trash", nullptr));
|
||||||
actionToggleStayOnTop->setText(QCoreApplication::translate("MainWindow", "Stay on top", nullptr));
|
|
||||||
actionToggleProtectMode->setText(QCoreApplication::translate("MainWindow", "Protected mode", nullptr));
|
|
||||||
actionToggleAvoidResetTransform->setText(QCoreApplication::translate("MainWindow", "Keep transformation", "The 'transformation' means the flip/rotation status that currently applied to the image view"));
|
actionToggleAvoidResetTransform->setText(QCoreApplication::translate("MainWindow", "Keep transformation", "The 'transformation' means the flip/rotation status that currently applied to the image view"));
|
||||||
actionSettings->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr));
|
actionSettings->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr));
|
||||||
actionHelp->setText(QCoreApplication::translate("MainWindow", "Help", nullptr));
|
actionHelp->setText(QCoreApplication::translate("MainWindow", "Help", nullptr));
|
||||||
@@ -119,19 +113,22 @@ void ActionManager::retranslateUi(MainWindow *mainWindow)
|
|||||||
QCoreApplication::translate(
|
QCoreApplication::translate(
|
||||||
"MainWindow", "Show in File Explorer",
|
"MainWindow", "Show in File Explorer",
|
||||||
"File Explorer is the name of explorer.exe under Windows"
|
"File Explorer is the name of explorer.exe under Windows"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
actionLocateInFileManager->setText(QCoreApplication::translate("MainWindow", "Show in directory", nullptr));
|
actionLocateInFileManager->setText(QCoreApplication::translate("MainWindow", "Show in directory", nullptr));
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
actionProperties->setText(QCoreApplication::translate("MainWindow", "Properties", nullptr));
|
actionProperties->setText(QCoreApplication::translate("MainWindow", "Properties", nullptr));
|
||||||
actionQuitApp->setText(QCoreApplication::translate("MainWindow", "Quit", nullptr));
|
actionQuitApp->setText(QCoreApplication::translate("MainWindow", "Quit", nullptr));
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
QMetaObject::connectSlotsByName(mainWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionManager::setupShortcuts()
|
void ActionManager::setupShortcuts()
|
||||||
{
|
{
|
||||||
actionOpen->setShortcut(QKeySequence::Open);
|
actionOpen->setShortcut(QKeySequence::Open);
|
||||||
actionSaveAs->setShortcut(QKeySequence::SaveAs);
|
|
||||||
actionActualSize->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
|
actionActualSize->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
|
||||||
actionZoomIn->setShortcut(QKeySequence::ZoomIn);
|
actionZoomIn->setShortcut(QKeySequence::ZoomIn);
|
||||||
actionZoomOut->setShortcut(QKeySequence::ZoomOut);
|
actionZoomOut->setShortcut(QKeySequence::ZoomOut);
|
||||||
@@ -156,3 +153,9 @@ void ActionManager::setupShortcuts()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActionManager::enablePrevNextPictureAction(bool enablePrevPictureAction, bool enableNextPictureAction)
|
||||||
|
{
|
||||||
|
actionPrevPicture->setEnabled(enablePrevPictureAction);
|
||||||
|
actionNextPicture->setEnabled(enableNextPictureAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-7
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
|
/// Action Manager is separated from Main Window
|
||||||
|
/// to manage all actions specifically.
|
||||||
class ActionManager
|
class ActionManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -16,14 +18,11 @@ public:
|
|||||||
~ActionManager() = default;
|
~ActionManager() = default;
|
||||||
|
|
||||||
void setupAction(MainWindow * mainWindow);
|
void setupAction(MainWindow * mainWindow);
|
||||||
void retranslateUi(MainWindow *MainWindow);
|
|
||||||
void setupShortcuts();
|
void setupShortcuts();
|
||||||
|
void enablePrevNextPictureAction(bool enablePrevPictureAction, bool enableNextPictureAction);
|
||||||
static QIcon loadHidpiIcon(const QString &resp, QSize sz = QSize(32, 32));
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QAction *actionOpen;
|
QAction *actionOpen;
|
||||||
QAction *actionSaveAs;
|
|
||||||
|
|
||||||
QAction *actionActualSize;
|
QAction *actionActualSize;
|
||||||
QAction *actionToggleMaximize;
|
QAction *actionToggleMaximize;
|
||||||
@@ -42,13 +41,10 @@ public:
|
|||||||
QAction *actionHorizontalFlip;
|
QAction *actionHorizontalFlip;
|
||||||
QAction *actionFitInView;
|
QAction *actionFitInView;
|
||||||
QAction *actionFitByWidth;
|
QAction *actionFitByWidth;
|
||||||
QAction *actionFitLongImage;
|
|
||||||
QAction *actionCopyPixmap;
|
QAction *actionCopyPixmap;
|
||||||
QAction *actionCopyFilePath;
|
QAction *actionCopyFilePath;
|
||||||
QAction *actionPaste;
|
QAction *actionPaste;
|
||||||
QAction *actionTrash;
|
QAction *actionTrash;
|
||||||
QAction *actionToggleStayOnTop;
|
|
||||||
QAction *actionToggleProtectMode;
|
|
||||||
QAction *actionToggleAvoidResetTransform;
|
QAction *actionToggleAvoidResetTransform;
|
||||||
QAction *actionSettings;
|
QAction *actionSettings;
|
||||||
QAction *actionHelp;
|
QAction *actionHelp;
|
||||||
|
|||||||
@@ -4,15 +4,12 @@
|
|||||||
|
|
||||||
#include "bottombuttongroup.h"
|
#include "bottombuttongroup.h"
|
||||||
|
|
||||||
#include "opacityhelper.h"
|
|
||||||
|
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
BottomButtonGroup::BottomButtonGroup(const std::vector<QAction *> &actionList, QWidget *parent)
|
BottomButtonGroup::BottomButtonGroup(const std::vector<QAction *> &actionList, QWidget *parent)
|
||||||
: QGroupBox (parent)
|
: QGroupBox (parent)
|
||||||
, m_opacityHelper(new OpacityHelper(this))
|
|
||||||
{
|
{
|
||||||
QHBoxLayout * mainLayout = new QHBoxLayout(this);
|
QHBoxLayout * mainLayout = new QHBoxLayout(this);
|
||||||
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||||
@@ -45,11 +42,6 @@ BottomButtonGroup::BottomButtonGroup(const std::vector<QAction *> &actionList, Q
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BottomButtonGroup::setOpacity(qreal opacity, bool animated)
|
|
||||||
{
|
|
||||||
m_opacityHelper->setOpacity(opacity, animated);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BottomButtonGroup::addButton(QAbstractButton *button)
|
void BottomButtonGroup::addButton(QAbstractButton *button)
|
||||||
{
|
{
|
||||||
layout()->addWidget(button);
|
layout()->addWidget(button);
|
||||||
|
|||||||
@@ -10,18 +10,13 @@
|
|||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
|
||||||
class OpacityHelper;
|
|
||||||
class BottomButtonGroup : public QGroupBox
|
class BottomButtonGroup : public QGroupBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit BottomButtonGroup(const std::vector<QAction *> & actionList, QWidget *parent = nullptr);
|
explicit BottomButtonGroup(const std::vector<QAction *> & actionList, QWidget *parent = nullptr);
|
||||||
|
|
||||||
void setOpacity(qreal opacity, bool animated = true);
|
|
||||||
void addButton(QAbstractButton *button);
|
void addButton(QAbstractButton *button);
|
||||||
|
|
||||||
private:
|
|
||||||
OpacityHelper * m_opacityHelper;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BOTTOMBUTTONGROUP_H
|
#endif // BOTTOMBUTTONGROUP_H
|
||||||
|
|||||||
+6
-106
@@ -14,15 +14,14 @@
|
|||||||
FramelessWindow::FramelessWindow(QWidget *parent)
|
FramelessWindow::FramelessWindow(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_centralLayout(new QVBoxLayout(this))
|
, m_centralLayout(new QVBoxLayout(this))
|
||||||
, m_oldCursorShape(Qt::ArrowCursor)
|
|
||||||
, m_oldEdges()
|
|
||||||
{
|
{
|
||||||
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
|
this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);
|
||||||
this->setMouseTracking(true);
|
|
||||||
this->setAttribute(Qt::WA_Hover, true);
|
|
||||||
this->installEventFilter(this);
|
|
||||||
|
|
||||||
m_centralLayout->setContentsMargins(QMargins());
|
// YYC MARK:
|
||||||
|
// 72 px is the height of Windows Photo Viewer's bottom line.
|
||||||
|
|
||||||
|
// TODO: This value may be declared as a constant.
|
||||||
|
m_centralLayout->setContentsMargins(QMargins(0, 0, 0, 72));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessWindow::setCentralWidget(QWidget *widget)
|
void FramelessWindow::setCentralWidget(QWidget *widget)
|
||||||
@@ -35,102 +34,3 @@ void FramelessWindow::setCentralWidget(QWidget *widget)
|
|||||||
m_centralLayout->addWidget(widget);
|
m_centralLayout->addWidget(widget);
|
||||||
m_centralWidget = widget;
|
m_centralWidget = widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FramelessWindow::installResizeCapture(QObject* widget)
|
|
||||||
{
|
|
||||||
widget->installEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FramelessWindow::eventFilter(QObject* o, QEvent* e)
|
|
||||||
{
|
|
||||||
switch (e->type()) {
|
|
||||||
case QEvent::HoverMove:
|
|
||||||
{
|
|
||||||
QWidget* wg = qobject_cast<QWidget*>(o);
|
|
||||||
if (wg != nullptr)
|
|
||||||
return mouseHover(static_cast<QHoverEvent*>(e), wg);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QEvent::MouseButtonPress:
|
|
||||||
return mousePress(static_cast<QMouseEvent*>(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
return QWidget::eventFilter(o, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FramelessWindow::mouseHover(QHoverEvent* event, QWidget* wg)
|
|
||||||
{
|
|
||||||
if (!isMaximized() && !isFullScreen()) {
|
|
||||||
QWindow* win = window()->windowHandle();
|
|
||||||
Qt::Edges edges = this->getEdgesByPos(wg->mapToGlobal(event->oldPos()), win->frameGeometry());
|
|
||||||
|
|
||||||
// backup & restore cursor shape
|
|
||||||
if (edges && !m_oldEdges)
|
|
||||||
// entering the edge. backup cursor shape
|
|
||||||
m_oldCursorShape = win->cursor().shape();
|
|
||||||
if (!edges && m_oldEdges)
|
|
||||||
// leaving the edge. restore cursor shape
|
|
||||||
win->setCursor(m_oldCursorShape);
|
|
||||||
|
|
||||||
// save the latest edges status
|
|
||||||
m_oldEdges = edges;
|
|
||||||
|
|
||||||
// show resize cursor shape if cursor is within border
|
|
||||||
if (edges) {
|
|
||||||
win->setCursor(this->getCursorByEdge(edges, Qt::ArrowCursor));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FramelessWindow::mousePress(QMouseEvent* event)
|
|
||||||
{
|
|
||||||
if (event->buttons() & Qt::LeftButton && !isMaximized() && !isFullScreen()) {
|
|
||||||
QWindow* win = window()->windowHandle();
|
|
||||||
Qt::Edges edges = this->getEdgesByPos(event->globalPosition().toPoint(), win->frameGeometry());
|
|
||||||
if (edges) {
|
|
||||||
win->startSystemResize(edges);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::CursorShape FramelessWindow::getCursorByEdge(const Qt::Edges& edges, Qt::CursorShape default_cursor)
|
|
||||||
{
|
|
||||||
if ((edges == (Qt::TopEdge | Qt::LeftEdge)) || (edges == (Qt::RightEdge | Qt::BottomEdge)))
|
|
||||||
return Qt::SizeFDiagCursor;
|
|
||||||
else if ((edges == (Qt::TopEdge | Qt::RightEdge)) || (edges == (Qt::LeftEdge | Qt::BottomEdge)))
|
|
||||||
return Qt::SizeBDiagCursor;
|
|
||||||
else if (edges & (Qt::TopEdge | Qt::BottomEdge))
|
|
||||||
return Qt::SizeVerCursor;
|
|
||||||
else if (edges & (Qt::LeftEdge | Qt::RightEdge))
|
|
||||||
return Qt::SizeHorCursor;
|
|
||||||
else
|
|
||||||
return default_cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::Edges FramelessWindow::getEdgesByPos(const QPoint gpos, const QRect& winrect)
|
|
||||||
{
|
|
||||||
const int borderWidth = 8;
|
|
||||||
Qt::Edges edges;
|
|
||||||
|
|
||||||
int x = gpos.x() - winrect.x();
|
|
||||||
int y = gpos.y() - winrect.y();
|
|
||||||
|
|
||||||
if (x < borderWidth)
|
|
||||||
edges |= Qt::LeftEdge;
|
|
||||||
if (x > (winrect.width() - borderWidth))
|
|
||||||
edges |= Qt::RightEdge;
|
|
||||||
if (y < borderWidth)
|
|
||||||
edges |= Qt::TopEdge;
|
|
||||||
if (y > (winrect.height() - borderWidth))
|
|
||||||
edges |= Qt::BottomEdge;
|
|
||||||
|
|
||||||
return edges;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,20 +18,8 @@ public:
|
|||||||
explicit FramelessWindow(QWidget *parent = nullptr);
|
explicit FramelessWindow(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void setCentralWidget(QWidget * widget);
|
void setCentralWidget(QWidget * widget);
|
||||||
void installResizeCapture(QObject* widget);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool eventFilter(QObject *o, QEvent *e) override;
|
|
||||||
bool mouseHover(QHoverEvent* event, QWidget* wg);
|
|
||||||
bool mousePress(QMouseEvent* event);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Qt::Edges m_oldEdges;
|
|
||||||
Qt::CursorShape m_oldCursorShape;
|
|
||||||
|
|
||||||
Qt::CursorShape getCursorByEdge(const Qt::Edges& edges, Qt::CursorShape default_cursor);
|
|
||||||
Qt::Edges getEdgesByPos(const QPoint pos, const QRect& winrect);
|
|
||||||
|
|
||||||
QVBoxLayout * m_centralLayout = nullptr;
|
QVBoxLayout * m_centralLayout = nullptr;
|
||||||
QWidget * m_centralWidget = nullptr; // just a pointer, doesn't take the ownership.
|
QWidget * m_centralWidget = nullptr; // just a pointer, doesn't take the ownership.
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-46
@@ -138,7 +138,6 @@ void GraphicsView::resetTransform()
|
|||||||
void GraphicsView::zoomView(qreal scaleFactor)
|
void GraphicsView::zoomView(qreal scaleFactor)
|
||||||
{
|
{
|
||||||
m_enableFitInView = false;
|
m_enableFitInView = false;
|
||||||
m_longImageMode = false;
|
|
||||||
scale(scaleFactor, scaleFactor);
|
scale(scaleFactor, scaleFactor);
|
||||||
applyTransformationModeByScaleFactor();
|
applyTransformationModeByScaleFactor();
|
||||||
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
|
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
|
||||||
@@ -280,31 +279,6 @@ void GraphicsView::fitByOrientation(Qt::Orientation ori, bool scaleDownOnly)
|
|||||||
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
|
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GraphicsView::isLongImage() const
|
|
||||||
{
|
|
||||||
// Get the transformed image size (considering rotation and other transforms)
|
|
||||||
QRectF transformedRect = transform().mapRect(sceneRect());
|
|
||||||
QSizeF imageSize = transformedRect.size();
|
|
||||||
|
|
||||||
if (imageSize.isEmpty()) return false;
|
|
||||||
|
|
||||||
qreal aspectRatio = imageSize.width() / imageSize.height();
|
|
||||||
|
|
||||||
// Check if aspect ratio exceeds 5:2 (wide) or 2:5 (tall)
|
|
||||||
return aspectRatio > 2.5 || aspectRatio < 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::fitLongImage()
|
|
||||||
{
|
|
||||||
QRectF transformedRect = transform().mapRect(sceneRect());
|
|
||||||
|
|
||||||
if (transformedRect.width() < transformedRect.height()) {
|
|
||||||
fitByOrientation(Qt::Horizontal, true);
|
|
||||||
} else {
|
|
||||||
fitByOrientation(Qt::Vertical, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::displayScene()
|
void GraphicsView::displayScene()
|
||||||
{
|
{
|
||||||
if (shouldAvoidTransform()) {
|
if (shouldAvoidTransform()) {
|
||||||
@@ -312,14 +286,6 @@ void GraphicsView::displayScene()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if should apply long image mode
|
|
||||||
if (Settings::instance()->autoLongImageMode() && isLongImage()) {
|
|
||||||
m_longImageMode = true;
|
|
||||||
m_firstUserMediaLoaded = true;
|
|
||||||
if (isSceneBiggerThanView()) fitLongImage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSceneBiggerThanView()) {
|
if (isSceneBiggerThanView()) {
|
||||||
// Do fit-in-view
|
// Do fit-in-view
|
||||||
fitInView(sceneRect(), Qt::KeepAspectRatio);
|
fitInView(sceneRect(), Qt::KeepAspectRatio);
|
||||||
@@ -330,7 +296,6 @@ void GraphicsView::displayScene()
|
|||||||
emit navigatorViewRequired(false, transform());
|
emit navigatorViewRequired(false, transform());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_longImageMode = false;
|
|
||||||
m_enableFitInView = true;
|
m_enableFitInView = true;
|
||||||
m_firstUserMediaLoaded = true;
|
m_firstUserMediaLoaded = true;
|
||||||
}
|
}
|
||||||
@@ -350,11 +315,6 @@ void GraphicsView::setEnableAutoFitInView(bool enable)
|
|||||||
m_enableFitInView = enable;
|
m_enableFitInView = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsView::setLongImageMode(bool enable)
|
|
||||||
{
|
|
||||||
m_longImageMode = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphicsView::avoidResetTransform() const
|
bool GraphicsView::avoidResetTransform() const
|
||||||
{
|
{
|
||||||
return m_avoidResetTransform;
|
return m_avoidResetTransform;
|
||||||
@@ -427,12 +387,7 @@ void GraphicsView::wheelEvent(QWheelEvent *event)
|
|||||||
|
|
||||||
void GraphicsView::resizeEvent(QResizeEvent *event)
|
void GraphicsView::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
if (m_longImageMode) {
|
if (m_enableFitInView) {
|
||||||
// In long image mode, reapply long image logic on resize
|
|
||||||
// We directly apply the long image mode logic without rechecking
|
|
||||||
// if we should enter long image mode, as the mode is already active
|
|
||||||
fitLongImage();
|
|
||||||
} else if (m_enableFitInView) {
|
|
||||||
bool originalSizeSmallerThanWindow = isThingSmallerThanWindowWith(resetScale(transform()));
|
bool originalSizeSmallerThanWindow = isThingSmallerThanWindowWith(resetScale(transform()));
|
||||||
if (originalSizeSmallerThanWindow && scaleFactor() >= 1) {
|
if (originalSizeSmallerThanWindow && scaleFactor() >= 1) {
|
||||||
// no longer need to do fitInView()
|
// no longer need to do fitInView()
|
||||||
|
|||||||
@@ -39,17 +39,12 @@ public:
|
|||||||
void displayScene();
|
void displayScene();
|
||||||
bool isSceneBiggerThanView() const;
|
bool isSceneBiggerThanView() const;
|
||||||
void setEnableAutoFitInView(bool enable = true);
|
void setEnableAutoFitInView(bool enable = true);
|
||||||
void setLongImageMode(bool enable = true);
|
|
||||||
|
|
||||||
bool avoidResetTransform() const;
|
bool avoidResetTransform() const;
|
||||||
void setAvoidResetTransform(bool avoidReset);
|
void setAvoidResetTransform(bool avoidReset);
|
||||||
|
|
||||||
static QTransform resetScale(const QTransform & orig);
|
static QTransform resetScale(const QTransform & orig);
|
||||||
|
|
||||||
// Long image mode support
|
|
||||||
bool isLongImage() const;
|
|
||||||
void fitLongImage();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void navigatorViewRequired(bool required, QTransform transform);
|
void navigatorViewRequired(bool required, QTransform transform);
|
||||||
void viewportRectChanged();
|
void viewportRectChanged();
|
||||||
@@ -75,7 +70,6 @@ private:
|
|||||||
// ... or even more? e.g. "fit/snap width" things...
|
// ... or even more? e.g. "fit/snap width" things...
|
||||||
// Currently it's "no fit" when it's false and "fit when view is smaller" when it's true.
|
// Currently it's "no fit" when it's false and "fit when view is smaller" when it's true.
|
||||||
bool m_enableFitInView = false;
|
bool m_enableFitInView = false;
|
||||||
bool m_longImageMode = false;
|
|
||||||
bool m_avoidResetTransform = false;
|
bool m_avoidResetTransform = false;
|
||||||
bool m_checkerboardEnabled = false;
|
bool m_checkerboardEnabled = false;
|
||||||
bool m_useLightCheckerboard = false;
|
bool m_useLightCheckerboard = false;
|
||||||
|
|||||||
+69
-309
@@ -5,7 +5,6 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "toolbutton.h"
|
|
||||||
#include "bottombuttongroup.h"
|
#include "bottombuttongroup.h"
|
||||||
#include "graphicsview.h"
|
#include "graphicsview.h"
|
||||||
#include "navigatorview.h"
|
#include "navigatorview.h"
|
||||||
@@ -38,7 +37,6 @@
|
|||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QImageWriter>
|
|
||||||
|
|
||||||
#ifdef HAVE_QTDBUS
|
#ifdef HAVE_QTDBUS
|
||||||
#include <QDBusInterface>
|
#include <QDBusInterface>
|
||||||
@@ -53,77 +51,44 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
, m_pm(new PlaylistManager(this))
|
, m_pm(new PlaylistManager(this))
|
||||||
, m_fileSystemWatcher(new QFileSystemWatcher(this))
|
, m_fileSystemWatcher(new QFileSystemWatcher(this))
|
||||||
{
|
{
|
||||||
if (Settings::instance()->stayOnTop()) {
|
|
||||||
this->setWindowFlag(Qt::WindowStaysOnTopHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->setAttribute(Qt::WA_TranslucentBackground, true);
|
this->setAttribute(Qt::WA_TranslucentBackground, true);
|
||||||
this->setMinimumSize(350, 330);
|
// YYC MARK:
|
||||||
|
// Blumis set original value is 350 x 330.
|
||||||
|
// It is too small for modern device,
|
||||||
|
// so I level it up to standard VGA resolution 640 x 480.
|
||||||
|
this->setMinimumSize(640, 480);
|
||||||
this->setWindowIcon(QIcon(u":/icons/app-icon.svg"_s));
|
this->setWindowIcon(QIcon(u":/icons/app-icon.svg"_s));
|
||||||
this->setMouseTracking(true);
|
this->setMouseTracking(true);
|
||||||
this->setAcceptDrops(true);
|
this->setAcceptDrops(true);
|
||||||
|
|
||||||
m_pm->setAutoLoadFilterSuffixes(supportedImageFormats());
|
m_pm->setAutoLoadFilterSuffixes(supportedImageFormats());
|
||||||
|
|
||||||
m_fadeOutAnimation = new QPropertyAnimation(this, "windowOpacity"_ba);
|
|
||||||
m_fadeOutAnimation->setDuration(300);
|
|
||||||
m_fadeOutAnimation->setStartValue(1);
|
|
||||||
m_fadeOutAnimation->setEndValue(0);
|
|
||||||
m_floatUpAnimation = new QPropertyAnimation(this, "geometry"_ba);
|
|
||||||
m_floatUpAnimation->setDuration(300);
|
|
||||||
m_floatUpAnimation->setEasingCurve(QEasingCurve::OutCirc);
|
|
||||||
m_exitAnimationGroup = new QParallelAnimationGroup(this);
|
|
||||||
m_exitAnimationGroup->addAnimation(m_fadeOutAnimation);
|
|
||||||
m_exitAnimationGroup->addAnimation(m_floatUpAnimation);
|
|
||||||
connect(m_exitAnimationGroup, &QParallelAnimationGroup::finished,
|
|
||||||
this, &MainWindow::doCloseWindow);
|
|
||||||
|
|
||||||
GraphicsScene * scene = new GraphicsScene(this);
|
GraphicsScene * scene = new GraphicsScene(this);
|
||||||
|
|
||||||
m_graphicsView = new GraphicsView(this);
|
m_gv = new GraphicsView(this);
|
||||||
m_graphicsView->setScene(scene);
|
|
||||||
this->setCentralWidget(m_graphicsView);
|
|
||||||
|
|
||||||
m_gv = new NavigatorView(this);
|
|
||||||
m_gv->setFixedSize(220, 160);
|
|
||||||
m_gv->setScene(scene);
|
m_gv->setScene(scene);
|
||||||
m_gv->setMainView(m_graphicsView);
|
this->setCentralWidget(m_gv);
|
||||||
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
|
|
||||||
|
|
||||||
connect(m_graphicsView, &GraphicsView::navigatorViewRequired,
|
m_nav = new NavigatorView(this);
|
||||||
|
// YYC MARK:
|
||||||
|
// Blumia set original value is 220 x 160.
|
||||||
|
// So if we set its height to 72 for respecting the height of Windows Photo Viewer,
|
||||||
|
// the width should be set to 99 for keeping aspect ratio.
|
||||||
|
m_nav->setFixedSize(99, 72);
|
||||||
|
m_nav->setScene(scene);
|
||||||
|
m_nav->setMainView(m_gv);
|
||||||
|
m_nav->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
connect(m_gv, &GraphicsView::navigatorViewRequired,
|
||||||
this, [this](bool required, const QTransform & tf){
|
this, [this](bool required, const QTransform & tf){
|
||||||
m_gv->setTransform(GraphicsView::resetScale(tf));
|
m_nav->setTransform(GraphicsView::resetScale(tf));
|
||||||
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
|
m_nav->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
|
||||||
m_gv->setVisible(required);
|
m_nav->setVisible(required);
|
||||||
m_gv->updateMainViewportRegion();
|
m_nav->updateMainViewportRegion();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_graphicsView, &GraphicsView::viewportRectChanged,
|
connect(m_gv, &GraphicsView::viewportRectChanged,
|
||||||
m_gv, &NavigatorView::updateMainViewportRegion);
|
m_nav, &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");
|
|
||||||
|
|
||||||
connect(m_closeButton, &QAbstractButton::clicked,
|
|
||||||
this, &MainWindow::closeWindow);
|
|
||||||
|
|
||||||
m_prevButton = new ToolButton(false, 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->setIconSize(QSize(75, 75));
|
|
||||||
m_nextButton->setIconResourcePath(":/icons/go-next.svg");
|
|
||||||
m_nextButton->setVisible(false);
|
|
||||||
m_nextButton->setOpacity(0, false);
|
|
||||||
|
|
||||||
connect(m_prevButton, &QAbstractButton::clicked,
|
|
||||||
this, &MainWindow::galleryPrev);
|
|
||||||
connect(m_nextButton, &QAbstractButton::clicked,
|
|
||||||
this, &MainWindow::galleryNext);
|
|
||||||
|
|
||||||
m_am->setupAction(this);
|
m_am->setupAction(this);
|
||||||
|
|
||||||
@@ -132,14 +97,12 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
m_am->actionToggleMaximize,
|
m_am->actionToggleMaximize,
|
||||||
m_am->actionZoomIn,
|
m_am->actionZoomIn,
|
||||||
m_am->actionZoomOut,
|
m_am->actionZoomOut,
|
||||||
|
m_am->actionPrevPicture,
|
||||||
|
m_am->actionNextPicture,
|
||||||
m_am->actionToggleCheckerboard,
|
m_am->actionToggleCheckerboard,
|
||||||
m_am->actionRotateClockwise
|
m_am->actionRotateClockwise
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
m_bottomButtonGroup->setOpacity(0, false);
|
|
||||||
m_gv->setOpacity(0, false);
|
|
||||||
m_closeButton->setOpacity(0, false);
|
|
||||||
|
|
||||||
connect(m_pm, &PlaylistManager::totalCountChanged, this, &MainWindow::updateGalleryButtonsVisibility);
|
connect(m_pm, &PlaylistManager::totalCountChanged, this, &MainWindow::updateGalleryButtonsVisibility);
|
||||||
|
|
||||||
connect(m_pm->model(), &PlaylistModel::modelReset, this, std::bind(&MainWindow::galleryCurrent, this, false, false));
|
connect(m_pm->model(), &PlaylistModel::modelReset, this, std::bind(&MainWindow::galleryCurrent, this, false, false));
|
||||||
@@ -157,15 +120,8 @@ MainWindow::MainWindow(QWidget *parent)
|
|||||||
|
|
||||||
QTimer::singleShot(0, this, [this](){
|
QTimer::singleShot(0, this, [this](){
|
||||||
m_am->setupShortcuts();
|
m_am->setupShortcuts();
|
||||||
Settings::instance()->applyUserShortcuts(this);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// allow some mouse events can go through these widgets for resizing window.
|
|
||||||
installResizeCapture(m_closeButton);
|
|
||||||
installResizeCapture(m_graphicsView);
|
|
||||||
installResizeCapture(m_graphicsView->viewport());
|
|
||||||
installResizeCapture(m_gv);
|
|
||||||
installResizeCapture(m_gv->viewport());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
@@ -185,15 +141,15 @@ void MainWindow::showUrls(const QList<QUrl> &urls)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_graphicsView->showFileFromPath(firstUrl.toLocalFile());
|
m_gv->showFileFromPath(firstUrl.toLocalFile());
|
||||||
m_pm->loadPlaylist(urls);
|
m_pm->loadPlaylist(urls);
|
||||||
} else {
|
} else {
|
||||||
m_graphicsView->showText(tr("File url list is empty"));
|
m_gv->showText(tr("File url list is empty"));
|
||||||
m_pm->setPlaylist(urls);
|
m_pm->setPlaylist(urls);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_gv->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
|
m_nav->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::initWindowSize()
|
void MainWindow::initWindowSize()
|
||||||
@@ -218,10 +174,10 @@ void MainWindow::adjustWindowSizeBySceneRect()
|
|||||||
{
|
{
|
||||||
if (m_pm->totalCount() < 1) return;
|
if (m_pm->totalCount() < 1) return;
|
||||||
|
|
||||||
QSize sceneSize = m_graphicsView->sceneRect().toRect().size();
|
QSize sceneSize = m_gv->sceneRect().toRect().size();
|
||||||
QSize sceneSizeWithMargins = sceneSize + QSize(130, 125);
|
QSize sceneSizeWithMargins = sceneSize + QSize(130, 125);
|
||||||
|
|
||||||
if (m_graphicsView->scaleFactor() < 1 || size().expandedTo(sceneSizeWithMargins) != size()) {
|
if (m_gv->scaleFactor() < 1 || size().expandedTo(sceneSizeWithMargins) != size()) {
|
||||||
// if it scaled down by the resize policy:
|
// if it scaled down by the resize policy:
|
||||||
QSize screenSize = qApp->screenAt(QCursor::pos())->availableSize();
|
QSize screenSize = qApp->screenAt(QCursor::pos())->availableSize();
|
||||||
if (screenSize.expandedTo(sceneSize) == screenSize) {
|
if (screenSize.expandedTo(sceneSize) == screenSize) {
|
||||||
@@ -235,7 +191,7 @@ void MainWindow::adjustWindowSizeBySceneRect()
|
|||||||
// The old window size may cause fitInView call from resize() and the
|
// The old window size may cause fitInView call from resize() and the
|
||||||
// above resize() call won't reset the scale back to 1:1, so we
|
// above resize() call won't reset the scale back to 1:1, so we
|
||||||
// just call resetScale() here to ensure the thing is no longer scaled.
|
// just call resetScale() here to ensure the thing is no longer scaled.
|
||||||
m_graphicsView->resetScale();
|
m_gv->resetScale();
|
||||||
centerWindow();
|
centerWindow();
|
||||||
} else {
|
} else {
|
||||||
// toggle maximum
|
// toggle maximum
|
||||||
@@ -263,7 +219,7 @@ void MainWindow::galleryPrev()
|
|||||||
QModelIndex index = m_pm->previousIndex();
|
QModelIndex index = m_pm->previousIndex();
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
m_pm->setCurrentIndex(index);
|
m_pm->setCurrentIndex(index);
|
||||||
m_graphicsView->showFileFromPath(m_pm->localFileByIndex(index));
|
m_gv->showFileFromPath(m_pm->localFileByIndex(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +231,7 @@ void MainWindow::galleryNext()
|
|||||||
QModelIndex index = m_pm->nextIndex();
|
QModelIndex index = m_pm->nextIndex();
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
m_pm->setCurrentIndex(index);
|
m_pm->setCurrentIndex(index);
|
||||||
m_graphicsView->showFileFromPath(m_pm->localFileByIndex(index));
|
m_gv->showFileFromPath(m_pm->localFileByIndex(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,11 +242,12 @@ void MainWindow::galleryCurrent(bool showLoadImageHintWhenEmpty, bool reloadImag
|
|||||||
bool shouldResetfileWatcher = true;
|
bool shouldResetfileWatcher = true;
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
const QString & localFilePath(m_pm->localFileByIndex(index));
|
const QString & localFilePath(m_pm->localFileByIndex(index));
|
||||||
if (reloadImage) m_graphicsView->showFileFromPath(localFilePath);
|
if (reloadImage) m_gv->showFileFromPath(localFilePath);
|
||||||
shouldResetfileWatcher = !updateFileWatcher(localFilePath);
|
shouldResetfileWatcher = !updateFileWatcher(localFilePath);
|
||||||
setWindowTitle(m_pm->urlByIndex(index).fileName());
|
setWindowTitle(m_pm->urlByIndex(index).fileName());
|
||||||
} else if (showLoadImageHintWhenEmpty && m_pm->totalCount() <= 0) {
|
} else if (showLoadImageHintWhenEmpty && m_pm->totalCount() <= 0) {
|
||||||
m_graphicsView->showText(QCoreApplication::translate("GraphicsScene", "Drag image here"));
|
m_gv->showText(QCoreApplication::translate("GraphicsScene", "Drag image here"));
|
||||||
|
setWindowTitle(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGalleryButtonsVisibility();
|
updateGalleryButtonsVisibility();
|
||||||
@@ -320,69 +277,14 @@ void MainWindow::showEvent(QShowEvent *event)
|
|||||||
|
|
||||||
void MainWindow::enterEvent(QEnterEvent *event)
|
void MainWindow::enterEvent(QEnterEvent *event)
|
||||||
{
|
{
|
||||||
m_bottomButtonGroup->setOpacity(1);
|
|
||||||
m_gv->setOpacity(1);
|
|
||||||
|
|
||||||
m_closeButton->setOpacity(1);
|
|
||||||
m_prevButton->setOpacity(1);
|
|
||||||
m_nextButton->setOpacity(1);
|
|
||||||
|
|
||||||
return FramelessWindow::enterEvent(event);
|
return FramelessWindow::enterEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::leaveEvent(QEvent *event)
|
void MainWindow::leaveEvent(QEvent *event)
|
||||||
{
|
{
|
||||||
m_bottomButtonGroup->setOpacity(0);
|
|
||||||
m_gv->setOpacity(0);
|
|
||||||
|
|
||||||
m_closeButton->setOpacity(0);
|
|
||||||
m_prevButton->setOpacity(0);
|
|
||||||
m_nextButton->setOpacity(0);
|
|
||||||
|
|
||||||
return FramelessWindow::leaveEvent(event);
|
return FramelessWindow::leaveEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() & Qt::LeftButton && !isMaximized()) {
|
|
||||||
m_clickedOnWindow = true;
|
|
||||||
m_oldMousePos = event->pos();
|
|
||||||
// qDebug() << m_oldMousePos << m_graphicsView->transform().m11()
|
|
||||||
// << m_graphicsView->transform().m22() << m_graphicsView->matrix().m12();
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
return FramelessWindow::mousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() & Qt::LeftButton && m_clickedOnWindow && !isMaximized() && !isFullScreen()) {
|
|
||||||
if (!window()->windowHandle()->startSystemMove()) {
|
|
||||||
move(event->globalPosition().toPoint() - m_oldMousePos);
|
|
||||||
}
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
return FramelessWindow::mouseMoveEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
m_clickedOnWindow = false;
|
|
||||||
|
|
||||||
// It seems the forward/back mouse button won't generate a key event [1] so we can't use
|
|
||||||
// QShortcut or QKeySequence to indicate these shortcuts, so we do it here.
|
|
||||||
// Reference:
|
|
||||||
// [1]: https://codereview.qt-project.org/c/qt/qtbase/+/177475
|
|
||||||
if (event->button() == Qt::ForwardButton || event->button() == Qt::BackButton) {
|
|
||||||
event->button() == Qt::BackButton ? galleryPrev() : galleryNext();
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
return FramelessWindow::mouseReleaseEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
|
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
// The forward/back mouse button can also used to trigger a mouse double-click event
|
// The forward/back mouse button can also used to trigger a mouse double-click event
|
||||||
@@ -475,17 +377,9 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
|
|
||||||
QAction * trash = m_am->actionTrash;
|
QAction * trash = m_am->actionTrash;
|
||||||
|
|
||||||
QAction * stayOnTopMode = m_am->actionToggleStayOnTop;
|
|
||||||
stayOnTopMode->setCheckable(true);
|
|
||||||
stayOnTopMode->setChecked(stayOnTop());
|
|
||||||
|
|
||||||
QAction * protectedMode = m_am->actionToggleProtectMode;
|
|
||||||
protectedMode->setCheckable(true);
|
|
||||||
protectedMode->setChecked(m_protectedMode);
|
|
||||||
|
|
||||||
QAction * avoidResetTransform = m_am->actionToggleAvoidResetTransform;
|
QAction * avoidResetTransform = m_am->actionToggleAvoidResetTransform;
|
||||||
avoidResetTransform->setCheckable(true);
|
avoidResetTransform->setCheckable(true);
|
||||||
avoidResetTransform->setChecked(m_graphicsView->avoidResetTransform());
|
avoidResetTransform->setChecked(m_gv->avoidResetTransform());
|
||||||
|
|
||||||
QAction * toggleSettings = m_am->actionSettings;
|
QAction * toggleSettings = m_am->actionSettings;
|
||||||
QAction * helpAction = m_am->actionHelp;
|
QAction * helpAction = m_am->actionHelp;
|
||||||
@@ -505,12 +399,6 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
menu->addAction(paste);
|
menu->addAction(paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (currentFileUrl.isValid()) {
|
|
||||||
menu->addAction(m_am->actionSaveAs);
|
|
||||||
}
|
|
||||||
#endif // 0
|
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
menu->addAction(m_am->actionHorizontalFlip);
|
menu->addAction(m_am->actionHorizontalFlip);
|
||||||
@@ -519,8 +407,6 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
|
|||||||
menu->addAction(m_am->actionFitByWidth);
|
menu->addAction(m_am->actionFitByWidth);
|
||||||
#endif // 0
|
#endif // 0
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(stayOnTopMode);
|
|
||||||
menu->addAction(protectedMode);
|
|
||||||
menu->addAction(avoidResetTransform);
|
menu->addAction(avoidResetTransform);
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(toggleSettings);
|
menu->addAction(toggleSettings);
|
||||||
@@ -565,7 +451,7 @@ void MainWindow::dropEvent(QDropEvent *event)
|
|||||||
if (mimeData->hasUrls()) {
|
if (mimeData->hasUrls()) {
|
||||||
const QList<QUrl> &urls = mimeData->urls();
|
const QList<QUrl> &urls = mimeData->urls();
|
||||||
if (urls.isEmpty()) {
|
if (urls.isEmpty()) {
|
||||||
m_graphicsView->showText(tr("File url list is empty"));
|
m_gv->showText(tr("File url list is empty"));
|
||||||
} else {
|
} else {
|
||||||
showUrls(urls);
|
showUrls(urls);
|
||||||
}
|
}
|
||||||
@@ -573,14 +459,14 @@ void MainWindow::dropEvent(QDropEvent *event)
|
|||||||
QImage img = qvariant_cast<QImage>(mimeData->imageData());
|
QImage img = qvariant_cast<QImage>(mimeData->imageData());
|
||||||
QPixmap pixmap = QPixmap::fromImage(img);
|
QPixmap pixmap = QPixmap::fromImage(img);
|
||||||
if (pixmap.isNull()) {
|
if (pixmap.isNull()) {
|
||||||
m_graphicsView->showText(tr("Image data is invalid"));
|
m_gv->showText(tr("Image data is invalid"));
|
||||||
} else {
|
} else {
|
||||||
m_graphicsView->showImage(pixmap);
|
m_gv->showImage(pixmap);
|
||||||
}
|
}
|
||||||
} else if (mimeData->hasText()) {
|
} else if (mimeData->hasText()) {
|
||||||
m_graphicsView->showText(mimeData->text());
|
m_gv->showText(mimeData->text());
|
||||||
} else {
|
} else {
|
||||||
m_graphicsView->showText(tr("Not supported mimedata: %1").arg(mimeData->formats().first()));
|
m_gv->showText(tr("Not supported mimedata: %1").arg(mimeData->formats().first()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,50 +484,18 @@ void MainWindow::centerWindow()
|
|||||||
|
|
||||||
void MainWindow::closeWindow()
|
void MainWindow::closeWindow()
|
||||||
{
|
{
|
||||||
if (Settings::instance()->useBuiltInCloseAnimation()) {
|
|
||||||
QRect windowRect(this->geometry());
|
|
||||||
m_floatUpAnimation->setStartValue(windowRect);
|
|
||||||
m_floatUpAnimation->setEndValue(windowRect.adjusted(0, -80, 0, 0));
|
|
||||||
m_floatUpAnimation->setStartValue(QRect(this->geometry().x(), this->geometry().y(), this->geometry().width(), this->geometry().height()));
|
|
||||||
m_floatUpAnimation->setEndValue(QRect(this->geometry().x(), this->geometry().y()-80, this->geometry().width(), this->geometry().height()));
|
|
||||||
m_exitAnimationGroup->start();
|
|
||||||
} else {
|
|
||||||
doCloseWindow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::updateWidgetsPosition()
|
void MainWindow::updateWidgetsPosition()
|
||||||
{
|
{
|
||||||
m_closeButton->move(width() - m_closeButton->width(), 0);
|
|
||||||
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);
|
|
||||||
m_bottomButtonGroup->move((width() - m_bottomButtonGroup->width()) / 2,
|
m_bottomButtonGroup->move((width() - m_bottomButtonGroup->width()) / 2,
|
||||||
height() - m_bottomButtonGroup->height());
|
(72 - m_bottomButtonGroup->height()) / 2 + (height() - 72));
|
||||||
m_gv->move(width() - m_gv->width(), height() - m_gv->height());
|
m_nav->move(width() - m_nav->width(), height() - m_nav->height());
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::toggleProtectedMode()
|
|
||||||
{
|
|
||||||
m_protectedMode = !m_protectedMode;
|
|
||||||
m_closeButton->setVisible(!m_protectedMode);
|
|
||||||
updateGalleryButtonsVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::toggleStayOnTop()
|
|
||||||
{
|
|
||||||
setWindowFlag(Qt::WindowStaysOnTopHint, !stayOnTop());
|
|
||||||
show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::toggleAvoidResetTransform()
|
void MainWindow::toggleAvoidResetTransform()
|
||||||
{
|
{
|
||||||
m_graphicsView->setAvoidResetTransform(!m_graphicsView->avoidResetTransform());
|
m_gv->setAvoidResetTransform(!m_gv->avoidResetTransform());
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::stayOnTop() const
|
|
||||||
{
|
|
||||||
return windowFlags().testFlag(Qt::WindowStaysOnTopHint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::canPaste() const
|
bool MainWindow::canPaste() const
|
||||||
@@ -664,9 +518,7 @@ bool MainWindow::canPaste() const
|
|||||||
|
|
||||||
void MainWindow::quitAppAction(bool force)
|
void MainWindow::quitAppAction(bool force)
|
||||||
{
|
{
|
||||||
if (!m_protectedMode || force) {
|
closeWindow();
|
||||||
closeWindow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::toggleFullscreen()
|
void MainWindow::toggleFullscreen()
|
||||||
@@ -703,75 +555,10 @@ void MainWindow::on_actionOpen_triggered()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionSaveAs_triggered()
|
|
||||||
{
|
|
||||||
QUrl currentFileUrl = currentImageFileUrl();
|
|
||||||
if (!currentFileUrl.isValid()) {
|
|
||||||
QMessageBox::warning(this, tr("Save As"), tr("No image is currently open."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList supportedFormats;
|
|
||||||
QStringList nameFilters;
|
|
||||||
|
|
||||||
const QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
|
|
||||||
for (const QByteArray &format : imageFormats) {
|
|
||||||
QString formatStr = QString::fromLatin1(format).toLower();
|
|
||||||
if (!formatStr.isEmpty()) {
|
|
||||||
supportedFormats << formatStr;
|
|
||||||
nameFilters << tr("%1 Image (*.%2)").arg(formatStr.toUpper(), formatStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageFormats.isEmpty()) {
|
|
||||||
QMessageBox::warning(this, tr("Save As"),
|
|
||||||
tr("No supported image formats are available."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString selectedFilter;
|
|
||||||
QString saveFilePath = QFileDialog::getSaveFileName(this,
|
|
||||||
tr("Save As"),
|
|
||||||
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
|
|
||||||
nameFilters.join(";;"),
|
|
||||||
&selectedFilter);
|
|
||||||
|
|
||||||
if (saveFilePath.isEmpty()) {
|
|
||||||
return; // User cancelled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the file has the correct extension
|
|
||||||
QString expectedExtension;
|
|
||||||
|
|
||||||
const int filterIndex = nameFilters.indexOf(selectedFilter);
|
|
||||||
if (filterIndex != -1) {
|
|
||||||
expectedExtension = supportedFormats.at(filterIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!expectedExtension.isEmpty() && !saveFilePath.endsWith('.' + expectedExtension, Qt::CaseInsensitive)) {
|
|
||||||
saveFilePath += '.' + expectedExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the image
|
|
||||||
QImageReader imageReader(currentFileUrl.toLocalFile());
|
|
||||||
imageReader.setAutoTransform(true);
|
|
||||||
imageReader.setDecideFormatFromContent(true);
|
|
||||||
imageReader.setAllocationLimit(0);
|
|
||||||
QImage img(imageReader.read());
|
|
||||||
QImageWriter writer(saveFilePath);
|
|
||||||
writer.setFormat(expectedExtension.toLatin1());
|
|
||||||
if (!writer.write(img)) {
|
|
||||||
QMessageBox::warning(this, tr("Save As"),
|
|
||||||
tr("Failed to save image: %1").arg(writer.errorString()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_actionActualSize_triggered()
|
void MainWindow::on_actionActualSize_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->resetScale();
|
m_gv->resetScale();
|
||||||
m_graphicsView->setEnableAutoFitInView(false);
|
m_gv->setEnableAutoFitInView(false);
|
||||||
m_graphicsView->setLongImageMode(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionToggleMaximize_triggered()
|
void MainWindow::on_actionToggleMaximize_triggered()
|
||||||
@@ -781,43 +568,36 @@ void MainWindow::on_actionToggleMaximize_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionZoomIn_triggered()
|
void MainWindow::on_actionZoomIn_triggered()
|
||||||
{
|
{
|
||||||
if (m_graphicsView->scaleFactor() < 1000) {
|
if (m_gv->scaleFactor() < 1000) {
|
||||||
m_graphicsView->zoomView(1.25);
|
m_gv->zoomView(1.25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionZoomOut_triggered()
|
void MainWindow::on_actionZoomOut_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->zoomView(0.8);
|
m_gv->zoomView(0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionHorizontalFlip_triggered()
|
void MainWindow::on_actionHorizontalFlip_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->flipView();
|
m_gv->flipView();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFitInView_triggered()
|
void MainWindow::on_actionFitInView_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
|
m_gv->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
|
||||||
m_graphicsView->setEnableAutoFitInView(m_graphicsView->scaleFactor() <= 1);
|
m_gv->setEnableAutoFitInView(m_gv->scaleFactor() <= 1);
|
||||||
m_graphicsView->setLongImageMode(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFitByWidth_triggered()
|
void MainWindow::on_actionFitByWidth_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->fitByOrientation();
|
m_gv->fitByOrientation();
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_actionFitLongImage_triggered()
|
|
||||||
{
|
|
||||||
m_graphicsView->setLongImageMode(true);
|
|
||||||
m_graphicsView->fitLongImage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionCopyPixmap_triggered()
|
void MainWindow::on_actionCopyPixmap_triggered()
|
||||||
{
|
{
|
||||||
QClipboard *cb = QApplication::clipboard();
|
QClipboard *cb = QApplication::clipboard();
|
||||||
cb->setPixmap(m_graphicsView->scene()->renderToPixmap());
|
cb->setPixmap(m_gv->scene()->renderToPixmap());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionCopyFilePath_triggered()
|
void MainWindow::on_actionCopyFilePath_triggered()
|
||||||
@@ -852,10 +632,10 @@ void MainWindow::on_actionPaste_triggered()
|
|||||||
|
|
||||||
if (!clipboardImage.isNull()) {
|
if (!clipboardImage.isNull()) {
|
||||||
setWindowTitle(tr("Image From Clipboard"));
|
setWindowTitle(tr("Image From Clipboard"));
|
||||||
m_graphicsView->showImage(clipboardImage);
|
m_gv->showImage(clipboardImage);
|
||||||
clearGallery();
|
clearGallery();
|
||||||
} else if (clipboardFileUrl.isValid()) {
|
} else if (clipboardFileUrl.isValid()) {
|
||||||
m_graphicsView->showFileFromPath(clipboardFileUrl.toLocalFile());
|
m_gv->showFileFromPath(clipboardFileUrl.toLocalFile());
|
||||||
m_pm->loadPlaylist(clipboardFileUrl);
|
m_pm->loadPlaylist(clipboardFileUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -885,19 +665,19 @@ void MainWindow::on_actionTrash_triggered()
|
|||||||
void MainWindow::on_actionToggleCheckerboard_triggered()
|
void MainWindow::on_actionToggleCheckerboard_triggered()
|
||||||
{
|
{
|
||||||
// TODO: is that okay to do this since we plan to support custom shortcuts?
|
// TODO: is that okay to do this since we plan to support custom shortcuts?
|
||||||
m_graphicsView->toggleCheckerboard(QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier));
|
m_gv->toggleCheckerboard(QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionRotateClockwise_triggered()
|
void MainWindow::on_actionRotateClockwise_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->rotateView();
|
m_gv->rotateView();
|
||||||
m_graphicsView->displayScene();
|
m_gv->displayScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionRotateCounterClockwise_triggered()
|
void MainWindow::on_actionRotateCounterClockwise_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->rotateView(false);
|
m_gv->rotateView(false);
|
||||||
m_graphicsView->displayScene();
|
m_gv->displayScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionPrevPicture_triggered()
|
void MainWindow::on_actionPrevPicture_triggered()
|
||||||
@@ -912,22 +692,12 @@ void MainWindow::on_actionNextPicture_triggered()
|
|||||||
|
|
||||||
void MainWindow::on_actionTogglePauseAnimation_triggered()
|
void MainWindow::on_actionTogglePauseAnimation_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->scene()->togglePauseAnimation();
|
m_gv->scene()->togglePauseAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionAnimationNextFrame_triggered()
|
void MainWindow::on_actionAnimationNextFrame_triggered()
|
||||||
{
|
{
|
||||||
m_graphicsView->scene()->skipAnimationFrame(1);
|
m_gv->scene()->skipAnimationFrame(1);
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_actionToggleStayOnTop_triggered()
|
|
||||||
{
|
|
||||||
toggleStayOnTop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::on_actionToggleProtectMode_triggered()
|
|
||||||
{
|
|
||||||
toggleProtectedMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionToggleAvoidResetTransform_triggered()
|
void MainWindow::on_actionToggleAvoidResetTransform_triggered()
|
||||||
@@ -1003,15 +773,6 @@ void MainWindow::on_actionQuitApp_triggered()
|
|||||||
quitAppAction(false);
|
quitAppAction(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::doCloseWindow()
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
this->hide();
|
|
||||||
#else
|
|
||||||
this->close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::updateFileWatcher(const QString &basePath)
|
bool MainWindow::updateFileWatcher(const QString &basePath)
|
||||||
{
|
{
|
||||||
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
|
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
|
||||||
@@ -1023,8 +784,7 @@ void MainWindow::updateGalleryButtonsVisibility()
|
|||||||
{
|
{
|
||||||
const int galleryFileCount = m_pm->totalCount();
|
const int galleryFileCount = m_pm->totalCount();
|
||||||
const bool loopGallery = Settings::instance()->loopGallery();
|
const bool loopGallery = Settings::instance()->loopGallery();
|
||||||
m_prevButton->setVisible(!m_protectedMode && galleryFileCount > 1);
|
m_am->enablePrevNextPictureAction(
|
||||||
m_nextButton->setVisible(!m_protectedMode && galleryFileCount > 1);
|
galleryFileCount > 1 && (loopGallery || !m_pm->isFirstIndex()),
|
||||||
m_prevButton->setEnabled(loopGallery || !m_pm->isFirstIndex());
|
galleryFileCount > 1 && (loopGallery || !m_pm->isLastIndex()));
|
||||||
m_nextButton->setEnabled(loopGallery || !m_pm->isLastIndex());
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-25
@@ -12,14 +12,12 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QGraphicsOpacityEffect;
|
|
||||||
class QGraphicsView;
|
class QGraphicsView;
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class ActionManager;
|
class ActionManager;
|
||||||
class PlaylistManager;
|
class PlaylistManager;
|
||||||
class ToolButton;
|
|
||||||
class GraphicsView;
|
class GraphicsView;
|
||||||
class NavigatorView;
|
class NavigatorView;
|
||||||
class BottomButtonGroup;
|
class BottomButtonGroup;
|
||||||
@@ -47,9 +45,6 @@ protected slots:
|
|||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
void enterEvent(QEnterEvent *event) override;
|
void enterEvent(QEnterEvent *event) override;
|
||||||
void leaveEvent(QEvent *event) override;
|
void leaveEvent(QEvent *event) override;
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
|
||||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
void wheelEvent(QWheelEvent *event) override;
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
@@ -61,10 +56,7 @@ protected slots:
|
|||||||
void centerWindow();
|
void centerWindow();
|
||||||
void closeWindow();
|
void closeWindow();
|
||||||
void updateWidgetsPosition();
|
void updateWidgetsPosition();
|
||||||
void toggleProtectedMode();
|
|
||||||
void toggleStayOnTop();
|
|
||||||
void toggleAvoidResetTransform();
|
void toggleAvoidResetTransform();
|
||||||
bool stayOnTop() const;
|
|
||||||
bool canPaste() const;
|
bool canPaste() const;
|
||||||
void quitAppAction(bool force = false);
|
void quitAppAction(bool force = false);
|
||||||
void toggleFullscreen();
|
void toggleFullscreen();
|
||||||
@@ -75,7 +67,6 @@ protected:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_actionOpen_triggered();
|
void on_actionOpen_triggered();
|
||||||
void on_actionSaveAs_triggered();
|
|
||||||
|
|
||||||
void on_actionActualSize_triggered();
|
void on_actionActualSize_triggered();
|
||||||
void on_actionToggleMaximize_triggered();
|
void on_actionToggleMaximize_triggered();
|
||||||
@@ -94,13 +85,10 @@ private slots:
|
|||||||
void on_actionHorizontalFlip_triggered();
|
void on_actionHorizontalFlip_triggered();
|
||||||
void on_actionFitInView_triggered();
|
void on_actionFitInView_triggered();
|
||||||
void on_actionFitByWidth_triggered();
|
void on_actionFitByWidth_triggered();
|
||||||
void on_actionFitLongImage_triggered();
|
|
||||||
void on_actionCopyPixmap_triggered();
|
void on_actionCopyPixmap_triggered();
|
||||||
void on_actionCopyFilePath_triggered();
|
void on_actionCopyFilePath_triggered();
|
||||||
void on_actionPaste_triggered();
|
void on_actionPaste_triggered();
|
||||||
void on_actionTrash_triggered();
|
void on_actionTrash_triggered();
|
||||||
void on_actionToggleStayOnTop_triggered();
|
|
||||||
void on_actionToggleProtectMode_triggered();
|
|
||||||
void on_actionToggleAvoidResetTransform_triggered();
|
void on_actionToggleAvoidResetTransform_triggered();
|
||||||
void on_actionSettings_triggered();
|
void on_actionSettings_triggered();
|
||||||
void on_actionHelp_triggered();
|
void on_actionHelp_triggered();
|
||||||
@@ -108,8 +96,6 @@ private slots:
|
|||||||
void on_actionLocateInFileManager_triggered();
|
void on_actionLocateInFileManager_triggered();
|
||||||
void on_actionQuitApp_triggered();
|
void on_actionQuitApp_triggered();
|
||||||
|
|
||||||
void doCloseWindow();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool updateFileWatcher(const QString & basePath = QString());
|
bool updateFileWatcher(const QString & basePath = QString());
|
||||||
void updateGalleryButtonsVisibility();
|
void updateGalleryButtonsVisibility();
|
||||||
@@ -117,20 +103,11 @@ private:
|
|||||||
private:
|
private:
|
||||||
ActionManager *m_am;
|
ActionManager *m_am;
|
||||||
PlaylistManager *m_pm;
|
PlaylistManager *m_pm;
|
||||||
|
GraphicsView *m_gv;
|
||||||
|
NavigatorView *m_nav;
|
||||||
|
|
||||||
QPoint m_oldMousePos;
|
|
||||||
QPropertyAnimation *m_fadeOutAnimation;
|
|
||||||
QPropertyAnimation *m_floatUpAnimation;
|
|
||||||
QParallelAnimationGroup *m_exitAnimationGroup;
|
|
||||||
QFileSystemWatcher *m_fileSystemWatcher;
|
QFileSystemWatcher *m_fileSystemWatcher;
|
||||||
ToolButton *m_closeButton;
|
|
||||||
ToolButton *m_prevButton;
|
|
||||||
ToolButton *m_nextButton;
|
|
||||||
GraphicsView *m_graphicsView;
|
|
||||||
NavigatorView *m_gv;
|
|
||||||
BottomButtonGroup *m_bottomButtonGroup;
|
BottomButtonGroup *m_bottomButtonGroup;
|
||||||
bool m_protectedMode = false;
|
|
||||||
bool m_clickedOnWindow = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "navigatorview.h"
|
#include "navigatorview.h"
|
||||||
|
|
||||||
#include "graphicsview.h"
|
#include "graphicsview.h"
|
||||||
#include "opacityhelper.h"
|
|
||||||
|
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
NavigatorView::NavigatorView(QWidget *parent)
|
NavigatorView::NavigatorView(QWidget *parent)
|
||||||
: QGraphicsView (parent)
|
: QGraphicsView (parent)
|
||||||
, m_viewportRegion(this->rect())
|
, m_viewportRegion(this->rect())
|
||||||
, m_opacityHelper(new OpacityHelper(this))
|
|
||||||
{
|
{
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
@@ -28,11 +26,6 @@ void NavigatorView::setMainView(GraphicsView *mainView)
|
|||||||
m_mainView = mainView;
|
m_mainView = mainView;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigatorView::setOpacity(qreal opacity, bool animated)
|
|
||||||
{
|
|
||||||
m_opacityHelper->setOpacity(opacity, animated);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NavigatorView::updateMainViewportRegion()
|
void NavigatorView::updateMainViewportRegion()
|
||||||
{
|
{
|
||||||
// Use QTimer::singleShot with lambda to delay the update
|
// Use QTimer::singleShot with lambda to delay the update
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
|
|
||||||
class OpacityHelper;
|
|
||||||
class GraphicsView;
|
class GraphicsView;
|
||||||
class NavigatorView : public QGraphicsView
|
class NavigatorView : public QGraphicsView
|
||||||
{
|
{
|
||||||
@@ -16,7 +15,6 @@ public:
|
|||||||
NavigatorView(QWidget *parent = nullptr);
|
NavigatorView(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void setMainView(GraphicsView *mainView);
|
void setMainView(GraphicsView *mainView);
|
||||||
void setOpacity(qreal opacity, bool animated = true);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateMainViewportRegion();
|
void updateMainViewportRegion();
|
||||||
@@ -32,7 +30,6 @@ private:
|
|||||||
bool m_mouseDown = false;
|
bool m_mouseDown = false;
|
||||||
QPolygon m_viewportRegion;
|
QPolygon m_viewportRegion;
|
||||||
QGraphicsView *m_mainView = nullptr;
|
QGraphicsView *m_mainView = nullptr;
|
||||||
OpacityHelper *m_opacityHelper = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NAVIGATORVIEW_H
|
#endif // NAVIGATORVIEW_H
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "opacityhelper.h"
|
|
||||||
|
|
||||||
#include <QGraphicsOpacityEffect>
|
|
||||||
#include <QPropertyAnimation>
|
|
||||||
|
|
||||||
OpacityHelper::OpacityHelper(QWidget *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_opacityFx(new QGraphicsOpacityEffect(parent))
|
|
||||||
, m_opacityAnimation(new QPropertyAnimation(m_opacityFx, "opacity"))
|
|
||||||
{
|
|
||||||
parent->setGraphicsEffect(m_opacityFx);
|
|
||||||
|
|
||||||
m_opacityAnimation->setDuration(300);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpacityHelper::setOpacity(qreal opacity, bool animated)
|
|
||||||
{
|
|
||||||
if (!animated) {
|
|
||||||
m_opacityFx->setOpacity(opacity);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_opacityAnimation->stop();
|
|
||||||
m_opacityAnimation->setStartValue(m_opacityFx->opacity());
|
|
||||||
m_opacityAnimation->setEndValue(opacity);
|
|
||||||
m_opacityAnimation->start();
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef OPACITYHELPER_H
|
|
||||||
#define OPACITYHELPER_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QGraphicsOpacityEffect;
|
|
||||||
class QPropertyAnimation;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class OpacityHelper : QObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OpacityHelper(QWidget * parent);
|
|
||||||
|
|
||||||
void setOpacity(qreal opacity, bool animated = true);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QGraphicsOpacityEffect * m_opacityFx;
|
|
||||||
QPropertyAnimation * m_opacityAnimation;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // OPACITYHELPER_H
|
|
||||||
+16
-97
@@ -13,6 +13,14 @@
|
|||||||
#include <QKeySequence>
|
#include <QKeySequence>
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
Settings *Settings::instance()
|
||||||
|
{
|
||||||
|
// SAFETY:
|
||||||
|
// This is a global singleton and it is leaked by design.
|
||||||
|
static Settings* INSTANCE = new Settings(nullptr);
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QEnumHelper
|
namespace QEnumHelper
|
||||||
{
|
{
|
||||||
template <typename E>
|
template <typename E>
|
||||||
@@ -34,27 +42,6 @@ namespace QEnumHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings *Settings::m_settings_instance = nullptr;
|
|
||||||
|
|
||||||
Settings *Settings::instance()
|
|
||||||
{
|
|
||||||
if (!m_settings_instance) {
|
|
||||||
m_settings_instance = new Settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_settings_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Settings::stayOnTop() const
|
|
||||||
{
|
|
||||||
return m_qsettings->value("stay_on_top", true).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Settings::useBuiltInCloseAnimation() const
|
|
||||||
{
|
|
||||||
return m_qsettings->value("use_built_in_close_animation", true).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Settings::useLightCheckerboard() const
|
bool Settings::useLightCheckerboard() const
|
||||||
{
|
{
|
||||||
return m_qsettings->value("use_light_checkerboard", false).toBool();
|
return m_qsettings->value("use_light_checkerboard", false).toBool();
|
||||||
@@ -65,11 +52,6 @@ bool Settings::loopGallery() const
|
|||||||
return m_qsettings->value("loop_gallery", true).toBool();
|
return m_qsettings->value("loop_gallery", true).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::autoLongImageMode() const
|
|
||||||
{
|
|
||||||
return m_qsettings->value("auto_long_image_mode", true).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Settings::svgTiny12Only() const
|
bool Settings::svgTiny12Only() const
|
||||||
{
|
{
|
||||||
// Qt 6.7.0's SVG support is terrible caused by huge memory usage, see QTBUG-124287
|
// Qt 6.7.0's SVG support is terrible caused by huge memory usage, see QTBUG-124287
|
||||||
@@ -85,44 +67,28 @@ bool Settings::svgTiny12Only() const
|
|||||||
|
|
||||||
Settings::DoubleClickBehavior Settings::doubleClickBehavior() const
|
Settings::DoubleClickBehavior Settings::doubleClickBehavior() const
|
||||||
{
|
{
|
||||||
QString result = m_qsettings->value("double_click_behavior", "Close").toString();
|
QString result = m_qsettings->value("double_click_behavior", "").toString();
|
||||||
|
return QEnumHelper::fromString<DoubleClickBehavior>(result, DoubleClickBehavior::Ignore);
|
||||||
return QEnumHelper::fromString<DoubleClickBehavior>(result, DoubleClickBehavior::Close);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::MouseWheelBehavior Settings::mouseWheelBehavior() const
|
Settings::MouseWheelBehavior Settings::mouseWheelBehavior() const
|
||||||
{
|
{
|
||||||
QString result = m_qsettings->value("mouse_wheel_behavior", "Zoom").toString();
|
QString result = m_qsettings->value("mouse_wheel_behavior", "").toString();
|
||||||
|
|
||||||
return QEnumHelper::fromString<MouseWheelBehavior>(result, MouseWheelBehavior::Zoom);
|
return QEnumHelper::fromString<MouseWheelBehavior>(result, MouseWheelBehavior::Zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::WindowSizeBehavior Settings::initWindowSizeBehavior() const
|
Settings::WindowSizeBehavior Settings::initWindowSizeBehavior() const
|
||||||
{
|
{
|
||||||
QString result = m_qsettings->value("init_window_size_behavior", "Auto").toString();
|
QString result = m_qsettings->value("init_window_size_behavior", "").toString();
|
||||||
|
return QEnumHelper::fromString<WindowSizeBehavior>(result, WindowSizeBehavior::Windowed);
|
||||||
return QEnumHelper::fromString<WindowSizeBehavior>(result, WindowSizeBehavior::Auto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::HighDpiScaleFactorRoundingPolicy Settings::hiDpiScaleFactorBehavior() const
|
Qt::HighDpiScaleFactorRoundingPolicy Settings::hiDpiScaleFactorBehavior() const
|
||||||
{
|
{
|
||||||
QString result = m_qsettings->value("hidpi_scale_factor_behavior", "PassThrough").toString();
|
QString result = m_qsettings->value("hidpi_scale_factor_behavior", "").toString();
|
||||||
|
|
||||||
return QEnumHelper::fromString<Qt::HighDpiScaleFactorRoundingPolicy>(result, Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
return QEnumHelper::fromString<Qt::HighDpiScaleFactorRoundingPolicy>(result, Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::setStayOnTop(bool on)
|
|
||||||
{
|
|
||||||
m_qsettings->setValue("stay_on_top", on);
|
|
||||||
m_qsettings->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Settings::setUseBuiltInCloseAnimation(bool on)
|
|
||||||
{
|
|
||||||
m_qsettings->setValue("use_built_in_close_animation", on);
|
|
||||||
m_qsettings->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Settings::setUseLightCheckerboard(bool light)
|
void Settings::setUseLightCheckerboard(bool light)
|
||||||
{
|
{
|
||||||
m_qsettings->setValue("use_light_checkerboard", light);
|
m_qsettings->setValue("use_light_checkerboard", light);
|
||||||
@@ -135,12 +101,6 @@ void Settings::setLoopGallery(bool on)
|
|||||||
m_qsettings->sync();
|
m_qsettings->sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::setAutoLongImageMode(bool on)
|
|
||||||
{
|
|
||||||
m_qsettings->setValue("auto_long_image_mode", on);
|
|
||||||
m_qsettings->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Settings::setSvgTiny12Only(bool on)
|
void Settings::setSvgTiny12Only(bool on)
|
||||||
{
|
{
|
||||||
m_qsettings->setValue("svg_tiny12_only", on);
|
m_qsettings->setValue("svg_tiny12_only", on);
|
||||||
@@ -171,47 +131,6 @@ void Settings::setHiDpiScaleFactorBehavior(Qt::HighDpiScaleFactorRoundingPolicy
|
|||||||
m_qsettings->sync();
|
m_qsettings->sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::applyUserShortcuts(QWidget *widget)
|
|
||||||
{
|
|
||||||
m_qsettings->beginGroup("shortcuts");
|
|
||||||
const QStringList shortcutNames = m_qsettings->allKeys();
|
|
||||||
for (const QString & name : shortcutNames) {
|
|
||||||
QList<QKeySequence> shortcuts = m_qsettings->value(name).value<QList<QKeySequence>>();
|
|
||||||
setShortcutsForAction(widget, name, shortcuts, false);
|
|
||||||
}
|
|
||||||
m_qsettings->endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Settings::setShortcutsForAction(QWidget *widget, const QString &objectName,
|
|
||||||
QList<QKeySequence> shortcuts, bool writeConfig)
|
|
||||||
{
|
|
||||||
QAction * targetAction = nullptr;
|
|
||||||
for (QAction * action : widget->actions()) {
|
|
||||||
if (action->objectName() == objectName) {
|
|
||||||
targetAction = action;
|
|
||||||
} else {
|
|
||||||
for (const QKeySequence & shortcut : std::as_const(shortcuts)) {
|
|
||||||
if (action->shortcuts().contains(shortcut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetAction) {
|
|
||||||
targetAction->setShortcuts(shortcuts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetAction && writeConfig) {
|
|
||||||
m_qsettings->beginGroup("shortcuts");
|
|
||||||
m_qsettings->setValue(objectName, QVariant::fromValue(shortcuts));
|
|
||||||
m_qsettings->endGroup();
|
|
||||||
m_qsettings->sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(FLAG_PORTABLE_MODE_SUPPORT) && defined(Q_OS_WIN)
|
#if defined(FLAG_PORTABLE_MODE_SUPPORT) && defined(Q_OS_WIN)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
// QCoreApplication::applicationDirPath() parses the "applicationDirPath" from arg0, which...
|
// QCoreApplication::applicationDirPath() parses the "applicationDirPath" from arg0, which...
|
||||||
@@ -230,8 +149,8 @@ QString getApplicationDirPath()
|
|||||||
}
|
}
|
||||||
#endif // defined(FLAG_PORTABLE_MODE_SUPPORT) && defined(Q_OS_WIN)
|
#endif // defined(FLAG_PORTABLE_MODE_SUPPORT) && defined(Q_OS_WIN)
|
||||||
|
|
||||||
Settings::Settings()
|
Settings::Settings(QObject *parent)
|
||||||
: QObject(qApp)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
QString configPath;
|
QString configPath;
|
||||||
|
|
||||||
|
|||||||
+2
-13
@@ -25,6 +25,7 @@ public:
|
|||||||
};
|
};
|
||||||
Q_ENUM(MouseWheelBehavior)
|
Q_ENUM(MouseWheelBehavior)
|
||||||
|
|
||||||
|
// TODO: I want to remove Auto behavior in future.
|
||||||
enum WindowSizeBehavior {
|
enum WindowSizeBehavior {
|
||||||
Auto,
|
Auto,
|
||||||
Maximized,
|
Maximized,
|
||||||
@@ -34,36 +35,24 @@ public:
|
|||||||
|
|
||||||
static Settings *instance();
|
static Settings *instance();
|
||||||
|
|
||||||
bool stayOnTop() const;
|
|
||||||
bool useBuiltInCloseAnimation() const;
|
|
||||||
bool useLightCheckerboard() const;
|
bool useLightCheckerboard() const;
|
||||||
bool loopGallery() const;
|
bool loopGallery() const;
|
||||||
bool autoLongImageMode() const;
|
|
||||||
bool svgTiny12Only() const;
|
bool svgTiny12Only() const;
|
||||||
DoubleClickBehavior doubleClickBehavior() const;
|
DoubleClickBehavior doubleClickBehavior() const;
|
||||||
MouseWheelBehavior mouseWheelBehavior() const;
|
MouseWheelBehavior mouseWheelBehavior() const;
|
||||||
WindowSizeBehavior initWindowSizeBehavior() const;
|
WindowSizeBehavior initWindowSizeBehavior() const;
|
||||||
Qt::HighDpiScaleFactorRoundingPolicy hiDpiScaleFactorBehavior() const;
|
Qt::HighDpiScaleFactorRoundingPolicy hiDpiScaleFactorBehavior() const;
|
||||||
|
|
||||||
void setStayOnTop(bool on);
|
|
||||||
void setUseBuiltInCloseAnimation(bool on);
|
|
||||||
void setUseLightCheckerboard(bool light);
|
void setUseLightCheckerboard(bool light);
|
||||||
void setLoopGallery(bool on);
|
void setLoopGallery(bool on);
|
||||||
void setAutoLongImageMode(bool on);
|
|
||||||
void setSvgTiny12Only(bool on);
|
void setSvgTiny12Only(bool on);
|
||||||
void setDoubleClickBehavior(DoubleClickBehavior dcb);
|
void setDoubleClickBehavior(DoubleClickBehavior dcb);
|
||||||
void setMouseWheelBehavior(MouseWheelBehavior mwb);
|
void setMouseWheelBehavior(MouseWheelBehavior mwb);
|
||||||
void setInitWindowSizeBehavior(WindowSizeBehavior wsb);
|
void setInitWindowSizeBehavior(WindowSizeBehavior wsb);
|
||||||
void setHiDpiScaleFactorBehavior(Qt::HighDpiScaleFactorRoundingPolicy hidpi);
|
void setHiDpiScaleFactorBehavior(Qt::HighDpiScaleFactorRoundingPolicy hidpi);
|
||||||
|
|
||||||
void applyUserShortcuts(QWidget * widget);
|
|
||||||
bool setShortcutsForAction(QWidget * widget, const QString & objectName,
|
|
||||||
QList<QKeySequence> shortcuts, bool writeConfig = true);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Settings();
|
Settings(QObject *parent = nullptr);
|
||||||
|
|
||||||
static Settings *m_settings_instance;
|
|
||||||
|
|
||||||
QSettings *m_qsettings;
|
QSettings *m_qsettings;
|
||||||
|
|
||||||
|
|||||||
+113
-170
@@ -3,158 +3,107 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
|
#include "ui_settingsdialog.h"
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "shortcutedit.h"
|
|
||||||
|
|
||||||
#include <QAction>
|
#include <QApplication>
|
||||||
#include <QCheckBox>
|
#include <QTimer>
|
||||||
#include <QComboBox>
|
|
||||||
#include <QFormLayout>
|
|
||||||
#include <QKeySequenceEdit>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QSplitter>
|
|
||||||
#include <QStringListModel>
|
#include <QStringListModel>
|
||||||
#include <QMessageBox>
|
#include <type_traits>
|
||||||
|
|
||||||
|
template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
|
||||||
|
class EnumComboBoxTransformer {
|
||||||
|
public:
|
||||||
|
using Pair = QPair<E, QString>;
|
||||||
|
using Pairs = QList<QPair<E, QString>>;
|
||||||
|
|
||||||
|
EnumComboBoxTransformer(Pairs &&pairs) : pairs(std::move(pairs)) {}
|
||||||
|
~EnumComboBoxTransformer() = default;
|
||||||
|
Q_DISABLE_COPY_MOVE(EnumComboBoxTransformer)
|
||||||
|
|
||||||
|
/// Build Qt model used for QComboBox
|
||||||
|
QStringListModel* buildComboBoxModel(QObject *parent = nullptr) const {
|
||||||
|
QStringList dropDownEntries;
|
||||||
|
for (const auto& pair : std::as_const(this->pairs)) {
|
||||||
|
dropDownEntries.append(pair.second);
|
||||||
|
}
|
||||||
|
return new QStringListModel(dropDownEntries, parent);
|
||||||
|
}
|
||||||
|
/// Convert QComboBox index to enum value.
|
||||||
|
E to_value(int index) const {
|
||||||
|
return this->pairs.at(static_cast<qsizetype>(index)).first;
|
||||||
|
}
|
||||||
|
/// Convert enum value to QComboBox index.
|
||||||
|
int to_index(E value) const {
|
||||||
|
for (qsizetype i = 0; i < pairs.size(); ++i) {
|
||||||
|
if (pairs.at(i).first == value) {
|
||||||
|
return static_cast<int>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Value not found
|
||||||
|
throw std::invalid_argument("invalid enum value for finding index");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Pairs pairs;
|
||||||
|
};
|
||||||
|
|
||||||
|
static EnumComboBoxTransformer<Settings::DoubleClickBehavior> DC_OPTIONS {
|
||||||
|
{
|
||||||
|
{Settings::DoubleClickBehavior::Ignore, QCoreApplication::translate("SettingsDialog", "Do nothing")},
|
||||||
|
{Settings::DoubleClickBehavior::Close, QCoreApplication::translate("SettingsDialog", "Close the window")},
|
||||||
|
{Settings::DoubleClickBehavior::Maximize, QCoreApplication::translate("SettingsDialog", "Toggle maximize")},
|
||||||
|
{Settings::DoubleClickBehavior::FullScreen, QCoreApplication::translate("SettingsDialog", "Toggle fullscreen")}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static EnumComboBoxTransformer<Settings::MouseWheelBehavior, QString> MW_OPTIONS {
|
||||||
|
{
|
||||||
|
{ Settings::MouseWheelBehavior::Zoom, QCoreApplication::translate("SettingsDialog", "Zoom in and out") },
|
||||||
|
{ Settings::MouseWheelBehavior::Switch, QCoreApplication::translate("SettingsDialog", "View next or previous item") }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static EnumComboBoxTransformer<Settings::WindowSizeBehavior, QString> IWS_OPTIONS {
|
||||||
|
{
|
||||||
|
{ Settings::WindowSizeBehavior::Auto, QCoreApplication::translate("SettingsDialog", "Auto size") },
|
||||||
|
{ Settings::WindowSizeBehavior::Maximized, QCoreApplication::translate("SettingsDialog", "Maximized") },
|
||||||
|
{ Settings::WindowSizeBehavior::Windowed, QCoreApplication::translate("SettingsDialog", "Windowed") }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static EnumComboBoxTransformer<Qt::HighDpiScaleFactorRoundingPolicy, QString> HIDPI_OPTIONS {
|
||||||
|
{
|
||||||
|
{ Qt::HighDpiScaleFactorRoundingPolicy::Round, QCoreApplication::translate("SettingsDialog", "Round (Integer scaling)", "This option means round up for .5 and above") },
|
||||||
|
{ Qt::HighDpiScaleFactorRoundingPolicy::Ceil, QCoreApplication::translate("SettingsDialog", "Ceil (Integer scaling)", "This option means always round up") },
|
||||||
|
{ Qt::HighDpiScaleFactorRoundingPolicy::Floor, QCoreApplication::translate("SettingsDialog", "Floor (Integer scaling)", "This option means always round down") },
|
||||||
|
{ Qt::HighDpiScaleFactorRoundingPolicy::PassThrough, QCoreApplication::translate("SettingsDialog", "Follow system (Fractional scaling)", "This option means don't round") }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget *parent)
|
SettingsDialog::SettingsDialog(QWidget *parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent), ui(new Ui::SettingsDialog)
|
||||||
, m_stayOnTop(new QCheckBox)
|
|
||||||
, m_useBuiltInCloseAnimation(new QCheckBox)
|
|
||||||
, m_useLightCheckerboard(new QCheckBox)
|
|
||||||
, m_loopGallery(new QCheckBox)
|
|
||||||
, m_autoLongImageMode(new QCheckBox)
|
|
||||||
, m_svgTiny12Only(new QCheckBox)
|
|
||||||
, m_doubleClickBehavior(new QComboBox)
|
|
||||||
, m_mouseWheelBehavior(new QComboBox)
|
|
||||||
, m_initWindowSizeBehavior(new QComboBox)
|
|
||||||
, m_hiDpiRoundingPolicyBehavior(new QComboBox)
|
|
||||||
{
|
{
|
||||||
this->setWindowTitle(tr("Settings"));
|
ui->setupUi(this);
|
||||||
|
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||||
|
|
||||||
QHBoxLayout * mainLayout = new QHBoxLayout(this);
|
auto* settings = Settings::instance();
|
||||||
QTabWidget * settingsTabs = new QTabWidget(this);
|
ui->m_useLightCheckerboard->setChecked(settings->useLightCheckerboard());
|
||||||
mainLayout->addWidget(settingsTabs);
|
ui->m_loopGallery->setChecked(settings->loopGallery());
|
||||||
|
ui->m_svgTiny12Only->setChecked(settings->svgTiny12Only());
|
||||||
QWidget * settingsFormHolder = new QWidget;
|
ui->m_doubleClickBehavior->setModel(DC_OPTIONS.buildComboBoxModel(this));
|
||||||
QFormLayout * settingsForm = new QFormLayout(settingsFormHolder);
|
Settings::DoubleClickBehavior dcb = settings->doubleClickBehavior();
|
||||||
settingsTabs->addTab(settingsFormHolder, tr("Options"));
|
ui->m_doubleClickBehavior->setCurrentIndex(DC_OPTIONS.to_index(dcb));
|
||||||
|
ui->m_mouseWheelBehavior->setModel(MW_OPTIONS.buildComboBoxModel(this));
|
||||||
QSplitter * shortcutEditorSplitter = new QSplitter;
|
Settings::MouseWheelBehavior mwb = settings->mouseWheelBehavior();
|
||||||
shortcutEditorSplitter->setOrientation(Qt::Vertical);
|
ui->m_mouseWheelBehavior->setCurrentIndex(MW_OPTIONS.to_index(mwb));
|
||||||
shortcutEditorSplitter->setChildrenCollapsible(false);
|
ui->m_initWindowSizeBehavior->setModel(IWS_OPTIONS.buildComboBoxModel(this));
|
||||||
QScrollArea * shortcutScrollArea = new QScrollArea;
|
Settings::WindowSizeBehavior iwsb = settings->initWindowSizeBehavior();
|
||||||
shortcutEditorSplitter->addWidget(shortcutScrollArea);
|
ui->m_initWindowSizeBehavior->setCurrentIndex(IWS_OPTIONS.to_index(iwsb));
|
||||||
shortcutScrollArea->setWidgetResizable(true);
|
ui->m_hiDpiRoundingPolicyBehavior->setModel(HIDPI_OPTIONS.buildComboBoxModel(this));
|
||||||
shortcutScrollArea->setMinimumHeight(200);
|
Qt::HighDpiScaleFactorRoundingPolicy hidpi = settings->hiDpiScaleFactorBehavior();
|
||||||
QWidget * shortcutsFormHolder = new QWidget;
|
ui->m_hiDpiRoundingPolicyBehavior->setCurrentIndex(HIDPI_OPTIONS.to_index(hidpi));
|
||||||
QFormLayout * shortcutsForm = new QFormLayout(shortcutsFormHolder);
|
|
||||||
shortcutScrollArea->setWidget(shortcutsFormHolder);
|
|
||||||
settingsTabs->addTab(shortcutEditorSplitter, tr("Shortcuts"));
|
|
||||||
|
|
||||||
for (const QAction * action : parent->actions()) {
|
|
||||||
ShortcutEdit * shortcutEdit = new ShortcutEdit;
|
|
||||||
shortcutEdit->setObjectName(QLatin1String("shortcut_") + action->objectName());
|
|
||||||
shortcutEdit->setShortcuts(action->shortcuts());
|
|
||||||
shortcutsForm->addRow(action->text(), shortcutEdit);
|
|
||||||
connect(shortcutEdit, &ShortcutEdit::editButtonClicked, this, [=](){
|
|
||||||
if (shortcutEditorSplitter->count() == 1) shortcutEditorSplitter->addWidget(new QWidget);
|
|
||||||
ShortcutEditor * shortcutEditor = new ShortcutEditor(shortcutEdit);
|
|
||||||
shortcutEditor->setDescription(tr("Editing shortcuts for action \"%1\":").arg(action->text()));
|
|
||||||
QWidget * oldEditor = shortcutEditorSplitter->replaceWidget(1, shortcutEditor);
|
|
||||||
shortcutEditorSplitter->setSizes({shortcutEditorSplitter->height(), 1});
|
|
||||||
oldEditor->deleteLater();
|
|
||||||
});
|
|
||||||
connect(shortcutEdit, &ShortcutEdit::applyShortcutsRequested, this, [=](QList<QKeySequence> newShortcuts){
|
|
||||||
bool succ = Settings::instance()->setShortcutsForAction(parent, shortcutEdit->objectName().mid(9),
|
|
||||||
newShortcuts);
|
|
||||||
if (!succ) {
|
|
||||||
QMessageBox::warning(this, tr("Failed to set shortcuts"),
|
|
||||||
tr("Please check if shortcuts are duplicated with existing shortcuts."));
|
|
||||||
}
|
|
||||||
shortcutEdit->setShortcuts(action->shortcuts());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static QList< QPair<Settings::DoubleClickBehavior, QString> > _dc_options {
|
|
||||||
{ Settings::DoubleClickBehavior::Ignore, tr("Do nothing") },
|
|
||||||
{ Settings::DoubleClickBehavior::Close, tr("Close the window") },
|
|
||||||
{ Settings::DoubleClickBehavior::Maximize, tr("Toggle maximize") },
|
|
||||||
{ Settings::DoubleClickBehavior::FullScreen, tr("Toggle fullscreen") }
|
|
||||||
};
|
|
||||||
|
|
||||||
static QList< QPair<Settings::MouseWheelBehavior, QString> > _mw_options {
|
|
||||||
{ Settings::MouseWheelBehavior::Zoom, tr("Zoom in and out") },
|
|
||||||
{ Settings::MouseWheelBehavior::Switch, tr("View next or previous item") }
|
|
||||||
};
|
|
||||||
|
|
||||||
static QList< QPair<Settings::WindowSizeBehavior, QString> > _iws_options {
|
|
||||||
{ Settings::WindowSizeBehavior::Auto, tr("Auto size") },
|
|
||||||
{ Settings::WindowSizeBehavior::Maximized, tr("Maximized") },
|
|
||||||
{ Settings::WindowSizeBehavior::Windowed, tr("Windowed") }
|
|
||||||
};
|
|
||||||
|
|
||||||
static QList< QPair<Qt::HighDpiScaleFactorRoundingPolicy, QString> > _hidpi_options {
|
|
||||||
{ Qt::HighDpiScaleFactorRoundingPolicy::Round, tr("Round (Integer scaling)", "This option means round up for .5 and above") },
|
|
||||||
{ Qt::HighDpiScaleFactorRoundingPolicy::Ceil, tr("Ceil (Integer scaling)", "This option means always round up") },
|
|
||||||
{ Qt::HighDpiScaleFactorRoundingPolicy::Floor, tr("Floor (Integer scaling)", "This option means always round down") },
|
|
||||||
{ Qt::HighDpiScaleFactorRoundingPolicy::PassThrough, tr("Follow system (Fractional scaling)", "This option means don't round") }
|
|
||||||
};
|
|
||||||
|
|
||||||
QStringList dcbDropDown;
|
|
||||||
for (const QPair<Settings::DoubleClickBehavior, QString> & dcOption : _dc_options) {
|
|
||||||
dcbDropDown.append(dcOption.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList mwbDropDown;
|
|
||||||
for (const QPair<Settings::MouseWheelBehavior, QString> & mwOption : _mw_options) {
|
|
||||||
mwbDropDown.append(mwOption.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList iwsbDropDown;
|
|
||||||
for (const QPair<Settings::WindowSizeBehavior, QString> & iwsOption : _iws_options) {
|
|
||||||
iwsbDropDown.append(iwsOption.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList hidpiDropDown;
|
|
||||||
for (const QPair<Qt::HighDpiScaleFactorRoundingPolicy, QString> & hidpiOption : _hidpi_options) {
|
|
||||||
hidpiDropDown.append(hidpiOption.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
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("Use light-color checkerboard"), m_useLightCheckerboard);
|
|
||||||
settingsForm->addRow(tr("Loop the loaded gallery"), m_loopGallery);
|
|
||||||
settingsForm->addRow(tr("Auto long image mode"), m_autoLongImageMode);
|
|
||||||
settingsForm->addRow(tr("Limit SVG support to SVG Tiny 1.2"), m_svgTiny12Only);
|
|
||||||
settingsForm->addRow(tr("Double-click behavior"), m_doubleClickBehavior);
|
|
||||||
settingsForm->addRow(tr("Mouse wheel behavior"), m_mouseWheelBehavior);
|
|
||||||
settingsForm->addRow(tr("Default window size"), m_initWindowSizeBehavior);
|
|
||||||
settingsForm->addRow(tr("HiDPI scale factor rounding policy"), m_hiDpiRoundingPolicyBehavior);
|
|
||||||
|
|
||||||
m_stayOnTop->setChecked(Settings::instance()->stayOnTop());
|
|
||||||
m_useBuiltInCloseAnimation->setChecked(Settings::instance()->useBuiltInCloseAnimation());
|
|
||||||
m_useLightCheckerboard->setChecked(Settings::instance()->useLightCheckerboard());
|
|
||||||
m_loopGallery->setChecked(Settings::instance()->loopGallery());
|
|
||||||
m_autoLongImageMode->setChecked(Settings::instance()->autoLongImageMode());
|
|
||||||
m_svgTiny12Only->setChecked(Settings::instance()->svgTiny12Only());
|
|
||||||
m_doubleClickBehavior->setModel(new QStringListModel(dcbDropDown));
|
|
||||||
Settings::DoubleClickBehavior dcb = Settings::instance()->doubleClickBehavior();
|
|
||||||
m_doubleClickBehavior->setCurrentIndex(static_cast<int>(dcb));
|
|
||||||
m_mouseWheelBehavior->setModel(new QStringListModel(mwbDropDown));
|
|
||||||
Settings::MouseWheelBehavior mwb = Settings::instance()->mouseWheelBehavior();
|
|
||||||
m_mouseWheelBehavior->setCurrentIndex(static_cast<int>(mwb));
|
|
||||||
m_initWindowSizeBehavior->setModel(new QStringListModel(iwsbDropDown));
|
|
||||||
Settings::WindowSizeBehavior iwsb = Settings::instance()->initWindowSizeBehavior();
|
|
||||||
m_initWindowSizeBehavior->setCurrentIndex(static_cast<int>(iwsb));
|
|
||||||
m_hiDpiRoundingPolicyBehavior->setModel(new QStringListModel(hidpiDropDown));
|
|
||||||
Qt::HighDpiScaleFactorRoundingPolicy hidpi = Settings::instance()->hiDpiScaleFactorBehavior();
|
|
||||||
for (int i = 0; i < _hidpi_options.count(); i++) {
|
|
||||||
if (_hidpi_options.at(i).first == hidpi) {
|
|
||||||
m_hiDpiRoundingPolicyBehavior->setCurrentIndex(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||||
# define QCHECKBOX_CHECKSTATECHANGED QCheckBox::checkStateChanged
|
# define QCHECKBOX_CHECKSTATECHANGED QCheckBox::checkStateChanged
|
||||||
@@ -164,51 +113,45 @@ SettingsDialog::SettingsDialog(QWidget *parent)
|
|||||||
# define QT_CHECKSTATE int
|
# define QT_CHECKSTATE int
|
||||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||||
|
|
||||||
connect(m_stayOnTop, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
connect(ui->m_useLightCheckerboard, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
||||||
Settings::instance()->setStayOnTop(state == Qt::Checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_useBuiltInCloseAnimation, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
|
||||||
Settings::instance()->setUseBuiltInCloseAnimation(state == Qt::Checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_useLightCheckerboard, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
|
||||||
Settings::instance()->setUseLightCheckerboard(state == Qt::Checked);
|
Settings::instance()->setUseLightCheckerboard(state == Qt::Checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_loopGallery, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
connect(ui->m_loopGallery, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
||||||
Settings::instance()->setLoopGallery(state == Qt::Checked);
|
Settings::instance()->setLoopGallery(state == Qt::Checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_autoLongImageMode, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
connect(ui->m_svgTiny12Only, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
||||||
Settings::instance()->setAutoLongImageMode(state == Qt::Checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_svgTiny12Only, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
|
|
||||||
Settings::instance()->setSvgTiny12Only(state == Qt::Checked);
|
Settings::instance()->setSvgTiny12Only(state == Qt::Checked);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_doubleClickBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
connect(ui->m_doubleClickBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
||||||
Settings::instance()->setDoubleClickBehavior(_dc_options.at(index).first);
|
Settings::instance()->setDoubleClickBehavior(DC_OPTIONS.to_value(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_mouseWheelBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
connect(ui->m_mouseWheelBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
||||||
Settings::instance()->setMouseWheelBehavior(_mw_options.at(index).first);
|
Settings::instance()->setMouseWheelBehavior(MW_OPTIONS.to_value(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_initWindowSizeBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
connect(ui->m_initWindowSizeBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
||||||
Settings::instance()->setInitWindowSizeBehavior(_iws_options.at(index).first);
|
Settings::instance()->setInitWindowSizeBehavior(IWS_OPTIONS.to_value(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_hiDpiRoundingPolicyBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
connect(ui->m_hiDpiRoundingPolicyBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
|
||||||
Settings::instance()->setHiDpiScaleFactorBehavior(_hidpi_options.at(index).first);
|
Settings::instance()->setHiDpiScaleFactorBehavior(HIDPI_OPTIONS.to_value(index));
|
||||||
});
|
});
|
||||||
|
|
||||||
adjustSize();
|
// YYC MARK:
|
||||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
// Use adjustSize() to make sure all ComboBox are expanded by its items.
|
||||||
|
// Because items are added after inserting them into layout.
|
||||||
|
// It is inviable to call this function in ctor because this relys on Qt event system.
|
||||||
|
// It only can be done by QTimer::singleShot().
|
||||||
|
QTimer::singleShot(0, this, [this](){
|
||||||
|
this->adjustSize();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
{
|
{
|
||||||
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-17
@@ -5,11 +5,14 @@
|
|||||||
#ifndef SETTINGSDIALOG_H
|
#ifndef SETTINGSDIALOG_H
|
||||||
#define SETTINGSDIALOG_H
|
#define SETTINGSDIALOG_H
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
class QCheckBox;
|
QT_BEGIN_NAMESPACE
|
||||||
class QComboBox;
|
namespace Ui {
|
||||||
|
class SettingsDialog;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -17,21 +20,8 @@ public:
|
|||||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCheckBox * m_stayOnTop = nullptr;
|
QT_PREPEND_NAMESPACE(Ui::SettingsDialog) *ui;
|
||||||
QCheckBox * m_useBuiltInCloseAnimation = nullptr;
|
|
||||||
QCheckBox * m_useLightCheckerboard = nullptr;
|
|
||||||
QCheckBox * m_loopGallery = nullptr;
|
|
||||||
QCheckBox * m_autoLongImageMode = nullptr;
|
|
||||||
QCheckBox * m_svgTiny12Only = nullptr;
|
|
||||||
QComboBox * m_doubleClickBehavior = nullptr;
|
|
||||||
QComboBox * m_mouseWheelBehavior = nullptr;
|
|
||||||
QComboBox * m_initWindowSizeBehavior = nullptr;
|
|
||||||
QComboBox * m_hiDpiRoundingPolicyBehavior = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
#endif // SETTINGSDIALOG_H
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SettingsDialog</class>
|
||||||
|
<widget class="QDialog" name="SettingsDialog">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::NonModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="mainLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="settingsTabs">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="optionsTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Options</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QFormLayout" name="settingsForm">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="m_useLightCheckerboard">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use light-color checkerboard</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="m_loopGallery">
|
||||||
|
<property name="text">
|
||||||
|
<string>Loop the loaded gallery</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="m_svgTiny12Only">
|
||||||
|
<property name="text">
|
||||||
|
<string>Limit SVG support to SVG Tiny 1.2</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="m_doubleClickBehaviorLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Double-click behavior</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="m_doubleClickBehavior"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="m_mouseWheelBehaviorLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Mouse wheel behavior</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QComboBox" name="m_mouseWheelBehavior"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="m_initWindowSizeBehaviorLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Default window size</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QComboBox" name="m_initWindowSizeBehavior"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="m_hiDpiRoundingPolicyBehaviorLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>HiDPI scale factor rounding policy</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QComboBox" name="m_hiDpiRoundingPolicyBehavior"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 Gary Wang <opensource@blumia.net>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "shortcutedit.h"
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QToolButton>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QFormLayout>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QKeySequenceEdit>
|
|
||||||
|
|
||||||
ShortcutEditor::ShortcutEditor(ShortcutEdit * shortcutEdit, QWidget * parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, m_descriptionLabel(new QLabel)
|
|
||||||
, m_shortcutEdit(shortcutEdit)
|
|
||||||
, m_shortcutLayout(new QFormLayout)
|
|
||||||
{
|
|
||||||
Q_CHECK_PTR(m_shortcutEdit);
|
|
||||||
|
|
||||||
QDialogButtonBox * buttons = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Discard);
|
|
||||||
|
|
||||||
QVBoxLayout * layout = new QVBoxLayout(this);
|
|
||||||
layout->addWidget(m_descriptionLabel);
|
|
||||||
layout->addLayout(m_shortcutLayout);
|
|
||||||
layout->addWidget(buttons);
|
|
||||||
|
|
||||||
connect(buttons, &QDialogButtonBox::clicked, this, [=](QAbstractButton *button){
|
|
||||||
if ((QPushButton *)button == buttons->button(QDialogButtonBox::Apply)) {
|
|
||||||
applyShortcuts();
|
|
||||||
} else {
|
|
||||||
reloadShortcuts();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(shortcutEdit, &ShortcutEdit::shortcutsChanged, this, &ShortcutEditor::reloadShortcuts);
|
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
|
|
||||||
|
|
||||||
reloadShortcuts();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutEditor::setDescription(const QString &desc)
|
|
||||||
{
|
|
||||||
m_descriptionLabel->setText(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutEditor::reloadShortcuts()
|
|
||||||
{
|
|
||||||
if (!m_keySequenceEdits.isEmpty()) {
|
|
||||||
for (QKeySequenceEdit * keyseqEdit : m_keySequenceEdits) {
|
|
||||||
m_shortcutLayout->removeRow(keyseqEdit);
|
|
||||||
}
|
|
||||||
m_keySequenceEdits.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QKeySequence> shortcuts = m_shortcutEdit->shortcuts();
|
|
||||||
shortcuts.append(QKeySequence());
|
|
||||||
for (const QKeySequence & shortcut : shortcuts) {
|
|
||||||
QKeySequenceEdit * keyseqEdit = new QKeySequenceEdit(this);
|
|
||||||
keyseqEdit->setClearButtonEnabled(true);
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
|
||||||
keyseqEdit->setMaximumSequenceLength(1);
|
|
||||||
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
|
||||||
keyseqEdit->setKeySequence(shortcut);
|
|
||||||
m_keySequenceEdits.append(keyseqEdit);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_keySequenceEdits.count(); i++) {
|
|
||||||
m_shortcutLayout->addRow(tr("Shortcut #%1").arg(i + 1), m_keySequenceEdits.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutEditor::applyShortcuts()
|
|
||||||
{
|
|
||||||
QList<QKeySequence> shortcuts;
|
|
||||||
for (const QKeySequenceEdit * keyseqEdit : m_keySequenceEdits) {
|
|
||||||
if (!keyseqEdit->keySequence().isEmpty() && !shortcuts.contains(keyseqEdit->keySequence())) {
|
|
||||||
shortcuts.append(keyseqEdit->keySequence());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emit m_shortcutEdit->applyShortcutsRequested(shortcuts);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
ShortcutEdit::ShortcutEdit(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, m_shortcutsLabel(new QLabel(this))
|
|
||||||
, m_setShortcutButton(new QToolButton(this))
|
|
||||||
{
|
|
||||||
m_setShortcutButton->setText("...");
|
|
||||||
|
|
||||||
QHBoxLayout * layout = new QHBoxLayout(this);
|
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
layout->addWidget(m_shortcutsLabel, 1);
|
|
||||||
layout->addWidget(m_setShortcutButton);
|
|
||||||
|
|
||||||
connect(this, &ShortcutEdit::shortcutsChanged, this, [=](){
|
|
||||||
QStringList shortcutTexts;
|
|
||||||
for (const QKeySequence & shortcut : std::as_const(m_shortcuts)) {
|
|
||||||
shortcutTexts.append(shortcut.toString(QKeySequence::NativeText));
|
|
||||||
}
|
|
||||||
m_shortcutsLabel->setText(shortcutTexts.isEmpty() ? tr("No shortcuts") : shortcutTexts.join(", "));
|
|
||||||
m_shortcutsLabel->setDisabled(shortcutTexts.isEmpty());
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_setShortcutButton, &QToolButton::clicked, this, &ShortcutEdit::editButtonClicked);
|
|
||||||
|
|
||||||
adjustSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutEdit::~ShortcutEdit()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QKeySequence> ShortcutEdit::shortcuts() const
|
|
||||||
{
|
|
||||||
return m_shortcuts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutEdit::setShortcuts(const QList<QKeySequence> &shortcuts)
|
|
||||||
{
|
|
||||||
m_shortcuts = shortcuts;
|
|
||||||
emit shortcutsChanged();
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 Gary Wang <opensource@blumia.net>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QList>
|
|
||||||
#include <QKeySequence>
|
|
||||||
|
|
||||||
class QLabel;
|
|
||||||
class QFormLayout;
|
|
||||||
class QToolButton;
|
|
||||||
class QKeySequenceEdit;
|
|
||||||
class ShortcutEdit;
|
|
||||||
class ShortcutEditor : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ShortcutEditor(ShortcutEdit * shortcutEdit, QWidget * parent = nullptr);
|
|
||||||
~ShortcutEditor() = default;
|
|
||||||
|
|
||||||
void setDescription(const QString & desc);
|
|
||||||
|
|
||||||
void reloadShortcuts();
|
|
||||||
void applyShortcuts();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel * m_descriptionLabel;
|
|
||||||
ShortcutEdit * m_shortcutEdit;
|
|
||||||
QFormLayout * m_shortcutLayout;
|
|
||||||
QList<QKeySequenceEdit *> m_keySequenceEdits;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShortcutEdit : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QList<QKeySequence> shortcuts MEMBER m_shortcuts WRITE setShortcuts NOTIFY shortcutsChanged)
|
|
||||||
public:
|
|
||||||
explicit ShortcutEdit(QWidget * parent = nullptr);
|
|
||||||
~ShortcutEdit();
|
|
||||||
|
|
||||||
QList<QKeySequence> shortcuts() const;
|
|
||||||
void setShortcuts(const QList<QKeySequence> &shortcuts);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void shortcutsChanged();
|
|
||||||
void editButtonClicked();
|
|
||||||
void applyShortcutsRequested(QList<QKeySequence> newShortcuts);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<QKeySequence> m_shortcuts;
|
|
||||||
QLabel * m_shortcutsLabel;
|
|
||||||
QToolButton * m_setShortcutButton;
|
|
||||||
};
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "toolbutton.h"
|
|
||||||
|
|
||||||
#include "actionmanager.h"
|
|
||||||
#include "opacityhelper.h"
|
|
||||||
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QGraphicsOpacityEffect>
|
|
||||||
#include <QPropertyAnimation>
|
|
||||||
|
|
||||||
ToolButton::ToolButton(bool hoverColor, QWidget *parent)
|
|
||||||
: QPushButton(parent)
|
|
||||||
, m_opacityHelper(new OpacityHelper(this))
|
|
||||||
{
|
|
||||||
setFlat(true);
|
|
||||||
QString qss = "QPushButton {"
|
|
||||||
"background: transparent;"
|
|
||||||
"}";
|
|
||||||
if (hoverColor) {
|
|
||||||
qss += "QPushButton:hover {"
|
|
||||||
"background: red;"
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
setStyleSheet(qss);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolButton::setIconResourcePath(const QString &iconp)
|
|
||||||
{
|
|
||||||
this->setIcon(ActionManager::loadHidpiIcon(iconp, this->iconSize()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToolButton::setOpacity(qreal opacity, bool animated)
|
|
||||||
{
|
|
||||||
m_opacityHelper->setOpacity(opacity, animated);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#ifndef TOOLBUTTON_H
|
|
||||||
#define TOOLBUTTON_H
|
|
||||||
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
class OpacityHelper;
|
|
||||||
class ToolButton : public QPushButton
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ToolButton(bool hoverColor = false, QWidget * parent = nullptr);
|
|
||||||
void setIconResourcePath(const QString &iconp);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setOpacity(qreal opacity, bool animated = true);
|
|
||||||
|
|
||||||
private:
|
|
||||||
OpacityHelper * m_opacityHelper;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // TOOLBUTTON_H
|
|
||||||
@@ -236,27 +236,27 @@
|
|||||||
<location filename="../mainwindow.cpp" line="734"/>
|
<location filename="../mainwindow.cpp" line="734"/>
|
||||||
<location filename="../mainwindow.cpp" line="764"/>
|
<location filename="../mainwindow.cpp" line="764"/>
|
||||||
<source>Save As</source>
|
<source>Save As</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>다른 이름으로 저장</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="710"/>
|
<location filename="../mainwindow.cpp" line="710"/>
|
||||||
<source>No image is currently open.</source>
|
<source>No image is currently open.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>현재 열려 있는 이미지가 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="722"/>
|
<location filename="../mainwindow.cpp" line="722"/>
|
||||||
<source>%1 Image (*.%2)</source>
|
<source>%1 Image (*.%2)</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>%1 이미지 (*.%2)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="728"/>
|
<location filename="../mainwindow.cpp" line="728"/>
|
||||||
<source>No supported image formats are available.</source>
|
<source>No supported image formats are available.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>지원되는 이미지 형식을 사용할 수 없습니다.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="765"/>
|
<location filename="../mainwindow.cpp" line="765"/>
|
||||||
<source>Failed to save image: %1</source>
|
<source>Failed to save image: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>이미지 저장 실패: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="854"/>
|
<location filename="../mainwindow.cpp" line="854"/>
|
||||||
@@ -320,7 +320,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="88"/>
|
<location filename="../actionmanager.cpp" line="88"/>
|
||||||
<source>Save &As...</source>
|
<source>Save &As...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>다른 이름으로 저장(&A)...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="93"/>
|
<location filename="../actionmanager.cpp" line="93"/>
|
||||||
|
|||||||
@@ -240,27 +240,27 @@
|
|||||||
<location filename="../mainwindow.cpp" line="734"/>
|
<location filename="../mainwindow.cpp" line="734"/>
|
||||||
<location filename="../mainwindow.cpp" line="764"/>
|
<location filename="../mainwindow.cpp" line="764"/>
|
||||||
<source>Save As</source>
|
<source>Save As</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Opslaan als</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="710"/>
|
<location filename="../mainwindow.cpp" line="710"/>
|
||||||
<source>No image is currently open.</source>
|
<source>No image is currently open.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Er is geen afbeelding geopend.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="722"/>
|
<location filename="../mainwindow.cpp" line="722"/>
|
||||||
<source>%1 Image (*.%2)</source>
|
<source>%1 Image (*.%2)</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>%1-afbeelding (*.%2)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="728"/>
|
<location filename="../mainwindow.cpp" line="728"/>
|
||||||
<source>No supported image formats are available.</source>
|
<source>No supported image formats are available.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Er zijn geen ondersteunde formaten beschikbaar.</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="765"/>
|
<location filename="../mainwindow.cpp" line="765"/>
|
||||||
<source>Failed to save image: %1</source>
|
<source>Failed to save image: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>De afbeelding kan niet worden opgeslagen: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="854"/>
|
<location filename="../mainwindow.cpp" line="854"/>
|
||||||
@@ -324,7 +324,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="88"/>
|
<location filename="../actionmanager.cpp" line="88"/>
|
||||||
<source>Save &As...</source>
|
<source>Save &As...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Opslaan &als…</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="93"/>
|
<location filename="../actionmanager.cpp" line="93"/>
|
||||||
@@ -359,7 +359,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="107"/>
|
<location filename="../actionmanager.cpp" line="107"/>
|
||||||
<source>Fit long image</source>
|
<source>Fit long image</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Lange afbeelding inpassen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="110"/>
|
<location filename="../actionmanager.cpp" line="110"/>
|
||||||
@@ -891,7 +891,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../settingsdialog.cpp" line="129"/>
|
<location filename="../settingsdialog.cpp" line="129"/>
|
||||||
<source>Limit SVG support to SVG Tiny 1.2</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../settingsdialog.cpp" line="130"/>
|
<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="734"/>
|
||||||
<location filename="../mainwindow.cpp" line="764"/>
|
<location filename="../mainwindow.cpp" line="764"/>
|
||||||
<source>Save As</source>
|
<source>Save As</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Lưu dưới dạng</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="710"/>
|
<location filename="../mainwindow.cpp" line="710"/>
|
||||||
<source>No image is currently open.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="722"/>
|
<location filename="../mainwindow.cpp" line="722"/>
|
||||||
<source>%1 Image (*.%2)</source>
|
<source>%1 Image (*.%2)</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Hình ảnh %1 (*.%2)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="728"/>
|
<location filename="../mainwindow.cpp" line="728"/>
|
||||||
<source>No supported image formats are available.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="765"/>
|
<location filename="../mainwindow.cpp" line="765"/>
|
||||||
<source>Failed to save image: %1</source>
|
<source>Failed to save image: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Không lưu được hình ảnh: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../mainwindow.cpp" line="854"/>
|
<location filename="../mainwindow.cpp" line="854"/>
|
||||||
@@ -309,7 +309,7 @@ Loc Huynh <https://github.com/hthienloc></translation>
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="88"/>
|
<location filename="../actionmanager.cpp" line="88"/>
|
||||||
<source>Save &As...</source>
|
<source>Save &As...</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Lưu &dưới dạng...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../actionmanager.cpp" line="93"/>
|
<location filename="../actionmanager.cpp" line="93"/>
|
||||||
|
|||||||
-158
@@ -1,158 +0,0 @@
|
|||||||
image:
|
|
||||||
- Visual Studio 2022
|
|
||||||
environment:
|
|
||||||
CMAKE_INSTALL_PREFIX: C:\projects\cmake
|
|
||||||
LIBZ: C:\projects\zlib
|
|
||||||
LIBEXPAT: C:\projects\libexpat
|
|
||||||
LIBAVIF: C:\projects\libavif
|
|
||||||
LIBEXIV2: C:\projects\exiv2
|
|
||||||
PPKG: C:\projects\ppkg
|
|
||||||
matrix:
|
|
||||||
- job_name: mingw_64_qt6_8
|
|
||||||
QTDIR: C:\Qt\6.8\mingw_64
|
|
||||||
MINGW64: C:\Qt\mingw_64\Tools\mingw1310_64
|
|
||||||
KF_BRANCH: master
|
|
||||||
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"
|
|
||||||
WINDEPLOYQT_ARGS: "--verbose=2 --no-quick-import --no-translations --no-opengl-sw --no-system-d3d-compiler --skip-plugin-types tls,networkinformation"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- mkdir %CMAKE_INSTALL_PREFIX%
|
|
||||||
- mkdir %LIBZ%
|
|
||||||
- mkdir %LIBEXPAT%
|
|
||||||
- mkdir %LIBAVIF%
|
|
||||||
- mkdir %LIBEXIV2%
|
|
||||||
- mkdir %PPKG%
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
- set PATH=%PATH%;%CMAKE_INSTALL_PREFIX%;%QTDIR%\bin;%MINGW64%\bin;%PPKG%
|
|
||||||
- set CC=%MINGW64%\bin\gcc.exe
|
|
||||||
- set CXX=%MINGW64%\bin\g++.exe
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
# prepare
|
|
||||||
- mkdir 3rdparty
|
|
||||||
- choco install ninja
|
|
||||||
- cd %PPKG%
|
|
||||||
- curl -fsSL -o ppkg.exe https://github.com/BLumia/pineapple-package-manager/releases/latest/download/ppkg.exe
|
|
||||||
- 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
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX%
|
|
||||||
- cmake --build . --config Release
|
|
||||||
- cmake --build . --config Release --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# install ECM so we can build KImageFormats
|
|
||||||
- cd 3rdparty
|
|
||||||
- git clone -b %KF_BRANCH% -q https://invent.kde.org/frameworks/extra-cmake-modules.git
|
|
||||||
- git rev-parse HEAD
|
|
||||||
- cd extra-cmake-modules
|
|
||||||
- cmake -G "Ninja" . -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX% -DBUILD_TESTING=OFF
|
|
||||||
- cmake --build .
|
|
||||||
- cmake --build . --target install
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# install AOM for libavif AV1 decoding support...
|
|
||||||
- cd 3rdparty
|
|
||||||
#- git clone -b v3.9.1 --depth 1 https://aomedia.googlesource.com/aom
|
|
||||||
#- cd aom
|
|
||||||
#- mkdir build.aom
|
|
||||||
#- cd build.aom
|
|
||||||
#- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX% -DENABLE_DOCS=OFF -DBUILD_SHARED_LIBS=ON -DAOM_TARGET_CPU=generic -DENABLE_TESTS=OFF -DENABLE_TESTDATA=OFF -DENABLE_TOOLS=OFF -DENABLE_EXAMPLES=0
|
|
||||||
#- cmake --build . --config Release
|
|
||||||
#- cmake --build . --config Release --target install/strip
|
|
||||||
- mkdir aom
|
|
||||||
- cd aom
|
|
||||||
- curl -fsSL -o ppkg-aom.zip https://sourceforge.net/projects/pineapple-package-manager/files/packages/mingw-w64-x86_64-windows/aom-3.9.1-2.zip
|
|
||||||
- ppkg ppkg-aom.zip
|
|
||||||
- 7z x ppkg-aom.zip LICENSE -y
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# install libavif for avif format support of KImageFormats
|
|
||||||
- cd %LIBAVIF%
|
|
||||||
- curl -fsSL -o libavif-v1_3_0.zip https://github.com/AOMediaCodec/libavif/archive/v1.3.0.zip
|
|
||||||
- 7z x libavif-v1_3_0.zip -y
|
|
||||||
- cd libavif-1.3.0
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX% -DAVIF_CODEC_AOM=SYSTEM -DAVIF_CODEC_AOM_DECODE=ON -DAVIF_CODEC_AOM_ENCODE=ON -DAVIF_LIBYUV=LOCAL
|
|
||||||
- cmake --build . --config Release
|
|
||||||
- cmake --build . --config Release --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# install KArchive for kra format support of KImageFormats
|
|
||||||
- cd 3rdparty
|
|
||||||
- git clone -b %KF_BRANCH% -q https://invent.kde.org/frameworks/karchive.git
|
|
||||||
- git rev-parse HEAD
|
|
||||||
- cd karchive
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -G "Ninja" -DBUILD_TESTING=OFF -DWITH_LIBZSTD=OFF -DWITH_BZIP2=OFF -DWITH_LIBLZMA=OFF -DWITH_OPENSSL=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX%
|
|
||||||
- cmake --build . --config Release
|
|
||||||
- cmake --build . --config Release --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# build libexpat for libexiv2
|
|
||||||
- cd %LIBEXPAT%
|
|
||||||
- curl -fsSL -o R_2_7_3.zip https://github.com/libexpat/libexpat/archive/R_2_7_3.zip
|
|
||||||
- 7z x R_2_7_3.zip -y
|
|
||||||
- cd libexpat-R_2_7_3/expat/
|
|
||||||
- cmake -G "Ninja" . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX% -DEXPAT_BUILD_EXAMPLES=OFF -DEXPAT_BUILD_TESTS=OFF -DEXPAT_BUILD_TOOLS=OFF
|
|
||||||
- cmake --build . --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# build libexiv2
|
|
||||||
- cd %LIBEXIV2%
|
|
||||||
- curl -fsSL -o v%EXIV2_VERSION%.zip https://github.com/Exiv2/exiv2/archive/v%EXIV2_VERSION%.zip
|
|
||||||
- 7z x v%EXIV2_VERSION%.zip -y
|
|
||||||
- cd exiv2-%EXIV2_VERSION%
|
|
||||||
- cmake -G "Ninja" . -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%CMAKE_INSTALL_PREFIX% %EXIV2_CMAKE_OPTIONS%
|
|
||||||
- cmake --build . --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# install KImageFormats
|
|
||||||
- cd 3rdparty
|
|
||||||
- git clone -b %KF_BRANCH% -q https://invent.kde.org/frameworks/kimageformats.git
|
|
||||||
- git rev-parse HEAD
|
|
||||||
- cd kimageformats
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -G "Ninja" -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_QTPLUGINDIR=%QTDIR%\plugins
|
|
||||||
- cmake --build . --config Release
|
|
||||||
- cmake --build . --config Release --target install/strip
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
# finally...
|
|
||||||
- mkdir build
|
|
||||||
- cd build
|
|
||||||
- cmake .. -G "Ninja" %PPIC_CMAKE_OPTIONS% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=%CMAKE_INSTALL_PREFIX% -DCMAKE_INSTALL_PREFIX='%cd%'
|
|
||||||
- cmake --build . --config Release
|
|
||||||
- cmake --build . --config Release --target install/strip
|
|
||||||
# fixme: I don't know how to NOT make the binary installed to the ./bin/ folder...
|
|
||||||
- cd bin
|
|
||||||
- copy %APPVEYOR_BUILD_FOLDER%\LICENSE
|
|
||||||
- copy %CMAKE_INSTALL_PREFIX%\bin\libaom.dll
|
|
||||||
- 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\libKF?Archive.dll
|
|
||||||
- windeployqt %WINDEPLOYQT_ARGS% .\ppic.exe
|
|
||||||
# copy 3rdparty licenses for the libs we vendored for windows...
|
|
||||||
- mkdir licenses
|
|
||||||
- cd licenses
|
|
||||||
- copy %APPVEYOR_BUILD_FOLDER%\3rdparty\aom\LICENSE License.aom.txt
|
|
||||||
- copy %APPVEYOR_BUILD_FOLDER%\3rdparty\karchive\LICENSES\LGPL-2.0-or-later.txt License.KArchive.txt
|
|
||||||
- copy %APPVEYOR_BUILD_FOLDER%\3rdparty\kimageformats\LICENSES\LGPL-2.1-or-later.txt License.kimageformats.txt
|
|
||||||
- copy %LIBEXPAT%\libexpat-R_2_7_3\expat\COPYING License.expat.txt
|
|
||||||
- copy %LIBAVIF%\libavif-1.3.0\LICENSE License.libavif.txt
|
|
||||||
- copy %LIBEXIV2%\exiv2-%EXIV2_VERSION%\COPYING License.exiv2.txt
|
|
||||||
# TODO: Qt, zlib
|
|
||||||
- cd ..
|
|
||||||
# for debug..
|
|
||||||
- tree /f
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
- xcopy %CMAKE_INSTALL_PREFIX% .\cmake-prefix-copy /E /H /C /I
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: build\bin
|
|
||||||
- path: cmake-prefix-copy
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<center>
|
||||||
|
<p>
|
||||||
|
<img width='128' height='128' src=':/icons/app-icon.svg'/>
|
||||||
|
<br/>
|
||||||
|
<!-- Application name -->
|
||||||
|
<span>%1</span>
|
||||||
|
<br/>
|
||||||
|
<!-- Version info -->
|
||||||
|
<span>%2</span>
|
||||||
|
</p>
|
||||||
|
<hr/>
|
||||||
|
<p>
|
||||||
|
<!-- Upstream author and fork author -->
|
||||||
|
<span>%3</span><span>: </span><a href='https://github.com/BLumia'>@BLumia</a>
|
||||||
|
<br/>
|
||||||
|
<span>%4</span><span>: </span><a href='https://github.com/yyc12345'>@yyc12345</a>
|
||||||
|
<br/>
|
||||||
|
<!-- Icon author -->
|
||||||
|
<span>%5</span><span>: </span><a href='https://github.com/Lovelyblack'>@Lovelyblack</a>
|
||||||
|
</p>
|
||||||
|
<hr/>
|
||||||
|
<p>
|
||||||
|
<!-- Built Qt version info -->
|
||||||
|
<span>%6</span>
|
||||||
|
<br/>
|
||||||
|
<!-- Source code -->
|
||||||
|
<span>%7</span><span>: </span><a href='https://github.com/BLumia/pineapple-pictures'>GitHub</a>
|
||||||
|
</p>
|
||||||
|
</center>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<!-- General help -->
|
||||||
|
<h1 align='center'>%1</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<span>%2</span>
|
||||||
|
<ul>
|
||||||
|
<li>%3</li>
|
||||||
|
<li>%4</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>%5</li>
|
||||||
|
<li>%6</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
<!-- Context menu option help -->
|
||||||
|
<h1 align='center'>%7</h1>
|
||||||
|
<!-- Context menu option list -->
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>%8</b><span>:</span>
|
||||||
|
<br/>
|
||||||
|
<span>%9</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!-- License -->
|
||||||
|
<h1 align='center'><b>%1</b></h1>
|
||||||
|
<p>%2</p>
|
||||||
|
<hr/>
|
||||||
|
<!-- VErbatime license -->
|
||||||
|
<h1 align='center'><b>%3</b></h1>
|
||||||
|
<pre>
|
||||||
|
Expat/MIT License
|
||||||
|
|
||||||
|
Copyright © 2026 BLumia
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
</pre>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<!-- Contributor title -->
|
||||||
|
<h1 align='center'>%1</h1>
|
||||||
|
<!-- Contributor list -->
|
||||||
|
<a href='https://github.com/BLumia/pineapple-pictures/graphs/contributors'>%2</a>
|
||||||
|
<!-- The message to thank these contributors -->
|
||||||
|
<p>%3</p>
|
||||||
|
|
||||||
|
<!-- Translator title -->
|
||||||
|
<h1 align='center'>%4</h1>
|
||||||
|
<!-- The message to thank these translators -->
|
||||||
|
<p>%5</p>
|
||||||
|
<ul>
|
||||||
|
<li><u>Catalan</u>: Toni Estévez</li>
|
||||||
|
<li><u>Chinese (Simplified)</u>: Percy Hong</li>
|
||||||
|
<li><u>Dutch</u>: Heimen Stoffels</li>
|
||||||
|
<li><u>French</u>: J. Lavoie, K. Herbert, Maxime Leroy</li>
|
||||||
|
<li><u>German</u>: K. Herbert, J. Lavoie, sal0max</li>
|
||||||
|
<li><u>Indonesian</u>: liimee, Reza Almanda</li>
|
||||||
|
<li><u>Italian</u>: albanobattistella</li>
|
||||||
|
<li><u>Japanese</u>: Black Cat, mmahhi, Percy Hong</li>
|
||||||
|
<li><u>Korean</u>: VenusGirl</li>
|
||||||
|
<li><u>Norwegian Bokmål</u>: Allan Nordhøy, ovl-1</li>
|
||||||
|
<li><u>Punjabi (Pakistan)</u>: bgo-eiu</li>
|
||||||
|
<li><u>Russian</u>: Sergey Shornikov, Artem, Andrey</li>
|
||||||
|
<li><u>Sinhala</u>: HelaBasa</li>
|
||||||
|
<li><u>Slovenian</u>: Andrej Poženel - SDT</li>
|
||||||
|
<li><u>Spanish</u>: Toni Estévez, Génesis Toxical, William(ѕ)ⁿ, gallegonovato</li>
|
||||||
|
<li><u>Tamil</u>: தமிழ்நேரம் (TamilNeram)</li>
|
||||||
|
<li><u>Turkish</u>: E-Akcaer, Oğuz Ersen, Sabri Ünal</li>
|
||||||
|
<li><u>Ukrainian</u>: Dan, volkov, Сергій</li>
|
||||||
|
<li><u>Vietnamese</u>: Loc Huynh</li>
|
||||||
|
</ul>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!-- Title -->
|
||||||
|
<h1 align='center'><b>%1</b></h1>
|
||||||
|
<!-- List intro message -->
|
||||||
|
<p>%2</p>
|
||||||
|
<p>%3</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href='https://www.qt.io/'>Qt</a><span>: </span>
|
||||||
|
<span>GPLv2 + GPLv3 + LGPLv2.1 + LGPLv3</span>
|
||||||
|
</li>
|
||||||
|
<li style="display: none;">
|
||||||
|
<a href='https://www.exiv2.org/'>Exiv2</a><span>: </span>
|
||||||
|
<span>GPLv2</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
@@ -10,6 +10,10 @@
|
|||||||
<file>icons/window-close.svg</file>
|
<file>icons/window-close.svg</file>
|
||||||
<file>icons/go-next.svg</file>
|
<file>icons/go-next.svg</file>
|
||||||
<file>icons/go-previous.svg</file>
|
<file>icons/go-previous.svg</file>
|
||||||
<file>plain/translators.html</file>
|
<file>plain/help.html</file>
|
||||||
|
<file>plain/about.html</file>
|
||||||
|
<file>plain/special-thanks.html</file>
|
||||||
|
<file>plain/license.html</file>
|
||||||
|
<file>plain/third-party-libs.html</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -4,18 +4,26 @@
|
|||||||
<name>Pineapple Pictures</name>
|
<name>Pineapple Pictures</name>
|
||||||
<name xml:lang="es">Pineapple Pictures</name>
|
<name xml:lang="es">Pineapple Pictures</name>
|
||||||
<name xml:lang="ja">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="nl">Pineapple Afbeeldingen</name>
|
||||||
<name xml:lang="ru">Pineapple Pictures</name>
|
<name xml:lang="ru">Pineapple Pictures</name>
|
||||||
|
<name xml:lang="sl">Pineapple Slike</name>
|
||||||
<name xml:lang="ta">அன்னாசி படங்கள்</name>
|
<name xml:lang="ta">அன்னாசி படங்கள்</name>
|
||||||
|
<name xml:lang="tr">Pineapple Resimler</name>
|
||||||
<name xml:lang="uk">Pineapple Pictures</name>
|
<name xml:lang="uk">Pineapple Pictures</name>
|
||||||
|
<name xml:lang="vi">Pineapple Pictures</name>
|
||||||
<name xml:lang="zh-CN">菠萝看图</name>
|
<name xml:lang="zh-CN">菠萝看图</name>
|
||||||
<summary>Image Viewer</summary>
|
<summary>Image Viewer</summary>
|
||||||
<summary xml:lang="es">Visor de imágenes</summary>
|
<summary xml:lang="es">Visor de imágenes</summary>
|
||||||
<summary xml:lang="ja">画像ビューアー</summary>
|
<summary xml:lang="ja">画像ビューアー</summary>
|
||||||
|
<summary xml:lang="ko">이미지 뷰어</summary>
|
||||||
<summary xml:lang="nl">Afbeeldingsweergave</summary>
|
<summary xml:lang="nl">Afbeeldingsweergave</summary>
|
||||||
<summary xml:lang="ru">Просмотр изображений</summary>
|
<summary xml:lang="ru">Просмотр изображений</summary>
|
||||||
|
<summary xml:lang="sl">Pregledovalnik slik</summary>
|
||||||
<summary xml:lang="ta">பட பார்வையாளர்</summary>
|
<summary xml:lang="ta">பட பார்வையாளர்</summary>
|
||||||
|
<summary xml:lang="tr">Resim Görüntüleyici</summary>
|
||||||
<summary xml:lang="uk">Переглядач зображень</summary>
|
<summary xml:lang="uk">Переглядач зображень</summary>
|
||||||
|
<summary xml:lang="vi">Trình xem ảnh</summary>
|
||||||
<summary xml:lang="zh-CN">图像查看器</summary>
|
<summary xml:lang="zh-CN">图像查看器</summary>
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<project_license>MIT</project_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>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="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="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="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="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="ta">அன்னாசி படங்கள் ஒரு இலகுரக மற்றும் பயன்படுத்த எளிதான பட பார்வையாளராகும், இது பெரிதாக்கும்போது ஒரு எளிமையான வழிசெலுத்தல் சிறுபடத்துடன் வருகிறது, மேலும் எந்த பட மேலாண்மை ஆதரவையும் கொண்டிருக்கவில்லை.</p>
|
||||||
<p xml:lang="uk">Pineapple Pictures – це легкий і простий у використанні переглядач зображень, який постачається зі зручною навігаційною мініатюрою при збільшенні масштабу і не містить жодної підтримки керування зображеннями.</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>
|
<p xml:lang="zh-CN">菠萝看图是一个轻量级易用的图像查看器,在图片放大时提供了方便的鸟瞰导航功能,且不包含任何图片管理功能。</p>
|
||||||
</description>
|
</description>
|
||||||
<developer id="net.blumia">
|
<developer id="net.blumia">
|
||||||
@@ -42,6 +53,7 @@
|
|||||||
<url type="bugtracker">https://github.com/BLumia/pineapple-pictures/issues</url>
|
<url type="bugtracker">https://github.com/BLumia/pineapple-pictures/issues</url>
|
||||||
<url type="donation">https://ko-fi.com/blumia</url>
|
<url type="donation">https://ko-fi.com/blumia</url>
|
||||||
<url type="translate">https://hosted.weblate.org/projects/pineapple-pictures/</url>
|
<url type="translate">https://hosted.weblate.org/projects/pineapple-pictures/</url>
|
||||||
|
<url type="vcs-browser">https://github.com/BLumia/pineapple-pictures</url>
|
||||||
<provides>
|
<provides>
|
||||||
<binary>ppic</binary>
|
<binary>ppic</binary>
|
||||||
</provides>
|
</provides>
|
||||||
@@ -50,10 +62,13 @@
|
|||||||
<caption>Main window when an image file is loaded</caption>
|
<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="es">Ventana principal cuando se carga un archivo de imagen</caption>
|
||||||
<caption xml:lang="ja">画像ファイル読み込み時のメインウィンドウ</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="nl">Hoofdvenster na het laden van een afbeelding</caption>
|
||||||
<caption xml:lang="ru">Основное окно после загрузки файла изображения</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="ta">ஒரு படக் கோப்பு ஏற்றப்படும் போது முதன்மையான சாளரம்</caption>
|
||||||
<caption xml:lang="uk">Головне вікно після завантаження файлу зображення</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>
|
<caption xml:lang="zh-CN">加载图片后的主窗口</caption>
|
||||||
<image type="source" width="1503" height="640">https://pineapple-pictures.sourceforge.io/ppic-gui-static.png</image>
|
<image type="source" width="1503" height="640">https://pineapple-pictures.sourceforge.io/ppic-gui-static.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
@@ -61,10 +76,13 @@
|
|||||||
<caption>Zooming in a raster image</caption>
|
<caption>Zooming in a raster image</caption>
|
||||||
<caption xml:lang="es">Ampliar una imagen rasterizada</caption>
|
<caption xml:lang="es">Ampliar una imagen rasterizada</caption>
|
||||||
<caption xml:lang="ja">ラスター画像の拡大</caption>
|
<caption xml:lang="ja">ラスター画像の拡大</caption>
|
||||||
|
<caption xml:lang="ko">래스터 이미지 확대하기</caption>
|
||||||
<caption xml:lang="nl">Inzoomen op een roosterafbeelding</caption>
|
<caption xml:lang="nl">Inzoomen op een roosterafbeelding</caption>
|
||||||
<caption xml:lang="ru">Масштабирование растрового изображения</caption>
|
<caption xml:lang="ru">Масштабирование растрового изображения</caption>
|
||||||
|
<caption xml:lang="sl">Povečevanje rastrske slike</caption>
|
||||||
<caption xml:lang="ta">ராச்டர் படத்தில் பெரிதாக்குதல்</caption>
|
<caption xml:lang="ta">ராச்டர் படத்தில் பெரிதாக்குதல்</caption>
|
||||||
<caption xml:lang="uk">Масштабування растрового зображення</caption>
|
<caption xml:lang="uk">Масштабування растрового зображення</caption>
|
||||||
|
<caption xml:lang="vi">Phóng to hình ảnh raster</caption>
|
||||||
<caption xml:lang="zh-CN">放大浏览位图</caption>
|
<caption xml:lang="zh-CN">放大浏览位图</caption>
|
||||||
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-raster.png</image>
|
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-raster.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
@@ -72,15 +90,49 @@
|
|||||||
<caption>Zooming in a vector image</caption>
|
<caption>Zooming in a vector image</caption>
|
||||||
<caption xml:lang="es">Ampliar una imagen vectorial</caption>
|
<caption xml:lang="es">Ampliar una imagen vectorial</caption>
|
||||||
<caption xml:lang="ja">ベクター画像の拡大</caption>
|
<caption xml:lang="ja">ベクター画像の拡大</caption>
|
||||||
|
<caption xml:lang="ko">벡터 이미지 확대하기</caption>
|
||||||
<caption xml:lang="nl">Inzoomen op een vectorafbeelding</caption>
|
<caption xml:lang="nl">Inzoomen op een vectorafbeelding</caption>
|
||||||
<caption xml:lang="ru">Масштабирование векторного изображения</caption>
|
<caption xml:lang="ru">Масштабирование векторного изображения</caption>
|
||||||
|
<caption xml:lang="sl">Povečevanje vektorske slike</caption>
|
||||||
<caption xml:lang="ta">ஒரு திசையன் படத்தில் பெரிதாக்குதல்</caption>
|
<caption xml:lang="ta">ஒரு திசையன் படத்தில் பெரிதாக்குதல்</caption>
|
||||||
<caption xml:lang="uk">Масштабування векторного зображення</caption>
|
<caption xml:lang="uk">Масштабування векторного зображення</caption>
|
||||||
|
<caption xml:lang="vi">Phóng to hình ảnh vector</caption>
|
||||||
<caption xml:lang="zh-CN">放大浏览矢量图</caption>
|
<caption xml:lang="zh-CN">放大浏览矢量图</caption>
|
||||||
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-svg.png</image>
|
<image type="source" width="771" height="553">https://pineapple-pictures.sourceforge.io/ppic-zoom-svg.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release type="stable" version="1.5.0" date="2026-07-04T00:00:00Z">
|
||||||
|
<description>
|
||||||
|
<p>This release adds the following features:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Introduced an optional titlebar that display file name and allows drag and double-click to move and toggle maximize state</li>
|
||||||
|
<li>Windows: Now the application will use the system's built-in animation for open window animation</li>
|
||||||
|
</ul>
|
||||||
|
<p>This release fixes the following bug:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Show empty url list should also reset window title to blank</li>
|
||||||
|
</ul>
|
||||||
|
<p>This release includes the following changes:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Update translations</li>
|
||||||
|
<li>Add vcs-browser link to AppStream metadata</li>
|
||||||
|
</ul>
|
||||||
|
<p>With contributions from:</p>
|
||||||
|
<p>Heimen Stoffels (vistaus), VenusGirl (VenusGirl)</p>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<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">
|
<release type="stable" version="1.4.0" date="2026-02-11T00:00:00Z">
|
||||||
<description>
|
<description>
|
||||||
<p>This release adds the following features:</p>
|
<p>This release adds the following features:</p>
|
||||||
|
|||||||
Vendored
-118
@@ -1,118 +0,0 @@
|
|||||||
targetApp=ppic.exe
|
|
||||||
friendlyAppName=Pineapple Pictures
|
|
||||||
friendlyAppName[zh_CN]=菠萝看图
|
|
||||||
openCommand="\"{targetAppFullPath}\" \"%1\""
|
|
||||||
|
|
||||||
[ProgId/jpeg]
|
|
||||||
name=JPEG Image
|
|
||||||
name[zh_CN]=JPEG 图像
|
|
||||||
extensions=jpg,jpeg,jfif
|
|
||||||
|
|
||||||
[ProgId/gif]
|
|
||||||
name=GIF Animation Image
|
|
||||||
name[zh_CN]=GIF 动图
|
|
||||||
|
|
||||||
[ProgId/png]
|
|
||||||
name=PNG Image
|
|
||||||
name[zh_CN]=PNG 图像
|
|
||||||
|
|
||||||
[ProgId/svg]
|
|
||||||
name=Scalable Vector Graphics
|
|
||||||
name[zh_CN]=可缩放矢量图像
|
|
||||||
|
|
||||||
[ProgId/webp]
|
|
||||||
name=WebP Image
|
|
||||||
name[zh_CN]=WebP 图像
|
|
||||||
extensions=webp
|
|
||||||
|
|
||||||
[ProgId/avif]
|
|
||||||
name=AV1 Image File
|
|
||||||
name[zh_CN]=AV1 图像文件
|
|
||||||
|
|
||||||
[ProgId/ico]
|
|
||||||
name=Windows Icon Image
|
|
||||||
name[zh_CN]=Windows 图标图像
|
|
||||||
|
|
||||||
[ProgId/icns]
|
|
||||||
name=Apple Icon Image
|
|
||||||
name[zh_CN]=苹果图标图像
|
|
||||||
|
|
||||||
[ProgId/psd]
|
|
||||||
name=Photoshop Document
|
|
||||||
name[zh_CN]=Photoshop 文档
|
|
||||||
extensions=psd,psb,pdd,psdt
|
|
||||||
|
|
||||||
[ProgId/kra]
|
|
||||||
name=Krita Document
|
|
||||||
name[zh_CN]=Krita 文档
|
|
||||||
|
|
||||||
[ProgId/xcf]
|
|
||||||
name=GIMP Document
|
|
||||||
name[zh_CN]=GIMP 文档
|
|
||||||
|
|
||||||
[ProgId/tga]
|
|
||||||
name=TARGA Image
|
|
||||||
name[zh_CN]=TARGA 图像
|
|
||||||
|
|
||||||
[ProgId/tif]
|
|
||||||
name=Tagged Image File Format
|
|
||||||
name[zh_CN]=标签图像文件格式
|
|
||||||
extensions=tif,tiff
|
|
||||||
|
|
||||||
[ProgId/bmp]
|
|
||||||
name=Bitmap
|
|
||||||
name[zh_CN]=位图
|
|
||||||
|
|
||||||
[ProgId/wbmp]
|
|
||||||
name=Wireless Bitmap
|
|
||||||
name[zh_CN]=无线位图
|
|
||||||
|
|
||||||
[ProgId/dds]
|
|
||||||
name=DirectDraw Surface
|
|
||||||
|
|
||||||
[ProgId/hdr]
|
|
||||||
name=High Dynamic Range Image
|
|
||||||
name[zh_CN]=高动态范围图像
|
|
||||||
|
|
||||||
[ProgId/iff]
|
|
||||||
name=Interchange File Format
|
|
||||||
name[zh_CN]=可交换文件格式
|
|
||||||
|
|
||||||
[ProgId/pic]
|
|
||||||
name=Softimage Picture
|
|
||||||
name[zh_CN]=Softimage 图像
|
|
||||||
|
|
||||||
[ProgId/pcx]
|
|
||||||
name=PiCture eXchange PC Paintbrush Image
|
|
||||||
name[zh_CN]=PC Paintbrush 图像
|
|
||||||
|
|
||||||
[ProgId/qoi]
|
|
||||||
name=Quite OK Image
|
|
||||||
name[zh_CN]=挺 OK 图像
|
|
||||||
|
|
||||||
[ProgId/ras]
|
|
||||||
name=Sun Raster Image
|
|
||||||
name[zh_CN]=Sun 栅格图象
|
|
||||||
extensions=ras,sun
|
|
||||||
|
|
||||||
[ProgId/ora]
|
|
||||||
name=OpenRaster Image
|
|
||||||
name[zh_CN]=OpenRaster 图象
|
|
||||||
|
|
||||||
[ProgId/ani]
|
|
||||||
name=Animated Cursor
|
|
||||||
name[zh_CN]=动态光标
|
|
||||||
|
|
||||||
[ProgId/pfm]
|
|
||||||
name=Portable Float Map
|
|
||||||
extensions=pfm,phm
|
|
||||||
|
|
||||||
[ProgId/rgb]
|
|
||||||
name=Silicon Graphics Image
|
|
||||||
extensions=rgb,rgba,bw,sgi
|
|
||||||
|
|
||||||
[ProgId/pxr]
|
|
||||||
name=Pixar Raster Image
|
|
||||||
|
|
||||||
[ProgId/sct]
|
|
||||||
name=Scitex Continuous Tone
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2025 Gary Wang <git@blumia.net>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
QT += core widgets gui svg svgwidgets
|
|
||||||
|
|
||||||
TARGET = ppic
|
|
||||||
TEMPLATE = app
|
|
||||||
DEFINES += PPIC_VERSION_STRING=\\\"x.y.z\\\"
|
|
||||||
|
|
||||||
win32 {
|
|
||||||
DEFINES += FLAG_PORTABLE_MODE_SUPPORT=1
|
|
||||||
}
|
|
||||||
|
|
||||||
# The following define makes your compiler emit warnings if you use
|
|
||||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
|
||||||
# depend on your compiler). Please consult the documentation of the
|
|
||||||
# deprecated API in order to know how to port your code away from it.
|
|
||||||
DEFINES += QT_DEPRECATED_WARNINGS
|
|
||||||
|
|
||||||
# You can also make your code fail to compile if you use deprecated APIs.
|
|
||||||
# In order to do so, uncomment the following line.
|
|
||||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
|
||||||
|
|
||||||
CONFIG += c++17 lrelease embed_translations
|
|
||||||
|
|
||||||
SOURCES += \
|
|
||||||
app/aboutdialog.cpp \
|
|
||||||
app/main.cpp \
|
|
||||||
app/framelesswindow.cpp \
|
|
||||||
app/mainwindow.cpp \
|
|
||||||
app/graphicsview.cpp \
|
|
||||||
app/bottombuttongroup.cpp \
|
|
||||||
app/graphicsscene.cpp \
|
|
||||||
app/navigatorview.cpp \
|
|
||||||
app/opacityhelper.cpp \
|
|
||||||
app/toolbutton.cpp \
|
|
||||||
app/settings.cpp \
|
|
||||||
app/settingsdialog.cpp \
|
|
||||||
app/metadatamodel.cpp \
|
|
||||||
app/metadatadialog.cpp \
|
|
||||||
app/exiv2wrapper.cpp \
|
|
||||||
app/actionmanager.cpp \
|
|
||||||
app/playlistmanager.cpp \
|
|
||||||
app/shortcutedit.cpp \
|
|
||||||
app/fileopeneventhandler.cpp
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
app/aboutdialog.h \
|
|
||||||
app/framelesswindow.h \
|
|
||||||
app/mainwindow.h \
|
|
||||||
app/graphicsview.h \
|
|
||||||
app/bottombuttongroup.h \
|
|
||||||
app/graphicsscene.h \
|
|
||||||
app/navigatorview.h \
|
|
||||||
app/opacityhelper.h \
|
|
||||||
app/toolbutton.h \
|
|
||||||
app/settings.h \
|
|
||||||
app/settingsdialog.h \
|
|
||||||
app/metadatamodel.h \
|
|
||||||
app/metadatadialog.h \
|
|
||||||
app/exiv2wrapper.h \
|
|
||||||
app/actionmanager.h \
|
|
||||||
app/playlistmanager.h \
|
|
||||||
app/shortcutedit.h \
|
|
||||||
app/fileopeneventhandler.h
|
|
||||||
|
|
||||||
TRANSLATIONS = \
|
|
||||||
app/translations/PineapplePictures_en.ts \
|
|
||||||
app/translations/PineapplePictures_zh_CN.ts \
|
|
||||||
app/translations/PineapplePictures_de.ts \
|
|
||||||
app/translations/PineapplePictures_es.ts \
|
|
||||||
app/translations/PineapplePictures_fr.ts \
|
|
||||||
app/translations/PineapplePictures_nb_NO.ts \
|
|
||||||
app/translations/PineapplePictures_nl.ts \
|
|
||||||
app/translations/PineapplePictures_ru.ts \
|
|
||||||
app/translations/PineapplePictures_si.ts \
|
|
||||||
app/translations/PineapplePictures_id.ts
|
|
||||||
|
|
||||||
# Default rules for deployment.
|
|
||||||
qnx: target.path = /tmp/$${TARGET}/bin
|
|
||||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
|
||||||
!isEmpty(target.path): INSTALLS += target
|
|
||||||
|
|
||||||
RESOURCES += \
|
|
||||||
assets/resources.qrc
|
|
||||||
|
|
||||||
# Generate from svg:
|
|
||||||
# magick -density 512x512 -background none app-icon.svg -define icon:auto-resize app-icon.ico
|
|
||||||
RC_ICONS = assets/icons/app-icon.ico
|
|
||||||
|
|
||||||
# Windows only, for rc file (we're not going to use the .rc file in this repo)
|
|
||||||
QMAKE_TARGET_PRODUCT = Pineapple Pictures
|
|
||||||
QMAKE_TARGET_DESCRIPTION = Pineapple Pictures - Image Viewer
|
|
||||||
QMAKE_TARGET_COPYRIGHT = MIT/Expat License - Copyright (C) 2024 Gary Wang
|
|
||||||
|
|
||||||
# MSVC only, since QMake doesn't have a CMAKE_CXX_STANDARD_LIBRARIES or cpp_winlibs similar thing
|
|
||||||
win32-msvc* {
|
|
||||||
LIBS += -luser32
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user