File indexing completed on 2024-04-28 04:18:54

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "zoomwidget.h"
0023 
0024 // stdc++
0025 #include <cmath>
0026 
0027 // Qt
0028 #include <QAction>
0029 #include <QActionGroup>
0030 #include <QHBoxLayout>
0031 #include <QSlider>
0032 #include <QToolButton>
0033 // KF
0034 
0035 // Local
0036 #include "signalblocker.h"
0037 #include "zoomcombobox/zoomcombobox.h"
0038 #include "zoomslider.h"
0039 
0040 namespace Gwenview
0041 {
0042 static const qreal MAGIC_K = 1.04;
0043 static const qreal MAGIC_OFFSET = 16.;
0044 static const qreal PRECISION = 100.;
0045 inline int sliderValueForZoom(qreal zoom)
0046 {
0047     return int(PRECISION * (log(zoom) / log(MAGIC_K) + MAGIC_OFFSET));
0048 }
0049 
0050 inline qreal zoomForSliderValue(int sliderValue)
0051 {
0052     return pow(MAGIC_K, sliderValue / PRECISION - MAGIC_OFFSET);
0053 }
0054 
0055 struct ZoomWidgetPrivate {
0056     ZoomWidget *q = nullptr;
0057 
0058     QToolButton *mFitButton = nullptr;
0059     ZoomSlider *mZoomSlider = nullptr;
0060     ZoomComboBox *mZoomComboBox = nullptr;
0061     QAction *mActualSizeAction = nullptr;
0062 
0063     bool mZoomUpdatedBySlider;
0064 
0065     void emitZoomChanged()
0066     {
0067         // Use QSlider::sliderPosition(), not QSlider::value() because when we are
0068         // called from slotZoomSliderActionTriggered(), QSlider::value() has not
0069         // been updated yet.
0070         qreal zoom = zoomForSliderValue(mZoomSlider->slider()->sliderPosition());
0071         mZoomUpdatedBySlider = true;
0072         Q_EMIT q->zoomChanged(zoom);
0073         mZoomUpdatedBySlider = false;
0074     }
0075 };
0076 
0077 ZoomWidget::ZoomWidget(QWidget *parent)
0078     : QFrame(parent)
0079     , d(new ZoomWidgetPrivate)
0080 {
0081     d->q = this;
0082     d->mZoomUpdatedBySlider = false;
0083 
0084     setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
0085 
0086     d->mFitButton = new QToolButton(this);
0087 
0088     d->mZoomSlider = new ZoomSlider(this);
0089     d->mZoomSlider->setMinimumWidth(150);
0090     d->mZoomSlider->slider()->setSingleStep(int(PRECISION));
0091     d->mZoomSlider->slider()->setPageStep(3 * int(PRECISION));
0092     connect(d->mZoomSlider->slider(), &QAbstractSlider::actionTriggered, this, &ZoomWidget::slotZoomSliderActionTriggered);
0093 
0094     d->mZoomComboBox = new ZoomComboBox(this);
0095     connect(d->mZoomComboBox, &ZoomComboBox::zoomChanged, this, &ZoomWidget::zoomChanged);
0096 
0097     // Layout
0098     auto layout = new QHBoxLayout(this);
0099     layout->setContentsMargins(0, 0, 0, 0);
0100     layout->setSpacing(0);
0101     layout->addWidget(d->mFitButton);
0102     layout->addWidget(d->mZoomSlider);
0103     layout->addWidget(d->mZoomComboBox);
0104 }
0105 
0106 ZoomWidget::~ZoomWidget()
0107 {
0108     delete d;
0109 }
0110 
0111 void ZoomWidget::setActions(QAction *zoomToFitAction, QAction *actualSizeAction, QAction *zoomInAction, QAction *zoomOutAction, QAction *zoomToFillAction)
0112 {
0113     d->mFitButton->setDefaultAction(zoomToFitAction);
0114     d->mFitButton->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextOnly);
0115 
0116     d->mZoomSlider->setZoomInAction(zoomInAction);
0117     d->mZoomSlider->setZoomOutAction(zoomOutAction);
0118 
0119     d->mZoomComboBox->setActions(zoomToFitAction, zoomToFillAction, actualSizeAction);
0120 
0121     auto actionGroup = new QActionGroup(d->q);
0122     actionGroup->addAction(zoomToFitAction);
0123     actionGroup->addAction(zoomToFillAction);
0124     actionGroup->addAction(actualSizeAction);
0125     actionGroup->setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional);
0126 
0127     d->mActualSizeAction = actualSizeAction;
0128 }
0129 
0130 void ZoomWidget::slotZoomSliderActionTriggered()
0131 {
0132     // The slider value changed because of the user (not because of range
0133     // changes). In this case disable zoom and apply slider value.
0134     d->emitZoomChanged();
0135 }
0136 
0137 void ZoomWidget::setZoom(qreal zoom)
0138 {
0139     if (zoom != 1.0) {
0140         d->mActualSizeAction->setChecked(false);
0141     }
0142     d->mZoomComboBox->setValue(zoom);
0143 
0144     // Don't change slider value if we come here because the slider change,
0145     // avoids choppy sliding scroll.
0146     if (!d->mZoomUpdatedBySlider) {
0147         QSlider *slider = d->mZoomSlider->slider();
0148         SignalBlocker blocker(slider);
0149         int value = sliderValueForZoom(zoom);
0150 
0151         if (value < slider->minimum()) {
0152             // It is possible that we are called *before* setMinimumZoom() as
0153             // been called. In this case, define the minimum ourself.
0154             d->mZoomSlider->setMinimum(value);
0155         }
0156         d->mZoomSlider->setValue(value);
0157     }
0158 }
0159 
0160 void ZoomWidget::setMinimumZoom(qreal minimumZoom)
0161 {
0162     d->mZoomSlider->setMinimum(sliderValueForZoom(minimumZoom));
0163     d->mZoomComboBox->setMinimum(minimumZoom);
0164 }
0165 
0166 void ZoomWidget::setMaximumZoom(qreal zoom)
0167 {
0168     d->mZoomSlider->setMaximum(sliderValueForZoom(zoom));
0169     d->mZoomComboBox->setMaximum(zoom);
0170 }
0171 
0172 } // namespace
0173 
0174 #include "moc_zoomwidget.cpp"