diff options
author | Chenca <renato.chencarek@openbossa.org> | 2009-10-23 14:12:37 -0300 |
---|---|---|
committer | Chenca <renato.chencarek@openbossa.org> | 2009-10-23 14:12:37 -0300 |
commit | ce6fd7f4f07ba0addc1a384043ca62ef7957aa43 (patch) | |
tree | 9e2aec3948ccbd4701402eb3505641da1040486c /shoplist | |
parent | cabc1ff0197b818948199daab771cdfa75a53d3f (diff) |
First version of ShopList added
Signed-off-by: Chenca <renato.chencarek@openbossa.org>
Diffstat (limited to 'shoplist')
58 files changed, 3257 insertions, 0 deletions
diff --git a/shoplist/images/background.png b/shoplist/images/background.png Binary files differnew file mode 100644 index 0000000..b1496b7 --- /dev/null +++ b/shoplist/images/background.png diff --git a/shoplist/images/background_landscape.png b/shoplist/images/background_landscape.png Binary files differnew file mode 100644 index 0000000..4300059 --- /dev/null +++ b/shoplist/images/background_landscape.png diff --git a/shoplist/images/button_close.png b/shoplist/images/button_close.png Binary files differnew file mode 100644 index 0000000..8bb6b9a --- /dev/null +++ b/shoplist/images/button_close.png diff --git a/shoplist/images/list_alldone_background.png b/shoplist/images/list_alldone_background.png Binary files differnew file mode 100644 index 0000000..62b581f --- /dev/null +++ b/shoplist/images/list_alldone_background.png diff --git a/shoplist/images/list_alldone_bt_startagain_off.png b/shoplist/images/list_alldone_bt_startagain_off.png Binary files differnew file mode 100644 index 0000000..3b59375 --- /dev/null +++ b/shoplist/images/list_alldone_bt_startagain_off.png diff --git a/shoplist/images/list_alldone_bt_startagain_on.png b/shoplist/images/list_alldone_bt_startagain_on.png Binary files differnew file mode 100644 index 0000000..fe67abf --- /dev/null +++ b/shoplist/images/list_alldone_bt_startagain_on.png diff --git a/shoplist/images/list_background.png b/shoplist/images/list_background.png Binary files differnew file mode 100644 index 0000000..534a626 --- /dev/null +++ b/shoplist/images/list_background.png diff --git a/shoplist/images/list_button_check.png b/shoplist/images/list_button_check.png Binary files differnew file mode 100644 index 0000000..295485d --- /dev/null +++ b/shoplist/images/list_button_check.png diff --git a/shoplist/images/list_button_checked.png b/shoplist/images/list_button_checked.png Binary files differnew file mode 100644 index 0000000..0b2b6a7 --- /dev/null +++ b/shoplist/images/list_button_checked.png diff --git a/shoplist/images/list_feather.png b/shoplist/images/list_feather.png Binary files differnew file mode 100644 index 0000000..f248385 --- /dev/null +++ b/shoplist/images/list_feather.png diff --git a/shoplist/images/list_icon_beverages.png b/shoplist/images/list_icon_beverages.png Binary files differnew file mode 100644 index 0000000..721fe20 --- /dev/null +++ b/shoplist/images/list_icon_beverages.png diff --git a/shoplist/images/list_icon_cleaning.png b/shoplist/images/list_icon_cleaning.png Binary files differnew file mode 100644 index 0000000..4497ad2 --- /dev/null +++ b/shoplist/images/list_icon_cleaning.png diff --git a/shoplist/images/list_icon_garden.png b/shoplist/images/list_icon_garden.png Binary files differnew file mode 100644 index 0000000..6c3215c --- /dev/null +++ b/shoplist/images/list_icon_garden.png diff --git a/shoplist/images/list_icon_groceries.png b/shoplist/images/list_icon_groceries.png Binary files differnew file mode 100644 index 0000000..361da1f --- /dev/null +++ b/shoplist/images/list_icon_groceries.png diff --git a/shoplist/images/list_icon_pet.png b/shoplist/images/list_icon_pet.png Binary files differnew file mode 100644 index 0000000..5b16ab1 --- /dev/null +++ b/shoplist/images/list_icon_pet.png diff --git a/shoplist/images/list_icon_toiletries.png b/shoplist/images/list_icon_toiletries.png Binary files differnew file mode 100644 index 0000000..48cecd3 --- /dev/null +++ b/shoplist/images/list_icon_toiletries.png diff --git a/shoplist/images/list_landscape_scrollbar.png b/shoplist/images/list_landscape_scrollbar.png Binary files differnew file mode 100644 index 0000000..b5c8947 --- /dev/null +++ b/shoplist/images/list_landscape_scrollbar.png diff --git a/shoplist/images/list_line.png b/shoplist/images/list_line.png Binary files differnew file mode 100644 index 0000000..81cdf11 --- /dev/null +++ b/shoplist/images/list_line.png diff --git a/shoplist/images/list_portrait_scrollbar.png b/shoplist/images/list_portrait_scrollbar.png Binary files differnew file mode 100644 index 0000000..2bd647b --- /dev/null +++ b/shoplist/images/list_portrait_scrollbar.png diff --git a/shoplist/images/list_scrollbar_knob.png b/shoplist/images/list_scrollbar_knob.png Binary files differnew file mode 100644 index 0000000..79b7d87 --- /dev/null +++ b/shoplist/images/list_scrollbar_knob.png diff --git a/shoplist/images/list_selecteditem_background.png b/shoplist/images/list_selecteditem_background.png Binary files differnew file mode 100644 index 0000000..e075a02 --- /dev/null +++ b/shoplist/images/list_selecteditem_background.png diff --git a/shoplist/images/list_shadow_beverages_50.png b/shoplist/images/list_shadow_beverages_50.png Binary files differnew file mode 100644 index 0000000..311a62e --- /dev/null +++ b/shoplist/images/list_shadow_beverages_50.png diff --git a/shoplist/images/list_shadow_cleaning_50.png b/shoplist/images/list_shadow_cleaning_50.png Binary files differnew file mode 100644 index 0000000..45e1368 --- /dev/null +++ b/shoplist/images/list_shadow_cleaning_50.png diff --git a/shoplist/images/list_shadow_garden_50.png b/shoplist/images/list_shadow_garden_50.png Binary files differnew file mode 100644 index 0000000..960b37a --- /dev/null +++ b/shoplist/images/list_shadow_garden_50.png diff --git a/shoplist/images/list_shadow_groceries_50.png b/shoplist/images/list_shadow_groceries_50.png Binary files differnew file mode 100644 index 0000000..82e02de --- /dev/null +++ b/shoplist/images/list_shadow_groceries_50.png diff --git a/shoplist/images/list_shadow_pet_50.png b/shoplist/images/list_shadow_pet_50.png Binary files differnew file mode 100644 index 0000000..a4a237e --- /dev/null +++ b/shoplist/images/list_shadow_pet_50.png diff --git a/shoplist/images/list_shadow_toiletries_50.png b/shoplist/images/list_shadow_toiletries_50.png Binary files differnew file mode 100644 index 0000000..74e872f --- /dev/null +++ b/shoplist/images/list_shadow_toiletries_50.png diff --git a/shoplist/images/list_title_beverages.png b/shoplist/images/list_title_beverages.png Binary files differnew file mode 100644 index 0000000..6d69a90 --- /dev/null +++ b/shoplist/images/list_title_beverages.png diff --git a/shoplist/images/list_title_cleaning.png b/shoplist/images/list_title_cleaning.png Binary files differnew file mode 100644 index 0000000..eeeb27a --- /dev/null +++ b/shoplist/images/list_title_cleaning.png diff --git a/shoplist/images/list_title_garden.png b/shoplist/images/list_title_garden.png Binary files differnew file mode 100644 index 0000000..c844dcb --- /dev/null +++ b/shoplist/images/list_title_garden.png diff --git a/shoplist/images/list_title_groceries.png b/shoplist/images/list_title_groceries.png Binary files differnew file mode 100644 index 0000000..4ecb14a --- /dev/null +++ b/shoplist/images/list_title_groceries.png diff --git a/shoplist/images/list_title_pet.png b/shoplist/images/list_title_pet.png Binary files differnew file mode 100644 index 0000000..45912b3 --- /dev/null +++ b/shoplist/images/list_title_pet.png diff --git a/shoplist/images/list_title_toiletries.png b/shoplist/images/list_title_toiletries.png Binary files differnew file mode 100644 index 0000000..8e1a1b3 --- /dev/null +++ b/shoplist/images/list_title_toiletries.png diff --git a/shoplist/resources.qrc b/shoplist/resources.qrc new file mode 100644 index 0000000..f5ce512 --- /dev/null +++ b/shoplist/resources.qrc @@ -0,0 +1,37 @@ +<RCC> + <qresource prefix="/"> + <file>images/background.png</file> + <file>images/button_close.png</file> + <file>images/list_alldone_background.png</file> + <file>images/list_alldone_bt_startagain_off.png</file> + <file>images/list_alldone_bt_startagain_on.png</file> + <file>images/list_background.png</file> + <file>images/list_button_checked.png</file> + <file>images/list_button_check.png</file> + <file>images/list_feather.png</file> + <file>images/list_icon_beverages.png</file> + <file>images/list_icon_cleaning.png</file> + <file>images/list_icon_garden.png</file> + <file>images/list_icon_groceries.png</file> + <file>images/list_icon_pet.png</file> + <file>images/list_icon_toiletries.png</file> + <file>images/list_landscape_scrollbar.png</file> + <file>images/list_line.png</file> + <file>images/list_portrait_scrollbar.png</file> + <file>images/list_scrollbar_knob.png</file> + <file>images/list_selecteditem_background.png</file> + <file>images/list_title_beverages.png</file> + <file>images/list_title_cleaning.png</file> + <file>images/list_title_garden.png</file> + <file>images/list_title_groceries.png</file> + <file>images/list_title_pet.png</file> + <file>images/list_title_toiletries.png</file> + <file>images/list_shadow_beverages_50.png</file> + <file>images/list_shadow_cleaning_50.png</file> + <file>images/list_shadow_garden_50.png</file> + <file>images/list_shadow_groceries_50.png</file> + <file>images/list_shadow_pet_50.png</file> + <file>images/list_shadow_toiletries_50.png</file> + <file>images/background_landscape.png</file> + </qresource> +</RCC> diff --git a/shoplist/shoplist.pro b/shoplist/shoplist.pro new file mode 100644 index 0000000..4df7a52 --- /dev/null +++ b/shoplist/shoplist.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +TARGET = + +include(src/src.pri) + +target.path = $$PREFIX/bin +INSTALLS += target + +RESOURCES += resources.qrc +QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.5 + +symbian { + HEADERS += src/sym_iap_util.h + LIBS += -lesock -lconnmon -lcone -lavkon + TARGET.CAPABILITY = NetworkServices + addFiles.sources = symbian.ini + addFiles.path = e:\resources\apps + DEPLOYMENT += addFiles + ICON = images/icon.svg + TARGET.UID3 = 0xe1234567 +} diff --git a/shoplist/src/contentlist.cpp b/shoplist/src/contentlist.cpp new file mode 100644 index 0000000..ba2d405 --- /dev/null +++ b/shoplist/src/contentlist.cpp @@ -0,0 +1,408 @@ +#include "contentlist.h" +#include <QPointer> + +// ContentListItem + +ContentListItem::ContentListItem(QGraphicsItem *parent) + : QGraphicsItem(parent) + , m_geometry(QRectF(0.0, 0.0, 0.0, 0.0)) +{ + setFlag(QGraphicsItem::ItemHasNoContents, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + updateGeometry(); +} + +QRectF ContentListItem::boundingRect() const +{ + return m_geometry; +} + +void ContentListItem::updateGeometry() +{ + if (!parentItem()) + return; + QRectF geometry(0.0, 0.0, parentItem()->boundingRect().width() - pos().x(), contentHeight()); + if (m_geometry != geometry) { + prepareGeometryChange(); + m_geometry = geometry; + update(); + } +} + +QVariant ContentListItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + Q_UNUSED(value); + switch (change) { + case QGraphicsItem::ItemParentHasChanged: + case QGraphicsItem::ItemPositionHasChanged: + updateGeometry(); + break; + default: + break; + } + return value; +} + +void ContentListItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); +} + +// ContentListActivity + +void ContentListActivity::addActivity(ContentListActivity *activity) +{ + int idx = m_list.m_queue.indexOf(this) + 1; + if (idx > 0) + m_list.m_queue.insert(idx + m_insertPos++, activity); +} + +bool ContentListActivity::active() +{ + return false; +} + +// SignalActivity + + +bool SignalActivity::run() +{ + emit notify(); + return false; +} + +// SortActivity + +bool SortActivity::run() +{ + qSort(m_list.m_items.begin(), m_list.m_items.end(), m_compare); + m_list.updateItems(); + return false; +} + +// AppendItemsActivity + +bool AppendItemsActivity::run() +{ + m_list.prepareGeometryChange(); + m_list.doAppendItems(m_items, true); + m_list.update(); + return false; +} + +// AnimationActivity + +AnimationActivity::AnimationActivity(QAbstractAnimation *animation, ContentList &list) + : ContentListActivity(list) + , m_animation(animation) +{ +} + +AnimationActivity::~AnimationActivity() +{ + if (m_animation && m_animation->state() == QAbstractAnimation::Stopped) + m_animation->deleteLater(); +} + + +bool AnimationActivity::run() +{ + if (!m_animation || m_animation->state() != QAbstractAnimation::Stopped) + return false; + + connect(m_animation, SIGNAL(finished()), this, SLOT(animationEnd())); + m_animation->start(QAbstractAnimation::DeleteWhenStopped); + return true; +} + +void AnimationActivity::animationEnd() +{ + m_animation = 0; + activityEnd(); +} + +// RemoveActivity + +RemoveActivity::RemoveActivity(int idx, bool destroyItem, bool notify, ContentList &list) + : ContentListActivity(list) + , m_idx(idx) + , m_destroyItem(destroyItem) + , m_item(0) + , m_active(false) + , m_notify(notify) +{ +} + +bool RemoveActivity::run() +{ + if (m_active) + return false; + + m_item = m_list.getItem(m_idx); + if (!m_item) + return false; + + QAbstractAnimation *animation = m_item->getHideAnimation(); + if (animation) { + connect(animation, SIGNAL(finished()), this, SLOT(hideEnd())); + animation->start(QAbstractAnimation::DeleteWhenStopped); + m_active = true; + } else + hideEnd(); + + return m_active; +} + +void RemoveActivity::hideEnd() +{ + m_item->hide(); + QAbstractAnimation *animation = m_list.getRemoveAnimation(m_idx); + m_list.doRemoveItem(m_idx, m_notify); + if (m_destroyItem) + m_item->deleteLater(); + m_active = animation != 0; + if (animation) { + connect(animation, SIGNAL(finished()), this, SLOT(activityEnd())); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } else + activityEnd(); +} + +// InsertActivity + +InsertActivity::InsertActivity(int idx, ContentListItem* item, bool notify, ContentList &list) + : ContentListActivity(list) + , m_idx(idx) + , m_item(item) + , m_active(false) + , m_notify(notify) +{ +} + +bool InsertActivity::run() +{ + if (m_active || !m_item) + return false; + + m_idx = m_idx < 0 ? 0 : m_idx > m_list.itemCount() ? m_list.itemCount() : m_idx; + + + QAbstractAnimation *animation = m_list.getInsertAnimation(m_idx, m_item->contentHeight()); + if (animation) { + connect(animation, SIGNAL(finished()), this, SLOT(showItem())); + animation->start(QAbstractAnimation::DeleteWhenStopped); + m_active = true; + } else + showItem(); + + return m_active; +} + +void InsertActivity::showItem() +{ + m_list.doInsertItem(m_idx, m_item, m_notify); + m_item->show(); + QAbstractAnimation *animation = m_item->getShowAnimation(); + m_active = animation != 0; + if (animation) { + connect(animation, SIGNAL(finished()), this, SLOT(activityEnd())); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } else + activityEnd(); +} + +// MoveActivity + +MoveActivity::MoveActivity(int from, int to, ContentList &list) + : ContentListActivity(list) + , m_from(from) + , m_to(to) +{ +} + +bool MoveActivity::run() +{ + if (m_from < 0 || m_from >= m_list.itemCount()) + return false; + + m_to = m_to < 0 ? 0 : m_to > m_list.itemCount() ? m_list.itemCount() : m_to; + + if (m_from == m_to || (m_from == m_list.itemCount() - 1 && m_to > m_from)) + return false; + + addActivity(new RemoveActivity(m_from, false, false, m_list)); + addActivity(new InsertActivity(m_to, m_list.getItem(m_from), false, m_list)); + + return false; +} + +// ContentList + +ContentList::ContentList(QGraphicsItem *parent) + : QGraphicsItem(parent) + , m_boundingRect(0.0, 0.0, 0.0, 0.0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, true); +} + +ContentList::ContentList(QList<ContentListItem*> items, QGraphicsItem *parent) + : QGraphicsItem(parent) + , m_boundingRect(0.0, 0.0, 0.0, 0.0) +{ + setFlag(QGraphicsItem::ItemHasNoContents, true); + doAppendItems(items, false); +} + +void ContentList::doAppendItems(QList<ContentListItem*> items, bool notify) +{ + qreal top = 0; + foreach(ContentListItem *item, m_items) + top += item->contentHeight(); + foreach(ContentListItem *item, items) { + item->setParentItem(this); + item->setPos(0, top); + top += item->contentHeight(); + m_items.append(item); + m_boundingRect.setHeight(m_boundingRect.height() + item->contentHeight()); + if (notify) + emit newContentItem(item); + } +} + +ContentList::~ContentList() +{ + foreach(ContentListActivity *activity, m_queue) + activity->deleteLater(); +} + +qreal ContentList::width() const +{ + return m_boundingRect.width(); +} + +void ContentList::setWidth(qreal width) +{ + if (m_boundingRect.width() != width) { + prepareGeometryChange(); + m_boundingRect.setWidth(width); + update(); + foreach(ContentListItem *item, m_items) + item->updateGeometry(); + } +} + +QRectF ContentList::boundingRect() const +{ + return m_boundingRect; +} + +void ContentList::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); +} + +bool ContentList::insertItem(int idx, ContentListItem* item) +{ + if (getItemIndex(item) != -1) + return false; + addActivity(new InsertActivity(idx, item, true, *this)); + return true; +} + +bool ContentList::removeItem(int idx) +{ + if (idx < 0 || idx >= m_items.count()) + return false; + addActivity(new RemoveActivity(idx, true, true, *this)); + return true; +} + +bool ContentList::moveItem(int from, int to) +{ + if (from < 0 || from >= m_items.count()) + return false; + + to = to < 0 ? 0 : to > m_items.count() ? m_items.count() : to; + + if (from == to) + return true; + addActivity(new MoveActivity(from, to, *this)); + return true; +} + +void ContentList::addActivity(ContentListActivity *activity) +{ + m_queue.append(activity); + checkQueue(); +} + +void ContentList::checkQueue() +{ + while (m_queue.count() > 0 && !m_queue[0]->active()) + if (!m_queue[0]->run()) + m_queue.takeFirst()->deleteLater(); +} + +void ContentList::activityEnd() +{ + m_queue.takeFirst()->deleteLater(); + checkQueue(); +} + +void ContentList::doRemoveItem(int idx, bool notify) +{ + if (idx >= 0 && idx < m_items.count()) { + ContentListItem *item = m_items[idx]; + item->setParentItem(0); + m_items.removeAt(idx); + if (notify) { + prepareGeometryChange(); + m_boundingRect.setHeight(m_boundingRect.height() - item->contentHeight()); + update(); + emit contentItemRemoved(item); + } + } +} + +void ContentList::doInsertItem(int idx, ContentListItem * item, bool notify) +{ + if (idx >= 0 && idx <= m_items.count()) { + qreal top = 0; + for (int i = 0; i < idx; ++i) + top += m_items[i]->contentHeight(); + item->setPos(0, top); + item->setParentItem(this); + if (notify) { + prepareGeometryChange(); + m_boundingRect.setHeight(m_boundingRect.height() + item->contentHeight()); + update(); + emit newContentItem(item); + } + m_items.insert(idx, item); + } +} + +void ContentList::updateItems() +{ + qreal top = 0; + foreach(ContentListItem *item, m_items) { + item->setPos(0, top); + top += item->contentHeight(); + } + update(); +} + +void ContentList::sortItems(ContentListItemCompare compare) +{ + addActivity(new SortActivity(*this, compare)); +} + +void ContentList::appendItems(QList<ContentListItem*> items) +{ + addActivity(new AppendItemsActivity(*this, items)); +} diff --git a/shoplist/src/contentlist.h b/shoplist/src/contentlist.h new file mode 100644 index 0000000..122fa3d --- /dev/null +++ b/shoplist/src/contentlist.h @@ -0,0 +1,235 @@ +#ifndef CONTENTLIST_H +#define CONTENTLIST_H + +#include <QList> +#include <QObject> +#include <QGraphicsObject> +#include <QAbstractAnimation> +#include <QPointer> + +#define ITEM_TOP_PROPERTY_NAME "top" + +class ContentListItem : public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(qreal top READ getTop WRITE setTop); +#ifndef Q_OS_MAC + Q_INTERFACES(QGraphicsItem); +#endif +public: + ContentListItem(QGraphicsItem *parent = 0); + virtual qreal contentHeight() const = 0; + QRectF boundingRect() const; + + virtual QAbstractAnimation *getShowAnimation() = 0; + virtual QAbstractAnimation *getHideAnimation() = 0; + +protected: + void updateGeometry(); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); +private: + friend class ContentList; + QRectF m_geometry; + qreal getTop() { return pos().y(); } + void setTop(qreal top) { setPos(pos().x(), top); } + +}; + +class ContentListActivity; +class RemoveActivity; +class InsertActivity; + +class ContentList + : public QObject + , public QGraphicsItem +{ + Q_OBJECT +#ifndef Q_OS_MAC + Q_INTERFACES(QGraphicsItem); +#endif +public: + ContentList(QGraphicsItem *parent = 0); + ContentList(QList<ContentListItem*> items, QGraphicsItem *parent = 0); + ~ContentList(); + + bool insertItem(int idx, ContentListItem*); + bool addItem(ContentListItem *item) { return insertItem(m_items.count(), item); } + bool removeItem(int idx); + bool removeItem(ContentListItem* item) { return removeItem(getItemIndex(item)); } + bool moveItem(int from, int to); + bool moveItem(ContentListItem* item, int to) { return moveItem(getItemIndex(item), to); } + void appendItems(QList<ContentListItem*> items); + + int itemCount() const { return m_items.count(); } + ContentListItem* getItem(int idx) { return m_items.at(idx); } + int getItemIndex(ContentListItem* item) { return m_items.indexOf(item); } + + bool busy() { return m_queue.count() > 0; } + void addActivity(ContentListActivity *); + + void updateItems(); + + QRectF boundingRect() const; + qreal width() const; + void setWidth(qreal width); + +signals: + void newContentItem(QGraphicsItem *item); + void contentItemRemoved(QGraphicsItem *item); + +protected: + virtual QAbstractAnimation *getInsertAnimation(int idx, qreal height) = 0; + virtual QAbstractAnimation *getRemoveAnimation(int idx) = 0; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + QList<ContentListItem*> getItems() { return m_items; } + + typedef bool (*ContentListItemCompare)(const ContentListItem* s1, const ContentListItem* s2); + void sortItems(ContentListItemCompare compare); + +private: + friend class ContentListActivity; + friend class RemoveActivity; + friend class InsertActivity; + friend class SortActivity; + friend class AppendItemsActivity; + QList<ContentListItem*> m_items; + QList<ContentListActivity*> m_queue; + + QRectF m_boundingRect; + + void updateGeometry(); + + void checkQueue(); + void activityEnd(); + void doRemoveItem(int idx, bool notify); + void doInsertItem(int idx, ContentListItem* item, bool notify); + void doAppendItems(QList<ContentListItem*> items, bool notify); + +}; + +class ContentListActivity : public QObject +{ + Q_OBJECT +public: + ContentListActivity(ContentList &list) : m_list(list), m_insertPos(0) {} + virtual bool run() = 0; + virtual bool active(); + +protected: + ContentList &m_list; + void addActivity(ContentListActivity*); + +protected slots: + void activityEnd() { m_list.activityEnd(); } + +private: + int m_insertPos; + +}; + +class SignalActivity : public ContentListActivity +{ + Q_OBJECT +public: + SignalActivity(ContentList &list) : ContentListActivity(list) {} + bool run(); + +signals: + void notify(); +}; + +class SortActivity : public ContentListActivity +{ + Q_OBJECT +public: + SortActivity(ContentList &list, ContentList::ContentListItemCompare compare) + : ContentListActivity(list), m_compare(compare) {} + bool run(); + +private: + ContentList::ContentListItemCompare m_compare; +}; + +class AppendItemsActivity : public ContentListActivity +{ + Q_OBJECT +public: + AppendItemsActivity(ContentList &list, QList<ContentListItem*> items) + : ContentListActivity(list), m_items(items) {} + bool run(); + +private: + QList<ContentListItem*> m_items; +}; + +class AnimationActivity : public ContentListActivity +{ + Q_OBJECT +public: + AnimationActivity(QAbstractAnimation *animation, ContentList &list); + ~AnimationActivity(); + bool run(); + bool active() { return m_animation && m_animation->state() != QAbstractAnimation::Stopped; } + +private slots: + void animationEnd(); + +private: + QAbstractAnimation *m_animation; + +}; + +class RemoveActivity : public ContentListActivity +{ + Q_OBJECT +public: + RemoveActivity(int idx, bool destroyItem, bool notify, ContentList &list); + bool run(); + bool active() { return m_active; } + +private slots: + void hideEnd(); + +private: + int m_idx; + bool m_destroyItem; + ContentListItem *m_item; + bool m_active; + const bool m_notify; + +}; + +class InsertActivity : public ContentListActivity +{ + Q_OBJECT +public: + InsertActivity(int idx, ContentListItem* item, bool notify, ContentList &list); + bool run(); + bool active() { return m_active; } + +private slots: + void showItem(); + +private: + int m_idx; + QPointer<ContentListItem> m_item; + bool m_active; + const bool m_notify; + +}; + +class MoveActivity : public ContentListActivity +{ +public: + MoveActivity(int from, int to, ContentList &list); + bool run(); + +private: + int m_from; + int m_to; + +}; + +#endif // CONTENTLIST_H diff --git a/shoplist/src/designinformation.cpp b/shoplist/src/designinformation.cpp new file mode 100644 index 0000000..b803835 --- /dev/null +++ b/shoplist/src/designinformation.cpp @@ -0,0 +1,361 @@ +#include "designinformation.h" +#include <QDebug> +#include <cmath> + +static const char *groceriesItemsText[] = { + "Apples","Avocado","Bacon","Bagels","Baking powder","Balsamic vinegar","Bananas", + "Basil","Basmati rice","Beans","Beef","Bell peppers","Biscuits","Blackberries","Blueberries", + "Brats","Bread (not white)","Broccoli","Brownie mix","Brussels sprouts","Burger patties", + "Butter","Cabbage","Cake mix","Canned tomatoes","Cantaloupe","Carrots","Cauliflower","Cereal", + "Cheddar","Cheese sticks","Cherries","Chicken","Chips","Chocolate chips","Cookie dough", + "Cookie mix","Cookies","Corn","Cottage cheese","Couscous","Crackers","Cream cheese", + "Cucumbers","Eggs","English muffins","Fish","Flour","Fruit snacks","Garlic","Ginger", + "Graham crackers","Granola bars","Grapefruit","Grapes","Green beans","Ground beef", + "Ground turkey","Ham","Hamburger buns","Honey","Hot cereal","Hot dogs","Hot-dog buns", + "Jello","Kiwi","Lasagna noodles","Lemons","Lettuce","Limes","Mac & Cheese","Marshmallow", + "Melon","Mozzarella","Muffin mix","Mushrooms","Mustard","Nectarines","Noodle & Sauce Mix", + "Oatmeal","Olive Oil","Onions","Oranges","Pancake mix","Pancake syrup","Parmesan", + "Pasta (whole wheat)","Peaches","Peanut butter","Pears","Peas","Peppers","Pizza sauce", + "Plums","Popcorn","Pork chops","Pork roast","Potatoes","Pudding","Quinoa","Raisins", + "Raspberries","Raw nuts","Rice","RiceARoni","Risotto","Sausage","Shredded cheese", + "Sliced cheese","Snap peas","Soup","Sour cream","Spaghetti sauce","Spaghetti", + "Squash","Steaks","Strawberries","String beans","Sugar","Tomato sauce","Tomatoes", + "Tortillas","Tuna","Veal","White bread","White vinegar","Whole wheat bread","Yogurt","Zucchini", + 0 +}; + +static const char *toiletriesItemsText[] = { + "Antiseptic cream","Baby lotion","Band aids","Bath soap","Body wash ","Conditioner", + "Contact solution","Deodorant","Facial soap","Facial tissue","Floss","Hair dye", + "Hand lotion","Leave-in conditioner","Moisturizer","Mouthwash","Pads","Rash creams", + "Razors","Shampoo","Shaving cream","Soap","Suntan lotion","Tampons","Toilet paper", + "Toothpaste","Vitamins","Wipes","Scented toilet paper", + 0 +}; + +static const char *cleaningItemsText[] = { + "Aluminum foil","Bleach","Cleanser","Dishwasher soap","Disinfectant","Dixie cups", + "Dusting spray","Fabric softener","Kleenex","Laundry detergent","Napkins","Paper towels", + "Plastic wrap","Toilet paper","Trash bags","Ziplock bags","Wax paper","Floor wax", + 0 +}; + +static const char *beveragesItemsText[] = { + "Apple juice","Beer","Black tea","Bottled water","Champagne","Cordial","Green tea","Ice tea", + "Juice boxes","KoolAid","Lemonade","Milk","Orange juice","Coca-Cola","Powerade", + "Raspberry juice","Red wine","Skinned milk","Sparkling water","Tea","Vodka","White wine", + "Coke Zero", + 0 +}; + +static const char *petItemsText[] = { + "Bird food","Bone","Cat food","Cat Litter","Catnip","Dog biscuit","Dog food","Dog toys", + "Dog treats","Fish food","Fish tanks pebbles","Hamster food","Hamster hammock","Pet wash", + "Turtle soap", + 0 +}; + +static const char *gardenItemsText[] = { + "Fertilizer","Flower seeds","Hose","Leaf bags","Manure","Scissors","Shovel","Vegetable seeds", + "Pesticide","Mouse trap", + 0 +}; + +DesignInformation::DesignInformation() + : m_scaleFactor(1, 1) +{ + + m_stringLists["category.string_list.beverages"] = beveragesItemsText; + m_stringLists["category.string_list.cleaning"] = cleaningItemsText; + m_stringLists["category.string_list.garden"] = gardenItemsText; + m_stringLists["category.string_list.groceries"] = groceriesItemsText; + m_stringLists["category.string_list.pet"] = petItemsText; + m_stringLists["category.string_list.toiletries"] = toiletriesItemsText; + + m_pixmaps["category.pic.beverages"] = new ValueData<QPixmap, QString>(":/images/list_icon_beverages.png"); + m_pixmaps["category.shadow.beverages"] = new ValueData<QPixmap, QString>(":/images/list_shadow_beverages_50.png"); + m_pixmaps["category.header.beverages"] = new ValueData<QPixmap, QString>(":/images/list_title_beverages.png"); + + m_pixmaps["category.pic.cleaning"] = new ValueData<QPixmap, QString>(":/images/list_icon_cleaning.png"); + m_pixmaps["category.shadow.cleaning"] = new ValueData<QPixmap, QString>(":/images/list_shadow_cleaning_50.png"); + m_pixmaps["category.header.cleaning"] = new ValueData<QPixmap, QString>(":/images/list_title_cleaning.png"); + + m_pixmaps["category.pic.garden"] = new ValueData<QPixmap, QString>(":/images/list_icon_garden.png"); + m_pixmaps["category.shadow.garden"] = new ValueData<QPixmap, QString>(":/images/list_shadow_garden_50.png"); + m_pixmaps["category.header.garden"] = new ValueData<QPixmap, QString>(":/images/list_title_garden.png"); + + m_pixmaps["category.pic.groceries"] = new ValueData<QPixmap, QString>(":/images/list_icon_groceries.png"); + m_pixmaps["category.shadow.groceries"] = new ValueData<QPixmap, QString>(":/images/list_shadow_groceries_50.png"); + m_pixmaps["category.header.groceries"] = new ValueData<QPixmap, QString>(":/images/list_title_groceries.png"); + + m_pixmaps["category.pic.pet"] = new ValueData<QPixmap, QString>(":/images/list_icon_pet.png"); + m_pixmaps["category.shadow.pet"] = new ValueData<QPixmap, QString>(":/images/list_shadow_pet_50.png"); + m_pixmaps["category.header.pet"] = new ValueData<QPixmap, QString>(":/images/list_title_pet.png"); + + m_pixmaps["category.pic.toiletries"] = new ValueData<QPixmap, QString>(":/images/list_icon_toiletries.png"); + m_pixmaps["category.shadow.toiletries"] = new ValueData<QPixmap, QString>(":/images/list_shadow_toiletries_50.png"); + m_pixmaps["category.header.toiletries"] = new ValueData<QPixmap, QString>(":/images/list_title_toiletries.png"); + + m_pixmaps["category.background"] = new ValueData<QPixmap, QString>(":/images/list_background.png"); + m_pixmaps["category.feather"] = new ValueData<QPixmap, QString>(":/images/list_feather.png"); + + m_positions["category.pic"] = new ValueData<QPointF>(QPointF(3, 0)); + m_positions["category.header"] = new ValueData<QPointF>(QPointF(110, 54)); + m_positions["category.background"] = new ValueData<QPointF>(QPointF(0, 39)); + m_positions["category.shadow"] = new ValueData<QPointF>(QPointF(3, -1)); + m_positions["category.list"] = new ValueData<QPointF>(QPointF(2, m_listTop)); + m_positions["category.feather"] = new ValueData<QPointF>(QPointF(3, m_listTop)); + m_positions["category.scrollbar"] = new ValueData<QPointF>(QPointF(415, 154)); + + m_verticalSizes["category.list_height"] = new ValueData<qreal>(800.0); + + // --------------------------------------------------------------------- + + m_pixmaps["list_item.checked"] = new ValueData<QPixmap, QString>(":/images/list_button_checked.png"); + m_positions["list_item.checked"] = new ValueData<QPointF>(QPointF(27, 28)); + + m_pixmaps["list_item.not_checked"] = new ValueData<QPixmap, QString>(":/images/list_button_check.png"); + m_positions["list_item.not_checked"] = new ValueData<QPointF>(QPointF(27, 28)); + + m_pixmaps["list_item.bottom_line"] = new ValueData<QPixmap, QString>(":/images/list_line.png"); + m_positions["list_item.bottom_line"] = new ValueData<QPointF>(QPointF(1, 88)); + + m_pixmaps["list_item.selected"] = new ValueData<QPixmap, QString>(":/images/list_selecteditem_background.png"); + m_positions["list_item.selected"] = new ValueData<QPointF>(QPointF(1, 0)); + + m_horizontalSizes["list_item.text_offset"] = new ValueData<qreal>(20.0); + + m_verticalSizes["list_item.height"] = new ValueData<qreal>(m_referenceItemHeight); + + m_fontSizes["list_item.font_size"] = new ValueData<int>(30); + + m_verticalSizes["list.vertical_speed"] = new ValueData<qreal>(1500.0); + + + // --------------------------------------------------------------------- + + m_pixmaps["background"] = new ValueData<QPixmap, QString>(":/images/background.png"); + + m_fontSizes["title"] = new ValueData<int>(18); + m_positions["title"] = new ValueData<QPointF>(QPointF(184, 7)); + + m_pixmaps["closeButton"] = new ValueData<QPixmap, QString>(":/images/button_close.png"); + m_positions["closeButton"] = new ValueData<QPointF>(QPointF(426, 8)); + m_horizontalSizes["close_button_spacing"] = new ValueData<qreal>(28.0); + + m_horizontalSizes["listset.center_position"] = new ValueData<qreal>(17.0); + + m_pixmaps["list.scrollbar.landscape"] = new ValueData<QPixmap, QString>(":/images/list_landscape_scrollbar.png"); + m_pixmaps["list.scrollbar.portrait"] = new ValueData<QPixmap, QString>(":/images/list_portrait_scrollbar.png"); + m_pixmaps["list.scrollbar.knob"] = new ValueData<QPixmap, QString>(":/images/list_scrollbar_knob.png"); + + // --------------------------------------------------------------------- + + m_pixmaps["alldone.background"] = new ValueData<QPixmap, QString>(":/images/list_alldone_background.png"); + + m_pixmaps["alldone.startagain_off"] = new ValueData<QPixmap, QString>(":/images/list_alldone_bt_startagain_off.png"); + m_positions["alldone.startagain_off"] = new ValueData<QPointF>(QPointF(-4, 54)); + + m_pixmaps["alldone.close_button"] = new ValueData<QPixmap, QString>(":/images/button_close.png"); + m_positions["alldone.close_button"] = new ValueData<QPointF>(QPointF(100, -95)); +} + +DesignInformation::~DesignInformation() +{ + qDeleteAll(m_pixmaps.values()); + qDeleteAll(m_positions.values()); + qDeleteAll(m_verticalSizes.values()); + qDeleteAll(m_horizontalSizes.values()); + qDeleteAll(m_fontSizes.values()); +} + +template<class T, class R> +void DesignInformation::resetValues(QMap<QString, ValueData<T,R>* > map) +{ + QList<ValueData<T,R>*> values = map.values(); + for (int i = 0; i < values.count(); ++i) { + values[i]->m_upToDate = false; + values[i]->m_value = T(); + } + +} + +void DesignInformation::setScaleFactor(QSizeF factor) +{ + DesignInformation *obj = DesignInformation::instance(); + resetValues(obj->m_pixmaps); + resetValues(obj->m_positions); + resetValues(obj->m_verticalSizes); + resetValues(obj->m_horizontalSizes); + resetValues(obj->m_fontSizes); + obj->m_scaleFactor = QSizeF(factor.width(), 1.0); + + DesignInformation::getPixmap("list_item.bottom_line"); + obj->m_scaleFactor = factor; +} + +qreal DesignInformation::calculateScaleFactor(QSizeF screenSize) +{ + qreal result = 1.0; + if (screenSize.width() < m_referenceWidth) { + result = m_referenceWidth / screenSize.width(); + screenSize.setWidth(result * screenSize.width()); + screenSize.setHeight(result * screenSize.height()); + } + + qreal referenceHeight = m_listTop + m_minimumVisibleItems * m_referenceItemHeight; + + if (screenSize.height() < referenceHeight) { + result *= referenceHeight / screenSize.height(); + screenSize.setWidth(result * screenSize.width()); + screenSize.setHeight(result * screenSize.height()); + } + return qMin(qreal(1.0) / result, qreal(1.0)); +} + +QSizeF DesignInformation::getScaleFactor(QSizeF screenSize) +{ + qreal scale1 = calculateScaleFactor(screenSize); + qreal scale2 = calculateScaleFactor(QSizeF(screenSize.height(), screenSize.width())); + + qreal factor = qMin(scale1, scale2); + return QSizeF(factor, factor); +} + +static QPixmap pixmapScale(QString reference, QSizeF scale) +{ + QPixmap ref(reference); + return ref.scaled(scale.width() * ref.width(), scale.height() * ref.height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + +void DesignInformation::setScreenSize(QSizeF size) +{ + DesignInformation *obj = DesignInformation::instance(); + if (obj->m_pixmaps.contains("background")) { + ValueData<QPixmap, QString>* data = obj->m_pixmaps["background"]; + QString file = size.width() > size.height() ? ":/images/background.png" + : ":/images/background_landscape.png"; + QPixmap ref = QPixmap(file); + data->m_value = ref.scaled(size.width(), size.height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + data->m_upToDate = true; + } + + QPixmap pixmap = getPixmap("category.background"); + + if (obj->m_horizontalSizes.contains("listset.center_position")) { + ValueData<qreal> *data = obj->m_horizontalSizes["listset.center_position"]; + data->m_value = (size.width() - pixmap.width()) / 2; + data->m_upToDate = true; + } + + if (obj->m_verticalSizes.contains("category.list_height")) { + ValueData<qreal> *data = obj->m_verticalSizes["category.list_height"]; + + QPointF backgroundPos = getPosition("category.background"); + QPointF listPos = getPosition("category.list"); + + data->m_value = backgroundPos.y() + pixmap.height() - listPos.y(); + data->m_value = qMin(size.height() - listPos.y(), data->m_value); + data->m_upToDate = true; + } + + if (obj->m_pixmaps.contains("list.scrollbar.portrait")) { + ValueData<QPixmap, QString>* data = obj->m_pixmaps["list.scrollbar.portrait"]; + QPixmap ref = QPixmap(":images/list_portrait_scrollbar.png"); + QSizeF scale = obj->m_scaleFactor; + data->m_value = ref.scaled(scale.width() * ref.width(), + getVerticalSize("category.list_height") - 20, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + data->m_upToDate = true; + } + + +} + +DesignInformation *DesignInformation::instance() +{ + static DesignInformation *obj(new DesignInformation()); + return obj; +} + +template<class T, class R, class F> +T DesignInformation::getValue(QMap<QString, ValueData<T,R>* > map, + QString key, F factory, QSizeF scale) +{ + if (map.contains(key)) { + ValueData<T,R> *data = map.value(key); + if (data->m_upToDate) + return data->m_value; + data->m_value = factory(data->m_reference, scale); + data->m_upToDate = true; + return data->m_value; + } + qWarning() << "key" << key << "not found"; + return T(); +} + +QPixmap DesignInformation::getPixmap(const QString &name) +{ + DesignInformation *obj = DesignInformation::instance(); + return getValue(obj->m_pixmaps, name, pixmapScale, obj->m_scaleFactor); +} + +static QPointF pointFScale(QPointF reference, QSizeF scale) +{ + return QPointF(reference.x() * scale.width(), reference.y() * scale.height()); +} + +QPointF DesignInformation::getPosition(const QString &name) +{ + DesignInformation *obj = DesignInformation::instance(); + return getValue(obj->m_positions, name, pointFScale, obj->m_scaleFactor); +} + +static qreal verticalScale(qreal reference, QSizeF scale) +{ + return reference * scale.height(); +} + +qreal DesignInformation::getVerticalSize(const QString &name) +{ + DesignInformation *obj = DesignInformation::instance(); + return getValue(obj->m_verticalSizes, name, verticalScale, obj->m_scaleFactor); +} + +static qreal horizontalScale(qreal reference, QSizeF scale) +{ + return reference * scale.width(); +} + +qreal DesignInformation::getHorizontalSize(const QString &name) +{ + DesignInformation *obj = DesignInformation::instance(); + return getValue(obj->m_horizontalSizes, name, horizontalScale, obj->m_scaleFactor); +} + +static int fontSizeScale(int reference, QSizeF scale) +{ + return reference * sqrt(scale.width() * scale.height()); +} + +int DesignInformation::getFontSize(const QString name) +{ + DesignInformation *obj = DesignInformation::instance(); + return getValue(obj->m_fontSizes, name, fontSizeScale, obj->m_scaleFactor); + +} + +QStringList DesignInformation::getStringList(const QString &name) +{ + DesignInformation *obj = DesignInformation::instance(); + if (obj && obj->m_stringLists.contains(name)) { + QStringList result; + for (const char **item = obj->m_stringLists.value(name); *item; ++item) + result << *item; + return result; + } + qWarning() << "position" << name << "not found"; + return QStringList(); +} diff --git a/shoplist/src/designinformation.h b/shoplist/src/designinformation.h new file mode 100644 index 0000000..698f9a6 --- /dev/null +++ b/shoplist/src/designinformation.h @@ -0,0 +1,55 @@ +#ifndef DESIGNINFORMATION_H +#define DESIGNINFORMATION_H + +#include <QString> +#include <QMap> +#include <QPixmap> + +class DesignInformation +{ +public: + static QPixmap getPixmap(const QString &name); + static QPointF getPosition(const QString &name); + static qreal getVerticalSize(const QString &name); + static qreal getHorizontalSize(const QString &name); + static int getFontSize(const QString name); + static QStringList getStringList(const QString &name); + static void setScaleFactor(QSizeF factor); + static QSizeF getScaleFactor(QSizeF screenSize); + static void setScreenSize(QSizeF size); +private: + template<class T, class R = T> class ValueData + { + public: + ValueData(R reference) : m_upToDate(false), m_reference(reference) {} + bool m_upToDate; + const R m_reference; + T m_value; + }; +private: + QSizeF m_scaleFactor; + QMap<QString, ValueData<QPixmap, QString>* > m_pixmaps; + QMap<QString, ValueData<QPointF>* > m_positions; + QMap<QString, ValueData<qreal>* > m_verticalSizes; + QMap<QString, ValueData<qreal>* > m_horizontalSizes; + QMap<QString, ValueData<int>* > m_fontSizes; + + QMap<QString, const char**> m_stringLists; + + static const int m_listTop = 142; + static const int m_referenceWidth = 480; + static const int m_minimumVisibleItems = 3; + static const int m_referenceItemHeight = 90; + + DesignInformation(); + ~DesignInformation(); + static inline DesignInformation *instance(); + static qreal calculateScaleFactor(QSizeF screenSize); + + template<class T, class R, class F> + static T getValue(QMap<QString, ValueData<T,R>* > map, QString key, F factory, QSizeF scale); + template<class T, class R> + static void resetValues(QMap<QString, ValueData<T,R>* > map); +}; + +#endif // DESIGNINFORMATION_H diff --git a/shoplist/src/gesturebox.cpp b/shoplist/src/gesturebox.cpp new file mode 100644 index 0000000..9fefe0e --- /dev/null +++ b/shoplist/src/gesturebox.cpp @@ -0,0 +1,359 @@ +#include "gesturebox.h" + +#include <QApplication> +#include <QGraphicsView> +#include <QMouseEvent> +#include <QDebug> +#include <QPointer> +#include <QTime> + +#include "gesturebox_p.h" + +// GestureData + +#ifdef Q_OS_SYMBIAN +static const int move_threshold = 20; +#else +static const int move_threshold = 5; +#endif + +GestureData::GestureData(QPoint pos, const QList<GestureBox*> &boxes, + QGraphicsView *view, QList<QEvent*> &ignoreList, bool sendClickEvents) + : m_state(Pressed) + , m_view(view) + , m_pressPos(pos) + , m_scenePressPos(m_view->mapToScene(m_pressPos)) + , m_boxes(boxes) + , m_ignoreList(ignoreList) + , m_time(QTime::currentTime()) + , m_currentScenePos(m_scenePressPos) + , m_speed(0.0, 0.0) + , m_sendClickEvents(sendClickEvents) +{ +} + +void GestureData::sendClick() +{ + if (m_sendClickEvents) { + QMouseEvent *press = new QMouseEvent(QEvent::MouseButtonPress, + m_pressPos, Qt::LeftButton, + Qt::LeftButton, Qt::NoModifier); + QMouseEvent *release = new QMouseEvent(QEvent::MouseButtonRelease, + m_pressPos, Qt::LeftButton, + Qt::LeftButton, Qt::NoModifier); + m_ignoreList << press; + m_ignoreList << release; + QApplication::postEvent(m_view->viewport(), press); + QApplication::postEvent(m_view->viewport(), release); + } +} + +void GestureData::release(QPoint pos) +{ + Q_UNUSED(pos); + switch (m_state) { + case Pressed: + sendClick(); + break; + case Moving: + foreach(GestureBox *box, m_boxes) + box->gestureEnd(box->mapFromScene(m_currentScenePos), m_speed); + break; + default: + break; + } + m_state = Ended; + deleteLater(); +} + +void GestureData::move(QPoint pos) +{ + if (m_state == Pressed && + abs(pos.x() - m_pressPos.x()) < move_threshold && + abs(pos.y() - m_pressPos.y()) < move_threshold) { + return; + } + QTime time(QTime::currentTime()); + QPointF scenePos = m_view->mapToScene(pos); + QPointF move = scenePos - m_currentScenePos; + qreal secs = (qreal)(m_time.msecsTo(time)) / 1000; + m_currentScenePos = scenePos; + m_speed = QPointF(move.x() / secs, move.y() / secs); + m_time = time; + + switch (m_state) { + case Pressed: + foreach(GestureBox *box, m_boxes) { + box->gestureStart(box->mapFromScene(m_scenePressPos)); + box->gestureMove(box->mapFromScene(m_currentScenePos), move, m_speed); + } + m_state = Moving; + break; + case Moving: + foreach(GestureBox *box, m_boxes) + box->gestureMove(box->mapFromScene(m_currentScenePos), move, m_speed); + break; + default: + break; + } +} + +// ViewData + +ViewData::~ViewData() +{ + if (m_gesture) + m_gesture->deleteLater(); +} + +bool ViewData::ignoreEvent(QEvent *event) +{ + return m_ignored.removeAll(event); +} + +bool ViewData::startGesture(GestureBox *box, QPointF scenePos, bool &sendClick) +{ + bool result = true; + bool click = true; + box->gestureMousePress(box->mapFromScene(scenePos), result, click); + if (result && !click) + sendClick = false; + return result; +} + +bool ViewData::press(QPoint pos) +{ + if (m_gesture && !m_gesture->ended()) { + qWarning() << "ERROR: unexpected mouse press during gesture."; + return false; + } + + if (!m_view || !m_view->scene()) + return false; + + QList<GestureBox*> boxes; + bool sendClick = true; + QPointF scenePos = m_view->mapToScene(pos); + + QList<QGraphicsItem*> itemList = m_view->scene()->items(scenePos, + Qt::IntersectsItemBoundingRect, + Qt::AscendingOrder); + + foreach(QGraphicsItem *item, itemList) { + GestureBox *box = dynamic_cast<GestureBox*>(item); + if (box && startGesture(box, scenePos, sendClick)) + boxes.append(box); + } + + if (boxes.isEmpty()) + return false; + + m_gesture = new GestureData(pos, boxes, m_view, m_ignored, sendClick); + return true; + +} + +bool ViewData::release(QPoint pos) +{ + if (!m_gesture || m_gesture->ended()) + return false; + + m_gesture->release(pos); + return true; +} + +bool ViewData::move(QPoint pos) +{ + if (!m_gesture || m_gesture->ended()) + return false; + + m_gesture->move(pos); + return true; +} + +QList<GestureBox*> ViewData::getBoxList(QPoint pos) { + QList<GestureBox*> result; + if (m_view->scene()) { + QPointF scenePos = m_view->mapToScene(pos); + QGraphicsItem *item = m_view->scene()->itemAt(scenePos); + while (item) { + GestureBox *box = dynamic_cast<GestureBox*>(item); + if (box && box->boundingRect().contains(box->mapFromScene(scenePos))) + result.append(box); + item = item->parentItem(); + } + } + return result; +} + +// GestureObserver + +GestureObserver *GestureObserver::instance() +{ + static GestureObserver *obj = new GestureObserver(); + return obj; +} + +void GestureObserver::addBox(GestureBox *box) +{ + if (box->scene()) { + foreach(QGraphicsView *view, box->scene()->views()) { + ViewData *data = getViewData(view); + if (data->m_boxes.indexOf(box) < 0) { + data->m_boxes.append(box); + m_boxes.insertMulti(box, data); + } + } + } +} + +void GestureObserver::removeBox(GestureBox *box) +{ + QList<ViewData*> dataList = m_boxes.values(box); + foreach(ViewData* data, dataList) { + data->m_boxes.removeAll(box); + if (data->m_boxes.count() == 0) { + m_views.remove(data->view()->viewport()); + data->view()->viewport()->removeEventFilter(this); + delete data; + } + } + m_boxes.remove(box); +} + + +ViewData *GestureObserver::getViewData(QGraphicsView *view) +{ + if (m_views.contains(view->viewport())) + return m_views.value(view->viewport()); + ViewData *data = new ViewData(view); + view->viewport()->installEventFilter(this); + connect(view->viewport(), SIGNAL(destroyed()), this, SLOT(viewDestroyed())); + m_views.insert(view->viewport(), data); + return data; +} + +void GestureObserver::viewDestroyed() +{ + if (!sender()->isWidgetType()) + return; + QWidget *viewport = qobject_cast<QWidget*>(sender()); + if (viewport || m_views.contains(viewport)) { + ViewData *data = m_views.take(viewport); + foreach (GestureBox *box, data->m_boxes) { + QList<ViewData*> boxes = m_boxes.values(box); + m_boxes.remove(box); + + foreach(ViewData *viewData, boxes) { + if (viewData->widget() != viewport) + m_boxes.insertMulti(box, viewData); + } + } + delete data; + } + +} + +bool GestureObserver::eventFilter(QObject *object, QEvent *event) +{ + if (!object->isWidgetType()) + return false; + + QEvent::Type type = event->type(); + if (type != QEvent::MouseButtonPress && type != QEvent::MouseButtonRelease && + type != QEvent::MouseMove) + return false; + + QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event); + if (!mouseEvent || mouseEvent->modifiers() != Qt::NoModifier) + return false; + + QWidget *view = dynamic_cast<QWidget*>(object); + if (!view || !m_views.contains(view)) + return false; + + ViewData *data = m_views[view]; + if (!data || data->ignoreEvent(event)) + return false; + + bool consumed = false; + + switch (mouseEvent->type()) { + case QEvent::MouseButtonPress: + consumed = mouseEvent->buttons() == Qt::LeftButton && data->press(mouseEvent->pos()); + break; + case QEvent::MouseButtonRelease: + consumed = data->release(mouseEvent->pos()); + break; + case QEvent::MouseMove: + consumed = data->move(mouseEvent->pos()); + break; + default: + break; + } + return consumed; +} + +// GestureBox + +GestureBox::GestureBox(QGraphicsItem *parent) + : QGraphicsItem(parent) +{ + GestureObserver::instance()->addBox(this); +} + +GestureBox::~GestureBox() +{ + GestureObserver::instance()->removeBox(this); +} + +QRectF GestureBox::rect() const +{ + QRectF result(m_boundingRect); + result.moveTo(pos()); + return result; +} + +void GestureBox::setRect(QRectF value) +{ + setPos(value.topLeft()); + value.moveTo(0.0, 0.0); + if (value != m_boundingRect) { + prepareGeometryChange(); + m_boundingRect = value; + update(); + } +} + +void GestureBox::setRect(qreal x, qreal y, qreal width, qreal height) +{ + setRect(QRectF(x, y, width, height)); +} + +QRectF GestureBox::boundingRect() const +{ + return m_boundingRect; +} + +void GestureBox::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); +} + +QVariant GestureBox::itemChange(GraphicsItemChange change, const QVariant &value) +{ + switch (change) { + case QGraphicsItem::ItemSceneHasChanged: + case QGraphicsItem::ItemParentHasChanged: + GestureObserver::instance()->addBox(this); + break; + default: + break; + } + return value; +} + + diff --git a/shoplist/src/gesturebox.h b/shoplist/src/gesturebox.h new file mode 100644 index 0000000..302f65c --- /dev/null +++ b/shoplist/src/gesturebox.h @@ -0,0 +1,33 @@ +#ifndef GESTUREBOX_H +#define GESTUREBOX_H + +#include <QGraphicsItem> + +class GestureBox : public QGraphicsItem +{ +public: + GestureBox(QGraphicsItem *parent = 0); + ~GestureBox(); + + QRectF rect() const; + void setRect(QRectF); + void setRect(qreal, qreal, qreal, qreal); + QRectF boundingRect() const; + +protected: + virtual void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick) = 0; + virtual void gestureStart(QPointF pos) = 0; + virtual void gestureMove(QPointF pos, QPointF movement, QPointF speed) = 0; + virtual void gestureEnd(QPointF pos, QPointF speed) = 0; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + +private: + friend class GestureData; + friend class ViewData; + QRectF m_boundingRect; + +}; + +#endif // GESTUREBOX_H diff --git a/shoplist/src/gesturebox_p.h b/shoplist/src/gesturebox_p.h new file mode 100644 index 0000000..f4f8452 --- /dev/null +++ b/shoplist/src/gesturebox_p.h @@ -0,0 +1,89 @@ +#ifndef GESTUREBOX_P_H +#define GESTUREBOX_P_H + +#include <QObject> +#include <QPointer> +#include <QTime> +#include <QPoint> +#include <QPointF> +#include <QGraphicsView> + +#include "gesturebox.h" + +class GestureData : public QObject +{ +public: + inline GestureData(QPoint pos, const QList<GestureBox*> &boxes, QGraphicsView *view, + QList<QEvent*> &ignoreList, bool sendClickEvents); + + inline void release(QPoint pos); + inline void move(QPoint pos); + inline bool ended() const { return m_state == Ended; } + +private: + enum State {Pressed, Moving, Ended}; + State m_state; + QGraphicsView * const m_view; + const QPoint m_pressPos; + const QPointF m_scenePressPos; + const QList<GestureBox*> m_boxes; + QList<QEvent*> &m_ignoreList; + QTime m_time; + QPointF m_currentScenePos; + QPointF m_speed; + bool m_sendClickEvents; + + void sendClick(); +}; + +class ViewData +{ +public: + ViewData(QGraphicsView *view) : m_view(view), m_gesture(0), m_widget(view->viewport()) {} + inline ~ViewData(); + + inline bool press(QPoint pos); + inline bool release(QPoint pos); + inline bool move(QPoint pos); + + QGraphicsView *view() { return m_view; } + QWidget *widget() { return m_widget; } + inline bool ignoreEvent(QEvent *event); + + QList<GestureBox*> m_boxes; + +private: + QGraphicsView *const m_view; + QPointer<GestureData> m_gesture; + QList<QEvent*> m_ignored; + QWidget * const m_widget; + + inline QList<GestureBox*> getBoxList(QPoint pos); + inline bool startGesture(GestureBox *box, QPointF scenePos, bool &sendClick); +}; + +class GestureObserver : public QObject +{ + Q_OBJECT +public: + static inline GestureObserver *instance(); + inline void addBox(GestureBox *box); + inline void removeBox(GestureBox *box); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +private: + QHash<QWidget*, ViewData*> m_views; + QHash<GestureBox*, ViewData*> m_boxes; + + GestureObserver() {} + ~GestureObserver() {} + ViewData *getViewData(QGraphicsView *view); + +private slots: + void viewDestroyed(); + +}; + +#endif // GESTUREBOX_P_H diff --git a/shoplist/src/listshadow.cpp b/shoplist/src/listshadow.cpp new file mode 100644 index 0000000..d6e3126 --- /dev/null +++ b/shoplist/src/listshadow.cpp @@ -0,0 +1,58 @@ +#include "listshadow.h" +#include <QPropertyAnimation> +#include "designinformation.h" + +ListShadow::ListShadow(QGraphicsItem *parent) + : QGraphicsPixmapItem(parent) + , m_focused(false) + , m_completed(false) + , m_animation(0) +{ + setOpacity(getTargetOpacity()); + setVisible(opacity() != 0.0); +} + +ListShadow::~ListShadow() +{ + if (m_animation) + m_animation->deleteLater(); +} + +void ListShadow::setFocused(bool focused) +{ + if (focused != m_focused) { + m_focused = focused; + createAnimation(getTargetOpacity()); + } +} + +void ListShadow::setCompleted(bool completed) +{ + if (m_completed != completed) { + m_completed = completed; + createAnimation(getTargetOpacity()); + } +} + +qreal ListShadow::getTargetOpacity() +{ + return !m_focused ? 1.0 : m_completed ? 0.2 : 0.0; +} + +void ListShadow::createAnimation(qreal target) +{ + if (m_animation) + m_animation->deleteLater(); + + QPropertyAnimation* animation = new QPropertyAnimation(this, "opacity"); + animation->setEasingCurve(QEasingCurve::OutExpo); + animation->setEndValue(target); + animation->setDuration(500); + if (target == 0.0) + connect(animation, SIGNAL(finished()), this, SLOT(doHide())); + connect(animation, SIGNAL(finished()), this, SLOT(animationEnd())); + + m_animation = animation; + m_animation->start(QAbstractAnimation::DeleteWhenStopped); + show(); +} diff --git a/shoplist/src/listshadow.h b/shoplist/src/listshadow.h new file mode 100644 index 0000000..faa913f --- /dev/null +++ b/shoplist/src/listshadow.h @@ -0,0 +1,34 @@ +#ifndef LISTSHADOW_H +#define LISTSHADOW_H + +#include <QAbstractAnimation> +#include <QGraphicsPixmapItem> + +class ListShadow : public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity); +public: + ListShadow(QGraphicsItem *parent = 0); + ~ListShadow(); + + bool focused() const { return m_focused; } + bool completed() const { return m_completed; } + + void setFocused(bool); + void setCompleted(bool); + +private slots: + void doHide() { hide(); } + void animationEnd() { m_animation = 0; } + +private: + bool m_focused; + bool m_completed; + QAbstractAnimation *m_animation; + + qreal getTargetOpacity(); + void createAnimation(qreal opacity); +}; + +#endif // LISTSHADOW_H diff --git a/shoplist/src/main.cpp b/shoplist/src/main.cpp new file mode 100644 index 0000000..c42b5c0 --- /dev/null +++ b/shoplist/src/main.cpp @@ -0,0 +1,54 @@ +#include <QtGui> +#include <QDebug> + +#include "maincontainer.h" +#include "designinformation.h" +#include "shoppinglistcategoryui.h" +#include "shoppinglistview.h" + +static ShoppingListCategoryUI *createList(const QString &category, int itemCount) +{ + ShoppingListCategoryUI *result = new ShoppingListCategoryUI(category); + QString textsKey = "category.string_list." + category; + QStringList texts = DesignInformation::getStringList(textsKey); + + QList<ContentListItem*> items; + + for (int i = 0; i < itemCount && i < texts.count(); ++i) { + ShoppingListItem *item = new ShoppingListItem(QString::number(i + 1) + " - " + texts[i], + result->list(), i); + items.append(item); + } + result->list()->appendItems(items); + + return result; +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + MainContainer* lists = new MainContainer(); + + QGraphicsScene scene; + ShoppingListView view(lists); + view.setScene(&scene); + + + QObject::connect(QApplication::desktop(), SIGNAL(resized(int)), &view, SLOT(resized(int))); + + view.adjustScalingFactor(); + view.adjustScreenSize(); + + lists->add(createList("groceries", 60)); + lists->add(createList("beverages", 20)); + lists->add(createList("cleaning", 60)); + lists->add(createList("garden", 80)); + lists->add(createList("pet", 100)); + lists->add(createList("toiletries", 200)); + + scene.addItem(lists); + + view.show(); + return app.exec(); +} diff --git a/shoplist/src/maincontainer.cpp b/shoplist/src/maincontainer.cpp new file mode 100644 index 0000000..1c35a58 --- /dev/null +++ b/shoplist/src/maincontainer.cpp @@ -0,0 +1,261 @@ +#include <QKeyEvent> +#include <QPointF> +#include <QPixmap> +#include <QGraphicsPixmapItem> +#include <QEvent> +#include <QGraphicsSceneMouseEvent> +#include <QCoreApplication> +#include <QGraphicsTextItem> +#include <QFont> + +#include <QAbstractAnimation> +#include <QPropertyAnimation> +#include <QParallelAnimationGroup> +#include <cmath> +#include <QDebug> +#include <QPainter> + +#include "maincontainer.h" +#include "designinformation.h" + +static const qreal listOffset = -1; + +// CloseButton + +CloseButton::CloseButton(QGraphicsItem *parent) + : QGraphicsPixmapItem(parent) +{ + setShapeMode(BoundingRectShape); + updateUI(); +} + +void CloseButton::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); + QCoreApplication::instance()->quit(); +} + +void CloseButton::updateUI() +{ + setPixmap(DesignInformation::getPixmap("closeButton")); + if (parentItem()) { + qreal top = DesignInformation::getPosition("closeButton").y(); + qreal left = parentItem()->boundingRect().width() - pixmap().width(); + left -= DesignInformation::getHorizontalSize("close_button_spacing"); + setPos(left, top); + } + else + setPos(DesignInformation::getPosition("closeButton")); + prepareGeometryChange(); + update(); +} + +QPainterPath CloseButton::shape() const +{ + return QGraphicsItem::shape(); +} + +QRectF CloseButton::boundingRect() const +{ + static const qreal minSize = 80.0; + QRectF result(QGraphicsPixmapItem::boundingRect()); + qreal hMargin = minSize < result.width() ? 0 : (minSize - result.width()) / 2; + qreal vMargin = minSize < result.height() ? 0 : (minSize - result.height()) / 2; + result.adjust(-hMargin, -vMargin, hMargin, vMargin); + return result; +} + +// ListChangeGestureBox + +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 = 60.0; + +ListChangeGestureBox::ListChangeGestureBox(QGraphicsItem *parent) + : GestureBox(parent) + , m_active(true) + , m_startPoint(0.0, 0.0) +{ +} + +void ListChangeGestureBox::setActivate(bool active) +{ + m_active = active; +} + +void ListChangeGestureBox::gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick) +{ + Q_UNUSED(pos); + Q_UNUSED(acceptClick); + startGesture = m_active; +} + +void ListChangeGestureBox::gestureStart(QPointF pos) +{ + m_startPoint = pos; + m_startTime = QTime::currentTime(); +} + +void ListChangeGestureBox::gestureMove(QPointF pos, QPointF movement, QPointF speed) +{ + Q_UNUSED(pos); + Q_UNUSED(movement); + Q_UNUSED(speed); +} + +void ListChangeGestureBox::gestureEnd(QPointF pos, QPointF speed) +{ + Q_UNUSED(speed); + qreal x = pos.x() - m_startPoint.x(); + qreal y = pos.y() - m_startPoint.y(); + if (!m_active + || fabs(y / x) > limit_angle_tg + || m_startTime.msecsTo(QTime::currentTime()) > max_gesture_time_ms + || fabs(x) < min_gesture_length) { + return; + } + + if (x > 0) + emit moveLeft(); + else + emit moveRight(); +} + +// MainContainer + +MainContainer::MainContainer() + : m_animation(0) + , m_lists() + , m_current(0) + , m_hold(new QGraphicsRectItem(this)) + , m_closeButton(new CloseButton(this)) + , m_gestureBox(new ListChangeGestureBox(this)) +{ + setPixmap(DesignInformation::getPixmap("background")); + m_hold->setFlags(QGraphicsItem::ItemHasNoContents); + m_hold->setPos(DesignInformation::getHorizontalSize("listset.center_position"), 0.0); + + m_gestureBox->setRect(boundingRect()); + + m_title = new QGraphicsTextItem(this); + QString html = "<span style=\"font-family:Nokia sans; color:#f8f8f8\">"; + html += "Shopping List </span>"; + m_title->setHtml(html); + + connect(m_gestureBox,SIGNAL(moveLeft()), this, SLOT(moveLeft())); + connect(m_gestureBox,SIGNAL(moveRight()), this, SLOT(moveRight())); +} + +void MainContainer::adjustGestureBox() +{ + m_gestureBox->setActivate(m_current >= 0 && m_current < m_lists.count()); +} + +void MainContainer::add(ShoppingListCategoryUI *list) +{ + + if (m_lists.indexOf(list) >= 0) + return; + + list->setParentItem(m_hold); + m_lists.append(list); + if (m_lists.count() == 1) + m_lists[0]->setFocused(true); + + adjustListsPositions(); + adjustGestureBox(); +} + +void MainContainer::adjustListsPositions() +{ + qreal listPos = 0.0; + + for (int i = 0; i < m_lists.count(); ++i) { + m_lists[i]->setPos(listPos, 0); + listPos += m_lists[i]->boundingRect().width() + listOffset; + m_lists[i]->setFocused(i == m_current); + } +} + +void MainContainer::updateUI() +{ + setPixmap(DesignInformation::getPixmap("background")); + foreach(ShoppingListCategoryUI *list, m_lists) + list->updateUI(); + + QFont font = m_title->font(); + font.setPixelSize(DesignInformation::getFontSize("title")); + m_title->setFont(font); + QPointF titlePos = DesignInformation::getPosition("title"); + qreal titleLeft = (boundingRect().width() - m_title->boundingRect().width()) / 2; + m_title->setPos(titleLeft, titlePos.y()); + + m_closeButton->updateUI(); + + adjustListsPositions(); + + qreal holdOffset = DesignInformation::getHorizontalSize("listset.center_position"); + if (m_current >= 0 && m_current < m_lists.count()) { + for (int i = 0; i < m_current; ++i) + holdOffset -= m_lists[i]->boundingRect().width(); + } + m_hold->setPos(holdOffset, 0); + + m_gestureBox->setRect(boundingRect()); + adjustGestureBox(); +} + +void MainContainer::moveLeft() +{ + startMovement(false); +} + +void MainContainer::moveRight() +{ + startMovement(true); +} + +void MainContainer::startMovement(bool leftToRight) +{ + int target = m_current + (leftToRight ? 1 : -1); + if (m_animation || target < 0 || target >= m_lists.count()) + return; + + QAbstractAnimation* anim = sideAnimation(leftToRight); + anim->start(QAbstractAnimation::DeleteWhenStopped); + m_animation = anim; + m_current = target; + + m_gestureBox->setActivate(false); + m_lists[m_current]->setFocused(true); + if (m_current > 0) + m_lists[m_current - 1]->setFocused(false); + if (m_current < m_lists.count() - 1) + m_lists[m_current + 1]->setFocused(false); +} + +void MainContainer::animationFinished() +{ + m_animation = 0; + adjustGestureBox(); +} + +QAbstractAnimation* MainContainer::sideAnimation(bool leftToRight) +{ + QParallelAnimationGroup* group = new QParallelAnimationGroup(); + + qreal left = m_lists[m_current]->boundingRect().width() + listOffset; + left = m_hold->pos().x() +(leftToRight ? -left : left); + + QPropertyAnimation* anim = new QPropertyAnimation(this, "position"); + anim->setEasingCurve(QEasingCurve::OutExpo); + anim->setDuration(500); + anim->setEndValue(QPointF(left, 0)); + + group->addAnimation(anim); + + connect(group, SIGNAL(finished()), this, SLOT(animationFinished())); + + return group; +} diff --git a/shoplist/src/maincontainer.h b/shoplist/src/maincontainer.h new file mode 100644 index 0000000..4ec67e7 --- /dev/null +++ b/shoplist/src/maincontainer.h @@ -0,0 +1,91 @@ +#ifndef _MAINCONTAINER_H_ +#define _MAINCONTAINER_H_ + +#include <QPointF> +#include <QObject> +#include <QKeyEvent> +#include <QAbstractAnimation> +#include <QPropertyAnimation> + +#include <QGraphicsRectItem> +#include <QGraphicsPixmapItem> + +#include "shoppinglistcategoryui.h" +#include "gesturebox.h" + +class CloseButton: public QGraphicsPixmapItem +{ +public: + CloseButton(QGraphicsItem *parent = 0); + QRectF boundingRect() const; + QPainterPath shape() const; + void updateUI(); +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); +}; + +class ListChangeGestureBox : public QObject, public GestureBox +{ + Q_OBJECT +public: + ListChangeGestureBox(QGraphicsItem *parent = 0); + void setActivate(bool active); + +signals: + void moveLeft(); + void moveRight(); + +protected: + void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick); + void gestureStart(QPointF pos); + void gestureMove(QPointF pos, QPointF movement, QPointF speed); + void gestureEnd(QPointF pos, QPointF speed); + +private: + bool m_active; + QPointF m_startPoint; + QTime m_startTime; +}; + +class MainContainer: public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT + Q_PROPERTY(QPointF position READ position WRITE setPosition); + +public: + MainContainer(); + virtual ~MainContainer() {}; + + void add(ShoppingListCategoryUI *); + + void updateUI(); + + + +public slots: + void moveLeft(); + void moveRight(); + +private slots: + void animationFinished(); + +private: + QPointF position() { return m_hold->pos(); } + void setPosition(QPointF pos) { m_hold->setPos(pos); } + + QAbstractAnimation* m_animation; + QAbstractAnimation* sideAnimation(bool leftToRight); + + QList<ShoppingListCategoryUI*> m_lists; + int m_current; + QGraphicsRectItem* m_hold; + CloseButton *m_closeButton; + ListChangeGestureBox *m_gestureBox; + QGraphicsTextItem* m_title; + + void adjustGestureBox(); + void startMovement(bool leftToRight); + void adjustListsPositions(); +}; + +#endif /* _MAINCONTAINER_H_ */ diff --git a/shoplist/src/popup.cpp b/shoplist/src/popup.cpp new file mode 100644 index 0000000..27196ba --- /dev/null +++ b/shoplist/src/popup.cpp @@ -0,0 +1,73 @@ +#include <QBrush> +#include <QEvent> +#include <QPointF> +#include <QGraphicsPixmapItem> + +#include "popup.h" +#include "designinformation.h" + +// PopupCloseButton + +PopupCloseButton::PopupCloseButton(QGraphicsItem *parent) + : QGraphicsPixmapItem(parent) +{ + setShapeMode(BoundingRectShape); +} + +QPainterPath PopupCloseButton::shape() const +{ + return QGraphicsItem::shape(); +} + +QRectF PopupCloseButton::boundingRect() const +{ + static const qreal minSize = 80.0; + QRectF result(QGraphicsPixmapItem::boundingRect()); + qreal hMargin = minSize < result.width() ? 0 : (minSize - result.width()) / 2; + qreal vMargin = minSize < result.height() ? 0 : (minSize - result.height()) / 2; + result.adjust(-hMargin, -vMargin, hMargin, vMargin); + return result; +} + +// PopUp + +PopUp::PopUp(QGraphicsItem* parent) + : QGraphicsRectItem(parent) +{ + m_allDone = new QGraphicsPixmapItem(this); + m_allDone->installSceneEventFilter(this); + + m_startAgain = new QGraphicsPixmapItem(m_allDone); + m_startAgain->installSceneEventFilter(this); + + m_close = new PopupCloseButton(m_allDone); + m_close->installSceneEventFilter(this); + + updateUI(); +} + +void PopUp::updateUI() +{ + m_allDone->setPixmap(DesignInformation::getPixmap("alldone.background")); + m_allDone->setPos((parentItem()->boundingRect().bottomRight()/2) - + (m_allDone->boundingRect().bottomRight()/2)); + + m_startAgain->setPixmap(DesignInformation::getPixmap("alldone.startagain_off")); + m_startAgain->setPos((m_allDone->boundingRect().bottomRight()/2) - + (m_startAgain->boundingRect().bottomRight()/2) + + DesignInformation::getPosition("alldone.startagain_off")); + + m_close->setPixmap(DesignInformation::getPixmap("alldone.close_button")); + m_close->setPos((m_allDone->boundingRect().bottomRight()/2) - + (m_close->boundingRect().bottomRight()/2) + + DesignInformation::getPosition("alldone.close_button")); +} + +bool PopUp::sceneEventFilter(QGraphicsItem *item, QEvent *event) +{ + if ((event->type() == QEvent::GraphicsSceneMousePress) and (item == m_startAgain)) + emit listStartAgain(); + else if ((event->type() == QEvent::GraphicsSceneMousePress) and (item == m_close)) + emit deletePopUp(); + return true; +} diff --git a/shoplist/src/popup.h b/shoplist/src/popup.h new file mode 100644 index 0000000..1a89e0a --- /dev/null +++ b/shoplist/src/popup.h @@ -0,0 +1,39 @@ +#ifndef POPUP_H +#define POPUP_H + +#include <QObject> +#include <QRect> +#include <QGraphicsObject> + +class PopupCloseButton: public QGraphicsPixmapItem +{ +public: + PopupCloseButton(QGraphicsItem *parent = 0); + QRectF boundingRect() const; + QPainterPath shape() const; +}; + +class PopUp : public QObject, public QGraphicsRectItem +{ + Q_OBJECT + +public: + PopUp(QGraphicsItem* parent = 0); + ~PopUp() {} + + void updateUI(); + +signals: + void listStartAgain(); + void deletePopUp(); + +protected: + bool sceneEventFilter(QGraphicsItem *item, QEvent *event); + +private: + QGraphicsPixmapItem* m_allDone; + QGraphicsPixmapItem* m_startAgain; + PopupCloseButton* m_close; +}; + +#endif // POPUP_H diff --git a/shoplist/src/scrollbar.cpp b/shoplist/src/scrollbar.cpp new file mode 100644 index 0000000..355e6dd --- /dev/null +++ b/shoplist/src/scrollbar.cpp @@ -0,0 +1,162 @@ +#include <QObject> +#include <QString> +#include <QTimer> +#include <QGraphicsPixmapItem> +#include <QPropertyAnimation> +#include <QAbstractAnimation> + +#include "designinformation.h" +#include "scrollbar.h" + +/*! \class ScrollBar + * + * \brief Fade in/out animations and position update callback + * + * Stick this over your items viewport and send \c updatePosition notifications + * to render the knob into the new position. It doesn't handle user input. + */ +ScrollBar::ScrollBar(QGraphicsItem* parent) + : QObject() + , QGraphicsPixmapItem(parent) + , m_state(Hidden) + , m_maximum(0) + , m_value(0) + , m_timer() + , m_animation(0) + , m_knob(this) +{ + hide(); + setValue(m_value); + setOpacity(0); + + m_timer.setSingleShot(true); + connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout())); + + updateUI(); +} + +void ScrollBar::updateUI() +{ + setPixmap(DesignInformation::getPixmap("list.scrollbar.portrait")); + m_knob.setPixmap(DesignInformation::getPixmap("list.scrollbar.knob")); +} + +qreal ScrollBar::maximum() { return m_maximum; } + +void ScrollBar::setMaximum(qreal max) +{ + if (max >= 0) + m_maximum = max; +} + +qreal ScrollBar::value() { return m_value; } + +void ScrollBar::setValue(qreal value) +{ + if (value < 0 || value > m_maximum) + return; + + m_value = value; + + int min = boundingRect().topLeft().y(); + int max = boundingRect().bottomLeft().y() - m_knob.boundingRect().height(); + + m_knob.setPos(0, (value * (max - min)) / m_maximum); +} + +void ScrollBar::updatePosition(qreal value) +{ + switch (m_state) { + case Visible: + startTimer(); + case ShowAnimation: + break; + case HideAnimation: + m_animation->stop(); + case Hidden: + QAbstractAnimation* anim = showAnimation(); + anim->start(QAbstractAnimation::DeleteWhenStopped); + m_state = ShowAnimation; + break; + } + + show(); + setValue(value); +} + +void ScrollBar::setLandscapeOrientation() +{ + setPixmap(DesignInformation::getPixmap("list.scrollbar.landscape")); + setValue(m_value); +} + +void ScrollBar::setPortraitOrientation() +{ + setPixmap(DesignInformation::getPixmap("list.scrollbar.portrait")); + setValue(m_value); +} + +void ScrollBar::startTimer() +{ + if (m_timer.isActive()) + m_timer.stop(); + + m_timer.start(1000); +} + +void ScrollBar::timeout() +{ + if (Visible != m_state) + return; + + m_state = HideAnimation; + QAbstractAnimation* anim = hideAnimation(); + m_animation = anim; + anim->start(QAbstractAnimation::DeleteWhenStopped); +} + +void ScrollBar::animationFinished() +{ + switch(m_state) { + case ShowAnimation: + startTimer(); + m_state = Visible; + break; + case HideAnimation: + hide(); + m_state = Hidden; + m_animation = 0; + break; + default: + break; + } +} + +QPropertyAnimation* ScrollBar::propertyAnimation(const char* name) +{ + QPropertyAnimation* anim = new QPropertyAnimation(); + anim->setTargetObject(this); + anim->setPropertyName(name); + anim->setEasingCurve(QEasingCurve::OutExpo); + anim->setDuration(500); + + connect(anim, SIGNAL(finished()), this, SLOT(animationFinished())); + + return anim; +} + +QAbstractAnimation* ScrollBar::showAnimation() +{ + QPropertyAnimation* anim = propertyAnimation("opacity"); + anim->setEndValue(1); + + return anim; +} + +QAbstractAnimation* ScrollBar::hideAnimation() +{ + QPropertyAnimation* anim = propertyAnimation("opacity"); + anim->setEndValue(0); + + return anim; +} diff --git a/shoplist/src/scrollbar.h b/shoplist/src/scrollbar.h new file mode 100644 index 0000000..5536a61 --- /dev/null +++ b/shoplist/src/scrollbar.h @@ -0,0 +1,59 @@ +#ifndef SCROLLBAR_H +#define SCROLLBAR_H + +#include <QObject> +#include <QString> +#include <QTimer> +#include <QGraphicsPixmapItem> +#include <QPropertyAnimation> +#include <QAbstractAnimation> + +class ScrollBar: public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT +public: + ScrollBar(QGraphicsItem* parent); + virtual ~ScrollBar() {} + + void updateUI(); + + qreal maximum(); + void setMaximum(qreal max); + + qreal value(); + void setValue(qreal); + + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity); + +public slots: + void updatePosition(qreal); + void setLandscapeOrientation(); + void setPortraitOrientation(); + +private slots: + void animationFinished(); + void timeout(); + +private: + QPropertyAnimation* propertyAnimation(const char* name); + QAbstractAnimation* showAnimation(); + QAbstractAnimation* hideAnimation(); + + void startTimer(); + + enum State { + Hidden, + ShowAnimation, + Visible, + HideAnimation + }; + State m_state; + + qreal m_maximum; + qreal m_value; + QTimer m_timer; + QAbstractAnimation* m_animation; + QGraphicsPixmapItem m_knob; +}; + +#endif /* SCROLLBAR_H */ diff --git a/shoplist/src/shoppinglist.cpp b/shoplist/src/shoppinglist.cpp new file mode 100644 index 0000000..4c61830 --- /dev/null +++ b/shoplist/src/shoppinglist.cpp @@ -0,0 +1,265 @@ +#include "shoppinglist.h" +#include <QPropertyAnimation> +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QGraphicsSceneMouseEvent> +#include <QFont> +#include <QDebug> +#include "designinformation.h" + +class ShoppingListItemClickActivity : public ContentListActivity +{ +public: + ShoppingListItemClickActivity(ShoppingListItem *item, bool check, ContentList &list) + : ContentListActivity(list) + , m_item(item) + , m_check(check) + {} + bool run(); +private: + ShoppingListItem *m_item; + const bool m_check; +}; + +bool ShoppingListItemClickActivity::run() +{ + int idx = m_list.getItemIndex(m_item); + if (idx >= 0 && m_item->checked() != m_check) { + + m_item->m_list->m_checkedItems += m_item->checked() ? -1 : 1; + + addActivity(new AnimationActivity(m_item->getSwitchAnimation(),m_list)); + + if (m_check) + addActivity(new MoveActivity(idx, m_list.itemCount(), m_list)); + + if (!m_check) { + int target = m_list.itemCount(); + for (int i = 0; target == m_list.itemCount() && i < m_list.itemCount(); ++i) { + ShoppingListItem *item = dynamic_cast<ShoppingListItem*>(m_list.getItem(i)); + if (!item || item == m_item) + continue; + if (item->checked()) { + if (idx > i) + addActivity(new MoveActivity(idx, i, m_list)); + break; + } + if (item->listPos() > m_item->listPos()) { + addActivity(new MoveActivity(idx, i, m_list)); + break; + } + } + } + + if (m_item->m_list->m_checkedItems == m_item->m_list->itemCount()) { + SignalActivity *activity = new SignalActivity(m_list); + connect(activity, SIGNAL(notify()), m_item->m_list, SIGNAL(completed())); + addActivity(activity); + } + } + return false; +} + +// ShoppingListItem + +ShoppingListItem::ShoppingListItem(const QString& text, ShoppingList *list, + int pos, QGraphicsItem *parent) + : ContentListItem(parent) + , m_list(list) + , m_pos(pos) + , m_contentHeight(DesignInformation::getVerticalSize("list_item.height")) +{ + m_image1 = new ShoppingListPixmap(this); + m_image2 = new ShoppingListPixmap(this); + m_line = new ShoppingListPixmap(this); + m_selected = new ShoppingListPixmap(this); + + m_image2->hide(); + m_image2->setOpacity(0); + m_selected->hide(); + m_selected->setOpacity(0); + + m_text = new QGraphicsTextItem(this); + QString html = "<span style=\"font-family:Nokia sans; color:#616161\">"; + html += text + "</span>"; + m_text->setHtml(html); + + updateUI(); +} + +void ShoppingListItem::updateUI() +{ + m_contentHeight = DesignInformation::getVerticalSize("list_item.height"); + + updatePixmapItem(m_image1, "list_item.not_checked", 1); + updatePixmapItem(m_image2, "list_item.checked", 1); + updatePixmapItem(m_line, "list_item.bottom_line", 1); + updatePixmapItem(m_selected, "list_item.selected", 0); + + QFont font = m_text->font(); + font.setPixelSize(DesignInformation::getFontSize("list_item.font_size")); + m_text->setFont(font); + + qreal textOffset = m_image1->pos().x() + m_image1->pixmap().width() + + DesignInformation::getHorizontalSize("list_item.text_offset"); + + qreal textTop = (contentHeight() - m_text->boundingRect().height()) / 2; + + m_text->setPos(textOffset, textTop); +} + +void ShoppingListItem::updatePixmapItem(ShoppingListPixmap *item, const QString &key, qreal zValue) +{ + item->setPixmap(DesignInformation::getPixmap(key)); + item->setPos(DesignInformation::getPosition(key)); + item->setZValue(zValue); +} + +qreal ShoppingListItem::contentHeight() const +{ + return m_contentHeight; +} + +QAbstractAnimation *ShoppingListItem::getShowAnimation() +{ + return getFadeAnimation(this, false, 150); +} + +QAbstractAnimation *ShoppingListItem::getHideAnimation() +{ + return getFadeAnimation(this, true, 150); +} + +template<class T> +QAbstractAnimation *ShoppingListItem::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; +} + +QAbstractAnimation *ShoppingListItem::getSwitchAnimation() +{ + ShoppingListPixmap *in = checked() ? m_image1 : m_image2; + ShoppingListPixmap *out = in == m_image1 ? m_image2 : m_image1; + + QParallelAnimationGroup *result = new QParallelAnimationGroup(); + + QAbstractAnimation *background = getFadeAnimation(m_selected, checked(), 150); + result->addAnimation(addAnimationPause(background, 50)); + + result->addAnimation(getFadeAnimation(in, false, 150)); + result->addAnimation(getFadeAnimation(out, true, 150)); + + return result; +} + +QAbstractAnimation *ShoppingListItem::addAnimationPause(QAbstractAnimation *animation, int msecs) +{ + QSequentialAnimationGroup *result = new QSequentialAnimationGroup(); + result->addPause(msecs); + result->addAnimation(animation); + return result; +} + +void ShoppingListItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); + if (m_list && !m_list->busy() && m_list->active()) + m_list->addActivity(new ShoppingListItemClickActivity(this, !checked(), *m_list)); +} + +void ShoppingListItem::uncheck() +{ + m_image1->show(); + m_image1->setOpacity(1.0); + m_image2->hide(); + m_image2->setOpacity(0); + m_selected->hide(); + m_selected->setOpacity(0); +} + +// ShoppingList + +ShoppingList::ShoppingList(QGraphicsItem *parent) + : ContentList(parent) + , m_checkedItems(0) + , m_active(true) +{ +} + +QAbstractAnimation *ShoppingList::getInsertAnimation(int idx, qreal height) +{ + if (idx < 0 || idx >= itemCount()) + return 0; + QList<QAbstractAnimation*> list; + for (int i = idx; i < itemCount(); ++i) + list.append(getItemMoveAnimation(i, height)); + return createCompoundAnimation(list); +} + +QAbstractAnimation *ShoppingList::getRemoveAnimation(int idx) +{ + if (idx < 0 || idx >= itemCount()) + return 0; + qreal offset = -getItem(idx)->contentHeight(); + QList<QAbstractAnimation*> list; + for (int i = idx + 1; i < itemCount(); ++i) + list.append(getItemMoveAnimation(i, offset)); + return createCompoundAnimation(list); +} + +QAbstractAnimation *ShoppingList::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 *ShoppingList::getItemMoveAnimation(int idx, qreal offset) +{ + ContentListItem *item = getItem(idx); + if (!item) + return 0; + + qreal itemTop = item->property(ITEM_TOP_PROPERTY_NAME).toReal(); + + QPropertyAnimation* lResult = new QPropertyAnimation(item, ITEM_TOP_PROPERTY_NAME); + + lResult->setEasingCurve(QEasingCurve::OutExpo); + lResult->setStartValue(itemTop); + lResult->setEndValue(itemTop + offset); + lResult->setDuration(150); + return lResult; +} + +static bool shoppingListItemCompare(const ContentListItem* s1, const ContentListItem* s2) +{ + const ShoppingListItem *item1 =dynamic_cast<const ShoppingListItem*>(s1); + const ShoppingListItem *item2 =dynamic_cast<const ShoppingListItem*>(s2); + return !item1 || !item2 ? false : item1->listPos() < item2->listPos(); +} + +void ShoppingList::clearChecks() +{ + foreach(ContentListItem *item, getItems()) + dynamic_cast<ShoppingListItem*>(item)->uncheck(); + m_checkedItems = 0; + sortItems(shoppingListItemCompare); +} + diff --git a/shoplist/src/shoppinglist.h b/shoplist/src/shoppinglist.h new file mode 100644 index 0000000..e05138b --- /dev/null +++ b/shoplist/src/shoppinglist.h @@ -0,0 +1,89 @@ +#ifndef SHOPPINGLIST_H +#define SHOPPINGLIST_H + +#include <QGraphicsPixmapItem> +#include <QGraphicsTextItem> +#include "contentlist.h" + +class ShoppingListPixmap : public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity); +public: + ShoppingListPixmap(QGraphicsItem *parent = 0) + : QGraphicsPixmapItem(parent) {} +public slots: + void doHide() { hide(); } +}; + +class ShoppingList : public ContentList +{ + Q_OBJECT +public: + ShoppingList(QGraphicsItem *parent = 0); + QAbstractAnimation *getInsertAnimation(int idx, qreal height); + QAbstractAnimation *getRemoveAnimation(int idx); + + void setActive(bool active) { m_active = active; } + bool active() const { return m_active; } + +signals: + void completed(); + +public slots: + void clearChecks(); + +private: + friend class ShoppingListItemClickActivity; + + int m_checkedItems; + bool m_active; + + QAbstractAnimation *getItemMoveAnimation(int idx, qreal offset); + QAbstractAnimation *createCompoundAnimation(QList<QAbstractAnimation*>); +}; + +class ShoppingListItem : public ContentListItem +{ + Q_OBJECT + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity); +public: + ShoppingListItem(const QString& text, ShoppingList *list, + int pos, QGraphicsItem *parent = 0); + + qreal contentHeight() const; + QAbstractAnimation *getShowAnimation(); + QAbstractAnimation *getHideAnimation(); + + bool checked() const { return m_image2->isVisible(); } + int listPos() const { return m_pos; } + void uncheck(); + + void updateUI(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + +private: + ShoppingListPixmap *m_image1; + ShoppingListPixmap *m_image2; + ShoppingListPixmap *m_line; + ShoppingListPixmap *m_selected; + QGraphicsTextItem *m_text; + QAbstractAnimation *getSwitchAnimation(); + QAbstractAnimation *addAnimationPause(QAbstractAnimation *animation, int msecs); + template<class T> QAbstractAnimation *getFadeAnimation(T *target, bool hide, int msecs); + void updatePixmapItem(ShoppingListPixmap *item, const QString &key, qreal zValue); + +private slots: + void doHide() { hide(); } + +private: + friend class ShoppingListItemClickActivity; + QPointer<ShoppingList> m_list; + const int m_pos; + qreal m_contentHeight; + +}; + +#endif // SHOPPINGLIST_H diff --git a/shoplist/src/shoppinglistcategoryui.cpp b/shoplist/src/shoppinglistcategoryui.cpp new file mode 100644 index 0000000..59a8ba5 --- /dev/null +++ b/shoplist/src/shoppinglistcategoryui.cpp @@ -0,0 +1,266 @@ +#include "shoppinglistcategoryui.h" +#include "designinformation.h" +#include "scrollbar.h" + +#include <QDebug> +#include <QtGlobal> +#include <cmath> + +// ShoppingListScrollBox + +#ifdef Q_OS_SYMBIAN +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; + +ShoppingListScrollBox::ShoppingListScrollBox(QGraphicsItem *content, QGraphicsItem *parent) + : GestureBox(parent) + , m_content(content) + , m_active(true) +{ + setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + if (m_content) + m_content->setParentItem(this); + + QObject* obj = dynamic_cast<QObject*>(parent); + if (obj) + connect(this, SIGNAL(updated(qreal, qreal, qreal)), + obj, SLOT(updated(qreal, qreal, qreal))); +} + +void ShoppingListScrollBox::gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick) +{ + Q_UNUSED(pos); + startGesture = m_active; + acceptClick = !m_ticker.isActive(); + if (m_ticker.isActive()) { + m_ticker.stop(); + m_speed = 0.0; + } +} + +void ShoppingListScrollBox::gestureStart(QPointF pos) +{ + m_startPoint = pos; +} + +void ShoppingListScrollBox::contentPositionUpdated() +{ + qreal max = m_content->boundingRect().height(); + + QPointF delta = boundingRect().topLeft() - mapFromItem(m_content, QPointF(0, 0)); + qreal cur = delta.y(); + + qreal step = boundingRect().height(); + + emit updated(max, cur, step); +} + +bool ShoppingListScrollBox::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 ShoppingListScrollBox::gestureMove(QPointF pos, QPointF movement, QPointF speed) +{ + Q_UNUSED(pos); + Q_UNUSED(speed); + move(movement); +} + +void ShoppingListScrollBox::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 = DesignInformation::getVerticalSize("list.vertical_speed"); + 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 ShoppingListScrollBox::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); +} + +// ShoppingListCategoryUI + +ShoppingListCategoryUI::ShoppingListCategoryUI(const QString &category, QGraphicsItem *parent) + : QGraphicsRectItem(parent) + , m_pic(0) + , m_header(0) + , m_background(0) + , m_shoppingList(0) + , m_scroll(0) + , m_scrollBox(0) + , m_popup(0) + , m_shadow(0) + , m_category(category) +{ + setFlag(QGraphicsItem::ItemHasNoContents, true); + + m_background = new QGraphicsPixmapItem(this); + m_pic = new QGraphicsPixmapItem(this); + m_header = new QGraphicsPixmapItem(this); + m_feather = new QGraphicsPixmapItem(this); + m_shoppingList = new ShoppingList(0); + m_scrollBox = new ShoppingListScrollBox(m_shoppingList, this); + m_shadow = new ListShadow(this); + m_scroll = new ScrollBar(this); + + m_shadow->setZValue(3); + m_background->setZValue(0); + m_pic->setZValue(1); + m_header->setZValue(1); + m_feather->setZValue(2); + m_scrollBox->setZValue(1); + m_scroll->setZValue(2); + + connect(m_shoppingList, SIGNAL(completed()), this, SLOT(showPopup())); + + updateUI(); +} + +void ShoppingListCategoryUI::updateUI() +{ + m_shadow->setPixmap(DesignInformation::getPixmap("category.shadow." + m_category)); + m_shadow->setPos(DesignInformation::getPosition("category.shadow")); + + m_background->setPixmap(DesignInformation::getPixmap("category.background")); + m_background->setPos(DesignInformation::getPosition("category.background")); + + m_pic->setPixmap(DesignInformation::getPixmap("category.pic." + m_category)); + m_pic->setPos(DesignInformation::getPosition("category.pic")); + + m_header->setPixmap(DesignInformation::getPixmap("category.header." + m_category)); + m_header->setPos(DesignInformation::getPosition("category.header")); + + m_feather->setPixmap(DesignInformation::getPixmap("category.feather")); + m_feather->setPos(DesignInformation::getPosition("category.feather")); + + QPointF listPos = DesignInformation::getPosition("category.list"); + + qreal listHeight = DesignInformation::getVerticalSize("category.list_height"); + + m_scrollBox->setRect(listPos.x(), listPos.y(), + m_background->pixmap().width() - listPos.x(), listHeight); + + m_shoppingList->setPos(0.0, 0.0); + m_shoppingList->setWidth(m_background->pixmap().width() - listPos.x()); + + setRect(0, 0, m_background->pixmap().width(), listPos.y() + listHeight); + + m_scroll->setPos(DesignInformation::getPosition("category.scrollbar")); + m_scroll->updateUI(); + + for (int i = 0; i < m_shoppingList->itemCount(); ++i) { + ShoppingListItem *item = dynamic_cast<ShoppingListItem*>(m_shoppingList->getItem(i)); + if (item) + item->updateUI(); + } + m_shoppingList->updateItems(); + + if (m_popup) + m_popup->updateUI(); +} + +void ShoppingListCategoryUI::updated(qreal max, qreal cur, qreal step) +{ + m_scroll->setMaximum(max - step); + m_scroll->updatePosition(cur); +} + +void ShoppingListCategoryUI::showPopup() +{ + if (m_popup) + return; + + m_popup = new PopUp(this); + m_popup->setZValue(10); + + connect(m_popup, SIGNAL(listStartAgain()), this, SLOT(clearList())); + connect(m_popup, SIGNAL(deletePopUp()), this, SLOT(hidePopup())); + setCompleted(true); +} + +void ShoppingListCategoryUI::clearList() +{ + m_shoppingList->clearChecks(); + hidePopup(); +} + +void ShoppingListCategoryUI::hidePopup() +{ + if (!m_popup) + return; + setCompleted(false); + m_popup->deleteLater(); + m_popup = 0; +} + +void ShoppingListCategoryUI::updateListState() +{ + m_scrollBox->setActive(m_shadow->focused() && !m_shadow->completed()); + m_shoppingList->setActive(m_shadow->focused() && !m_shadow->completed()); + +} + +void ShoppingListCategoryUI::setFocused(bool focused) +{ + m_shadow->setFocused(focused); + updateListState(); +} + +void ShoppingListCategoryUI::setCompleted(bool completed) +{ + m_shadow->setCompleted(completed); + updateListState(); +} diff --git a/shoplist/src/shoppinglistcategoryui.h b/shoplist/src/shoppinglistcategoryui.h new file mode 100644 index 0000000..195fd3e --- /dev/null +++ b/shoplist/src/shoppinglistcategoryui.h @@ -0,0 +1,81 @@ +#ifndef SHOPPINGLISTCATEGORYUI_H +#define SHOPPINGLISTCATEGORYUI_H + +#include <QObject> +#include <QGraphicsRectItem> +#include <QGraphicsPixmapItem> +#include <QBasicTimer> +#include <QTime> +#include "shoppinglist.h" +#include "scrollbar.h" +#include "gesturebox.h" +#include "popup.h" +#include "listshadow.h" + +class ShoppingListScrollBox : public QObject, public GestureBox +{ + Q_OBJECT +public: + ShoppingListScrollBox(QGraphicsItem *content, QGraphicsItem *parent = 0); + void setActive(bool active) { m_active = active; } + bool active() const { return m_active; } + +signals: + void updated(qreal, qreal, qreal); + +protected: + void gestureMousePress(QPointF pos, bool &startGesture, bool &acceptClick); + void gestureStart(QPointF pos); + void gestureMove(QPointF pos, QPointF movement, QPointF speed); + void gestureEnd(QPointF pos, QPointF speed); + + void timerEvent(QTimerEvent *event); + +private: + QGraphicsItem * const m_content; + QBasicTimer m_ticker; + qreal m_speed; + qreal m_acceleration; + QTime m_time; + bool m_active; + QPointF m_startPoint; + bool move(QPointF movement); + void contentPositionUpdated(); +}; + +class ShoppingListCategoryUI : public QObject, public QGraphicsRectItem +{ + Q_OBJECT +public: + ShoppingListCategoryUI(const QString &category, QGraphicsItem *parent = 0); + ShoppingList *list() { return m_shoppingList; } + void updateUI(); + + bool focused() const { return m_shadow->focused(); } + void setFocused(bool focused); + +public slots: + void updated(qreal, qreal, qreal); + +private: + QGraphicsPixmapItem *m_pic; + QGraphicsPixmapItem *m_header; + QGraphicsPixmapItem *m_background; + QGraphicsPixmapItem *m_feather; + ShoppingList *m_shoppingList; + ScrollBar* m_scroll; + ShoppingListScrollBox *m_scrollBox; + PopUp *m_popup; + ListShadow *m_shadow; + const QString m_category; + + void updateListState(); + void setCompleted(bool completed); + +private slots: + void showPopup(); + void hidePopup(); + void clearList(); +}; + +#endif // SHOPPINGLISTCATEGORYUI_H diff --git a/shoplist/src/shoppinglistview.cpp b/shoplist/src/shoppinglistview.cpp new file mode 100644 index 0000000..9867574 --- /dev/null +++ b/shoplist/src/shoppinglistview.cpp @@ -0,0 +1,79 @@ +#include "shoppinglistview.h" + +#include <QApplication> +#include <QDesktopWidget> + +ShoppingListView::ShoppingListView(MainContainer *container) + : m_mainContainer(container) +{ + setRenderHints(QPainter::TextAntialiasing); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFrameShape(QFrame::NoFrame); + setWindowTitle("Shopping List"); + setViewportUpdateMode(BoundingRectViewportUpdate); + +} + +void ShoppingListView::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); + if (event->key() == Qt::Key_F5) + adjustScreenSize(); + if (event->key() == Qt::Key_Left) + m_mainContainer->moveLeft(); + if (event->key() == Qt::Key_Right) + m_mainContainer->moveRight(); +} + +void ShoppingListView::adjustScreenSize() +{ + + QSizeF screenSize(QApplication::desktop()->screenGeometry().size()); + if (screenSize.width() > 480) + screenSize.setWidth(480); + if (screenSize.height() > 864) + screenSize.setHeight(864); + + /* +#ifdef Q_OS_SYMBIAN + QSizeF screenSize(QApplication::desktop()->screenGeometry().size()); +#else + QSizeF screenSize(geometry().size()); +#endif + */ + DesignInformation::setScreenSize(screenSize); + setGeometry(0, 0, screenSize.width(), screenSize.height()); + if (scene()) + scene()->setSceneRect(0.0, 0.0, screenSize.width(), screenSize.height()); + centerOn(m_mainContainer); + m_mainContainer->updateUI(); +} + +void ShoppingListView::adjustScalingFactor() +{ + + QSizeF screenSize(QApplication::desktop()->screenGeometry().size()); + if (screenSize.width() > 480) + screenSize.setWidth(480); + if (screenSize.height() > 864) + screenSize.setHeight(864); + + /* +#ifdef Q_OS_SYMBIAN + QSizeF screenSize(QApplication::desktop()->screenGeometry().size()); +#else + QSizeF screenSize(geometry().size()); +#endif +*/ + screenSize.setWidth(qMin(int(screenSize.width()), 480)); + screenSize.setHeight(qMin(int(screenSize.height()), 864)); + DesignInformation::setScaleFactor(DesignInformation::getScaleFactor(screenSize)); +} + +void ShoppingListView::resized(int screen) +{ + Q_UNUSED(screen); + adjustScreenSize(); +} + diff --git a/shoplist/src/shoppinglistview.h b/shoplist/src/shoppinglistview.h new file mode 100644 index 0000000..4e35dee --- /dev/null +++ b/shoplist/src/shoppinglistview.h @@ -0,0 +1,26 @@ +#ifndef SHOPPINGLISTVIEW_H +#define SHOPPINGLISTVIEW_H + +#include <QGraphicsView> +#include "maincontainer.h" +#include "designinformation.h" + +class ShoppingListView : public QGraphicsView +{ + Q_OBJECT +public: + ShoppingListView(MainContainer *container); + void adjustScreenSize(); + void adjustScalingFactor(); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + MainContainer *m_mainContainer; + +private slots: + void resized(int screen); +}; + +#endif // SHOPPINGLISTVIEW_H diff --git a/shoplist/src/src.pri b/shoplist/src/src.pri new file mode 100644 index 0000000..a20ff8f --- /dev/null +++ b/shoplist/src/src.pri @@ -0,0 +1,22 @@ +HEADERS = src/scrollbar.h \ + src/maincontainer.h \ + src/designinformation.h \ + src/contentlist.h \ + src/shoppinglist.h \ + src/shoppinglistcategoryui.h \ + src/popup.h \ + src/gesturebox.h \ + src/listshadow.h \ + src/gesturebox_p.h \ + src/shoppinglistview.h +SOURCES = src/scrollbar.cpp \ + src/main.cpp \ + src/maincontainer.cpp \ + src/contentlist.cpp \ + src/designinformation.cpp \ + src/shoppinglist.cpp \ + src/shoppinglistcategoryui.cpp \ + src/popup.cpp \ + src/gesturebox.cpp \ + src/listshadow.cpp \ + src/shoppinglistview.cpp |