#include "citylist.h" #include "settings.h" #include "pixmaploader.h" #include #include #include #include #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); } templateQAbstractAnimation *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 &contentList, QObject *holder) : ContentList(0) , m_holder(holder) , m_contentList(contentList) { QList 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(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 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(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 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(getItem(i)); if (item) list.append(item->getButtonAnimation(true)); } } } m_contentList.removeAt(idx); return createCompoundAnimation(list); } QAbstractAnimation *CityContentList::createCompoundAnimation(QList 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 &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)); }