Merge lp:~nik90/unav/revamp-grid-view-code into lp:unav

Proposed by Nekhelesh Ramananthan
Status: Merged
Merged at revision: 15
Proposed branch: lp:~nik90/unav/revamp-grid-view-code
Merge into: lp:unav
Prerequisite: lp:~costales/unav/submenu-for-nearbypage
Diff against target: 564 lines (+209/-251)
5 files modified
qml/CustomGridView.qml (+53/-0)
qml/Nearby.qml (+28/-84)
qml/RoutePage.qml (+37/-83)
qml/SharePage.qml (+28/-84)
qml/components/GridDelegate.qml (+63/-0)
To merge this branch: bzr merge lp:~nik90/unav/revamp-grid-view-code
Reviewer Review Type Date Requested Status
costales Approve
Review via email: mp+290704@code.launchpad.net

This proposal supersedes a proposal from 2016-04-01.

Commit message

- Converted Grid and GridDelegate into generic reuseable components
- Made grid text translatable

Description of the change

This branch essentially refactors the grid view code. I noticed that the same code was used in SharePage.qml, RoutePage.qml and NearBy.qml. So I made it into a generic component and removed a lot of duplicate code.

The same situation applied to the grid delegate which I made into a separate reusable component.

Fixed a bug in trunk where grid view text was not translatable.

To post a comment you must log in.
lp:~nik90/unav/revamp-grid-view-code updated
29. By Nekhelesh Ramananthan

Small tweak based on phone testing

30. By Nekhelesh Ramananthan

Fixed missing Popup import in Share page

Revision history for this message
costales (costales) wrote :

Working perfect for me.

@Joerg? :)

review: Approve
Revision history for this message
JkB (joergberroth) wrote :

Cannot test it right now, but i believe in you two.... :-)

m Freitag, 1. April 2016 15:43:16 CEST schrieb costales
<email address hidden>:
> Review: Approve
>
> Working perfect for me.
>
> @Joerg? :)

--
Versandt, mit Dekko von meinem Ubuntu-Gerät

Revision history for this message
costales (costales) wrote :

Merged then! :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'qml/CustomGridView.qml'
2--- qml/CustomGridView.qml 1970-01-01 00:00:00 +0000
3+++ qml/CustomGridView.qml 2016-04-01 11:46:32 +0000
4@@ -0,0 +1,53 @@
5+/*
6+ * Copyright (C) 2016 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU General Public License version 3, as published
10+ * by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful, but
13+ * WITHOUT ANY WARRANTY; without even the implied warranties of
14+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+ * PURPOSE. See the GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License along
18+ * with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+import QtQuick 2.4
22+
23+/*
24+ This component provide a grid view similar to the ubuntu settings app. It was adapted from the ubuntu-settings-app found at
25+ http://bazaar.launchpad.net/~system-settings-touch/ubuntu-system-settings/trunk/view/head:/src/qml/CategoryGrid.qml
26+*/
27+
28+Grid {
29+ id: gridView
30+
31+ // Public APIs
32+ property alias model: repeater.model
33+ property alias delegate: repeater.delegate
34+
35+ //from system-settings (lp:ubuntu-system-settings)
36+ property int itemWidth: units.gu(12)
37+
38+ // The amount of whitespace, including column spacing
39+ property int space: Math.min(units.gu(5), parent.width - columns * itemWidth)
40+
41+ // The column spacing is 1/n of the left/right margins
42+ property int n: 1
43+
44+ rowSpacing: units.gu(6)
45+ columnSpacing: space / ((2 * n) + (columns - 1))
46+ width: (columns * itemWidth) + columnSpacing * (columns - 1)
47+
48+ columns: {
49+ var items = Math.floor(parent.width / itemWidth)
50+ var count = repeater.count
51+ return count < items ? count : items
52+ }
53+
54+ Repeater {
55+ id: repeater
56+ }
57+}
58
59=== modified file 'qml/Nearby.qml'
60--- qml/Nearby.qml 2016-04-01 11:46:32 +0000
61+++ qml/Nearby.qml 2016-04-01 11:46:32 +0000
62@@ -17,8 +17,7 @@
63
64 import QtQuick 2.4
65 import Ubuntu.Components 1.3
66-import Ubuntu.Components.Popups 1.3
67-import Ubuntu.Components.ListItems 1.3 as ListItem
68+import "components"
69
70 Page {
71 id: nearbyPage
72@@ -31,7 +30,6 @@
73 iconName: "back"
74 text: i18n.tr("Back")
75 shortcut: "Escape"
76- enabled: header === standardHeader
77 onTriggered: {
78 mainPageStack.pop(nearbyPage)
79 mainPageStack.push(Qt.resolvedUrl("RoutePage.qml"))
80@@ -41,98 +39,44 @@
81
82 Flickable {
83 id: flickable
84- anchors.fill: parent
85- anchors.topMargin: units.gu(6)
86+
87+ anchors { fill: parent; topMargin: units.gu(4); margins: units.gu(2) }
88 height: contentItem.childrenRect.height
89 boundsBehavior: (contentHeight > nearbyPage.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
90
91 ListModel {
92 id: nearbyPageModel
93- ListElement {section: 0 ; text: "Current"; source: "../nav/img/pages/share/current.svg" }
94- ListElement {section: 1 ; text: "Destination"; source: "../nav/img/pages/share/destination.svg"}
95- ListElement {section: 2 ; text: "From Map"; source: "../nav/img/pages/share/fromMap.svg" }
96+
97+ Component.onCompleted: initialize()
98+
99+ function initialize() {
100+ nearbyPageModel.append({mode: "CURRENT", text: i18n.tr("Current"), source: "../nav/img/pages/share/current.svg", visible: (mainPageStack.center_onpos !== 0 && mainPageStack.currentLat !== "null" && mainPageStack.currentLng !== "null") })
101+ nearbyPageModel.append({mode: "DESTINATION", text: i18n.tr("Destination"), source: "../nav/img/pages/share/destination.svg", visible: (mainPageStack.routeState !== 'no' && mainPageStack.endLat !== "null" && mainPageStack.endLng !== "null") })
102+ nearbyPageModel.append({mode: "MAP", text: i18n.tr("From Map"), source: "../nav/img/pages/share/fromMap.svg", visible: true })
103+ }
104 }
105
106- Grid {
107+ CustomGridView {
108 id: nearbyPageGrid
109- //from system-settings (lp:ubuntu-system-settings)
110- property int itemWidth: units.gu(14)
111- // The amount of whitespace, including column spacing
112- property int space: parent.width - columns * itemWidth
113- // The column spacing is 1/n of the left/right margins
114- property int n: 1
115-
116- columnSpacing: space / ((2 * n) + (columns - 1))
117- rowSpacing: units.gu(6)
118- width: (columns * itemWidth) + columnSpacing * (columns - 1)
119- anchors.horizontalCenter: parent.horizontalCenter
120- columns: {
121- var items = Math.floor(parent.width / itemWidth)
122- var count = repeater.count
123- return count < items ? count : items
124- }
125-
126- Repeater {
127- id: repeater
128- model: nearbyPageModel
129- delegate: Component {
130- id: buttonComponent
131- AbstractButton {
132- id: button
133- width: col.width
134- visible: ((model.section === 0 && (mainPageStack.center_onpos !== 0 && mainPageStack.currentLat !== "null" && mainPageStack.currentLng !== "null")) ||
135- (model.section === 1 && (mainPageStack.routeState !== 'no' && mainPageStack.endLat !== "null" && mainPageStack.endLng !== "null")) ||
136- (model.section === 2))
137- onClicked: {
138- mainPageStack.pop(nearbyPage);
139- if (model.text === "Current") {
140- mainPageStack.push(Qt.resolvedUrl("./PoiPage.qml"), {"fromPage": "Nearby.qml", "lat": mainPageStack.currentLat, "lng": mainPageStack.currentLng})
141- } else if (model.text === "Destination") {
142- mainPageStack.push(Qt.resolvedUrl("./PoiPage.qml"), {"fromPage": "Nearby.qml", "lat": mainPageStack.endLat, "lng": mainPageStack.endLng})
143- }
144- }
145-
146- height: col.height
147-
148- Column {
149- id: col
150- width: units.gu (14)
151- height: units.gu (8)
152- anchors.left: parent.left
153- anchors.right: parent.right
154-
155- Icon {
156- id: icon
157- anchors.horizontalCenter: parent.horizontalCenter
158- width: height
159- height: units.gu(4)
160- source: model.source
161- }
162-
163- Label {
164- anchors.horizontalCenter: parent.horizontalCenter
165- anchors.bottom: parent.bottom
166- text: i18n.tr(model.text)
167- width: col.width
168- horizontalAlignment: Text.AlignHCenter
169- fontSize: "small"
170- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
171- }
172- }
173-
174- UbuntuShape {
175- z: -1
176- visible: button.pressed
177- anchors{
178- fill: col
179- margins: -units.gu(0.25)
180- }
181- backgroundColor: UbuntuColors.darkGrey
182- opacity: 0.15
183- }
184+
185+ model: nearbyPageModel
186+ delegate: GridDelegate {
187+ id: delegate
188+
189+ visible: model.visible
190+ title: model.text
191+ icon: model.source
192+
193+ onClicked: {
194+ mainPageStack.pop(nearbyPage);
195+ if (model.mode === "CURRENT") {
196+ mainPageStack.push(Qt.resolvedUrl("./PoiPage.qml"), {"fromPage": "Nearby.qml", "lat": mainPageStack.currentLat, "lng": mainPageStack.currentLng})
197+ } else if (model.mode === "DESTINATION") {
198+ mainPageStack.push(Qt.resolvedUrl("./PoiPage.qml"), {"fromPage": "Nearby.qml", "lat": mainPageStack.endLat, "lng": mainPageStack.endLng})
199 }
200 }
201 }
202 }
203 }
204 }
205+
206
207=== modified file 'qml/RoutePage.qml'
208--- qml/RoutePage.qml 2016-04-01 11:46:32 +0000
209+++ qml/RoutePage.qml 2016-04-01 11:46:32 +0000
210@@ -17,6 +17,7 @@
211
212 import QtQuick 2.4
213 import Ubuntu.Components 1.3
214+import "components"
215
216 Page {
217 id: routePage
218@@ -31,97 +32,50 @@
219 Flickable {
220 id: flickable
221
222- anchors { fill: parent; margins: units.gu(2); topMargin: units.gu(6) }
223+ anchors { fill: parent; margins: units.gu(2); topMargin: units.gu(4) }
224 height: contentItem.childrenRect.height
225 boundsBehavior: (contentHeight > routePage.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
226
227 ListModel {
228 id: routePageModel
229- ListElement {section: 0 ; text: "Search"; iconName: "../nav/img/pages/route/search.svg" }
230- ListElement {section: 0 ; text: "Favorites"; iconName: "../nav/img/pages/route/favorites.svg" }
231- ListElement {section: 0 ; text: "Nearby"; iconName: "../nav/img/pages/route/nearby.svg"}
232- ListElement {section: 0 ; text: "Coordinates"; iconName: "../nav/img/pages/route/coordinates.svg"}
233- ListElement {section: 0 ; text: "Share"; iconName: "../nav/img/pages/route/share.svg"}
234- ListElement {section: 1 ; text: "Cancel Route"; iconName: "../nav/img/pages/route/cancel.svg" }
235+ Component.onCompleted: initialize()
236+
237+ function initialize() {
238+ routePageModel.append({mode: "SEARCH", text: i18n.tr("Search"), source: "../nav/img/pages/route/search.svg", visible: true})
239+ routePageModel.append({mode: "FAVORITES", text: i18n.tr("Favorites"), source: "../nav/img/pages/route/favorites.svg", visible: true})
240+ routePageModel.append({mode: "NEARBY", text: i18n.tr("Nearby"), source: "../nav/img/pages/route/nearby.svg", visible: true})
241+ routePageModel.append({mode: "COORDINATES", text: i18n.tr("Coordinates"), source: "../nav/img/pages/route/coordinates.svg", visible: true})
242+ routePageModel.append({mode: "SHARE", text: i18n.tr("Share"), source: "../nav/img/pages/route/share.svg", visible: true})
243+ routePageModel.append({mode: "CANCEL", text: i18n.tr("Cancel Route"), source: "../nav/img/pages/route/cancel.svg", visible: mainPageStack.routeState !== 'no'})
244+ }
245 }
246
247- Grid {
248+ CustomGridView {
249 id: routePageGrid
250- //from system-settings (lp:ubuntu-system-settings)
251- property int itemWidth: units.gu(14)
252- // The amount of whitespace, including column spacing
253- property int space: parent.width - columns * itemWidth
254- // The column spacing is 1/n of the left/right margins
255- property int n: 1
256-
257- columnSpacing: space / ((2 * n) + (columns - 1))
258- rowSpacing: units.gu(6)
259- width: (columns * itemWidth) + columnSpacing * (columns - 1)
260- anchors.horizontalCenter: parent.horizontalCenter
261- columns: {
262- var items = Math.floor(parent.width / itemWidth)
263- var count = repeater.count
264- return count < items ? count : items
265- }
266-
267- Repeater {
268- id: repeater
269-
270- model: routePageModel
271-
272- delegate: AbstractButton {
273- id: button
274-
275- width: col.width
276- height: col.height
277- visible: (model.section === 0 || (model.section === 1 && mainPageStack.routeState !== 'no'))
278-
279- UbuntuShape {
280- opacity: 0.15
281- visible: button.pressed
282- anchors { fill: col; margins: -units.gu(1) }
283- backgroundColor: UbuntuColors.darkGrey
284- }
285-
286- Column {
287- id: col
288-
289- spacing: units.gu(2)
290- width: units.gu (14)
291-
292- Icon {
293- width: height
294- height: units.gu(4)
295- source: model.iconName
296- anchors.horizontalCenter: parent.horizontalCenter
297- }
298-
299- Label {
300- width: col.width
301- fontSize: "small"
302- text: i18n.tr(model.text)
303- horizontalAlignment: Text.AlignHCenter
304- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
305- anchors.horizontalCenter: parent.horizontalCenter
306- }
307- }
308-
309- onClicked: {
310- mainPageStack.pop(routePage)
311- if (model.text === "Favorites") {
312- mainPageStack.push(Qt.resolvedUrl("FavoritesPage.qml"))
313- } else if (model.text === "Search") {
314- mainPageStack.push(Qt.resolvedUrl("SearchPage.qml"))
315- } else if (model.text === "Nearby") {
316- mainPageStack.push(Qt.resolvedUrl("Nearby.qml"))
317- } else if (model.text === "Coordinates") {
318- mainPageStack.push(Qt.resolvedUrl("Coordinate.qml"))
319- } else if (model.text === "Share") {
320- mainPageStack.push(Qt.resolvedUrl("SharePage.qml"))
321- } else if (model.text === "Cancel Route") {
322- mainPageStack.routeState = 'no';
323- mainPageStack.executeJavaScript("click_cancel_route();")
324- }
325+
326+ model: routePageModel
327+ delegate: GridDelegate {
328+ id: delegate
329+
330+ title: model.text
331+ icon: model.source
332+ visible: model.visible
333+
334+ onClicked: {
335+ mainPageStack.pop(routePage)
336+ if (model.mode === "FAVORITES") {
337+ mainPageStack.push(Qt.resolvedUrl("FavoritesPage.qml"))
338+ } else if (model.mode === "SEARCH") {
339+ mainPageStack.push(Qt.resolvedUrl("SearchPage.qml"))
340+ } else if (model.mode === "NEARBY") {
341+ mainPageStack.push(Qt.resolvedUrl("Nearby.qml"))
342+ } else if (model.mode === "COORDINATES") {
343+ mainPageStack.push(Qt.resolvedUrl("Coordinate.qml"))
344+ } else if (model.mode === "SHARE") {
345+ mainPageStack.push(Qt.resolvedUrl("SharePage.qml"))
346+ } else if (model.mode === "CANCEL") {
347+ mainPageStack.routeState = 'no';
348+ mainPageStack.executeJavaScript("click_cancel_route();")
349 }
350 }
351 }
352
353=== modified file 'qml/SharePage.qml'
354--- qml/SharePage.qml 2016-04-01 11:46:32 +0000
355+++ qml/SharePage.qml 2016-04-01 11:46:32 +0000
356@@ -18,7 +18,7 @@
357 import QtQuick 2.4
358 import Ubuntu.Components 1.3
359 import Ubuntu.Components.Popups 1.3
360-import Ubuntu.Components.ListItems 1.3 as ListItem
361+import "components"
362
363 Page {
364 id: sharePage
365@@ -31,7 +31,6 @@
366 iconName: "back"
367 text: i18n.tr("Back")
368 shortcut: "Escape"
369- enabled: header === standardHeader
370 onTriggered: {
371 mainPageStack.pop(sharePage)
372 mainPageStack.push(Qt.resolvedUrl("RoutePage.qml"))
373@@ -41,96 +40,41 @@
374
375 Flickable {
376 id: flickable
377- anchors.fill: parent
378- anchors.topMargin: units.gu(6)
379+
380+ anchors { fill: parent; topMargin: units.gu(4); margins: units.gu(2) }
381 height: contentItem.childrenRect.height
382 boundsBehavior: (contentHeight > sharePage.height) ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
383
384 ListModel {
385 id: sharePageModel
386- ListElement {section: 0 ; text: "Current"; source: "../nav/img/pages/share/current.svg" }
387- ListElement {section: 1 ; text: "Destination"; source: "../nav/img/pages/share/destination.svg"}
388- ListElement {section: 2 ; text: "From Map"; source: "../nav/img/pages/share/fromMap.svg" }
389+
390+ Component.onCompleted: initialize()
391+
392+ function initialize() {
393+ sharePageModel.append({mode: "CURRENT", text: i18n.tr("Current"), source: "../nav/img/pages/share/current.svg", visible: (mainPageStack.center_onpos !== 0 && mainPageStack.currentLat !== "null" && mainPageStack.currentLng !== "null") })
394+ sharePageModel.append({mode: "DESTINATION", text: i18n.tr("Destination"), source: "../nav/img/pages/share/destination.svg", visible: (mainPageStack.routeState !== 'no' && mainPageStack.endLat !== "null" && mainPageStack.endLng !== "null") })
395+ sharePageModel.append({mode: "MAP", text: i18n.tr("From Map"), source: "../nav/img/pages/share/fromMap.svg", visible: true })
396+ }
397 }
398
399- Grid {
400+ CustomGridView {
401 id: sharePageGrid
402- //from system-settings (lp:ubuntu-system-settings)
403- property int itemWidth: units.gu(14)
404- // The amount of whitespace, including column spacing
405- property int space: parent.width - columns * itemWidth
406- // The column spacing is 1/n of the left/right margins
407- property int n: 1
408-
409- columnSpacing: space / ((2 * n) + (columns - 1))
410- rowSpacing: units.gu(6)
411- width: (columns * itemWidth) + columnSpacing * (columns - 1)
412- anchors.horizontalCenter: parent.horizontalCenter
413- columns: {
414- var items = Math.floor(parent.width / itemWidth)
415- var count = repeater.count
416- return count < items ? count : items
417- }
418-
419- Repeater {
420- id: repeater
421- model: sharePageModel
422- delegate: Component {
423- id: buttonComponent
424- AbstractButton {
425- id: button
426- width: col.width
427- visible: ((model.section === 0 && (mainPageStack.center_onpos !== 0 && mainPageStack.currentLat !== "null" && mainPageStack.currentLng !== "null")) ||
428- (model.section === 1 && (mainPageStack.routeState !== 'no' && mainPageStack.endLat !== "null" && mainPageStack.endLng !== "null")) ||
429- (model.section === 2))
430- onClicked: {
431- if (model.text === "Current") {
432- PopupUtils.open(Qt.resolvedUrl("./Share.qml"), navApp, {"lat": mainPageStack.currentLat, "lon": mainPageStack.currentLng})
433- } else if (model.text === "Destination") {
434- PopupUtils.open(Qt.resolvedUrl("./Share.qml"), navApp, {"lat": mainPageStack.endLat, "lon": mainPageStack.endLng})
435- } else if (model.text === "From Map") {
436- mainPageStack.pop(sharePage); // Show map
437- }
438- }
439-
440- height: col.height
441-
442- Column {
443- id: col
444- width: units.gu (14)
445- height: units.gu (8)
446- anchors.left: parent.left
447- anchors.right: parent.right
448-
449- Icon {
450- id: icon
451- anchors.horizontalCenter: parent.horizontalCenter
452- width: height
453- height: units.gu(4)
454- source: model.source
455- }
456-
457- Label {
458- anchors.horizontalCenter: parent.horizontalCenter
459- anchors.bottom: parent.bottom
460- text: i18n.tr(model.text)
461- width: col.width
462- horizontalAlignment: Text.AlignHCenter
463- fontSize: "small"
464- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
465- }
466- }
467-
468- UbuntuShape {
469- z: -1
470- visible: button.pressed
471- anchors{
472- fill: col
473- margins: -units.gu(0.25)
474- }
475- backgroundColor: UbuntuColors.darkGrey
476- opacity: 0.15
477- }
478+
479+ model: sharePageModel
480+ delegate: GridDelegate {
481+ id: delegate
482+
483+ title: model.text
484+ icon: model.source
485+ visible: model.visible
486+
487+ onClicked: {
488+ if (model.mode === "CURRENT") {
489+ PopupUtils.open(Qt.resolvedUrl("./Share.qml"), navApp, {"lat": mainPageStack.currentLat, "lon": mainPageStack.currentLng})
490+ } else if (model.model === "DESTINATION") {
491+ PopupUtils.open(Qt.resolvedUrl("./Share.qml"), navApp, {"lat": mainPageStack.endLat, "lon": mainPageStack.endLng})
492+ } else if (model.mode === "MAP") {
493+ mainPageStack.pop(sharePage); // Show map
494 }
495 }
496 }
497
498=== added file 'qml/components/GridDelegate.qml'
499--- qml/components/GridDelegate.qml 1970-01-01 00:00:00 +0000
500+++ qml/components/GridDelegate.qml 2016-04-01 11:46:32 +0000
501@@ -0,0 +1,63 @@
502+/*
503+ * uNav http://launchpad.net/unav
504+ * Copyright (C) 2016 Nekhelesh Ramananthan https://launchpad.net/~nik90
505+ *
506+ * uNav is free software; you can redistribute it and/or modify
507+ * it under the terms of the GNU General Public License as published by
508+ * the Free Software Foundation; either version 3 of the License, or
509+ * (at your option) any later version.
510+ *
511+ * uNav is distributed in the hope that it will be useful,
512+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
513+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
514+ * GNU General Public License for more details.
515+ */
516+
517+import QtQuick 2.4
518+import Ubuntu.Components 1.3
519+
520+AbstractButton {
521+ id: gridDelegate
522+
523+ // Public APIs
524+ property string title
525+ property alias icon: _icon.source
526+
527+ width: delegateColumn.width
528+ height: delegateColumn.height
529+
530+ Loader {
531+ sourceComponent: gridDelegate.pressed ? backgroundComponent : undefined
532+ anchors { fill: delegateColumn; margins: -units.gu(1) }
533+ }
534+
535+ Component {
536+ id: backgroundComponent
537+ UbuntuShape {
538+ opacity: 0.15
539+ backgroundColor: UbuntuColors.darkGrey
540+ }
541+ }
542+
543+ Column {
544+ id: delegateColumn
545+
546+ width: units.gu(12)
547+ spacing: units.gu(2)
548+
549+ Icon {
550+ id: _icon
551+ width: height
552+ height: units.gu(4)
553+ anchors.horizontalCenter: parent.horizontalCenter
554+ }
555+
556+ Label {
557+ width: parent.width
558+ textSize: Label.Small
559+ text: title
560+ horizontalAlignment: Text.AlignHCenter
561+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
562+ }
563+ }
564+}

Subscribers

People subscribed via source and target branches