Merge lp:~zsombi/ubuntu-ui-toolkit/increasedSensingArea into lp:ubuntu-ui-toolkit/staging
- increasedSensingArea
- Merge into staging
Status: | Merged |
---|---|
Approved by: | Tim Peeters |
Approved revision: | 1731 |
Merged at revision: | 1866 |
Proposed branch: | lp:~zsombi/ubuntu-ui-toolkit/increasedSensingArea |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Diff against target: |
565 lines (+375/-0) 8 files modified
components.api (+7/-0) src/Ubuntu/Components/plugin/plugin.cpp (+1/-0) src/Ubuntu/Components/plugin/plugin.pri (+1/-0) src/Ubuntu/Components/plugin/ucabstractbutton.cpp (+135/-0) src/Ubuntu/Components/plugin/ucabstractbutton.h (+9/-0) src/Ubuntu/Components/plugin/ucabstractbutton_p.h (+2/-0) src/Ubuntu/Components/plugin/ucmargins.h (+76/-0) tests/unit_x11/tst_components/tst_abstractbutton13.qml (+144/-0) |
To merge this branch: | bzr merge lp:~zsombi/ubuntu-ui-toolkit/increasedSensingArea |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ubuntu-sdk-build-bot | continuous-integration | Approve | |
Tim Peeters | Approve | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+277450@code.launchpad.net |
Commit message
AbstractButton handles 4x4GU minimal sensing area, exposes sensingMargins to extend sensing area even further.
Description of the change
Andrea Bernabei (faenil) wrote : | # |
Zsombor Egri (zsombi) wrote : | # |
> left some comments :)
See my replies inline.
Zsombor Egri (zsombi) wrote : | # |
> > left some comments :)
>
> See my replies inline.
Ehm... seems the comments got vanished :/
Anyways, the comments were applied, see below, except the 8mm not being 4GU, we measured it, it is approximately 4GU everywhere. Remember, the GU is chosen to show the same size everywhere. And N10 is NOT (and I really mean it!!!!) 90 GU for sure! N7 is, but N10 is NOT. Please!!! It's like Bq and Meizu having the same GU width/height!!! Forget that, it's bullshit!!!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1725
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1726
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1727
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Tim Peeters (tpeeters) wrote : | # |
function cleanup() {
417 + buttonWithSensi
418 + buttonWithSensi
419 + buttonWithSensi
420 + buttonWithSensi
421 + buttonWithSensi
422 + buttonWithSensi
423 + buttonWithSensi
424 + signalSpy.target = absButton;
I suggest to use some indentation here to make it not misleading for a human reader.
Tim Peeters (tpeeters) wrote : | # |
^or add 0; to each line.
Tim Peeters (tpeeters) wrote : | # |
There is no unit test for:
1. Negative sensing margins
2. Initializing a component with sensor margins (without setting them in JS afterwards)
Zsombor Egri (zsombi) wrote : | # |
> There is no unit test for:
> 1. Negative sensing margins
This makes sense. Will add a test case for that!
> 2. Initializing a component with sensor margins (without setting them in JS
> afterwards)
What is the difference? They both call the setters...
- 1728. By Zsombor Egri
-
more tests added
- 1729. By Zsombor Egri
-
getting branch head in sync
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1729
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1729
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 1730. By Zsombor Egri
-
roll back a floating change
- 1731. By Zsombor Egri
-
staging sync
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Tim Peeters (tpeeters) wrote : | # |
Looks good, thanks.
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1731
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'components.api' |
2 | --- components.api 2016-02-17 12:57:49 +0000 |
3 | +++ components.api 2016-02-24 06:31:56 +0000 |
4 | @@ -8,6 +8,7 @@ |
5 | signal clicked() |
6 | signal pressAndHold() |
7 | readonly property bool pressed |
8 | + readonly property UCMargins sensingMargins |
9 | Ubuntu.Components.Action 1.3 1.0 0.1 UCAction: QtObject |
10 | property string description |
11 | property bool enabled |
12 | @@ -1557,6 +1558,12 @@ |
13 | UCListItemExpansion: QtObject |
14 | property bool expanded |
15 | property double height |
16 | +UCMargins: QtObject |
17 | + property double all |
18 | + property double bottom |
19 | + property double left |
20 | + property double right |
21 | + property double top |
22 | UCStateSaverAttached: QtObject |
23 | property bool enabled |
24 | property string properties |
25 | |
26 | === modified file 'src/Ubuntu/Components/plugin/plugin.cpp' |
27 | --- src/Ubuntu/Components/plugin/plugin.cpp 2016-02-12 13:46:51 +0000 |
28 | +++ src/Ubuntu/Components/plugin/plugin.cpp 2016-02-24 06:31:56 +0000 |
29 | @@ -222,6 +222,7 @@ |
30 | qmlRegisterType<UCProportionalShape>(uri, 1, 3, "ProportionalShape"); |
31 | qmlRegisterType<LiveTimer>(uri, 1, 3, "LiveTimer"); |
32 | qmlRegisterType<UCAbstractButton>(uri, 1, 3, "AbstractButton"); |
33 | + qmlRegisterType<UCMargins>(); |
34 | qmlRegisterUncreatableType<UCSlotsAttached>(uri, 1, 3, "SlotsAttached", "Not instantiable"); |
35 | qmlRegisterUncreatableType<UCSlotsLayoutPadding>(uri, 1, 3, "SlotsLayoutPadding", "Not instantiable"); |
36 | qmlRegisterType<UCListItemLayout>(uri, 1, 3, "ListItemLayout"); |
37 | |
38 | === modified file 'src/Ubuntu/Components/plugin/plugin.pri' |
39 | --- src/Ubuntu/Components/plugin/plugin.pri 2016-02-08 12:47:32 +0000 |
40 | +++ src/Ubuntu/Components/plugin/plugin.pri 2016-02-24 06:31:56 +0000 |
41 | @@ -90,6 +90,7 @@ |
42 | $$PWD/uchaptics.h \ |
43 | $$PWD/ucabstractbutton.h \ |
44 | $$PWD/ucabstractbutton_p.h \ |
45 | + $$PWD/ucmargins.h \ |
46 | $$PWD/ucthemingextension.h \ |
47 | $$PWD/ucheader.h \ |
48 | $$PWD/uclabel.h \ |
49 | |
50 | === modified file 'src/Ubuntu/Components/plugin/ucabstractbutton.cpp' |
51 | --- src/Ubuntu/Components/plugin/ucabstractbutton.cpp 2016-02-11 08:54:26 +0000 |
52 | +++ src/Ubuntu/Components/plugin/ucabstractbutton.cpp 2016-02-24 06:31:56 +0000 |
53 | @@ -17,11 +17,15 @@ |
54 | #include "ucabstractbutton.h" |
55 | #include "ucabstractbutton_p.h" |
56 | #include "uchaptics.h" |
57 | +#include "ucunits.h" |
58 | #include "ucaction.h" |
59 | #include <QtQuick/private/qquickitem_p.h> |
60 | #include <QtQuick/private/qquickmousearea_p.h> |
61 | #include <QtQml/private/qqmlglobal_p.h> |
62 | |
63 | +#define MIN_SENSING_WIDTH_GU 4 |
64 | +#define MIN_SENSING_HEIGHT_GU 4 |
65 | + |
66 | UCAbstractButtonPrivate::UCAbstractButtonPrivate() |
67 | : UCActionItemPrivate() |
68 | , mouseArea(new QQuickMouseArea) |
69 | @@ -39,6 +43,7 @@ |
70 | /*! |
71 | \qmltype AbstractButton |
72 | \instantiates UCAbstractButton |
73 | + \inherits ActionItem |
74 | \inqmlmodule Ubuntu.Components 1.1 |
75 | \ingroup ubuntu |
76 | \brief The AbstractButton class defines the behavior of the button. |
77 | @@ -49,6 +54,34 @@ |
78 | If an action is specified, the button's clicked signal will trigger the action. |
79 | Subclasses of AbstractButton can use other properties of action (for example |
80 | the text and iconName). |
81 | + |
82 | + \section2 Sensing area |
83 | + It has been proven that, on touch devices in order to properly aim an active |
84 | + component a minimum of 8x8 millimeters (i.e. 4x4 grid units) area has to be |
85 | + provided. However not all the visuals are of that size, as Icons for example |
86 | + are defaulted to be 2x2 grid units, but a component containing a single Icon |
87 | + still has to be able to capture the press events. Therefore AbstractButton |
88 | + makes sure this rule of 4x4 grid units for the sensing area is provided. In |
89 | + addition it exposes the \l sensingMargins property which extends the component's |
90 | + sensing area in all the directions, so other use cases when the sensing area |
91 | + needs to be extended outside of the component's area, or restricted on a |
92 | + given area of the component can be implemented. The following example extends |
93 | + the sensing area on the left, top and bottom with 1 grid units, and on the |
94 | + right with 10 grid units. |
95 | + \qml |
96 | + AbstractButton { |
97 | + width: units.gu(2) |
98 | + height: units.gu(2) |
99 | + sensingMargins { |
100 | + left: units.gu(1) |
101 | + top: units.gu(1) |
102 | + bottom: units.gu(1) |
103 | + right: units.gu(10) |
104 | + } |
105 | + } |
106 | + \endqml |
107 | + \note Do not set clipping for the component as that will restrict the sensing |
108 | + area to be available on the visual area only. |
109 | */ |
110 | |
111 | /*! |
112 | @@ -111,6 +144,9 @@ |
113 | UCActionItemPrivate::completeComponentInitialization(); |
114 | Q_Q(UCAbstractButton); |
115 | |
116 | + // adjust sensing area |
117 | + _q_adjustSensingArea(); |
118 | + |
119 | // bind mouse area |
120 | QObject::connect(mouseArea, &QQuickMouseArea::pressedChanged, q, &UCAbstractButton::pressedChanged); |
121 | QObject::connect(mouseArea, &QQuickMouseArea::hoveredChanged, q, &UCAbstractButton::hoveredChanged); |
122 | @@ -122,6 +158,9 @@ |
123 | // may not be available on compoennt completion |
124 | void UCAbstractButtonPrivate::_q_mouseAreaPressed() |
125 | { |
126 | + if (!mouseArea->pressed()) { |
127 | + return; |
128 | + } |
129 | bool longPressConnected = isPressAndHoldConnected(); |
130 | Q_Q(UCAbstractButton); |
131 | if (longPressConnected && !pressAndHoldConnected) { |
132 | @@ -175,6 +214,48 @@ |
133 | } |
134 | } |
135 | |
136 | +void UCAbstractButtonPrivate::_q_adjustSensingArea() |
137 | +{ |
138 | + Q_Q(UCAbstractButton); |
139 | + if (!componentComplete) { |
140 | + // we do not hammer the component until completion |
141 | + return; |
142 | + } |
143 | + // use the sensingMargins in the minimum calculation |
144 | + qreal minimumWidth = UCUnits::instance()->gu(MIN_SENSING_WIDTH_GU); |
145 | + qreal minimumHeight = UCUnits::instance()->gu(MIN_SENSING_HEIGHT_GU); |
146 | + qreal hDelta = minimumWidth |
147 | + - (q->width() + (sensingMargins ? (sensingMargins->left() + sensingMargins->right()) : 0.0)); |
148 | + qreal vDelta = minimumHeight |
149 | + - (q->height() + (sensingMargins ? (sensingMargins->top() + sensingMargins->bottom()) : 0.0)); |
150 | + // adjust the sensing area |
151 | + QQuickAnchors *mouseAreaAnchors = QQuickItemPrivate::get(mouseArea)->anchors(); |
152 | + if (hDelta >= 0) { |
153 | + // the horizontal size is still smaller than the minimum |
154 | + mouseAreaAnchors->setLeftMargin(-(hDelta / 2 + (sensingMargins ? sensingMargins->left() : 0.0))); |
155 | + mouseAreaAnchors->setRightMargin(-(hDelta / 2 + (sensingMargins ? sensingMargins->right() : 0.0))); |
156 | + } else if (sensingMargins) { |
157 | + mouseAreaAnchors->setLeftMargin(-sensingMargins->left()); |
158 | + mouseAreaAnchors->setRightMargin(-sensingMargins->right()); |
159 | + } |
160 | + if (vDelta >= 0) { |
161 | + // the vertical size is still smaller than the minimum |
162 | + mouseAreaAnchors->setTopMargin(-(vDelta / 2 + (sensingMargins ? sensingMargins->top() : 0.0))); |
163 | + mouseAreaAnchors->setBottomMargin(-(vDelta / 2 + (sensingMargins ? sensingMargins->bottom() : 0.0))); |
164 | + } else if (sensingMargins) { |
165 | + mouseAreaAnchors->setTopMargin(-sensingMargins->top()); |
166 | + mouseAreaAnchors->setBottomMargin(-sensingMargins->bottom()); |
167 | + } |
168 | +} |
169 | + |
170 | +void UCAbstractButton::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
171 | +{ |
172 | + UCActionItem::geometryChanged(newGeometry, oldGeometry); |
173 | + |
174 | + // adjust internal mouse area's size |
175 | + d_func()->_q_adjustSensingArea(); |
176 | +} |
177 | + |
178 | /*! |
179 | * \qmlproperty bool AbstractButton::pressed |
180 | * True if the user presses a mouse button in the button's mouse area. |
181 | @@ -212,4 +293,58 @@ |
182 | return d->mouseArea; |
183 | } |
184 | |
185 | +/*! |
186 | + * \qmlpropertygroup ::AbstractButton::sensingMargins |
187 | + * \qmlproperty real AbstractButton::sensingMargins.left |
188 | + * \qmlproperty real AbstractButton::sensingMargins.right |
189 | + * \qmlproperty real AbstractButton::sensingMargins.top |
190 | + * \qmlproperty real AbstractButton::sensingMargins.bottom |
191 | + * \qmlproperty real AbstractButton::sensingMargins.all |
192 | + * The property group specifies the margins extending the visual area where the |
193 | + * touch and mouse events are sensed. Positive values mean the area will be extended |
194 | + * on the specified direction outside of the visual area, negative values mean |
195 | + * the sensing will fall under the component's visual border. |
196 | + * All values default to 0. |
197 | + * |
198 | + * \note If the visual area and the sensing margins are not reaching the 4x4 grid |
199 | + * units limit, the component will fall back to these minimum limits. |
200 | + * For example, extending a 2x2 grid unit visual component into 5x4 grid units |
201 | + * sensing area would look as follows: |
202 | + * \qml |
203 | + * AbstractButton { |
204 | + * width: units.gu(2) |
205 | + * height: units.gu(2) |
206 | + * Icon { |
207 | + * name: "settings" |
208 | + * } |
209 | + * sensingArea { |
210 | + * // no need to set the vertical direction as the minimum of |
211 | + * // 4 grid units will be taken automatically |
212 | + * leftMargin: units.gu(1) |
213 | + * // we only have to add 2 grid units as the width + left margin |
214 | + * // already gives us 3 grid units out of 5 |
215 | + * rightMargin: units.gu(2) |
216 | + * } |
217 | + * } |
218 | + * \endqml |
219 | + */ |
220 | +UCMargins *UCAbstractButton::sensingMargins() |
221 | +{ |
222 | + Q_D(UCAbstractButton); |
223 | + if (!d->sensingMargins) { |
224 | + d->sensingMargins = new UCMargins(this); |
225 | + |
226 | + // as this is the first time we create the sensing margins we only |
227 | + // connect now to grid unit changes to keep sensing area size in sync |
228 | + connect(UCUnits::instance(), SIGNAL(gridUnitChanged()), this, SLOT(_q_adjustSensingArea())); |
229 | + // also connect to the margin changes |
230 | + connect(d->sensingMargins, SIGNAL(leftChanged()), this, SLOT(_q_adjustSensingArea())); |
231 | + connect(d->sensingMargins, SIGNAL(rightChanged()), this, SLOT(_q_adjustSensingArea())); |
232 | + connect(d->sensingMargins, SIGNAL(topChanged()), this, SLOT(_q_adjustSensingArea())); |
233 | + connect(d->sensingMargins, SIGNAL(bottomChanged()), this, SLOT(_q_adjustSensingArea())); |
234 | + connect(d->sensingMargins, SIGNAL(allChanged()), this, SLOT(_q_adjustSensingArea())); |
235 | + } |
236 | + return d->sensingMargins; |
237 | +} |
238 | + |
239 | #include "moc_ucabstractbutton.cpp" |
240 | |
241 | === modified file 'src/Ubuntu/Components/plugin/ucabstractbutton.h' |
242 | --- src/Ubuntu/Components/plugin/ucabstractbutton.h 2015-12-17 13:00:10 +0000 |
243 | +++ src/Ubuntu/Components/plugin/ucabstractbutton.h 2016-02-24 06:31:56 +0000 |
244 | @@ -18,6 +18,8 @@ |
245 | #define UCABSTRACTBUTTON_H |
246 | |
247 | #include "ucactionitem.h" |
248 | +#include "ucmargins.h" |
249 | +#include <QtQuick/private/qquickevents_p_p.h> |
250 | |
251 | class QQuickMouseArea; |
252 | class QQuickMouseEvent; |
253 | @@ -27,6 +29,7 @@ |
254 | Q_OBJECT |
255 | Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged) |
256 | Q_PROPERTY(bool hovered READ hovered NOTIFY hoveredChanged) |
257 | + Q_PROPERTY(UCMargins *sensingMargins READ sensingMargins CONSTANT FINAL) |
258 | |
259 | // internal, declared to support the deprecated ListItem module |
260 | Q_PROPERTY(bool __acceptEvents READ acceptEvents WRITE setAcceptEvents) |
261 | @@ -36,6 +39,7 @@ |
262 | |
263 | bool pressed() const; |
264 | bool hovered() const; |
265 | + UCMargins *sensingMargins(); |
266 | |
267 | bool privateAcceptEvents() const; |
268 | void setPrivateAcceptEvents(bool accept); |
269 | @@ -45,6 +49,8 @@ |
270 | |
271 | protected: |
272 | void classBegin(); |
273 | + virtual void geometryChanged(const QRectF &newGeometry, |
274 | + const QRectF &oldGeometry); |
275 | void keyReleaseEvent(QKeyEvent *key); |
276 | |
277 | Q_SIGNALS: |
278 | @@ -60,6 +66,9 @@ |
279 | Q_PRIVATE_SLOT(d_func(), void _q_mouseAreaPressed()) |
280 | Q_PRIVATE_SLOT(d_func(), void _q_mouseAreaClicked()) |
281 | Q_PRIVATE_SLOT(d_func(), void _q_mouseAreaPressAndHold()) |
282 | + Q_PRIVATE_SLOT(d_func(), void _q_adjustSensingArea()) |
283 | }; |
284 | |
285 | +QML_DECLARE_TYPE(UCMargins) |
286 | + |
287 | #endif // UCABSTRACTBUTTON_H |
288 | |
289 | === modified file 'src/Ubuntu/Components/plugin/ucabstractbutton_p.h' |
290 | --- src/Ubuntu/Components/plugin/ucabstractbutton_p.h 2015-12-17 13:00:10 +0000 |
291 | +++ src/Ubuntu/Components/plugin/ucabstractbutton_p.h 2016-02-24 06:31:56 +0000 |
292 | @@ -41,8 +41,10 @@ |
293 | void _q_mouseAreaPressed(); |
294 | void _q_mouseAreaClicked(); |
295 | void _q_mouseAreaPressAndHold(); |
296 | + void _q_adjustSensingArea(); |
297 | |
298 | QQuickMouseArea *mouseArea; |
299 | + UCMargins *sensingMargins = nullptr; |
300 | bool acceptEvents:1; |
301 | bool pressAndHoldConnected:1; |
302 | }; |
303 | |
304 | === added file 'src/Ubuntu/Components/plugin/ucmargins.h' |
305 | --- src/Ubuntu/Components/plugin/ucmargins.h 1970-01-01 00:00:00 +0000 |
306 | +++ src/Ubuntu/Components/plugin/ucmargins.h 2016-02-24 06:31:56 +0000 |
307 | @@ -0,0 +1,76 @@ |
308 | +/* |
309 | + * Copyright 2015 Canonical Ltd. |
310 | + * |
311 | + * This program is free software; you can redistribute it and/or modify |
312 | + * it under the terms of the GNU Lesser General Public License as published by |
313 | + * the Free Software Foundation; version 3. |
314 | + * |
315 | + * This program is distributed in the hope that it will be useful, |
316 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
317 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
318 | + * GNU Lesser General Public License for more details. |
319 | + * |
320 | + * You should have received a copy of the GNU Lesser General Public License |
321 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
322 | + */ |
323 | + |
324 | +#ifndef UCMARGINS_H |
325 | +#define UCMARGINS_H |
326 | + |
327 | +#include <QtCore/QObject> |
328 | +#include <QtQuick/QQuickItem> |
329 | + |
330 | +class UCMargins : public QObject |
331 | +{ |
332 | + Q_OBJECT |
333 | + Q_PROPERTY(qreal left MEMBER m_left NOTIFY leftChanged FINAL) |
334 | + Q_PROPERTY(qreal top MEMBER m_top NOTIFY topChanged FINAL) |
335 | + Q_PROPERTY(qreal right MEMBER m_right NOTIFY rightChanged FINAL) |
336 | + Q_PROPERTY(qreal bottom MEMBER m_bottom NOTIFY bottomChanged FINAL) |
337 | + Q_PROPERTY(qreal all MEMBER m_all NOTIFY allChanged FINAL) |
338 | +public: |
339 | + UCMargins(QObject *parent = 0); |
340 | + |
341 | + qreal left() const; |
342 | + qreal top() const; |
343 | + qreal right() const; |
344 | + qreal bottom() const; |
345 | + |
346 | +Q_SIGNALS: |
347 | + void leftChanged(); |
348 | + void topChanged(); |
349 | + void rightChanged(); |
350 | + void bottomChanged(); |
351 | + void allChanged(); |
352 | + |
353 | +private: |
354 | + qreal m_left = 0.0; |
355 | + qreal m_top = 0.0; |
356 | + qreal m_right = 0.0; |
357 | + qreal m_bottom = 0.0; |
358 | + qreal m_all = 0.0; |
359 | +}; |
360 | + |
361 | +inline UCMargins::UCMargins(QObject *parent) : QObject(parent) {} |
362 | + |
363 | +inline qreal UCMargins::left() const |
364 | +{ |
365 | + return qFuzzyIsNull(m_left) ? m_all : m_left; |
366 | +} |
367 | + |
368 | +inline qreal UCMargins::top() const |
369 | +{ |
370 | + return qFuzzyIsNull(m_top) ? m_all : m_top; |
371 | +} |
372 | + |
373 | +inline qreal UCMargins::right() const |
374 | +{ |
375 | + return qFuzzyIsNull(m_right) ? m_all : m_right; |
376 | +} |
377 | + |
378 | +inline qreal UCMargins::bottom() const |
379 | +{ |
380 | + return qFuzzyIsNull(m_bottom) ? m_all : m_bottom; |
381 | +} |
382 | + |
383 | +#endif // UCMARGINS_H |
384 | |
385 | === modified file 'tests/unit_x11/tst_components/tst_abstractbutton13.qml' |
386 | --- tests/unit_x11/tst_components/tst_abstractbutton13.qml 2015-12-12 07:22:08 +0000 |
387 | +++ tests/unit_x11/tst_components/tst_abstractbutton13.qml 2016-02-24 06:31:56 +0000 |
388 | @@ -41,6 +41,41 @@ |
389 | height: width |
390 | function trigger() {} |
391 | } |
392 | + Item { |
393 | + // have enough space for the test subject |
394 | + width: units.gu(10) |
395 | + height: units.gu(10) |
396 | + AbstractButton { |
397 | + id: buttonWithSensing |
398 | + anchors.centerIn: parent |
399 | + } |
400 | + } |
401 | + Rectangle { |
402 | + color: "red" |
403 | + width: units.gu(10) |
404 | + height: units.gu(10) |
405 | + AbstractButton { |
406 | + id: increasedSensing |
407 | + anchors.centerIn: parent |
408 | + width: units.gu(4) |
409 | + height: units.gu(4) |
410 | + sensingMargins { |
411 | + left: units.gu(2) |
412 | + right: units.gu(2) |
413 | + top: units.gu(2) |
414 | + bottom: units.gu(2) |
415 | + } |
416 | + style: Item { |
417 | + anchors.fill: parent |
418 | + Rectangle { |
419 | + color: "blue" |
420 | + parent: styledItem.__mouseArea |
421 | + anchors.fill: parent |
422 | + } |
423 | + } |
424 | + } |
425 | + } |
426 | + |
427 | AbstractButton { |
428 | id: suppressTrigger2 |
429 | width: units.gu(10) |
430 | @@ -53,6 +88,7 @@ |
431 | height: width |
432 | function trigger(v) { triggered(v) } |
433 | } |
434 | + |
435 | Loader { |
436 | id: loader |
437 | width: units.gu(10) |
438 | @@ -103,6 +139,14 @@ |
439 | when: windowShown |
440 | |
441 | function cleanup() { |
442 | + buttonWithSensing.sensingMargins.left = 0; |
443 | + buttonWithSensing.sensingMargins.top = 0; |
444 | + buttonWithSensing.sensingMargins.right = 0; |
445 | + buttonWithSensing.sensingMargins.bottom = 0; |
446 | + buttonWithSensing.sensingMargins.all = 0; |
447 | + buttonWithSensing.width = 0; |
448 | + buttonWithSensing.height = 0; |
449 | + signalSpy.target = absButton; |
450 | signalSpy.clear(); |
451 | signalSpy.target = absButton; |
452 | triggeredSpy.clear(); |
453 | @@ -110,6 +154,14 @@ |
454 | loader.longPress = false; |
455 | } |
456 | |
457 | + function initTestCase() { |
458 | + compare(buttonWithSensing.sensingMargins.left, 0); |
459 | + compare(buttonWithSensing.sensingMargins.right, 0); |
460 | + compare(buttonWithSensing.sensingMargins.top, 0); |
461 | + compare(buttonWithSensing.sensingMargins.bottom, 0); |
462 | + compare(buttonWithSensing.sensingMargins.all, 0); |
463 | + } |
464 | + |
465 | function test_action() { |
466 | compare(absButton.action, null,"Action is null by default") |
467 | absButton.action = action1 |
468 | @@ -184,5 +236,97 @@ |
469 | mouseClick(loader.item, centerOf(loader.item).x, centerOf(loader.item).y); |
470 | compare(loader.click, true, "clicked not captured by Connection"); |
471 | } |
472 | + |
473 | + function test_sensing_area_data() { |
474 | + return [ |
475 | + // margins is [left, top, right, bottom] |
476 | + {tag: "zero size, no margins, click in visual", sizeGU: [0, 0], clickGU: [0, 0], sensingGU: [4, 4]}, |
477 | + {tag: "zero size, no margins, click in sensing", sizeGU: [0, 0], clickGU: [4, 4], sensingGU: [4, 4]}, |
478 | + {tag: "zero size, 1GU margins, click in visual", sizeGU: [0, 0], marginsGU: [1, 1, 1, 1], clickGU: [0, 0], sensingGU: [4, 4]}, |
479 | + {tag: "zero size, 1GU margins, click in sensing", sizeGU: [0, 0], marginsGU: [1, 1, 1, 1], clickGU: [4, 4], sensingGU: [4, 4]}, |
480 | + {tag: "zero size, 3GU margins horizontal, click in sensing", sizeGU: [0, 0], marginsGU: [3, 0, 3, 0], clickGU: [4, 4], sensingGU: [6, 4]}, |
481 | + {tag: "zero size, 3GU margins vertical, click in sensing", sizeGU: [0, 0], marginsGU: [0, 3, 0, 3], clickGU: [4, 4], sensingGU: [4, 6]}, |
482 | + {tag: "zero size, 3GU margins around, click in sensing", sizeGU: [0, 0], marginsGU: [3, 3, 3, 3], clickGU: [4, 4], sensingGU: [6, 6]}, |
483 | + |
484 | + {tag: "3x3GU size, no margins, click in visual", sizeGU: [3, 3], clickGU: [0, 0], sensingGU: [4, 4]}, |
485 | + {tag: "3x3GU size, no margins, click in sensing", sizeGU: [3, 3], clickGU: [4, 4], sensingGU: [4, 4]}, |
486 | + {tag: "3x3GU size, 1GU margins, click in visual", sizeGU: [3, 3], marginsGU: [1, 1, 1, 1], clickGU: [0, 0], sensingGU: [5, 5]}, |
487 | + {tag: "3x3GU size, 1GU margins, click in sensing", sizeGU: [3, 3], marginsGU: [1, 1, 1, 1], clickGU: [4, 4], sensingGU: [5, 5]}, |
488 | + {tag: "3x3GU size, 3GU margins horizontal, click in sensing", sizeGU: [3, 3], marginsGU: [3, 0, 3, 0], clickGU: [4, 4], sensingGU: [9, 4]}, |
489 | + {tag: "3x3GU size, 3GU margins vertical, click in sensing", sizeGU: [3, 3], marginsGU: [0, 3, 0, 3], clickGU: [4, 4], sensingGU: [4, 9]}, |
490 | + {tag: "3x3GU size, 3GU margins around, click in sensing", sizeGU: [3, 3], marginsGU: [3, 3, 3, 3], clickGU: [4, 4], sensingGU: [9, 9]}, |
491 | + |
492 | + {tag: "5x5GU size, no margins, click in visual", sizeGU: [5, 5], clickGU: [0, 0], sensingGU: [5, 5]}, |
493 | + {tag: "5x5GU size, no margins, click in sensing", sizeGU: [5, 5], clickGU: [4, 4], sensingGU: [5, 5]}, |
494 | + {tag: "5x5GU size, 1GU margins, click in visual", sizeGU: [5, 5], marginsGU: [1, 1, 1, 1], clickGU: [0, 0], sensingGU: [7, 7]}, |
495 | + {tag: "5x5GU size, 1GU margins, click in sensing", sizeGU: [5, 5], marginsGU: [1, 1, 1, 1], clickGU: [4, 4], sensingGU: [7, 7]}, |
496 | + {tag: "5x5GU size, 3GU margins horizontal, click in sensing", sizeGU: [5, 5], marginsGU: [3, 0, 3, 0], clickGU: [4, 4], sensingGU: [11, 5]}, |
497 | + {tag: "5x5GU size, 3GU margins vertical, click in sensing", sizeGU: [5, 5], marginsGU: [0, 3, 0, 3], clickGU: [4, 4], sensingGU: [5, 11]}, |
498 | + {tag: "5x5GU size, 3GU margins around, click in sensing", sizeGU: [5, 5], marginsGU: [3, 3, 3, 3], clickGU: [4, 4], sensingGU: [11, 11]}, |
499 | + |
500 | + {tag: "zero size, no margins, click out of sensing area", sizeGU: [0, 0], clickGU: [5, 5], sensingGU: [4, 4], fail: true}, |
501 | + {tag: "2x2GU size, no margins, click out of sensing area", sizeGU: [2, 2], clickGU: [5, 5], sensingGU: [4, 4], fail: true}, |
502 | + {tag: "4x4GU size, no margins, click out of sensing area", sizeGU: [4, 4], clickGU: [5, 5], sensingGU: [4, 4], fail: true}, |
503 | + {tag: "2x2GU size, 1GU margins around, click out of sensing area", sizeGU: [2, 2], marginsGU: [1, 1, 1, 1], clickGU: [5, 5], sensingGU: [4, 4], fail: true}, |
504 | + {tag: "4x4GU size, 1GU margins around, click out of sensing area", sizeGU: [4, 4], marginsGU: [1, 1, 1, 1], clickGU: [6.1, 6.1], sensingGU: [6, 6], fail: true}, |
505 | + |
506 | + // test margins.all |
507 | + {tag: "zero size, 5GU margins.all, click in sensing area", sizeGU: [0, 0], marginsAll: units.gu(5), clickGU: [5, 5], sensingGU: [10, 10]}, |
508 | + {tag: "2x2 size, 2GU margins.all, click in sensing area", sizeGU: [2, 2], marginsAll: units.gu(2), clickGU: [6, 6], sensingGU: [6, 6]}, |
509 | + {tag: "zero size, 5GU margins.all, click out of sensing area", sizeGU: [0, 0], marginsAll: units.gu(5), clickGU: [10.1, 10.1], sensingGU: [10, 10], fail: true}, |
510 | + {tag: "2x2 size, 2GU margins.all, click out of sensing area", sizeGU: [2, 2], marginsAll: units.gu(2), clickGU: [6.1, 6.1], sensingGU: [6, 6], fail: true}, |
511 | + |
512 | + // test negative margins |
513 | + {tag: "zero size, -1GU margins.all, click in sensing area", sizeGU: [0, 0], marginsAll: -units.gu(1), clickGU: [4, 4], sensingGU: [4, 4]}, |
514 | + {tag: "2x2 size, -1GU margins.all, click in sensing area", sizeGU: [2, 2], marginsAll: -units.gu(1), clickGU: [4, 4], sensingGU: [4, 4]}, |
515 | + {tag: "zero size, -1GU margins horizontal, click in sensing area", sizeGU: [0, 0], marginsGU: [-1, 0, -1, 0], clickGU: [4, 4], sensingGU: [4, 4]}, |
516 | + {tag: "zero size, -1GU margins vertical, click in sensing area", sizeGU: [0, 0], marginsGU: [0, -1, 0, -1], clickGU: [4, 4], sensingGU: [4, 4]}, |
517 | + {tag: "2x2 size, -1GU margins horizontal, click in sensing area", sizeGU: [2, 2], marginsGU: [-1, 0, -1, 0], clickGU: [4, 4], sensingGU: [4, 4]}, |
518 | + {tag: "2x2 size, -1GU margins vertical, click in sensing area", sizeGU: [2, 2], marginsGU: [0, -1, 0, -1], clickGU: [4, 4], sensingGU: [4, 4]}, |
519 | + {tag: "4x4 size, -1GU margins.all, click in sensing area", sizeGU: [4, 4], marginsAll: -units.gu(1), clickGU: [4, 4], sensingGU: [4, 4]}, |
520 | + {tag: "4x4 size, -1GU margins horizontal, click in sensing area", sizeGU: [4, 4], marginsGU: [-1, 0, -1, 0], clickGU: [4, 4], sensingGU: [4, 4]}, |
521 | + {tag: "4x4 size, -1GU margins vertical, click in sensing area", sizeGU: [4, 4], marginsGU: [0, -1, 0, -1], clickGU: [4, 4], sensingGU: [4, 4]}, |
522 | + |
523 | + // bigger size than minimum, decrease sensing area |
524 | + {tag: "5x5 size, -1GU margins.all, click in sensing area", sizeGU: [5, 5], marginsAll: -units.gu(1), clickGU: [4, 4], sensingGU: [4, 4]}, |
525 | + {tag: "5x5 size, -1GU margins horizontal, click in sensing area", sizeGU: [5, 5], marginsGU: [-1, 0, -1, 0], clickGU: [4, 5], sensingGU: [4, 5]}, |
526 | + {tag: "5x5 size, -1GU margins vertical, click in sensing area", sizeGU: [5, 5], marginsGU: [0, -1, 0, -1], clickGU: [5, 4], sensingGU: [5, 4]}, |
527 | + ]; |
528 | + } |
529 | + function test_sensing_area(data) { |
530 | + signalSpy.target = buttonWithSensing; |
531 | + buttonWithSensing.objectName = data.tag; |
532 | + buttonWithSensing.width = units.gu(data.sizeGU[0]); |
533 | + buttonWithSensing.height = units.gu(data.sizeGU[1]); |
534 | + if (data.marginsGU) { |
535 | + buttonWithSensing.sensingMargins.left = units.gu(data.marginsGU[0]); |
536 | + buttonWithSensing.sensingMargins.top = units.gu(data.marginsGU[1]); |
537 | + buttonWithSensing.sensingMargins.right = units.gu(data.marginsGU[2]); |
538 | + buttonWithSensing.sensingMargins.bottom = units.gu(data.marginsGU[3]); |
539 | + } else if (data.marginsAll) { |
540 | + buttonWithSensing.sensingMargins.all = data.marginsAll; |
541 | + } |
542 | + |
543 | + if (data.sensingGU) { |
544 | + compare(buttonWithSensing.__mouseArea.width, units.gu(data.sensingGU[0]), "unexpected horizontal sensing size"); |
545 | + compare(buttonWithSensing.__mouseArea.height, units.gu(data.sensingGU[1]), "unexpected vertical sensing size"); |
546 | + } |
547 | + if (data.fail) { |
548 | + expectFailContinue(data.tag, "no signal"); |
549 | + } |
550 | + mouseClick(buttonWithSensing.__mouseArea, units.gu(data.clickGU[0]), units.gu(data.clickGU[1])); |
551 | + signalSpy.wait(500); |
552 | + } |
553 | + |
554 | + function test_predeclared_sensing_area() { |
555 | + var point = centerOf(increasedSensing.parent); |
556 | + // move the point to the edge of the sensing area |
557 | + point.x -= increasedSensing.sensingMargins.left; |
558 | + point.y -= increasedSensing.sensingMargins.top; |
559 | + // click |
560 | + signalSpy.target = increasedSensing; |
561 | + mouseClick(increasedSensing.parent, point.x, point.y); |
562 | + signalSpy.wait(500); |
563 | + } |
564 | } |
565 | } |
left some comments :)