1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
4 * Based on the Itemviews NG project from Trolltech Labs *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
22 #include "kitemlistcontainer.h"
24 #include "kitemlistcontroller.h"
25 #include "kitemlistview.h"
26 #include "private/kitemlistsmoothscroller.h"
28 #include <QApplication>
29 #include <QGraphicsScene>
30 #include <QGraphicsView>
32 #include <QStyleOption>
35 * Replaces the default viewport of KItemListContainer by a
36 * non-scrollable viewport. The scrolling is done in an optimized
37 * way by KItemListView internally.
39 class KItemListContainerViewport
: public QGraphicsView
44 KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
);
46 void wheelEvent(QWheelEvent
* event
) override
;
49 KItemListContainerViewport::KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
) :
50 QGraphicsView(scene
, parent
)
52 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
53 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
54 setViewportMargins(0, 0, 0, 0);
55 setFrameShape(QFrame::NoFrame
);
58 void KItemListContainerViewport::wheelEvent(QWheelEvent
* event
)
60 // Assure that the wheel-event gets forwarded to the parent
61 // and not handled at all by QGraphicsView.
65 KItemListContainer::KItemListContainer(KItemListController
* controller
, QWidget
* parent
) :
66 QAbstractScrollArea(parent
),
67 m_controller(controller
),
68 m_horizontalSmoothScroller(nullptr),
69 m_verticalSmoothScroller(nullptr)
72 controller
->setParent(this);
74 QGraphicsView
* graphicsView
= new KItemListContainerViewport(new QGraphicsScene(this), this);
75 setViewport(graphicsView
);
77 m_horizontalSmoothScroller
= new KItemListSmoothScroller(horizontalScrollBar(), this);
78 m_verticalSmoothScroller
= new KItemListSmoothScroller(verticalScrollBar(), this);
80 if (controller
->model()) {
81 slotModelChanged(controller
->model(), nullptr);
83 if (controller
->view()) {
84 slotViewChanged(controller
->view(), nullptr);
87 connect(controller
, &KItemListController::modelChanged
,
88 this, &KItemListContainer::slotModelChanged
);
89 connect(controller
, &KItemListController::viewChanged
,
90 this, &KItemListContainer::slotViewChanged
);
93 KItemListContainer::~KItemListContainer()
95 // Don't rely on the QObject-order to delete the controller, otherwise
96 // the QGraphicsScene might get deleted before the view.
98 m_controller
= nullptr;
101 KItemListController
* KItemListContainer::controller() const
106 void KItemListContainer::setEnabledFrame(bool enable
)
108 QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
110 setFrameShape(QFrame::StyledPanel
);
111 graphicsView
->setPalette(palette());
112 graphicsView
->viewport()->setAutoFillBackground(true);
114 setFrameShape(QFrame::NoFrame
);
115 // Make the background of the container transparent and apply the window-text color
116 // to the text color, so that enough contrast is given for all color
118 QPalette p
= graphicsView
->palette();
119 p
.setColor(QPalette::Active
, QPalette::Text
, p
.color(QPalette::Active
, QPalette::WindowText
));
120 p
.setColor(QPalette::Inactive
, QPalette::Text
, p
.color(QPalette::Inactive
, QPalette::WindowText
));
121 p
.setColor(QPalette::Disabled
, QPalette::Text
, p
.color(QPalette::Disabled
, QPalette::WindowText
));
122 graphicsView
->setPalette(p
);
123 graphicsView
->viewport()->setAutoFillBackground(false);
127 bool KItemListContainer::enabledFrame() const
129 const QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
130 return graphicsView
->autoFillBackground();
133 void KItemListContainer::keyPressEvent(QKeyEvent
* event
)
135 // TODO: We should find a better way to handle the key press events in the view.
136 // The reasons why we need this hack are:
137 // 1. Without reimplementing keyPressEvent() here, the event would not reach the QGraphicsView.
138 // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
139 // simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
141 KItemListView
* view
= m_controller
->view();
143 QApplication::sendEvent(view
, event
);
147 void KItemListContainer::showEvent(QShowEvent
* event
)
149 QAbstractScrollArea::showEvent(event
);
153 void KItemListContainer::resizeEvent(QResizeEvent
* event
)
155 QAbstractScrollArea::resizeEvent(event
);
159 void KItemListContainer::scrollContentsBy(int dx
, int dy
)
161 m_horizontalSmoothScroller
->scrollContentsBy(dx
);
162 m_verticalSmoothScroller
->scrollContentsBy(dy
);
165 void KItemListContainer::wheelEvent(QWheelEvent
* event
)
167 if (event
->modifiers().testFlag(Qt::ControlModifier
)) {
172 KItemListView
* view
= m_controller
->view();
178 const bool scrollHorizontally
= (event
->orientation() == Qt::Horizontal
) ||
179 (event
->orientation() == Qt::Vertical
&& !verticalScrollBar()->isVisible());
180 KItemListSmoothScroller
* smoothScroller
= scrollHorizontally
?
181 m_horizontalSmoothScroller
: m_verticalSmoothScroller
;
183 smoothScroller
->handleWheelEvent(event
);
186 void KItemListContainer::slotScrollOrientationChanged(Qt::Orientation current
, Qt::Orientation previous
)
189 updateSmoothScrollers(current
);
192 void KItemListContainer::slotModelChanged(KItemModelBase
* current
, KItemModelBase
* previous
)
198 void KItemListContainer::slotViewChanged(KItemListView
* current
, KItemListView
* previous
)
200 QGraphicsScene
* scene
= static_cast<QGraphicsView
*>(viewport())->scene();
202 scene
->removeItem(previous
);
203 disconnect(previous
, &KItemListView::scrollOrientationChanged
,
204 this, &KItemListContainer::slotScrollOrientationChanged
);
205 disconnect(previous
, &KItemListView::scrollOffsetChanged
,
206 this, &KItemListContainer::updateScrollOffsetScrollBar
);
207 disconnect(previous
, &KItemListView::maximumScrollOffsetChanged
,
208 this, &KItemListContainer::updateScrollOffsetScrollBar
);
209 disconnect(previous
, &KItemListView::itemOffsetChanged
,
210 this, &KItemListContainer::updateItemOffsetScrollBar
);
211 disconnect(previous
, &KItemListView::maximumItemOffsetChanged
,
212 this, &KItemListContainer::updateItemOffsetScrollBar
);
213 disconnect(previous
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
214 m_horizontalSmoothScroller
->setTargetObject(nullptr);
215 m_verticalSmoothScroller
->setTargetObject(nullptr);
218 scene
->addItem(current
);
219 connect(current
, &KItemListView::scrollOrientationChanged
,
220 this, &KItemListContainer::slotScrollOrientationChanged
);
221 connect(current
, &KItemListView::scrollOffsetChanged
,
222 this, &KItemListContainer::updateScrollOffsetScrollBar
);
223 connect(current
, &KItemListView::maximumScrollOffsetChanged
,
224 this, &KItemListContainer::updateScrollOffsetScrollBar
);
225 connect(current
, &KItemListView::itemOffsetChanged
,
226 this, &KItemListContainer::updateItemOffsetScrollBar
);
227 connect(current
, &KItemListView::maximumItemOffsetChanged
,
228 this, &KItemListContainer::updateItemOffsetScrollBar
);
229 connect(current
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
230 m_horizontalSmoothScroller
->setTargetObject(current
);
231 m_verticalSmoothScroller
->setTargetObject(current
);
232 updateSmoothScrollers(current
->scrollOrientation());
236 void KItemListContainer::scrollTo(qreal offset
)
238 const KItemListView
* view
= m_controller
->view();
240 if (view
->scrollOrientation() == Qt::Vertical
) {
241 m_verticalSmoothScroller
->scrollTo(offset
);
243 m_horizontalSmoothScroller
->scrollTo(offset
);
248 void KItemListContainer::updateScrollOffsetScrollBar()
250 const KItemListView
* view
= m_controller
->view();
255 KItemListSmoothScroller
* smoothScroller
= nullptr;
256 QScrollBar
* scrollOffsetScrollBar
= nullptr;
260 if (view
->scrollOrientation() == Qt::Vertical
) {
261 smoothScroller
= m_verticalSmoothScroller
;
262 scrollOffsetScrollBar
= verticalScrollBar();
263 singleStep
= view
->itemSizeHint().height();
264 // We cannot use view->size().height() because this height might
265 // include the header widget, which is not part of the scrolled area.
266 pageStep
= view
->verticalPageStep();
268 // However, the total height of the view must be considered for the
269 // maximum value of the scroll bar. Note that the view's scrollOffset()
270 // refers to the offset of the top part of the view, which might be
271 // hidden behind the header.
272 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().height()));
274 smoothScroller
= m_horizontalSmoothScroller
;
275 scrollOffsetScrollBar
= horizontalScrollBar();
276 singleStep
= view
->itemSize().width();
277 pageStep
= view
->size().width();
278 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().width()));
281 const int value
= view
->scrollOffset();
282 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
283 const bool updatePolicy
= (scrollOffsetScrollBar
->maximum() > 0 && maximum
== 0)
284 || horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn
;
286 scrollOffsetScrollBar
->setSingleStep(singleStep
);
287 scrollOffsetScrollBar
->setPageStep(pageStep
);
288 scrollOffsetScrollBar
->setMinimum(0);
289 scrollOffsetScrollBar
->setMaximum(maximum
);
290 scrollOffsetScrollBar
->setValue(value
);
293 // Prevent a potential endless layout loop (see bug #293318).
294 updateScrollOffsetScrollBarPolicy();
299 void KItemListContainer::updateItemOffsetScrollBar()
301 const KItemListView
* view
= m_controller
->view();
306 KItemListSmoothScroller
* smoothScroller
= nullptr;
307 QScrollBar
* itemOffsetScrollBar
= nullptr;
310 if (view
->scrollOrientation() == Qt::Vertical
) {
311 smoothScroller
= m_horizontalSmoothScroller
;
312 itemOffsetScrollBar
= horizontalScrollBar();
313 singleStep
= view
->size().width() / 10;
314 pageStep
= view
->size().width();
316 smoothScroller
= m_verticalSmoothScroller
;
317 itemOffsetScrollBar
= verticalScrollBar();
318 singleStep
= view
->size().height() / 10;
319 pageStep
= view
->size().height();
322 const int value
= view
->itemOffset();
323 const int maximum
= qMax(0, int(view
->maximumItemOffset()) - pageStep
);
324 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
325 itemOffsetScrollBar
->setSingleStep(singleStep
);
326 itemOffsetScrollBar
->setPageStep(pageStep
);
327 itemOffsetScrollBar
->setMinimum(0);
328 itemOffsetScrollBar
->setMaximum(maximum
);
329 itemOffsetScrollBar
->setValue(value
);
333 void KItemListContainer::updateGeometries()
335 QRect rect
= geometry();
337 int extra
= frameWidth() * 2;
339 option
.initFrom(this);
340 int scrollbarSpacing
= 0;
341 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents
, &option
, this)) {
342 scrollbarSpacing
= style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing
, &option
, this);
345 const int widthDec
= verticalScrollBar()->isVisible()
346 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
349 const int heightDec
= horizontalScrollBar()->isVisible()
350 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
353 const QRectF
newGeometry(0, 0, rect
.width() - widthDec
,
354 rect
.height() - heightDec
);
355 if (m_controller
->view()->geometry() != newGeometry
) {
356 m_controller
->view()->setGeometry(newGeometry
);
358 // Get the real geometry of the view again since the scrollbars
359 // visibilities and the view geometry may have changed in re-layout.
360 static_cast<KItemListContainerViewport
*>(viewport())->scene()->setSceneRect(m_controller
->view()->geometry());
361 static_cast<KItemListContainerViewport
*>(viewport())->viewport()->setGeometry(m_controller
->view()->geometry().toRect());
363 updateScrollOffsetScrollBar();
364 updateItemOffsetScrollBar();
368 void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation
)
370 if (orientation
== Qt::Vertical
) {
371 m_verticalSmoothScroller
->setPropertyName("scrollOffset");
372 m_horizontalSmoothScroller
->setPropertyName("itemOffset");
374 m_horizontalSmoothScroller
->setPropertyName("scrollOffset");
375 m_verticalSmoothScroller
->setPropertyName("itemOffset");
379 void KItemListContainer::updateScrollOffsetScrollBarPolicy()
381 const KItemListView
* view
= m_controller
->view();
383 const bool vertical
= (view
->scrollOrientation() == Qt::Vertical
);
386 option
.initFrom(this);
387 const int scrollBarInc
= style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this);
389 QSizeF newViewSize
= m_controller
->view()->size();
391 newViewSize
.rwidth() += scrollBarInc
;
393 newViewSize
.rheight() += scrollBarInc
;
396 const Qt::ScrollBarPolicy policy
= view
->scrollBarRequired(newViewSize
)
397 ? Qt::ScrollBarAlwaysOn
: Qt::ScrollBarAsNeeded
;
399 setVerticalScrollBarPolicy(policy
);
401 setHorizontalScrollBarPolicy(policy
);
405 #include "kitemlistcontainer.moc"