kitemviews/kitemlistcontainer.cpp
kitemviews/kitemlistcontroller.cpp
kitemviews/kitemlistgroupheader.cpp
+ kitemviews/kitemlistrubberband.cpp
kitemviews/kitemlistselectionmanager.cpp
kitemviews/kitemlistsizehintresolver.cpp
kitemviews/kitemliststyleoption.cpp
QGraphicsScene* scene = static_cast<QGraphicsView*>(viewport())->scene();
if (previous) {
scene->removeItem(previous);
- disconnect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
- disconnect(previous, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
+ disconnect(previous, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+ disconnect(previous, SIGNAL(maximumOffsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+ disconnect(previous, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal)));
m_viewOffsetAnimation->setTargetObject(0);
}
if (current) {
scene->addItem(current);
- connect(current, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
- connect(current, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
+ connect(current, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+ connect(current, SIGNAL(maximumOffsetChanged(qreal,qreal)), this, SLOT(updateScrollBars()));
+ connect(current, SIGNAL(scrollTo(qreal)), this, SLOT(scrollTo(qreal)));
m_viewOffsetAnimation->setTargetObject(current);
}
}
+void KItemListContainer::scrollTo(qreal offset)
+{
+ const KItemListView* view = m_controller->view();
+ if (!view) {
+ return;
+ }
+
+ QScrollBar* scrollBar = (view->scrollOrientation() == Qt::Vertical)
+ ? verticalScrollBar() : horizontalScrollBar();
+ scrollBar->setValue(offset);
+}
+
void KItemListContainer::updateScrollBars()
{
const KItemListView* view = m_controller->view();
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
- disconnect(view, SIGNAL(scrollTo(int)),
- otherScrollBar, SLOT(setValue(int)));
- connect(view, SIGNAL(scrollTo(int)),
- scrollBar, SLOT(setValue(int)));
-
// Make sure that the other scroll bar is hidden
otherScrollBar->setMaximum(0);
otherScrollBar->setValue(0);
setViewport(graphicsView);
m_viewOffsetAnimation = new QPropertyAnimation(this, "offset");
- m_viewOffsetAnimation->setDuration(500);
+ m_viewOffsetAnimation->setDuration(300);
horizontalScrollBar()->installEventFilter(this);
verticalScrollBar()->installEventFilter(this);
private slots:
void slotModelChanged(KItemModelBase* current, KItemModelBase* previous);
void slotViewChanged(KItemListView* current, KItemListView* previous);
+ void scrollTo(qreal offset);
void updateScrollBars();
private:
#include "kitemlistcontroller.h"
#include "kitemlistview.h"
+#include "kitemlistrubberband_p.h"
#include "kitemlistselectionmanager.h"
#include <QEvent>
#include <QGraphicsSceneEvent>
-#include <QTransform>
#include <KDebug>
}
KItemListView* oldView = m_view;
+ if (oldView) {
+ disconnect(oldView, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
+ }
+
m_view = view;
if (m_view) {
m_view->setController(this);
m_view->setModel(m_model);
+ connect(m_view, SIGNAL(offsetChanged(qreal,qreal)), this, SLOT(slotViewOffsetChanged(qreal,qreal)));
}
emit viewChanged(m_view, oldView);
bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
+ if (!m_view) {
+ return false;
+ }
+
const QPointF pos = transform.map(event->pos());
m_pressedIndex = m_view->itemAt(pos);
switch (m_selectionBehavior) {
case NoSelection:
- return true;
+ break;
+
case SingleSelection:
m_selectionManager->setSelected(m_pressedIndex);
- return true;
+ break;
+
case MultiSelection:
if (controlPressed) {
m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Toggle);
m_selectionManager->beginAnchoredSelection(m_pressedIndex);
- }
- else {
- if (shiftPressed && m_selectionManager->isAnchoredSelectionActive()) {
- // The anchored selection is continued automatically by calling
- // m_selectionManager->setCurrentItem(m_pressedIndex), see above -> nothing more to do here
- return true;
- }
-
+ } else if (!shiftPressed || !m_selectionManager->isAnchoredSelectionActive()) {
// Select the pressed item and start a new anchored selection
m_selectionManager->setSelected(m_pressedIndex, 1, KItemListSelectionManager::Select);
m_selectionManager->beginAnchoredSelection(m_pressedIndex);
}
+ break;
+
+ default:
+ Q_ASSERT(false);
+ break;
}
return true;
+ } else {
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
+ QPointF startPos = pos;
+ if (m_view->scrollOrientation() == Qt::Vertical) {
+ startPos.ry() += m_view->offset();
+ } else {
+ startPos.rx() += m_view->offset();
+ }
+ rubberBand->setStartPosition(startPos);
+ rubberBand->setEndPosition(startPos);
+ rubberBand->setActive(true);
}
return false;
bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
- Q_UNUSED(event);
- Q_UNUSED(transform);
+ if (!m_view) {
+ return false;
+ }
+
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
+ if (rubberBand->isActive()) {
+ QPointF endPos = transform.map(event->pos());
+ if (m_view->scrollOrientation() == Qt::Vertical) {
+ endPos.ry() += m_view->offset();
+ } else {
+ endPos.rx() += m_view->offset();
+ }
+ rubberBand->setEndPosition(endPos);
+ }
+
return false;
}
bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
- if (m_view) {
- const QPointF pos = transform.map(event->pos());
- const int index = m_view->itemAt(pos);
- const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier;
-
- if (index >= 0 && index == m_pressedIndex) {
- // The release event is done above the same item as the press event
- bool emitItemClicked = true;
- if (event->button() & Qt::LeftButton) {
- if (m_view->isAboveExpansionToggle(index, pos)) {
- emit itemExpansionToggleClicked(index);
- emitItemClicked = false;
- }
- else if (shiftOrControlPressed) {
- // The mouse click should only update the selection, not trigger the item
- emitItemClicked = false;
- }
- }
+ if (!m_view) {
+ return false;
+ }
+
+ m_view->rubberBand()->setActive(false);
- if (emitItemClicked) {
- emit itemClicked(index, event->button());
+ const QPointF pos = transform.map(event->pos());
+ const int index = m_view->itemAt(pos);
+ const bool shiftOrControlPressed = event->modifiers() & Qt::ShiftModifier || event->modifiers() & Qt::ControlModifier;
+
+ if (index >= 0 && index == m_pressedIndex) {
+ // The release event is done above the same item as the press event
+ bool emitItemClicked = true;
+ if (event->button() & Qt::LeftButton) {
+ if (m_view->isAboveExpansionToggle(index, pos)) {
+ emit itemExpansionToggleClicked(index);
+ emitItemClicked = false;
}
- } else if (!shiftOrControlPressed) {
- m_selectionManager->clearSelection();
+ else if (shiftOrControlPressed) {
+ // The mouse click should only update the selection, not trigger the item
+ emitItemClicked = false;
+ }
+ }
+
+ if (emitItemClicked) {
+ emit itemClicked(index, event->button());
}
+ } else if (!shiftOrControlPressed) {
+ m_selectionManager->clearSelection();
}
m_pressedIndex = -1;
return false;
}
+void KItemListController::slotViewOffsetChanged(qreal current, qreal previous)
+{
+ if (!m_view) {
+ return;
+ }
+
+ KItemListRubberBand* rubberBand = m_view->rubberBand();
+ if (rubberBand->isActive()) {
+ const qreal diff = current - previous;
+ // TODO: Ideally just QCursor::pos() should be used as
+ // new end-position but it seems there is no easy way
+ // to have something like QWidget::mapFromGlobal() for QGraphicsWidget
+ // (... or I just missed an easy way to do the mapping)
+ QPointF endPos = rubberBand->endPosition();
+ if (m_view->scrollOrientation() == Qt::Vertical) {
+ endPos.ry() += diff;
+ } else {
+ endPos.rx() += diff;
+ }
+
+ rubberBand->setEndPosition(endPos);
+ }
+}
+
#include "kitemlistcontroller.moc"
#include <QObject>
class KItemModelBase;
+class KItemListRubberBandManager;
class KItemListSelectionManager;
class KItemListView;
class QGraphicsSceneHoverEvent;
void modelChanged(KItemModelBase* current, KItemModelBase* previous);
void viewChanged(KItemListView* current, KItemListView* previous);
+private slots:
+ void slotViewOffsetChanged(qreal current, qreal previous);
+
private:
SelectionBehavior m_selectionBehavior;
KItemModelBase* m_model;
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include "kitemlistrubberband_p.h"
+
+KItemListRubberBand::KItemListRubberBand(QObject* parent) :
+ QObject(parent),
+ m_active(false),
+ m_startPos(),
+ m_endPos()
+{
+}
+
+KItemListRubberBand::~KItemListRubberBand()
+{
+}
+
+void KItemListRubberBand::setStartPosition(const QPointF& pos)
+{
+ if (m_startPos != pos) {
+ const QPointF previous = m_startPos;
+ m_startPos = pos;
+ emit startPositionChanged(m_startPos, previous);
+ }
+}
+
+QPointF KItemListRubberBand::startPosition() const
+{
+ return m_startPos;
+}
+
+void KItemListRubberBand::setEndPosition(const QPointF& pos)
+{
+ if (m_endPos != pos) {
+ const QPointF previous = m_endPos;
+ m_endPos = pos;
+ emit endPositionChanged(m_endPos, previous);
+ }
+}
+
+QPointF KItemListRubberBand::endPosition() const
+{
+ return m_endPos;
+}
+
+void KItemListRubberBand::setActive(bool active)
+{
+ if (m_active != active) {
+ m_active = active;
+ emit activationChanged(active);
+
+ if (!active) {
+ m_startPos = QPointF();
+ m_endPos = QPointF();
+ }
+ }
+}
+
+bool KItemListRubberBand::isActive() const
+{
+ return m_active;
+}
+
+#include "kitemlistrubberband_p.moc"
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#ifndef KITEMLISTRUBBERBAND_H
+#define KITEMLISTRUBBERBAND_H
+
+#include <libdolphin_export.h>
+#include <QObject>
+#include <QPointF>
+
+/**
+ * @brief Manages the rubberband when selecting items.
+ */
+class LIBDOLPHINPRIVATE_EXPORT KItemListRubberBand : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit KItemListRubberBand(QObject* parent = 0);
+ virtual ~KItemListRubberBand();
+
+ void setStartPosition(const QPointF& pos);
+ QPointF startPosition() const;
+
+ void setEndPosition(const QPointF& pos);
+ QPointF endPosition() const;
+
+ void setActive(bool active);
+ bool isActive() const;
+
+signals:
+ void activationChanged(bool active);
+ void startPositionChanged(const QPointF& current, const QPointF& previous);
+ void endPositionChanged(const QPointF& current, const QPointF& previous);
+
+private:
+ bool m_active;
+ QPointF m_startPos;
+ QPointF m_endPos;
+};
+
+#endif
+
+
#include "kitemlistcontroller.h"
#include "kitemlistgroupheader.h"
+#include "kitemlistrubberband_p.h"
#include "kitemlistselectionmanager.h"
#include "kitemlistsizehintresolver_p.h"
#include "kitemlistviewlayouter_p.h"
#include <KDebug>
+#include <QCursor>
#include <QGraphicsSceneMouseEvent>
+#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
+#include <QStyleOptionRubberBand>
#include <QTimer>
KItemListView::KItemListView(QGraphicsWidget* parent) :
QGraphicsWidget(parent),
+ m_autoScrollMarginEnabled(false),
m_grouped(false),
m_activeTransactions(0),
m_itemSize(),
m_animation(0),
m_layoutTimer(0),
m_oldOffset(0),
- m_oldMaximumOffset(0)
+ m_oldMaximumOffset(0),
+ m_rubberBand(0),
+ m_mousePos()
{
setAcceptHoverEvents(true);
m_layoutTimer->setInterval(300);
m_layoutTimer->setSingleShot(true);
connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(slotLayoutTimerFinished()));
+
+ m_rubberBand = new KItemListRubberBand(this);
+ connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool)));
}
KItemListView::~KItemListView()
return m_activeTransactions > 0;
}
+void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
+{
+ QGraphicsWidget::paint(painter, option, widget);
+
+ if (m_rubberBand->isActive()) {
+ QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(),
+ m_rubberBand->endPosition()).normalized();
+
+ const QPointF topLeft = rubberBandRect.topLeft();
+ if (scrollOrientation() == Qt::Vertical) {
+ rubberBandRect.moveTo(topLeft.x(), topLeft.y() - offset());
+ } else {
+ rubberBandRect.moveTo(topLeft.x() - offset(), topLeft.y());
+ }
+
+ QStyleOptionRubberBand opt;
+ opt.initFrom(widget);
+ opt.shape = QRubberBand::Rectangle;
+ opt.opaque = false;
+ opt.rect = rubberBandRect.toRect();
+ style()->drawControl(QStyle::CE_RubberBand, &opt, painter);
+ }
+}
+
void KItemListView::initializeItemListWidget(KItemListWidget* item)
{
Q_UNUSED(item);
void KItemListView::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
+ m_mousePos = transform().map(event->pos());
event->accept();
}
+void KItemListView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+ m_mousePos = transform().map(event->pos());
+ QGraphicsWidget::mouseMoveEvent(event);
+}
+
QList<KItemListWidget*> KItemListView::visibleItemListWidgets() const
{
return m_visibleItems.values();
foreach (const KItemRange& range, itemRanges) {
// range.index is related to the model before anything has been inserted.
// As in each loop the current item-range gets inserted the index must
- // be increased by the already previoulsy inserted items.
+ // be increased by the already previously inserted items.
const int index = range.index + previouslyInsertedCount;
const int count = range.count;
if (index < 0 || count <= 0) {
doLayout(Animation, 0, 0);
}
+void KItemListView::slotRubberBandStartPosChanged()
+{
+ update();
+}
+
+void KItemListView::slotRubberBandEndPosChanged()
+{
+ triggerAutoScrolling();
+ update();
+}
+
+void KItemListView::slotRubberBandActivationChanged(bool active)
+{
+ if (active) {
+ connect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandStartPosChanged()));
+ connect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandEndPosChanged()));
+ } else {
+ disconnect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandStartPosChanged()));
+ disconnect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandEndPosChanged()));
+ }
+
+ update();
+}
+
void KItemListView::setController(KItemListController* controller)
{
if (m_controller != controller) {
onModelChanged(model, previous);
}
+KItemListRubberBand* KItemListView::rubberBand() const
+{
+ return m_rubberBand;
+}
+
void KItemListView::updateLayout()
{
doLayout(Animation, 0, 0);
void KItemListView::emitOffsetChanges()
{
- const int newOffset = m_layouter->offset();
+ const qreal newOffset = m_layouter->offset();
if (m_oldOffset != newOffset) {
emit offsetChanged(newOffset, m_oldOffset);
m_oldOffset = newOffset;
}
- const int newMaximumOffset = m_layouter->maximumOffset();
+ const qreal newMaximumOffset = m_layouter->maximumOffset();
if (m_oldMaximumOffset != newMaximumOffset) {
emit maximumOffsetChanged(newMaximumOffset, m_oldMaximumOffset);
m_oldMaximumOffset = newMaximumOffset;
widget->setData(m_model->data(index));
}
+void KItemListView::triggerAutoScrolling()
+{
+ const int pos = (scrollOrientation() == Qt::Vertical) ? m_mousePos.y() : m_mousePos.x();
+ const int inc = calculateAutoScrollingIncrement(pos, size().height());
+ if (inc != 0) {
+ emit scrollTo(offset() + inc);
+ }
+}
+
+int KItemListView::calculateAutoScrollingIncrement(int pos, int size)
+{
+ int inc = 0;
+
+ const int minSpeed = 4;
+ const int maxSpeed = 768;
+ const int speedLimiter = 48;
+ const int autoScrollBorder = 64;
+
+ if (pos < autoScrollBorder) {
+ inc = -minSpeed + qAbs(pos - autoScrollBorder) * (pos - autoScrollBorder) / speedLimiter;
+ if (inc < -maxSpeed) {
+ inc = -maxSpeed;
+ }
+ } else if (pos > size - autoScrollBorder) {
+ inc = minSpeed + qAbs(pos - size + autoScrollBorder) * (pos - size + autoScrollBorder) / speedLimiter;
+ if (inc > maxSpeed) {
+ inc = maxSpeed;
+ }
+ }
+
+ return inc;
+}
+
+
KItemListCreatorBase::~KItemListCreatorBase()
{
qDeleteAll(m_recycleableWidgets);
class KItemListGroupHeader;
class KItemListGroupHeaderCreatorBase;
class KItemListSizeHintResolver;
+class KItemListRubberBand;
class KItemListViewAnimation;
class KItemListViewLayouter;
class KItemListWidget;
void endTransaction();
bool isTransactionActive() const;
+ /**
+ * @reimp
+ */
+ virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
signals:
- void offsetChanged(int current, int previous);
- void maximumOffsetChanged(int current, int previous);
- void scrollTo(int newOffset);
+ void offsetChanged(qreal current, qreal previous);
+ void maximumOffsetChanged(qreal current, qreal previous);
+ void scrollTo(qreal newOffset);
protected:
virtual void initializeItemListWidget(KItemListWidget* item);
virtual bool event(QEvent* event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
QList<KItemListWidget*> visibleItemListWidgets() const;
KItemListViewAnimation::AnimationType type);
void slotLayoutTimerFinished();
+ void slotRubberBandStartPosChanged();
+ void slotRubberBandEndPosChanged();
+ void slotRubberBandActivationChanged(bool active);
+
private:
enum LayoutAnimationHint
{
void setController(KItemListController* controller);
void setModel(KItemModelBase* model);
+ KItemListRubberBand* rubberBand() const;
+
void updateLayout();
void doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
void doGroupHeadersLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
*/
void updateWidgetProperties(KItemListWidget* widget, int index);
+ /**
+ * Emits the signal scrollTo() with the corresponding target offset if the current
+ * mouse position is above the autoscroll-margin.
+ */
+ void triggerAutoScrolling();
+
+ /**
+ * Helper function for triggerAutoScrolling(). Returns the scroll increment
+ * that should be added to the offset() based on the available size \a size
+ * and the current mouse position \a pos. As soon as \a pos is inside
+ * the autoscroll-margin a value != 0 will be returned.
+ */
+ static int calculateAutoScrollingIncrement(int pos, int size);
+
private:
+ bool m_autoScrollMarginEnabled;
bool m_grouped;
int m_activeTransactions; // Counter for beginTransaction()/endTransaction()
KItemListViewAnimation* m_animation;
QTimer* m_layoutTimer; // Triggers an asynchronous doLayout() call.
- int m_oldOffset;
- int m_oldMaximumOffset;
+ qreal m_oldOffset;
+ qreal m_oldMaximumOffset;
+
+ KItemListRubberBand* m_rubberBand;
+
+ QPointF m_mousePos;
friend class KItemListController;
};