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 "kitemlistselectionmanager.h"
27 #include "kitemlistview.h"
28 #include "kitemmodelbase.h"
30 #include "private/kitemlistsmoothscroller.h"
32 #include <QApplication>
33 #include <QGraphicsScene>
34 #include <QGraphicsView>
35 #include <QPropertyAnimation>
38 #include <QStyleOption>
42 #include "kitemlistviewaccessible.h"
45 * Replaces the default viewport of KItemListContainer by a
46 * non-scrollable viewport. The scrolling is done in an optimized
47 * way by KItemListView internally.
49 class KItemListContainerViewport
: public QGraphicsView
52 KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
);
54 virtual void wheelEvent(QWheelEvent
* event
);
57 KItemListContainerViewport::KItemListContainerViewport(QGraphicsScene
* scene
, QWidget
* parent
) :
58 QGraphicsView(scene
, parent
)
60 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
61 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
62 setViewportMargins(0, 0, 0, 0);
63 setFrameShape(QFrame::NoFrame
);
66 void KItemListContainerViewport::wheelEvent(QWheelEvent
* event
)
68 // Assure that the wheel-event gets forwarded to the parent
69 // and not handled at all by QGraphicsView.
73 QAccessibleInterface
* accessibleContainerFactory(const QString
&key
, QObject
*object
)
76 if (KItemListContainer
*view
= qobject_cast
<KItemListContainer
*>(object
))
77 return new KItemListContainerAccessible(view
);
78 if (KItemListView
*view
= qobject_cast
<KItemListView
*>(object
))
79 return new KItemListViewAccessible(view
);
83 KItemListContainer::KItemListContainer(KItemListController
* controller
, QWidget
* parent
) :
84 QAbstractScrollArea(parent
),
85 m_controller(controller
),
86 m_horizontalSmoothScroller(0),
87 m_verticalSmoothScroller(0)
90 controller
->setParent(this);
92 QGraphicsView
* graphicsView
= new KItemListContainerViewport(new QGraphicsScene(this), this);
93 setViewport(graphicsView
);
95 m_horizontalSmoothScroller
= new KItemListSmoothScroller(horizontalScrollBar(), this);
96 m_verticalSmoothScroller
= new KItemListSmoothScroller(verticalScrollBar(), this);
98 if (controller
->model()) {
99 slotModelChanged(controller
->model(), 0);
101 if (controller
->view()) {
102 slotViewChanged(controller
->view(), 0);
105 connect(controller
, SIGNAL(modelChanged(KItemModelBase
*,KItemModelBase
*)),
106 this, SLOT(slotModelChanged(KItemModelBase
*,KItemModelBase
*)));
107 connect(controller
, SIGNAL(viewChanged(KItemListView
*,KItemListView
*)),
108 this, SLOT(slotViewChanged(KItemListView
*,KItemListView
*)));
110 #ifndef QT_NO_ACCESSIBILITY
111 QAccessible::installFactory(accessibleContainerFactory
);
115 KItemListContainer::~KItemListContainer()
117 // Don't rely on the QObject-order to delete the controller, otherwise
118 // the QGraphicsScene might get deleted before the view.
121 #ifndef QT_NO_ACCESSIBIILTY
122 QAccessible::removeFactory(accessibleContainerFactory
);
126 KItemListController
* KItemListContainer::controller() const
131 void KItemListContainer::setEnabledFrame(bool enable
)
133 QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
135 setFrameShape(QFrame::StyledPanel
);
136 graphicsView
->setPalette(palette());
137 graphicsView
->viewport()->setAutoFillBackground(true);
139 setFrameShape(QFrame::NoFrame
);
140 // Make the background of the container transparent and apply the window-text color
141 // to the text color, so that enough contrast is given for all color
143 QPalette p
= graphicsView
->palette();
144 p
.setColor(QPalette::Active
, QPalette::Text
, p
.color(QPalette::Active
, QPalette::WindowText
));
145 p
.setColor(QPalette::Inactive
, QPalette::Text
, p
.color(QPalette::Inactive
, QPalette::WindowText
));
146 p
.setColor(QPalette::Disabled
, QPalette::Text
, p
.color(QPalette::Disabled
, QPalette::WindowText
));
147 graphicsView
->setPalette(p
);
148 graphicsView
->viewport()->setAutoFillBackground(false);
152 bool KItemListContainer::enabledFrame() const
154 const QGraphicsView
* graphicsView
= qobject_cast
<QGraphicsView
*>(viewport());
155 return graphicsView
->autoFillBackground();
158 void KItemListContainer::keyPressEvent(QKeyEvent
* event
)
160 // TODO: We should find a better way to handle the key press events in the view.
161 // The reasons why we need this hack are:
162 // 1. Without reimplementing keyPressEvent() here, the event would not reach the QGraphicsView.
163 // 2. By default, the KItemListView does not have the keyboard focus in the QGraphicsScene, so
164 // simply sending the event to the QGraphicsView which is the KItemListContainer's viewport
166 KItemListView
* view
= m_controller
->view();
168 QApplication::sendEvent(view
, event
);
170 QAccessible::updateAccessibility(view
, m_controller
->selectionManager()->currentItem()+1, QAccessible::Focus
);
171 QAccessible::updateAccessibility(view
, m_controller
->selectionManager()->currentItem()+1, QAccessible::LocationChanged
);
174 void KItemListContainer::showEvent(QShowEvent
* event
)
176 QAbstractScrollArea::showEvent(event
);
180 void KItemListContainer::resizeEvent(QResizeEvent
* event
)
182 QAbstractScrollArea::resizeEvent(event
);
186 void KItemListContainer::scrollContentsBy(int dx
, int dy
)
188 m_horizontalSmoothScroller
->scrollContentsBy(dx
);
189 m_verticalSmoothScroller
->scrollContentsBy(dy
);
190 QAccessible::updateAccessibility(m_controller
->view(), m_controller
->selectionManager()->currentItem()+1, QAccessible::Focus
);
191 QAccessible::updateAccessibility(m_controller
->view(), m_controller
->selectionManager()->currentItem()+1, QAccessible::LocationChanged
);
194 void KItemListContainer::wheelEvent(QWheelEvent
* event
)
196 if (event
->modifiers().testFlag(Qt::ControlModifier
)) {
201 KItemListView
* view
= m_controller
->view();
207 const bool scrollHorizontally
= (event
->orientation() == Qt::Horizontal
) ||
208 (event
->orientation() == Qt::Vertical
&& !verticalScrollBar()->isVisible());
209 KItemListSmoothScroller
* smoothScroller
= scrollHorizontally
?
210 m_horizontalSmoothScroller
: m_verticalSmoothScroller
;
212 const int numDegrees
= event
->delta() / 8;
213 const int numSteps
= numDegrees
/ 15;
215 const QScrollBar
* scrollBar
= smoothScroller
->scrollBar();
216 smoothScroller
->scrollTo(scrollBar
->value() - numSteps
* scrollBar
->pageStep() / 4);
221 void KItemListContainer::slotScrollOrientationChanged(Qt::Orientation current
, Qt::Orientation previous
)
224 updateSmoothScrollers(current
);
227 void KItemListContainer::slotModelChanged(KItemModelBase
* current
, KItemModelBase
* previous
)
233 void KItemListContainer::slotViewChanged(KItemListView
* current
, KItemListView
* previous
)
235 QGraphicsScene
* scene
= static_cast<QGraphicsView
*>(viewport())->scene();
237 scene
->removeItem(previous
);
238 disconnect(previous
, SIGNAL(scrollOrientationChanged(Qt::Orientation
,Qt::Orientation
)), this, SLOT(slotScrollOrientationChanged(Qt::Orientation
,Qt::Orientation
)));
239 disconnect(previous
, SIGNAL(scrollOffsetChanged(qreal
,qreal
)), this, SLOT(updateScrollOffsetScrollBar()));
240 disconnect(previous
, SIGNAL(maximumScrollOffsetChanged(qreal
,qreal
)), this, SLOT(updateScrollOffsetScrollBar()));
241 disconnect(previous
, SIGNAL(itemOffsetChanged(qreal
,qreal
)), this, SLOT(updateItemOffsetScrollBar()));
242 disconnect(previous
, SIGNAL(maximumItemOffsetChanged(qreal
,qreal
)), this, SLOT(updateItemOffsetScrollBar()));
243 disconnect(previous
, SIGNAL(scrollTo(qreal
)), this, SLOT(scrollTo(qreal
)));
244 m_horizontalSmoothScroller
->setTargetObject(0);
245 m_verticalSmoothScroller
->setTargetObject(0);
248 scene
->addItem(current
);
249 connect(current
, SIGNAL(scrollOrientationChanged(Qt::Orientation
,Qt::Orientation
)), this, SLOT(slotScrollOrientationChanged(Qt::Orientation
,Qt::Orientation
)));
250 connect(current
, SIGNAL(scrollOffsetChanged(qreal
,qreal
)), this, SLOT(updateScrollOffsetScrollBar()));
251 connect(current
, SIGNAL(maximumScrollOffsetChanged(qreal
,qreal
)), this, SLOT(updateScrollOffsetScrollBar()));
252 connect(current
, SIGNAL(itemOffsetChanged(qreal
,qreal
)), this, SLOT(updateItemOffsetScrollBar()));
253 connect(current
, SIGNAL(maximumItemOffsetChanged(qreal
,qreal
)), this, SLOT(updateItemOffsetScrollBar()));
254 connect(current
, SIGNAL(scrollTo(qreal
)), this, SLOT(scrollTo(qreal
)));
255 m_horizontalSmoothScroller
->setTargetObject(current
);
256 m_verticalSmoothScroller
->setTargetObject(current
);
257 updateSmoothScrollers(current
->scrollOrientation());
261 void KItemListContainer::scrollTo(qreal offset
)
263 const KItemListView
* view
= m_controller
->view();
265 if (view
->scrollOrientation() == Qt::Vertical
) {
266 m_verticalSmoothScroller
->scrollTo(offset
);
268 m_horizontalSmoothScroller
->scrollTo(offset
);
273 void KItemListContainer::updateScrollOffsetScrollBar()
275 const KItemListView
* view
= m_controller
->view();
280 KItemListSmoothScroller
* smoothScroller
= 0;
281 QScrollBar
* scrollOffsetScrollBar
= 0;
284 if (view
->scrollOrientation() == Qt::Vertical
) {
285 smoothScroller
= m_verticalSmoothScroller
;
286 scrollOffsetScrollBar
= verticalScrollBar();
287 singleStep
= view
->itemSize().height();
288 pageStep
= view
->size().height();
290 smoothScroller
= m_horizontalSmoothScroller
;
291 scrollOffsetScrollBar
= horizontalScrollBar();
292 singleStep
= view
->itemSize().width();
293 pageStep
= view
->size().width();
296 const int value
= view
->scrollOffset();
297 const int maximum
= qMax(0, int(view
->maximumScrollOffset() - pageStep
));
298 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
299 const bool updatePolicy
= (scrollOffsetScrollBar
->maximum() > 0 && maximum
== 0)
300 || horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn
;
302 scrollOffsetScrollBar
->setSingleStep(singleStep
);
303 scrollOffsetScrollBar
->setPageStep(pageStep
);
304 scrollOffsetScrollBar
->setMinimum(0);
305 scrollOffsetScrollBar
->setMaximum(maximum
);
306 scrollOffsetScrollBar
->setValue(value
);
309 // Prevent a potential endless layout loop (see bug #293318).
310 updateScrollOffsetScrollBarPolicy();
315 void KItemListContainer::updateItemOffsetScrollBar()
317 const KItemListView
* view
= m_controller
->view();
322 KItemListSmoothScroller
* smoothScroller
= 0;
323 QScrollBar
* itemOffsetScrollBar
= 0;
326 if (view
->scrollOrientation() == Qt::Vertical
) {
327 smoothScroller
= m_horizontalSmoothScroller
;
328 itemOffsetScrollBar
= horizontalScrollBar();
329 singleStep
= view
->size().width() / 10;
330 pageStep
= view
->size().width();
332 smoothScroller
= m_verticalSmoothScroller
;
333 itemOffsetScrollBar
= verticalScrollBar();
334 singleStep
= view
->size().height() / 10;
335 pageStep
= view
->size().height();
338 const int value
= view
->itemOffset();
339 const int maximum
= qMax(0, int(view
->maximumItemOffset()) - pageStep
);
340 if (smoothScroller
->requestScrollBarUpdate(maximum
)) {
341 itemOffsetScrollBar
->setSingleStep(singleStep
);
342 itemOffsetScrollBar
->setPageStep(pageStep
);
343 itemOffsetScrollBar
->setMinimum(0);
344 itemOffsetScrollBar
->setMaximum(maximum
);
345 itemOffsetScrollBar
->setValue(value
);
349 void KItemListContainer::updateGeometries()
351 QRect rect
= geometry();
353 int extra
= frameWidth() * 2;
355 option
.initFrom(this);
356 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents
, &option
, this)) {
357 extra
+= style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing
, &option
, this);
360 const int widthDec
= verticalScrollBar()->isVisible()
361 ? extra
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
364 const int heightDec
= horizontalScrollBar()->isVisible()
365 ? extra
+ style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this)
368 rect
.adjust(0, 0, -widthDec
, -heightDec
);
370 const QRectF
newGeometry(0, 0, rect
.width(), rect
.height());
371 if (m_controller
->view()->geometry() != newGeometry
) {
372 m_controller
->view()->setGeometry(newGeometry
);
374 static_cast<KItemListContainerViewport
*>(viewport())->scene()->setSceneRect(0, 0, rect
.width(), rect
.height());
375 static_cast<KItemListContainerViewport
*>(viewport())->viewport()->setGeometry(QRect(0, 0, rect
.width(), rect
.height()));
377 updateScrollOffsetScrollBar();
378 updateItemOffsetScrollBar();
379 QAccessible::updateAccessibility(m_controller
->view(), 0, QAccessible::LocationChanged
);
380 QAccessible::updateAccessibility(m_controller
->view(), m_controller
->selectionManager()->currentItem()+1, QAccessible::LocationChanged
);
381 QAccessible::updateAccessibility(m_controller
->view(), m_controller
->selectionManager()->currentItem()+1, QAccessible::Focus
);
385 void KItemListContainer::updateSmoothScrollers(Qt::Orientation orientation
)
387 if (orientation
== Qt::Vertical
) {
388 m_verticalSmoothScroller
->setPropertyName("scrollOffset");
389 m_horizontalSmoothScroller
->setPropertyName("itemOffset");
391 m_horizontalSmoothScroller
->setPropertyName("scrollOffset");
392 m_verticalSmoothScroller
->setPropertyName("itemOffset");
396 void KItemListContainer::updateScrollOffsetScrollBarPolicy()
398 const KItemListView
* view
= m_controller
->view();
400 const bool vertical
= (view
->scrollOrientation() == Qt::Vertical
);
403 option
.initFrom(this);
404 const int scrollBarInc
= style()->pixelMetric(QStyle::PM_ScrollBarExtent
, &option
, this);
406 QSizeF newViewSize
= m_controller
->view()->size();
408 newViewSize
.rwidth() += scrollBarInc
;
410 newViewSize
.rheight() += scrollBarInc
;
413 const Qt::ScrollBarPolicy policy
= view
->scrollBarRequired(newViewSize
)
414 ? Qt::ScrollBarAlwaysOn
: Qt::ScrollBarAsNeeded
;
416 setVerticalScrollBarPolicy(policy
);
418 setHorizontalScrollBarPolicy(policy
);
422 #include "kitemlistcontainer.moc"