1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
4 * Based on the Itemviews NG project from Trolltech Labs: *
5 * http://qt.gitorious.org/qt-labs/itemviews-ng *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
21 ***************************************************************************/
23 #include "kitemlistcontainer.h"
25 #include "kitemlistcontroller.h"
26 #include "kitemlistview.h"
27 #include "kitemmodelbase.h"
29 #include "private/kitemlistsmoothscroller.h"
31 #include <QApplication>
32 #include <QGraphicsScene>
33 #include <QGraphicsView>
36 #include <QStyleOption>
40 * Replaces the default viewport of KItemListContainer by a
41 * non-scrollable viewport. The scrolling is done in an optimized
42 * way by KItemListView internally.
44 class KItemListContainerViewport
: public QGraphicsView
47 KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
);
49 virtual void wheelEvent(QWheelEvent
* event
);
52 KItemListContainerViewport::KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
) :
53 QGraphicsView(scene
, parent
)
55 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
56 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
57 setViewportMargins(0, 0, 0, 0);
58 setFrameShape(QFrame::NoFrame
);
61 void KItemListContainerViewport::wheelEvent(QWheelEvent
* event
)
63 // Assure that the wheel-event gets forwarded to the parent
64 // and not handled at all by QGraphicsView.
68 KItemListContainer::KItemListContainer(KItemListController
* controller
, QWidget
* parent
) :
69 QAbstractScrollArea(parent
),
70 m_controller(controller
),
71 m_horizontalSmoothScroller(0),
72 m_verticalSmoothScroller(0)
75 controller
->setParent(this);
77 QGraphicsView
* graphicsView
= new KItemListContainerViewport(new QGraphicsScene(this), this);
78 setViewport(graphicsView
);
80 m_horizontalSmoothScroller
= new KItemListSmoothScroller(horizontalScrollBar(), this);
81 m_verticalSmoothScroller
= new KItemListSmoothScroller(verticalScrollBar(), this);
83 if (controller
->model()) {
84 slotModelChanged(controller
->model(), 0);
86 if (controller
->view()) {
87 slotViewChanged(controller
->view(), 0);
90 connect(controller
, &KItemListController::modelChanged
,
91 this, &KItemListContainer::slotModelChanged
);
92 connect(controller
, &KItemListController::viewChanged
,
93 this, &KItemListContainer::slotViewChanged
);
96 KItemListContainer::~KItemListContainer()
98 // Don't rely on the QObject-order to delete the controller, otherwise
99 // the QGraphicsScene might get deleted before the view.
104 KItemListController
* KItemListContainer::controller() const
109 void KItemListContainer::setEnabledFrame(bool enable
)
111 QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
113 setFrameShape(QFrame::StyledPanel
);
114 graphicsView
->setPalette(palette());
115 graphicsView
->viewport()->setAutoFillBackground(true);
117 setFrameShape(QFrame::NoFrame
);
118 // Make the background of the container transparent and apply the window-text color
119 // to the text color, so that enough contrast is given for all color
121 QPalette p
= graphicsView
->palette();
122 p
.setColor(QPalette::Active
, QPalette::Text
, p
.color(QPalette::Active
, QPalette::WindowText
));
123 p
.setColor(QPalette::Inactive
, QPalette::Text
, p
.color(QPalette::Inactive
, QPalette::WindowText
));
124 p
.setColor(QPalette::Disabled
, QPalette::Text
, p
.color(QPalette::Disabled
, QPalette::WindowText
));
125 graphicsView
->setPalette(p
);
126 graphicsView
->viewport()->setAutoFillBackground(false);
130 bool KItemListContainer::enabledFrame() const
132 const QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
133 return graphicsView
->autoFillBackground();
136 void KItemListContainer::keyPressEvent(QKeyEvent
* event
)
138 // TODO: We should find a better way to handle the key press events in the view.
139 // The reasons why we need this hack are:
140 // 1. Without reimplementing keyPressEvent() here, the event would not reach the QGraphicsView.
141 // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
142 // simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
144 KItemListView
* view
= m_controller
->view();
146 QApplication::sendEvent(view
, event
);
150 void KItemListContainer::showEvent(QShowEvent
* event
)
152 QAbstractScrollArea::showEvent(event
);
156 void KItemListContainer::resizeEvent(QResizeEvent
* event
)
158 QAbstractScrollArea::resizeEvent(event
);
162 void KItemListContainer::scrollContentsBy(int dx
, int dy
)
164 m_horizontalSmoothScroller
->scrollContentsBy(dx
);
165 m_verticalSmoothScroller
->scrollContentsBy(dy
);
168 void KItemListContainer::wheelEvent(QWheelEvent
* event
)
170 if (event
->modifiers().testFlag(Qt::ControlModifier
)) {
175 KItemListView
* view
= m_controller
->view();
181 const bool scrollHorizontally
= (event
->orientation() == Qt::Horizontal
) ||
182 (event
->orientation() == Qt::Vertical
&& !verticalScrollBar()->isVisible());
183 KItemListSmoothScroller
* smoothScroller
= scrollHorizontally
?
184 m_horizontalSmoothScroller
: m_verticalSmoothScroller
;
186 const QScrollBar
* scrollBar
= smoothScroller
->scrollBar();
187 if (!event
->pixelDelta().isNull()) {
188 const int numPixels
= event
->pixelDelta().y();
189 if (event
->modifiers().testFlag(Qt::ShiftModifier
)) {
190 const int scrollingDirection
= numPixels
> 0 ? 1 : -1;
191 smoothScroller
->scrollTo(scrollBar
->value() - scrollBar
->pageStep() * scrollingDirection
);
193 smoothScroller
->scrollTo(scrollBar
->value() - numPixels
);
196 const int numDegrees
= event
->angleDelta().y() / 8;
197 const int numSteps
= qApp
->wheelScrollLines() * numDegrees
/ 15;
198 if (event
->modifiers().testFlag(Qt::ShiftModifier
)) {
199 const int scrollingDirection
= numSteps
> 0 ? 1 : -1;
200 smoothScroller
->scrollTo(scrollBar
->value() - scrollBar
->pageStep() * scrollingDirection
);
202 smoothScroller
->scrollTo(scrollBar
->value() - numSteps
* scrollBar
->pageStep() / 12);
209 void KItemListContainer::slotScrollOrientationChanged(Qt::Orientation current
, Qt::Orientation previous
)
212 updateSmoothScrollers(current
);
215 void KItemListContainer::slotModelChanged(KItemModelBase
* current
, KItemModelBase
* previous
)
221 void KItemListContainer::slotViewChanged(KItemListView
* current
, KItemListView
* previous
)
223 QGraphicsScene
* scene
= static_cast<QGraphicsView
*>(viewport())->scene();
225 scene
->removeItem(previous
);
226 disconnect(previous
, &KItemListView::scrollOrientationChanged
,
227 this, &KItemListContainer::slotScrollOrientationChanged
);
228 disconnect(previous
, &KItemListView::scrollOffsetChanged
,
229 this, &KItemListContainer::updateScrollOffsetScrollBar
);
230 disconnect(previous
, &KItemListView::maximumScrollOffsetChanged
,
231 this, &KItemListContainer::updateScrollOffsetScrollBar
);
232 disconnect(previous
, &KItemListView::itemOffsetChanged
,
233 this, &KItemListContainer::updateItemOffsetScrollBar
);
234 disconnect(previous
, &KItemListView::maximumItemOffsetChanged
,
235 this, &KItemListContainer::updateItemOffsetScrollBar
);
236 disconnect(previous
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
237 m_horizontalSmoothScroller
->setTargetObject(0);
238 m_verticalSmoothScroller
->setTargetObject(0);
241 scene
->addItem(current
);
242 connect(current
, &KItemListView::scrollOrientationChanged
,
243 this, &KItemListContainer::slotScrollOrientationChanged
);
244 connect(current
, &KItemListView::scrollOffsetChanged
,
245 this, &KItemListContainer::updateScrollOffsetScrollBar
);
246 connect(current
, &KItemListView::maximumScrollOffsetChanged
,
247 this, &KItemListContainer::updateScrollOffsetScrollBar
);
248 connect(current
, &KItemListView::itemOffsetChanged
,
249 this, &KItemListContainer::updateItemOffsetScrollBar
);
250 connect(current
, &KItemListView::maximumItemOffsetChanged
,
251 this, &KItemListContainer::updateItemOffsetScrollBar
);
252 connect(current
, &KItemListView::scrollTo
, this, &KItemListContainer::scrollTo
);
253 m_horizontalSmoothScroller
->setTargetObject(current
);
254 m_verticalSmoothScroller
->setTargetObject(current
);
255 updateSmoothScrollers(current
->scrollOrientation());
259 void KItemListContainer::scrollTo(qreal offset
)
261 const KItemListView
* view
= m_controller
->view();
263 if (view
->scrollOrientation() == Qt::Vertical
) {
264 m_verticalSmoothScroller
->scrollTo(offset
);
266 m_horizontalSmoothScroller
->scrollTo(offset
);
271 void KItemListContainer::updateScrollOffsetScrollBar()
273 const KItemListView
* view
= m_controller
->view();
278 KItemListSmoothScroller
* smoothScroller
= 0;
279 QScrollBar
* scrollOffsetScrollBar
= 0;
283 if (view
->scrollOrientation() == Qt::Vertical
) {
284 smoothScroller
= m_verticalSmoothScroller
;
285 scrollOffsetScrollBar
= verticalScrollBar();
286 singleStep
= view
->itemSize().height();
287 // We cannot use view->size().height() because this height might
288 // include the header widget, which is not part of the scrolled area.
289 pageStep
= view
->verticalPageStep();
291 // However, the total height of the view must be considered for the
292 // maximum value of the scroll bar. Note that the view's scrollOffset()
293 // refers to the offset of the top part of the view, which might be
294 // hidden behind the header.
295 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().height()));
297 smoothScroller
= m_horizontalSmoothScroller
;
298 scrollOffsetScrollBar
= horizontalScrollBar();
299 singleStep
= view
->itemSize().width();
300 pageStep
= view
->size().width();
301 maximum
= qMax(0, int(view
->maximumScrollOffset() - view
->size().width()));
304 const int value
= view
->scrollOffset();
305 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
306 const bool updatePolicy
= (scrollOffsetScrollBar
->maximum() > 0 && maximum
== 0)
307 || horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn
;
309 scrollOffsetScrollBar
->setSingleStep(singleStep
);
310 scrollOffsetScrollBar
->setPageStep(pageStep
);
311 scrollOffsetScrollBar
->setMinimum(0);
312 scrollOffsetScrollBar
->setMaximum(maximum
);
313 scrollOffsetScrollBar
->setValue(value
);
316 // Prevent a potential endless layout loop (see bug #293318).
317 updateScrollOffsetScrollBarPolicy();
322 void KItemListContainer::updateItemOffsetScrollBar()
324 const KItemListView
* view
= m_controller
->view();
329 KItemListSmoothScroller
* smoothScroller
= 0;
330 QScrollBar
* itemOffsetScrollBar
= 0;
333 if (view
->scrollOrientation() == Qt::Vertical
) {
334 smoothScroller
= m_horizontalSmoothScroller
;
335 itemOffsetScrollBar
= horizontalScrollBar();
336 singleStep
= view
->size().width() / 10;
337 pageStep
= view
->size().width();
339 smoothScroller
= m_verticalSmoothScroller
;
340 itemOffsetScrollBar
= verticalScrollBar();
341 singleStep
= view
->size().height() / 10;
342 pageStep
= view
->size().height();
345 const int value
= view
->itemOffset();
346 const int maximum
= qMax(0, int(view
->maximumItemOffset()) - pageStep
);
347 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
348 itemOffsetScrollBar
->setSingleStep(singleStep
);
349 itemOffsetScrollBar
->setPageStep(pageStep
);
350 itemOffsetScrollBar
->setMinimum(0);
351 itemOffsetScrollBar
->setMaximum(maximum
);
352 itemOffsetScrollBar
->setValue(value
);
356 void KItemListContainer::updateGeometries()
358 QRect rect
= geometry();
360 int extra
= frameWidth() * 2;
362 option
.initFrom(this);
363 int scrollbarSpacing
= 0;
364 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents
, &option
, this)) {
365 scrollbarSpacing
= style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing
, &option
, this);
368 const int widthDec
= verticalScrollBar()->isVisible()
369 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
372 const int heightDec
= horizontalScrollBar()->isVisible()
373 ? extra
+ scrollbarSpacing
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
376 const QRectF
newGeometry(0, 0, rect
.width() - widthDec
,
377 rect
.height() - heightDec
);
378 if (m_controller
->view()->geometry() != newGeometry
) {
379 m_controller
->view()->setGeometry(newGeometry
);
381 // Get the real geometry of the view again since the scrollbars
382 // visibilities and the view geometry may have changed in re-layout.
383 static_cast<KItemListContainerViewport
*>(viewport())->scene()->setSceneRect(m_controller
->view()->geometry());
384 static_cast<KItemListContainerViewport
*>(viewport())->viewport()->setGeometry(m_controller
->view()->geometry().toRect());
386 updateScrollOffsetScrollBar();
387 updateItemOffsetScrollBar();
391 void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation
)
393 if (orientation
== Qt::Vertical
) {
394 m_verticalSmoothScroller
->setPropertyName("scrollOffset");
395 m_horizontalSmoothScroller
->setPropertyName("itemOffset");
397 m_horizontalSmoothScroller
->setPropertyName("scrollOffset");
398 m_verticalSmoothScroller
->setPropertyName("itemOffset");
402 void KItemListContainer::updateScrollOffsetScrollBarPolicy()
404 const KItemListView
* view
= m_controller
->view();
406 const bool vertical
= (view
->scrollOrientation() == Qt::Vertical
);
409 option
.initFrom(this);
410 const int scrollBarInc
= style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this);
412 QSizeF newViewSize
= m_controller
->view()->size();
414 newViewSize
.rwidth() += scrollBarInc
;
416 newViewSize
.rheight() += scrollBarInc
;
419 const Qt::ScrollBarPolicy policy
= view
->scrollBarRequired(newViewSize
)
420 ? Qt::ScrollBarAlwaysOn
: Qt::ScrollBarAsNeeded
;
422 setVerticalScrollBarPolicy(policy
);
424 setHorizontalScrollBarPolicy(policy
);