summaryrefslogtreecommitdiffstats
path: root/weather/src/citylist.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'weather/src/citylist.cpp')
-rw-r--r--weather/src/citylist.cpp465
1 files changed, 465 insertions, 0 deletions
diff --git a/weather/src/citylist.cpp b/weather/src/citylist.cpp
new file mode 100644
index 0000000..d3ac7fd
--- /dev/null
+++ b/weather/src/citylist.cpp
@@ -0,0 +1,465 @@
+#include "citylist.h"
+#include "settings.h"
+#include "pixmaploader.h"
+#include <QPainter>
+#include <cmath>
+#include <QParallelAnimationGroup>
+#include <QPropertyAnimation>
+
+#define LIST_TOP_PIXMAP (PixmapLoader::getPic("list_top"))
+
+#define ITEM_BACKGROUND (PixmapLoader::getPic("list_item_bg"))
+#define SELECTED_ITEM_BACKGROUND (PixmapLoader::getPic("list_item_selected_bg"))
+
+#define ITEM_BUTTON_PIXMAP (PixmapLoader::getPic("button_list_delete"))
+#define ITEM_BUTTON_LEFT (Settings::scaleWidth(406.0))
+
+#define ITEM_CHECK_PIXMAP (PixmapLoader::getPic("list_check"))
+#define ITEM_CHECK_LEFT (Settings::scaleWidth(10.0))
+
+#define TEXT_LEFT (2 * ITEM_CHECK_LEFT + ITEM_CHECK_PIXMAP.width())
+
+#define CITY_NAME_FONT_SIZE (Settings::scaleHeight(50.0))
+
+static const int maxVisibleItems = 6;
+static const int minListCount = 3;
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *parent, QGraphicsItem *item)
+{
+ const qreal top = (parent->boundingRect().height() - item->boundingRect().height()) / 2;
+ return top - parent->boundingRect().top() - item->boundingRect().top();
+}
+
+static inline qreal getCenterVerticalPos(QGraphicsItem *item)
+{
+ return getCenterVerticalPos(item->parentItem(), item);
+}
+
+// CityListScrollBox
+
+#ifdef QT_ARCH_ARM
+static const int acceleration_factor = 12;
+#else
+static const int acceleration_factor = 64;
+#endif
+
+
+static const int max_gesture_time_ms = 500;
+static const qreal degrees = 30.0;
+static const qreal limit_angle_tg = tan(degrees * 3.141592 / 180.0);
+static const qreal min_gesture_length = 100.0;
+
+CityListScrollBox::CityListScrollBox(QGraphicsItem *content, QGraphicsItem *parent)
+ : GestureBox(parent)
+ , m_content(content)
+ , m_scroll(new ScrollBar(this))
+{
+ setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
+ if (m_content)
+ m_content->setParentItem(this);
+
+ m_scroll->setPos(Settings::scaleWidth(460.0), Settings::scaleHeight(32.5));
+
+ m_content->setZValue(0.0);
+ m_scroll->setZValue(1.0);
+}
+
+void CityListScrollBox::gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick)
+{
+ Q_UNUSED(pos);
+ startGesture = true;
+ acceptClick = !m_ticker.isActive();
+ if (m_ticker.isActive()) {
+ m_ticker.stop();
+ m_speed = 0.0;
+ }
+}
+
+void CityListScrollBox::gestureStart(QPointF pos)
+{
+ m_startPoint = pos;
+}
+
+void CityListScrollBox::contentPositionUpdated()
+{
+ qreal height = boundingRect().height();
+ qreal content = m_content->boundingRect().height();
+
+ if (height < content) {
+ qreal pos = content - height;
+ pos = -m_content->pos().y() / pos;
+ m_scroll->setValue(pos < 0.0 ? 0.0 : pos);
+ } else
+ if (m_content->pos().y() != 0.0) {
+ m_content->setPos(0.0, 0.0);
+ m_scroll->setValue(0.0);
+ }
+
+}
+
+bool CityListScrollBox::move(QPointF movement)
+{
+ if (m_content && m_content->boundingRect().height() > boundingRect().height()) {
+
+ qreal dist = movement.y();
+
+
+ if (dist > 0 && m_content->pos().y() < 0) {
+ qreal top = m_content->pos().y() + dist;
+ m_content->setPos(m_content->pos().x(), top > 0 ? 0 : top);
+ contentPositionUpdated();
+ return true;
+ }
+
+ if (dist < 0) {
+ qreal top = m_content->pos().y() + dist;
+ if (top + m_content->boundingRect().height() >= boundingRect().height()) {
+ m_content->setPos(m_content->pos().x(), top);
+ contentPositionUpdated();
+ return true;
+ }
+ }
+
+ }
+ return false;
+}
+
+void CityListScrollBox::gestureMove(QPointF pos, QPointF movement, QPointF speed)
+{
+ Q_UNUSED(pos);
+ Q_UNUSED(speed);
+ move(movement);
+}
+
+void CityListScrollBox::gestureEnd(QPointF pos, QPointF speed)
+{
+ qreal x = pos.x() - m_startPoint.x();
+ qreal y = pos.y() - m_startPoint.y();
+ if (fabs(x / y) > limit_angle_tg || fabs(y) < min_gesture_length)
+ return;
+
+ qreal topSpeed = Settings::scaleHeight(1500.0);
+ m_acceleration = topSpeed / acceleration_factor;
+ m_speed = qBound(-topSpeed, speed.y(), topSpeed);
+ if (m_speed != 0) {
+ m_time = QTime::currentTime();
+ m_ticker.start(20, this);
+ }
+}
+
+void CityListScrollBox::timerEvent(QTimerEvent *event)
+{
+ bool stopTimer = true;
+ if (m_speed != 0.0) {
+ QTime now(QTime::currentTime());
+ qreal movement = m_speed * (qreal)m_time.msecsTo(now) / 1000.0;
+ m_time = now;
+ stopTimer = !move(QPointF(0, movement));
+ if (!stopTimer) {
+ m_speed = m_speed > 0.0 ? qMax(qreal(0.0), m_speed - m_acceleration)
+ : qMin(qreal(0.0), m_speed + m_acceleration);
+ stopTimer = m_speed == 0.0;
+ }
+ }
+ if (stopTimer)
+ m_ticker.stop();
+ QObject::timerEvent(event);
+}
+
+// CityListText
+
+CityListText::CityListText(const QString &text, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_painter(CITY_NAME_FONT_SIZE, Qt::white, text)
+{
+ setCacheMode(ItemCoordinateCache);
+ m_painter.setPos(0.0, 0.0);
+ m_painter.setMaxWidth(ITEM_BUTTON_LEFT - TEXT_LEFT);
+}
+
+QRectF CityListText::boundingRect() const
+{
+ return QRectF(0.0, 0.0, m_painter.width(), m_painter.height());
+}
+
+void CityListText::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ m_painter.paint(painter);
+}
+
+// CityListItem
+
+CityListItem::CityListItem(const ForecastData &data, CityContentList *list, bool deleteVisible)
+ : ContentListItem(0)
+ , m_height(ITEM_BACKGROUND.height())
+ , m_data(data)
+ , m_list(list)
+ , m_background(new QGraphicsPixmapItem(ITEM_BACKGROUND, this))
+ , m_check(new QGraphicsPixmapItem(ITEM_CHECK_PIXMAP, m_background))
+ , m_delete(new PixmapButton(80.0, ITEM_BUTTON_PIXMAP, m_background))
+ , m_text(new CityListText(m_data.cityName(), this))
+{
+ m_delete->setPos(ITEM_BUTTON_LEFT, getCenterVerticalPos(m_delete));
+ m_delete->setVisible(deleteVisible);
+ m_delete->setOpacity(deleteVisible ? 1.0 : 0.0);
+ m_check->setPos(ITEM_CHECK_LEFT, getCenterVerticalPos(m_check));
+ m_check->hide();
+ m_text->setPos(TEXT_LEFT, getCenterVerticalPos(m_text));
+ m_text->setCacheMode(ItemCoordinateCache);
+
+ connect(m_delete, SIGNAL(clicked()), this, SLOT(removeFromList()));
+}
+
+void CityListItem::select(bool selected)
+{
+ m_background->setPixmap(selected ? SELECTED_ITEM_BACKGROUND : ITEM_BACKGROUND);
+ m_check->setVisible(selected);
+}
+
+void CityListItem::removeFromList()
+{
+ if (!m_list->busy())
+ m_list->removeItem(this);
+}
+
+qreal CityListItem::contentHeight() const
+{
+ return m_height;
+}
+
+QAbstractAnimation *CityListItem::getButtonAnimation(bool hide)
+{
+ return getFadeAnimation(m_delete, hide, 150);
+}
+
+QAbstractAnimation *CityListItem::getShowAnimation()
+{
+ return getFadeAnimation(this, false, 150);
+}
+
+QAbstractAnimation *CityListItem::getHideAnimation()
+{
+ return getFadeAnimation(this, true, 150);
+}
+
+template<class T>QAbstractAnimation *CityListItem::getFadeAnimation(T *target, bool hide, int msecs)
+{
+ target->setOpacity(hide ? 1.0 : 0.0);
+ target->show();
+ QPropertyAnimation* lResult = new QPropertyAnimation(target, "opacity");
+ lResult->setEasingCurve(QEasingCurve::OutExpo);
+ lResult->setStartValue(hide ? 1.0 : 0.0);
+ lResult->setEndValue(hide ? 0.0 : 1.0);
+ lResult->setDuration(msecs);
+ if (hide)
+ connect(lResult, SIGNAL(finished()), target, SLOT(doHide()));
+ return lResult;
+}
+
+// CityContentList
+
+CityContentList::CityContentList(QList<ForecastData> &contentList, QObject *holder)
+ : ContentList(0)
+ , m_holder(holder)
+ , m_contentList(contentList)
+{
+ QList<ContentListItem*> items;
+ foreach (const ForecastData &data, m_contentList)
+ items.append(new CityListItem(data, this, m_contentList.count() > minListCount));
+ appendItems(items);
+}
+
+void CityContentList::addForecast(const ForecastData &data)
+{
+ m_contentList.append(data);
+ addItem(new CityListItem(data, this, m_contentList.count() > minListCount));
+}
+
+void CityContentList::select(const QString &selected)
+{
+ for (int i = 0; i < itemCount(); ++i) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ item->select(item->data().key() == selected);
+ }
+}
+
+QAbstractAnimation *CityContentList::getInsertAnimation(int idx, qreal height)
+{
+ if (idx < 0 || idx > itemCount())
+ return 0;
+
+ QList<QAbstractAnimation*> list;
+
+ if (m_holder && itemCount() < maxVisibleItems)
+ list.append(getMoveAnimation(m_holder, -height));
+
+ if (idx < itemCount()) {
+ for (int i = idx; i < itemCount(); ++i)
+ list.append(getItemMoveAnimation(i, height));
+ }
+
+ if (parentItem() && itemCount() >= maxVisibleItems) {
+ qreal top = parentItem()->boundingRect().height() - boundingRect().height() - height;
+ list.append(getMoveAnimation(this, top - pos().y()));
+
+ }
+
+ if (itemCount() == minListCount) {
+ for (int i = 0; i < minListCount; ++i) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ list.append(item->getButtonAnimation(false));
+ }
+ }
+
+ return createCompoundAnimation(list);
+}
+
+QAbstractAnimation *CityContentList::getRemoveAnimation(int idx)
+{
+ if (idx < 0 || idx >= itemCount())
+ return 0;
+
+ qreal offset = -getItem(idx)->contentHeight();
+ QList<QAbstractAnimation*> list;
+
+ if (m_holder && itemCount() <= maxVisibleItems)
+ list.append(getMoveAnimation(m_holder, -offset));
+
+ for (int i = idx + 1; i < itemCount(); ++i)
+ list.append(getItemMoveAnimation(i, offset));
+
+ qreal newHeight = boundingRect().height() - getItem(idx)->contentHeight();
+
+ if (parentItem()) {
+ if (newHeight < parentItem()->boundingRect().height()) {
+ if (pos().y() != 0)
+ list.append(getMoveAnimation(this, -pos().y()));
+ } else {
+ if (pos().y() + newHeight < parentItem()->boundingRect().bottom()) {
+ qreal offset = parentItem()->boundingRect().bottom() - pos().y() - newHeight;
+ list.append(getMoveAnimation(this, offset));
+ }
+ }
+ }
+
+ if (itemCount() == minListCount + 1) {
+ for (int i = 0; i <= minListCount; ++i) {
+ if (i != idx) {
+ CityListItem *item = static_cast<CityListItem*>(getItem(i));
+ if (item)
+ list.append(item->getButtonAnimation(true));
+
+ }
+ }
+ }
+
+ m_contentList.removeAt(idx);
+
+
+ return createCompoundAnimation(list);
+}
+
+QAbstractAnimation *CityContentList::createCompoundAnimation(QList<QAbstractAnimation*> list)
+{
+ if (list.count() == 0)
+ return 0;
+ if (list.count() == 1)
+ return list[0];
+ QParallelAnimationGroup *result = new QParallelAnimationGroup();
+ for (int i = 0; i < list.count(); ++i)
+ result->addAnimation(list[i]);
+ return result;
+
+}
+
+QAbstractAnimation *CityContentList::getMoveAnimation(QObject *object, qreal offset)
+{
+ if (!object)
+ return 0;
+ qreal itemTop = object->property(ITEM_TOP_PROPERTY_NAME).toReal();
+ QPropertyAnimation* lResult = new QPropertyAnimation(object, ITEM_TOP_PROPERTY_NAME);
+
+ lResult->setEasingCurve(QEasingCurve::OutExpo);
+ lResult->setStartValue(itemTop);
+ lResult->setEndValue(itemTop + offset);
+ lResult->setDuration(150);
+ return lResult;
+}
+
+QAbstractAnimation *CityContentList::getItemMoveAnimation(int idx, qreal offset)
+{
+ return getMoveAnimation(getItem(idx), offset);
+}
+
+// CityList
+
+CityList::CityList(QList<ForecastData> &contentList, QGraphicsItem *parent)
+ : QGraphicsItem(parent)
+ , m_itemHeight(ITEM_BACKGROUND.height())
+ , m_topHeight(LIST_TOP_PIXMAP.height())
+ , m_boundingRect(getBoundingRect())
+ , m_paintRect(getPaintRect())
+ , m_top(new QGraphicsPixmapItem(LIST_TOP_PIXMAP, this))
+ , m_list(new CityContentList(contentList, this))
+ , m_scrollBox(new CityListScrollBox(m_list, this))
+{
+ m_top->setPos(-m_top->boundingRect().left(), -m_top->boundingRect().top());
+ m_scrollBox->setRect(m_paintRect);
+ m_list->setWidth(m_paintRect.width());
+}
+
+qreal CityList::initialTop() const
+{
+ int count = maxVisibleItems - m_list->itemCount();
+ if (count <= 0)
+ return 0;
+ return count * m_itemHeight;
+}
+
+QRectF CityList::getBoundingRect()
+{
+ QRectF result(QPointF(0.0, 0.0), Settings::windowSize());
+ result.setHeight(m_topHeight + maxVisibleItems * m_itemHeight);
+ result.moveTo(0.0, 0.0);
+ return result;
+}
+
+QRectF CityList::getPaintRect()
+{
+ QRectF result(QPointF(0.0, 0.0), Settings::windowSize());
+ result.setHeight(maxVisibleItems * m_itemHeight);
+ result.moveTo(0.0, m_topHeight);
+ return result;
+}
+
+int CityList::loadImages()
+{
+ PixmapLoader::load("list_top");
+ PixmapLoader::load("list_item_bg");
+ PixmapLoader::load("list_item_selected_bg");
+ PixmapLoader::load("button_list_delete");
+ PixmapLoader::load("list_check");
+ return 5;
+}
+
+void CityList::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+QRectF CityList::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+void CityList::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *opt, QWidget *widget)
+{
+ Q_UNUSED(opt);
+ Q_UNUSED(widget);
+ painter->fillRect(m_paintRect, QColor(7, 18, 23, 255));
+}