2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
4 * Based on the Itemviews NG project from Trolltech Labs
6 * SPDX-License-Identifier: GPL-2.0-or-later
9 #include "kitemlistcontainer.h"
11 #include "kitemlistcontroller.h"
12 #include "kitemlistview.h"
13 #include "private/kitemlistsmoothscroller.h"
15 #include <QApplication>
16 #include <QFontMetrics>
17 #include <QGraphicsScene>
18 #include <QGraphicsView>
20 #include <QStyleOption>
23 * Replaces the default viewport of KItemListContainer by a
24 * non-scrollable viewport. The scrolling is done in an optimized
25 * way by KItemListView internally.
27 class KItemListContainerViewport
: public QGraphicsView
32 KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
);
34 void wheelEvent(QWheelEvent
* event
) override
;
37 KItemListContainerViewport::KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
) :
38 QGraphicsView(scene
, parent
)
40 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
41 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
42 setViewportMargins(0, 0, 0, 0);
43 setFrameShape(QFrame::NoFrame
);
46 void KItemListContainerViewport::wheelEvent(QWheelEvent
* event
)
48 // Assure that the wheel-event gets forwarded to the parent
49 // and not handled at all by QGraphicsView.
53 KItemListContainer::KItemListContainer(KItemListController
* controller
, QWidget
* parent
) :
54 QAbstractScrollArea(parent
),
55 m_controller(controller
),
56 m_horizontalSmoothScroller(nullptr),
57 m_verticalSmoothScroller(nullptr)
60 controller
->setParent(this);
62 QGraphicsView
* graphicsView
= new KItemListContainerViewport(new QGraphicsScene(this), this);
63 setViewport(graphicsView
);
65 m_horizontalSmoothScroller
= new KItemListSmoothScroller(horizontalScrollBar(), this);
66 m_verticalSmoothScroller
= new KItemListSmoothScroller(verticalScrollBar(), this);
68 if (controller
->model()) {
69 slotModelChanged(controller
->model(), nullptr);
71 if (controller
->view()) {
72 slotViewChanged(controller
->view(), nullptr);
75 connect(controller
, &KItemListController::modelChanged
,
76 this, &KItemListContainer::slotModelChanged
);
77 connect(controller
, &KItemListController::viewChanged
,
78 this, &KItemListContainer::slotViewChanged
);
81 KItemListContainer::~KItemListContainer()
83 // Don't rely on the QObject-order to delete the controller, otherwise
84 // the QGraphicsScene might get deleted before the view.
86 m_controller
= nullptr;
89 KItemListController
* KItemListContainer::controller() const
94 void KItemListContainer::setEnabledFrame(bool enable
)
96 QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
98 setFrameShape(QFrame::StyledPanel
);
99 graphicsView
->setPalette(palette());
100 graphicsView
->viewport()->setAutoFillBackground(true);
102 setFrameShape(QFrame::NoFrame
);
103 // Make the background of the container transparent and apply the window-text color
104 // to the text color, so that enough contrast is given for all color
106 QPalette p
= graphicsView
->palette();
107 p
.setColor(QPalette::Active
, QPalette::Text
, p
.color(QPalette::Active
, QPalette::WindowText
));
108 p
.setColor(QPalette::Inactive
, QPalette::Text
, p
.color(QPalette::Inactive
, QPalette::WindowText
));
109 p
.setColor(QPalette::Disabled
, QPalette::Text
, p
.color(QPalette::Disabled
, QPalette::WindowText
));
110 graphicsView
->setPalette(p
);
111 graphicsView
->viewport()->setAutoFillBackground(false);
115 bool KItemListContainer::enabledFrame() const
117 const QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
118 return graphicsView
->autoFillBackground();
121 void KItemListContainer::keyPressEvent(QKeyEvent
* event
)
123 // TODO: We should find a better way to handle the key press events in the view.
124 // The reasons why we need this hack are:
125 // 1. Without reimplementing keyPressEvent() here, the event would not reach the QGraphicsView.
126 // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
127 // simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
129 KItemListView
* view
= m_controller
->view();
131 QApplication::sendEvent(view
, event
);
135 void KItemListContainer::showEvent(QShowEvent
* event
)
137 QAbstractScrollArea::showEvent(event
);
141 void KItemListContainer::resizeEvent(QResizeEvent
* event
)
143 QAbstractScrollArea::resizeEvent(event
);
147 void KItemListContainer::scrollContentsBy(int dx
, int dy
)
149 m_horizontalSmoothScroller
->scrollContentsBy(dx
);
150 m_verticalSmoothScroller
->scrollContentsBy(dy
);
153 void KItemListContainer::wheelEvent(QWheelEvent
* event
)
155 if (event
->modifiers().testFlag(Qt::ControlModifier
)) {
160 KItemListView
* view
= m_controller
->view();
166 const bool scrollHorizontally
= (event
->angleDelta().x() != 0) ||
167 (event
->angleDelta().y() != 0 && !verticalScrollBar()->isVisible());
168 KItemListSmoothScroller
* smoothScroller
= scrollHorizontally
?
169 m_horizontalSmoothScroller
: m_verticalSmoothScroller
;
171 smoothScroller
->handleWheelEvent(event
);
174 void KItemListContainer::slotScrollOrientationChanged(Qt::Orientation current
, Qt::Orientation previous
)
177 updateSmoothScrollers(current
);
180 void KItemListContainer::slotModelChanged(KItemModelBase
* current
, KItemModelBase
* previous
)
186 void KItemListContainer::slotViewChanged(KItemListView
* current
, KItemListView
* previous
)
188 QGraphicsScene
* scene
= static_cast<QGraphicsView
*>(viewport())->scene();
190 scene
->removeItem(previous
);
191 disconnect(previous
, &KItemListView::scrollOrientationChanged
,
192 this, &KItemListContainer::slotScrollOrientationChanged
);
193 disconnect(previous
, &KItemListView::scrollOffsetChanged
,
194 this, &KItemListContainer::updateScrollOffsetScrollBar
);
195 disconnect(previous
, &KItemListView::maximumScrollOffsetChanged
,
196 this, &KItemListContainer::updateScrollOffsetScrollBar
);
197 disconnect(previous
, &KItemListView::itemOffsetChanged
,
198 this, &KItemListContainer::updateItemOffsetScrollBar
);
199 disconnect(previous
, &KItemListView::maximumItemOffsetChanged
,
200 this, &KItemListContainer::updateItemOffsetScrollBar
);
201 disconnect(previous
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
202 m_horizontalSmoothScroller
->setTargetObject(nullptr);
203 m_verticalSmoothScroller
->setTargetObject(nullptr);
206 scene
->addItem(current
);
207 connect(current
, &KItemListView::scrollOrientationChanged
,
208 this, &KItemListContainer::slotScrollOrientationChanged
);
209 connect(current
, &KItemListView::scrollOffsetChanged
,
210 this, &KItemListContainer::updateScrollOffsetScrollBar
);
211 connect(current
, &KItemListView::maximumScrollOffsetChanged
,
212 this, &KItemListContainer::updateScrollOffsetScrollBar
);
213 connect(current
, &KItemListView::itemOffsetChanged
,
214 this, &KItemListContainer::updateItemOffsetScrollBar
);
215 connect(current
, &KItemListView::maximumItemOffsetChanged
,
216 this, &KItemListContainer::updateItemOffsetScrollBar
);
217 connect(current
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
218 m_horizontalSmoothScroller
->setTargetObject(current
);
219 m_verticalSmoothScroller
->setTargetObject(current
);
220 updateSmoothScrollers(current
->scrollOrientation());
224 void KItemListContainer::scrollTo(qreal offset
)
226 const KItemListView
* view
= m_controller
->view();
228 if (view
->scrollOrientation() == Qt::Vertical
) {
229 m_verticalSmoothScroller
->scrollTo(offset
);
231 m_horizontalSmoothScroller
->scrollTo(offset
);
236 void KItemListContainer::updateScrollOffsetScrollBar()
238 const KItemListView
* view
= m_controller
->view();
243 KItemListSmoothScroller
* smoothScroller
= nullptr;
244 QScrollBar
* scrollOffsetScrollBar
= nullptr;
248 if (view
->scrollOrientation() == Qt::Vertical
) {
249 smoothScroller
= m_verticalSmoothScroller
;
250 scrollOffsetScrollBar
= verticalScrollBar();
252 // Don't scroll super fast when using a wheel mouse:
253 // We want to consider one "line" to be the text label which has a
254 // roughly fixed height rather than using the height of the icon which
256 const QFontMetrics
metrics(font());
257 singleStep
= metrics
.height() * QApplication::wheelScrollLines();
259 // We cannot use view->size().height() because this height might
260 // include the header widget, which is not part of the scrolled area.
261 pageStep
= view
->verticalPageStep();
263 // However, the total height of the view must be considered for the
264 // maximum value of the scroll bar. Note that the view's scrollOffset()
265 // refers to the offset of the top part of the view, which might be
266 // hidden behind the header.
267 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().height()));
269 smoothScroller
= m_horizontalSmoothScroller
;
270 scrollOffsetScrollBar
= horizontalScrollBar();
271 singleStep
= view
->itemSize().width();
272 pageStep
= view
->size().width();
273 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().width()));
276 const int value
= view
->scrollOffset();
277 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
278 const bool updatePolicy
= (scrollOffsetScrollBar
->maximum() > 0 && maximum
== 0)
279 || horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn
;
281 scrollOffsetScrollBar
->setSingleStep(singleStep
);
282 scrollOffsetScrollBar
->setPageStep(pageStep
);
283 scrollOffsetScrollBar
->setMinimum(0);
284 scrollOffsetScrollBar
->setMaximum(maximum
);
285 scrollOffsetScrollBar
->setValue(value
);
288 // Prevent a potential endless layout loop (see bug #293318).
289 updateScrollOffsetScrollBarPolicy();
294 void KItemListContainer::updateItemOffsetScrollBar()
296 const KItemListView
* view
= m_controller
->view();
301 KItemListSmoothScroller
* smoothScroller
= nullptr;
302 QScrollBar
* itemOffsetScrollBar
= nullptr;
305 if (view
->scrollOrientation() == Qt::Vertical
) {
306 smoothScroller
= m_horizontalSmoothScroller
;
307 itemOffsetScrollBar
= horizontalScrollBar();
308 singleStep
= view
->size().width() / 10;
309 pageStep
= view
->size().width();
311 smoothScroller
= m_verticalSmoothScroller
;
312 itemOffsetScrollBar
= verticalScrollBar();
313 singleStep
= view
->size().height() / 10;
314 pageStep
= view
->size().height();
317 const int value
= view
->itemOffset();
318 const int maximum
= qMax(0, int(view
->maximumItemOffset()) - pageStep
);
319 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
320 itemOffsetScrollBar
->setSingleStep(singleStep
);
321 itemOffsetScrollBar
->setPageStep(pageStep
);
322 itemOffsetScrollBar
->setMinimum(0);
323 itemOffsetScrollBar
->setMaximum(maximum
);
324 itemOffsetScrollBar
->setValue(value
);
328 void KItemListContainer::updateGeometries()
330 QRect rect
= geometry();
332 int extra
= frameWidth() * 2;
334 option
.initFrom(this);
335 int scrollbarSpacing
= 0;
336 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents
, &option
, this)) {
337 scrollbarSpacing
= style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing
, &option
, this);
340 const int widthDec
= verticalScrollBar()->isVisible()
341 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
344 const int heightDec
= horizontalScrollBar()->isVisible()
345 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
348 const QRectF
newGeometry(0, 0, rect
.width() - widthDec
,
349 rect
.height() - heightDec
);
350 if (m_controller
->view()->geometry() != newGeometry
) {
351 m_controller
->view()->setGeometry(newGeometry
);
353 // Get the real geometry of the view again since the scrollbars
354 // visibilities and the view geometry may have changed in re-layout.
355 static_cast<KItemListContainerViewport
*>(viewport())->scene()->setSceneRect(m_controller
->view()->geometry());
356 static_cast<KItemListContainerViewport
*>(viewport())->viewport()->setGeometry(m_controller
->view()->geometry().toRect());
358 updateScrollOffsetScrollBar();
359 updateItemOffsetScrollBar();
363 void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation
)
365 if (orientation
== Qt::Vertical
) {
366 m_verticalSmoothScroller
->setPropertyName("scrollOffset");
367 m_horizontalSmoothScroller
->setPropertyName("itemOffset");
369 m_horizontalSmoothScroller
->setPropertyName("scrollOffset");
370 m_verticalSmoothScroller
->setPropertyName("itemOffset");
374 void KItemListContainer::updateScrollOffsetScrollBarPolicy()
376 const KItemListView
* view
= m_controller
->view();
378 const bool vertical
= (view
->scrollOrientation() == Qt::Vertical
);
381 option
.initFrom(this);
382 const int scrollBarInc
= style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this);
384 QSizeF newViewSize
= m_controller
->view()->size();
386 newViewSize
.rwidth() += scrollBarInc
;
388 newViewSize
.rheight() += scrollBarInc
;
391 const Qt::ScrollBarPolicy policy
= view
->scrollBarRequired(newViewSize
)
392 ? Qt::ScrollBarAlwaysOn
: Qt::ScrollBarAsNeeded
;
394 setVerticalScrollBarPolicy(policy
);
396 setHorizontalScrollBarPolicy(policy
);
400 #include "kitemlistcontainer.moc"