on xenial help sign in
improve listview animations and handling
commit0bcac194ea2206d7123c417413cc551423f97987 authorMichael Zanetti <michael.zanetti@canonical.com> dateThu 16 Mar 2017, 17:31 (10 years ago) parent6e2f048
2 files changed+278−253
qml/Stage/Spread/ScreensAndWorkspaces.qml+5−2 qml/Stage/Spread/Workspaces.qml+273−251
qml/Stage/Spread/ScreensAndWorkspaces.qml+5−2
@@ -106,10 +106,13 @@ Item {
106106 id: workspaces
107107 height: parent.height - header.height - units.gu(2)
108108 width: {
109+ var width = 0;
109110 if (screensProxy.count == 1) {
110− return Math.min(implicitWidth, root.width - units.gu(8));
111+ width = Math.min(implicitWidth, root.width - units.gu(8));
112+ } else {
113+ width = Math.min(implicitWidth, model.screen.active ? root.width - units.gu(48) : units.gu(40))
111114 }
112− return Math.min(implicitWidth, model.screen.active ? root.width - units.gu(48) : units.gu(40))
115+ return Math.max(workspaces.minimumWidth, width);
113116 }
114117
115118 Behavior on width { UbuntuNumberAnimation {} }
qml/Stage/Spread/Workspaces.qml+273−251
@@ -7,6 +7,10 @@ import "../../Components"
77Item {
88 id: root
99 implicitWidth: listView.contentWidth
10+ readonly property int minimumWidth: {
11+ var count = Math.min(3, listView.count);
12+ return listView.itemWidth * count + listView.spacing * (count - 1)
13+ }
1014
1115 property QtObject screen: null
1216 property alias workspaceModel: listView.model
@@ -71,309 +75,327 @@ Item {
7175 }
7276 }
7377
74− ListView {
75− id: listView
78+ Item {
79+ // We need to clip the listview as it has left/right margins and it would
80+ // overlap with items next to it and eat mouse input. However, we can't
81+ // just clip at the actual bounds as the delegates have the close button
82+ // on hover which reaches a bit outside, so lets some margins for the clipping
7683 anchors.fill: parent
77− anchors.leftMargin: -itemWidth
78− anchors.rightMargin: -itemWidth
79− boundsBehavior: Flickable.StopAtBounds
80−
81− orientation: ListView.Horizontal
82− spacing: units.gu(1)
83− leftMargin: itemWidth
84− rightMargin: itemWidth
85−
86− property var screenSize: screen.availableModes[screen.currentModeIndex].size
87− property int itemWidth: height * screenSize.width / screenSize.height
88− property int foldingAreaWidth: units.gu(10)
89− property real realContentX: contentX - originX + leftMargin
90− property int dropItemIndex: -1
91− property int hoveredWorkspaceIndex: -1
92− property string hoveredHalf: "" // left or right
93−
94− function getDropIndex(drag) {
95− var coords = mapToItem(listView.contentItem, drag.x, drag.y)
96− var index = Math.floor((drag.x + listView.realContentX) / (listView.itemWidth + listView.spacing));
97− if (index < 0) index = 0;
98− var upperLimit = dropItemIndex == -1 ? listView.count : listView.count - 1
99− if (index > upperLimit) index = upperLimit;
100− return index;
101− }
84+ anchors.margins: -units.gu(2)
85+ clip: true
86+
87+ ListView {
88+ id: listView
89+ anchors {
90+ fill: parent
91+ topMargin: -parent.anchors.margins
92+ bottomMargin: -parent.anchors.margins
93+ leftMargin: -itemWidth - parent.anchors.margins
94+ rightMargin: -itemWidth - parent.anchors.margins
95+ }
96+ boundsBehavior: Flickable.StopAtBounds
10297
103− function updateDropProperties(drag) {
104− var coords = mapToItem(listView.contentItem, drag.x, drag.y)
105− var index = Math.floor(drag.x + listView.realContentX) / (listView.itemWidth + listView.spacing);
106− if (index < 0) {
107− listView.hoveredWorkspaceIndex = -1;
108− listView.hoveredHalf = "";
109− return;
98+ Behavior on contentX {
99+ SmoothedAnimation { duration: 200 }
110100 }
111101
112− var upperLimit = dropItemIndex == -1 ? listView.count : listView.count - 1
113− if (index > upperLimit) index = upperLimit;
114− listView.hoveredWorkspaceIndex = index;
115− print("updating drag:", (drag.x + listView.realContentX) % (listView.itemWidth + listView.spacing))
116− var pixelsInTile = (drag.x + listView.realContentX) % (listView.itemWidth + listView.spacing);
117− listView.hoveredHalf = (pixelsInTile / listView.itemWidth) < .5 ? "left" : "right";
118− }
102+ orientation: ListView.Horizontal
103+ spacing: units.gu(1)
104+ leftMargin: itemWidth
105+ rightMargin: itemWidth
119106
120− function progressiveScroll(mouseX) {
121− var progress = Math.max(0, Math.min(1, (mouseX - listView.foldingAreaWidth) / (width - listView.leftMargin * 2 - listView.foldingAreaWidth * 2)))
122− listView.contentX = listView.originX + (listView.contentWidth - listView.width + listView.leftMargin + listView.rightMargin) * progress - listView.leftMargin
123− }
107+ property var screenSize: screen.availableModes[screen.currentModeIndex].size
108+ property int itemWidth: height * screenSize.width / screenSize.height
109+ property int foldingAreaWidth: itemWidth / 2
110+ property int maxAngle: 40
124111
125− displaced: Transition { UbuntuNumberAnimation { properties: "x" } }
112+ property real realContentX: contentX - originX + leftMargin
113+ property int dropItemIndex: -1
114+ property int hoveredWorkspaceIndex: -1
115+ property string hoveredHalf: "" // left or right
116+
117+ function getDropIndex(drag) {
118+ var coords = mapToItem(listView.contentItem, drag.x, drag.y)
119+ var index = Math.floor((drag.x + listView.realContentX) / (listView.itemWidth + listView.spacing));
120+ if (index < 0) index = 0;
121+ var upperLimit = dropItemIndex == -1 ? listView.count : listView.count - 1
122+ if (index > upperLimit) index = upperLimit;
123+ return index;
124+ }
126125
127− delegate: Item {
128− id: workspaceDelegate
129− objectName: "delegate" + index
130− height: parent.height
131− width: listView.itemWidth
132− Behavior on width { UbuntuNumberAnimation {} }
133− visible: listView.dropItemIndex !== index
126+ function updateDropProperties(drag) {
127+ var coords = mapToItem(listView.contentItem, drag.x, drag.y)
128+ var index = Math.floor(drag.x + listView.realContentX) / (listView.itemWidth + listView.spacing);
129+ if (index < 0) {
130+ listView.hoveredWorkspaceIndex = -1;
131+ listView.hoveredHalf = "";
132+ return;
133+ }
134134
135− property int itemX: -listView.realContentX + index * (listView.itemWidth + listView.spacing)
136− property int distanceFromLeft: itemX //- listView.leftMargin
137− property int distanceFromRight: listView.width - listView.leftMargin - listView.rightMargin - itemX - listView.itemWidth
135+ var upperLimit = dropItemIndex == -1 ? listView.count : listView.count - 1
136+ if (index > upperLimit) index = upperLimit;
137+ listView.hoveredWorkspaceIndex = index;
138+ print("updating drag:", (drag.x + listView.realContentX) % (listView.itemWidth + listView.spacing))
139+ var pixelsInTile = (drag.x + listView.realContentX) % (listView.itemWidth + listView.spacing);
140+ listView.hoveredHalf = (pixelsInTile / listView.itemWidth) < .5 ? "left" : "right";
141+ }
138142
139− property int maxAngle: 40
143+ function progressiveScroll(mouseX) {
144+ var progress = Math.max(0, Math.min(1, (mouseX - listView.itemWidth) / (width - listView.leftMargin * 2 - listView.itemWidth * 2)))
145+ listView.contentX = listView.originX + (listView.contentWidth - listView.width + listView.leftMargin + listView.rightMargin) * progress - listView.leftMargin
146+ }
147+
148+ displaced: Transition { UbuntuNumberAnimation { properties: "x" } }
140149
141− property int itemAngle: {
142− if (index == 0) {
143− if (distanceFromLeft < 0) {
144− var progress = (distanceFromLeft + listView.foldingAreaWidth) / listView.foldingAreaWidth
145− return MathUtils.linearAnimation(1, -1, 0, maxAngle, Math.max(-1, Math.min(1, progress)));
150+ delegate: Item {
151+ id: workspaceDelegate
152+ objectName: "delegate" + index
153+ height: parent.height
154+ width: listView.itemWidth
155+ Behavior on width { UbuntuNumberAnimation {} }
156+ visible: listView.dropItemIndex !== index
157+
158+ property int itemX: -listView.realContentX + index * (listView.itemWidth + listView.spacing)
159+ property int distanceFromLeft: itemX //- listView.leftMargin
160+ property int distanceFromRight: listView.width - listView.leftMargin - listView.rightMargin - itemX - listView.itemWidth
161+
162+ property int itemAngle: {
163+ if (index == 0) {
164+ if (distanceFromLeft < 0) {
165+ var progress = (distanceFromLeft + listView.foldingAreaWidth) / listView.foldingAreaWidth
166+ return MathUtils.linearAnimation(1, -1, 0, listView.maxAngle, Math.max(-1, Math.min(1, progress)));
167+ }
168+ return 0
146169 }
147− return 0
148− }
149− if (index == listView.count - 1) {
150− if (distanceFromRight < 0) {
151− var progress = (distanceFromRight + listView.foldingAreaWidth) / listView.foldingAreaWidth
152− return MathUtils.linearAnimation(1, -1, 0, -maxAngle, Math.max(-1, Math.min(1, progress)));
170+ if (index == listView.count - 1) {
171+ if (distanceFromRight < 0) {
172+ var progress = (distanceFromRight + listView.foldingAreaWidth) / listView.foldingAreaWidth
173+ return MathUtils.linearAnimation(1, -1, 0, -listView.maxAngle, Math.max(-1, Math.min(1, progress)));
174+ }
175+ return 0
153176 }
154− return 0
155− }
156177
157− if (distanceFromLeft < listView.foldingAreaWidth) {
158− // itemX : 10gu = p : 100
159− var progress = distanceFromLeft / listView.foldingAreaWidth
160− return MathUtils.linearAnimation(1, -1, 0, maxAngle, Math.max(-1, Math.min(1, progress)));
161− }
162− if (distanceFromRight < listView.foldingAreaWidth) {
163− var progress = distanceFromRight / listView.foldingAreaWidth
164− return MathUtils.linearAnimation(1, -1, 0, -maxAngle, Math.max(-1, Math.min(1, progress)));
165− }
166− return 0
167− }
168−
169− property int itemOffset: {
170− var rotationOffset = (1 - Math.cos(itemAngle * Math.PI / 360)) * listView.itemWidth
171− if (index == 0) {
172− if (distanceFromLeft < 0) {
173− return -distanceFromLeft - rotationOffset
178+ if (distanceFromLeft < listView.foldingAreaWidth) {
179+ // itemX : 10gu = p : 100
180+ var progress = distanceFromLeft / listView.foldingAreaWidth
181+ return MathUtils.linearAnimation(1, -1, 0, listView.maxAngle, Math.max(-1, Math.min(1, progress)));
174182 }
175− return 0
176− }
177− if (index == listView.count - 1) {
178− if (distanceFromRight < 0) {
179− return distanceFromRight + rotationOffset
183+ if (distanceFromRight < listView.foldingAreaWidth) {
184+ var progress = distanceFromRight / listView.foldingAreaWidth
185+ return MathUtils.linearAnimation(1, -1, 0, -listView.maxAngle, Math.max(-1, Math.min(1, progress)));
180186 }
181187 return 0
182188 }
183189
184− if (itemX < -listView.foldingAreaWidth) {
185− return -itemX - rotationOffset
186− }
187− if (distanceFromLeft < listView.foldingAreaWidth) {
188− return (listView.foldingAreaWidth - distanceFromLeft) / 2 - rotationOffset
189− }
190+ property int itemOffset: {
191+ if (index == 0) {
192+ if (distanceFromLeft < 0) {
193+ return -distanceFromLeft
194+ }
195+ return 0
196+ }
197+ if (index == listView.count - 1) {
198+ if (distanceFromRight < 0) {
199+ return distanceFromRight
200+ }
201+ return 0
202+ }
190203
191− if (distanceFromRight < -listView.foldingAreaWidth) {
192− return distanceFromRight + rotationOffset
193− }
204+ if (itemX < -listView.foldingAreaWidth) {
205+ return -itemX
206+ }
207+ if (distanceFromLeft < listView.foldingAreaWidth) {
208+ return (listView.foldingAreaWidth - distanceFromLeft) / 2
209+ }
194210
195− if (distanceFromRight < listView.foldingAreaWidth) {
196− return -(listView.foldingAreaWidth - distanceFromRight) / 2 + rotationOffset
197− }
211+ if (distanceFromRight < -listView.foldingAreaWidth) {
212+ return distanceFromRight
213+ }
198214
199− return 0
200− }
215+ if (distanceFromRight < listView.foldingAreaWidth) {
216+ return -(listView.foldingAreaWidth - distanceFromRight) / 2
217+ }
201218
202−// onItemXChanged: if (index == 1) print("x", itemX, listView.contentX)
203−
204− z: itemOffset < 0 ? itemOffset : -itemOffset
205− transform: [
206− Rotation {
207− angle: itemAngle
208− axis { x: 0; y: 1; z: 0 }
209− origin { x: itemAngle < 0 ? listView.itemWidth : 0; y: height / 2 }
210− },
211− Translate {
212− x: itemOffset
219+ return 0
213220 }
214− ]
215221
216− WorkspacePreview {
217− height: listView.height
218− width: listView.itemWidth
219− background: root.background
220− screenHeight: screen.availableModes[screen.currentModeIndex].size.height
221− containsDragLeft: listView.hoveredWorkspaceIndex == index && listView.hoveredHalf == "left"
222− containsDragRight: listView.hoveredWorkspaceIndex == index && listView.hoveredHalf == "right"
223− isActive: model.workspace.active
224− }
225− MouseArea {
226− anchors.fill: parent
227− onClicked: {
228− model.workspace.activate();
222+ // onItemXChanged: if (index == 1) print("x", itemX, listView.contentX)
223+
224+ z: itemOffset < 0 ? itemOffset : -itemOffset
225+ transform: [
226+ Rotation {
227+ angle: itemAngle
228+ axis { x: 0; y: 1; z: 0 }
229+ origin { x: itemAngle < 0 ? listView.itemWidth : 0; y: height / 2 }
230+ },
231+ Translate {
232+ x: itemOffset
233+ }
234+ ]
235+
236+ WorkspacePreview {
237+ height: listView.height
238+ width: listView.itemWidth
239+ background: root.background
240+ screenHeight: screen.availableModes[screen.currentModeIndex].size.height
241+ containsDragLeft: listView.hoveredWorkspaceIndex == index && listView.hoveredHalf == "left"
242+ containsDragRight: listView.hoveredWorkspaceIndex == index && listView.hoveredHalf == "right"
243+ isActive: model.workspace.active
229244 }
230− onDoubleClicked: {
231− model.workspace.activate();
232− root.closeSpread();
245+ MouseArea {
246+ anchors.fill: parent
247+ onClicked: {
248+ print("clickkkked!")
249+ model.workspace.activate();
250+ }
251+ onDoubleClicked: {
252+ model.workspace.activate();
253+ root.closeSpread();
254+ }
233255 }
234− }
235256
236− MouseArea {
237− id: closeMouseArea
238− objectName: "closeMouseArea"
239− anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 }
240− hoverEnabled: true
241− height: units.gu(4)
242− width: height
243−
244− onClicked: {
245− model.workspace.unassign();
246− root.commitScreenSetup();
247− }
248− Image {
249− id: closeImage
250− source: "../graphics/window-close.svg"
251− anchors.fill: closeMouseArea
252− anchors.margins: units.gu(1)
253− sourceSize.width: width
254− sourceSize.height: height
255− readonly property var mousePos: hoverMouseArea.mapToItem(workspaceDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
256− readonly property bool shown: (hoverMouseArea.containsMouse || parent.containsMouse)
257− && mousePos.y < workspaceDelegate.width / 4
258− && mousePos.y > -units.gu(2)
259− && mousePos.x > -units.gu(2)
260− && mousePos.x < workspaceDelegate.height / 4
261− opacity: shown ? 1 : 0
262− visible: opacity > 0
263− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
257+ MouseArea {
258+ id: closeMouseArea
259+ objectName: "closeMouseArea"
260+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 }
261+ hoverEnabled: true
262+ height: units.gu(4)
263+ width: height
264+
265+ onClicked: {
266+ model.workspace.unassign();
267+ root.commitScreenSetup();
268+ }
269+ Image {
270+ id: closeImage
271+ source: "../graphics/window-close.svg"
272+ anchors.fill: closeMouseArea
273+ anchors.margins: units.gu(1)
274+ sourceSize.width: width
275+ sourceSize.height: height
276+ readonly property var mousePos: hoverMouseArea.mapToItem(workspaceDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
277+ readonly property bool shown: (hoverMouseArea.containsMouse || parent.containsMouse)
278+ && mousePos.y < workspaceDelegate.width / 4
279+ && mousePos.y > -units.gu(2)
280+ && mousePos.x > -units.gu(2)
281+ && mousePos.x < workspaceDelegate.height / 4
282+ opacity: shown ? 1 : 0
283+ visible: opacity > 0
284+ Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
264285
286+ }
265287 }
266288 }
267− }
268289
269− MouseArea {
270− id: hoverMouseArea
271− anchors.fill: parent
272− hoverEnabled: true
273− propagateComposedEvents: true
274− anchors.leftMargin: listView.leftMargin
275− anchors.rightMargin: listView.rightMargin
290+ MouseArea {
291+ id: hoverMouseArea
292+ anchors.fill: parent
293+ hoverEnabled: true
294+ propagateComposedEvents: true
295+ anchors.leftMargin: listView.leftMargin
296+ anchors.rightMargin: listView.rightMargin
276297
277− property int draggedIndex: -1
298+ property int draggedIndex: -1
278299
279− property int startX: 0
280− property int startY: 0
300+ property int startX: 0
301+ property int startY: 0
281302
282− onMouseXChanged: {
283− if (!pressed || dragging) {
284− listView.progressiveScroll(mouseX)
303+ onMouseXChanged: {
304+ if (!pressed || dragging) {
305+ listView.progressiveScroll(mouseX)
306+ }
285307 }
286− }
287− onMouseYChanged: {
288− if (Math.abs(mouseY - startY) > units.gu(3)) {
289− drag.axis = Drag.XAndYAxis;
308+ onMouseYChanged: {
309+ if (Math.abs(mouseY - startY) > units.gu(3)) {
310+ drag.axis = Drag.XAndYAxis;
311+ }
290312 }
291− }
292313
293− onReleased: {
294− var result = fakeDragItem.Drag.drop();
295−// if (result == Qt.IgnoreAction) {
296−// WorkspaceManager.destroyWorkspace(fakeDragItem.workspace);
297−// }
298− root.commitScreenSetup();
299− drag.target = null;
300− }
314+ onReleased: {
315+ var result = fakeDragItem.Drag.drop();
316+ // if (result == Qt.IgnoreAction) {
317+ // WorkspaceManager.destroyWorkspace(fakeDragItem.workspace);
318+ // }
319+ root.commitScreenSetup();
320+ drag.target = null;
321+ }
301322
302− property bool dragging: drag.active
303− onDraggingChanged: {
304− if (drag.active) {
305− print("drai", draggedIndex)
306− var ws = listView.model.get(draggedIndex);
307− print("ws:", ws)
308− ws.unassign();
309−// listView.model.remove(draggedIndex, 1)
323+ property bool dragging: drag.active
324+ onDraggingChanged: {
325+ if (drag.active) {
326+ print("drai", draggedIndex)
327+ var ws = listView.model.get(draggedIndex);
328+ print("ws:", ws)
329+ ws.unassign();
330+ // listView.model.remove(draggedIndex, 1)
331+ }
310332 }
311− }
312333
313− onPressed: {
314− startX = mouseX;
315− startY = mouseY;
316− if (listView.model.count < 2) return;
334+ onPressed: {
335+ startX = mouseX;
336+ startY = mouseY;
337+ if (listView.model.count < 2) return;
317338
318− var coords = mapToItem(listView.contentItem, mouseX, mouseY)
319− draggedIndex = listView.indexAt(coords.x, coords.y)
320− var clickedItem = listView.itemAt(coords.x, coords.y)
339+ var coords = mapToItem(listView.contentItem, mouseX, mouseY)
340+ draggedIndex = listView.indexAt(coords.x, coords.y)
341+ var clickedItem = listView.itemAt(coords.x, coords.y)
321342
322− var itemCoords = clickedItem.mapToItem(listView, -listView.leftMargin, 0);
323− fakeDragItem.x = itemCoords.x
324− fakeDragItem.y = itemCoords.y
325− fakeDragItem.workspace = listView.model.get(draggedIndex)
343+ var itemCoords = clickedItem.mapToItem(listView, -listView.leftMargin, 0);
344+ fakeDragItem.x = itemCoords.x
345+ fakeDragItem.y = itemCoords.y
346+ fakeDragItem.workspace = listView.model.get(draggedIndex)
326347
327− var mouseCoordsInItem = mapToItem(clickedItem, mouseX, mouseY);
328− fakeDragItem.Drag.hotSpot.x = mouseCoordsInItem.x
329− fakeDragItem.Drag.hotSpot.y = mouseCoordsInItem.y
348+ var mouseCoordsInItem = mapToItem(clickedItem, mouseX, mouseY);
349+ fakeDragItem.Drag.hotSpot.x = mouseCoordsInItem.x
350+ fakeDragItem.Drag.hotSpot.y = mouseCoordsInItem.y
330351
331− drag.axis = Drag.YAxis;
332− drag.target = fakeDragItem;
333− }
352+ drag.axis = Drag.YAxis;
353+ drag.target = fakeDragItem;
354+ }
334355
335− WorkspacePreview {
336− id: fakeDragItem
337− height: listView.height
338− width: listView.itemWidth
339− background: root.background
340− screenHeight: screen.availableModes[screen.currentModeIndex].size.height
341− visible: Drag.active
356+ WorkspacePreview {
357+ id: fakeDragItem
358+ height: listView.height
359+ width: listView.itemWidth
360+ background: root.background
361+ screenHeight: screen.availableModes[screen.currentModeIndex].size.height
362+ visible: Drag.active
342363
343− Drag.active: hoverMouseArea.drag.active
344− Drag.keys: ['workspace']
364+ Drag.active: hoverMouseArea.drag.active
365+ Drag.keys: ['workspace']
345366
346− property var workspace
347− property bool inDropArea: false
367+ property var workspace
368+ property bool inDropArea: false
348369
349− Rectangle {
350− anchors.fill: parent
351− color: "#33000000"
352− opacity: parent.inDropArea ? 0 : 1
353− Behavior on opacity { UbuntuNumberAnimation { } }
354370 Rectangle {
355− anchors.centerIn: parent
356− width: units.gu(6)
357− height: units.gu(6)
358− radius: width / 2
359− color: "#aa000000"
371+ anchors.fill: parent
372+ color: "#33000000"
373+ opacity: parent.inDropArea ? 0 : 1
374+ Behavior on opacity { UbuntuNumberAnimation { } }
375+ Rectangle {
376+ anchors.centerIn: parent
377+ width: units.gu(6)
378+ height: units.gu(6)
379+ radius: width / 2
380+ color: "#aa000000"
381+ }
382+
383+ Icon {
384+ height: units.gu(3)
385+ width: height
386+ anchors.centerIn: parent
387+ name: "edit-delete"
388+ color: "white"
389+ }
360390 }
361391
362− Icon {
363− height: units.gu(3)
364− width: height
365− anchors.centerIn: parent
366− name: "edit-delete"
367− color: "white"
368− }
392+ states: [
393+ State {
394+ when: fakeDragItem.Drag.active
395+ ParentChange { target: fakeDragItem; parent: shell }
396+ }
397+ ]
369398 }
370−
371− states: [
372− State {
373− when: fakeDragItem.Drag.active
374− ParentChange { target: fakeDragItem; parent: shell }
375− }
376− ]
377399 }
378400 }
379401 }