Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
zoomwidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "zoomwidget_p.h"
5
6#include <QtWidgets/qgraphicsscene.h>
7#include <QtWidgets/qgraphicsproxywidget.h>
8#include <QtWidgets/qmenu.h>
9#include <QtWidgets/qscrollbar.h>
10
11#include <QtGui/qaction.h>
12#include <QtGui/qactiongroup.h>
13#include <QtGui/qevent.h>
14
15#include <QtCore/qtextstream.h>
16#include <QtCore/qmath.h>
17#include <QtCore/qdebug.h>
18#include <QtCore/qlist.h>
19
21
22using namespace Qt::StringLiterals;
23
24enum { debugZoomWidget = 0 };
25
26static const int menuZoomList[] = { 100, 25, 50, 75, 125, 150 , 175, 200 };
27
28namespace qdesigner_internal {
29
30// ---------- ZoomMenu
31
35{
37 for (int zoom : menuZoomList) {
38 //: Zoom factor
40 a->setCheckable(true);
42 if (zoom == 100)
43 a->setChecked(true);
45 }
46}
47
48int ZoomMenu::zoomOf(const QAction *a)
49{
50 return a->data().toInt();
51}
52
54{
55 const auto za = m_menuActions->actions();
56 for (QAction *a : za) {
57 m->addAction(a);
58 if (zoomOf(a) == 100)
59 m->addSeparator();
60 }
61}
62
63int ZoomMenu::zoom() const
64{
66}
67
69{
70 const auto za = m_menuActions->actions();
71 for (QAction *a : za) {
72 if (zoomOf(a) == percent) {
73 a->setChecked(true);
74 return;
75 }
76 }
77}
78
80{
82}
83
85{
86 QList<int> rc;
87 const int nz = sizeof(menuZoomList)/sizeof(int);
88 rc.reserve(nz);
89 for (int i = 0; i < nz; i++)
91 return rc;
92}
93
94// --------- ZoomView
107
108int ZoomView::zoom() const
109{
110 return m_zoom;
111}
112
114{
115 const QPoint origin(0 ,0);
116 const QPoint current = scrollPosition();
117 if (current != origin) {
118 if (debugZoomWidget)
119 qDebug() << "ZoomView::scrollToOrigin from " << current;
121 }
122}
123
125{
126 if (debugZoomWidget)
127 qDebug() << "ZoomView::setZoom" << percent;
128
129 if (m_zoom == percent)
130 return;
131
132 m_zoom = percent;
133 const qreal hundred = 100.0;
134 m_zoomFactor = static_cast<qreal>(m_zoom) / hundred;
135
136 applyZoom();
137 if (m_zoomMenu) // Do not force them into existence
139
142}
143
145{
146}
147
149{
150 return m_zoomFactor;
151}
152
157
162
164{
165 if (!m_zoomMenu) {
166 m_zoomMenu = new ZoomMenu(this);
169 }
170 return m_zoomMenu;
171}
172
174{
175 if (debugZoomWidget > 1)
176 qDebug() << "ZoomView::contextMenuEvent" << event->pos() << event->globalPos() << zoom() << '%';
177
180 } else {
182 }
183}
184
191
196
202
203// -------------- ZoomProxyWidget
208
210{
211 switch (change) {
212 case ItemPositionChange: {
213 const QPointF newPos = value.toPointF();
214 const QPointF desiredPos = QPointF(0, 0);
216 qDebug() << "ZoomProxyWidget::itemChange: refusing " << newPos;
217 return desiredPos;
218 }
219 default:
220 break;
221 }
223}
224
225/* ZoomedEventFilterRedirector: Event filter for the zoomed widget.
226 * It redirects the events to another handler of ZoomWidget as its
227 * base class QScrollArea also implements eventFilter() for its viewport. */
228
229static constexpr auto zoomedEventFilterRedirectorNameC = "__qt_ZoomedEventFilterRedirector"_L1;
230
233
234public:
236 bool eventFilter(QObject *watched, QEvent *event) override;
237
238private:
239 ZoomWidget *m_zw;
240};
241
242ZoomedEventFilterRedirector::ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent) :
243 QObject(parent),
244 m_zw(zw)
245{
246 setObjectName(zoomedEventFilterRedirectorNameC);
247}
248
249bool ZoomedEventFilterRedirector::eventFilter(QObject *watched, QEvent *event)
250{
251 return m_zw->zoomedEventFilter(watched, event);
252}
253
254
255// --------- ZoomWidget
256
263
265{
266 if (debugZoomWidget)
267 qDebug() << "ZoomWidget::setWidget" << w << Qt::bin << wFlags;
268
269 if (m_proxy) {
271 if (QWidget *w = m_proxy->widget()) {
272 // remove the event filter
275 }
277 }
278 // Set window flags on the outer proxy for them to take effect
279 m_proxy = createProxyWidget(nullptr, Qt::Window);
281
285 resizeToWidgetSize(); // Do manually for new widget
286 m_proxy->show();
287}
288
297
299{
300 return QSize(0, 0);
301}
302
304{
307 const QSizeF rc = QSizeF(left + right, top + bottom);
308 return rc;
309}
310
312{
313 if (m_proxy)
314 return m_proxy->widget()->size();
315 return QSize(0, 0);
316}
317
318/* Convert widget size to QGraphicsView size.
319 * Watch out for limits (0, QWIDGETSIZE_MAX); just pass them on */
320
322{
323 const QSize vpMargin = viewPortMargin();
325 const int width = s.width();
326
327 QSize rc = s;
328 bool valid = false;
329 if (width != 0 && width != QWIDGETSIZE_MAX) {
330 valid = true;
331 rc.setWidth(vpMargin.width() + qCeil(deco.width() + zoomFactor() * static_cast<qreal>(width)));
332 }
333
334 const int height = s.height();
335 if (height != 0 && height != QWIDGETSIZE_MAX) {
336 valid = true;
337 rc.setHeight(vpMargin.height() + qCeil(deco.height() + zoomFactor() * static_cast<qreal>(height)));
338 }
339
340 if (ptrToValid)
341 *ptrToValid = valid;
342
343 return rc;
344}
345
346// On changing zoom: Make QGraphicsView big enough to hold the widget
348{
349 if (!m_proxy)
350 return;
351
352 m_viewResizeBlocked = true;
353 // Convert size, apply transformed min/max size if applicable
354 const QSize wsize = widgetSize();
356
357 bool hasMinimumSize = false;
360
361 bool hasMaximumSize = false;
364
365 if (debugZoomWidget) {
366 qDebug()
367 << "ZoomWidget::resizeToWidgetSize()\n"
368 << "Widget: " << wsize << "(scaled)" << (wsize * zoomFactor()) << " Min/Max" << minimumSize << maximumSize << '\n'
370 }
371 // Apply
372 if (hasMinimumSize)
374 if (hasMaximumSize)
376 // now resize
378 if (debugZoomWidget)
379 qDebug() << "ZoomWidget::resizeToWidgetSize(): resulting view size" << size();
380 m_viewResizeBlocked = false;
381}
382
384{
386}
387
388/* virtual */ void ZoomWidget::doResize(const QSize &s)
389{
390 if (debugZoomWidget > 1)
391 qDebug() << ">ZoomWidget::doResize() " << s;
392 resize(s);
393}
394
396{
397 /* QGraphicsView Resized from outside: Adapt widget. For some reason,
398 * the size passed in the event is not to be trusted. This might be due
399 * to some QScrollArea event fiddling. Have QScrollArea resize first
400 * and the use the size ZoomView::resizeEvent(event); */
402 if (debugZoomWidget > 1)
403 qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size();
410 m_widgetResizeBlocked = false;
411 if (debugZoomWidget > 1)
412 qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size();
413 }
414}
415
417{
418 if (!m_proxy)
420
422 const QSize rc = viewPortMargin() + (wmsh * zoomFactor()).toSize();
423 if (debugZoomWidget > 1)
424 qDebug() << "minimumSizeHint()" << rc;
425 return rc;
426}
427
429{
430 if (!m_proxy)
431 return QGraphicsView::sizeHint();
432
433 const QSizeF wsh = m_proxy->widget()->sizeHint();
434 const QSize rc = viewPortMargin() + (wsh * zoomFactor()).toSize();
435 if (debugZoomWidget > 1)
436 qDebug() << "sizeHint()" << rc;
437 return rc;
438}
439
441{
442 switch (event->type()) {
443 case QEvent::KeyPress:
444 if (debugZoomWidget) { // Debug helper: Press 'D' on the zoomed widget
445 const QKeyEvent *kevent = static_cast<QKeyEvent*>(event);
446 if (kevent->key() == Qt::Key_D)
447 dump();
448 }
449 break;
450 case QEvent::Resize:
451 if (debugZoomWidget > 1) {
452 const QResizeEvent *re = static_cast<const QResizeEvent *>(event);
453 qDebug() << "ZoomWidget::zoomedEventFilter" << re->oldSize() << re->size() << " at " << m_proxy->widget()->geometry();
454 }
457
458 break;
459 case QEvent::ContextMenu:
461 // Calculate global position from scaled
464 const QPointF pos = QPointF(origin + (QPointF(ce->pos()) * zoomFactor()));
466 ce->accept();
467 return true;
468 }
469 break;
470 default:
471 break;
472 }
473 return false;
474}
475
477{
478 if (m_proxy)
479 m_proxy->setAcceptDrops(true);
480}
481
483{
484 return m_proxy ? m_proxy->acceptDrops() : false;
485}
486
487 // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget
492
493void ZoomWidget::dump() const
494{
495
496 qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry()
497 << "Scroll: " << scrollPosition() << "Transform: " << transform() << " SceneRect: " << sceneRect();
498 if (m_proxy) {
499 qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size()
500 << "\nProxy size hint"
504 << "\nTransform: " << m_proxy->transform()
505 << "\nWidget: " << m_proxy->widget()->geometry()
506 << "scaled" << (zoomFactor() * m_proxy->widget()->size());
507 }
508}
509
510} // namespace qdesigner_internal
511
512QT_END_NAMESPACE
bool eventFilter(QObject *watched, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static constexpr auto zoomedEventFilterRedirectorNameC
static const int menuZoomList[]
@ debugZoomWidget