=== modified file 'components.api'
--- components.api 2015-02-12 14:34:18 +0000
+++ components.api 2015-02-19 08:31:57 +0000
@@ -927,13 +927,11 @@
name: "UCListItem"
prototype: "UCStyledItemBase"
exports: ["ListItem 1.2"]
- name: "PanelStatus"
Property { name: "contentItem"; type: "QQuickItem"; isReadonly: true; isPointer: true }
Property { name: "divider"; type: "UCListItemDivider"; isReadonly: true; isPointer: true }
Property { name: "leadingActions"; type: "UCListItemActions"; isPointer: true }
Property { name: "trailingActions"; type: "UCListItemActions"; isPointer: true }
Property { name: "highlighted"; type: "bool"; isReadonly: true }
- Property { name: "swipeOvershoot"; type: "double" }
Property { name: "contentMoving"; type: "bool"; isReadonly: true }
Property { name: "color"; type: "QColor" }
Property { name: "highlightColor"; type: "QColor" }
@@ -952,15 +950,6 @@
Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
Property { name: "actions"; type: "UCAction"; isList: true; isReadonly: true }
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
- name: "UCListItemAttached"
- prototype: "QObject"
- Property { name: "actions"; type: "UCListItemActions"; isReadonly: true; isPointer: true }
- Property { name: "visibleActions"; type: "UCAction"; isList: true; isReadonly: true }
- Property { name: "index"; type: "int"; isReadonly: true }
- Property { name: "panelStatus"; type: "UCListItem::PanelStatus"; isReadonly: true }
- Method {
- name: "snapToPosition"
- Parameter { name: "position"; type: "double" }
name: "UCListItemDivider"
prototype: "QQuickItem"
Property { name: "colorFrom"; type: "QColor" }
@@ -968,11 +957,11 @@
name: "UCListItemStyle"
prototype: "QQuickItem"
exports: ["Ubuntu.Components.Styles/ListItemStyle 1.2"]
- Property { name: "actionsDelegate"; type: "QQmlComponent"; isPointer: true }
- Property { name: "selectionDelegate"; type: "QQmlComponent"; isPointer: true }
- Property { name: "dragHandlerDelegate"; type: "QQmlComponent"; isPointer: true }
Property { name: "snapAnimation"; type: "QQuickAbstractAnimation"; isPointer: true }
- Property { name: "swipeOvershoot"; type: "double" }
+ Method {
+ name: "swipeEvent"
+ Parameter { name: "event"; type: "UCSwipeEvent"; isPointer: true }
+ Method { name: "rebound" }
name: "UCMouse"
prototype: "QObject"
exports: ["Mouse 0.1", "Mouse 1.0"]
@@ -1048,6 +1037,14 @@
name: "requestFocus"
Parameter { name: "reason"; type: "Qt::FocusReason" }
Method { name: "requestFocus"; revision: 1; type: "bool" }
+ name: "UCSwipeEvent"
+ prototype: "QObject"
+ exports: ["SwipeEvent 1.2"]
+ name: "Status"
+ Property { name: "to"; type: "QPointF"; isReadonly: true }
+ Property { name: "from"; type: "QPointF"; isReadonly: true }
+ Property { name: "content"; type: "QPointF" }
+ Property { name: "status"; type: "Status"; isReadonly: true }
name: "UCUbuntuAnimation"
prototype: "QObject"
exports: ["UbuntuAnimation 0.1", "UbuntuAnimation 1.0"]
=== modified file 'examples/ubuntu-ui-toolkit-gallery/NewListItems.qml'
--- examples/ubuntu-ui-toolkit-gallery/NewListItems.qml 2015-01-05 20:55:12 +0000
+++ examples/ubuntu-ui-toolkit-gallery/NewListItems.qml 2015-02-19 08:31:57 +0000
@@ -97,7 +97,6 @@
}
ListItemWithLabel {
text: i18n.tr("Custom action delegates")
- swipeOvershoot: 0
leadingActions: ListItemActions {
actions: [
Action {
=== removed file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml'
--- modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 2015-02-06 07:51:31 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemPanel.qml 1970-01-01 00:00:00 +0000
@@ -1,194 +0,0 @@
-/*
- * Copyright 2014 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-import QtQuick 2.2
-import Ubuntu.Components 1.2
-
-/*
- This component is the holder of the ListItem actions.
- */
-Item {
-
- // styling properties
- /*
- Color of the background.
- */
- // FIXME: use Palette colors instead when available
- property color backgroundColor: (leading ? UbuntuColors.red : "white")
-
- /*
- Color used in coloring the icons.
- */
- // FIXME: use Palette colors instead when available
- property color foregroundColor: leading ? "white" : UbuntuColors.darkGrey
-
- /*
- Specifies the width of the component visualizing the action.
- */
- property real paintedActionWidth: units.gu(2.5)
-
- // panel implementation
- id: panel
- width: Math.max(
- actionsRow.childrenRect.width,
- ListItem.visibleActions.length * MathUtils.clamp(paintedActionWidth, height, actionsRow.maxItemWidth))
-
- // used for module/autopilot testing
- objectName: "ListItemPanel" + (leading ? "Leading" : "Trailing")
-
- /*
- Specifies whether the panel is used to visualize leading or trailing actions.
- */
- readonly property bool leading: ListItem.panelStatus == ListItem.Leading
- readonly property real swipedOffset: leading ? width + x : styledItem.width - x;
- readonly property bool swiping: styledItem.highlighted && styledItem.contentMoving
-
- anchors {
- left: leading ? undefined : styledItem.contentItem.right
- right: leading ? styledItem.contentItem.left : undefined
- // anchor to the top of the item but to the bottom of the contentItem, so we do not draw over the divider
- top: styledItem.top
- bottom: styledItem.contentItem.bottom
- bottomMargin: -styledItem.contentItem.anchors.bottomMargin
- // adjust margins
- leftMargin: leading ? 0 : styledItem.contentItem.anchors.rightMargin
- rightMargin: leading ? styledItem.contentItem.anchors.leftMargin : 0
- }
-
- Rectangle {
- objectName: "panel_background"
- anchors {
- fill: parent
- // add 4 times the overshoot margins to cover the background when tugged
- leftMargin: (leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0
- rightMargin: (!leading && styledItem) ? -units.gu(4 * styledItem.swipeOvershoot) : 0
- }
- color: panel.backgroundColor
- }
-
- // handle action triggering
- Connections {
- target: styledItem
- onContentMovementEnded: {
- if (actionsRow.selectedAction) {
- actionsRow.selectedAction.trigger(actionsRow.listItemIndex);
- actionsRow.listItemIndex = -1;
- actionsRow.selectedAction = null;
- }
- }
- }
-
- // track drag direction, so we know in which direction we should snap
- property real prevX: 0.0
- property real snapChangerLimit: 0.0
- property real threshold: units.gu(1)
- property bool snapIn: false
- onXChanged: {
- if (prevX < x && (snapChangerLimit <= x)) {
- snapIn = leading;
- snapChangerLimit = x - threshold;
- } else if (prevX > x && (x < snapChangerLimit)) {
- snapIn = !leading;
- snapChangerLimit = x + threshold;
- }
- prevX = x;
- }
- // default snapping!
- onSwipingChanged: {
- if (swiping) {
- // the dragging got started, set prevX
- prevX = panel.x;
- return;
- }
- if (!visible) {
- return;
- }
- // snap in if the offset is bigger than the overshoot and the direction of the drag is to reveal the panel
- var snapPos = (swipedOffset > units.gu(2) && snapIn) ? panel.width : 0.0;
- ListItem.snapToPosition(snapPos);
- }
-
- Row {
- id: actionsRow
- anchors {
- left: parent.left
- top: parent.top
- bottom: parent.bottom
- leftMargin: spacing
- }
-
- property real maxItemWidth: styledItem.width / panel.ListItem.visibleActions.length
-
- property Action selectedAction
- property int listItemIndex: -1
-
- Repeater {
- model: panel.ListItem.visibleActions
- AbstractButton {
- id: actionButton
- action: modelData
- enabled: action.enabled
- opacity: action.enabled ? 1.0 : 0.5
- width: MathUtils.clamp(delegateLoader.item ? delegateLoader.item.width : 0, height, actionsRow.maxItemWidth)
- anchors {
- top: parent.top
- bottom: parent.bottom
- }
- function trigger() {
- actionsRow.selectedAction = modelData;
- actionsRow.listItemIndex = panel.ListItem.index;
- panel.ListItem.snapToPosition(0.0);
- }
-
- Rectangle {
- anchors.fill: parent
- color: Theme.palette.selected.background
- visible: pressed
- }
-
- Loader {
- id: delegateLoader
- height: parent.height
- sourceComponent: panel.ListItem.actions.delegate ? panel.ListItem.actions.delegate : defaultDelegate
- property Action action: modelData
- property int index: index
- property bool pressed: actionButton.pressed
- onItemChanged: {
- // use action's objectName to identify the visualized action
- if (item && item.objectName === "") {
- item.objectName = modelData.objectName;
- actionButton.objectName = "actionbutton_" + modelData.objectName
- }
- }
- }
- }
- }
- }
-
- Component {
- id: defaultDelegate
- Item {
- width: height
- Icon {
- width: panel.paintedActionWidth
- height: width
- name: action.iconName
- color: panel.foregroundColor
- anchors.centerIn: parent
- }
- }
- }
-}
=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml'
--- modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2015-02-06 09:09:07 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/ListItemStyle.qml 2015-02-19 08:31:57 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2014-2015 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -20,10 +20,233 @@
Styles.ListItemStyle {
- swipeOvershoot: units.gu(2)
- actionsDelegate: ListItemPanel{}
-
- snapAnimation: UbuntuNumberAnimation {
- duration: UbuntuAnimation.SnapDuration
+ id: listItemStyle
+ /*
+ * Take over the ListItem's index context property as repeater used in panel
+ * overrides the property.
+ */
+ readonly property int listItemIndex: index
+
+ // anchoring
+ anchors {
+ top: parent ? parent.top : undefined
+ bottom: parent ? parent.bottom : undefined
+ bottomMargin: styledItem.divider.visible ? styledItem.divider.height : 0
+ left: styledItem.contentItem.left
+ leftMargin: -styledItem.contentItem.anchors.leftMargin
+ right: styledItem.contentItem.right
+ rightMargin: -styledItem.contentItem.anchors.rightMargin
+ }
+
+ // leading/trailing panels
+ Component {
+ id: panelComponent
+ Rectangle {
+ id: panel
+ objectName: "ListItemPanel" + (leading ? "Leading" : "Trailing")
+ property bool leading: false
+ readonly property real panelWidth: actionsRow.width
+
+ // FIXME use theme palette colors once stabilized
+ color: leading ? UbuntuColors.red : "white"
+ anchors.fill: parent
+ width: parent.width
+
+ readonly property ListItemActions itemActions: leading ? styledItem.leadingActions : styledItem.trailingActions
+
+ Row {
+ id: actionsRow
+ anchors {
+ left: leading ? undefined : parent.left
+ right: leading ? parent.right : undefined
+ top: parent.top
+ bottom: parent.bottom
+ leftMargin: spacing
+ }
+
+ property real maxItemWidth: parent.width / itemActions.actions.length
+
+ Repeater {
+ model: itemActions.actions
+ AbstractButton {
+ id: actionButton
+ action: modelData
+ enabled: action.enabled
+ opacity: action.enabled ? 1.0 : 0.5
+ width: MathUtils.clamp(delegateLoader.item ? delegateLoader.item.width : 0, height, actionsRow.maxItemWidth)
+ anchors {
+ top: parent ? parent.top : undefined
+ bottom: parent ? parent.bottom : undefined
+ }
+ function trigger() {
+ internals.selectedAction = modelData;
+ listItemStyle.rebound();
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: Theme.palette.selected.background
+ visible: pressed
+ }
+
+ Loader {
+ id: delegateLoader
+ height: parent.height
+ sourceComponent: itemActions.delegate ? itemActions.delegate : defaultDelegate
+ property Action action: modelData
+ property int index: index
+ property bool pressed: actionButton.pressed
+ onItemChanged: {
+ // use action's objectName to identify the visualized action
+ if (item && item.objectName === "") {
+ item.objectName = modelData.objectName;
+ actionButton.objectName = "actionbutton_" + modelData.objectName
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: defaultDelegate
+ Item {
+ width: height
+ Icon {
+ width: units.gu(2.5)
+ height: width
+ name: action.iconName
+ // FIXME use theme palette colors once stabilized
+ color: leading ? "white" : UbuntuColors.darkGrey
+ anchors.centerIn: parent
+ }
+ }
+ }
+ }
+ }
+
+ // leading panel loader
+ Loader {
+ id: leadingLoader
+ objectName: "leading_loader"
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ right: parent.left
+ }
+ width: parent.width
+ sourceComponent: styledItem.leadingActions && styledItem.leadingActions.actions.length > 0 ?
+ panelComponent : null
+ onItemChanged: {
+ if (item) {
+ item.leading = true;
+ }
+ }
+ }
+ // trailing panel loader
+ Loader {
+ id: trailingLoader
+ objectName: "trailing_loader"
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ left: parent.right
+ }
+ width: parent.width
+ sourceComponent: styledItem.trailingActions && styledItem.trailingActions.actions.length > 0 ?
+ panelComponent : null
+ onItemChanged: {
+ if (item) {
+ item.leading = false;
+ }
+ }
+ }
+
+ // internals
+ QtObject {
+ id: internals
+ // action triggered
+ property Action selectedAction
+ // swipe handling
+ readonly property Item swipedPanel: listItemStyle.x > 0 ? leadingLoader.item : trailingLoader.item
+ readonly property bool leadingPanel: listItemStyle.x > 0
+ readonly property real swipedOffset: leadingPanel ? listItemStyle.x : -listItemStyle.x
+ readonly property real panelWidth: swipedPanel ? swipedPanel.panelWidth : 0
+ property real prevX: 0.0
+ property real snapChangerLimit: 0.0
+ readonly property real threshold: units.gu(1.5)
+ property bool snapIn: false
+
+ // update snap direction
+ function updateSnapDirection() {
+ if (prevX < listItemStyle.x && (snapChangerLimit <= listItemStyle.x)) {
+ snapIn = leadingPanel;
+ snapChangerLimit = listItemStyle.x - threshold;
+ } else if (prevX > listItemStyle.x && (listItemStyle.x < snapChangerLimit)) {
+ snapIn = !leadingPanel;
+ snapChangerLimit = listItemStyle.x + threshold;
+ }
+ prevX = listItemStyle.x;
+ }
+ // perform snapIn/Out
+ function snap() {
+ var snapPos = (swipedOffset > units.gu(2) && snapIn) ? panelWidth : 0.0;
+ snapPos *= leadingPanel ? 1 : -1;
+ snapAnimation.snapTo(snapPos);
+ }
+ // handle elasticity on overshoot
+ function overshoot(event) {
+ var offset = event.content.x - styledItem.contentItem.anchors.leftMargin;
+ offset *= leadingPanel ? 1 : -1;
+ if (offset > panelWidth) {
+ // do elastic move
+ event.content.x = styledItem.contentItem.x + (event.to.x - event.from.x) / 2;
+ }
+ }
+ }
+ snapAnimation: SmoothedAnimation {
+ objectName: "snap_animation"
+ target: styledItem.contentItem
+ property: "x"
+ // use 50GU/second velocity
+ velocity: units.gu(60)
+ onStopped: {
+ // trigger action
+ if (to == styledItem.contentItem.anchors.leftMargin && internals.selectedAction) {
+ internals.selectedAction.trigger(listItemIndex);
+ internals.selectedAction = null;
+ }
+ }
+ // animated snapping
+ function snapTo(pos) {
+ if (pos == to && styledItem.contentItem.x == to) {
+ return;
+ }
+
+ stop();
+ from = styledItem.contentItem.x;
+ if (!pos) {
+ pos = styledItem.contentItem.anchors.leftMargin;
+ }
+ to = pos;
+ start();
+ }
+ }
+
+ onXChanged: internals.updateSnapDirection()
+ // overriding default functions
+ function swipeEvent(event) {
+ if (event.status == SwipeEvent.Started) {
+ internals.prevX = x;
+ snapAnimation.stop();
+ } else if (event.status == SwipeEvent.Finished) {
+ internals.snap();
+ } else if (event.status == SwipeEvent.Updated) {
+ // handle elasticity when overshooting
+ internals.overshoot(event)
+ }
+ }
+ function rebound() {
+ snapAnimation.snapTo(0);
}
}
=== modified file 'modules/Ubuntu/Components/Themes/Ambiance/qmldir'
--- modules/Ubuntu/Components/Themes/Ambiance/qmldir 2014-12-02 15:00:37 +0000
+++ modules/Ubuntu/Components/Themes/Ambiance/qmldir 2015-02-19 08:31:57 +0000
@@ -56,5 +56,4 @@
internal HeadDividerStyle HeadDividerStyle.qml
#version 1.2
-ListItemPanel 1.2 ListItemPanel.qml
ListItemStyle 1.2 ListItemStyle.qml
=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
--- modules/Ubuntu/Components/plugin/plugin.cpp 2015-02-10 16:47:59 +0000
+++ modules/Ubuntu/Components/plugin/plugin.cpp 2015-02-19 08:31:57 +0000
@@ -170,10 +170,11 @@
qmlRegisterType(uri, 1, 1, "ServiceProperties");
// ListItem and related types, released to 1.2
- qmlRegisterType(uri, 1, 2, "ListItem");
+ qmlRegisterType(uri, 1, 2, "ListItem");
qmlRegisterType();
- qmlRegisterType(uri, 1, 2, "ListItemActions");
- qmlRegisterUncreatableType(uri, 1, 2, "ViewItems", "Not instantiable");
+ qmlRegisterUncreatableType(uri, 1, 2, "SwipeEvent", "This is an event object.");
+ qmlRegisterType(uri, 1, 2, "ListItemActions");
+ qmlRegisterUncreatableType(uri, 1, 2, "ViewItems", "Not instantiable");
}
void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
--- modules/Ubuntu/Components/plugin/plugin.pro 2015-02-10 16:47:59 +0000
+++ modules/Ubuntu/Components/plugin/plugin.pro 2015-02-19 08:31:57 +0000
@@ -118,8 +118,6 @@
propertychange_p.cpp \
uclistitemstyle.cpp \
ucviewitemsattached.cpp \
- uclistitemattached.cpp \
- ucactionpanel_p.cpp \
ucserviceproperties.cpp
# adapters
=== removed file 'modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp'
--- modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp 2015-02-06 07:51:31 +0000
+++ modules/Ubuntu/Components/plugin/ucactionpanel_p.cpp 1970-01-01 00:00:00 +0000
@@ -1,134 +0,0 @@
-/*
- * Copyright 2015 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see .
- */
-
-#include "uclistitemactions.h"
-#include "uclistitemactions_p.h"
-#include "uclistitem_p.h"
-#include "quickutils.h"
-#include "i18n.h"
-#include "plugin.h"
-#include
-#include "ucaction.h"
-#include "ucunits.h"
-#include "uclistitemstyle.h"
-
-UCActionPanel::UCActionPanel(UCListItem *item, bool leading)
- : QObject(item)
- , listItem(item)
- , panelItem(0)
- , status(UCListItem::None)
- , leading(leading)
- , connected(false)
-{
-
-}
-
-UCActionPanel::~UCActionPanel()
-{
-}
-
-UCListItemAttached *UCActionPanel::attachedObject()
-{
- if (!panelItem) {
- return 0;
- }
- return static_cast(
- qmlAttachedPropertiesObject(panelItem, false));
-}
-
-bool UCActionPanel::createPanel(QQmlComponent *panelDelegate)
-{
- if (panelItem || !panelDelegate) {
- return false;
- }
- if (!panelDelegate->isError()) {
- // use ListItem's style context to be able to access ther styledItem context property
- UCListItemPrivate *pListItem = UCListItemPrivate::get(listItem);
- QQmlContext *parentContext = qmlContext(pListItem->styleItem);
- QQmlContext *context = new QQmlContext(parentContext, parentContext);
- panelItem = qobject_cast(panelDelegate->beginCreate(context));
- if (!panelItem) {
- qmlInfo(listItem) << UbuntuI18n::tr("Error creating ListItem actions panel");
- } else {
- QQml_setParent_noEvent(panelItem, listItem);
- panelItem->setParentItem(listItem);
- // create attached property!
- UCListItemAttached *attached = static_cast(
- qmlAttachedPropertiesObject(panelItem));
- if (!attached->actions()) {
- attached->setList(listItem, leading, true);
- } else {
- // container is set, but we need to emit the signal again so we get the
- // attached props updated for those cases when the attached property is
- // created before the statement above
- Q_EMIT attached->actionsChanged();
- }
- panelDelegate->completeCreate();
- }
- } else {
- qmlInfo(listItem) << panelDelegate->errorString();
- }
- return panelItem != 0;
-}
-
-UCListItemActions *UCActionPanel::actions()
-{
- return leading ? UCListItemPrivate::get(listItem)->leadingActions :
- UCListItemPrivate::get(listItem)->trailingActions;
-}
-
-QQuickItem *UCActionPanel::panel() const
-{
- return panelItem;
-}
-
-bool UCActionPanel::grabPanel(UCActionPanel **panel, UCListItem *item, bool leading)
-{
- if (!(*panel) && item) {
- UCListItemActions *actions = leading ? UCListItemPrivate::get(item)->leadingActions :
- UCListItemPrivate::get(item)->trailingActions;
- if (actions) {
- (*panel) = new UCActionPanel(item, leading);
- UCListItemPrivate *pItem = UCListItemPrivate::get((*panel)->listItem);
- pItem->initStyleItem();
- if (!pItem->styleItem ||
- (pItem->styleItem && !(*panel)->createPanel(pItem->styleItem->m_actionsDelegate))) {
- delete (*panel);
- (*panel) = 0;
- }
- }
- }
- if (*panel) {
- (*panel)->connected = true;
- (*panel)->status = leading ? UCListItem::Leading : UCListItem::Trailing;
- Q_EMIT (*panel)->statusChanged();
- }
- return (*panel) != 0;
-}
-
-void UCActionPanel::ungrabPanel(UCActionPanel *panel)
-{
- if (!panel || !panel->connected) {
- return;
- }
- panel->connected = false;
- panel->panelItem->setVisible(false);
-}
-
-bool UCActionPanel::isConnected(UCActionPanel *panel)
-{
- return panel && panel->connected;
-}
=== modified file 'modules/Ubuntu/Components/plugin/uclistitem.cpp'
--- modules/Ubuntu/Components/plugin/uclistitem.cpp 2015-02-10 13:44:25 +0000
+++ modules/Ubuntu/Components/plugin/uclistitem.cpp 2015-02-19 08:31:57 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Canonical Ltd.
+ * Copyright 2014-2015 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -49,183 +49,6 @@
}
return result;
}
-/******************************************************************************
- * SnapAnimator
- *
- * The class handles the animation executed when the ListItemAction panel is
- * swiped. The animation is executed from the swipe position the mouse/touch is
- * released to the desired position given in snap(). The action panel is assumed
- * to be anchored to the ListItem.contentItem left or right, depending on which
- * action list is swiped in. Therefore the animator only changes the ListItem.contentItem
- * x coordinate.
- * The animation is defined by the style.
- */
-ListItemAnimator::ListItemAnimator(QObject *parent)
- : QObject(parent)
- , activeAnimations(0)
- , item(0)
-{
-}
-ListItemAnimator::~ListItemAnimator()
-{
- stop();
- // make sure we cannot animate anymore, for safety
- item = 0;
-}
-
-/*
- * Snap the ListItem.contentItem in or out, depending on the position specified
- * in "to" parameter. If the position is 0, a snap out will be executed - see
- * snapOut(). In any other cases a snap in action will be performed - see snapIn().
- */
-bool ListItemAnimator::snap(qreal to)
-{
- if (!item) {
- return false;
- }
- UCListItemPrivate *listItem = UCListItemPrivate::get(item);
- bool doSnapOut = (to == 0.0);
- activeAnimations |= (doSnapOut ? SnapOutAnimation : SnapInAnimation);
- // fix snap position, take leftMargin into account!
- to += QQuickItemPrivate::get(listItem->contentItem)->anchors()->leftMargin();
- if (to == listItem->contentItem->x()) {
- // there was no move, so we only do some cleanup
- completeAnimation();
- return true;
- }
-
- QQuickAbstractAnimation *snap = getSnapBehavior();
- if (snap) {
- snap->setAlwaysRunToEnd(false);
- connect(snap, &QQuickAbstractAnimation::runningChanged,
- this, &ListItemAnimator::completeAnimation,
- Qt::DirectConnection);
- }
- listItem->setContentMoving(true);
- if (snapBehavior) {
- snapBehavior->setEnabled(true);
- snapBehavior->write(to);
- }
- if (!snap) {
- // complete, as we don't have animation
- completeAnimation();
- }
- return true;
-}
-
-/*
- * The function completes a running snap animation.
- */
-void ListItemAnimator::stop()
-{
- if (snapBehavior && snapBehavior->enabled()) {
- QQuickAbstractAnimation *animation = snapBehavior->animation();
- if (animation) {
- // set animation to be user controlled temporarily so we can invoke stop()
- animation->setEnableUserControl();
- animation->stop();
- animation->setDisableUserControl();
- }
- snapBehavior->setEnabled(false);
- }
-}
-
-// handles animation completion
-void ListItemAnimator::completeAnimation()
-{
- QQuickAbstractAnimation *animation = static_cast(sender());
- if (animation && animation->isRunning()) {
- return;
- }
-
- // complete animations
- UCListItemPrivate *listItem = UCListItemPrivate::get(item);
- if (activeAnimations & SnapInAnimation) {
- listItem->snapIn();
- activeAnimations &= ~SnapInAnimation;
- } else if (activeAnimations & SnapOutAnimation) {
- listItem->snapOut();
- activeAnimations &= ~SnapOutAnimation;
- }
-
- // clean animations
- if (animation && !activeAnimations) {
- disconnect(animation, &QQuickAbstractAnimation::runningChanged,
- this, &ListItemAnimator::completeAnimation);
- }
- if (snapBehavior && snapBehavior->enabled()) {
- snapBehavior->setEnabled(false);
- }
-}
-
-/*
- * Snap out is performed when the ListItem.contentItem returns back to its original
- * X coordinates (0). At this point both leading and trailing action panels will
- * be disconnected, ascending Flickables will get unlocked (interactive value restored
- * to the state before they were locked) and ListItem.contentMoving will be reset.
- */
-void UCListItemPrivate::snapOut()
-{
- setHighlighted(false);
- setSwiped(false);
- if (parentAttached) {
- Q_Q(UCListItem);
- // restore flickable's interactive and cleanup
- parentAttached->disableInteractive(q, false);
- // no need to listen flickables any longer
- listenToRebind(false);
- }
- // disconnect actions
- UCActionPanel::ungrabPanel(leadingPanel);
- UCActionPanel::ungrabPanel(trailingPanel);
- // lock contentItem left/right edges
- lockContentItem(true);
- // set contentMoved to false
- setContentMoving(false);
-}
-
-/*
- * Snap in only resets the ListItem.contentMoving property, but will keep leading/trailing
- * actions connected as well as all ascendant Flickables locked (interactive = false).
- */
-void UCListItemPrivate::snapIn()
-{
- // turn content moving off
- setContentMoving(false);
-}
-
-/*
- * Returns the animation specified by the style, and configures the snapBehavior
- * controlling the animation.
- */
-QQuickAbstractAnimation *ListItemAnimator::getSnapBehavior()
-{
- if (snapBehavior) {
- return snapBehavior->animation();
- }
-
- UCListItemPrivate *listItem = UCListItemPrivate::get(item);
- // in order to get Behavior working properly, it must be created to have
- // ListItem.contentItem as parent, and must be in the same context as its
- // parent item
- snapBehavior = new QQuickBehavior(listItem->contentItem);
- snapBehavior->setParent(listItem->contentItem);
- QQmlContext *context = qmlContext(listItem->contentItem);
- QQmlEngine::setContextForObject(snapBehavior.data(), context);
-
- listItem->initStyleItem();
- QQuickAbstractAnimation *animation = listItem->styleItem ? listItem->styleItem->m_snapAnimation : 0;
- if (animation) {
- // patch behavior, use the same context as the animation
- snapBehavior->setAnimation(animation);
-
- // transfer animation to the contentItem
- animation->setParent(listItem->contentItem);
- }
- QQmlProperty property(listItem->contentItem, "x", context);
- snapBehavior->setTarget(property);
- return animation;
-}
/******************************************************************************
* Divider
@@ -375,10 +198,7 @@
, suppressClick(false)
, ready(false)
, customColor(false)
- , customOvershoot(false)
- , flicked(false)
, xAxisMoveThresholdGU(DEFAULT_SWIPE_THRESHOLD_GU)
- , overshoot(0)
, color(Qt::transparent)
, highlightColor(Qt::transparent)
, parentAttached(0)
@@ -386,8 +206,6 @@
, divider(new UCListItemDivider)
, leadingActions(0)
, trailingActions(0)
- , leadingPanel(0)
- , trailingPanel(0)
, mainAction(0)
, styleComponent(0)
, implicitStyleComponent(0)
@@ -401,7 +219,6 @@
void UCListItemPrivate::init()
{
Q_Q(UCListItem);
- animator.init(q);
contentItem->setObjectName("ListItemHolder");
QQml_setParent_noEvent(contentItem, q);
contentItem->setParentItem(q);
@@ -415,6 +232,10 @@
// turn activeFocusOnPress on
q->setActiveFocusOnPress(true);
+ // update swiped state
+ QObject::connect(contentItem, SIGNAL(xChanged()),
+ q, SLOT(_q_updateSwiping()), Qt::DirectConnection);
+
// catch theme changes
QObject::connect(&UCTheme::instance(), SIGNAL(nameChanged()), q, SLOT(_q_updateThemedData()));
_q_updateThemedData();
@@ -478,6 +299,12 @@
q->update();
}
+// update contentMoving property when ListItemStyle.snapAnimation stopped() signal is emitted
+void UCListItemPrivate::_q_contentMoving()
+{
+ setContentMoving(styleItem->m_snapAnimation->isRunning());
+}
+
/*!
* \qmlproperty Component ListItem::style
* Holds the style of the component defining the components visualizing the leading/
@@ -536,8 +363,10 @@
delete implicitStyleComponent;
Q_Q(UCListItem);
implicitStyleComponent = UCTheme::instance().createStyleComponent("ListItemStyle.qml", q);
- // set the objectnane for testing in tst_listitems.qml
- implicitStyleComponent->setObjectName("ListItemThemeStyle");
+ if (implicitStyleComponent) {
+ // set the objectnane for testing in tst_listitems.qml
+ implicitStyleComponent->setObjectName("ListItemThemeStyle");
+ }
// re-create style instance if it was created using the implicit style
if (reloadStyle) {
initStyleItem();
@@ -567,8 +396,12 @@
qmlInfo(q) << delegate->errorString();
return;
}
- QQmlContext *context = new QQmlContext(qmlContext(q));
+ QQmlContext *context = new QQmlContext(qmlContext(q), qmlContext(q));
context->setContextProperty("styledItem", q);
+ // also declare index property in case not defined
+ if (!context->contextProperty("index").isValid()) {
+ context->setContextProperty("index", index());
+ }
QObject *object = delegate->beginCreate(context);
styleItem = qobject_cast(object);
if (!styleItem) {
@@ -577,16 +410,10 @@
delete context;
return;
}
- context->setParent(styleItem);
QQml_setParent_noEvent(styleItem, q);
+ styleItem->setParentItem(q);
delegate->completeCreate();
Q_EMIT q->__styleInstanceChanged();
-
- // get the overshoot value from the style!
- if (!customOvershoot) {
- overshoot = styleItem->m_swipeOvershoot;
- Q_EMIT q->swipeOvershootChanged();
- }
}
/*!
@@ -658,7 +485,15 @@
Q_EMIT q->highlightedChanged();
}
}
-// toggles the tugged flag and installs/removes event filter
+// toggles the swiped flag and installs/removes event filter to capture pointer events outside
+// of list item area
+void UCListItemPrivate::_q_updateSwiping()
+{
+ if (swiped) {
+ setSwiped((contentItem->position() != zeroPos) || highlighted);
+ }
+}
+
void UCListItemPrivate::setSwiped(bool swiped)
{
suppressClick = swiped;
@@ -672,6 +507,8 @@
window->installEventFilter(q);
} else {
window->removeEventFilter(q);
+ // lock contentItem left/right edges
+ lockContentItem(true);
}
}
@@ -691,6 +528,7 @@
if (lock) {
contentAnchors->setLeft(left());
contentAnchors->setRight(right());
+ zeroPos = contentItem->position();
} else {
contentAnchors->resetLeft();
contentAnchors->resetRight();
@@ -706,17 +544,50 @@
q->update();
}
-// clamps the X value and moves the contentItem to the new X value
-void UCListItemPrivate::clampAndMoveX(qreal &x, qreal dx)
-{
- x += dx;
- // min cannot be less than the trailing's panel width
- QQuickItem *leadingPanelItem = leadingPanel ? leadingPanel->panel() : 0;
- QQuickItem *trailingPanelItem = trailingPanel ? trailingPanel->panel() : 0;
- qreal min = (trailingPanelItem) ? -trailingPanelItem->width() - overshoot: 0;
- // max cannot be bigger than 0 or the leading's width in case we have leading panel
- qreal max = (leadingPanelItem) ? leadingPanelItem->width() + overshoot: 0;
- x = CLAMP(x, min, max);
+/*
+ * Snap out is performed when the ListItem.contentItem returns back to its original
+ * X coordinates (0). At this point both leading and trailing action panels will
+ * be disconnected, ascending Flickables will get unlocked (interactive value restored
+ * to the state before they were locked) and ListItem.contentMoving will be reset.
+ */
+void UCListItemPrivate::snapOut()
+{
+ if (!ready) {
+ return;
+ }
+ setHighlighted(false);
+ if (parentAttached) {
+ Q_Q(UCListItem);
+ // restore flickable's interactive and cleanup
+ parentAttached->disableInteractive(q, false);
+ // no need to listen flickables any longer
+ listenToRebind(false);
+ }
+ if (styleItem) {
+ styleItem->invokeRebound();
+ }
+}
+
+// emits the style signal swipeEvent()
+void UCListItemPrivate::swipeEvent(const QPointF &localPos, UCSwipeEvent::Status status)
+{
+ UCSwipeEvent event(localPos, lastPos, contentItem->position() + (localPos - lastPos), status);
+ // clamp to the edges if the edge (leading/trailing) doesn't have actions defined
+ if ((event.m_contentPos.x() < zeroPos.x() && !trailingActions) ||
+ (event.m_contentPos.x() > zeroPos.x() && !leadingActions)) {
+ event.m_contentPos = zeroPos;
+ }
+ if (styleItem) {
+ styleItem->invokeSwipeEvent(&event);
+ }
+ if (event.m_contentPos != contentItem->position()) {
+ contentItem->setPosition(event.m_contentPos);
+ lastPos = localPos;
+ if (status == UCSwipeEvent::Updated) {
+ setContentMoving(true);
+ setSwiped(true);
+ }
+ }
}
/*!
@@ -814,7 +685,7 @@
* or to the back of the item, and are revealed by swiping the item horizontally.
* The swipe is started only after the mouse/touch move had passed a given threshold.
* The actions are visualized by a panel, which is configurable through the \l
- * ListItemStyle::actionsDelegate style property.
+ * ListItemStyle.
*
* The actions are configured through the \l leadingActions as well as \l
* trailingActions properties.
@@ -898,11 +769,6 @@
{
}
-UCListItemAttached *UCListItem::qmlAttachedProperties(QObject *owner)
-{
- return new UCListItemAttached(owner);
-}
-
void UCListItem::componentComplete()
{
UCStyledItemBase::componentComplete();
@@ -1017,22 +883,19 @@
// while moving, we cannot select any items
return;
}
- if (d->canHighlight(event) && !d->suppressClick
+ if (d->canHighlight(event)
&& !d->highlighted && event->button() == Qt::LeftButton) {
- // stop any ongoing animation!
- d->animator.stop();
+ // create style instance
+ d->initStyleItem();
d->setHighlighted(true);
d->lastPos = d->pressedPos = event->localPos();
// connect the Flickable to know when to rebound
d->listenToRebind(true);
- // if it was moved, grab the panels
- if (d->swiped) {
- UCActionPanel::grabPanel(&d->leadingPanel, this, true);
- UCActionPanel::grabPanel(&d->trailingPanel, this, false);
- if (d->parentAttached) {
- d->parentAttached->disableInteractive(this, true);
- }
+ if (d->swiped && d->parentAttached) {
+ d->parentAttached->disableInteractive(this, true);
}
+ // stop any ongoing animation!
+ d->swipeEvent(event->localPos(), UCSwipeEvent::Started);
}
// accept the event so we get the rest of the events as well
event->setAccepted(true);
@@ -1044,6 +907,7 @@
Q_D(UCListItem);
// set released
if (d->highlighted) {
+ // unblock ascending Flickables
d->listenToRebind(false);
if (d->parentAttached) {
d->parentAttached->disableInteractive(this, false);
@@ -1057,8 +921,10 @@
Q_EMIT d->mainAction->trigger(d->index());
}
}
- d->animator.snap(0);
+ d->snapOut();
} else {
+ // inform style about mouse/touch release
+ d->swipeEvent(event->localPos(), UCSwipeEvent::Finished);
d->suppressClick = false;
}
}
@@ -1070,10 +936,10 @@
Q_D(UCListItem);
UCStyledItemBase::mouseMoveEvent(event);
+// qDebug() << "MOVE" << event->localPos();
+
// accept the tugging only if the move is within the threshold
- bool leadingAttached = UCActionPanel::isConnected(d->leadingPanel);
- bool trailingAttached = UCActionPanel::isConnected(d->trailingPanel);
- if (d->highlighted && !(leadingAttached || trailingAttached)) {
+ if (d->highlighted && !d->swiped && (d->leadingActions || d->trailingActions)) {
// check if we can initiate the drag at all
// only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid
qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU);
@@ -1083,54 +949,21 @@
if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) {
// the press went out of the threshold area, enable move, if the direction allows it
d->lastPos = event->localPos();
- // tries to connect both panels so we do no longer need to take care which
- // got connected ad which not; this may fail in case of shared ListItemActions,
- // as then the panel is shared between the list items, and the panel might be
- // still in use in other panels. See UCListItemActionsPrivate::connectToListItem
- leadingAttached = UCActionPanel::grabPanel(&d->leadingPanel, this, true);
- trailingAttached = UCActionPanel::grabPanel(&d->trailingPanel, this, false);
// unlock contentItem's left/right edges
d->lockContentItem(false);
if (d->parentAttached) {
d->parentAttached->disableInteractive(this, true);
}
- }
- }
-
- if (leadingAttached || trailingAttached) {
- qreal x = d->contentItem->x();
- qreal dx = event->localPos().x() - d->lastPos.x();
- d->lastPos = event->localPos();
-
- if (dx) {
- // stop pressAndHold timer as we started to drag
- d->pressAndHoldTimer.stop();
- d->setContentMoving(true);
- // clamp X into allowed dragging area
- d->clampAndMoveX(x, dx);
- // block flickable
d->setSwiped(true);
- d->contentItem->setX(x);
- // decide which panel is visible by checking the contentItem's X coordinates
- qreal margin = QQuickItemPrivate::get(d->contentItem)->anchors()->leftMargin();
- if (d->contentItem->x() > margin) {
- if (d->leadingPanel) {
- d->leadingPanel->panel()->setVisible(true);
- }
- if (d->trailingPanel) {
- d->trailingPanel->panel()->setVisible(false);
- }
- } else if (d->contentItem->x() < margin) {
- // trailing revealed
- if (d->leadingPanel) {
- d->leadingPanel->panel()->setVisible(false);
- }
- if (d->trailingPanel) {
- d->trailingPanel->panel()->setVisible(true);
- }
- }
}
}
+
+ if (d->swiped) {
+ d->pressAndHoldTimer.stop();
+
+ // send swipe event to style and update contentItem position
+ d->swipeEvent(event->localPos(), UCSwipeEvent::Updated);
+ }
}
bool UCListItem::childMouseEventFilter(QQuickItem *child, QEvent *event)
@@ -1173,7 +1006,7 @@
}
if (!myPos.isNull() && !contains(myPos)) {
Q_D(UCListItem);
- d->animator.snap(0);
+ d->snapOut();
// only accept event, but let it be handled by the underlying or surrounding Flickables
event->accept();
}
@@ -1215,9 +1048,6 @@
}
// snap out before we change the actions
d->snapOut();
- // then delete panel
- delete d->leadingPanel;
- d->leadingPanel = 0;
d->leadingActions = actions;
Q_EMIT leadingActionsChanged();
}
@@ -1243,9 +1073,6 @@
}
// snap out before we change the actions
d->snapOut();
- // then delete panel
- delete d->trailingPanel;
- d->trailingPanel = 0;
d->trailingActions = actions;
Q_EMIT trailingActionsChanged();
}
@@ -1465,43 +1292,6 @@
}
/*!
- * \qmlproperty real ListItem::swipeOvershoot
- * The property configures the overshoot value on swiping. Its default value is
- * configured by the \l {ListItemStyle}{style}. Any positive value overrides the
- * default value, and any negative or undefined value resets it back to the default.
- */
-qreal UCListItemPrivate::swipeOvershoot() const
-{
- return overshoot;
-}
-void UCListItemPrivate::setSwipeOvershoot(qreal overshoot)
-{
- // mark any positive value as custom even if it is the same as the previous one
- customOvershoot = (overshoot >= 0.0);
- // same value should be guarded only if the style hasn't been loaded yet
- // swipeOvershoot can be set to 0 prior the style is loaded.
- if (this->overshoot == overshoot && styleItem) {
- return;
- }
- if (!customOvershoot) {
- resetSwipeOvershoot();
- return;
- }
- this->overshoot = overshoot;
- update();
- Q_Q(UCListItem);
- Q_EMIT q->swipeOvershootChanged();
-}
-void UCListItemPrivate::resetSwipeOvershoot()
-{
- customOvershoot = false;
- overshoot = styleItem ? styleItem->m_swipeOvershoot : 0.0;
- update();
- Q_Q(UCListItem);
- Q_EMIT q->swipeOvershootChanged();
-}
-
-/*!
* \qmlproperty list