mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 02:42:52 +00:00
295 lines
8.3 KiB
C++
295 lines
8.3 KiB
C++
// vim: set tabstop=4 shiftwidth=4 expandtab:
|
|
/*
|
|
Gwenview: an image viewer
|
|
Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
|
|
|
|
*/
|
|
// Self
|
|
#include "moc_birdeyeview.cpp"
|
|
|
|
// Local
|
|
#include <lib/document/document.h>
|
|
#include <lib/documentview/documentview.h>
|
|
|
|
// KDE
|
|
#include <KDebug>
|
|
|
|
// Qt
|
|
#include <QApplication>
|
|
#include <QCursor>
|
|
#include <QGraphicsSceneEvent>
|
|
#include <QPainter>
|
|
#include <QPropertyAnimation>
|
|
#include <QTimer>
|
|
|
|
namespace Gwenview
|
|
{
|
|
|
|
static qreal MIN_SIZE = 72;
|
|
static qreal VIEW_OFFSET = MIN_SIZE / 4;
|
|
|
|
static int AUTOHIDE_DELAY = 2000;
|
|
|
|
/**
|
|
* Returns a QRectF whose coordinates are rounded to completely contains rect
|
|
*/
|
|
inline QRectF alignedRectF(const QRectF& rect)
|
|
{
|
|
return QRectF(rect.toAlignedRect());
|
|
}
|
|
|
|
struct BirdEyeViewPrivate
|
|
{
|
|
BirdEyeView* q;
|
|
DocumentView* mDocView;
|
|
QPropertyAnimation* mOpacityAnim;
|
|
QTimer* mAutoHideTimer;
|
|
QRectF mVisibleRect;
|
|
QPointF mStartDragMousePos;
|
|
QPointF mStartDragViewPos;
|
|
|
|
void updateCursor(const QPointF& pos)
|
|
{
|
|
q->setCursor(mVisibleRect.contains(pos) ? Qt::OpenHandCursor : Qt::ArrowCursor);
|
|
}
|
|
|
|
void updateVisibility()
|
|
{
|
|
bool visible;
|
|
if (!mDocView->canZoom() || mDocView->zoomToFit()) {
|
|
// No need to show
|
|
visible = false;
|
|
} else if (mDocView->isAnimated()) {
|
|
// Do not show while animated
|
|
visible = false;
|
|
} else if (mVisibleRect == q->boundingRect()) {
|
|
// All of the image is visible
|
|
visible = false;
|
|
} else if (q->isUnderMouse() || !mStartDragMousePos.isNull()) {
|
|
// User is interacting or about to interact with birdeyeview
|
|
visible = true;
|
|
} else if (mAutoHideTimer->isActive()) {
|
|
// User triggered some activity recently (move mouse, scroll, zoom)
|
|
visible = true;
|
|
} else {
|
|
// No recent activity
|
|
visible = false;
|
|
}
|
|
qreal wantedOpacity = visible ? 1 : 0;
|
|
if (!qFuzzyCompare(wantedOpacity, q->opacity())) {
|
|
mOpacityAnim->setEndValue(wantedOpacity);
|
|
mOpacityAnim->start();
|
|
}
|
|
|
|
if (visible) {
|
|
mAutoHideTimer->start();
|
|
}
|
|
}
|
|
};
|
|
|
|
BirdEyeView::BirdEyeView(DocumentView* docView)
|
|
: QGraphicsWidget(docView)
|
|
, d(new BirdEyeViewPrivate)
|
|
{
|
|
d->q = this;
|
|
d->mDocView = docView;
|
|
setFlag(ItemIsSelectable);
|
|
setCursor(Qt::ArrowCursor);
|
|
setAcceptHoverEvents(true);
|
|
|
|
d->mOpacityAnim = new QPropertyAnimation(this, "opacity", this);
|
|
|
|
d->mAutoHideTimer = new QTimer(this);
|
|
d->mAutoHideTimer->setSingleShot(true);
|
|
d->mAutoHideTimer->setInterval(AUTOHIDE_DELAY);
|
|
connect(d->mAutoHideTimer, SIGNAL(timeout()), SLOT(slotAutoHideTimeout()));
|
|
|
|
// Hide ourself by default, to avoid startup flashes (if we let updateOpacity
|
|
// update opacity, it will do so through an animation)
|
|
setOpacity(0);
|
|
slotZoomOrSizeChanged();
|
|
|
|
connect(docView->document().data(), SIGNAL(metaInfoUpdated()), SLOT(slotZoomOrSizeChanged()));
|
|
connect(docView, SIGNAL(zoomChanged(qreal)), SLOT(slotZoomOrSizeChanged()));
|
|
connect(docView, SIGNAL(zoomToFitChanged(bool)), SLOT(slotZoomOrSizeChanged()));
|
|
connect(docView, SIGNAL(positionChanged()), SLOT(slotPositionChanged()));
|
|
}
|
|
|
|
BirdEyeView::~BirdEyeView()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void BirdEyeView::adjustGeometry()
|
|
{
|
|
if (!d->mDocView->canZoom() || d->mDocView->zoomToFit()) {
|
|
return;
|
|
}
|
|
QSizeF size = d->mDocView->document()->size();
|
|
size.scale(MIN_SIZE, MIN_SIZE, Qt::KeepAspectRatioByExpanding);
|
|
QRectF docViewRect = d->mDocView->boundingRect();
|
|
int maxBevHeight = docViewRect.height() - 2 * VIEW_OFFSET;
|
|
int maxBevWidth = docViewRect.width() - 2 * VIEW_OFFSET;
|
|
if (size.height() > maxBevHeight) {
|
|
size.scale(MIN_SIZE, maxBevHeight, Qt::KeepAspectRatio);
|
|
}
|
|
if (size.width() > maxBevWidth) {
|
|
size.scale(maxBevWidth, MIN_SIZE, Qt::KeepAspectRatio);
|
|
}
|
|
QRectF geom = QRectF(
|
|
QApplication::isRightToLeft()
|
|
? docViewRect.left() + VIEW_OFFSET
|
|
: docViewRect.right() - VIEW_OFFSET - size.width(),
|
|
docViewRect.bottom() - VIEW_OFFSET - size.height(),
|
|
size.width(),
|
|
size.height()
|
|
);
|
|
setGeometry(alignedRectF(geom));
|
|
adjustVisibleRect();
|
|
}
|
|
|
|
void BirdEyeView::adjustVisibleRect()
|
|
{
|
|
QSizeF docSize = d->mDocView->document()->size();
|
|
qreal viewZoom = d->mDocView->zoom();
|
|
qreal bevZoom;
|
|
if (docSize.height() > docSize.width()) {
|
|
bevZoom = size().height() / docSize.height();
|
|
} else {
|
|
bevZoom = size().width() / docSize.width();
|
|
}
|
|
|
|
if (qFuzzyIsNull(viewZoom) || qFuzzyIsNull(bevZoom)) {
|
|
// Prevent divide-by-zero crashes
|
|
return;
|
|
}
|
|
|
|
QRectF rect = QRectF(
|
|
QPointF(d->mDocView->position()) / viewZoom * bevZoom,
|
|
(d->mDocView->size() / viewZoom).boundedTo(docSize) * bevZoom);
|
|
d->mVisibleRect = rect;
|
|
}
|
|
|
|
void BirdEyeView::slotAutoHideTimeout()
|
|
{
|
|
d->updateVisibility();
|
|
}
|
|
|
|
void BirdEyeView::slotZoomOrSizeChanged()
|
|
{
|
|
if (!d->mDocView->canZoom() || d->mDocView->zoomToFit()) {
|
|
d->updateVisibility();
|
|
return;
|
|
}
|
|
adjustGeometry();
|
|
update();
|
|
d->mAutoHideTimer->start();
|
|
d->updateVisibility();
|
|
}
|
|
|
|
void BirdEyeView::slotPositionChanged()
|
|
{
|
|
adjustVisibleRect();
|
|
update();
|
|
d->mAutoHideTimer->start();
|
|
d->updateVisibility();
|
|
}
|
|
|
|
inline void drawTransparentRect(QPainter* painter, const QRectF& rect, const QColor& color)
|
|
{
|
|
QColor bg = color;
|
|
bg.setAlphaF(.33);
|
|
QColor fg = color;
|
|
fg.setAlphaF(.66);
|
|
painter->setPen(fg);
|
|
painter->setBrush(bg);
|
|
painter->drawRect(rect.adjusted(0, 0, -1, -1));
|
|
}
|
|
|
|
void BirdEyeView::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
|
|
{
|
|
static const QColor bgColor = QColor::fromHsvF(0, 0, .33);
|
|
drawTransparentRect(painter, boundingRect(), bgColor);
|
|
drawTransparentRect(painter, d->mVisibleRect, Qt::white);
|
|
}
|
|
|
|
void BirdEyeView::onMouseMoved()
|
|
{
|
|
d->mAutoHideTimer->start();
|
|
d->updateVisibility();
|
|
}
|
|
|
|
void BirdEyeView::slotIsAnimatedChanged()
|
|
{
|
|
d->updateVisibility();
|
|
}
|
|
|
|
void BirdEyeView::mousePressEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
if (d->mVisibleRect.contains(event->pos())) {
|
|
setCursor(Qt::ClosedHandCursor);
|
|
d->mStartDragMousePos = event->pos();
|
|
d->mStartDragViewPos = d->mDocView->position();
|
|
}
|
|
}
|
|
|
|
void BirdEyeView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
QGraphicsItem::mouseMoveEvent(event);
|
|
if (d->mStartDragMousePos.isNull()) {
|
|
// Do not drag if mouse was pressed outside visible rect
|
|
return;
|
|
}
|
|
qreal ratio = qMin(d->mDocView->boundingRect().height() / d->mVisibleRect.height(),
|
|
d->mDocView->boundingRect().width() / d->mVisibleRect.width());
|
|
QPointF mousePos = event->pos();
|
|
QPointF viewPos = d->mStartDragViewPos + (mousePos - d->mStartDragMousePos) * ratio;
|
|
|
|
d->mDocView->setPosition(viewPos.toPoint());
|
|
}
|
|
|
|
void BirdEyeView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
|
|
{
|
|
QGraphicsItem::mouseReleaseEvent(event);
|
|
if (d->mStartDragMousePos.isNull()) {
|
|
return;
|
|
}
|
|
d->updateCursor(event->pos());
|
|
d->mStartDragMousePos = QPointF();
|
|
d->mAutoHideTimer->start();
|
|
}
|
|
|
|
void BirdEyeView::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
|
|
{
|
|
d->mAutoHideTimer->stop();
|
|
d->updateVisibility();
|
|
}
|
|
|
|
void BirdEyeView::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
|
|
{
|
|
if (d->mStartDragMousePos.isNull()) {
|
|
d->updateCursor(event->pos());
|
|
}
|
|
}
|
|
|
|
void BirdEyeView::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
|
|
{
|
|
d->mAutoHideTimer->start();
|
|
d->updateVisibility();
|
|
}
|
|
|
|
} // namespace
|