Merge lp:~tpeeters/ubuntu-ui-toolkit/panel into lp:ubuntu-ui-toolkit

Proposed by Tim Peeters
Status: Merged
Approved by: Zsombor Egri
Approved revision: 507
Merged at revision: 461
Proposed branch: lp:~tpeeters/ubuntu-ui-toolkit/panel
Merge into: lp:ubuntu-ui-toolkit
Diff against target: 1218 lines (+753/-142)
10 files modified
CHANGES (+8/-1)
demos/Popover.qml (+2/-2)
modules/Ubuntu/Components/Panel.qml (+328/-82)
modules/Ubuntu/Components/Toolbar.qml (+54/-34)
modules/Ubuntu/Components/ToolbarActions.qml (+31/-11)
modules/Ubuntu/Components/qmldir (+1/-1)
tests/unit/tst_components/tst_panel.qml (+251/-0)
tests/unit/tst_components/tst_toolbar.qml (+74/-0)
themes/Ambiance/qmltheme/ToolbarButtonDelegate.qml (+0/-3)
themes/Ambiance/qmltheme/ToolbarDelegate.qml (+4/-8)
To merge this branch: bzr merge lp:~tpeeters/ubuntu-ui-toolkit/panel
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Andrea Cimitan (community) Needs Fixing
Tim Peeters Approve
Florian Boucault (community) Needs Fixing
Zsombor Egri Needs Fixing
Review via email: mp+158399@code.launchpad.net

Commit message

Add a new Panel component that can be swiped in and out from an edge of the window by the user. As opposed to the default toolbar (which inherits from the Panel), a Panel can be attached to any edge of the screen, and the developer is free to define its contents. For standard cases, the automatic toolbar from the MainView and Page can still be used.

API changes:
* CHANGED IN Toolbar: property bool active TO property bool opened
* CHANGED IN Toolbar: property bool lock TO property bool locked
* CHANGED IN ToolbarActions: property bool active TO property bool opened
* CHANGED IN ToolbarActions: property bool lock TO property bool locked

Description of the change

Add a new Panel component that can be swiped in and out from an edge of the window by the user. As opposed to the default toolbar (which inherits from the Panel), a Panel can be attached to any edge of the screen, and the developer is free to define its contents. For standard cases, the automatic toolbar from the MainView and Page can still be used.

API changes:
* CHANGED IN Toolbar: property bool active TO property bool opened
* CHANGED IN Toolbar: property bool lock TO property bool locked
* CHANGED IN ToolbarActions: property bool active TO property bool opened
* CHANGED IN ToolbarActions: property bool lock TO property bool locked

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

Ready for review!

After the latest changes, the user can swipe the toolbar out, even on top of toolbar buttons, while standard components such as Sliders and Buttons will fully work and prevent mouse events from passing through.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Zsombor Egri (zsombi) wrote :

file: ....modules/Ubuntu/Components/Toolbar.qml:134, unknown) - file:///home/zsombor/canonical/reviews/panel/modules/Ubuntu/Components/Toolbar.qml:134:5: QML Row: Binding loop detected for property "width"

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

What happens if you add a Panel to a MainView?

review: Needs Information
Revision history for this message
Zsombor Egri (zsombi) wrote :

174 + /*! \internal */
175 default property alias contents: bar.data

We agreed once that we document default properties too.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

183 + \li Qt.AlignLeft to swipe in the panel from the left
184 + \li Qt.AlignRight to swipe in the panel from the right

Think about LTR/RTL, thus AlignLeading/AlignTrailing would be more suitable...

Revision history for this message
Zsombor Egri (zsombi) wrote :

521 + // v will always be in the range 0..size, where v==0 means spread, v==size means hidden.
522 + property real v: panel.active ? 0 : size

Maybe a better name for "v"?

Revision history for this message
Zsombor Egri (zsombi) wrote :

568 + // All theming items go into the background because only the children
569 // of the GenericToolbar are being shown/hidden while the toolbar

GenericToolbar->Panel

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

So, as theming is moved inside the internal item, there won't be possibility to change the style class of the toolbar, right?
For now it's OK, however we need to come back to this when we will be able to position the delegate freely in a styled item.

Revision history for this message
Zsombor Egri (zsombi) wrote :

604 - enabled: action && action.enabled
605 + property bool enabled: action && action.enabled

What was the reason to override Item's enabled property?

review: Needs Information
Revision history for this message
Zsombor Egri (zsombi) wrote :

Unit tests to guard swipe interactions are missing.

review: Needs Fixing
Revision history for this message
Tim Peeters (tpeeters) wrote :

> What happens if you add a Panel to a MainView?

I made a small change, and now:
- Panels that align left/right/top always work
- Panels that align bottom work if no tools were defined for the toolbar (thus, toolbar is locked in inactive position)
- If there is a toolbar with tools, bottom-aligned panels do not work because the mousearea for swiping the toolbar is on top of them.

Sounds good to me. If you want your own panel at the bottom, do not define tools for the toolbar.

Revision history for this message
Tim Peeters (tpeeters) wrote :

> 183 + \li Qt.AlignLeft to swipe in the panel from the left
> 184 + \li Qt.AlignRight to swipe in the panel from the right
>
> Think about LTR/RTL, thus AlignLeading/AlignTrailing would be more suitable...

The developer still needs to set the anchors himself, and align only specifies which direction to swipe inside the edge of the area of the Panel to show/hide the panel. So, when going from LTR to RTL, the anchors would also need to be updated and it would only be confusing if the swiping direction would change when the anchors don't change automatically.

An example of why the anchors are not automatic, is this Panel (it is in the examples from documentation):
Panel {
  anchors {
    right: parent.right
    bottom: parent.bottom
    top: parent.top
  }
  width: parent.width / 2
}

This can be aligned top, bottom or right, which only defines the swiping direction, not the location of the panel when it is active.

Revision history for this message
Tim Peeters (tpeeters) wrote :

> 604 - enabled: action && action.enabled
> 605 + property bool enabled: action && action.enabled
>
> What was the reason to override Item's enabled property?

I forgot its there. Reverted.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Zsombor Egri (zsombi) wrote :

Found one more: (file:///home/zsombor/canonical/reviews/panel/modules/Ubuntu/Components/AbstractButton.qml:83, unknown) - file:///home/zsombor/canonical/reviews/panel/modules/Ubuntu/Components/AbstractButton.qml:83: Error: Insufficient arguments

This one on Toolbars, One->Two->Three, then swipe to show toolbar, and press "Cancel"

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

(file:///home/zsombor/canonical/reviews/panel/modules/Ubuntu/Components/Toolbar.qml:135, unknown) - file:///home/zsombor/canonical/reviews/panel/modules/Ubuntu/Components/Toolbar.qml:135:5: QML Row: Binding loop detected for property "width"

The binding loop it's still there.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

Rev. 458 - I don't necessarily think that we override default property, we define one. "contents" property is not Item's default one.

A better way to say in documentation would be: Default property, holds the content which will be swiped in and out.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

Would it be possible to split test_swipe() in smaller test cases?

Revision history for this message
Zsombor Egri (zsombi) wrote :

> > 183 + \li Qt.AlignLeft to swipe in the panel from the left
> > 184 + \li Qt.AlignRight to swipe in the panel from the right
> >
> > Think about LTR/RTL, thus AlignLeading/AlignTrailing would be more
> suitable...
>
> The developer still needs to set the anchors himself, and align only specifies
> which direction to swipe inside the edge of the area of the Panel to show/hide
> the panel. So, when going from LTR to RTL, the anchors would also need to be
> updated and it would only be confusing if the swiping direction would change
> when the anchors don't change automatically.
>
> An example of why the anchors are not automatic, is this Panel (it is in the
> examples from documentation):
> Panel {
> anchors {
> right: parent.right
> bottom: parent.bottom
> top: parent.top
> }
> width: parent.width / 2
> }
>
> This can be aligned top, bottom or right, which only defines the swiping
> direction, not the location of the panel when it is active.

Well, if you anchor an item to left on LTR, it will be anchored to right on RTL if I'm not mistaken :) so in that sense my comments are right.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

I'm switching between pages in the Toolbars demo like a maniac, but I don't get the binding loop any more.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Florian Boucault (fboucault) wrote :

In documentation of Panel:

"Any Items can be placed inside the Panel, but MouseAreas can block mouse events from reaching the panel and thus obstruct the swiping behavior for hiding the panel. As a result, the user cannot start swiping on the buttons in the examples above in order to hide the panel. To remedy this, clicked() signals are forwarded from the panel to its children."

That does not sound right. Why can't the MouseArea inside Panel be placed on top of all the content and refuse mouse events that it does not care about?

Revision history for this message
Florian Boucault (fboucault) wrote :

Panel.active is not a good name. It is not very clear what it does by just looking at it. Is it a synonym for enabled? No.
Opened/Closed would seem closer to what it actually is.

review: Needs Fixing
Revision history for this message
Florian Boucault (fboucault) wrote :

"Note that when using leading or trailing alignment in combination with LayoutMirroring.enabled = true, LayoutMirroring.childrenInherit must also be set in order for the Panel's MouseAreas to be anchored correctly."

Can't that be done automatically?

review: Needs Fixing
Revision history for this message
Florian Boucault (fboucault) wrote :

Panel.contents is an alias and its type must be documented explicitly.

review: Needs Fixing
Revision history for this message
Florian Boucault (fboucault) wrote :

Should we document the default values of the following properties?
- hintSize
- triggerSize
- lock

Revision history for this message
Florian Boucault (fboucault) wrote :

* The second example in documentation (the one with no theming) is less complex than the first one and should be put first.
* Overall these code examples could be simplified a bit:
 - replace the root Rectangle object with an Item
 - in the second example, put the Panel at the bottom with an height of units.gu(8), to be identical to the first example (developers understand how to place Items in QML, no need to demonstrate variations of it)

review: Needs Fixing
Revision history for this message
Florian Boucault (fboucault) wrote :

Please check with interested parties if the Panel suits them:
- Webbrowser (oSoMoN)
- Media player (renato)
- Shell (Saviq & Cimi)

Revision history for this message
Tim Peeters (tpeeters) wrote :

> In documentation of Panel:
>
> "Any Items can be placed inside the Panel, but MouseAreas can block mouse
> events from reaching the panel and thus obstruct the swiping behavior for
> hiding the panel. As a result, the user cannot start swiping on the buttons in
> the examples above in order to hide the panel. To remedy this, clicked()
> signals are forwarded from the panel to its children."
>
> That does not sound right. Why can't the MouseArea inside Panel be placed on
> top of all the content and refuse mouse events that it does not care about?

If the mouseArea accepts pressed events, then the panel cannot detect swipes anymore, and if the mosueArea does not accept pressed events, it cannot detect a click. So the only solution we could think of something in the panel to get a clicked signal, while the user can still swipe the panel is to have the panel itself to handle the mouse events and detect the click on the Item inside it.

The current toolbar can have toolbar buttons where you can swipe and click (that is also supported now), but no components in the toolbar can detect other events (if you place a "normal" button it won't have the pressed event so you cannot see the pressed animation, sliders etc do not work)

Revision history for this message
Andrea Cimitan (cimi) wrote :

Two small things

- On the panel component, there's a magic number (if (draggingArea.dragVelocity < -44) and > 44) that should ideally be calculated in a resolution independent way.

- We are missing tests for this slow swipes indeed (with a slow drag velocity between -44 and +44), one solution is to do multiple mouse moves without releasing the mouse press, adding few waits. The line not tested is:
panel.state = (bar.position < bar.size / 2) ? "spread" : "";

Revision history for this message
Tim Peeters (tpeeters) wrote :

I verified on the device that the toolbars in gallery-app and phone-app work exactly as before.

review: Approve
Revision history for this message
Andrea Cimitan (cimi) :
review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

fyi, a little program I am using for testing:
http://pastebin.ubuntu.com/5721799/

Revision history for this message
Tim Peeters (tpeeters) wrote :

> Panel.active is not a good name. It is not very clear what it does by just
> looking at it. Is it a synonym for enabled? No.
> Opened/Closed would seem closer to what it actually is.

This was already used in Toolbar, and is also used in the tools property for pages, so a lot of applications need to be changed if we rename active to opened.

Revision history for this message
Tim Peeters (tpeeters) wrote :

> > Panel.active is not a good name. It is not very clear what it does by just
> > looking at it. Is it a synonym for enabled? No.
> > Opened/Closed would seem closer to what it actually is.
>
> This was already used in Toolbar, and is also used in the tools property for
> pages, so a lot of applications need to be changed if we rename active to
> opened.

But I do agree with your comment, so I'm not sure what to do in this case.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Tim Peeters (tpeeters) wrote :

Tested with webbrowser-app. Seems to work well https://code.launchpad.net/~tpeeters/webbrowser-app/panel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) wrote :

It's good enough for the LensBar on the Dash (works), testing this afternoon/tomorrow morning to see how it works on the Launcher, which I believe will be more complicated due to Mouse events required by the Launcher.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~tpeeters/ubuntu-ui-toolkit/panel updated
506. By Tim Peeters

print warning when using deprecated properties

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~tpeeters/ubuntu-ui-toolkit/panel updated
507. By Tim Peeters

add FIXME

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CHANGES'
2--- CHANGES 2013-04-26 20:32:38 +0000
3+++ CHANGES 2013-04-30 17:34:37 +0000
4@@ -10,7 +10,14 @@
5 API Changes
6 ***********
7
8-* None
9+* CHANGED IN Toolbar: property bool active TO property bool opened
10+* CHANGED IN Toolbar: property bool lock TO property bool locked
11+* CHANGED IN ToolbarActions: property bool active TO property bool opened
12+* CHANGED IN ToolbarActions: property bool lock TO property bool locked
13+* DEPRECATED IN Toolbar: property bool active
14+* DEPRECATED IN Toolbar: property bool lock
15+* DEPRECATED IN ToolbarActions: property bool active
16+* DEPRECATED IN ToolbarActions: property bool lock
17
18 Compatibility Breaks
19 ********************
20
21=== modified file 'demos/Popover.qml'
22--- demos/Popover.qml 2013-04-26 20:31:16 +0000
23+++ demos/Popover.qml 2013-04-30 17:34:37 +0000
24@@ -26,8 +26,8 @@
25 iconSource: "call_icon.png"
26 onTriggered: PopupUtils.open(actionSelectionPopover, caller)
27 }
28- lock: true
29- active: true
30+ locked: true
31+ opened: true
32 }
33
34 TemplateSection {
35
36=== renamed file 'modules/Ubuntu/Components/GenericToolbar.qml' => 'modules/Ubuntu/Components/Panel.qml'
37--- modules/Ubuntu/Components/GenericToolbar.qml 2013-04-26 20:31:16 +0000
38+++ modules/Ubuntu/Components/Panel.qml 2013-04-30 17:34:37 +0000
39@@ -18,49 +18,193 @@
40 import Ubuntu.Components 0.1 as Toolkit
41
42 /*!
43- \internal
44- \qmltype GenericToolbar
45+ \qmltype Panel
46 \inqmlmodule Ubuntu.Components 0.1
47 \ingroup ubuntu
48+ \brief A panel that can be swiped in and out from an edge of the window by the user.
49+ For most applications, it is highly recommended to use the \l MainView instead which includes
50+ a toolbar at its bottom that can be swiped in or out.
51+
52+ Unless your application has very specific needs for a Panel, use a \l MainView with the
53+ default toolbar.
54+
55+ When using a Panel, do not theme it directly, but theme its contents, because
56+ the Panel itself should not have visible elements, since it is
57+ in the view (to detect mouse events) even when its contents should be invisible.
58+
59+ Set the anchors and/or width/height of the Panel to specify the area that the Panel covers when
60+ opened.
61+
62+ A black panel that can be swiped in from the lower-right of the window can be created like this:
63+ \qml
64+ import QtQuick 2.0
65+ import Ubuntu.Components 0.1
66+
67+ Item {
68+ width: units.gu(80)
69+ height: units.gu(80)
70+
71+ Panel {
72+ id: panel
73+ anchors {
74+ right: parent.right
75+ bottom: parent.bottom
76+ }
77+ width: parent.width / 2
78+ height: units.gu(8)
79+
80+ Rectangle {
81+ anchors.fill: parent
82+ color: "black"
83+
84+ Button {
85+ anchors.centerIn: parent
86+ text: "Click"
87+ }
88+ }
89+ }
90+ }
91+ \endqml
92+
93+ A panel that looks like the standard (bottom-aligned) toolbar, but with custom contents, can be created like this:
94+ \qml
95+ import QtQuick 2.0
96+ import Ubuntu.Components 0.1
97+
98+ Item {
99+ width: units.gu(80)
100+ height: units.gu(80)
101+
102+ Panel {
103+ id: panel
104+ anchors {
105+ left: parent.left
106+ right: parent.right
107+ bottom: parent.bottom
108+ }
109+ height: units.gu(8)
110+
111+ Item {
112+ anchors.fill: parent
113+ ItemStyle.class: "toolbar"
114+
115+ // two properties used by the toolbar delegate:
116+ property bool opened: panel.opened
117+ property bool animating: panel.animating
118+
119+ Button {
120+ anchors.centerIn: parent
121+ text: "Click"
122+ }
123+ }
124+ }
125+ }
126+ \endqml
127+
128+ Any Items can be placed inside the Panel, but MouseAreas can block mouse events from reaching
129+ the panel and thus obstruct the swiping behavior for hiding the panel. As a result, the user cannot
130+ start swiping on the buttons in the examples above in order to hide the panel. To remedy this, clicked()
131+ signals are forwarded from the panel to its children. The children's clicked() signal does not have
132+ a mouse parameter. Example:
133+ \qml
134+ import QtQuick 2.0
135+ import Ubuntu.Components 0.1
136+
137+ Rectangle {
138+ color: "grey"
139+ width: units.gu(40)
140+ height: units.gu(40)
141+
142+ Panel {
143+ id: panel
144+ anchors {
145+ left: parent.left
146+ right: parent.right
147+ bottom: parent.bottom
148+ }
149+ height: units.gu(8)
150+
151+ Rectangle {
152+ color: "white"
153+ anchors.fill: parent
154+ Rectangle {
155+ width: units.gu(8)
156+ height: units.gu(4)
157+ anchors.centerIn: parent
158+ color: "red"
159+ signal clicked()
160+ onClicked: print("The red rectangle was clicked");
161+ }
162+ }
163+ }
164+ }
165+ \endqml
166+ Like this, the red rectangle accepts clicked() events, but the user can still swipe down on top
167+ of the rectangle in order to hide the panel.
168 */
169 Item {
170- id: bottomBar
171- anchors {
172- left: parent.left
173- right: parent.right
174- bottom: parent.bottom
175- }
176+ id: panel
177+
178+ /*!
179+ Default property, holds the content which will be swiped in and out.
180+ \qmlproperty list<Object> contents
181+ */
182 default property alias contents: bar.data
183
184 /*!
185- When active, the bar is visible, otherwise it is hidden.
186- Use bottom edge swipe up/down to activate/deactivate the bar.
187- The active property is not updated until the swipe gesture is completed.
188+ The property defines the alignment of the panel.
189+ The implementation supports the following values:
190+ \list
191+ \li Qt.AlignBottom to swipe in the panel from the bottom (default)
192+ \li Qt.AlignTop to swipe in the panel from the top
193+ \li Qt.AlignLeft to swipe in the panel from the left
194+ \li Qt.AlignRight to swipe in the panel from the right
195+ \li Qt.AlignLeading left when layout mirrorring is disabled, right otherwise
196+ \li Qt.AlignTrailing right when layout mirroring is disabled, left otherwise
197+ \endlist
198+ The default value is \b Qt.AlignBottom, and it is not recommended to change the
199+ default value because the left, right and top edge are already used for system
200+ functions, while the bottom edge is reserved for app-specific functionality
201+ such as a default toolbar. The use of Qt.AlignLeading and Qt.AlignTrailing is
202+ preferred over Qt.AlignLeft and Qt.AlignRight in order to more easily support
203+ right-to-left user interfaces that use LayoutMirroring.
204+ */
205+ property int align: Qt.AlignBottom
206+
207+ /*!
208+ When opened, the panel is visible, otherwise it is hidden.
209+ Use edge swipes to open/close the panel.
210+ The opened property is not updated until the swipe gesture is completed.
211 */
212- property bool active: false
213- onActiveChanged: {
214- if (active) state = "spread";
215+ property bool opened: false
216+ /*! \internal */
217+ onOpenedChanged: {
218+ if (opened) state = "spread";
219 else state = "";
220 }
221
222 /*!
223- Disable bottom edge swipe to activate/deactivate the toolbar.
224+ Disable edge swipe to open/close the panel. False by default.
225 */
226- property bool lock: false
227- onLockChanged: {
228+ property bool locked: false
229+ /*! \internal */
230+ onLockedChanged: {
231 if (state == "hint" || state == "moving") {
232 draggingArea.finishMoving();
233 }
234 }
235
236 /*!
237- How much of the toolbar to show when starting interaction.
238+ How much of the panel to show when the user touches the panel's edge.
239+ This gives the user a hint that there is a panel hiding at that edge and
240+ invites him/her to swipe to show the panel completely. Default value: units.gu(2).
241 */
242- property real hintSize: units.gu(1)
243+ property real hintSize: units.gu(2)
244
245 /*!
246- The height of the mouse area used to detect edge swipes to
247- activate the toolbar.
248+ The size (height for top or bottom-aligned panels, width for left or right-aligned
249+ panels) of the mouse area used to detect edge swipes to open the panel, when
250+ the panel is not opened. Default value: units.gu(2).
251 */
252 property real triggerSize: units.gu(2)
253
254@@ -69,28 +213,28 @@
255 name: "hint"
256 PropertyChanges {
257 target: bar
258- y: bar.height - bottomBar.hintSize
259+ position: bar.size - panel.hintSize
260 }
261 },
262 State {
263 name: "moving"
264 PropertyChanges {
265 target: bar
266- y: MathUtils.clamp(draggingArea.mouseY - internal.movingDelta, 0, bar.height)
267+ position: MathUtils.clamp(draggingArea.mousePosition - internal.movingDelta, 0, bar.size)
268 }
269 },
270 State {
271 name: "spread"
272 PropertyChanges {
273 target: bar
274- y: 0
275+ position: 0
276 }
277 },
278 State {
279 name: ""
280 PropertyChanges {
281 target: bar
282- y: bar.height
283+ position: bar.size
284 explicit: true
285 }
286 }
287@@ -99,15 +243,15 @@
288 /*!
289 The toolbar is currently not in a stable hidden or visible state.
290 */
291- readonly property bool animating: draggingArea.pressed || (state == "" && bar.y != bar.height) || (state == "spread" && bar.y != 0)
292+ readonly property bool animating: draggingArea.pressed || (state == "" && bar.position != bar.size) || (state == "spread" && bar.position != 0)
293
294 transitions: [
295 Transition {
296 to: ""
297 PropertyAnimation {
298 target: bar
299- properties: "y"
300- duration: 50
301+ properties: "position"
302+ duration: internal.transitionDuration
303 easing.type: Easing.OutQuad
304 }
305 },
306@@ -115,8 +259,8 @@
307 to: "hint"
308 PropertyAnimation {
309 target: bar
310- properties: "y"
311- duration: 50
312+ properties: "position"
313+ duration: internal.transitionDuration
314 easing.type: Easing.OutQuad
315 }
316 },
317@@ -124,8 +268,8 @@
318 to: "spread"
319 PropertyAnimation {
320 target: bar
321- properties: "y"
322- duration: 50
323+ properties: "position"
324+ duration: internal.transitionDuration
325 easing.type: Easing.OutQuad
326 }
327 }
328@@ -133,108 +277,210 @@
329
330 QtObject {
331 id: internal
332+
333+ /*!
334+ The duration in milliseconds of sliding in or out transitions when opening, closing, and showing the hint.
335+ Default value: 250
336+ // FIXME: Update to use ubuntuFastBeat when animation speeds are added to the SDK.
337+ */
338+ property real transitionDuration: 250
339+
340+
341 property string previousState: ""
342 property int movingDelta
343
344 // Used for recovering the state from before
345 // bottomBarVisibilityCommunicator forced the toolbar to hide.
346- property bool savedLock: bottomBar.lock
347- property bool savedActive: bottomBar.active
348+ property bool savedlocked: panel.locked
349+ property bool savedOpened: panel.opened
350+
351+ // Convert from Qt.AlignLeading to Qt.AlignTrailing to Qt.AlignLeft and Qt.AlignRight
352+ property int align: {
353+ if (panel.align === Qt.AlignLeading) {
354+ if (panel.LayoutMirroring.enabled) {
355+ return Qt.AlignRight;
356+ } else {
357+ return Qt.AlignLeft;
358+ }
359+ } else if (panel.align === Qt.AlignTrailing) {
360+ if (panel.LayoutMirroring.enabled) {
361+ return Qt.AlignLeft;
362+ } else {
363+ return Qt.AlignRight;
364+ }
365+ } else {
366+ return panel.align;
367+ }
368+ }
369+
370+ readonly property int orientation: (panel.align === Qt.AlignTop || panel.align === Qt.AlignBottom)
371+ ? Qt.Horizontal : Qt.Vertical
372 }
373
374 Connections {
375+ // FIXME: bottomBarVisibilityCommunicator is not the most-suitable name anymore.
376 target: bottomBarVisibilityCommunicator
377 onForceHiddenChanged: {
378 if (bottomBarVisibilityCommunicator.forceHidden) {
379- internal.savedLock = bottomBar.lock;
380- internal.savedActive = bottomBar.active;
381- bottomBar.active = false;
382- bottomBar.lock = true;
383+ internal.savedLocked = panel.locked;
384+ internal.savedOpened = panel.opened;
385+ panel.opened = false;
386+ panel.locked = true;
387 } else { // don't force hidden
388- bottomBar.lock = internal.savedLock;
389- if (internal.savedLock) bottomBar.active = internal.savedActive;
390- // if the toolbar was locked, do not slide it back in
391- // until the user performs a bottom-edge-swipe.
392+ panel.locked = internal.savedlocked;
393+ if (internal.savedlocked) panel.opened = internal.savedOpened;
394+ // if the panel was locked, do not slide it back in
395+ // until the user performs an edge swipe.
396 }
397 }
398 }
399
400+ /*! \internal */
401 onStateChanged: {
402 if (state == "hint") {
403- internal.movingDelta = bottomBar.hintSize + draggingArea.initialY - bar.height;
404+ internal.movingDelta = panel.hintSize + draggingArea.initialPosition - bar.size;
405 } else if (state == "moving" && internal.previousState == "spread") {
406- internal.movingDelta = draggingArea.initialY;
407+ internal.movingDelta = draggingArea.initialPosition;
408 } else if (state == "spread") {
409- bottomBar.active = true;
410+ panel.opened = true;
411 } else if (state == "") {
412- bottomBar.active = false;
413+ panel.opened = false;
414 }
415 internal.previousState = state;
416 }
417
418- Item {
419- id: bar
420- height: parent.height
421- anchors {
422- left: parent.left
423- right: parent.right
424- }
425-
426- y: bottomBar.active ? 0 : height
427- }
428-
429 Toolkit.InverseMouseArea {
430 anchors.fill: draggingArea
431 onClicked: {
432 mouse.accepted = false;
433 // the mouse click may cause an update
434- // of lock by the clicked Item behind
435- if (!bottomBar.lock) bottomBar.active = false;
436+ // of locked by the clicked Item behind
437+ if (!panel.locked) panel.opened = false;
438 }
439 propagateComposedEvents: true
440- visible: bottomBar.lock == false && bottomBar.state == "spread"
441+ visible: panel.locked == false && panel.state == "spread"
442 }
443
444 DraggingArea {
445- orientation: Qt.Vertical
446 id: draggingArea
447- anchors {
448- bottom: parent.bottom
449- left: parent.left
450- right: parent.right
451- }
452- height: bottomBar.active ? bar.height + units.gu(1) : toolbar.triggerSize
453+ orientation: internal.orientation === Qt.Horizontal ? Qt.Vertical : Qt.Horizontal
454 zeroVelocityCounts: true
455- propagateComposedEvents: true
456- visible: !bottomBar.lock
457-
458- property int initialY
459+ anchors {
460+ top: panel.align === Qt.AlignBottom ? undefined : parent.top
461+ bottom: panel.align === Qt.AlignTop ? undefined : parent.bottom
462+ left: panel.align === Qt.AlignRight || panel.align === Qt.AlignTrailing ? undefined : parent.left
463+ right: panel.align === Qt.AlignLeft || panel.align === Qt.AlignLeading ? undefined : parent.right
464+ }
465+ height: internal.orientation === Qt.Horizontal ? panel.opened ? bar.size + units.gu(1) : panel.triggerSize : undefined
466+ width: internal.orientation === Qt.Vertical ? panel.opened ? bar.size + units.gu(1) : panel.triggerSize : undefined
467+ visible: !panel.locked || panel.opened
468+
469+ property int mousePosition: getMousePosition()
470+ function getMousePosition() {
471+ switch (internal.align) {
472+ case Qt.AlignLeft:
473+ return -mouseX;
474+ case Qt.AlignRight:
475+ return mouseX;
476+ case Qt.AlignBottom:
477+ return mouseY;
478+ case Qt.AlignTop:
479+ return -mouseY;
480+ }
481+ }
482+
483+ // set in onPressed, reset when entering "moving" state
484+ property Item pressedItem: null
485+
486+ // find the first child with a clicked property:
487+ function getClickableItem(mouse) {
488+ var item = bar; // contains the children
489+ while (item && !item.hasOwnProperty("clicked")) {
490+ var coords = mapToItem(item, mouse.x, mouse.y);
491+ // FIXME: When using a ListView the highlight may be
492+ // returned instead of the Item that you are looking for
493+ item = item.childAt(coords.x, coords.y);
494+ }
495+ return item; // will be null if no item has clicked() signal.
496+ }
497+
498+ // forward clicked events to any child Item with a clicked() signal, not
499+ // just MouseAreas since MouseAreas would block swiping of the panel.
500+ // This must also happen when the panel is locked, so the DraggingArea is
501+ // never disabled, and other signal handlers will return when panel.locked is true.
502+ onClicked: {
503+ if (pressedItem && pressedItem === getClickableItem(mouse)) {
504+ // Click event positioned at the Item where the user first pressed
505+ pressedItem.clicked();
506+ }
507+ }
508+
509+ property int initialPosition
510 onPressed: {
511- initialY = mouseY;
512- if (bottomBar.state == "") bottomBar.state = "hint";
513- else bottomBar.state = "moving";
514+ pressedItem = getClickableItem(mouse)
515+ if (panel.locked) return;
516+ initialPosition = getMousePosition();
517+ if (panel.state == "") panel.state = "hint";
518 }
519
520 onPositionChanged: {
521- if (bottomBar.state == "hint" && mouseY < initialY) {
522- bottomBar.state = "moving";
523+ if (panel.locked) return;
524+ if (panel.state == "hint" && mousePosition < initialPosition) {
525+ panel.state = "moving";
526+ pressedItem = null;
527+ } else if (panel.state == "spread" && mousePosition > initialPosition) {
528+ panel.state = "moving";
529+ pressedItem = null;
530 }
531 }
532
533- onReleased: finishMoving()
534+ onReleased: {
535+ if (panel.locked) return;
536+ finishMoving();
537+ }
538 // Mouse cursor moving out of the window while pressed on desktop
539- onCanceled: finishMoving()
540+ onCanceled: {
541+ if (panel.locked) return;
542+ finishMoving();
543+ }
544
545- // FIXME: Make all parameters below themable.
546+ // FIXME: Make all parameters below themable and resolution-independent.
547 // The value of 44 was copied from the Launcher.
548 function finishMoving() {
549 if (draggingArea.dragVelocity < -44) {
550- bottomBar.state = "spread";
551+ if (internal.align === Qt.AlignBottom || internal.align === Qt.AlignRight) {
552+ panel.state = "spread";
553+ } else {
554+ panel.state = "";
555+ }
556 } else if (draggingArea.dragVelocity > 44) {
557- bottomBar.state = "";
558+ if (internal.align === Qt.AlignBottom || internal.align === Qt.AlignRight) {
559+ panel.state = "";
560+ } else {
561+ panel.state = "spread";
562+ }
563 } else {
564- bottomBar.state = (bar.y < bar.height / 2) ? "spread" : "";
565+ panel.state = (bar.position < bar.size / 2) ? "spread" : "";
566 }
567 }
568 }
569+
570+ Item {
571+ id: bar
572+ height: parent.height
573+ width: parent.width
574+ anchors {
575+ left: internal.orientation === Qt.Horizontal ? parent.left : undefined
576+ right: internal.orientation === Qt.Horizontal ? parent.right : undefined
577+ top: internal.orientation === Qt.Vertical ? parent.top : undefined
578+ bottom: internal.orientation === Qt.Vertical ? parent.bottom : undefined
579+ }
580+
581+ property real size: internal.orientation === Qt.Horizontal ? height : width
582+ //position will always be in the range 0..size, where position==0 means spread, position==size means hidden.
583+ property real position: panel.opened ? 0 : size
584+
585+ y: internal.align === Qt.AlignTop ? -position : internal.align === Qt.AlignBottom ? position : 0
586+ x: internal.align === Qt.AlignLeft ? -position : internal.align === Qt.AlignRight ? position : 0
587+ }
588 }
589
590=== modified file 'modules/Ubuntu/Components/Toolbar.qml'
591--- modules/Ubuntu/Components/Toolbar.qml 2013-04-26 20:31:16 +0000
592+++ modules/Ubuntu/Components/Toolbar.qml 2013-04-30 17:34:37 +0000
593@@ -29,13 +29,34 @@
594 \brief Application toolbar. This class is not exposed because it will
595 be automatically added when a Page defines tools.
596 */
597-GenericToolbar {
598+Panel {
599 id: toolbar
600- Theming.ItemStyle.class: "toolbar"
601-
602+ anchors {
603+ left: parent.left
604+ right: parent.right
605+ bottom: parent.bottom
606+ }
607 height: background.height
608- hintSize: units.gu(2)
609- triggerSize: units.gu(2)
610+
611+ /*!
612+ \deprecated
613+ Use property bool opened instead.
614+ */
615+ property bool active
616+ onActiveChanged: {
617+ print("Toolbar.active property is DEPRECATED. Use opened instead.");
618+ toolbar.opened = active;
619+ }
620+
621+ /*
622+ \deprecated
623+ Use property locked instead.
624+ */
625+ property bool lock
626+ onLockChanged: {
627+ print("Toolbar.lock property is DEPRECATED. Use locked instead.");
628+ toolbar.locked = lock;
629+ }
630
631 /*!
632 \preliminary
633@@ -43,42 +64,45 @@
634 */
635 property ToolbarActions tools: null
636 onToolsChanged: {
637- if (tools && tools.active && tools.lock) {
638+ if (tools && tools.opened && tools.locked) {
639 // toolbar is locked in visible state.
640 internal.visibleTools = tools;
641- active = true;
642- } else if (!active && !animating) {
643+ opened = true;
644+ } else if (!opened && !animating) {
645 // toolbar is invisible
646 internal.visibleTools = tools;
647 } else {
648- active = false;
649+ opened = false;
650 // internal.visibleTools will be updated
651 // when the hide animation is finished
652 }
653 }
654
655- // if tools is not specified, lock the toolbar in inactive position
656- lock: tools ? tools.lock : true
657+ // if tools is not specified, lock the toolbar in closed position
658+ locked: tools ? tools.locked : true
659
660 Connections {
661 target: tools
662- onActiveChanged: toolbar.active = tools.active;
663+ onOpenedChanged: toolbar.opened = tools.opened;
664+ onLockedChanged: toolbar.locked = tools.locked;
665 }
666- onActiveChanged: if (tools) tools.active = toolbar.active
667+ onOpenedChanged: if (tools) tools.opened = toolbar.opened
668+ onLockedChanged: if (tools) tools.locked = toolbar.locked
669 QtObject {
670 id: internal
671 property ToolbarActions visibleTools: tools
672 }
673
674 onAnimatingChanged: {
675- if (!animating && !active) {
676+ if (!animating && !opened) {
677 internal.visibleTools = toolbar.tools;
678 }
679 }
680
681 Item {
682- // All visual items go into the background because only the children
683- // of the GenericToolbar are being shown/hidden while the toolbar
684+ // FIXME:
685+ // All theming items go into the background because only the children
686+ // of the Panel are being shown/hidden while the toolbar
687 // itself may stay in place.
688 id: background
689 anchors {
690@@ -88,29 +112,24 @@
691 }
692 height: units.gu(8)
693
694- Theming.ItemStyle.style: toolbar.Theming.ItemStyle.style
695- Theming.ItemStyle.delegate: toolbar.Theming.ItemStyle.delegate
696-
697- MouseArea {
698- // don't let mouse events go through the toolbar
699- anchors.fill: parent
700- // FIXME: Bug in qml? Without onClicked below, this MouseArea
701- // seems disabled.
702- onClicked: { }
703- }
704+ Theming.ItemStyle.class: "toolbar"
705+ // The values of opened and animated properties are used in the delegate
706+ property bool opened: toolbar.opened
707+ property bool animating: toolbar.animating
708 }
709
710 Component {
711 id: toolButtonComponent
712- Button {
713+ Item {
714 id: toolButton
715 Theming.ItemStyle.class: "toolbar-button"
716- text: action && action.text ? action.text : ""
717- iconSource: action && action.iconSource ? action.iconSource : ""
718+ property string text: action && action.text ? action.text : ""
719+ property url iconSource: action && action.iconSource ? action.iconSource : ""
720+ signal clicked()
721 onClicked: action.triggered(toolButton)
722 enabled: action && action.enabled
723 visible: action && action.visible
724- width: visible ? implicitWidth : 0
725+ width: units.gu(5)
726 height: toolbar.height
727 }
728 }
729@@ -126,11 +145,13 @@
730 }
731 onStatusChanged: {
732 if (item && status == Loader.Ready && action && action.itemHint) {
733- if (item.hasOwnProperty("clicked")) item.clicked.connect(action.triggered);
734- if (item.hasOwnProperty("accepted")) item.accepted.connect(action.triggered);
735- if (item.hasOwnProperty("triggered")) item.accepted.connect(action.triggered);
736+ if (item.hasOwnProperty("clicked")) item.clicked.connect(backButton.itemTriggered);
737+ if (item.hasOwnProperty("accepted")) item.accepted.connect(backButton.itemTriggered);
738+ if (item.hasOwnProperty("triggered")) item.accepted.connect(backButton.itemTtriggered);
739 }
740 }
741+ signal itemTriggered()
742+ onItemTriggered: action.triggered(item)
743 }
744
745 Row {
746@@ -141,7 +162,6 @@
747 top: parent.top
748 rightMargin: units.gu(2)
749 }
750- width: childrenRect.width
751 spacing: units.gu(1)
752
753 Repeater {
754
755=== modified file 'modules/Ubuntu/Components/ToolbarActions.qml'
756--- modules/Ubuntu/Components/ToolbarActions.qml 2013-04-26 20:31:16 +0000
757+++ modules/Ubuntu/Components/ToolbarActions.qml 2013-04-30 17:34:37 +0000
758@@ -30,7 +30,7 @@
759 When a \l Page is used inside a \l Tabs or \l PageStack, the toolbar will automatically show
760 the tools of the active \l Page. When the active \l Page inside the \l Tabs or \l PageStack
761 is updated by changing the selected \l Tab or by pushing/popping a \l Page on the \l PageStack,
762- the toolbar will automatically hide, except if the new active \l Page has the \l lock property set.
763+ the toolbar will automatically hide, except if the new active \l Page has the \l locked property set.
764
765 \qml
766 import QtQuick 2.0
767@@ -61,8 +61,8 @@
768 text: "cancel"
769 }
770 }
771- lock: true
772- active: true
773+ locked: true
774+ opened: true
775 }
776 }
777 }
778@@ -98,16 +98,36 @@
779 property Item __pageStack: null
780
781 /*!
782- The toolbar is active
783- */
784- property bool active: false
785-
786- /*!
787- The toolbar cannot be made active or inactive by bottom-edge swipes.
788+ The toolbar is opened
789+ */
790+ property bool opened: false
791+
792+ /*!
793+ \deprecated
794+ Use property opened instead.
795+ */
796+ property bool active
797+ onActiveChanged: {
798+ print("ToolbarActions.active property is DEPRECATED. Use opened instead.");
799+ toolbarActions.opened = active;
800+ }
801+
802+ /*!
803+ \deprecated
804+ Use property locked instead.
805+ */
806+ property bool lock: toolbarActions.locked
807+ onLockChanged: {
808+ print("ToolbarActions.lock property is DEPRECATED. Use locked instead.");
809+ toolbarActions.locked = lock;
810+ }
811+
812+ /*!
813+ The toolbar cannot be opened/closed by bottom-edge swipes.
814 If the ToolbarActions contains no visible actions, it is automatically
815- locked (in inactive state).
816+ locked (in closed state).
817 */
818- property bool lock: !toolbarActions.__hasVisibleActions()
819+ property bool locked: !toolbarActions.__hasVisibleActions()
820
821 /*!
822 \internal
823
824=== modified file 'modules/Ubuntu/Components/qmldir'
825--- modules/Ubuntu/Components/qmldir 2013-04-26 20:31:16 +0000
826+++ modules/Ubuntu/Components/qmldir 2013-04-30 17:34:37 +0000
827@@ -5,7 +5,7 @@
828 ToolbarActions 0.1 ToolbarActions.qml
829 MainView 0.1 MainView.qml
830 Button 0.1 Button.qml
831-internal GenericToolbar GenericToolbar.qml
832+Panel 0.1 Panel.qml
833 internal DraggingArea DraggingArea.qml
834 Tabs 0.1 Tabs.qml
835 Tab 0.1 Tab.qml
836
837=== added file 'tests/unit/tst_components/tst_panel.qml'
838--- tests/unit/tst_components/tst_panel.qml 1970-01-01 00:00:00 +0000
839+++ tests/unit/tst_components/tst_panel.qml 2013-04-30 17:34:37 +0000
840@@ -0,0 +1,251 @@
841+/*
842+ * Copyright 2012 Canonical Ltd.
843+ *
844+ * This program is free software; you can redistribute it and/or modify
845+ * it under the terms of the GNU Lesser General Public License as published by
846+ * the Free Software Foundation; version 3.
847+ *
848+ * This program is distributed in the hope that it will be useful,
849+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
850+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
851+ * GNU Lesser General Public License for more details.
852+ *
853+ * You should have received a copy of the GNU Lesser General Public License
854+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
855+ */
856+
857+import QtQuick 2.0
858+import QtTest 1.0
859+import Ubuntu.Components 0.1
860+
861+Item {
862+ width: 200
863+ height: 200
864+ id: root
865+
866+ Panel {
867+ id: panel
868+ anchors {
869+ bottom: parent.bottom
870+ left: parent.left
871+ right: parent.right
872+ }
873+ height: parent.height / 2
874+ }
875+
876+ TestCase {
877+ id: testCase
878+ name: "PanelAPI"
879+ when: windowShown
880+
881+ function initTestCase() {
882+ compare(panel.align, Qt.AlignBottom, "Panel initially aligned at bottom");
883+ compare(panel.opened, false, "Panel initially not opened");
884+ compare(panel.locked, false, "Panel initially not locked");
885+ compare(panel.hintSize, units.gu(2), "Default hintSize is 2 grid units");
886+ compare(panel.triggerSize, units.gu(2), "Default triggerSize is 2 grid units");
887+ }
888+
889+ function test_align() {
890+ panel.align = Qt.AlignTop;
891+ compare(panel.align, Qt.AlignTop, "Can set align to top");
892+ panel.align = Qt.AlignLeft;
893+ compare(panel.align, Qt.AlignLeft, "Can set align to left");
894+ panel.align = Qt.AlignRight;
895+ compare(panel.align, Qt.AlignRight, "Can set align to right");
896+ panel.align = Qt.AlignBottom;
897+ compare(panel.align, Qt.AlignBottom, "Can set align to bottom");
898+ }
899+
900+ function test_opened() {
901+ panel.opened = true;
902+ compare(panel.opened, true, "Can set opened");
903+ panel.opened = false;
904+ compare(panel.opened, false, "Can unset opened");
905+ }
906+
907+ function test_locked() {
908+ panel.locked = true;
909+ compare(panel.locked, true, "Can set locked");
910+ panel.locked = false;
911+ compare(panel.locked, false, "Can unset locked");
912+ }
913+
914+ function test_hintSize() {
915+ panel.hintSize = units.gu(1);
916+ compare(panel.hintSize, units.gu(1), "Can set hintSize");
917+ panel.hintSize = units.gu(2);
918+ }
919+
920+ function test_triggerSize() {
921+ panel.triggerSize = units.gu(5);
922+ compare(panel.triggerSize, units.gu(5), "Can set triggerSize");
923+ panel.triggerSize = units.gu(2);
924+ }
925+
926+ function test_swipeBottomPanel() {
927+ // swipe bottom-aligned panel in and out
928+ swipeTests.slowMouseMove = false;
929+ swipeTests.swipeUpDown();
930+ swipeTests.slowMouseMove = true;
931+ swipeTests.swipeUpDown();
932+ swipeTests.slowMouseMove = false;
933+ }
934+
935+ function test_swipeLeftPanel() {
936+ // swipe a left-aligned panel in and out
937+ panel.align = Qt.AlignLeft;
938+ swipeTests.slowMouseMove = false;
939+ swipeTests.swipeRightLeft();
940+ swipeTests.slowMouseMove = true;
941+ swipeTests.swipeRightLeft();
942+ swipeTests.slowMouseMove = false;
943+ panel.align = Qt.AlignBottom;
944+ }
945+
946+ function test_swipeRightPanel() {
947+ // swipe right-aligned panel in and out
948+ panel.align = Qt.AlignRight;
949+ swipeTests.slowMouseMove = false;
950+ swipeTests.swipeLeftRight();
951+ swipeTests.slowMouseMove = true;
952+ swipeTests.swipeLeftRight();
953+ swipeTests.slowMouseMove = false;
954+ panel.align = Qt.AlignBottom;
955+ }
956+
957+ function test_swipeLeadingPanel() {
958+ // swipe leading-aligned panel in and out
959+ panel.align = Qt.AlignLeading;
960+ swipeTests.swipeRightLeft();
961+ panel.LayoutMirroring.enabled = true;
962+ panel.LayoutMirroring.childrenInherit = true;
963+ swipeTests.slowMouseMove = false;
964+ swipeTests.swipeLeftRight();
965+ swipeTests.slowMouseMove = true;
966+ swipeTests.swipeLeftRight();
967+ swipeTests.slowMouseMove = false;
968+ panel.LayoutMirroring.enabled = false;
969+ panel.align = Qt.AlignBottom;
970+ }
971+
972+ function test_swipeTrailingPanel() {
973+ // swipe trailing-aligned panel in and out
974+ panel.align = Qt.AlignTrailing;
975+ swipeTests.swipeLeftRight();
976+ panel.LayoutMirroring.enabled = true;
977+ panel.LayoutMirroring.childrenInherit = true;
978+ swipeTests.slowMouseMove = false;
979+ swipeTests.swipeRightLeft();
980+ swipeTests.slowMouseMove = true;
981+ swipeTests.swipeRightLeft();
982+ swipeTests.slowMouseMove = false;
983+ panel.LayoutMirroring.enabled = false;
984+ panel.align = Qt.AlignBottom;
985+ }
986+
987+ function test_swipeTopPanel() {
988+ // swipe top-aligned panel in and out
989+ panel.anchors.top = root.top;
990+ panel.anchors.bottom = undefined;
991+ panel.align = Qt.AlignTop;
992+
993+ swipeTests.slowMouseMove = false;
994+ swipeTests.swipeDownUp();
995+ swipeTests.slowMouseMove = true;
996+ swipeTests.swipeDownUp();
997+ swipeTests.slowMouseMove = false;
998+
999+ // revert to original state
1000+ panel.anchors.bottom = root.bottom;
1001+ panel.anchors.top = undefined;
1002+ panel.align = Qt.AlignBottom;
1003+ }
1004+
1005+ QtObject {
1006+ id: swipeTests
1007+
1008+ // waiting time in ms after move, to simulate slow drag vs fast swipe
1009+ property int moveDelay: slowMouseMove ? 400 : 0
1010+ property bool slowMouseMove: false
1011+
1012+ function swipeUpDown() {
1013+ testCase.compare(panel.opened, false, "Panel initially not opened")
1014+ var x = root.width / 2; var y = root.height - 1;
1015+ var dx = 0;
1016+ var dy = -panel.height / 2;
1017+ testCase.mousePress(root, x, y, Qt.LeftButton);
1018+ testCase.mouseMove(root, x + dx, y + dy, moveDelay);
1019+ testCase.mouseRelease(root, x + dx, y + dy, Qt.LeftButton);
1020+ testCase.compare(panel.opened, true, "Panel activated by swiping up (delay: "+moveDelay+")")
1021+ x = panel.width / 2;
1022+ y = 10;
1023+ testCase.mousePress(panel, x, y, Qt.LeftButton);
1024+ testCase.mouseMove(panel, x - dx, y - dy, moveDelay);
1025+ testCase.mouseRelease(panel, x - dx, y - dy, Qt.LeftButton);
1026+ testCase.compare(panel.opened, false, "Panel deactivated by swiping down (delay: "+moveDelay+")")
1027+ }
1028+
1029+ function swipeRightLeft() {
1030+ testCase.compare(panel.opened, false, "Panel initially not opened")
1031+ var x = 1;
1032+ var y = 3 * root.height / 4;
1033+ var dx = panel.width / 2;
1034+ var dy = 0;
1035+ testCase.mousePress(root, x, y, Qt.LeftButton);
1036+ testCase.mouseMove(root, x + dx, y + dy, moveDelay);
1037+ testCase.mouseRelease(root, x + dx, y + dy, Qt.Leftbutton);
1038+ testCase.compare(panel.opened, true, "Left-aligned panel activated by swiping to the right (delay: "+moveDelay+")");
1039+ x = 3 * panel.width / 4;
1040+ y = panel.height / 2;
1041+ testCase.mousePress(panel, x, y, Qt.LeftButton);
1042+ testCase.mouseMove(root, x - dx, y - dy, moveDelay);
1043+ testCase.mouseRelease(panel, x - dx, y - dy, Qt.LeftButton);
1044+ testCase.compare(panel.opened, false, "Left-aligned panel deactivated by swiping to the left (delay: "+moveDelay+"");
1045+ }
1046+
1047+ function swipeLeftRight() {
1048+ testCase.compare(panel.opened, false, "Panel initially not opened")
1049+ var x = root.width - 1;
1050+ var y = 3 * root.height / 4;
1051+ var dx = -panel.width / 2;
1052+ var dy = 0;
1053+ testCase.mousePress(root, x, y, Qt.LeftButton);
1054+ testCase.mouseMove(root, x + dx, y + dy, moveDelay);
1055+ testCase.mouseRelease(root, x + dx, y + dy, Qt.Leftbutton);
1056+ testCase.compare(panel.opened, true, "Right-aligned panel activated by swiping to the left (delay: "+moveDelay+"");
1057+ x = 1;
1058+ y = panel.height / 2;
1059+ testCase.mousePress(panel, x, y, Qt.LeftButton);
1060+ testCase.mouseMove(panel, -dx, -dy, moveDelay);
1061+ testCase.mouseRelease(panel, x - dx, y - dy, Qt.LeftButton);
1062+ testCase.compare(panel.opened, false, "Right-aligned panel deactivating by swiping to the right (delay: "+moveDelay+"");
1063+ }
1064+
1065+ function swipeDownUp() {
1066+ testCase.compare(panel.opened, false, "Panel initially not opened")
1067+ var x = root.width / 2;
1068+ var y = 1;
1069+ var dx = 0;
1070+ var dy = panel.height / 2;
1071+ testCase.mousePress(root, x, y, Qt.LeftButton);
1072+ testCase.mouseMove(root, x + dx, y + dy, moveDelay);
1073+ testCase.mouseRelease(root, x + dx, y + dy, Qt.LeftButton);
1074+ testCase.compare(panel.opened, true, "Top-aligned panel activated by swiping down (delay: "+moveDelay+"");
1075+ x = panel.width / 2;
1076+ y = panel.height - 1;
1077+ testCase.mousePress(panel, x, y, Qt.LeftButton);
1078+ testCase.mouseMove(panel, x - dx, y - dy, moveDelay);
1079+ testCase.mouseRelease(panel, x - dx, y - dy, Qt.LeftButton);
1080+ testCase.compare(panel.opened, false, "Top-aligned panel deactivated by swiping up (delay: "+moveDelay+"");
1081+ }
1082+
1083+ function test_clickToDeactivate() {
1084+ panel.opened = true;
1085+ compare(panel.opened && panel.align === Qt.AlignBottom, true, "Panel is opened and bottom-aligned");
1086+ mouseClick(root, root.width / 2, 5, Qt.LeftButton);
1087+ compare(panel.opened, false, "Panel is deactivated by clicking in the view outside of the panel");
1088+ }
1089+ }
1090+ }
1091+}
1092
1093=== added file 'tests/unit/tst_components/tst_toolbar.qml'
1094--- tests/unit/tst_components/tst_toolbar.qml 1970-01-01 00:00:00 +0000
1095+++ tests/unit/tst_components/tst_toolbar.qml 2013-04-30 17:34:37 +0000
1096@@ -0,0 +1,74 @@
1097+/*
1098+ * Copyright 2012 Canonical Ltd.
1099+ *
1100+ * This program is free software; you can redistribute it and/or modify
1101+ * it under the terms of the GNU Lesser General Public License as published by
1102+ * the Free Software Foundation; version 3.
1103+ *
1104+ * This program is distributed in the hope that it will be useful,
1105+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1106+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1107+ * GNU Lesser General Public License for more details.
1108+ *
1109+ * You should have received a copy of the GNU Lesser General Public License
1110+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1111+ */
1112+
1113+import QtQuick 2.0
1114+import QtTest 1.0
1115+import Ubuntu.Components 0.1
1116+
1117+Item {
1118+ width: 200
1119+ height: 200
1120+
1121+ MainView {
1122+ anchors.fill: parent
1123+ id: mainView
1124+ Page {
1125+ id: page
1126+ tools: ToolbarActions {
1127+ id: toolbarActions
1128+ Action {
1129+ text: "action1"
1130+ }
1131+ }
1132+ }
1133+ }
1134+
1135+ TestCase {
1136+ name: "ToolbarAPI"
1137+ when: windowShown
1138+
1139+ function initTestCase() {
1140+ compare(page.tools, toolbarActions, "Page tools are set initially");
1141+ compare(mainView.toolbar.tools, page.tools, "Toolbar tools are set to page tools initially");
1142+ compare(mainView.toolbar.tools.opened, false, "Toolbar is closed initially");
1143+ compare(mainView.toolbar.tools.locked, false, "Toolbar is initially not locked");
1144+ }
1145+
1146+ function test_opened() {
1147+ compare(mainView.toolbar.tools.opened, false, "Toolbar initially closed");
1148+ mainView.toolbar.opened = true;
1149+ compare(mainView.toolbar.opened, true, "Toolbar can be made opened");
1150+ mainView.toolbar.opened = false;
1151+ compare(mainView.toolbar.opened, false, "Toolbar can be made closed");
1152+ page.tools.opened = true;
1153+ compare(mainView.toolbar.opened, true, "Toolbar can be made opened by setting page.tools.opened");
1154+ page.tools.opened = false;
1155+ compare(mainView.toolbar.opened, false, "Toolbar can be made closed by setting page.tools.opened to false");
1156+ }
1157+
1158+ function test_locked() {
1159+ compare(mainView.toolbar.tools.locked, false, "Toolbar initially not locked");
1160+ mainView.toolbar.locked = true;
1161+ compare(mainView.toolbar.locked, true, "Toolbar can be locked");
1162+ mainView.toolbar.locked = false;
1163+ compare(mainView.toolbar.locked, false, "Toolbar can be unlocked");
1164+ page.tools.locked = true;
1165+ compare(mainView.toolbar.locked, true, "Toolbar can be locked by setting page.tools.locked");
1166+ page.tools.locked = false;
1167+ compare(mainView.toolbar.locked, false, "Toolbar can be unlocked by setting page.tools.locked to false");
1168+ }
1169+ }
1170+}
1171
1172=== modified file 'themes/Ambiance/qmltheme/ToolbarButtonDelegate.qml'
1173--- themes/Ambiance/qmltheme/ToolbarButtonDelegate.qml 2013-04-26 20:31:16 +0000
1174+++ themes/Ambiance/qmltheme/ToolbarButtonDelegate.qml 2013-04-30 17:34:37 +0000
1175@@ -25,9 +25,6 @@
1176
1177 anchors.fill: parent
1178
1179- implicitWidth: units.gu(5)
1180- implicitHeight: units.gu(5)
1181-
1182 Item {
1183 anchors.centerIn: parent
1184 height: icon.height + label.height + label.anchors.topMargin
1185
1186=== modified file 'themes/Ambiance/qmltheme/ToolbarDelegate.qml'
1187--- themes/Ambiance/qmltheme/ToolbarDelegate.qml 2013-04-26 20:31:16 +0000
1188+++ themes/Ambiance/qmltheme/ToolbarDelegate.qml 2013-04-30 17:34:37 +0000
1189@@ -23,17 +23,13 @@
1190 property real barOpacity
1191
1192 anchors.fill: parent
1193+ property alias contentItem: background
1194
1195 Rectangle {
1196 id: background
1197- anchors {
1198- left: parent.left
1199- right: parent.right
1200- bottom: parent.bottom
1201- }
1202- height: parent.height //- dropshadow.height
1203+ anchors.fill: parent
1204 color: visuals.color
1205- opacity: barOpacity
1206+ opacity: visuals.barOpacity
1207 }
1208
1209 Image {
1210@@ -44,7 +40,7 @@
1211 bottom: background.top
1212 }
1213 source: Qt.resolvedUrl("artwork/toolbar_dropshadow.png")
1214- opacity: item.state == "" ? 0.0 : 0.5
1215+ opacity: item.active || item.animating ? 0.5 : 0.0
1216 Behavior on opacity {
1217 NumberAnimation { duration: 50; easing.type: Easing.OutQuad }
1218 }

Subscribers

People subscribed via source and target branches

to status/vote changes: