File indexing completed on 2024-04-28 04:18:50
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2007 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, Boston, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "fullscreenbar.h" 0023 0024 // Qt 0025 #include <QAction> 0026 #include <QApplication> 0027 #include <QBitmap> 0028 #include <QEvent> 0029 #include <QLayout> 0030 #include <QMouseEvent> 0031 #include <QScreen> 0032 #include <QTimeLine> 0033 #include <QTimer> 0034 #include <QToolButton> 0035 0036 // KF 0037 #include <KLocalizedString> 0038 #include <chrono> 0039 0040 using namespace std::chrono_literals; 0041 0042 // Local 0043 0044 namespace Gwenview 0045 { 0046 static const int SLIDE_DURATION = 150; 0047 static const int AUTO_HIDE_CURSOR_TIMEOUT = 1000; 0048 0049 // How long before the bar slide out after switching to fullscreen 0050 static const int INITIAL_HIDE_TIMEOUT = 2000; 0051 0052 // Do not slide bar out if mouse is less than this amount of pixels below bar, to 0053 // prevent accidental slide outs 0054 static const int EXTRA_BAR_HEIGHT = 20; 0055 0056 struct FullScreenBarPrivate { 0057 FullScreenBar *q = nullptr; 0058 QTimeLine *mTimeLine = nullptr; 0059 QTimer *mAutoHideCursorTimer = nullptr; 0060 bool mAutoHidingEnabled; 0061 bool mEdgeTriggerEnabled; 0062 QTimer *mInitialHideTimer = nullptr; 0063 0064 void startTimeLine() 0065 { 0066 if (mTimeLine->state() != QTimeLine::Running) { 0067 mTimeLine->start(); 0068 } 0069 } 0070 0071 void hideCursor() 0072 { 0073 QBitmap empty(32, 32); 0074 empty.clear(); 0075 const QCursor blankCursor(empty, empty); 0076 QApplication::setOverrideCursor(blankCursor); 0077 } 0078 0079 /** 0080 * Returns the rectangle in which the mouse must enter to trigger bar 0081 * sliding. The rectangle is in global coords. 0082 */ 0083 QRect slideInTriggerRect() const 0084 { 0085 const QScreen *screen = QGuiApplication::screenAt(QCursor::pos()); 0086 0087 if (!screen) { 0088 return {}; 0089 } 0090 0091 QRect rect = screen->geometry(); 0092 // Take parent widget position into account because it may not be at 0093 // the top of the screen, for example when the save bar warning is 0094 // shown. 0095 rect.setHeight(q->parentWidget()->y() + q->height() + EXTRA_BAR_HEIGHT); 0096 return rect; 0097 } 0098 0099 bool shouldHide() const 0100 { 0101 Q_ASSERT(q->parentWidget()); 0102 0103 if (!mAutoHidingEnabled) { 0104 return false; 0105 } 0106 if (slideInTriggerRect().contains(QCursor::pos())) { 0107 return false; 0108 } 0109 if (QApplication::activePopupWidget()) { 0110 return false; 0111 } 0112 // Do not hide if a button is down, which can happen when we are 0113 // using a scroll bar. 0114 if (QApplication::mouseButtons() != Qt::NoButton) { 0115 return false; 0116 } 0117 return true; 0118 } 0119 }; 0120 0121 FullScreenBar::FullScreenBar(QWidget *parent) 0122 : QFrame(parent) 0123 , d(new FullScreenBarPrivate) 0124 { 0125 d->q = this; 0126 d->mAutoHidingEnabled = true; 0127 d->mEdgeTriggerEnabled = true; 0128 setObjectName(QStringLiteral("fullScreenBar")); 0129 0130 d->mTimeLine = new QTimeLine(SLIDE_DURATION, this); 0131 connect(d->mTimeLine, &QTimeLine::valueChanged, this, &FullScreenBar::moveBar); 0132 0133 d->mAutoHideCursorTimer = new QTimer(this); 0134 d->mAutoHideCursorTimer->setInterval(AUTO_HIDE_CURSOR_TIMEOUT); 0135 d->mAutoHideCursorTimer->setSingleShot(true); 0136 connect(d->mAutoHideCursorTimer, &QTimer::timeout, this, &FullScreenBar::slotAutoHideCursorTimeout); 0137 0138 d->mInitialHideTimer = new QTimer(this); 0139 d->mInitialHideTimer->setInterval(INITIAL_HIDE_TIMEOUT); 0140 d->mInitialHideTimer->setSingleShot(true); 0141 connect(d->mInitialHideTimer, &QTimer::timeout, this, &FullScreenBar::slideOut); 0142 0143 hide(); 0144 } 0145 0146 FullScreenBar::~FullScreenBar() 0147 { 0148 delete d; 0149 } 0150 0151 QSize FullScreenBar::sizeHint() const 0152 { 0153 QSize sh = QFrame::sizeHint(); 0154 if (!layout()) { 0155 return sh; 0156 } 0157 0158 if (layout()->expandingDirections() & Qt::Horizontal) { 0159 sh.setWidth(parentWidget()->width()); 0160 } 0161 return sh; 0162 } 0163 0164 void FullScreenBar::moveBar(qreal value) 0165 { 0166 move(0, -height() + int(value * height())); 0167 0168 // For some reason, if Gwenview is started with command line options to 0169 // start a slideshow, the bar might end up below the view. Calling raise() 0170 // here fixes it. 0171 raise(); 0172 } 0173 0174 void FullScreenBar::setActivated(bool activated) 0175 { 0176 if (activated) { 0177 // Delay installation of event filter because switching to fullscreen 0178 // cause a few window adjustments, which seems to generate unwanted 0179 // mouse events, which cause the bar to slide in. 0180 QTimer::singleShot(500ms, this, &FullScreenBar::delayedInstallEventFilter); 0181 0182 adjustSize(); 0183 0184 // Make sure the widget is visible on start 0185 move(0, 0); 0186 raise(); 0187 show(); 0188 } else { 0189 qApp->removeEventFilter(this); 0190 hide(); 0191 d->mAutoHideCursorTimer->stop(); 0192 QApplication::restoreOverrideCursor(); 0193 } 0194 } 0195 0196 void FullScreenBar::delayedInstallEventFilter() 0197 { 0198 qApp->installEventFilter(this); 0199 if (d->shouldHide()) { 0200 d->mInitialHideTimer->start(); 0201 d->hideCursor(); 0202 } 0203 } 0204 0205 void FullScreenBar::slotAutoHideCursorTimeout() 0206 { 0207 if (d->shouldHide()) { 0208 d->hideCursor(); 0209 } else { 0210 d->mAutoHideCursorTimer->start(); 0211 } 0212 } 0213 0214 void FullScreenBar::slideOut() 0215 { 0216 d->mInitialHideTimer->stop(); 0217 d->mTimeLine->setDirection(QTimeLine::Backward); 0218 d->startTimeLine(); 0219 } 0220 0221 void FullScreenBar::slideIn() 0222 { 0223 d->mInitialHideTimer->stop(); 0224 d->mTimeLine->setDirection(QTimeLine::Forward); 0225 d->startTimeLine(); 0226 } 0227 0228 bool FullScreenBar::eventFilter(QObject *object, QEvent *event) 0229 { 0230 if (event->type() == QEvent::MouseMove) { 0231 QApplication::restoreOverrideCursor(); 0232 d->mAutoHideCursorTimer->start(); 0233 if (y() == 0) { 0234 if (d->shouldHide()) { 0235 slideOut(); 0236 } 0237 } else { 0238 auto mouseEvent = static_cast<QMouseEvent *>(event); 0239 if (d->mEdgeTriggerEnabled && mouseEvent->buttons() == 0 && d->slideInTriggerRect().contains(QCursor::pos())) { 0240 slideIn(); 0241 } 0242 } 0243 return false; 0244 } 0245 0246 if (event->type() == QEvent::MouseButtonRelease) { 0247 // This can happen if user released the mouse after using a scrollbar 0248 // in the content (the bar does not hide while a button is down) 0249 if (y() == 0 && d->shouldHide()) { 0250 slideOut(); 0251 } 0252 return false; 0253 } 0254 0255 // Filtering message on tooltip text for CJK to remove accelerators. 0256 // Quoting ktoolbar.cpp: 0257 // """ 0258 // CJK languages use more verbose accelerator marker: they add a Latin 0259 // letter in parenthesis, and put accelerator on that. Hence, the default 0260 // removal of ampersand only may not be enough there, instead the whole 0261 // parenthesis construct should be removed. Use KLocale's method to do this. 0262 // """ 0263 if (event->type() == QEvent::Show || event->type() == QEvent::Paint) { 0264 auto button = qobject_cast<QToolButton *>(object); 0265 if (button && !button->actions().isEmpty()) { 0266 QAction *action = button->actions().constFirst(); 0267 const QString toolTip = KLocalizedString::removeAcceleratorMarker(action->toolTip()); 0268 // Filtering message requested by translators (scripting). 0269 button->setToolTip(i18nc("@info:tooltip of custom toolbar button", "%1", toolTip)); 0270 } 0271 } 0272 0273 return false; 0274 } 0275 0276 void FullScreenBar::setAutoHidingEnabled(bool value) 0277 { 0278 d->mAutoHidingEnabled = value; 0279 } 0280 0281 void FullScreenBar::setEdgeTriggerEnabled(bool value) 0282 { 0283 d->mEdgeTriggerEnabled = value; 0284 } 0285 0286 } // namespace 0287 0288 #include "moc_fullscreenbar.cpp"