Merge lp:~zsombi/ubuntu-ui-toolkit/scrollbar-delegate-fix into lp:~ui-toolkit/ubuntu-ui-toolkit/trunk
- scrollbar-delegate-fix
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~zsombi/ubuntu-ui-toolkit/scrollbar-delegate-fix |
Merge into: | lp:~ui-toolkit/ubuntu-ui-toolkit/trunk |
Diff against target: |
1434 lines (+462/-491) 14 files modified
CHANGES (+14/-0) debian/changelog (+6/-0) demos/ScrollBars.qml (+7/-15) modules/Ubuntu/Components/ModelSectionCounter.qml (+63/-36) modules/Ubuntu/Components/plugin/shapeitem.cpp (+70/-64) modules/Ubuntu/Components/plugin/shapeitem.h (+3/-4) modules/Ubuntu/Components/qmldir (+2/-1) modules/Ubuntu/Components/scrollbarUtils.js (+127/-0) tests/unit/tst_components/tst_modelsectioncounter.qml (+66/-29) tests/unit/tst_components/tst_scrollbar.qml (+0/-98) themes/Ambiance/qmltheme/ScrollSliderSizer.qml (+0/-55) themes/Ambiance/qmltheme/ScrollbarDelegate.qml (+99/-133) themes/Ambiance/qmltheme/ScrollbarStyle.qml (+0/-54) themes/Ambiance/qmltheme/default.qmltheme (+5/-2) |
To merge this branch: | bzr merge lp:~zsombi/ubuntu-ui-toolkit/scrollbar-delegate-fix |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gerry Boland (community) | Needs Fixing | ||
Review via email: mp+142303@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-01-09.
Commit message
Scrollbar slider size and positioning fix for ListViews with header, footer and sections.
Description of the change
Scrollbar slider size and positioning fix for ListViews.
Gerry Boland (gerboland) wrote : | # |
Gerry Boland (gerboland) wrote : | # |
modules/
- property int sectionCount: 0
+ property int count: 0
Changing API. Are we certain no projects are using this? This change necessary?
Gerry Boland (gerboland) wrote : | # |
> modules/
> - property int sectionCount: 0
> + property int count: 0
> Changing API. Are we certain no projects are using this? This change
> necessary?
Sorry please ignore, I'm talking rubbish
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 04:35 PM, Gerry Boland wrote:
> modules/
> - property int sectionCount: 0
> + property int count: 0
> Changing API. Are we certain no projects are using this? This change necessary?
It's an internal component, there shouldn't be anybody using it.
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 04:20 PM, Gerry Boland wrote:
> demos/ScrollBar
> + highlightFollow
> + highlightRangeMode: ListView.ApplyRange
> Why needed?
I had problems when the CurrentLabelAtStart was set, and that was
solving it... Need to check it again, and will remove if it doesn't
cause problems anymore.
- 267. By Zsombor Egri
-
some comments applied. ModelSectionCounter made private. Unit test updated.
Gerry Boland (gerboland) wrote : | # |
+++ modules/
+ScrollbarUtils 0.1 scrollbarUtils.js
internal?
Gerry Boland (gerboland) wrote : | # |
+ if (flickable && flickable.
Feels a little shaky, no better way?
Gerry Boland (gerboland) wrote : | # |
function sliderSize(
It would be a little more efficient to read the values of (flickable.
Gerry Boland (gerboland) wrote : | # |
+ The scroll and drag functions require a slider that is not having any minimum
"a slider that does not have any minimum"
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 05:19 PM, Gerry Boland wrote:
> +++ modules/
> +ScrollbarUtils 0.1 scrollbarUtils.js
> internal?
Nope
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 05:27 PM, Gerry Boland wrote:
> + if (flickable && flickable.
> Feels a little shaky, no better way?
Well, as there's no way to have type checking, that's the closest way to
differentiate ListView from Flickable and GridView...
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 05:37 PM, Gerry Boland wrote:
> function sliderSize(
> It would be a little more efficient to read the values of (flickable.
I was thinking of creating a JSON object and store all these properties
there.. however reading these values are not that problematic, as far as
I know properties are hashed anyway... but this could be speeded up a
bit. Then I read somewhere that using too many variables may also cause
performance issues...
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 05:38 PM, Gerry Boland wrote:
> + The scroll and drag functions require a slider that is not having any minimum
> "a slider that does not have any minimum"
ack
Gerry Boland (gerboland) wrote : | # |
+ var propContent = (vertical) ? "contentY" : "contentX";
You do this & similar in each function. It would be more elegant to have a object with a vertical property that is only changed when necessary, and private propOrigin, propContent and propPos vars based on that.
Gerry Boland (gerboland) wrote : | # |
+++ tests/unit/
+ wait(0);
I never like seeing lines like this in a test. Is it really needed?
Gerry Boland (gerboland) wrote : | # |
Just a functional problem I've noticed when interactive=true and with a mouse. This a supported use-case?
Say for example you've a long list, so the slider is small. With my mouse, I can approach the bottom right of the list view's view and a thumb appears there. Then as a consequence if I grab the thumb, I can't scroll the list much at all.
Gerry Boland (gerboland) wrote : | # |
+++ themes/
+ onActiveChanged: {
+ print(drag.active)
Remove please
Gerry Boland (gerboland) wrote : | # |
+ // update thump position
Typo: thumb
Juhapekka Piiroinen (juhapekka-piiroinen) wrote : | # |
This MR needs to be resubmitted against the new trunk.
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 07:48 PM, Gerry Boland wrote:
> + var propContent = (vertical) ? "contentY" : "contentX";
> You do this & similar in each function. It would be more elegant to have a object with a vertical property that is only changed when necessary, and private propOrigin, propContent and propPos vars based on that.
Agreed... and as said in a previous post, I'll do it.
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 07:51 PM, Gerry Boland wrote:
> +++ tests/unit/
> + wait(0);
> I never like seeing lines like this in a test. Is it really needed?
I had that line there because previously the test had only one type of
model, and was filled in a loop upon component completion. In those
cases you need to get the event loop executed somehow, and wait(0) does
that, so the model gets updated for the next line of execution. It is no
longer needed, so I'll remove it.
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 07:59 PM, Gerry Boland wrote:
> Review: Needs Fixing
>
> +++ themes/
> + onActiveChanged: {
> + print(drag.active)
> Remove please
already removed.
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 08:00 PM, Gerry Boland wrote:
> + // update thump position
> Typo: thumb
ack
Zsombor Egri (zsombi) wrote : | # |
On 01/08/2013 07:55 PM, Gerry Boland wrote:
> Review: Needs Information
>
> Just a functional problem I've noticed when interactive=true and with a mouse. This a supported use-case?
>
> Say for example you've a long list, so the slider is small. With my mouse, I can approach the bottom right of the list view's view and a thumb appears there. Then as a consequence if I grab the thumb, I can't scroll the list much at all.
This is the way Scrollbars work in Ubuntu now. The way our Scrollbar
works was taken from Marumbi, and the functionality was kept as was
there. If we need to follow the current behavior, then I have to change
it again, as currently the thumb is just a decoration during dragging.
Gerry Boland (gerboland) wrote : | # |
> On 01/08/2013 05:19 PM, Gerry Boland wrote:
> > +++ modules/
> > +ScrollbarUtils 0.1 scrollbarUtils.js
> > internal?
> Nope
Why?
- 268. By Zsombor Egri
-
second wave of comments applied
Gerry Boland (gerboland) wrote : | # |
> On 01/08/2013 07:55 PM, Gerry Boland wrote:
> > Review: Needs Information
> >
> > Just a functional problem I've noticed when interactive=true and with a
> mouse. This a supported use-case?
> >
> > Say for example you've a long list, so the slider is small. With my mouse, I
> can approach the bottom right of the list view's view and a thumb appears
> there. Then as a consequence if I grab the thumb, I can't scroll the list much
> at all.
> This is the way Scrollbars work in Ubuntu now. The way our Scrollbar
> works was taken from Marumbi, and the functionality was kept as was
> there. If we need to follow the current behavior, then I have to change
> it again, as currently the thumb is just a decoration during dragging.
Ok let's leave it out of this MR, as I think it needs to be considered separately.
- 269. By Zsombor Egri
-
trunk merge
Zsombor Egri (zsombi) wrote : | # |
On 01/09/2013 04:00 PM, Gerry Boland wrote:
>> On 01/08/2013 05:19 PM, Gerry Boland wrote:
>>> +++ modules/
>>> +ScrollbarUtils 0.1 scrollbarUtils.js
>>> internal?
>> Nope
> Why?
if you happen to create a new delegate, these utility funtions can be
used, so need to be public.
- 270. By Zsombor Egri
-
interactive turned off, animations fixed
Unmerged revisions
- 270. By Zsombor Egri
-
interactive turned off, animations fixed
- 269. By Zsombor Egri
-
trunk merge
- 268. By Zsombor Egri
-
second wave of comments applied
- 267. By Zsombor Egri
-
some comments applied. ModelSectionCounter made private. Unit test updated.
- 266. By Zsombor Egri
-
trunk merge
- 265. By Zsombor Egri
-
documentation fixes
- 264. By Zsombor Egri
-
test scrollbar delegate removed
- 263. By Zsombor Egri
-
section and model counting not needed anymore for proper scrollbar slider sizing and positioning
- 262. By Zsombor Egri
-
leftover code
- 261. By Zsombor Egri
-
trunk merge
Preview Diff
1 | === modified file 'CHANGES' |
2 | --- CHANGES 2013-01-07 18:28:42 +0000 |
3 | +++ CHANGES 2013-01-09 14:06:21 +0000 |
4 | @@ -15,6 +15,20 @@ |
5 | API Changes |
6 | *********** |
7 | |
8 | +* None |
9 | + |
10 | + |
11 | +SDK 0.1.25 |
12 | +########## |
13 | + |
14 | +Compatibility Breaks |
15 | +******************** |
16 | + |
17 | +* None |
18 | + |
19 | +API Changes |
20 | +*********** |
21 | + |
22 | * Removed UbuntuShape::borderSource and UbuntuShape::maskSource. That API in |
23 | this form reveals to be too difficult to maintain while keeping an efficient |
24 | implementation. In the future, we plan to expose these features through |
25 | |
26 | === modified file 'debian/changelog' |
27 | --- debian/changelog 2012-12-21 09:37:46 +0000 |
28 | +++ debian/changelog 2013-01-09 14:06:21 +0000 |
29 | @@ -1,3 +1,9 @@ |
30 | +qt-components-ubuntu (0.1.25) quantal; urgency=low |
31 | + |
32 | + * New release |
33 | + |
34 | + -- Florian Boucault <florian.boucault@canonical.com> Tue, 08 Jan 2013 18:46:47 +0000 |
35 | + |
36 | qt-components-ubuntu (0.1.24) quantal; urgency=low |
37 | |
38 | * New release |
39 | |
40 | === modified file 'demos/ScrollBars.qml' |
41 | --- demos/ScrollBars.qml 2012-12-13 08:24:38 +0000 |
42 | +++ demos/ScrollBars.qml 2013-01-09 14:06:21 +0000 |
43 | @@ -77,20 +77,12 @@ |
44 | |
45 | section.property: "title" |
46 | section.criteria: ViewSection.FirstCharacter |
47 | - section.delegate: Header { |
48 | - text: "Section - " + section |
49 | - } |
50 | - |
51 | - delegate: Standard { |
52 | - text: "Data - " + label |
53 | - } |
54 | - |
55 | - header: Header { |
56 | - text: "List header" |
57 | - } |
58 | - footer: Header { |
59 | - text: "List footer" |
60 | - } |
61 | + section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels |
62 | + |
63 | + section.delegate: Header { text: "Section - " + section } |
64 | + delegate: Standard { text: "Data - " + label } |
65 | + header: Header { text: "List header" } |
66 | + footer: Header { text: "List footer" } |
67 | } |
68 | ListModel { |
69 | id: listModel |
70 | @@ -99,7 +91,7 @@ |
71 | listModel.append({"title": i, "label": i}) |
72 | } |
73 | } |
74 | - } |
75 | + } |
76 | |
77 | Scrollbar { |
78 | flickableItem: listView |
79 | |
80 | === modified file 'modules/Ubuntu/Components/ModelSectionCounter.qml' |
81 | --- modules/Ubuntu/Components/ModelSectionCounter.qml 2012-11-30 17:39:06 +0000 |
82 | +++ modules/Ubuntu/Components/ModelSectionCounter.qml 2013-01-09 14:06:21 +0000 |
83 | @@ -25,6 +25,7 @@ |
84 | */ |
85 | |
86 | Object { |
87 | + id: counter |
88 | |
89 | /*! |
90 | \preliminary |
91 | @@ -36,13 +37,13 @@ |
92 | \preliminary |
93 | The property contains the section counts of the given view. |
94 | */ |
95 | - property int sectionCount: 0 |
96 | + property int count: 0 |
97 | |
98 | /*! |
99 | \preliminary |
100 | The property contains the section Item height. |
101 | */ |
102 | - property real sectionHeight: 0 |
103 | + readonly property alias sectionHeight: internals.sectionHeight |
104 | |
105 | /*! |
106 | \preliminary |
107 | @@ -57,10 +58,19 @@ |
108 | This property holds the cached sections when the cacheSections property is set, and |
109 | is an empty list when no caching is requested. |
110 | */ |
111 | - property var sectionCache: [] |
112 | + property var cache: [] |
113 | + |
114 | + /*! |
115 | + \internal |
116 | + */ |
117 | + onCacheSectionsChanged: internals.checkSections() |
118 | |
119 | QtObject { |
120 | id: internals |
121 | + |
122 | + property real sectionHeight: (count > 0 && view.section.delegate) ? |
123 | + QuickUtils.modelDelegateHeight(view.section.delegate, view.model) : 0.0 |
124 | + |
125 | property var myView: null |
126 | function disconnectPreviousView() |
127 | { |
128 | @@ -80,6 +90,9 @@ |
129 | |
130 | if (myView.model.itemsRemoved) |
131 | myView.model.itemsRemoved.disconnect(checkSections); |
132 | + |
133 | + if (myView.section.delegateChanged) |
134 | + myView.section.delegateChanged.disconnect(checkSections); |
135 | } |
136 | |
137 | function initSectionCounter() |
138 | @@ -99,38 +112,50 @@ |
139 | |
140 | if (myView.model.itemsRemoved) |
141 | myView.model.itemsRemoved.connect(checkSections); |
142 | + |
143 | + if (myView.section.delegateChanged) |
144 | + myView.section.delegateChanged.connect(checkSections); |
145 | + |
146 | + // finally check sections |
147 | + checkSections(); |
148 | } |
149 | |
150 | function checkSections() |
151 | { |
152 | - if (undefined === view.section.property || "" === view.section.property) |
153 | - return; |
154 | - function sectionString(str) |
155 | - { |
156 | - return (view.section.criteria === ViewSection.FirstCharacter) ? str.charAt(0) : str; |
157 | - } |
158 | - |
159 | var sections = 0, sectionStack = []; |
160 | - var current = "", |
161 | - prop = view.section.property, |
162 | - item, section = ""; |
163 | - for (var i = 0, count = (typeof view.model.count === 'undefined' ? view.model.length : view.model.count); i < count; i++) { |
164 | - item = view.model.get(i); |
165 | - section = sectionString(JSON.stringify(item[prop])).toLowerCase(); |
166 | - if (section !== current) { |
167 | - current = section; |
168 | - sections++; |
169 | - if (cacheSections) |
170 | - sectionStack.push(current); |
171 | + if (view && view.section && undefined !== view.section.property && "" !== view.section.property) { |
172 | + function sectionString(str) |
173 | + { |
174 | + if (str === undefined) |
175 | + return ""; |
176 | + return (view.section.criteria === ViewSection.FirstCharacter) ? str.charAt(1) : str; |
177 | + } |
178 | + |
179 | + var current = "", |
180 | + prop = view.section.property, |
181 | + item = null, section = "", |
182 | + count = (typeof view.model.length === 'undefined' ? view.model.count : view.model.length); |
183 | + for (var i = 0; i < count; i++) { |
184 | + if (view.model.hasOwnProperty("get")) { |
185 | + item = view.model.get(i); |
186 | + section = sectionString(JSON.stringify(item[prop])).toLowerCase(); |
187 | + } else { |
188 | + item = view.model[i]; |
189 | + if (item.hasOwnProperty(prop)) |
190 | + section = sectionString(JSON.stringify(item[prop])).toLowerCase(); |
191 | + else |
192 | + section = sectionString(JSON.stringify(item)).toLowerCase(); |
193 | + } |
194 | + if (section !== current) { |
195 | + current = section; |
196 | + sections++; |
197 | + if (cacheSections) |
198 | + sectionStack.push(current); |
199 | + } |
200 | } |
201 | } |
202 | - if (sectionCount != sections && sectionCount <= 0 && sections > 0) { |
203 | - sectionHeight = QuickUtils.modelDelegateHeight(view.section.delegate, view.model); |
204 | - } else if (sections <= 0) |
205 | - sectionHeight = 0; |
206 | - |
207 | - sectionCount = sections; |
208 | - sectionCache = sectionStack; |
209 | + counter.count = sections; |
210 | + counter.cache = sectionStack; |
211 | } |
212 | } |
213 | |
214 | @@ -138,14 +163,16 @@ |
215 | |
216 | onViewChanged: { |
217 | internals.disconnectPreviousView() |
218 | - if (view && view.model) { |
219 | - internals.initSectionCounter(); |
220 | - } else if (view) { |
221 | - view.modelChanged.connect(function() |
222 | - { |
223 | - if (view.model) |
224 | - internals.initSectionCounter(); |
225 | - }); |
226 | + if (view && view.hasOwnProperty("model")) { |
227 | + if (view && view.model) { |
228 | + internals.initSectionCounter(); |
229 | + } else if (view) { |
230 | + view.modelChanged.connect(function() |
231 | + { |
232 | + if (view && view.model) |
233 | + internals.initSectionCounter(); |
234 | + }); |
235 | + } |
236 | } |
237 | } |
238 | } |
239 | |
240 | === modified file 'modules/Ubuntu/Components/plugin/shapeitem.cpp' |
241 | --- modules/Ubuntu/Components/plugin/shapeitem.cpp 2013-01-08 02:51:09 +0000 |
242 | +++ modules/Ubuntu/Components/plugin/shapeitem.cpp 2013-01-09 14:06:21 +0000 |
243 | @@ -266,7 +266,6 @@ |
244 | QSGNode* ShapeItem::updatePaintNode(QSGNode* old_node, UpdatePaintNodeData* data) |
245 | { |
246 | Q_UNUSED(data); |
247 | - bool setPosition = false; |
248 | |
249 | // FIXME(loicm) Shape textures are stored in the read-only data section of the plugin as it |
250 | // avoids having to deal with paths for now. It should preferably be loaded from a file. |
251 | @@ -286,55 +285,43 @@ |
252 | node = new ShapeNode(this); |
253 | } |
254 | |
255 | - // Update dirty parameters. |
256 | ShapeTexturedMaterial* texturedMaterial = node->texturedMaterial(); |
257 | ShapeColoredMaterial* coloredMaterial = node->coloredMaterial(); |
258 | - if (dirtyFlags_ & ShapeItem::DirtyBaseColor) |
259 | - coloredMaterial->setBaseColor(baseColor_); |
260 | - if (dirtyFlags_ & ShapeItem::DirtyGradientColor) |
261 | - coloredMaterial->setGradientColor(gradientColor_); |
262 | - if (dirtyFlags_ & (ShapeItem::DirtyBorder | ShapeItem::DirtyRadius |
263 | - | ShapeItem::DirtyGridUnit)) { |
264 | - TextureData* textureData = (gridUnit_ > lowHighTextureThreshold) ? |
265 | - &shapeTextureHigh : &shapeTextureLow; |
266 | - node->setShapeCoordinate(border_, radius_, textureData); |
267 | - } |
268 | - if (dirtyFlags_ & (ShapeItem::DirtyGeometry | ShapeItem::DirtyRadius |
269 | - | ShapeItem::DirtyStretched | ShapeItem::DirtyHAlignment |
270 | - | ShapeItem::DirtyVAlignment | ShapeItem::DirtyGridUnit)) { |
271 | - setPosition = true; |
272 | - } |
273 | - if (dirtyFlags_ & ShapeItem::DirtyImage) { |
274 | - texturedMaterial->setImage(image_); |
275 | - node->setMaterialType(image_ ? ShapeNode::TexturedMaterial : ShapeNode::ColoredMaterial); |
276 | - setPosition = true; |
277 | - } |
278 | - dirtyFlags_ = ShapeItem::NotDirty; |
279 | - |
280 | - if (setPosition) { |
281 | - int scaledDown = 0; |
282 | - TextureData* textureData = (gridUnit_ > lowHighTextureThreshold) ? |
283 | - &shapeTextureHigh : &shapeTextureLow; |
284 | - // Get the radius considering the current grid unit and the texture raster grid unit. |
285 | - float radius = (radius_ == ShapeItem::SmallRadius) ? |
286 | - textureData->smallRadius : textureData->mediumRadius; |
287 | - const float scaleFactor = gridUnit_ / textureData->gridUnit; |
288 | - radius *= scaleFactor; |
289 | - if (scaleFactor != 1.0f) { |
290 | - scaledDown |= 1; |
291 | - } |
292 | - // When the item size is less than 2 radii, the radius is scaled down anyhow. |
293 | - const float halfMinWidthHeight = qMin(geometry_.width(), geometry_.height()) * 0.5f; |
294 | - if (radius > halfMinWidthHeight) { |
295 | - radius = halfMinWidthHeight; |
296 | - scaledDown |= 1; |
297 | - } |
298 | - // Set the shape texture to be used depending on current grid unit. |
299 | - coloredMaterial->setShapeTexture(textureData->texture, !!scaledDown); |
300 | - texturedMaterial->setShapeTexture(textureData->texture, !!scaledDown); |
301 | - // Update vertex position and shape coordinate attributes. |
302 | - node->setPosition(geometry_, radius, image_, stretched_, hAlignment_, vAlignment_); |
303 | - } |
304 | + TextureData* textureData = (gridUnit_ > lowHighTextureThreshold) ? |
305 | + &shapeTextureHigh : &shapeTextureLow; |
306 | + |
307 | + // Set the shape texture to be used by the materials depending on current grid unit. The radius |
308 | + // is set considering the current grid unit and the texture raster grid unit. When the item size |
309 | + // is less than 2 radii, the radius is scaled down anyhow. |
310 | + float radius = (radius_ == ShapeItem::SmallRadius) ? |
311 | + textureData->smallRadius : textureData->mediumRadius; |
312 | + const float scaleFactor = gridUnit_ / textureData->gridUnit; |
313 | + radius *= scaleFactor; |
314 | + int scaledDown = 0; |
315 | + if (scaleFactor != 1.0f) { |
316 | + scaledDown |= 1; |
317 | + } |
318 | + const float halfMinWidthHeight = qMin(geometry_.width(), geometry_.height()) * 0.5f; |
319 | + if (radius > halfMinWidthHeight) { |
320 | + radius = halfMinWidthHeight; |
321 | + scaledDown |= 1; |
322 | + } |
323 | + coloredMaterial->setShapeTexture(textureData->texture, !!scaledDown); |
324 | + texturedMaterial->setShapeTexture(textureData->texture, !!scaledDown); |
325 | + |
326 | + // Update the other material properties. |
327 | + coloredMaterial->setBaseColor(baseColor_); |
328 | + coloredMaterial->setGradientColor(gradientColor_); |
329 | + texturedMaterial->setImage(image_); |
330 | + |
331 | + // Update node vertices and type. |
332 | + int index = (border_ == ShapeItem::RawBorder) ? |
333 | + 0 : (border_ == ShapeItem::IdleBorder) ? 1 : 2; |
334 | + if (radius_ == ShapeItem::SmallRadius) |
335 | + index += 3; |
336 | + node->setVertices(geometry_, radius, image_, stretched_, hAlignment_, vAlignment_, |
337 | + textureData->coordinate[index]); |
338 | + node->setMaterialType(image_ ? ShapeNode::TexturedMaterial : ShapeNode::ColoredMaterial); |
339 | |
340 | return node; |
341 | } |
342 | @@ -357,8 +344,9 @@ |
343 | setFlag(UsePreprocess, false); |
344 | } |
345 | |
346 | -void ShapeNode::setPosition(const QRectF& geometry, float radius, QQuickItem* image, bool stretched, |
347 | - ShapeItem::HAlignment hAlignment, ShapeItem::VAlignment vAlignment) |
348 | +void ShapeNode::setVertices(const QRectF& geometry, float radius, QQuickItem* image, bool stretched, |
349 | + ShapeItem::HAlignment hAlignment, ShapeItem::VAlignment vAlignment, |
350 | + float shapeCoordinate[][2]) |
351 | { |
352 | ShapeNode::Vertex* vertices = reinterpret_cast<ShapeNode::Vertex*>(geometry_.vertexData()); |
353 | const QSGTextureProvider* provider = image ? image->textureProvider() : NULL; |
354 | @@ -410,92 +398,110 @@ |
355 | // Set top row of 4 vertices. |
356 | vertices[0].position[0] = 0.0f; |
357 | vertices[0].position[1] = 0.0f; |
358 | + vertices[0].shapeCoordinate[0] = shapeCoordinate[0][0]; |
359 | + vertices[0].shapeCoordinate[1] = shapeCoordinate[0][1]; |
360 | vertices[0].imageCoordinate[0] = leftCoordinate; |
361 | vertices[0].imageCoordinate[1] = topCoordinate; |
362 | vertices[1].position[0] = radius; |
363 | vertices[1].position[1] = 0.0f; |
364 | + vertices[1].shapeCoordinate[0] = shapeCoordinate[1][0]; |
365 | + vertices[1].shapeCoordinate[1] = shapeCoordinate[1][1]; |
366 | vertices[1].imageCoordinate[0] = radiusCoordinateWidth; |
367 | vertices[1].imageCoordinate[1] = topCoordinate; |
368 | vertices[2].position[0] = width - radius; |
369 | vertices[2].position[1] = 0.0f; |
370 | + vertices[2].shapeCoordinate[0] = shapeCoordinate[2][0]; |
371 | + vertices[2].shapeCoordinate[1] = shapeCoordinate[2][1]; |
372 | vertices[2].imageCoordinate[0] = rightCoordinate - radiusCoordinateWidth; |
373 | vertices[2].imageCoordinate[1] = topCoordinate; |
374 | vertices[3].position[0] = width; |
375 | vertices[3].position[1] = 0.0f; |
376 | + vertices[3].shapeCoordinate[0] = shapeCoordinate[3][0]; |
377 | + vertices[3].shapeCoordinate[1] = shapeCoordinate[3][1]; |
378 | vertices[3].imageCoordinate[0] = rightCoordinate; |
379 | vertices[3].imageCoordinate[1] = topCoordinate; |
380 | |
381 | // Set middle-top row of 4 vertices. |
382 | vertices[4].position[0] = 0.0f; |
383 | vertices[4].position[1] = radius; |
384 | + vertices[4].shapeCoordinate[0] = shapeCoordinate[4][0]; |
385 | + vertices[4].shapeCoordinate[1] = shapeCoordinate[4][1]; |
386 | vertices[4].imageCoordinate[0] = leftCoordinate; |
387 | vertices[4].imageCoordinate[1] = topCoordinate + radiusCoordinateHeight; |
388 | vertices[5].position[0] = radius; |
389 | vertices[5].position[1] = radius; |
390 | + vertices[5].shapeCoordinate[0] = shapeCoordinate[5][0]; |
391 | + vertices[5].shapeCoordinate[1] = shapeCoordinate[5][1]; |
392 | vertices[5].imageCoordinate[0] = leftCoordinate + radiusCoordinateWidth; |
393 | vertices[5].imageCoordinate[1] = topCoordinate + radiusCoordinateHeight; |
394 | vertices[6].position[0] = width - radius; |
395 | vertices[6].position[1] = radius; |
396 | + vertices[6].shapeCoordinate[0] = shapeCoordinate[6][0]; |
397 | + vertices[6].shapeCoordinate[1] = shapeCoordinate[6][1]; |
398 | vertices[6].imageCoordinate[0] = rightCoordinate - radiusCoordinateWidth; |
399 | vertices[6].imageCoordinate[1] = topCoordinate + radiusCoordinateHeight; |
400 | vertices[7].position[0] = width; |
401 | vertices[7].position[1] = radius; |
402 | + vertices[7].shapeCoordinate[0] = shapeCoordinate[7][0]; |
403 | + vertices[7].shapeCoordinate[1] = shapeCoordinate[7][1]; |
404 | vertices[7].imageCoordinate[0] = rightCoordinate; |
405 | vertices[7].imageCoordinate[1] = topCoordinate + radiusCoordinateHeight; |
406 | |
407 | // Set middle-bottom row of 4 vertices. |
408 | vertices[8].position[0] = 0.0f; |
409 | vertices[8].position[1] = height - radius; |
410 | + vertices[8].shapeCoordinate[0] = shapeCoordinate[8][0]; |
411 | + vertices[8].shapeCoordinate[1] = shapeCoordinate[8][1]; |
412 | vertices[8].imageCoordinate[0] = leftCoordinate; |
413 | vertices[8].imageCoordinate[1] = bottomCoordinate - radiusCoordinateHeight; |
414 | vertices[9].position[0] = radius; |
415 | vertices[9].position[1] = height - radius; |
416 | + vertices[9].shapeCoordinate[0] = shapeCoordinate[9][0]; |
417 | + vertices[9].shapeCoordinate[1] = shapeCoordinate[9][1]; |
418 | vertices[9].imageCoordinate[0] = leftCoordinate + radiusCoordinateWidth; |
419 | vertices[9].imageCoordinate[1] = bottomCoordinate - radiusCoordinateHeight; |
420 | vertices[10].position[0] = width - radius; |
421 | vertices[10].position[1] = height - radius; |
422 | + vertices[10].shapeCoordinate[0] = shapeCoordinate[10][0]; |
423 | + vertices[10].shapeCoordinate[1] = shapeCoordinate[10][1]; |
424 | vertices[10].imageCoordinate[0] = rightCoordinate - radiusCoordinateWidth; |
425 | vertices[10].imageCoordinate[1] = bottomCoordinate - radiusCoordinateHeight; |
426 | vertices[11].position[0] = width; |
427 | vertices[11].position[1] = height - radius; |
428 | + vertices[11].shapeCoordinate[0] = shapeCoordinate[11][0]; |
429 | + vertices[11].shapeCoordinate[1] = shapeCoordinate[11][1]; |
430 | vertices[11].imageCoordinate[0] = rightCoordinate; |
431 | vertices[11].imageCoordinate[1] = bottomCoordinate - radiusCoordinateHeight; |
432 | |
433 | // Set bottom row of 4 vertices. |
434 | vertices[12].position[0] = 0.0f; |
435 | vertices[12].position[1] = height; |
436 | + vertices[12].shapeCoordinate[0] = shapeCoordinate[12][0]; |
437 | + vertices[12].shapeCoordinate[1] = shapeCoordinate[12][1]; |
438 | vertices[12].imageCoordinate[0] = leftCoordinate; |
439 | vertices[12].imageCoordinate[1] = bottomCoordinate; |
440 | vertices[13].position[0] = radius; |
441 | vertices[13].position[1] = height; |
442 | + vertices[13].shapeCoordinate[0] = shapeCoordinate[13][0]; |
443 | + vertices[13].shapeCoordinate[1] = shapeCoordinate[13][1]; |
444 | vertices[13].imageCoordinate[0] = leftCoordinate + radiusCoordinateWidth; |
445 | vertices[13].imageCoordinate[1] = bottomCoordinate; |
446 | vertices[14].position[0] = width - radius; |
447 | vertices[14].position[1] = height; |
448 | + vertices[14].shapeCoordinate[0] = shapeCoordinate[14][0]; |
449 | + vertices[14].shapeCoordinate[1] = shapeCoordinate[14][1]; |
450 | vertices[14].imageCoordinate[0] = rightCoordinate - radiusCoordinateWidth; |
451 | vertices[14].imageCoordinate[1] = bottomCoordinate; |
452 | vertices[15].position[0] = width; |
453 | vertices[15].position[1] = height; |
454 | + vertices[15].shapeCoordinate[0] = shapeCoordinate[15][0]; |
455 | + vertices[15].shapeCoordinate[1] = shapeCoordinate[15][1]; |
456 | vertices[15].imageCoordinate[0] = rightCoordinate; |
457 | vertices[15].imageCoordinate[1] = bottomCoordinate; |
458 | |
459 | markDirty(DirtyGeometry); |
460 | } |
461 | |
462 | -void ShapeNode::setShapeCoordinate(ShapeItem::Border border, ShapeItem::Radius radius, |
463 | - TextureData* textureData) |
464 | -{ |
465 | - ShapeNode::Vertex* vertices = reinterpret_cast<ShapeNode::Vertex*>(geometry_.vertexData()); |
466 | - int index = (border == ShapeItem::RawBorder) ? 0 : (border == ShapeItem::IdleBorder) ? 1 : 2; |
467 | - if (radius == ShapeItem::SmallRadius) |
468 | - index += 3; |
469 | - for (int i = 0; i < 16; i++) { |
470 | - vertices[i].shapeCoordinate[0] = textureData->coordinate[index][i][0]; |
471 | - vertices[i].shapeCoordinate[1] = textureData->coordinate[index][i][1]; |
472 | - } |
473 | - markDirty(DirtyGeometry); |
474 | -} |
475 | - |
476 | void ShapeNode::setMaterialType(ShapeNode::MaterialType material) |
477 | { |
478 | if (currentMaterial_ != material) { |
479 | |
480 | === modified file 'modules/Ubuntu/Components/plugin/shapeitem.h' |
481 | --- modules/Ubuntu/Components/plugin/shapeitem.h 2013-01-07 07:59:56 +0000 |
482 | +++ modules/Ubuntu/Components/plugin/shapeitem.h 2013-01-09 14:06:21 +0000 |
483 | @@ -220,10 +220,9 @@ |
484 | ShapeNode(ShapeItem* item); |
485 | ShapeTexturedMaterial* texturedMaterial() { return &texturedMaterial_; } |
486 | ShapeColoredMaterial* coloredMaterial() { return &coloredMaterial_; } |
487 | - void setPosition(const QRectF& geometry, float radius, QQuickItem* image, bool stretched, |
488 | - ShapeItem::HAlignment hAlignment, ShapeItem::VAlignment vAlignment); |
489 | - void setShapeCoordinate(ShapeItem::Border border, ShapeItem::Radius radius, |
490 | - TextureData* textureData); |
491 | + void setVertices(const QRectF& geometry, float radius, QQuickItem* image, bool stretched, |
492 | + ShapeItem::HAlignment hAlignment, ShapeItem::VAlignment vAlignment, |
493 | + float shapeCoordinate[][2]); |
494 | void setMaterialType(MaterialType material); |
495 | |
496 | private: |
497 | |
498 | === modified file 'modules/Ubuntu/Components/qmldir' |
499 | --- modules/Ubuntu/Components/qmldir 2012-12-12 06:29:58 +0000 |
500 | +++ modules/Ubuntu/Components/qmldir 2013-01-09 14:06:21 +0000 |
501 | @@ -14,7 +14,7 @@ |
502 | CheckBox 0.1 CheckBox.qml |
503 | Slider 0.1 Slider.qml |
504 | Scrollbar 0.1 Scrollbar.qml |
505 | -ModelSectionCounter 0.1 ModelSectionCounter.qml |
506 | +internal ModelSectionCounter ModelSectionCounter.qml |
507 | Object 0.1 Object.qml |
508 | TabButton 0.1 TabButton.qml |
509 | Page 0.1 Page.qml |
510 | @@ -27,3 +27,4 @@ |
511 | MathUtils 0.1 mathUtils.js |
512 | ComponentUtils 0.1 componentUtils.js |
513 | SliderUtils 0.1 sliderUtils.js |
514 | +ScrollbarUtils 0.1 scrollbarUtils.js |
515 | |
516 | === added file 'modules/Ubuntu/Components/scrollbarUtils.js' |
517 | --- modules/Ubuntu/Components/scrollbarUtils.js 1970-01-01 00:00:00 +0000 |
518 | +++ modules/Ubuntu/Components/scrollbarUtils.js 2013-01-09 14:06:21 +0000 |
519 | @@ -0,0 +1,127 @@ |
520 | +/* |
521 | + * Copyright 2012 Canonical Ltd. |
522 | + * |
523 | + * This program is free software; you can redistribute it and/or modify |
524 | + * it under the terms of the GNU Lesser General Public License as published by |
525 | + * the Free Software Foundation; version 3. |
526 | + * |
527 | + * This program is distributed in the hope that it will be useful, |
528 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
529 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
530 | + * GNU Lesser General Public License for more details. |
531 | + * |
532 | + * You should have received a copy of the GNU Lesser General Public License |
533 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
534 | + */ |
535 | + |
536 | +Qt.include("mathUtils.js") |
537 | + |
538 | +/*! |
539 | + \internal |
540 | + Object storing property names used in calculations. |
541 | + */ |
542 | +var _obj = { |
543 | + scrollbar: null, |
544 | + vertical: false, |
545 | + propOrigin: "", |
546 | + propContent: "", |
547 | + propPosRatio: "", |
548 | + propSizeRatio: "", |
549 | + propCoordinate: "", |
550 | + propSize: "", |
551 | + refresh: function () { |
552 | + _obj.vertical = (_obj.scrollbar.align === Qt.AlignLeading) || (_obj.scrollbar.align === Qt.AlignTrailing) |
553 | + _obj.propOrigin = (_obj.vertical) ? "originY" : "originX"; |
554 | + _obj.propContent = (_obj.vertical) ? "contentY" : "contentX"; |
555 | + _obj.propPosRatio = (_obj.vertical) ? "yPosition" : "xPosition"; |
556 | + _obj.propSizeRatio = (_obj.vertical) ? "heightRatio" : "widthRatio"; |
557 | + _obj.propCoordinate = (_obj.vertical) ? "y" : "x"; |
558 | + _obj.propSize = (_obj.vertical) ? "height" : "width"; |
559 | + } |
560 | +} |
561 | + |
562 | +/*! |
563 | + \internal |
564 | + Checks whether the _obj is valid or not. Must be called in every function |
565 | + as those can be invoked prior to the host (delegate) component completion. |
566 | + */ |
567 | +function __check(sb) { |
568 | + if (sb !== null && (_obj.scrollbar !== sb)) { |
569 | + _obj.scrollbar = sb; |
570 | + sb.flickableItemChanged.connect(_obj.refresh); |
571 | + sb.alignChanged.connect(_obj.refresh); |
572 | + _obj.refresh(); |
573 | + } |
574 | + |
575 | + return _obj.scrollbar; |
576 | +} |
577 | + |
578 | +/*! |
579 | + Returns whether the scrollbar is vertical or horizontal. |
580 | + */ |
581 | +function isVertical(scrollbar) { |
582 | + if (!__check(scrollbar)) return 0; |
583 | + return _obj.vertical; |
584 | +} |
585 | + |
586 | +/*! |
587 | + Calculates the slider position based on the visible area's ratios. |
588 | + */ |
589 | +function sliderPos(scrollbar, min, max) { |
590 | + if (!__check(scrollbar)) return 0; |
591 | + return clamp(scrollbar.flickableItem.visibleArea[_obj.propPosRatio] * scrollbar.flickableItem[_obj.propSize], min, max); |
592 | +} |
593 | + |
594 | +/*! |
595 | + Calculates the slider size for ListViews based on the visible area's position |
596 | + and size ratios, clamping it between min and max. |
597 | + |
598 | + The function can be used in Scrollbar delegates to calculate the size of the slider. |
599 | + */ |
600 | +function sliderSize(scrollbar, min, max) { |
601 | + if (!__check(scrollbar)) return 0; |
602 | + var sizeRatio = scrollbar.flickableItem.visibleArea[_obj.propSizeRatio]; |
603 | + var posRatio = scrollbar.flickableItem.visibleArea[_obj.propPosRatio]; |
604 | + var sizeUnderflow = (sizeRatio * max) < min ? min - (sizeRatio * max) : 0 |
605 | + var startPos = posRatio * (max - sizeUnderflow) |
606 | + var endPos = (posRatio + sizeRatio) * (max - sizeUnderflow) + sizeUnderflow |
607 | + var overshootStart = startPos < 0 ? -startPos : 0 |
608 | + var overshootEnd = endPos > max ? endPos - max : 0 |
609 | + |
610 | + // overshoot adjusted start and end |
611 | + var adjustedStartPos = startPos + overshootStart |
612 | + var adjustedEndPos = endPos - overshootStart - overshootEnd |
613 | + |
614 | + // final position and size of thumb |
615 | + var position = adjustedStartPos + min > max ? max - min : adjustedStartPos |
616 | + var result = (adjustedEndPos - position) < min ? min : (adjustedEndPos - position) |
617 | + |
618 | + return result; |
619 | +} |
620 | + |
621 | +/*! |
622 | + The function calculates and clamps the position to be scrolled to the minimum |
623 | + and maximum values. |
624 | + |
625 | + The scroll and drag functions require a slider that does not have any minimum |
626 | + size set (meaning the minimum is set to 0.0). Implementations should consider |
627 | + using an invisible cursor to drag the slider and the ListView position. |
628 | + */ |
629 | +function scrollAndClamp(scrollbar, amount, min, max) { |
630 | + if (!__check(scrollbar)) return 0; |
631 | + return scrollbar.flickableItem[_obj.propOrigin] + |
632 | + clamp(scrollbar.flickableItem[_obj.propContent] - scrollbar.flickableItem[_obj.propOrigin] + amount, |
633 | + min, max); |
634 | +} |
635 | + |
636 | +/*! |
637 | + The function calculates the new position of the dragged slider. The amount is |
638 | + relative to the contentSize, which is either the flickable's contentHeight or |
639 | + contentWidth or other calculated value, depending on its orientation. The pageSize |
640 | + specifies the visibleArea, and it is usually the heigtht/width of the scrolling area. |
641 | + */ |
642 | +function dragAndClamp(scrollbar, cursor, contentSize, pageSize) { |
643 | + if (!__check(scrollbar)) return 0; |
644 | + scrollbar.flickableItem[_obj.propContent] = |
645 | + scrollbar.flickableItem[_obj.propOrigin] + cursor[_obj.propCoordinate] * contentSize / pageSize; |
646 | +} |
647 | |
648 | === modified file 'tests/unit/tst_components/tst_modelsectioncounter.qml' |
649 | --- tests/unit/tst_components/tst_modelsectioncounter.qml 2012-11-21 14:55:54 +0000 |
650 | +++ tests/unit/tst_components/tst_modelsectioncounter.qml 2013-01-09 14:06:21 +0000 |
651 | @@ -5,52 +5,89 @@ |
652 | TestCase { |
653 | name: "ModelSectionCounterAPI" |
654 | |
655 | - function test_defaults() |
656 | - { |
657 | + function initTestCase() { |
658 | + var component = Qt.createComponent(Qt.resolvedUrl("../../../modules/Ubuntu/Components/ModelSectionCounter.qml")); |
659 | + counter = component.createObject(parent); |
660 | + } |
661 | + |
662 | + function test_0_defaults() { |
663 | compare(counter.view, null, "ModelSectionCounter does not have valid view set"); |
664 | - compare(counter.sectionCount, 0, "ModelSectionCounter default sectionCount is 0"); |
665 | + compare(counter.count, 0, "ModelSectionCounter default sectionCount is 0"); |
666 | compare(counter.cacheSections, false, "ModelSectionCounter does not cache sections"); |
667 | - compare(counter.sectionCache, [], "ModelSectionCounter has empty section cache list"); |
668 | - } |
669 | - |
670 | - function test_sectionCount() |
671 | - { |
672 | + compare(counter.cache, [], "ModelSectionCounter has empty section cache list"); |
673 | + } |
674 | + |
675 | + function test_1_count_data() { |
676 | + return [ |
677 | + {tag: "Empty model", model: emptyModel, sectionProperty: "label", expect: 0, expectFail: false}, |
678 | + {tag: "ListModel", model: objectModel, sectionProperty: "label", expect: 8, expectFail: false}, |
679 | + {tag: "StringList model", model: stringModel, sectionProperty: "modelData", expect: 8, expectFail: false}, |
680 | + {tag: "VariantList model", model: variantModel, sectionProperty: "label", expect: 8, expectFail: false}, |
681 | + ]; |
682 | + } |
683 | + |
684 | + function test_1_count(data) { |
685 | + if (data.expectFail) |
686 | + expectFail("", data.tag); |
687 | + list.model = data.model; |
688 | + list.section.property = data.sectionProperty; |
689 | + list.section.criteria = ViewSection.FirstCharacter; |
690 | counter.view = list; |
691 | - compare(counter.view, list, "List not set"); |
692 | - compare(counter.sectionCount, 10, "Section count is wrong"); |
693 | + compare(counter.count, data.expect, "Section count is wrong"); |
694 | } |
695 | |
696 | - function test_sectionCache() |
697 | - { |
698 | + function test_2_cache() { |
699 | + list.model = objectModel; |
700 | + list.section.property = "label"; |
701 | + list.section.criteria = ViewSection.FirstCharacter; |
702 | counter.view = list; |
703 | counter.cacheSections = true; |
704 | - var cache = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; |
705 | - compare(counter.sectionCache, cache, "Section cache is wrong"); |
706 | + var cache = ["1", "2", "3", "4", "5", "6", "7", "8"]; |
707 | + compare(counter.cache, cache, "Section cache is wrong"); |
708 | |
709 | counter.cacheSections = false; |
710 | cache = []; |
711 | - compare(counter.sectionCache, cache, "Section cache is wrong"); |
712 | + compare(counter.cache, cache, "Section cache is wrong"); |
713 | } |
714 | |
715 | Item { |
716 | - ListModel { |
717 | - id: listModel |
718 | - Component.onCompleted: { |
719 | - for (var i = 0; i < 10; i++) { |
720 | - listModel.append({"label": i}) |
721 | - } |
722 | - } |
723 | - } |
724 | ListView { |
725 | id: list |
726 | - model: listModel |
727 | |
728 | - delegate: Item{ height: 40} |
729 | - section.property: "label" |
730 | - section.criteria: ViewSection.FirstCharacter |
731 | + delegate: Item{ height: 40 } |
732 | + section.delegate: Item{ height: 10 } |
733 | } |
734 | } |
735 | - ModelSectionCounter { |
736 | - id: counter |
737 | + |
738 | + property var counter |
739 | + |
740 | + property var emptyModel: ListModel {} |
741 | + |
742 | + property var objectModel: ListModel { |
743 | + ListElement {label: "1"} |
744 | + ListElement {label: "10"} |
745 | + ListElement {label: "100"} |
746 | + ListElement {label: "2"} |
747 | + ListElement {label: "3"} |
748 | + ListElement {label: "4"} |
749 | + ListElement {label: "5"} |
750 | + ListElement {label: "6"} |
751 | + ListElement {label: "7"} |
752 | + ListElement {label: "8"} |
753 | } |
754 | + |
755 | + property var variantModel: [ |
756 | + {"label": "1"}, |
757 | + {"label": "10"}, |
758 | + {"label": "100"}, |
759 | + {"label": "2"}, |
760 | + {"label": "3"}, |
761 | + {"label": "4"}, |
762 | + {"label": "5"}, |
763 | + {"label": "6"}, |
764 | + {"label": "7"}, |
765 | + {"label": "8"} |
766 | + ] |
767 | + |
768 | + property var stringModel: ["1", "10", "100", "2", "3", "4", "5", "6", "7", "8"] |
769 | } |
770 | |
771 | === modified file 'tests/unit/tst_components/tst_scrollbar.qml' |
772 | --- tests/unit/tst_components/tst_scrollbar.qml 2012-12-13 12:06:13 +0000 |
773 | +++ tests/unit/tst_components/tst_scrollbar.qml 2013-01-09 14:06:21 +0000 |
774 | @@ -64,61 +64,6 @@ |
775 | } |
776 | } |
777 | |
778 | - function test_scrollbar_height_with_ListView_data() { |
779 | - return [ |
780 | - {tag: "Empty model", model: emptyModel, expect: 0, expectFail: false}, |
781 | - {tag: "Empty model + header", model: emptyModel, header: headerFooter, expect: 20, expectFail: false}, |
782 | - {tag: "Empty model + header + footer", model: emptyModel, header: headerFooter, footer: headerFooter, expect: 40, expectFail: false}, |
783 | - {tag: "Empty model + header + footer + section (inline)", model: emptyModel, header: headerFooter, footer: headerFooter, section: section, expect: 40, expectFail: false}, |
784 | - {tag: "Empty model + header + footer + section (top)", model: emptyModel, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart, expect: 40, expectFail: false}, |
785 | - {tag: "Empty model + header + footer + section (bottom)", model: emptyModel, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.NextLabelAtEnd, expect: 40, expectFail: false}, |
786 | - {tag: "Empty model + header + footer + section (top+bottom)", model: emptyModel, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd, expect: 40, expectFail: false}, |
787 | - |
788 | - {tag: "ObjectList model", model: objectList, expect: 150, expectFail: false}, |
789 | - {tag: "ObjectList model + header", model: objectList, header: headerFooter, expect: 170, expectFail: false}, |
790 | - {tag: "ObjectList model + header + footer", model: objectList, header: headerFooter, footer: headerFooter, expect: 190, expectFail: false}, |
791 | - {tag: "ObjectList model + header + footer + section (inline)", model: objectList, header: headerFooter, footer: headerFooter, section: section, expect: 290, expectFail: true}, |
792 | - {tag: "ObjectList model + header + footer + section (top)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart, expect: 190, expectFail: false}, |
793 | - {tag: "ObjectList model + header + footer + section (bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
794 | - {tag: "ObjectList model + header + footer + section (top+bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
795 | - |
796 | - {tag: "VariantList model", model: objectList, expect: 150, expectFail: false}, |
797 | - {tag: "VariantList model + header", model: objectList, header: headerFooter, expect: 170, expectFail: false}, |
798 | - {tag: "VariantList model + header + footer", model: objectList, header: headerFooter, footer: headerFooter, expect: 190, expectFail: false}, |
799 | - {tag: "VariantList model + header + footer + section (inline)", model: objectList, header: headerFooter, footer: headerFooter, section: section, expect: 290, expectFail: true}, |
800 | - {tag: "VariantList model + header + footer + section (top)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart, expect: 190, expectFail: false}, |
801 | - {tag: "VariantList model + header + footer + section (bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
802 | - {tag: "VariantList model + header + footer + section (top+bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
803 | - |
804 | - {tag: "StringList model", model: objectList, expect: 150, expectFail: false}, |
805 | - {tag: "StringList model + header", model: objectList, header: headerFooter, expect: 170, expectFail: false}, |
806 | - {tag: "StringList model + header + footer", model: objectList, header: headerFooter, footer: headerFooter, expect: 190, expectFail: false}, |
807 | - {tag: "StringList model + header + footer + section (inline)", model: objectList, header: headerFooter, footer: headerFooter, section: section, expect: 290, expectFail: true}, |
808 | - {tag: "StringList model + header + footer + section (top)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart, expect: 190, expectFail: false}, |
809 | - {tag: "StringList model + header + footer + section (bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
810 | - {tag: "StringList model + header + footer + section (top+bottom)", model: objectList, header: headerFooter, footer: headerFooter, section: section, sectionPositioning: ViewSection.CurrentLabelAtStart | ViewSection.NextLabelAtEnd, expect: 190, expectFail: false}, |
811 | - ]; |
812 | - } |
813 | - |
814 | - function test_scrollbar_height_with_ListView(data) { |
815 | - if (data.expectFail) |
816 | - expectFail("", data.tag); |
817 | - |
818 | - // set data |
819 | - listView.model = data["model"] ? data.model : null; |
820 | - listView.header = data["header"] ? data.header : null; |
821 | - listView.footer = data["footer"] ? data.footer : null; |
822 | - listView.section.property = "label"; |
823 | - listView.section.criteria = ViewSection.FirstCharacter; |
824 | - listView.section.labelPositioning = data["sectionPositioning"] ? data.sectionPositioning : ViewSection.InlineLabels; |
825 | - listView.section.delegate = data["section"] ? data.section : null; |
826 | - scrollbar.flickableItem = listView; |
827 | - |
828 | - wait(200); |
829 | - |
830 | - compare(scrollbar.ItemStyle.delegate.contentSize, data.expect, data.tag); |
831 | - } |
832 | - |
833 | Rectangle { |
834 | id: nonFlickable |
835 | } |
836 | @@ -129,9 +74,6 @@ |
837 | |
838 | ListView { |
839 | id: listView |
840 | - delegate: Item { |
841 | - height: 15 |
842 | - } |
843 | } |
844 | |
845 | Scrollbar { |
846 | @@ -142,44 +84,4 @@ |
847 | signalName: "styleChanged" |
848 | } |
849 | } |
850 | - |
851 | - Component { |
852 | - id: headerFooter |
853 | - Item { height: 20} |
854 | - } |
855 | - |
856 | - Component { |
857 | - id: section |
858 | - Item { height: 10} |
859 | - } |
860 | - |
861 | - property var emptyModel: ListModel {} |
862 | - |
863 | - property var objectList: ListModel { |
864 | - ListElement {label: "1"} |
865 | - ListElement {label: "10"} |
866 | - ListElement {label: "100"} |
867 | - ListElement {label: "2"} |
868 | - ListElement {label: "3"} |
869 | - ListElement {label: "4"} |
870 | - ListElement {label: "5"} |
871 | - ListElement {label: "6"} |
872 | - ListElement {label: "7"} |
873 | - ListElement {label: "8"} |
874 | - } |
875 | - |
876 | - property var variantList: [ |
877 | - {"label": "1"}, |
878 | - {"label": "10"}, |
879 | - {"label": "100"}, |
880 | - {"label": "2"}, |
881 | - {"label": "3"}, |
882 | - {"label": "4"}, |
883 | - {"label": "5"}, |
884 | - {"label": "6"}, |
885 | - {"label": "7"}, |
886 | - {"label": "8"} |
887 | - ] |
888 | - |
889 | - property var stringList: ["1", "10", "100", "2", "3", "4", "5", "6", "7", "8", "9"] |
890 | } |
891 | |
892 | === removed file 'themes/Ambiance/qmltheme/ScrollSliderSizer.qml' |
893 | --- themes/Ambiance/qmltheme/ScrollSliderSizer.qml 2012-11-22 16:08:44 +0000 |
894 | +++ themes/Ambiance/qmltheme/ScrollSliderSizer.qml 1970-01-01 00:00:00 +0000 |
895 | @@ -1,55 +0,0 @@ |
896 | -/* |
897 | - * Copyright 2012 Canonical Ltd. |
898 | - * |
899 | - * This program is free software; you can redistribute it and/or modify |
900 | - * it under the terms of the GNU Lesser General Public License as published by |
901 | - * the Free Software Foundation; version 3. |
902 | - * |
903 | - * This program is distributed in the hope that it will be useful, |
904 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
905 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
906 | - * GNU Lesser General Public License for more details. |
907 | - * |
908 | - * You should have received a copy of the GNU Lesser General Public License |
909 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
910 | - */ |
911 | - |
912 | -import QtQuick 2.0 |
913 | - |
914 | -/*! |
915 | - \internal |
916 | - \qmltype ScrollSliderSizer |
917 | - \inqmlmodule Ubuntu.Components 0.1 |
918 | - \ingroup ubuntu |
919 | - \brief Scrollbar Decorator (Slider) sizer object, provides sizing and elasticity |
920 | - support for the slider |
921 | - */ |
922 | -QtObject { |
923 | - // relative (0..1) position of top and bottom |
924 | - property real positionRatio |
925 | - property real sizeRatio |
926 | - |
927 | - // max position and min size |
928 | - property real maximumPosition |
929 | - property real minimumSize |
930 | - |
931 | - // size underflow |
932 | - property real sizeUnderflow: (sizeRatio * maximumPosition) < minimumSize ? minimumSize - (sizeRatio * maximumPosition) : 0 |
933 | - |
934 | - // raw start and end position considering minimum size |
935 | - property real rawStartPosition: positionRatio * (maximumPosition - sizeUnderflow) |
936 | - property real rawEndPosition: (positionRatio + sizeRatio) * (maximumPosition - sizeUnderflow) + sizeUnderflow |
937 | - |
938 | - // overshoot amount at start and end |
939 | - property real overshootStart: rawStartPosition < 0 ? -rawStartPosition : 0 |
940 | - property real overshootEnd: rawEndPosition > maximumPosition ? rawEndPosition - maximumPosition : 0 |
941 | - |
942 | - // overshoot adjusted start and end |
943 | - property real adjustedStartPosition: rawStartPosition + overshootStart |
944 | - property real adjustedEndPosition: rawEndPosition - overshootStart - overshootEnd |
945 | - |
946 | - // final position and size of thumb |
947 | - property int position: 0.5 + (adjustedStartPosition + minimumSize > maximumPosition ? maximumPosition - minimumSize : adjustedStartPosition) |
948 | - property int size: 0.5 + ((adjustedEndPosition - position) < minimumSize ? minimumSize : (adjustedEndPosition - position)) |
949 | -} |
950 | - |
951 | |
952 | === modified file 'themes/Ambiance/qmltheme/ScrollbarDelegate.qml' |
953 | --- themes/Ambiance/qmltheme/ScrollbarDelegate.qml 2012-12-13 13:42:30 +0000 |
954 | +++ themes/Ambiance/qmltheme/ScrollbarDelegate.qml 2013-01-09 14:06:21 +0000 |
955 | @@ -24,99 +24,55 @@ |
956 | On active scrollbars, positioning is handled so that the logic updates the flickable's |
957 | X/Y content positions, which is then synched with the contentPosition by the main |
958 | element. |
959 | + |
960 | + Style properties used: |
961 | + - interactive: bool - drives the interactive behavior of the scrollbar |
962 | + - minimumSliderSize: real - specifies the minimum size of the slider |
963 | + * overlay |
964 | + - overlay: bool - true if the scrollbar is overlay type |
965 | + - overlayOpacityWhenHidden: opacity when hidden |
966 | + - overlayOpacityWhenShown: opacity when shown |
967 | + * animations - where duration and easing properties are used only |
968 | + - scrollbarFadeInAnimation: PropertyAnimation - animation used when fade in |
969 | + - scrollbarFadeOutAnimation: PropertyAnimation - animation used when fade out |
970 | + - scrollbarFadeOutPause: int - miliseconds to pause before fade out |
971 | + * behaviors - animations are used as declared |
972 | + - sliderAnimation: PropertyAnimation - animation for the slider size |
973 | + - thumbConnectorFading: PropertyAnimation - animation for the thumb connector |
974 | + - thumbFading: PropertyAnimation - animation for the thumb fading |
975 | + * other styling properties |
976 | + - color sliderColor: color for the slider |
977 | + - color thumbConnectorColor: thumb connector color |
978 | + - url forwardThumbReleased: forward thumb image when released |
979 | + - url forwardThumbPressed: forward thumb image when pressed |
980 | + - url backwardThumbReleased: backward thumb image when released |
981 | + - url backwardThumbPressed: backward thumb image when pressed |
982 | + - real scrollAreaThickness: scrollbar area thickness, the area where the |
983 | + slider, thumb and thumb-connector appear |
984 | + - real thumbConnectorMargin: margin of the thumb connector aligned to the |
985 | + thumb visuals |
986 | */ |
987 | |
988 | Item { |
989 | id: visuals |
990 | // helper properties to ease code readability |
991 | property Flickable flickableItem: item.flickableItem |
992 | - property bool interactive: item.__interactive |
993 | + property bool interactive: StyleUtils.itemStyleProperty("interactive", false) |
994 | property bool isScrollable: item.__private.scrollable && pageSize > 0.0 |
995 | && contentSize > 0.0 && contentSize > pageSize |
996 | - property bool isVertical: (item.__private.vertical) |
997 | + property bool isVertical: ScrollbarUtils.isVertical(item) |
998 | property bool frontAligned: (item.align === Qt.AlignLeading) |
999 | property bool rearAligned: (item.align === Qt.AlignTrailing) |
1000 | property bool topAligned: (item.align === Qt.AlignTop) |
1001 | property bool bottomAligned: (item.align === Qt.AlignBottom) |
1002 | |
1003 | property real pageSize: (isVertical) ? item.height : item.width |
1004 | - property real contentSize: (listView) ? |
1005 | - listView.size : |
1006 | - ((isVertical) ? item.flickableItem.contentHeight : item.flickableItem.contentWidth) |
1007 | + property real contentSize: (isVertical) ? item.flickableItem.contentHeight : item.flickableItem.contentWidth |
1008 | property real overlayOpacityWhenShown: StyleUtils.itemStyleProperty("overlayOpacityWhenShown", 0.6) |
1009 | property real overlayOpacityWhenHidden: StyleUtils.itemStyleProperty("overlayOpacityWhenHidden", 0.0) |
1010 | property bool overlay: StyleUtils.itemStyleProperty("overlay", false) && !interactive |
1011 | |
1012 | - property real contentPosition |
1013 | - property QtObject listView: logicLoader.item |
1014 | - |
1015 | - /* Removing the first row of the ListView's model will render |
1016 | - ListView.contentY invalid and therefore break the scrollbar's position. |
1017 | - This is fixable in QtQuick 2.0 thanks to the introduction of the |
1018 | - Flickable.originY property, however the property is not reliable enough. |
1019 | - Therefore we compute originY manually using the fact that |
1020 | - ListView.visibleArea.yPosition is not rendered invalid by removing the |
1021 | - first row of the ListView's model. |
1022 | - Unfortunately the result is not flawless when the ListView uses section |
1023 | - headers because ListView.visibleArea.yPosition is often slightly incorrect. |
1024 | - |
1025 | - Ref.: https://bugreports.qt-project.org/browse/QTBUG-20927 |
1026 | - https://bugreports.qt-project.org/browse/QTBUG-21358 |
1027 | - http://doc-snapshot.qt-project.org/5.0/qml-qtquick2-flickable.html#originX-prop |
1028 | - */ |
1029 | - property real originX: (listView) ? -item.flickableItem.contentX + Math.round(item.flickableItem.visibleArea.xPosition * contentSize) : item.flickableItem.originX |
1030 | - property real originY: (listView) ? -item.flickableItem.contentY + Math.round(item.flickableItem.visibleArea.yPosition * contentSize) : item.flickableItem.originY |
1031 | - |
1032 | - // common logic for Flickable and ListView to update contentPosition when Flicked |
1033 | - Connections { |
1034 | - target: item.flickableItem |
1035 | - onContentYChanged: if (isVertical) contentPosition = MathUtils.clamp(item.flickableItem.contentY - visuals.originY, 0.0, contentSize) |
1036 | - onContentXChanged: if (!isVertical) contentPosition = MathUtils.clamp(item.flickableItem.contentX - visuals.originX, 0.0, contentSize) |
1037 | - } |
1038 | - // logic for ListView |
1039 | - Component { |
1040 | - id: listViewLogic |
1041 | - Object { |
1042 | - /* ListView.contentHeight is not reliable when section headers are defined. |
1043 | - In that case we compute 'size' manually. |
1044 | - |
1045 | - Ref.: https://bugreports.qt-project.org/browse/QTBUG-17057 |
1046 | - https://bugreports.qt-project.org/browse/QTBUG-19941 |
1047 | - */ |
1048 | - property real size: sectionCounter.sectionCount * sectionHeight + itemsSize + spacingSize + headerSize + footerSize |
1049 | - property int sectionHeight: sectionCounter.sectionHeight |
1050 | - property int spacingSize: flickableItem.spacing * (flickableItem.count - 1) |
1051 | - property int itemsSize: flickableItem.count * QuickUtils.modelDelegateHeight(flickableItem.delegate, flickableItem.model) |
1052 | - property int headerSize: flickableItem.header ? flickableItem.headerItem.height : 0 |
1053 | - property int footerSize: flickableItem.footer ? flickableItem.footerItem.height : 0 |
1054 | - |
1055 | - // need to capture count change otherwise the count won't be |
1056 | - // reported for the proxy models |
1057 | - Connections { |
1058 | - target: flickableItem |
1059 | - onCountChanged: itemsSize = flickableItem.count * QuickUtils.modelDelegateHeight(flickableItem.delegate, flickableItem.model) |
1060 | - } |
1061 | - |
1062 | - ModelSectionCounter { |
1063 | - id: sectionCounter |
1064 | - view: flickableItem |
1065 | - } |
1066 | - } |
1067 | - } |
1068 | - Loader { id:logicLoader } |
1069 | - onFlickableItemChanged: { |
1070 | - if (flickableItem) { |
1071 | - if (flickableItem.hasOwnProperty("header")) { |
1072 | - // we consider Grids same as Flickables |
1073 | - if (flickableItem.hasOwnProperty("cellWidth")) { |
1074 | - logicLoader.sourceComponent = undefined; |
1075 | - } else { |
1076 | - logicLoader.sourceComponent = listViewLogic; |
1077 | - } |
1078 | - } else |
1079 | - logicLoader.sourceComponent = undefined; |
1080 | - } |
1081 | - } |
1082 | + property real minimumSliderSize: StyleUtils.itemStyleProperty("minimumSliderSize", units.gu(2)) |
1083 | |
1084 | /***************************************** |
1085 | Visuals |
1086 | @@ -135,6 +91,7 @@ |
1087 | } else |
1088 | return 'shown'; |
1089 | } |
1090 | + onStateChanged: print(state) |
1091 | |
1092 | states: [ |
1093 | State { |
1094 | @@ -168,6 +125,7 @@ |
1095 | target: visuals |
1096 | property: "opacity" |
1097 | duration: StyleUtils.itemStyleProperty("scrollbarFadeInAnimation").duration |
1098 | + easing: StyleUtils.itemStyleProperty("scrollbarFadeInAnimation").easing |
1099 | } |
1100 | }, |
1101 | Transition { |
1102 | @@ -177,6 +135,7 @@ |
1103 | target: visuals |
1104 | property: "opacity" |
1105 | duration: StyleUtils.itemStyleProperty("scrollbarFadeInAnimation").duration |
1106 | + easing: StyleUtils.itemStyleProperty("scrollbarFadeInAnimation").easing |
1107 | } |
1108 | }, |
1109 | Transition { |
1110 | @@ -187,35 +146,20 @@ |
1111 | NumberAnimation { |
1112 | target: visuals |
1113 | property: "opacity" |
1114 | - //to: overlayOpacityWhenHidden |
1115 | duration: StyleUtils.itemStyleProperty("scrollbarFadeOutAnimation").duration |
1116 | - easing.type: StyleUtils.itemStyleProperty("scrollbarFadeOutAnimation").easing.type |
1117 | + easing: StyleUtils.itemStyleProperty("scrollbarFadeOutAnimation").easing |
1118 | } |
1119 | } |
1120 | } |
1121 | ] |
1122 | |
1123 | - /* Scroll by amount pixels never overshooting */ |
1124 | - function scrollBy(amount) { |
1125 | - var destination = contentPosition + amount |
1126 | - destination += (isVertical) ? visuals.originY : visuals.originX |
1127 | - scrollAnimation.to = MathUtils.clamp(destination, 0, contentSize - pageSize) |
1128 | - scrollAnimation.restart() |
1129 | - } |
1130 | - |
1131 | - function scrollOnePageBackward() { |
1132 | - scrollBy(-pageSize) |
1133 | - } |
1134 | - |
1135 | - function scrollOnePageForward() { |
1136 | - scrollBy(pageSize) |
1137 | - } |
1138 | - |
1139 | function mapToPoint(map) |
1140 | { |
1141 | return Qt.point(map.x, map.y) |
1142 | } |
1143 | |
1144 | + SystemPalette { id: systemColors } |
1145 | + |
1146 | SmoothedAnimation { |
1147 | id: scrollAnimation |
1148 | |
1149 | @@ -229,7 +173,7 @@ |
1150 | Item { |
1151 | id: scrollbarArea |
1152 | |
1153 | - property real thickness: itemStyle.scrollAreaThickness |
1154 | + property real thickness: StyleUtils.itemStyleProperty("scrollAreaThickness", units.dp(2)) |
1155 | property real proximityThickness: (isVertical) ? item.width - thickness : item.height - thickness |
1156 | anchors { |
1157 | fill: parent |
1158 | @@ -255,6 +199,7 @@ |
1159 | enabled: isScrollable && interactive |
1160 | hoverEnabled: true |
1161 | onEntered: thumb.show(); |
1162 | + |
1163 | onPressed: mouse.accepted = false |
1164 | onClicked: mouse.accepted = false |
1165 | onReleased: mouse.accepted = false |
1166 | @@ -264,10 +209,22 @@ |
1167 | // The slider's position represents which part of the flickable is visible. |
1168 | // The slider's size represents the size the visible part relative to the |
1169 | // total size of the flickable. |
1170 | + Item { |
1171 | + id: scrollCursor |
1172 | + x: (isVertical) ? 0 : ScrollbarUtils.sliderPos(item, 0.0, item.width - scrollCursor.width) |
1173 | + y: (!isVertical) ? 0 : ScrollbarUtils.sliderPos(item, 0.0, item.height - scrollCursor.height) |
1174 | + width: (isVertical) ? scrollbarArea.thickness : ScrollbarUtils.sliderSize(item, 0.0, flickableItem.width) |
1175 | + height: (!isVertical) ? scrollbarArea.thickness : ScrollbarUtils.sliderSize(item, 0.0, flickableItem.height) |
1176 | + |
1177 | + function drag() { |
1178 | + ScrollbarUtils.dragAndClamp(item, scrollCursor, contentSize, pageSize); |
1179 | + } |
1180 | + } |
1181 | + |
1182 | Rectangle { |
1183 | id: slider |
1184 | |
1185 | - color: itemStyle.sliderColor |
1186 | + color: StyleUtils.itemStyleProperty("sliderColor", systemColors.highlight) |
1187 | |
1188 | anchors { |
1189 | left: (isVertical) ? scrollbarArea.left : undefined |
1190 | @@ -276,18 +233,10 @@ |
1191 | bottom: (!isVertical) ? scrollbarArea.bottom : undefined |
1192 | } |
1193 | |
1194 | - x: (isVertical) ? 0 : MathUtils.clampAndProject(contentPosition, 0.0, contentSize - pageSize, 0.0, item.width - slider.width) |
1195 | - y: (!isVertical) ? 0 : MathUtils.clampAndProject(contentPosition, 0.0, contentSize - pageSize, 0.0, item.height - slider.height) |
1196 | - width: (isVertical) ? scrollbarArea.thickness : sliderSizer.size |
1197 | - height: (!isVertical) ? scrollbarArea.thickness : sliderSizer.size |
1198 | - |
1199 | - ScrollSliderSizer { |
1200 | - id: sliderSizer |
1201 | - positionRatio: (isVertical) ? item.flickableItem.visibleArea.yPosition : item.flickableItem.visibleArea.xPosition |
1202 | - sizeRatio: (isVertical) ? item.flickableItem.visibleArea.heightRatio : item.flickableItem.visibleArea.widthRatio |
1203 | - maximumPosition: (isVertical) ? item.flickableItem.height : item.flickableItem.width |
1204 | - minimumSize: units.gu(2) |
1205 | - } |
1206 | + x: (isVertical) ? 0 : ScrollbarUtils.sliderPos(item, 0.0, item.width - slider.width) |
1207 | + y: (!isVertical) ? 0 : ScrollbarUtils.sliderPos(item, 0.0, item.height - slider.height) |
1208 | + width: (isVertical) ? scrollbarArea.thickness : ScrollbarUtils.sliderSize(item, minimumSliderSize, flickableItem.width) |
1209 | + height: (!isVertical) ? scrollbarArea.thickness : ScrollbarUtils.sliderSize(item, minimumSliderSize, flickableItem.height) |
1210 | |
1211 | Behavior on width { |
1212 | enabled: (!isVertical) |
1213 | @@ -301,12 +250,18 @@ |
1214 | script: StyleUtils.animate("sliderAnimation") |
1215 | } |
1216 | } |
1217 | + |
1218 | + function scroll(amount) { |
1219 | + scrollAnimation.to = ScrollbarUtils.scrollAndClamp(item, amount, 0.0, contentSize - pageSize); |
1220 | + scrollAnimation.restart(); |
1221 | + } |
1222 | } |
1223 | |
1224 | // The sliderThumbConnector ensures a visual connection between the slider and the thumb |
1225 | Rectangle { |
1226 | id: sliderThumbConnector |
1227 | |
1228 | + property real thumbConnectorMargin: StyleUtils.itemStyleProperty("thumbConnectorMargin", units.dp(3)) |
1229 | property bool isThumbAboveSlider: (isVertical) ? thumb.y < slider.y : thumb.x < slider.x |
1230 | anchors { |
1231 | left: (isVertical) ? scrollbarArea.left : (isThumbAboveSlider ? thumb.left : slider.right) |
1232 | @@ -314,12 +269,12 @@ |
1233 | top: (!isVertical) ? scrollbarArea.top : (isThumbAboveSlider ? thumb.top : slider.bottom) |
1234 | bottom: (!isVertical) ? scrollbarArea.bottom : (isThumbAboveSlider ? slider.top : thumb.bottom) |
1235 | |
1236 | - leftMargin : (isVertical) ? 0 : (isThumbAboveSlider ? itemStyle.thumbConnectorMargin : 0) |
1237 | - rightMargin : (isVertical) ? 0 : (isThumbAboveSlider ? 0 : itemStyle.thumbConnectorMargin) |
1238 | - topMargin : (!isVertical) ? 0 : (isThumbAboveSlider ? itemStyle.thumbConnectorMargin : 0) |
1239 | - bottomMargin : (!isVertical) ? 0 : (isThumbAboveSlider ? 0 : itemStyle.thumbConnectorMargin) |
1240 | + leftMargin : (isVertical) ? 0 : (isThumbAboveSlider ? thumbConnectorMargin : 0) |
1241 | + rightMargin : (isVertical) ? 0 : (isThumbAboveSlider ? 0 : thumbConnectorMargin) |
1242 | + topMargin : (!isVertical) ? 0 : (isThumbAboveSlider ? thumbConnectorMargin : 0) |
1243 | + bottomMargin : (!isVertical) ? 0 : (isThumbAboveSlider ? 0 : thumbConnectorMargin) |
1244 | } |
1245 | - color: itemStyle.thumbConnectorColor |
1246 | + color: StyleUtils.itemStyleProperty("thumbConnectorColor", "white") |
1247 | opacity: thumb.shown ? 1.0 : 0.0 |
1248 | Behavior on opacity {animation: ScriptAction {script: StyleUtils.animate("thumbConnectorFading")}} |
1249 | } |
1250 | @@ -361,46 +316,53 @@ |
1251 | } |
1252 | onClicked: { |
1253 | if (inThumbBottom) |
1254 | - scrollOnePageForward() |
1255 | + slider.scroll(pageSize) |
1256 | else if (inThumbTop) |
1257 | - scrollOnePageBackward() |
1258 | + slider.scroll(-pageSize) |
1259 | } |
1260 | |
1261 | // Dragging behaviour |
1262 | function resetDrag() { |
1263 | + thumbYStart = thumb.y |
1264 | + thumbXStart = thumb.x |
1265 | dragYStart = drag.target.y |
1266 | - thumbYStart = thumb.y |
1267 | - sliderYStart = slider.y |
1268 | dragXStart = drag.target.x |
1269 | - thumbXStart = thumb.x |
1270 | - sliderXStart = slider.x |
1271 | } |
1272 | |
1273 | - property int sliderYStart |
1274 | property int thumbYStart |
1275 | property int dragYStart |
1276 | property int dragYAmount: thumbArea.drag.target.y - thumbArea.dragYStart |
1277 | - property int sliderXStart |
1278 | property int thumbXStart |
1279 | property int dragXStart |
1280 | property int dragXAmount: thumbArea.drag.target.x - thumbArea.dragXStart |
1281 | drag { |
1282 | - target: Item {} |
1283 | + target: scrollCursor |
1284 | axis: (isVertical) ? Drag.YAxis : Drag.XAxis |
1285 | - filterChildren: true |
1286 | - onActiveChanged: if (drag.active) resetDrag() |
1287 | + minimumY: 0 |
1288 | + maximumY: flickableItem.height - scrollCursor.height |
1289 | + minimumX: 0 |
1290 | + maximumX: flickableItem.width - scrollCursor.width |
1291 | + onActiveChanged: { |
1292 | + if (drag.active) resetDrag() |
1293 | + } |
1294 | } |
1295 | - // update flickableItem's and thumb's position |
1296 | - // cannot use Binding as there would be a binding loop |
1297 | + // update thumb position |
1298 | onDragYAmountChanged: { |
1299 | - var pos = MathUtils.clampAndProject(thumbArea.sliderYStart + thumbArea.dragYAmount, 0.0, item.height - slider.height, 0.0, contentSize - pageSize); |
1300 | - item.flickableItem.contentY = MathUtils.clamp(pos + visuals.originY, 0.0, contentSize - pageSize); |
1301 | - thumb.y = MathUtils.clamp(thumbArea.thumbYStart + thumbArea.dragYAmount, 0, thumb.maximumPos); |
1302 | + if (drag.active) { |
1303 | + thumb.y = MathUtils.clamp(thumbArea.thumbYStart + thumbArea.dragYAmount, 0, thumb.maximumPos); |
1304 | + } |
1305 | } |
1306 | onDragXAmountChanged: { |
1307 | - var pos = MathUtils.clampAndProject(thumbArea.sliderXStart + thumbArea.dragXAmount, 0.0, item.width - slider.width, 0.0, contentSize - pageSize); |
1308 | - item.flickableItem.contentX = MathUtils.clamp(pos + visuals.originX, 0.0, contentSize - pageSize); |
1309 | - thumb.x = MathUtils.clamp(thumbArea.thumbXStart + thumbArea.dragXAmount, 0, thumb.maximumPos); |
1310 | + if (drag.active) { |
1311 | + thumb.x = MathUtils.clamp(thumbArea.thumbXStart + thumbArea.dragXAmount, 0, thumb.maximumPos); |
1312 | + } |
1313 | + } |
1314 | + |
1315 | + // drag slider and content to the proper position |
1316 | + onPositionChanged: { |
1317 | + if (pressedButtons == Qt.LeftButton) { |
1318 | + scrollCursor.drag() |
1319 | + } |
1320 | } |
1321 | } |
1322 | |
1323 | @@ -477,17 +439,21 @@ |
1324 | opacity: shown ? (thumbArea.containsMouse || thumbArea.drag.active ? 1.0 : 0.5) : 0.0 |
1325 | Behavior on opacity {animation: ScriptAction {script: StyleUtils.animate("thumbFading")}} |
1326 | |
1327 | + property url backwardPressed: StyleUtils.itemStyleProperty("backwardThumbPressed", "") |
1328 | + property url backwardReleased: StyleUtils.itemStyleProperty("backwardThumbReleased", "") |
1329 | + property url forwardPressed: StyleUtils.itemStyleProperty("forwardThumbPressed", "") |
1330 | + property url forwardReleased: StyleUtils.itemStyleProperty("forwardThumbReleased", "") |
1331 | Flow { |
1332 | // disable mirroring as thumbs are placed in the same way no matter of RTL or LTR |
1333 | LayoutMirroring.enabled: false |
1334 | flow: (isVertical) ? Flow.TopToBottom : Flow.LeftToRight |
1335 | Image { |
1336 | id: thumbTop |
1337 | - source: thumbArea.inThumbTop && thumbArea.pressed ? itemStyle.backwardThumbPressed : itemStyle.backwardThumbReleased |
1338 | + source: thumbArea.inThumbTop && thumbArea.pressed ? thumb.backwardPressed : thumb.backwardReleased |
1339 | } |
1340 | Image { |
1341 | id: thumbBottom |
1342 | - source: thumbArea.inThumbBottom && thumbArea.pressed ? itemStyle.forwardThumbPressed : itemStyle.forwardThumbReleased |
1343 | + source: thumbArea.inThumbBottom && thumbArea.pressed ? thumb.forwardPressed : thumb.forwardReleased |
1344 | } |
1345 | } |
1346 | } |
1347 | |
1348 | === removed file 'themes/Ambiance/qmltheme/ScrollbarStyle.qml' |
1349 | --- themes/Ambiance/qmltheme/ScrollbarStyle.qml 2012-11-29 11:41:29 +0000 |
1350 | +++ themes/Ambiance/qmltheme/ScrollbarStyle.qml 1970-01-01 00:00:00 +0000 |
1351 | @@ -1,54 +0,0 @@ |
1352 | -/* |
1353 | - * Copyright 2012 Canonical Ltd. |
1354 | - * |
1355 | - * This program is free software; you can redistribute it and/or modify |
1356 | - * it under the terms of the GNU Lesser General Public License as published by |
1357 | - * the Free Software Foundation; version 3. |
1358 | - * |
1359 | - * This program is distributed in the hope that it will be useful, |
1360 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1361 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1362 | - * GNU Lesser General Public License for more details. |
1363 | - * |
1364 | - * You should have received a copy of the GNU Lesser General Public License |
1365 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1366 | - */ |
1367 | - |
1368 | -import QtQuick 2.0 |
1369 | - |
1370 | -QtObject { |
1371 | - /*! |
1372 | - This property drives the interactive behavior of the scrollbar |
1373 | - */ |
1374 | - property bool interactive |
1375 | - |
1376 | - /*! |
1377 | - Overlay: when set, opacity specified in overlayOpacity will be used; |
1378 | - when no overlay is specuified, the scrollbar is 100% visible |
1379 | - */ |
1380 | - property bool overlay |
1381 | - property var overlayOpacityWhenHidden |
1382 | - property var overlayOpacityWhenShown |
1383 | - |
1384 | - /*! |
1385 | - Scrollbar animations |
1386 | - */ |
1387 | - property PropertyAnimation scrollbarFadeInAnimation |
1388 | - property var scrollbarFadeOutPause |
1389 | - property PropertyAnimation scrollbarFadeOutAnimation |
1390 | - /*! |
1391 | - Scrollbar element animations. |
1392 | - */ |
1393 | - property PropertyAnimation sliderAnimation |
1394 | - property PropertyAnimation thumbConnectorFading |
1395 | - property PropertyAnimation thumbFading |
1396 | - property color sliderColor |
1397 | - property color thumbConnectorColor |
1398 | - property url forwardThumbReleased |
1399 | - property url forwardThumbPressed |
1400 | - property url backwardThumbReleased |
1401 | - property url backwardThumbPressed |
1402 | - property real sensingAreaThickness |
1403 | - property real scrollAreaThickness |
1404 | - property real thumbConnectorMargin |
1405 | -} |
1406 | |
1407 | === modified file 'themes/Ambiance/qmltheme/default.qmltheme' |
1408 | --- themes/Ambiance/qmltheme/default.qmltheme 2012-12-20 09:06:37 +0000 |
1409 | +++ themes/Ambiance/qmltheme/default.qmltheme 2013-01-09 14:06:21 +0000 |
1410 | @@ -19,7 +19,7 @@ |
1411 | @qml-mapping(.new-tabs, NewTabsStyle, NewTabsDelegate); |
1412 | @qml-mapping(.tab-button, TabButtonStyle, TabButtonDelegate); |
1413 | @qml-mapping(.custom-button, ButtonStyle, ButtonDelegate); |
1414 | -@qml-mapping(.scrollbar, ScrollbarStyle, ScrollbarDelegate); |
1415 | +@qml-mapping(.scrollbar, , ScrollbarDelegate); |
1416 | @qml-mapping (.popover-foreground, , PopoverForegroundDelegate); |
1417 | @qml-mapping (.sheet-foreground, , SheetForegroundDelegate); |
1418 | @qml-mapping (.dialog-foreground, , DialogForegroundDelegate); |
1419 | @@ -176,7 +176,6 @@ |
1420 | backwardThumbPressed: (item.align === Qt.AlignLeading || item.align === Qt.AlignTrailing) ? url("artwork/ScrollbarTopPressed.png") : url("artwork/ScrollbarLeftPressed.png"); |
1421 | forwardThumbReleased: (item.align === Qt.AlignLeading || item.align === Qt.AlignTrailing) ? url("artwork/ScrollbarBottomIdle.png") : url("artwork/ScrollbarRightIdle.png"); |
1422 | forwardThumbPressed: (item.align === Qt.AlignLeading || item.align === Qt.AlignTrailing) ? url("artwork/ScrollbarBottomPressed.png") : url("artwork/ScrollbarRightPressed.png"); |
1423 | - sensingAreaThickness: units.gu(4); |
1424 | scrollAreaThickness: units.dp(2); |
1425 | thumbConnectorMargin: units.dp(3); |
1426 | sliderColor: "#fc7134"; |
1427 | @@ -278,3 +277,7 @@ |
1428 | color: "white"; |
1429 | weight: Font.Bold; |
1430 | } |
1431 | + |
1432 | +.scrollbar { |
1433 | + interactive: true |
1434 | +} |
demos/ScrollBar s.qml sCurrentItem: true
+ highlightFollow
+ highlightRangeMode: ListView.ApplyRange
Why needed?