1
0

feat: update action manager

- add and delete some actions in action manager.
- rename "fit to view" to "fit to screen".
- update main window right click menu and toolbar.
- use special state update slot in action manager instead of function located in main window.
This commit is contained in:
2026-07-12 21:00:11 +08:00
parent 369a17ed7d
commit dbf8c09368
8 changed files with 388 additions and 324 deletions

View File

@@ -4,7 +4,11 @@
#include "actionmanager.h"
#include "settings.h"
#include "mainwindow.h"
#include "playlistmanager.h"
#include "graphicsview.h"
#include "graphicsscene.h"
#include <QGuiApplication>
#include <QSvgRenderer>
@@ -25,6 +29,8 @@ void ActionManager::setupAction(MainWindow *mainWindow)
{
// region: Action and Its Icon Creation
// Declare some convenient macros and lambdas.
#define ICON_NAME(name)\
QStringLiteral(":/icons/" #name ".svg")
@@ -39,40 +45,51 @@ void ActionManager::setupAction(MainWindow *mainWindow)
w->addAction(*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, actionToggleMaximize, view-fullscreen);
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomIn, zoom-in);
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomOut, zoom-out);
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleCheckerboard, view-background-checkerboard);
CREATE_NEW_ICON_ACTION(mainWindow, actionRotateClockwise, object-rotate-right);
/// This macro for register normal action.
#define CREATE_NEW_ACTION(w, a) create_action(w, &a, QString(), ACTION_NAME(a))
/// This macro for register Qt theme action.
#define CREATE_NEW_THEMEICON_ACTION(w, a, i) create_action(w, &a, QLatin1String(STRIFY(i)), ACTION_NAME(a), true)
/// This macro for register action with our custom icon.
#define CREATE_NEW_ICON_ACTION(w, a, i) create_action(w, &a, ICON_NAME(i), ACTION_NAME(a), false)
// TODO: We may replace all Qt theme icons to our custom icon for visual consistency.
// TODO: Some icons are invalid.
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionCopyPixmap, edit-copy);
CREATE_NEW_ACTION(mainWindow, actionCopyFilePath);
CREATE_NEW_ICON_ACTION(mainWindow, actionPrevPicture, go-previous);
CREATE_NEW_ICON_ACTION(mainWindow, actionNextPicture, go-next);
#undef CREATE_NEW_ICON_ACTION
#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)
CREATE_NEW_ACTION(mainWindow, actionRotateCounterClockwise);
CREATE_NEW_ICON_ACTION(mainWindow, actionActualSize, zoom-original);
CREATE_NEW_ICON_ACTION(mainWindow, actionFitToScreen, app-icon);
CREATE_NEW_ICON_ACTION(mainWindow, actionFitByWidth, app-icon);
CREATE_NEW_ICON_ACTION(mainWindow, actionFitByHeight, app-icon);
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomIn, zoom-in);
CREATE_NEW_ICON_ACTION(mainWindow, actionZoomOut, zoom-out);
CREATE_NEW_ICON_ACTION(mainWindow, actionFlipHorizontal, app-icon);
CREATE_NEW_ICON_ACTION(mainWindow, actionFlipVertical, app-icon);
CREATE_NEW_ICON_ACTION(mainWindow, actionRotateClockwise, object-rotate-right);
CREATE_NEW_ICON_ACTION(mainWindow, actionRotateCounterClockwise, app-icon);
CREATE_NEW_ACTION(mainWindow, actionToggleAvoidResetTransform);
CREATE_NEW_ICON_ACTION(mainWindow, actionToggleCheckerboard, view-background-checkerboard);
CREATE_NEW_ACTION(mainWindow, actionTogglePauseAnimation);
CREATE_NEW_ACTION(mainWindow, actionAnimationNextFrame);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionOpen, document-open);
CREATE_NEW_ACTION(mainWindow, actionHorizontalFlip);
CREATE_NEW_ACTION(mainWindow, actionFitInView);
CREATE_NEW_ACTION(mainWindow, actionFitByWidth);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionCopyPixmap, edit-copy);
CREATE_NEW_ACTION(mainWindow, actionCopyFilePath);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionPaste, edit-paste);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionTrash, edit-delete);
CREATE_NEW_ACTION(mainWindow, actionToggleAvoidResetTransform);
CREATE_NEW_ACTION(mainWindow, actionSettings);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionHelp, system-help);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionLocateInFileManager, system-file-manager);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionProperties, document-properties);
CREATE_NEW_ACTION(mainWindow, actionQuitApp);
#undef CREATE_NEW_ACTION
CREATE_NEW_ACTION(mainWindow, actionSettings);
CREATE_NEW_THEMEICON_ACTION(mainWindow, actionHelp, system-help);
// Remove convenient macros
#undef CREATE_NEW_ICON_ACTION
#undef CREATE_NEW_THEMEICON_ACTION
#undef CREATE_NEW_ACTION
#undef STRIFY
#undef ACTION_NAME
@@ -80,34 +97,40 @@ void ActionManager::setupAction(MainWindow *mainWindow)
// endregion
// region: Setup Checkable Action
actionToggleAvoidResetTransform->setCheckable(true);
actionToggleCheckerboard->setCheckable(true);
actionTogglePauseAnimation->setCheckable(true);
// endregion
// region: Action Translatable Text Setup
actionOpen->setText(QCoreApplication::translate("MainWindow", "&Open...", nullptr));
actionActualSize->setText(QCoreApplication::translate("MainWindow", "Actual size", nullptr));
actionToggleMaximize->setText(QCoreApplication::translate("MainWindow", "Toggle maximize", nullptr));
actionZoomIn->setText(QCoreApplication::translate("MainWindow", "Zoom in", nullptr));
actionZoomOut->setText(QCoreApplication::translate("MainWindow", "Zoom out", nullptr));
actionToggleCheckerboard->setText(QCoreApplication::translate("MainWindow", "Toggle Checkerboard", nullptr));
actionRotateClockwise->setText(QCoreApplication::translate("MainWindow", "Rotate right", nullptr));
actionRotateCounterClockwise->setText(QCoreApplication::translate("MainWindow", "Rotate left", nullptr));
actionCopyPixmap->setText(QCoreApplication::translate("MainWindow", "Copy P&ixmap", nullptr));
actionCopyFilePath->setText(QCoreApplication::translate("MainWindow", "Copy &File Path", nullptr));
actionPrevPicture->setText(QCoreApplication::translate("MainWindow", "Previous image", nullptr));
actionNextPicture->setText(QCoreApplication::translate("MainWindow", "Next image", nullptr));
actionActualSize->setText(QCoreApplication::translate("MainWindow", "Actual size", nullptr));
actionFitToScreen->setText(QCoreApplication::translate("MainWindow", "Fit to screen", nullptr));
actionFitByWidth->setText(QCoreApplication::translate("MainWindow", "Fit by width", nullptr));
actionFitByHeight->setText(QCoreApplication::translate("MainWindow", "Fit by height", nullptr));
actionZoomIn->setText(QCoreApplication::translate("MainWindow", "Zoom in", nullptr));
actionZoomOut->setText(QCoreApplication::translate("MainWindow", "Zoom out", nullptr));
actionFlipHorizontal->setText(QCoreApplication::translate("MainWindow", "Flip &Horizontally", nullptr));
actionFlipVertical->setText(QCoreApplication::translate("MainWindow", "Flip &Vertically", nullptr));
actionRotateClockwise->setText(QCoreApplication::translate("MainWindow", "Rotate right", nullptr));
actionRotateCounterClockwise->setText(QCoreApplication::translate("MainWindow", "Rotate left", nullptr));
actionToggleAvoidResetTransform->setText(QCoreApplication::translate("MainWindow", "Keep transformation", "The 'transformation' means the flip/rotation status that currently applied to the image view"));
actionToggleCheckerboard->setText(QCoreApplication::translate("MainWindow", "Toggle Checkerboard", nullptr));
actionTogglePauseAnimation->setText(QCoreApplication::translate("MainWindow", "Pause/Resume Animation", nullptr));
actionAnimationNextFrame->setText(QCoreApplication::translate("MainWindow", "Animation Go to Next Frame", nullptr));
actionHorizontalFlip->setText(QCoreApplication::translate("MainWindow", "Flip &Horizontally", nullptr));
actionFitInView->setText(QCoreApplication::translate("MainWindow", "Fit to view", nullptr));
actionFitByWidth->setText(QCoreApplication::translate("MainWindow", "Fit to width", nullptr));
actionCopyPixmap->setText(QCoreApplication::translate("MainWindow", "Copy P&ixmap", nullptr));
actionCopyFilePath->setText(QCoreApplication::translate("MainWindow", "Copy &File Path", nullptr));
actionPaste->setText(QCoreApplication::translate("MainWindow", "&Paste", nullptr));
actionTrash->setText(QCoreApplication::translate("MainWindow", "Move to Trash", nullptr));
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));
actionHelp->setText(QCoreApplication::translate("MainWindow", "Help", nullptr));
#ifdef Q_OS_WIN
actionLocateInFileManager->setText(
QCoreApplication::translate(
@@ -119,8 +142,9 @@ void ActionManager::setupAction(MainWindow *mainWindow)
actionLocateInFileManager->setText(QCoreApplication::translate("MainWindow", "Show in directory", nullptr));
#endif // Q_OS_WIN
actionProperties->setText(QCoreApplication::translate("MainWindow", "Properties", nullptr));
actionQuitApp->setText(QCoreApplication::translate("MainWindow", "Quit", nullptr));
actionSettings->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr));
actionHelp->setText(QCoreApplication::translate("MainWindow", "Help", nullptr));
// endregion
QMetaObject::connectSlotsByName(mainWindow);
@@ -128,34 +152,57 @@ void ActionManager::setupAction(MainWindow *mainWindow)
void ActionManager::setupShortcuts()
{
actionOpen->setShortcut(QKeySequence::Open);
actionCopyPixmap->setShortcut(QKeySequence::Copy);
actionPrevPicture->setShortcuts({
QKeySequence(Qt::Key_Left),
QKeySequence(Qt::Key_PageUp),
});
actionNextPicture->setShortcuts({
QKeySequence(Qt::Key_Right),
QKeySequence(Qt::Key_PageDown),
});
actionActualSize->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_0));
actionZoomIn->setShortcut(QKeySequence::ZoomIn);
actionZoomOut->setShortcut(QKeySequence::ZoomOut);
actionPrevPicture->setShortcuts({
QKeySequence(Qt::Key_PageUp),
QKeySequence(Qt::Key_Left),
});
actionNextPicture->setShortcuts({
QKeySequence(Qt::Key_PageDown),
QKeySequence(Qt::Key_Right),
});
actionHorizontalFlip->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
actionCopyPixmap->setShortcut(QKeySequence::Copy);
actionPaste->setShortcut(QKeySequence::Paste);
actionTrash->setShortcut(QKeySequence::Delete);
actionHelp->setShortcut(QKeySequence::HelpContents);
actionSettings->setShortcut(QKeySequence::Preferences);
actionProperties->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_I));
actionQuitApp->setShortcuts({
QKeySequence(Qt::Key_Space),
QKeySequence(Qt::Key_Escape)
});
actionSettings->setShortcut(QKeySequence::Preferences);
actionHelp->setShortcut(QKeySequence::HelpContents);
}
void ActionManager::enablePrevNextPictureAction(bool enablePrevPictureAction, bool enableNextPictureAction)
void ActionManager::updateActionState(PlaylistManager* pm, GraphicsView* gv)
{
actionPrevPicture->setEnabled(enablePrevPictureAction);
actionNextPicture->setEnabled(enableNextPictureAction);
const auto* gs = gv->scene();
const auto* settings = Settings::instance();
// Update Prev/Next Picture State
const int galleryFileCount = pm->totalCount();
const bool loopGallery = settings->loopGallery();
actionPrevPicture->setEnabled(galleryFileCount > 1 && (loopGallery || !pm->isFirstIndex()));
actionNextPicture->setEnabled(galleryFileCount > 1 && (loopGallery || !pm->isLastIndex()));
// Update Avoid Reset Transform
actionToggleAvoidResetTransform->setCheckable(gv->avoidResetTransform());
// Update Checkerboard
actionToggleCheckerboard->setChecked(gv->checkerboard());
// Update Animation Graphics Action
bool canPauseAnimation = gs->canPauseAnimation();
actionTogglePauseAnimation->setEnabled(canPauseAnimation);
if (canPauseAnimation) {
actionTogglePauseAnimation->setChecked(gs->isPauseAnimation());
} else {
actionTogglePauseAnimation->setChecked(false);
}
bool canSkipAnimation = gs->canSkipAnimationFrame();
actionAnimationNextFrame->setEnabled(canSkipAnimation);
// TODO: Add more...
}

View File

@@ -8,6 +8,8 @@
#include <QAction>
class MainWindow;
class PlaylistManager;
class GraphicsView;
/// Action Manager is separated from Main Window
/// to manage all actions specifically.
@@ -19,38 +21,40 @@ public:
void setupAction(MainWindow * mainWindow);
void setupShortcuts();
void enablePrevNextPictureAction(bool enablePrevPictureAction, bool enableNextPictureAction);
public slots:
void updateActionState(PlaylistManager* pm, GraphicsView* gv);
public:
QAction *actionOpen;
QAction *actionActualSize;
QAction *actionToggleMaximize;
QAction *actionZoomIn;
QAction *actionZoomOut;
QAction *actionToggleCheckerboard;
QAction *actionRotateClockwise;
QAction *actionRotateCounterClockwise;
QAction *actionCopyPixmap;
QAction *actionCopyFilePath;
QAction *actionPrevPicture;
QAction *actionNextPicture;
QAction *actionActualSize;
QAction *actionFitToScreen;
QAction *actionFitByWidth;
QAction *actionFitByHeight;
QAction *actionZoomIn;
QAction *actionZoomOut;
QAction *actionFlipHorizontal;
QAction *actionFlipVertical;
QAction *actionRotateClockwise;
QAction *actionRotateCounterClockwise;
QAction *actionToggleAvoidResetTransform;
QAction *actionToggleCheckerboard;
QAction *actionTogglePauseAnimation;
QAction *actionAnimationNextFrame;
QAction *actionHorizontalFlip;
QAction *actionFitInView;
QAction *actionFitByWidth;
QAction *actionCopyPixmap;
QAction *actionCopyFilePath;
QAction *actionPaste;
QAction *actionTrash;
QAction *actionToggleAvoidResetTransform;
QAction *actionSettings;
QAction *actionHelp;
QAction *actionLocateInFileManager;
QAction *actionProperties;
QAction *actionQuitApp;
QAction *actionSettings;
QAction *actionHelp;
};
#endif // ACTIONMANAGER_H

View File

@@ -200,27 +200,57 @@ bool GraphicsScene::trySetTransformationMode(Qt::TransformationMode mode, float
return false;
}
/// Fetch graphics item as our custom animated item.
/// Return nullptr means this item is not animated item.
static PGraphicsMovieItem* fetchAnimatedItem(QGraphicsItem *item) {
return qgraphicsitem_cast<PGraphicsMovieItem *>(item);
}
bool GraphicsScene::canPauseAnimation() const {
return fetchAnimatedItem(m_theThing) != nullptr;
}
bool GraphicsScene::isPauseAnimation() const {
auto* animatedItem = fetchAnimatedItem(m_theThing);
if (animatedItem != nullptr) {
return animatedItem->movie()->state() == QMovie::Paused;
} else {
return false;
}
}
bool GraphicsScene::togglePauseAnimation()
{
PGraphicsMovieItem * animatedItem = qgraphicsitem_cast<PGraphicsMovieItem *>(m_theThing);
if (animatedItem) {
auto* animatedItem = fetchAnimatedItem(m_theThing);
if (animatedItem != nullptr) {
animatedItem->movie()->setPaused(animatedItem->movie()->state() != QMovie::Paused);
return true;
} else {
return false;
}
return false;
}
bool GraphicsScene::canSkipAnimationFrame() const {
return isPauseAnimation();
}
bool GraphicsScene::skipAnimationFrame(int delta)
{
PGraphicsMovieItem * animatedItem = qgraphicsitem_cast<PGraphicsMovieItem *>(m_theThing);
if (animatedItem) {
const int frameCount = animatedItem->movie()->frameCount();
const int currentFrame = animatedItem->movie()->currentFrameNumber();
const int targetFrame = (currentFrame + delta) % frameCount;
animatedItem->movie()->setPaused(true);
return animatedItem->movie()->jumpToFrame(targetFrame);
auto* animatedItem = fetchAnimatedItem(m_theThing);
// Only paused animated item can skip frame
if (animatedItem == nullptr) {
return false;
}
return false;
if (animatedItem->movie()->state() != QMovie::Paused) {
return false;
}
// Perform skip
const int frameCount = animatedItem->movie()->frameCount();
const int currentFrame = animatedItem->movie()->currentFrameNumber();
const int targetFrame = (currentFrame + delta) % frameCount;
return animatedItem->movie()->jumpToFrame(targetFrame);
}
QPixmap GraphicsScene::renderToPixmap()

View File

@@ -21,7 +21,19 @@ public:
bool trySetTransformationMode(Qt::TransformationMode mode, float scaleHint);
/// Check whether current thing is animation.
/// Only animation thing can be paused.
bool canPauseAnimation() const;
/// Check whether we are pasuing an animation graphics.
bool isPauseAnimation() const;
/// Toggle animation graphics pause state.
/// Return true for successfully toggle, otherwise false.
bool togglePauseAnimation();
/// Check whether we can skip animation frame.
/// Only paused animation graphics can skip frame.
bool canSkipAnimationFrame() const;
/// Skip paused animation graphics with given frame.
/// Return true for successfully skip, otherwise false.
bool skipAnimationFrame(int delta = 1);
QPixmap renderToPixmap();

View File

@@ -140,7 +140,7 @@ void GraphicsView::resetTransform()
void GraphicsView::zoomView(qreal scaleFactor)
{
m_enableFitInView = false;
m_enableFitToScreen = false;
scale(scaleFactor, scaleFactor);
applyTransformationModeByScaleFactor();
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
@@ -223,6 +223,8 @@ void GraphicsView::fitByOrientation(Qt::Orientation ori, bool scaleDownOnly)
// Position the image correctly based on orientation with rotation consideration
QRectF originalScene = sceneRect();
QTransform currentTransform = transform();
// TODO: May be optimized by matrix arithmetic operation?
if (ori == Qt::Horizontal) {
// For horizontal fit (fit by width), position at top (for tall images)
@@ -276,7 +278,7 @@ void GraphicsView::fitByOrientation(Qt::Orientation ori, bool scaleDownOnly)
centerOn(sceneLeftCenter);
}
m_enableFitInView = false;
m_enableFitToScreen = false;
applyTransformationModeByScaleFactor();
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
@@ -299,7 +301,7 @@ void GraphicsView::displayScene()
emit navigatorViewRequired(false, transform());
}
m_enableFitInView = true;
m_enableFitToScreen = true;
m_firstUserMediaLoaded = true;
}
@@ -313,9 +315,9 @@ bool GraphicsView::isSceneBiggerThanView() const
}
// Automately do fit in view when viewport(window) smaller than image original size.
void GraphicsView::setEnableAutoFitInView(bool enable)
void GraphicsView::setEnableAutoFitToScreen(bool enable)
{
m_enableFitInView = enable;
m_enableFitToScreen = enable;
}
bool GraphicsView::avoidResetTransform() const
@@ -328,6 +330,15 @@ void GraphicsView::setAvoidResetTransform(bool avoidReset)
m_avoidResetTransform = avoidReset;
}
bool GraphicsView::checkerboard() const {
return m_checkerboardEnabled;
}
void GraphicsView::toggleCheckerboard(bool invertCheckerboardColor)
{
setCheckerboardEnabled(!m_checkerboardEnabled, invertCheckerboardColor);
}
inline double zeroOrOne(double number)
{
return qFuzzyIsNull(number) ? 0 : (number > 0 ? 1 : -1);
@@ -342,11 +353,6 @@ QTransform GraphicsView::resetScale(const QTransform & orig)
orig.dx(), orig.dy());
}
void GraphicsView::toggleCheckerboard(bool invertCheckerboardColor)
{
setCheckerboardEnabled(!m_checkerboardEnabled, invertCheckerboardColor);
}
void GraphicsView::updateStyle(Qt::ColorScheme colorScheme) {
// YYC MARK:
// If we are using Windows 10 and lower system or Qt which do not support "Windows 11"
@@ -423,11 +429,11 @@ void GraphicsView::wheelEvent(QWheelEvent *event)
void GraphicsView::resizeEvent(QResizeEvent *event)
{
if (m_enableFitInView) {
if (m_enableFitToScreen) {
bool originalSizeSmallerThanWindow = isThingSmallerThanWindowWith(resetScale(transform()));
if (originalSizeSmallerThanWindow && scaleFactor() >= 1) {
// no longer need to do fitInView()
// but we leave the m_enableFitInView value unchanged in case
// but we leave the m_enableToScreen value unchanged in case
// user resize down the window again.
} else if (originalSizeSmallerThanWindow && scaleFactor() < 1) {
resetScale();

View File

@@ -38,11 +38,14 @@ public:
void displayScene();
bool isSceneBiggerThanView() const;
void setEnableAutoFitInView(bool enable = true);
void setEnableAutoFitToScreen(bool enable = true);
bool avoidResetTransform() const;
void setAvoidResetTransform(bool avoidReset);
bool checkerboard() const;
void toggleCheckerboard(bool invertCheckerboardColor = false);
static QTransform resetScale(const QTransform & orig);
signals:
@@ -50,7 +53,6 @@ signals:
void viewportRectChanged();
public slots:
void toggleCheckerboard(bool invertCheckerboardColor = false);
void updateStyle(Qt::ColorScheme colorScheme);
private:
@@ -70,7 +72,7 @@ private:
// Consider switch to 3 state for "no fit", "always fit" and "fit when view is smaller"?
// ... 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.
bool m_enableFitInView = false;
bool m_enableFitToScreen = false;
bool m_avoidResetTransform = false;
bool m_checkerboardEnabled = false;
bool m_useLightCheckerboard = false;

View File

@@ -92,13 +92,16 @@ MainWindow::MainWindow(QWidget *parent)
m_bottomButtonGroup = new BottomButtonGroup({
m_am->actionActualSize,
m_am->actionToggleMaximize,
m_am->actionFitToScreen,
m_am->actionZoomIn,
m_am->actionZoomOut,
m_am->actionPrevPicture,
m_am->actionProperties,
m_am->actionNextPicture,
m_am->actionRotateCounterClockwise,
m_am->actionRotateClockwise,
m_am->actionFlipHorizontal,
m_am->actionToggleCheckerboard,
m_am->actionRotateClockwise
}, this);
connect(m_pm, &PlaylistManager::totalCountChanged, this, &MainWindow::updateGalleryButtonsVisibility);
@@ -358,65 +361,73 @@ void MainWindow::resizeEvent(QResizeEvent *event)
void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
// TODO: Update this function.
QMenu * menu = new QMenu;
// TODO: currentImageFileUrl() may be redundant.
QMenu * copyMenu = new QMenu(tr("&Copy"));
QUrl currentFileUrl = currentImageFileUrl();
QAction * copyPixmap = m_am->actionCopyPixmap;
QAction * copyFilePath = m_am->actionCopyFilePath;
copyMenu->setIcon(QIcon::fromTheme(u"edit-copy"_s));
copyMenu->addAction(copyPixmap);
copyMenu->addAction(m_am->actionCopyPixmap);
if (currentFileUrl.isValid()) {
copyMenu->addAction(copyFilePath);
copyMenu->addAction(m_am->actionCopyFilePath);
}
QAction * paste = m_am->actionPaste;
QAction * trash = m_am->actionTrash;
QAction * avoidResetTransform = m_am->actionToggleAvoidResetTransform;
avoidResetTransform->setCheckable(true);
avoidResetTransform->setChecked(m_gv->avoidResetTransform());
QAction * toggleSettings = m_am->actionSettings;
QAction * helpAction = m_am->actionHelp;
QAction * propertiesAction = m_am->actionProperties;
#if 0
menu->addAction(m_am->actionOpen);
#endif // 0
if (copyMenu->actions().count() == 1) {
menu->addActions(copyMenu->actions());
} else {
menu->addMenu(copyMenu);
}
if (canPaste()) {
menu->addAction(paste);
}
menu->addSeparator();
menu->addAction(m_am->actionPrevPicture);
menu->addAction(m_am->actionNextPicture);
menu->addSeparator();
menu->addAction(m_am->actionHorizontalFlip);
#if 0
menu->addAction(m_am->actionFitInView);
menu->addAction(m_am->actionActualSize);
menu->addAction(m_am->actionFitToScreen);
menu->addAction(m_am->actionFitByWidth);
#endif // 0
menu->addAction(m_am->actionFitByHeight);
menu->addAction(m_am->actionZoomIn);
menu->addAction(m_am->actionZoomOut);
menu->addSeparator();
menu->addAction(m_am->actionFlipHorizontal);
menu->addAction(m_am->actionFlipVertical);
menu->addAction(m_am->actionRotateClockwise);
menu->addAction(m_am->actionRotateCounterClockwise);
menu->addSeparator();
// TODO: Move checked update to action manager.
QAction * avoidResetTransform = m_am->actionToggleAvoidResetTransform;
avoidResetTransform->setCheckable(true);
avoidResetTransform->setChecked(m_gv->avoidResetTransform());
menu->addAction(avoidResetTransform);
menu->addSeparator();
menu->addAction(toggleSettings);
menu->addAction(helpAction);
menu->addAction(m_am->actionToggleCheckerboard);
QAction * pauseAnimation = m_am->actionTogglePauseAnimation;
pauseAnimation->setCheckable(true);
pauseAnimation->setChecked(m_gv->scene()->pauseAnimation());
menu->addAction(pauseAnimation);
menu->addAction(m_am->actionAnimationNextFrame);
if (currentFileUrl.isValid()) {
menu->addSeparator();
if (currentFileUrl.isLocalFile()) {
menu->addAction(trash);
menu->addAction(m_am->actionTrash);
menu->addAction(m_am->actionLocateInFileManager);
}
menu->addAction(propertiesAction);
menu->addAction(m_am->actionProperties);
}
menu->addSeparator();
menu->addAction(m_am->actionSettings);
menu->addAction(m_am->actionHelp);
menu->exec(mapToGlobal(event->pos()));
menu->deleteLater();
copyMenu->deleteLater();
@@ -542,55 +553,7 @@ QSize MainWindow::sizeHint() const
return QSize(710, 530);
}
void MainWindow::on_actionOpen_triggered()
{
QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
QUrl pictureUrl = picturesLocations.isEmpty() ? QUrl::fromLocalFile(picturesLocations.first())
: QUrl::fromLocalFile(QDir::homePath());
QList<QUrl> urls(QFileDialog::getOpenFileUrls(this, QString(), pictureUrl));
if (!urls.isEmpty()) {
showUrls(urls);
}
}
void MainWindow::on_actionActualSize_triggered()
{
m_gv->resetScale();
m_gv->setEnableAutoFitInView(false);
}
void MainWindow::on_actionToggleMaximize_triggered()
{
toggleMaximize();
}
void MainWindow::on_actionZoomIn_triggered()
{
if (m_gv->scaleFactor() < 1000) {
m_gv->zoomView(1.25);
}
}
void MainWindow::on_actionZoomOut_triggered()
{
m_gv->zoomView(0.8);
}
void MainWindow::on_actionHorizontalFlip_triggered()
{
m_gv->flipView();
}
void MainWindow::on_actionFitInView_triggered()
{
m_gv->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
m_gv->setEnableAutoFitInView(m_gv->scaleFactor() <= 1);
}
void MainWindow::on_actionFitByWidth_triggered()
{
m_gv->fitByOrientation();
}
// region: Action Trigger
void MainWindow::on_actionCopyPixmap_triggered()
{
@@ -607,43 +570,99 @@ void MainWindow::on_actionCopyFilePath_triggered()
}
}
void MainWindow::on_actionPaste_triggered()
void MainWindow::on_actionPrevPicture_triggered()
{
QImage clipboardImage;
QUrl clipboardFileUrl;
galleryPrev();
}
const QMimeData * clipboardData = QApplication::clipboard()->mimeData();
if (clipboardData->hasImage()) {
QVariant imageVariant(clipboardData->imageData());
if (imageVariant.isValid()) {
clipboardImage = qvariant_cast<QImage>(imageVariant);
}
} else if (clipboardData->hasText()) {
QString clipboardText(clipboardData->text());
if (clipboardText.startsWith("PICTURE:")) {
QString maybeFilename(clipboardText.mid(8));
if (QFile::exists(maybeFilename)) {
clipboardFileUrl = QUrl::fromLocalFile(maybeFilename);
}
}
}
void MainWindow::on_actionNextPicture_triggered()
{
galleryNext();
}
if (!clipboardImage.isNull()) {
setWindowTitle(tr("Image From Clipboard"));
m_gv->showImage(clipboardImage);
clearGallery();
} else if (clipboardFileUrl.isValid()) {
m_gv->showFileFromPath(clipboardFileUrl.toLocalFile());
m_pm->loadPlaylist(clipboardFileUrl);
void MainWindow::on_actionActualSize_triggered()
{
m_gv->resetScale();
m_gv->setEnableAutoFitToScreen(false);
}
void MainWindow::on_actionFitToScreen_triggered()
{
m_gv->fitInView(m_nav->sceneRect(), Qt::KeepAspectRatio);
m_gv->setEnableAutoFitToScreen(m_gv->scaleFactor() <= 1);
}
void MainWindow::on_actionFitByWidth_triggered()
{
m_gv->fitByOrientation(Qt::Orientation::Horizontal);
}
void MainWindow::on_actionFitByHeight_triggered()
{
m_gv->fitByOrientation(Qt::Orientation::Vertical);
}
void MainWindow::on_actionZoomIn_triggered()
{
if (m_gv->scaleFactor() < 1000) {
m_gv->zoomView(1.25);
}
}
void MainWindow::on_actionZoomOut_triggered()
{
m_gv->zoomView(0.8);
}
void MainWindow::on_actionFlipHorizontal_triggered()
{
m_gv->flipView(true);
}
void MainWindow::on_actionFlipVertical_triggered()
{
m_gv->flipView(false);
}
void MainWindow::on_actionRotateClockwise_triggered()
{
m_gv->rotateView(true);
m_gv->displayScene();
}
void MainWindow::on_actionRotateCounterClockwise_triggered()
{
m_gv->rotateView(false);
m_gv->displayScene();
}
void MainWindow::on_actionToggleAvoidResetTransform_triggered()
{
toggleAvoidResetTransform();
}
void MainWindow::on_actionToggleCheckerboard_triggered()
{
m_gv->toggleCheckerboard(QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier));
}
void MainWindow::on_actionTogglePauseAnimation_triggered()
{
m_gv->scene()->togglePauseAnimation();
}
void MainWindow::on_actionAnimationNextFrame_triggered()
{
m_gv->scene()->skipAnimationFrame(1);
}
void MainWindow::on_actionTrash_triggered()
{
QModelIndex index = m_pm->curIndex();
if (!m_pm->urlByIndex(index).isLocalFile()) return;
auto index = m_pm->currentIndex();
if (!index.has_value()) return;
if (!m_pm->urlByIndex(*index).isLocalFile()) return;
QFile file(m_pm->localFileByIndex(index));
QFile file(m_pm->localFileByIndex(*index));
QFileInfo fileInfo(file.fileName());
QMessageBox::StandardButton result = QMessageBox::question(this, tr("Move to Trash"),
@@ -660,77 +679,6 @@ void MainWindow::on_actionTrash_triggered()
}
}
void MainWindow::on_actionToggleCheckerboard_triggered()
{
// TODO: is that okay to do this since we plan to support custom shortcuts?
m_gv->toggleCheckerboard(QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier));
}
void MainWindow::on_actionRotateClockwise_triggered()
{
m_gv->rotateView();
m_gv->displayScene();
}
void MainWindow::on_actionRotateCounterClockwise_triggered()
{
m_gv->rotateView(false);
m_gv->displayScene();
}
void MainWindow::on_actionPrevPicture_triggered()
{
galleryPrev();
}
void MainWindow::on_actionNextPicture_triggered()
{
galleryNext();
}
void MainWindow::on_actionTogglePauseAnimation_triggered()
{
m_gv->scene()->togglePauseAnimation();
}
void MainWindow::on_actionAnimationNextFrame_triggered()
{
m_gv->scene()->skipAnimationFrame(1);
}
void MainWindow::on_actionToggleAvoidResetTransform_triggered()
{
toggleAvoidResetTransform();
}
void MainWindow::on_actionSettings_triggered()
{
SettingsDialog * sd = new SettingsDialog(this);
sd->exec();
sd->deleteLater();
}
void MainWindow::on_actionHelp_triggered()
{
AboutDialog * ad = new AboutDialog(this);
ad->exec();
ad->deleteLater();
}
void MainWindow::on_actionProperties_triggered()
{
QUrl currentFileUrl = currentImageFileUrl();
if (!currentFileUrl.isValid()) return;
MetadataModel * md = new MetadataModel();
md->setFile(currentFileUrl.toLocalFile());
MetadataDialog * ad = new MetadataDialog(this);
ad->setMetadataModel(md);
ad->exec();
ad->deleteLater();
}
void MainWindow::on_actionLocateInFileManager_triggered()
{
QUrl currentFileUrl = currentImageFileUrl();
@@ -766,23 +714,39 @@ void MainWindow::on_actionLocateInFileManager_triggered()
#endif // Q_OS_WIN
}
void MainWindow::on_actionQuitApp_triggered()
void MainWindow::on_actionProperties_triggered()
{
quitAppAction(false);
QUrl currentFileUrl = currentImageFileUrl();
if (!currentFileUrl.isValid()) return;
MetadataModel * md = new MetadataModel();
md->setFile(currentFileUrl.toLocalFile());
MetadataDialog * ad = new MetadataDialog(this);
ad->setMetadataModel(md);
ad->exec();
ad->deleteLater();
}
void MainWindow::on_actionSettings_triggered()
{
SettingsDialog * sd = new SettingsDialog(this);
sd->exec();
sd->deleteLater();
}
void MainWindow::on_actionHelp_triggered()
{
AboutDialog * ad = new AboutDialog(this);
ad->exec();
ad->deleteLater();
}
// endregion
bool MainWindow::updateFileWatcher(const QString &basePath)
{
m_fileSystemWatcher->removePaths(m_fileSystemWatcher->files());
if (!basePath.isEmpty()) return m_fileSystemWatcher->addPath(basePath);
return false;
}
void MainWindow::updateGalleryButtonsVisibility()
{
const int galleryFileCount = m_pm->totalCount();
const bool loopGallery = Settings::instance()->loopGallery();
m_am->enablePrevNextPictureAction(
galleryFileCount > 1 && (loopGallery || !m_pm->isFirstIndex()),
galleryFileCount > 1 && (loopGallery || !m_pm->isLastIndex()));
}

View File

@@ -66,39 +66,38 @@ protected:
QSize sizeHint() const override;
private slots:
void on_actionOpen_triggered();
void on_actionActualSize_triggered();
void on_actionToggleMaximize_triggered();
void on_actionZoomIn_triggered();
void on_actionZoomOut_triggered();
void on_actionToggleCheckerboard_triggered();
void on_actionRotateClockwise_triggered();
void on_actionRotateCounterClockwise_triggered();
void on_actionCopyPixmap_triggered();
void on_actionCopyFilePath_triggered();
void on_actionPrevPicture_triggered();
void on_actionNextPicture_triggered();
void on_actionActualSize_triggered();
void on_actionFitToScreen_triggered();
void on_actionFitByWidth_triggered();
void on_actionFitByHeight_triggered();
void on_actionZoomIn_triggered();
void on_actionZoomOut_triggered();
void on_actionFlipHorizontal_triggered();
void on_actionFlipVertical_triggered();
void on_actionRotateClockwise_triggered();
void on_actionRotateCounterClockwise_triggered();
void on_actionToggleAvoidResetTransform_triggered();
void on_actionToggleCheckerboard_triggered();
void on_actionTogglePauseAnimation_triggered();
void on_actionAnimationNextFrame_triggered();
void on_actionHorizontalFlip_triggered();
void on_actionFitInView_triggered();
void on_actionFitByWidth_triggered();
void on_actionCopyPixmap_triggered();
void on_actionCopyFilePath_triggered();
void on_actionPaste_triggered();
void on_actionTrash_triggered();
void on_actionToggleAvoidResetTransform_triggered();
void on_actionLocateInFileManager_triggered();
void on_actionProperties_triggered();
void on_actionSettings_triggered();
void on_actionHelp_triggered();
void on_actionProperties_triggered();
void on_actionLocateInFileManager_triggered();
void on_actionQuitApp_triggered();
private:
bool updateFileWatcher(const QString & basePath = QString());
void updateGalleryButtonsVisibility();
private:
ActionManager *m_am;