Merge lp:~fboucault/ubuntu-terminal-app/tiled_view into lp:ubuntu-terminal-app

Proposed by Florian Boucault
Status: Merged
Merged at revision: 325
Proposed branch: lp:~fboucault/ubuntu-terminal-app/tiled_view
Merge into: lp:ubuntu-terminal-app
Diff against target: 1857 lines (+1413/-150)
18 files modified
po/com.ubuntu.terminal.pot (+13/-5)
src/app/qml/AlternateActionPopover.qml (+14/-1)
src/app/qml/Settings/SettingsShortcutsSection.qml (+30/-0)
src/app/qml/TabsModel.qml (+6/-5)
src/app/qml/TabsPage.qml (+1/-1)
src/app/qml/Terminal.qml (+117/-4)
src/app/qml/TerminalPage.qml (+6/-127)
src/app/qml/TerminalSettings.qml (+12/-0)
src/app/qml/TerminalWindow.qml (+20/-5)
src/app/qml/TiledTerminalView.qml (+96/-0)
src/app/qml/TiledView.qml (+135/-0)
src/app/qml/TiledViewSeparator.qml (+76/-0)
src/app/qml/binarytree.js (+378/-0)
src/plugin/qmltermwidget/lib/TerminalDisplay.h (+2/-2)
tests/CMakeLists.txt (+1/-0)
tests/qtquicktest/CMakeLists.txt (+20/-0)
tests/qtquicktest/qtquicktest.cpp (+18/-0)
tests/qtquicktest/tst_TiledView.qml (+468/-0)
To merge this branch: bzr merge lp:~fboucault/ubuntu-terminal-app/tiled_view
Reviewer Review Type Date Requested Status
Ubuntu Terminal Developers Pending
Review via email: mp+314617@code.launchpad.net

Commit message

New feature: terminals can be organised as tiles.

Description of the change

New feature: terminals can be organised as tiles.

To post a comment you must log in.
333. By Florian Boucault

Added automated tests for tile resizing.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'po/com.ubuntu.terminal.pot'
2--- po/com.ubuntu.terminal.pot 2017-01-06 08:39:01 +0000
3+++ po/com.ubuntu.terminal.pot 2017-01-12 13:29:38 +0000
4@@ -8,7 +8,7 @@
5 msgstr ""
6 "Project-Id-Version: \n"
7 "Report-Msgid-Bugs-To: \n"
8-"POT-Creation-Date: 2017-01-06 08:38+0000\n"
9+"POT-Creation-Date: 2017-01-12 14:17+0100\n"
10 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Language-Team: LANGUAGE <LL@li.org>\n"
13@@ -29,15 +29,23 @@
14 msgid "Paste"
15 msgstr ""
16
17-#: ../src/app/qml/AlternateActionPopover.qml:86 ../src/app/qml/TabsPage.qml:32
18+#: ../src/app/qml/AlternateActionPopover.qml:86
19+msgid "Split horizontally"
20+msgstr ""
21+
22+#: ../src/app/qml/AlternateActionPopover.qml:92
23+msgid "Split vertically"
24+msgstr ""
25+
26+#: ../src/app/qml/AlternateActionPopover.qml:99 ../src/app/qml/TabsPage.qml:32
27 msgid "New tab"
28 msgstr ""
29
30-#: ../src/app/qml/AlternateActionPopover.qml:91
31+#: ../src/app/qml/AlternateActionPopover.qml:104
32 msgid "New window"
33 msgstr ""
34
35-#: ../src/app/qml/AlternateActionPopover.qml:96
36+#: ../src/app/qml/AlternateActionPopover.qml:109
37 msgid "Close"
38 msgstr ""
39
40@@ -303,7 +311,7 @@
41 msgid "Tabs"
42 msgstr ""
43
44-#: ../src/app/qml/TerminalPage.qml:252
45+#: ../src/app/qml/TerminalPage.qml:132
46 msgid "Selection Mode"
47 msgstr ""
48
49
50=== modified file 'src/app/qml/AlternateActionPopover.qml'
51--- src/app/qml/AlternateActionPopover.qml 2017-01-06 08:38:45 +0000
52+++ src/app/qml/AlternateActionPopover.qml 2017-01-12 13:29:38 +0000
53@@ -83,6 +83,19 @@
54 property bool divider: true
55 }
56 Action {
57+ text: i18n.tr("Split horizontally")
58+ onTriggered: tiledTerminalView.splitTerminal(terminal, Qt.Vertical)
59+ shortcut: settings.shortcutSplitHorizontally
60+ enabled: terminal.height >= 2 * tiledTerminalView.minimumTileHeight
61+ }
62+ Action {
63+ text: i18n.tr("Split vertically")
64+ onTriggered: tiledTerminalView.splitTerminal(terminal, Qt.Horizontal)
65+ shortcut: settings.shortcutSplitVertically
66+ enabled: terminal.width >= 2 * tiledTerminalView.minimumTileWidth
67+ property bool divider: true
68+ }
69+ Action {
70 text: i18n.tr("New tab")
71 onTriggered: tabsModel.addTerminalTab()
72 shortcut: settings.shortcutNewTab
73@@ -94,7 +107,7 @@
74 }
75 Action {
76 text: i18n.tr("Close")
77- onTriggered: tabsModel.removeItem(tabsModel.indexOf(tabsModel.currentItem))
78+ onTriggered: terminal.finished()
79 shortcut: settings.shortcutCloseTab
80 }
81 }
82
83=== modified file 'src/app/qml/Settings/SettingsShortcutsSection.qml'
84--- src/app/qml/Settings/SettingsShortcutsSection.qml 2016-11-30 22:56:25 +0000
85+++ src/app/qml/Settings/SettingsShortcutsSection.qml 2017-01-12 13:29:38 +0000
86@@ -186,6 +186,36 @@
87 actionLabel: QT_TR_NOOP("Toggle fullscreen")
88 shortcutSetting: "shortcutFullscreen"
89 }
90+ ListElement {
91+ section: QT_TR_NOOP("View")
92+ actionLabel: QT_TR_NOOP("Split terminal horizontally")
93+ shortcutSetting: "shortcutSplitHorizontally"
94+ }
95+ ListElement {
96+ section: QT_TR_NOOP("View")
97+ actionLabel: QT_TR_NOOP("Split terminal vertically")
98+ shortcutSetting: "shortcutSplitVertically"
99+ }
100+ ListElement {
101+ section: QT_TR_NOOP("View")
102+ actionLabel: QT_TR_NOOP("Navigate to terminal above")
103+ shortcutSetting: "shortcutMoveToTileAbove"
104+ }
105+ ListElement {
106+ section: QT_TR_NOOP("View")
107+ actionLabel: QT_TR_NOOP("Navigate to terminal below")
108+ shortcutSetting: "shortcutMoveToTileBelow"
109+ }
110+ ListElement {
111+ section: QT_TR_NOOP("View")
112+ actionLabel: QT_TR_NOOP("Navigate to terminal on the left")
113+ shortcutSetting: "shortcutMoveToTileLeft"
114+ }
115+ ListElement {
116+ section: QT_TR_NOOP("View")
117+ actionLabel: QT_TR_NOOP("Navigate to terminal on the right")
118+ shortcutSetting: "shortcutMoveToTileRight"
119+ }
120 }
121
122 delegate: ShortcutRow {
123
124=== modified file 'src/app/qml/TabsModel.qml'
125--- src/app/qml/TabsModel.qml 2016-12-12 21:18:17 +0000
126+++ src/app/qml/TabsModel.qml 2017-01-12 13:29:38 +0000
127@@ -34,17 +34,18 @@
128 moveItem(from, to);
129 }
130
131- property Component terminalComponent: TerminalComponent {}
132+ property Component tiledViewComponent: TiledTerminalView {}
133
134 function addTerminalTab(initialWorkingDirectory) {
135 if (currentItem) {
136- initialWorkingDirectory = currentItem.session.getWorkingDirectory();
137+ initialWorkingDirectory = currentItem.focusedTerminal.session.getWorkingDirectory();
138 }
139
140- var termObject = terminalComponent.createObject(terminalPage.terminalContainer,
141+ var tiledView = tiledViewComponent.createObject(terminalPage.terminalContainer,
142 {"initialWorkingDirectory": initialWorkingDirectory,
143- "visible": Qt.binding(function () { return tabsModel.currentItem === termObject})});
144- tabsModel.addItem(termObject);
145+ "visible": Qt.binding(function () { return tabsModel.currentItem === tiledView})});
146+ tiledView.emptied.connect(function () {tabsModel.removeItem(tabsModel.indexOf(tiledView));})
147+ tabsModel.addItem(tiledView);
148 currentIndex = tabsModel.count - 1;
149 }
150
151
152=== modified file 'src/app/qml/TabsPage.qml'
153--- src/app/qml/TabsPage.qml 2016-12-12 12:44:22 +0000
154+++ src/app/qml/TabsPage.qml 2017-01-12 13:29:38 +0000
155@@ -103,7 +103,7 @@
156 Label {
157 anchors { fill: blackRect; margins: units.dp(2) }
158 property var tab: tabsModel.itemAt(index)
159- text: tab ? tab.session.title : ""
160+ text: tab && tab.focusedTerminal ? tab.focusedTerminal.session.title : ""
161 wrapMode: Text.Wrap
162 color: "white"
163 }
164
165=== renamed file 'src/app/qml/TerminalComponent.qml' => 'src/app/qml/Terminal.qml'
166--- src/app/qml/TerminalComponent.qml 2016-12-12 12:44:22 +0000
167+++ src/app/qml/Terminal.qml 2017-01-12 13:29:38 +0000
168@@ -17,20 +17,21 @@
169 */
170 import QtQuick 2.4
171 import Ubuntu.Components 1.3
172+import Ubuntu.Components.Popups 1.3
173 import QMLTermWidget 1.0
174 import Terminal 0.1
175
176 QMLTermWidget {
177 id: terminal
178- width: parent.width
179- height: parent.height
180
181 colorScheme: settings.colorScheme
182 font.family: settings.fontStyle
183 font.pixelSize: FontUtils.sizeToPixels("medium") * settings.fontSize / 10
184
185+ property bool isDarkBackground: ColorUtils.luminance(backgroundColor) <= 0.85
186+ property color contourColor: isDarkBackground ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : Qt.rgba(0.0, 0.0, 0.0, 0.2)
187 property string initialWorkingDirectory
188- signal sessionFinished(var session);
189+ signal finished()
190
191 session: QMLTermSession {
192 id: terminalSession
193@@ -68,7 +69,7 @@
194 "-o", "LogLevel=Error",
195 "mkdir -p `dirname %1`; echo -n $$ > %1; cd %2; bash".arg(sshShellPidFile).arg(initialWorkingDirectory)]
196 : [])
197- onFinished: tabsModel.removeItem(tabsModel.indexOf(terminal))
198+ onFinished: terminal.finished()
199 }
200
201 property int totalLines: terminal.scrollbarMaximum - terminal.scrollbarMinimum + terminal.lines
202@@ -77,4 +78,116 @@
203 terminalSession.startShellProgram();
204 forceActiveFocus();
205 }
206+
207+ // TODO: This invisible button is used to position the popover where the
208+ // alternate action was called. Terrible terrible workaround!
209+ Item {
210+ id: hiddenButton
211+ width: 1
212+ height: 1
213+ visible: false
214+ enabled: false
215+ }
216+
217+ TerminalInputArea {
218+ id: inputArea
219+ enabled: terminalPage.state != "SELECTION"
220+ anchors.fill: parent
221+ // FIXME: should anchor to the bottom of the window to cater for the case when the OSK is up
222+
223+ // This is the minimum wheel event registered by the plugin (with the current settings).
224+ property real wheelValue: 40
225+
226+ // This is needed to fake a "flickable" scrolling.
227+ swipeDelta: terminal.fontMetrics.height
228+
229+ // Mouse actions
230+ onMouseMoveDetected: terminal.simulateMouseMove(x, y, button, buttons, modifiers);
231+ onDoubleClickDetected: terminal.simulateMouseDoubleClick(x, y, button, buttons, modifiers);
232+ onMousePressDetected: {
233+ terminal.forceActiveFocus();
234+ terminal.simulateMousePress(x, y, button, buttons, modifiers);
235+ }
236+ onMouseReleaseDetected: terminal.simulateMouseRelease(x, y, button, buttons, modifiers);
237+ onMouseWheelDetected: terminal.simulateWheel(x, y, buttons, modifiers, angleDelta);
238+
239+ // Touch actions
240+ onTouchPress: terminal.forceActiveFocus()
241+ onTouchClick: terminal.simulateKeyPress(Qt.Key_Tab, Qt.NoModifier, true, 0, "");
242+ onTouchPressAndHold: alternateAction(x, y);
243+
244+ // Swipe actions
245+ onSwipeYDetected: {
246+ if (steps > 0) {
247+ simulateSwipeDown(steps);
248+ } else {
249+ simulateSwipeUp(-steps);
250+ }
251+ }
252+ onSwipeXDetected: {
253+ if (steps > 0) {
254+ simulateSwipeRight(steps);
255+ } else {
256+ simulateSwipeLeft(-steps);
257+ }
258+ }
259+ onTwoFingerSwipeYDetected: {
260+ if (steps > 0) {
261+ simulateDualSwipeDown(steps);
262+ } else {
263+ simulateDualSwipeUp(-steps);
264+ }
265+ }
266+
267+ function simulateSwipeUp(steps) {
268+ while(steps > 0) {
269+ terminal.simulateKeyPress(Qt.Key_Up, Qt.NoModifier, true, 0, "");
270+ steps--;
271+ }
272+ }
273+ function simulateSwipeDown(steps) {
274+ while(steps > 0) {
275+ terminal.simulateKeyPress(Qt.Key_Down, Qt.NoModifier, true, 0, "");
276+ steps--;
277+ }
278+ }
279+ function simulateSwipeLeft(steps) {
280+ while(steps > 0) {
281+ terminal.simulateKeyPress(Qt.Key_Left, Qt.NoModifier, true, 0, "");
282+ steps--;
283+ }
284+ }
285+ function simulateSwipeRight(steps) {
286+ while(steps > 0) {
287+ terminal.simulateKeyPress(Qt.Key_Right, Qt.NoModifier, true, 0, "");
288+ steps--;
289+ }
290+ }
291+ function simulateDualSwipeUp(steps) {
292+ while(steps > 0) {
293+ terminal.simulateWheel(width * 0.5, height * 0.5, Qt.NoButton, Qt.NoModifier, Qt.point(0, -wheelValue));
294+ steps--;
295+ }
296+ }
297+ function simulateDualSwipeDown(steps) {
298+ while(steps > 0) {
299+ terminal.simulateWheel(width * 0.5, height * 0.5, Qt.NoButton, Qt.NoModifier, Qt.point(0, wheelValue));
300+ steps--;
301+ }
302+ }
303+
304+ // Semantic actions
305+ onAlternateAction: {
306+ // Force the hiddenButton in the event position.
307+ hiddenButton.x = x;
308+ hiddenButton.y = y;
309+ PopupUtils.open(Qt.resolvedUrl("AlternateActionPopover.qml"),
310+ hiddenButton);
311+ }
312+ }
313+
314+ QMLTermScrollbar {
315+ anchors.fill: parent
316+ terminal: terminal
317+ }
318 }
319
320=== modified file 'src/app/qml/TerminalPage.qml'
321--- src/app/qml/TerminalPage.qml 2017-01-06 08:37:39 +0000
322+++ src/app/qml/TerminalPage.qml 2017-01-12 13:29:38 +0000
323@@ -27,7 +27,7 @@
324 Page {
325 id: terminalPage
326 property alias terminalContainer: terminalContainer
327- property Item terminal
328+ property Terminal terminal
329 property var tabsModel
330 property bool narrowLayout
331 theme: ThemeSettings {
332@@ -50,15 +50,15 @@
333 top: parent.top
334 right: parent.right
335 }
336- property bool isDarkBackground: ColorUtils.luminance(backgroundColor) <= 0.85
337+ property bool isDarkBackground: terminalPage.terminal && terminalPage.terminal.isDarkBackground
338 actionColor: isDarkBackground ? "white" : "black"
339 backgroundColor: terminalPage.terminal ? terminalPage.terminal.backgroundColor : ""
340 foregroundColor: terminalPage.terminal ? terminalPage.terminal.foregroundColor : ""
341- contourColor: isDarkBackground ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : Qt.rgba(0.0, 0.0, 0.0, 0.2)
342+ contourColor: terminalPage.terminal ? terminalPage.terminal.contourColor : ""
343 color: isDarkBackground ? Qt.tint(backgroundColor, "#0DFFFFFF") : Qt.tint(backgroundColor, "#0D000000")
344 model: terminalPage.tabsModel
345 function titleFromModelItem(modelItem) {
346- return modelItem.session.title;
347+ return modelItem.focusedTerminal ? modelItem.focusedTerminal.session.title : "";
348 }
349
350 actions: [
351@@ -84,7 +84,6 @@
352 top: terminalPage.narrowLayout ? parent.top : tabsBar.bottom;
353 right: parent.right;
354 bottom: keyboardBarLoader.top
355- margins: units.gu(1)
356 }
357
358 Binding {
359@@ -94,125 +93,6 @@
360 }
361 }
362
363- QMLTermScrollbar {
364- anchors {
365- top: terminalContainer.anchors.top
366- bottom: terminalContainer.anchors.bottom
367- left: terminalContainer.anchors.left
368- right: terminalContainer.anchors.right
369- }
370-
371- terminal: terminalPage.terminal
372- z: inputArea.z + 1
373- }
374-
375- // TODO: This invisible button is used to position the popover where the
376- // alternate action was called. Terrible terrible workaround!
377- Button {
378- id: hiddenButton
379- width: 5
380- height: 5
381- visible: false
382- enabled: false
383- }
384-
385- TerminalInputArea{
386- id: inputArea
387- anchors {
388- left: terminalContainer.anchors.left
389- top: terminalContainer.anchors.top
390- right: terminalContainer.anchors.right
391- bottom: parent.bottom
392- margins: terminalContainer.anchors.margins
393- }
394- enabled: terminal
395-
396- // This is the minimum wheel event registered by the plugin (with the current settings).
397- property real wheelValue: 40
398-
399- // This is needed to fake a "flickable" scrolling.
400- swipeDelta: terminal ? terminal.fontMetrics.height : 0
401-
402- // Mouse actions
403- onMouseMoveDetected: terminal.simulateMouseMove(x, y, button, buttons, modifiers);
404- onDoubleClickDetected: terminal.simulateMouseDoubleClick(x, y, button, buttons, modifiers);
405- onMousePressDetected: terminal.simulateMousePress(x, y, button, buttons, modifiers);
406- onMouseReleaseDetected: terminal.simulateMouseRelease(x, y, button, buttons, modifiers);
407- onMouseWheelDetected: terminal.simulateWheel(x, y, buttons, modifiers, angleDelta);
408-
409- // Touch actions
410- onTouchClick: terminal.simulateKeyPress(Qt.Key_Tab, Qt.NoModifier, true, 0, "");
411- onTouchPressAndHold: alternateAction(x, y);
412-
413- // Swipe actions
414- onSwipeYDetected: {
415- if (steps > 0) {
416- simulateSwipeDown(steps);
417- } else {
418- simulateSwipeUp(-steps);
419- }
420- }
421- onSwipeXDetected: {
422- if (steps > 0) {
423- simulateSwipeRight(steps);
424- } else {
425- simulateSwipeLeft(-steps);
426- }
427- }
428- onTwoFingerSwipeYDetected: {
429- if (steps > 0) {
430- simulateDualSwipeDown(steps);
431- } else {
432- simulateDualSwipeUp(-steps);
433- }
434- }
435-
436- function simulateSwipeUp(steps) {
437- while(steps > 0) {
438- terminal.simulateKeyPress(Qt.Key_Up, Qt.NoModifier, true, 0, "");
439- steps--;
440- }
441- }
442- function simulateSwipeDown(steps) {
443- while(steps > 0) {
444- terminal.simulateKeyPress(Qt.Key_Down, Qt.NoModifier, true, 0, "");
445- steps--;
446- }
447- }
448- function simulateSwipeLeft(steps) {
449- while(steps > 0) {
450- terminal.simulateKeyPress(Qt.Key_Left, Qt.NoModifier, true, 0, "");
451- steps--;
452- }
453- }
454- function simulateSwipeRight(steps) {
455- while(steps > 0) {
456- terminal.simulateKeyPress(Qt.Key_Right, Qt.NoModifier, true, 0, "");
457- steps--;
458- }
459- }
460- function simulateDualSwipeUp(steps) {
461- while(steps > 0) {
462- terminal.simulateWheel(width * 0.5, height * 0.5, Qt.NoButton, Qt.NoModifier, Qt.point(0, -wheelValue));
463- steps--;
464- }
465- }
466- function simulateDualSwipeDown(steps) {
467- while(steps > 0) {
468- terminal.simulateWheel(width * 0.5, height * 0.5, Qt.NoButton, Qt.NoModifier, Qt.point(0, wheelValue));
469- steps--;
470- }
471- }
472-
473- // Semantic actions
474- onAlternateAction: {
475- // Force the hiddenButton in the event position.
476- hiddenButton.x = x;
477- hiddenButton.y = y;
478- PopupUtils.open(Qt.resolvedUrl("AlternateActionPopover.qml"), hiddenButton);
479- }
480- }
481-
482 Loader {
483 id: keyboardBarLoader
484 height: active ? units.gu(5) : 0
485@@ -227,7 +107,7 @@
486 backgroundColor: tabsBar.color
487 foregroundColor: tabsBar.foregroundColor
488 onSimulateKey: terminal.simulateKeyPress(key, mod, true, 0, "");
489- onSimulateCommand: terminal.session.sendText(command);
490+ onSimulateCommand: terminal.focusedTerminal.session.sendText(command);
491 }
492 }
493
494@@ -267,7 +147,7 @@
495 iconName: "close"
496 onTriggered: {
497 terminalPage.state = "DEFAULT";
498- PopupUtils.open(Qt.resolvedUrl("AlternateActionPopover.qml"), hiddenButton);
499+ PopupUtils.open(Qt.resolvedUrl("AlternateActionPopover.qml"));
500 }
501 }
502 }
503@@ -333,7 +213,6 @@
504 PropertyChanges { target: keyboardButton; visible: false }
505 PropertyChanges { target: bottomMessage; active: true }
506 PropertyChanges { target: keyboardBarLoader; enabled: false }
507- PropertyChanges { target: inputArea; enabled: false }
508 }
509 ]
510 }
511
512=== modified file 'src/app/qml/TerminalSettings.qml'
513--- src/app/qml/TerminalSettings.qml 2016-11-30 09:17:02 +0000
514+++ src/app/qml/TerminalSettings.qml 2017-01-12 13:29:38 +0000
515@@ -34,6 +34,12 @@
516 property alias shortcutCopy: innerSettings.shortcutCopy
517 property alias shortcutPaste: innerSettings.shortcutPaste
518 property alias shortcutFullscreen: innerSettings.shortcutFullscreen
519+ property alias shortcutSplitHorizontally: innerSettings.shortcutSplitHorizontally
520+ property alias shortcutSplitVertically: innerSettings.shortcutSplitVertically
521+ property alias shortcutMoveToTileAbove: innerSettings.shortcutMoveToTileAbove
522+ property alias shortcutMoveToTileBelow: innerSettings.shortcutMoveToTileBelow
523+ property alias shortcutMoveToTileLeft: innerSettings.shortcutMoveToTileLeft
524+ property alias shortcutMoveToTileRight: innerSettings.shortcutMoveToTileRight
525
526 readonly property int defaultFontSize: 12
527 readonly property int minFontSize: 4
528@@ -73,6 +79,12 @@
529 property string shortcutCopy: "Ctrl+Shift+C"
530 property string shortcutPaste: "Ctrl+Shift+V"
531 property string shortcutFullscreen: "F11"
532+ property string shortcutSplitHorizontally: "Ctrl+Shift+E"
533+ property string shortcutSplitVertically: "Ctrl+Shift+O"
534+ property string shortcutMoveToTileAbove: "Alt+Up"
535+ property string shortcutMoveToTileBelow: "Alt+Down"
536+ property string shortcutMoveToTileLeft: "Alt+Left"
537+ property string shortcutMoveToTileRight: "Alt+Right"
538 }
539
540 // Load the keyboard profiles.
541
542=== modified file 'src/app/qml/TerminalWindow.qml'
543--- src/app/qml/TerminalWindow.qml 2016-12-12 13:41:58 +0000
544+++ src/app/qml/TerminalWindow.qml 2017-01-12 13:29:38 +0000
545@@ -25,7 +25,7 @@
546 Window {
547 id: terminalWindow
548
549- title: tabsModel.currentItem ? tabsModel.currentItem.session.title : ""
550+ title: tabsModel.currentItem && tabsModel.currentItem.focusedTerminal ? tabsModel.currentItem.focusedTerminal.session.title : ""
551 color: terminalPage.active && terminalPage.terminal ? terminalPage.terminal.backgroundColor : theme.palette.selected.overlay
552 contentOrientation: Screen.orientation
553
554@@ -35,7 +35,7 @@
555 Binding {
556 target: terminalAppRoot
557 property: "focusedTerminal"
558- value: tabsModel.currentItem
559+ value: tabsModel.currentItem ? tabsModel.currentItem.focusedTerminal : null
560 when: terminalWindow.active
561 }
562
563@@ -71,6 +71,21 @@
564 }
565 }
566
567+ property TiledTerminalView tiledTerminalView: tabsModel.currentItem
568+ Shortcut {
569+ sequence: settings.shortcutSplitVertically
570+ onActivated: tiledTerminalView.splitTerminal(tiledTerminalView.focusedTerminal,
571+ Qt.Horizontal)
572+ enabled: tiledTerminalView.focusedTerminal.width >= 2 * tiledTerminalView.minimumTileWidth
573+ }
574+
575+ Shortcut {
576+ sequence: settings.shortcutSplitHorizontally
577+ onActivated: tiledTerminalView.splitTerminal(tiledTerminalView.focusedTerminal,
578+ Qt.Vertical)
579+ enabled: tiledTerminalView.focusedTerminal.height >= 2 * tiledTerminalView.minimumTileHeight
580+ }
581+
582 Shortcut {
583 sequence: settings.shortcutNewTab
584 onActivated: tabsModel.addTerminalTab()
585@@ -98,12 +113,12 @@
586
587 Shortcut {
588 sequence: settings.shortcutCopy
589- onActivated: tabsModel.currentItem.copyClipboard()
590+ onActivated: tabsModel.currentItem.focusedTerminal.copyClipboard()
591 }
592
593 Shortcut {
594 sequence: settings.shortcutPaste
595- onActivated: tabsModel.currentItem.pasteClipboard()
596+ onActivated: tabsModel.currentItem.focusedTerminal.pasteClipboard()
597 }
598
599 Shortcut {
600@@ -141,7 +156,7 @@
601 TerminalPage {
602 id: terminalPage
603 tabsModel: tabsModel
604- terminal: tabsModel.currentItem
605+ terminal: tabsModel.currentItem ? tabsModel.currentItem.focusedTerminal : null
606 narrowLayout: terminalWindow.narrowLayout
607 // Hide terminal data when the access is still not granted
608 layer.enabled: authService.isDialogVisible
609
610=== added file 'src/app/qml/TiledTerminalView.qml'
611--- src/app/qml/TiledTerminalView.qml 1970-01-01 00:00:00 +0000
612+++ src/app/qml/TiledTerminalView.qml 2017-01-12 13:29:38 +0000
613@@ -0,0 +1,96 @@
614+/*
615+ * Copyright (C) 2016-2017 Canonical Ltd
616+ *
617+ * This program is free software: you can redistribute it and/or modify
618+ * it under the terms of the GNU General Public License version 3 as
619+ * published by the Free Software Foundation.
620+ *
621+ * This program is distributed in the hope that it will be useful,
622+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
623+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
624+ * GNU General Public License for more details.
625+ *
626+ * You should have received a copy of the GNU General Public License
627+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
628+ *
629+ * Authored-by: Florian Boucault <florian.boucault@canonical.com>
630+ */
631+import QtQuick 2.5
632+import Ubuntu.Components 1.3
633+
634+TiledView {
635+ id: tiledTerminalView
636+ anchors.fill: parent
637+
638+ property string initialWorkingDirectory
639+ property Terminal focusedTerminal
640+ signal emptied
641+ onCountChanged: if (count == 0) emptied()
642+
643+ function splitTerminal(terminal, orientation) {
644+ var initialWorkingDirectory = focusedTerminal.session.getWorkingDirectory();
645+ var newTerminal = terminalComponent.createObject(tiledTerminalView,
646+ {"initialWorkingDirectory": initialWorkingDirectory});
647+ tiledTerminalView.setOrientation(terminal, orientation);
648+ tiledTerminalView.add(terminal, newTerminal, Qt.AlignTrailing);
649+ }
650+
651+ handleDelegate: Rectangle {
652+ implicitWidth: units.dp(1)
653+ implicitHeight: units.dp(1)
654+ color: focusedTerminal ? focusedTerminal.contourColor : ""
655+ }
656+
657+ Component.onCompleted: {
658+ var newTerminal = terminalComponent.createObject(tiledTerminalView,
659+ {"initialWorkingDirectory": initialWorkingDirectory});
660+ setRootItem(newTerminal);
661+ }
662+
663+ function moveFocus(direction) {
664+ var terminal = tiledTerminalView.closestTileInDirection(focusedTerminal, direction);
665+ if (terminal) {
666+ terminal.focus = true;
667+ }
668+ }
669+
670+ Shortcut {
671+ sequence: settings.shortcutMoveToTileRight
672+ enabled: tiledTerminalView.focus
673+ onActivated: moveFocus(Qt.AlignRight)
674+ }
675+
676+ Shortcut {
677+ sequence: settings.shortcutMoveToTileLeft
678+ enabled: tiledTerminalView.focus
679+ onActivated: moveFocus(Qt.AlignLeft)
680+ }
681+
682+ Shortcut {
683+ sequence: settings.shortcutMoveToTileAbove
684+ enabled: tiledTerminalView.focus
685+ onActivated: moveFocus(Qt.AlignTop)
686+ }
687+
688+ Shortcut {
689+ sequence: settings.shortcutMoveToTileBelow
690+ enabled: tiledTerminalView.focus
691+ onActivated: moveFocus(Qt.AlignBottom)
692+ }
693+
694+ property real minimumTileWidth: units.gu(10)
695+ property real minimumTileHeight: units.gu(10)
696+ property Component terminalComponent: Terminal {
697+ id: terminal
698+ Component.onCompleted: if (focus) tiledTerminalView.focusedTerminal = terminal
699+ onFocusChanged: if (focus) tiledTerminalView.focusedTerminal = terminal
700+ onFinished: {
701+ if (terminal.focus) {
702+ var nextTerminal = tiledTerminalView.closestTile(terminal);
703+ if (nextTerminal) nextTerminal.focus = true;
704+ }
705+ tiledTerminalView.remove(terminal);
706+ terminal.destroy();
707+ }
708+ }
709+}
710
711=== added file 'src/app/qml/TiledView.qml'
712--- src/app/qml/TiledView.qml 1970-01-01 00:00:00 +0000
713+++ src/app/qml/TiledView.qml 2017-01-12 13:29:38 +0000
714@@ -0,0 +1,135 @@
715+/*
716+ * Copyright (C) 2016-2017 Canonical Ltd
717+ *
718+ * This program is free software: you can redistribute it and/or modify
719+ * it under the terms of the GNU General Public License version 3 as
720+ * published by the Free Software Foundation.
721+ *
722+ * This program is distributed in the hope that it will be useful,
723+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
724+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
725+ * GNU General Public License for more details.
726+ *
727+ * You should have received a copy of the GNU General Public License
728+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
729+ *
730+ * Authored-by: Florian Boucault <florian.boucault@canonical.com>
731+ */
732+import QtQuick 2.5
733+import "binarytree.js" as BinaryTree
734+
735+FocusScope {
736+ id: tiledView
737+
738+ property Component handleDelegate: Rectangle {
739+ implicitWidth: units.dp(1)
740+ implicitHeight: units.dp(1)
741+ color: "white"
742+ }
743+
744+ property int count: 0
745+
746+ // FIXME: odd semantics: what if setRootItem is called later?
747+ function setRootItem(rootItem) {
748+ if (rootItem && rootItem === __rootNode.value) {
749+ return null;
750+ }
751+
752+ var oldRoot = __rootNode.value;
753+ if (rootItem) {
754+ count = 1;
755+ } else {
756+ count = 0;
757+ __rootNode.cleanup();
758+ }
759+ __rootNode.setValue(rootItem);
760+
761+ return oldRoot;
762+ }
763+
764+ property var __rootNode: new BinaryTree.Node()
765+ Component.onDestruction: __rootNode.cleanup()
766+
767+ Component.onCompleted: {
768+ __rootNode.setWidth(width);
769+ __rootNode.setHeight(height);
770+ }
771+
772+ onWidthChanged: __rootNode.setWidth(width)
773+ onHeightChanged: __rootNode.setHeight(height)
774+
775+ Component {
776+ id: separatorComponent
777+ TiledViewSeparator {
778+ handleDelegate: tiledView.handleDelegate
779+ }
780+ }
781+
782+ function add(obj, newObj, side) {
783+ var node = __rootNode.findNodeWithValue(obj);
784+ var otherSide;
785+ if (side == Qt.AlignLeading) {
786+ otherSide = Qt.AlignTrailing;
787+ } else {
788+ otherSide = Qt.AlignLeading;
789+ }
790+
791+ node.value = null;
792+ node.setLeftRatio(0.5);
793+ var separator = separatorComponent.createObject(tiledView, {"node": node});
794+ node.setSeparator(separator);
795+
796+ var nodeSide = new BinaryTree.Node();
797+ nodeSide.setValue(newObj);
798+ node.setChild(side, nodeSide);
799+
800+ var nodeOtherSide = new BinaryTree.Node();
801+ nodeOtherSide.setValue(obj);
802+ node.setChild(otherSide, nodeOtherSide);
803+ count += 1;
804+ }
805+
806+ function remove(obj) {
807+ var node = __rootNode.findNodeWithValue(obj);
808+ var sibling = node.getSibling();
809+ if (sibling) {
810+ node.parent.copy(sibling);
811+ }
812+ count -= 1;
813+ }
814+
815+ function closestTile(obj) {
816+ var node = __rootNode.findNodeWithValue(obj);
817+ var sibling = node.closestNodeWithValue();
818+ if (sibling) {
819+ return sibling.value;
820+ } else {
821+ return null;
822+ }
823+ }
824+
825+ function closestTileInDirection(obj, direction) {
826+ var node = __rootNode.findNodeWithValue(obj);
827+ var closestNode = node.closestNodeWithValueInDirection(direction);
828+ if (closestNode && closestNode.value) {
829+ return closestNode.value;
830+ } else {
831+ return null;
832+ }
833+ }
834+
835+ function getOrientation(obj) {
836+ var node = __rootNode.findNodeWithValue(obj);
837+ return node.orientation;
838+ }
839+
840+ function setOrientation(obj, orientation) {
841+ var node = __rootNode.findNodeWithValue(obj);
842+ node.setOrientation(orientation);
843+ }
844+
845+ function move(obj, targetObj, side) {
846+ remove(obj);
847+ add(targetObj, obj, side);
848+ }
849+}
850
851=== added file 'src/app/qml/TiledViewSeparator.qml'
852--- src/app/qml/TiledViewSeparator.qml 1970-01-01 00:00:00 +0000
853+++ src/app/qml/TiledViewSeparator.qml 2017-01-12 13:29:38 +0000
854@@ -0,0 +1,76 @@
855+/*
856+ * Copyright (C) 2016-2017 Canonical Ltd
857+ *
858+ * This program is free software: you can redistribute it and/or modify
859+ * it under the terms of the GNU General Public License version 3 as
860+ * published by the Free Software Foundation.
861+ *
862+ * This program is distributed in the hope that it will be useful,
863+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
864+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
865+ * GNU General Public License for more details.
866+ *
867+ * You should have received a copy of the GNU General Public License
868+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
869+ *
870+ * Authored-by: Florian Boucault <florian.boucault@canonical.com>
871+ */
872+import QtQuick 2.4
873+import Ubuntu.Components 1.3
874+
875+Item {
876+ id: separator
877+
878+ property int orientation: Qt.Horizontal
879+ property var node
880+ property Component handleDelegate
881+
882+ Loader {
883+ id: handleLoader
884+ sourceComponent: handleDelegate
885+ anchors.fill: parent
886+ }
887+
888+ implicitWidth: handleLoader.implicitWidth
889+ implicitHeight: handleLoader.implicitHeight
890+ z: 1
891+
892+ MouseArea {
893+ anchors.centerIn: parent
894+ width: orientation == Qt.Vertical ? units.gu(1) : parent.width
895+ height: orientation == Qt.Vertical ? parent.height : units.gu(1)
896+ cursorShape: orientation == Qt.Horizontal ? Qt.SizeVerCursor : Qt.SizeHorCursor
897+ drag {
898+ axis: orientation == Qt.Horizontal ? Drag.YAxis : Drag.XAxis
899+ target: resizer
900+ smoothed: false
901+ }
902+ onPressed: {
903+ resizer.initialRatio = node.leftRatio;
904+ resizer.x = 0;
905+ resizer.y = 0;
906+ }
907+ enabled: separator.visible
908+ }
909+
910+ function clamp(value, min, max) {
911+ return Math.min(Math.max(min, value), max);
912+ }
913+
914+ Item {
915+ id: resizer
916+ property real initialRatio
917+ property real minimumRatio: 0.1
918+ parent: null
919+ onXChanged: {
920+ var ratio = initialRatio + x / node.width;
921+ ratio = clamp(ratio, minimumRatio, 1.0-minimumRatio);
922+ node.setLeftRatio(ratio);
923+ }
924+ onYChanged: {
925+ var ratio = initialRatio + y / node.height;
926+ ratio = clamp(ratio, minimumRatio, 1.0-minimumRatio);
927+ node.setLeftRatio(ratio);
928+ }
929+ }
930+}
931
932=== added file 'src/app/qml/binarytree.js'
933--- src/app/qml/binarytree.js 1970-01-01 00:00:00 +0000
934+++ src/app/qml/binarytree.js 2017-01-12 13:29:38 +0000
935@@ -0,0 +1,378 @@
936+/*
937+ * Copyright (C) 2016-2017 Canonical Ltd
938+ *
939+ * This program is free software: you can redistribute it and/or modify
940+ * it under the terms of the GNU General Public License version 3 as
941+ * published by the Free Software Foundation.
942+ *
943+ * This program is distributed in the hope that it will be useful,
944+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
945+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
946+ * GNU General Public License for more details.
947+ *
948+ * You should have received a copy of the GNU General Public License
949+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
950+ *
951+ * Authored-by: Florian Boucault <florian.boucault@canonical.com>
952+ */
953+.pragma library
954+
955+function Node() {
956+ this.value = null;
957+ this.parent = null;
958+ this.left = null;
959+ this.right = null;
960+ this.separator = null;
961+ this.leftRatio = 0.5;
962+ this.rightRatio = 0.5;
963+ this.orientation = Qt.Horizontal;
964+ this.originX = 0;
965+ this.originY = 0;
966+ this.x = 0;
967+ this.y = 0;
968+ this.width = 0;
969+ this.height = 0;
970+}
971+
972+Node.prototype.cleanup = function cleanup(value) {
973+ if (this.separator) {
974+ this.separator.destroy();
975+ this.separator = null;
976+ }
977+ this.parent = null;
978+ this.value = null;
979+ if (this.left) {
980+ this.left.cleanup();
981+ this.left = null;
982+ }
983+ if (this.right) {
984+ this.right.cleanup();
985+ this.right = null;
986+ }
987+}
988+
989+Node.prototype.setValue = function setValue(value) {
990+ this.value = value;
991+ this.updateX();
992+ this.updateY();
993+ this.updateWidth();
994+ this.updateHeight();
995+}
996+
997+Node.prototype.updateWidth = function updateWidth() {
998+ if (this.value) {
999+ this.value.width = this.width;
1000+ } else if (this.orientation == Qt.Horizontal) {
1001+ if (this.right && !this.left) {
1002+ this.right.setWidth(this.width);
1003+ this.right.setX(0);
1004+ }
1005+ if (this.left && !this.right) {
1006+ this.left.setWidth(this.width);
1007+ this.left.setX(0);
1008+ }
1009+ if (this.right && this.left) {
1010+ this.left.setWidth(this.width * this.leftRatio);
1011+ this.left.setX(0);
1012+ this.right.setWidth(this.width * this.rightRatio);
1013+ this.right.setX(this.left.width);
1014+ }
1015+ } else if (this.orientation == Qt.Vertical) {
1016+ if (this.left) {
1017+ this.left.setWidth(this.width);
1018+ this.left.setX(0);
1019+ }
1020+ if (this.right) {
1021+ this.right.setWidth(this.width);
1022+ this.right.setX(0);
1023+ }
1024+ }
1025+ this.updateSeparator();
1026+}
1027+
1028+Node.prototype.setWidth = function setWidth(width) {
1029+ this.width = width;
1030+ this.updateWidth();
1031+};
1032+
1033+Node.prototype.updateHeight = function updateHeight() {
1034+ if (this.value) {
1035+ this.value.height = this.height;
1036+ } else if (this.orientation == Qt.Vertical) {
1037+ if (this.right && !this.left) {
1038+ this.right.setHeight(this.height);
1039+ this.right.setY(0);
1040+ }
1041+ if (this.left && !this.right) {
1042+ this.left.setHeight(this.height);
1043+ this.left.setY(0);
1044+ }
1045+ if (this.right && this.left) {
1046+ this.left.setHeight(this.height * this.leftRatio);
1047+ this.left.setY(0);
1048+ this.right.setHeight(this.height * this.rightRatio);
1049+ this.right.setY(this.left.height);
1050+ }
1051+ } else if (this.orientation == Qt.Horizontal) {
1052+ if (this.left) {
1053+ this.left.setHeight(this.height);
1054+ this.left.setY(0);
1055+ }
1056+ if (this.right) {
1057+ this.right.setHeight(this.height);
1058+ this.right.setY(0);
1059+ }
1060+ }
1061+ this.updateSeparator();
1062+}
1063+
1064+Node.prototype.setHeight = function setHeight(height) {
1065+ this.height = height;
1066+ this.updateHeight();
1067+};
1068+
1069+Node.prototype.setLeftRatio = function setLeftRatio(ratio) {
1070+ this.leftRatio = ratio;
1071+ this.rightRatio = 1.0 - this.leftRatio;
1072+ if (this.orientation == Qt.Horizontal) {
1073+ this.updateWidth();
1074+ } else {
1075+ this.updateHeight();
1076+ }
1077+};
1078+
1079+Node.prototype.setRightRatio = function setRightRatio(ratio) {
1080+ this.rightRatio = ratio;
1081+ this.leftRatio = 1.0 - this.rightRatio;
1082+ if (this.orientation == Qt.Horizontal) {
1083+ this.updateWidth();
1084+ } else {
1085+ this.updateHeight();
1086+ }
1087+};
1088+
1089+Node.prototype.setChild = function setChild(side, childNode) {
1090+ switch (side) {
1091+ case Qt.AlignLeading:
1092+ if (this.left) {
1093+ // FIXME: breaks copy()
1094+// this.left.cleanup();
1095+ }
1096+ this.left = childNode;
1097+ break;
1098+ case Qt.AlignTrailing:
1099+ if (this.right) {
1100+ // FIXME: breaks copy()
1101+// this.right.cleanup();
1102+ }
1103+ this.right = childNode;
1104+ break;
1105+ default:
1106+ break;
1107+ }
1108+ if (childNode) {
1109+ childNode.parent = this;
1110+ }
1111+ this.updateX();
1112+ this.updateY();
1113+ this.updateWidth();
1114+ this.updateHeight();
1115+}
1116+
1117+Node.prototype.updateSeparator = function updateSeparator() {
1118+ if (this.separator) {
1119+ this.separator.orientation = this.orientation == Qt.Vertical ? Qt.Horizontal : Qt.Vertical;
1120+ if (this.left && this.right) {
1121+ // FIXME: separator should be centered
1122+ this.separator.x = this.right.originX + this.right.x;
1123+ this.separator.y = this.right.originY + this.right.y;
1124+ if (this.separator.orientation == Qt.Vertical) {
1125+ this.separator.width = this.separator.implicitWidth;
1126+ this.separator.height = this.right.height;
1127+ } else if (this.separator.orientation == Qt.Horizontal) {
1128+ this.separator.width = this.right.width;
1129+ this.separator.height = this.separator.implicitHeight;
1130+ }
1131+ this.separator.visible = true;
1132+ } else {
1133+ this.separator.visible = false;
1134+ }
1135+ }
1136+}
1137+
1138+Node.prototype.setSeparator = function setSeparator(separator) {
1139+ this.separator = separator;
1140+ this.updateSeparator();
1141+}
1142+
1143+Node.prototype.copy = function copy(otherNode) {
1144+ if (!otherNode) return;
1145+ var newParent = otherNode.parent;
1146+
1147+ this.orientation = otherNode.orientation;
1148+ this.setValue(otherNode.value);
1149+ this.setChild(Qt.AlignLeading, otherNode.left);
1150+ this.setChild(Qt.AlignTrailing, otherNode.right);
1151+
1152+ if (newParent.left === otherNode) {
1153+ newParent.setChild(Qt.AlignLeading, this);
1154+ } else if (otherNode.parent.right === otherNode) {
1155+ newParent.setChild(Qt.AlignTrailing, this);
1156+ }
1157+ otherNode.left = null;
1158+ otherNode.right = null;
1159+ otherNode.cleanup();
1160+ this.updateX();
1161+ this.updateY();
1162+ this.updateWidth();
1163+ this.updateHeight();
1164+}
1165+
1166+Node.prototype.setOrientation = function setOrientation(orientation) {
1167+ this.orientation = orientation;
1168+ this.updateWidth();
1169+ this.updateHeight();
1170+ this.updateSeparator();
1171+}
1172+
1173+Node.prototype.updateX = function updateX() {
1174+ var absoluteX = this.originX + this.x;
1175+ if (this.value) {
1176+ this.value.x = Math.round(absoluteX);
1177+ } else {
1178+ if (this.left) {
1179+ this.left.setOriginX(absoluteX);
1180+ }
1181+ if (this.right) {
1182+ this.right.setOriginX(absoluteX);
1183+ }
1184+ }
1185+ this.updateSeparator();
1186+}
1187+
1188+Node.prototype.setX = function setX(x) {
1189+ this.x = x;
1190+ this.updateX();
1191+};
1192+
1193+Node.prototype.setOriginX = function setOriginX(x) {
1194+ this.originX = x;
1195+ this.updateX();
1196+};
1197+
1198+Node.prototype.updateY = function updateY() {
1199+ var absoluteY = this.originY + this.y;
1200+ if (this.value) {
1201+ this.value.y = Math.round(absoluteY);
1202+ } else {
1203+ if (this.left) {
1204+ this.left.setOriginY(absoluteY);
1205+ }
1206+ if (this.right) {
1207+ this.right.setOriginY(absoluteY);
1208+ }
1209+ }
1210+ this.updateSeparator();
1211+}
1212+
1213+Node.prototype.setY = function setY(y) {
1214+ this.y = y;
1215+ this.updateY();
1216+};
1217+
1218+Node.prototype.setOriginY = function setOriginY(y) {
1219+ this.originY = y;
1220+ this.updateY();
1221+};
1222+
1223+Node.prototype.getSibling = function getSibling() {
1224+ if (this.parent) {
1225+ if (this.parent.left === this) {
1226+ return this.parent.right;
1227+ } else {
1228+ return this.parent.left;
1229+ }
1230+ }
1231+ return null;
1232+}
1233+
1234+Node.prototype.findNodeWithValue = function findNodeWithValue(value) {
1235+ if (this.value === value) return this;
1236+ var result;
1237+ if (this.left) {
1238+ result = this.left.findNodeWithValue(value);
1239+ if (result) {
1240+ return result;
1241+ }
1242+ }
1243+ if (this.right) {
1244+ result = this.right.findNodeWithValue(value);
1245+ if (result) {
1246+ return result;
1247+ }
1248+ }
1249+ return null;
1250+};
1251+
1252+Node.prototype.closestChildWithValue = function closestChildWithValue(sides) {
1253+ var currentLevelNodes = [this];
1254+ var nextLevelNodes = [];
1255+ while (currentLevelNodes.length != 0) {
1256+ for (var i=0; i<currentLevelNodes.length; i++) {
1257+ var node = currentLevelNodes[i];
1258+ if (node.value) {
1259+ return node;
1260+ }
1261+ if ((sides == undefined || sides & Qt.AlignLeading) && node.left) {
1262+ nextLevelNodes.push(node.left);
1263+ }
1264+ if ((sides == undefined || sides & Qt.AlignTrailing) && node.right) {
1265+ nextLevelNodes.push(node.right);
1266+ }
1267+ }
1268+ currentLevelNodes = nextLevelNodes;
1269+ nextLevelNodes = [];
1270+ }
1271+ return null;
1272+}
1273+
1274+Node.prototype.closestNodeWithValue = function closestNodeWithValue() {
1275+ // explore sibling hierarchy
1276+ var sibling = this.getSibling();
1277+ if (sibling) {
1278+ var closestChild = sibling.closestChildWithValue(Qt.AlignLeading | Qt.AlignTrailing);
1279+ if (closestChild) {
1280+ return closestChild;
1281+ }
1282+ }
1283+ // explore parent's sibling hierarchy
1284+ if (this.parent) {
1285+ var parentSibling = this.parent.getSibling();
1286+ if (parentSibling) {
1287+ var closestChild = parentSibling.closestChildWithValue(Qt.AlignLeading | Qt.AlignTrailing);
1288+ if (closestChild) {
1289+ return closestChild;
1290+ }
1291+ }
1292+ }
1293+ return null;
1294+};
1295+
1296+Node.prototype.closestNodeWithValueInDirection = function closestNodeWithValueInDirection(direction) {
1297+ if (this.parent) {
1298+ if (this.parent.left === this) {
1299+ if ((direction == Qt.AlignRight && this.parent.orientation == Qt.Horizontal) ||
1300+ (direction == Qt.AlignBottom && this.parent.orientation == Qt.Vertical)) {
1301+ return this.parent.right.closestChildWithValue(Qt.AlignLeading);
1302+ }
1303+ } else if (this.parent.right === this) {
1304+ if ((direction == Qt.AlignLeft && this.parent.orientation == Qt.Horizontal) ||
1305+ (direction == Qt.AlignTop && this.parent.orientation == Qt.Vertical)) {
1306+ return this.parent.left.closestChildWithValue(Qt.AlignTrailing);
1307+ }
1308+ }
1309+ return this.parent.closestNodeWithValueInDirection(direction);
1310+ } else {
1311+ return null;
1312+ }
1313+}
1314
1315=== modified file 'src/plugin/qmltermwidget/lib/TerminalDisplay.h'
1316--- src/plugin/qmltermwidget/lib/TerminalDisplay.h 2017-01-06 08:37:18 +0000
1317+++ src/plugin/qmltermwidget/lib/TerminalDisplay.h 2017-01-12 13:29:38 +0000
1318@@ -898,8 +898,8 @@
1319
1320 //the delay in milliseconds between redrawing blinking text
1321 static const int TEXT_BLINK_DELAY = 500;
1322- static const int DEFAULT_LEFT_MARGIN = 1;
1323- static const int DEFAULT_TOP_MARGIN = 1;
1324+ static const int DEFAULT_LEFT_MARGIN = 8;
1325+ static const int DEFAULT_TOP_MARGIN = 8;
1326
1327 // QMLTermWidget port functions
1328 QFont m_font;
1329
1330=== modified file 'tests/CMakeLists.txt'
1331--- tests/CMakeLists.txt 2015-02-24 23:23:36 +0000
1332+++ tests/CMakeLists.txt 2017-01-12 13:29:38 +0000
1333@@ -1,1 +1,2 @@
1334 add_subdirectory(autopilot)
1335+add_subdirectory(qtquicktest)
1336
1337=== added directory 'tests/qtquicktest'
1338=== added file 'tests/qtquicktest/CMakeLists.txt'
1339--- tests/qtquicktest/CMakeLists.txt 1970-01-01 00:00:00 +0000
1340+++ tests/qtquicktest/CMakeLists.txt 2017-01-12 13:29:38 +0000
1341@@ -0,0 +1,20 @@
1342+find_program(XVFB_RUN_BIN xvfb-run)
1343+if(NOT XVFB_RUN_BIN)
1344+ message(FATAL_ERROR "Could not find xvfb-run, please install the xvfb package")
1345+endif()
1346+set(XVFB_RUN_CMD ${XVFB_RUN_BIN} -a -s "-screen 0 1024x768x24")
1347+
1348+include(FindPkgConfig)
1349+find_package(Qt5Core)
1350+
1351+# copy qml test files to build directory
1352+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
1353+add_custom_target(qmlTestFiles ALL
1354+ COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/*.qml ${CMAKE_CURRENT_BINARY_DIR}
1355+)
1356+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
1357+
1358+set(QTQUICK_TEST tst_qtquicktest)
1359+add_executable(${QTQUICK_TEST} qtquicktest.cpp)
1360+qt5_use_modules(${QTQUICK_TEST} Core Qml Quick Test QuickTest)
1361+add_test(${QTQUICK_TEST} ${XVFB_RUN_CMD} ${CMAKE_CURRENT_BINARY_DIR}/${QTQUICK_TEST})
1362
1363=== added file 'tests/qtquicktest/qtquicktest.cpp'
1364--- tests/qtquicktest/qtquicktest.cpp 1970-01-01 00:00:00 +0000
1365+++ tests/qtquicktest/qtquicktest.cpp 2017-01-12 13:29:38 +0000
1366@@ -0,0 +1,18 @@
1367+/*
1368+ * Copyright (C) 2012 Canonical, Ltd.
1369+ *
1370+ * This program is free software; you can redistribute it and/or modify
1371+ * it under the terms of the GNU General Public License as published by
1372+ * the Free Software Foundation; version 3.
1373+ *
1374+ * This program is distributed in the hope that it will be useful,
1375+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1376+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1377+ * GNU General Public License for more details.
1378+ *
1379+ * You should have received a copy of the GNU General Public License
1380+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1381+ */
1382+
1383+#include <QtQuickTest/quicktest.h>
1384+QUICK_TEST_MAIN(qtquicktest)
1385
1386=== added file 'tests/qtquicktest/tst_TiledView.qml'
1387--- tests/qtquicktest/tst_TiledView.qml 1970-01-01 00:00:00 +0000
1388+++ tests/qtquicktest/tst_TiledView.qml 2017-01-12 13:29:38 +0000
1389@@ -0,0 +1,468 @@
1390+/*
1391+ * Copyright (C) 2017 Canonical Ltd
1392+ *
1393+ * This program is free software: you can redistribute it and/or modify
1394+ * it under the terms of the GNU General Public License version 3 as
1395+ * published by the Free Software Foundation.
1396+ *
1397+ * This program is distributed in the hope that it will be useful,
1398+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1399+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1400+ * GNU General Public License for more details.
1401+ *
1402+ * You should have received a copy of the GNU General Public License
1403+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1404+ *
1405+ * Authored-by: Florian Boucault <florian.boucault@canonical.com>
1406+ */
1407+
1408+import QtQuick 2.4
1409+import QtTest 1.0
1410+import "../../src/app/qml"
1411+
1412+TiledView {
1413+ id: tiledView
1414+ width: 800
1415+ height: 600
1416+
1417+ TestCase {
1418+ name: "TiledView"
1419+ when: windowShown
1420+
1421+ property Component objectComponent: Rectangle {
1422+ width: 100
1423+ height: 50
1424+ color: "red"
1425+ }
1426+
1427+ SignalSpy {
1428+ id: countSpy
1429+ target: tiledView
1430+ signalName: "countChanged"
1431+ }
1432+
1433+ function init() {
1434+ countSpy.clear();
1435+ }
1436+
1437+ function test_defaults() {
1438+ compare(tiledView.count, 0);
1439+ }
1440+
1441+ function test_setRootItemValid() {
1442+ var rightObject = objectComponent.createObject(tiledView);
1443+ var oldRoot = tiledView.setRootItem(rightObject);
1444+ compare(oldRoot, null);
1445+ compare(countSpy.count, 1);
1446+ compare(tiledView.count, 1);
1447+ compare(rightObject.width, tiledView.width);
1448+ compare(rightObject.height, tiledView.height);
1449+ compare(tiledView.getOrientation(rightObject), Qt.Horizontal);
1450+ tiledView.setRootItem(null);
1451+ rightObject.destroy();
1452+ }
1453+
1454+ function test_setRootItemNull() {
1455+ var oldRoot = tiledView.setRootItem(null);
1456+ compare(oldRoot, null);
1457+ compare(countSpy.count, 0);
1458+ compare(tiledView.count, 0);
1459+ }
1460+
1461+ function test_resetRootItem() {
1462+ // set to new object
1463+ var rightObject = objectComponent.createObject(tiledView);
1464+ var oldRoot = tiledView.setRootItem(rightObject);
1465+ compare(oldRoot, null);
1466+ compare(countSpy.count, 1);
1467+ compare(tiledView.count, 1);
1468+
1469+ // set to same object
1470+ oldRoot = tiledView.setRootItem(rightObject);
1471+ compare(oldRoot, null);
1472+ compare(countSpy.count, 1);
1473+ compare(tiledView.count, 1);
1474+
1475+ // set to another new object
1476+ var rightObject2 = objectComponent.createObject(tiledView);
1477+ oldRoot = tiledView.setRootItem(rightObject2);
1478+ compare(oldRoot, rightObject);
1479+ compare(countSpy.count, 1);
1480+ compare(tiledView.count, 1);
1481+
1482+ // set to null
1483+ oldRoot = tiledView.setRootItem(null);
1484+ compare(oldRoot, rightObject2);
1485+ compare(countSpy.count, 2);
1486+ compare(tiledView.count, 0);
1487+
1488+ rightObject.destroy();
1489+ rightObject2.destroy();
1490+ }
1491+
1492+ function verifySetRootItem(object) {
1493+ tiledView.setRootItem(object);
1494+ compare(tiledView.count, 1);
1495+ compare(object.width, tiledView.width);
1496+ compare(object.height, tiledView.height);
1497+ }
1498+
1499+ function verifySetOrientation(object, orientation) {
1500+ tiledView.setOrientation(object, orientation);
1501+ compare(tiledView.getOrientation(object), orientation);
1502+ }
1503+
1504+ function verifyAdd(object, rightObject, side) {
1505+ var previousX = object.x;
1506+ var previousY = object.y;
1507+ var previousWidth = object.width;
1508+ var previousHeight = object.height;
1509+ var previousCount = tiledView.count;
1510+ var orientation = tiledView.getOrientation(object);
1511+
1512+ tiledView.add(object, rightObject, side);
1513+ compare(tiledView.count, previousCount+1);
1514+ compare(tiledView.getOrientation(object), Qt.Horizontal);
1515+ compare(tiledView.getOrientation(rightObject), Qt.Horizontal);
1516+
1517+ if (orientation == Qt.Horizontal) {
1518+ compare(object.width, previousWidth / 2);
1519+ compare(object.height, previousHeight);
1520+ compare(rightObject.width, previousWidth / 2);
1521+ compare(rightObject.height, previousHeight);
1522+ if (side == Qt.AlignTrailing) {
1523+ compare(object.x, previousX);
1524+ compare(object.y, previousY);
1525+ compare(rightObject.x, previousX + Math.round(previousWidth / 2));
1526+ compare(rightObject.y, previousY);
1527+ } else if (side == Qt.AlignLeading) {
1528+ compare(rightObject.x, previousX);
1529+ compare(rightObject.y, previousY);
1530+ compare(object.x, previousX + Math.round(previousWidth / 2));
1531+ compare(object.y, previousY);
1532+ }
1533+ } else if (orientation == Qt.Vertical) {
1534+ compare(object.width, previousWidth);
1535+ compare(object.height, previousHeight / 2);
1536+ compare(rightObject.width, previousWidth);
1537+ compare(rightObject.height, previousHeight / 2);
1538+ if (side == Qt.AlignTrailing) {
1539+ compare(object.x, previousX);
1540+ compare(object.y, previousY);
1541+ compare(rightObject.x, previousX);
1542+ compare(rightObject.y, previousY + Math.round(previousHeight / 2));
1543+ } else if (side == Qt.AlignLeading) {
1544+ compare(rightObject.x, previousX);
1545+ compare(rightObject.y, previousY);
1546+ compare(object.x, previousX);
1547+ compare(object.y, previousY + Math.round(previousHeight / 2));
1548+ }
1549+ }
1550+ }
1551+
1552+ function verifyRemove(object) {
1553+ var node = tiledView.__rootNode.findNodeWithValue(object);
1554+ var siblingNode = node.getSibling();
1555+ var siblingObject = siblingNode.value;
1556+
1557+ var side;
1558+ if (node.parent.left === node) {
1559+ side = Qt.AlignLeading;
1560+ } else {
1561+ side = Qt.AlignTrailing;
1562+ }
1563+ var orientation = node.parent.orientation;
1564+
1565+ var expectedX;
1566+ var expectedY;
1567+ var expectedWidth;
1568+ var expectedHeight;
1569+ if (orientation == Qt.Horizontal) {
1570+ expectedWidth = object.width + siblingNode.width;
1571+ expectedHeight = siblingNode.height;
1572+ if (side == Qt.AlignTrailing) {
1573+ expectedX = siblingNode.x;
1574+ expectedY = siblingNode.y;
1575+ } else if (side == Qt.AlignLeading) {
1576+ expectedX = object.x;
1577+ expectedY = siblingNode.y;
1578+ }
1579+ } else if (orientation == Qt.Vertical) {
1580+ expectedWidth = siblingNode.width;
1581+ expectedHeight = object.height + siblingNode.height;
1582+ if (side == Qt.AlignTrailing) {
1583+ expectedX = siblingNode.x;
1584+ expectedY = siblingNode.y;
1585+ } else if (side == Qt.AlignLeading) {
1586+ expectedX = siblingNode.x;
1587+ expectedY = object.y;
1588+ }
1589+ }
1590+
1591+ var previousCount = tiledView.count;
1592+
1593+ tiledView.remove(object);
1594+ // TODO: we verify that the resulting node has the correct x/y,width/height
1595+ // but we could go further and verify that all its children also do
1596+ var removeNode = tiledView.__rootNode.findNodeWithValue(siblingObject);
1597+ compare(tiledView.count, previousCount-1);
1598+ compare(removeNode.width, expectedWidth);
1599+ compare(removeNode.height, expectedHeight);
1600+ compare(removeNode.x, expectedX);
1601+ compare(removeNode.y, expectedY);
1602+ }
1603+
1604+ function test_simpleAdd_data() {
1605+ return [
1606+ {orientation: Qt.Horizontal, side: Qt.AlignTrailing},
1607+ {orientation: Qt.Vertical, side: Qt.AlignTrailing},
1608+ {orientation: Qt.Horizontal, side: Qt.AlignLeading},
1609+ {orientation: Qt.Vertical, side: Qt.AlignLeading},
1610+ ];
1611+ }
1612+
1613+ function test_simpleAdd(data) {
1614+ var rootObject = objectComponent.createObject(tiledView);
1615+ verifySetRootItem(rootObject);
1616+ verifySetOrientation(rootObject, data.orientation);
1617+
1618+ var rightObject = objectComponent.createObject(tiledView);
1619+ verifyAdd(rootObject, rightObject, data.side);
1620+
1621+ tiledView.setRootItem(null);
1622+ rootObject.destroy();
1623+ rightObject.destroy();
1624+ }
1625+
1626+ function test_nestedAdds() {
1627+ var objects = [];
1628+
1629+ objects["0"] = objectComponent.createObject(tiledView);
1630+ verifySetRootItem(objects["0"]);
1631+
1632+ objects["1"] = objectComponent.createObject(tiledView);
1633+ verifySetOrientation(objects["0"], Qt.Horizontal);
1634+ verifyAdd(objects["0"], objects["1"], Qt.AlignTrailing);
1635+
1636+ objects["2"] = objectComponent.createObject(tiledView);
1637+ verifySetOrientation(objects["1"], Qt.Horizontal);
1638+ verifyAdd(objects["1"], objects["2"], Qt.AlignTrailing);
1639+
1640+ objects["3"] = objectComponent.createObject(tiledView);
1641+ verifySetOrientation(objects["2"], Qt.Horizontal);
1642+ verifyAdd(objects["2"], objects["3"], Qt.AlignTrailing);
1643+
1644+ objects["4"] = objectComponent.createObject(tiledView);
1645+ verifySetOrientation(objects["3"], Qt.Horizontal);
1646+ verifyAdd(objects["3"], objects["4"], Qt.AlignTrailing);
1647+
1648+ objects["5"] = objectComponent.createObject(tiledView);
1649+ verifySetOrientation(objects["4"], Qt.Horizontal);
1650+ verifyAdd(objects["4"], objects["5"], Qt.AlignTrailing);
1651+
1652+ objects["6"] = objectComponent.createObject(tiledView);
1653+ verifySetOrientation(objects["5"], Qt.Horizontal);
1654+ verifyAdd(objects["5"], objects["6"], Qt.AlignTrailing);
1655+
1656+ tiledView.setRootItem(null);
1657+ for (var i=0; i<objects.length; i++) {
1658+ objects[i].destroy();
1659+ }
1660+ }
1661+
1662+ function test_resizeView() {
1663+ var leftObject = objectComponent.createObject(tiledView);
1664+ verifySetRootItem(leftObject);
1665+
1666+ var bottomRightObject = objectComponent.createObject(tiledView);
1667+ verifySetOrientation(leftObject, Qt.Horizontal);
1668+ verifyAdd(leftObject, bottomRightObject, Qt.AlignTrailing);
1669+
1670+ var topRightObject = objectComponent.createObject(tiledView);
1671+ verifySetOrientation(bottomRightObject, Qt.Vertical);
1672+ verifyAdd(bottomRightObject, topRightObject, Qt.AlignLeading);
1673+
1674+ var initialWidth = tiledView.width;
1675+ var initialHeight = tiledView.height;
1676+ var factor = 0.7;
1677+
1678+ // storing sizes before resizing
1679+ var sizes = {"leftObject": {"width": leftObject.width, "height": leftObject.height},
1680+ "bottomRightObject": {"width": bottomRightObject.width, "height": bottomRightObject.height},
1681+ "topRightObject": {"width": topRightObject.width, "height": topRightObject.height}};
1682+ tiledView.width = initialWidth * factor;
1683+ compare(leftObject.width, sizes.leftObject.width * factor);
1684+ compare(bottomRightObject.width, sizes.bottomRightObject.width * factor);
1685+ compare(topRightObject.width, sizes.topRightObject.width * factor);
1686+ compare(leftObject.height, sizes.leftObject.height);
1687+ compare(bottomRightObject.height, sizes.bottomRightObject.height);
1688+ compare(topRightObject.height, sizes.topRightObject.height);
1689+
1690+ tiledView.height = initialHeight * factor;
1691+ compare(leftObject.width, sizes.leftObject.width * factor);
1692+ compare(bottomRightObject.width, sizes.bottomRightObject.width * factor);
1693+ compare(topRightObject.width, sizes.topRightObject.width * factor);
1694+ compare(leftObject.height, sizes.leftObject.height * factor);
1695+ compare(bottomRightObject.height, sizes.bottomRightObject.height * factor);
1696+ compare(topRightObject.height, sizes.topRightObject.height * factor);
1697+
1698+ // resetting initial size
1699+ tiledView.width = initialWidth;
1700+ tiledView.height = initialHeight;
1701+ compare(leftObject.width, sizes.leftObject.width);
1702+ compare(bottomRightObject.width, sizes.bottomRightObject.width);
1703+ compare(topRightObject.width, sizes.topRightObject.width);
1704+ compare(leftObject.height, sizes.leftObject.height);
1705+ compare(bottomRightObject.height, sizes.bottomRightObject.height);
1706+ compare(topRightObject.height, sizes.topRightObject.height);
1707+
1708+ tiledView.setRootItem(null);
1709+ leftObject.destroy();
1710+ bottomRightObject.destroy();
1711+ topRightObject.destroy();
1712+ }
1713+
1714+ function test_simpleRemove_data() {
1715+ return [
1716+ {orientation: Qt.Horizontal, side: Qt.AlignTrailing},
1717+ {orientation: Qt.Vertical, side: Qt.AlignTrailing},
1718+ {orientation: Qt.Horizontal, side: Qt.AlignLeading},
1719+ {orientation: Qt.Vertical, side: Qt.AlignLeading},
1720+ ];
1721+ }
1722+
1723+ function test_simpleRemove(data) {
1724+ var rootObject = objectComponent.createObject(tiledView);
1725+ verifySetRootItem(rootObject);
1726+ verifySetOrientation(rootObject, data.orientation);
1727+
1728+ var rightObject = objectComponent.createObject(tiledView);
1729+ verifyAdd(rootObject, rightObject, data.side);
1730+ verifyRemove(rightObject);
1731+
1732+ verifyAdd(rootObject, rightObject, data.side);
1733+ verifyRemove(rootObject);
1734+
1735+ verifyAdd(rightObject, rootObject, data.side);
1736+ verifyRemove(rightObject);
1737+
1738+ tiledView.setRootItem(null);
1739+ rootObject.destroy();
1740+ rightObject.destroy();
1741+ }
1742+
1743+ function test_nestedAddsRemoveRoot() {
1744+ var objects = [];
1745+
1746+ objects["0"] = objectComponent.createObject(tiledView);
1747+ verifySetRootItem(objects["0"]);
1748+
1749+ objects["1"] = objectComponent.createObject(tiledView);
1750+ verifySetOrientation(objects["0"], Qt.Horizontal);
1751+ verifyAdd(objects["0"], objects["1"], Qt.AlignTrailing);
1752+
1753+ objects["2"] = objectComponent.createObject(tiledView);
1754+ verifySetOrientation(objects["1"], Qt.Horizontal);
1755+ verifyAdd(objects["1"], objects["2"], Qt.AlignTrailing);
1756+
1757+ // remove root
1758+ verifyRemove(objects["0"]);
1759+
1760+ // add further
1761+ objects["3"] = objectComponent.createObject(tiledView);
1762+ verifySetOrientation(objects["2"], Qt.Horizontal);
1763+ verifyAdd(objects["2"], objects["3"], Qt.AlignTrailing);
1764+
1765+ verifyRemove(objects["2"]);
1766+
1767+ objects["4"] = objectComponent.createObject(tiledView);
1768+ verifySetOrientation(objects["1"], Qt.Horizontal);
1769+ verifyAdd(objects["1"], objects["4"], Qt.AlignTrailing);
1770+
1771+ verifyRemove(objects["1"]);
1772+
1773+ tiledView.setRootItem(null);
1774+ for (var i=0; i<objects.length; i++) {
1775+ objects[i].destroy();
1776+ }
1777+ }
1778+
1779+ function test_addsVertical() {
1780+ var objects = [];
1781+
1782+ objects["0"] = objectComponent.createObject(tiledView);
1783+ verifySetRootItem(objects["0"]);
1784+
1785+ objects["1"] = objectComponent.createObject(tiledView);
1786+ verifySetOrientation(objects["0"], Qt.Vertical);
1787+ verifyAdd(objects["0"], objects["1"], Qt.AlignTrailing);
1788+
1789+ objects["2"] = objectComponent.createObject(tiledView);
1790+ verifySetOrientation(objects["1"], Qt.Vertical);
1791+ verifyAdd(objects["1"], objects["2"], Qt.AlignTrailing);
1792+
1793+ objects["3"] = objectComponent.createObject(tiledView);
1794+ verifySetOrientation(objects["2"], Qt.Vertical);
1795+ verifyAdd(objects["2"], objects["3"], Qt.AlignLeading);
1796+
1797+ objects["4"] = objectComponent.createObject(tiledView);
1798+ verifySetOrientation(objects["3"], Qt.Vertical);
1799+ verifyAdd(objects["3"], objects["4"], Qt.AlignTrailing);
1800+
1801+ tiledView.setRootItem(null);
1802+ for (var i=0; i<objects.length; i++) {
1803+ objects[i].destroy();
1804+ }
1805+ }
1806+
1807+ function verifyResize(leftObject, rightObject, orientation, dragDistance) {
1808+ var horizontalMove = (orientation == Qt.Horizontal ? dragDistance : 0);
1809+ var verticalMove = (orientation == Qt.Vertical ? dragDistance : 0);
1810+ var expectedLeft = {"x": leftObject.x,
1811+ "y": leftObject.y,
1812+ "width": leftObject.width + horizontalMove,
1813+ "height": leftObject.height + verticalMove};
1814+ var expectedRight = {"x": rightObject.x + horizontalMove,
1815+ "y": rightObject.y + verticalMove,
1816+ "width": rightObject.width - horizontalMove,
1817+ "height": rightObject.height - verticalMove};
1818+
1819+ if (orientation == Qt.Horizontal) {
1820+ mouseDrag(tiledView, rightObject.x, rightObject.y/2, dragDistance, 0);
1821+ } else if (orientation == Qt.Vertical) {
1822+ mouseDrag(tiledView, rightObject.x/2, rightObject.y, 0, dragDistance);
1823+ }
1824+
1825+ compare(leftObject.x, expectedLeft.x);
1826+ compare(leftObject.y, expectedLeft.y);
1827+ compare(leftObject.width, expectedLeft.width);
1828+ compare(leftObject.height, expectedLeft.height);
1829+ compare(rightObject.x, expectedRight.x);
1830+ compare(rightObject.y, expectedRight.y);
1831+ compare(rightObject.width, expectedRight.width);
1832+ compare(rightObject.height, expectedRight.height);
1833+ }
1834+
1835+ function test_resizeSplit_data() {
1836+ return [
1837+ {orientation: Qt.Horizontal, distance: 100},
1838+ {orientation: Qt.Vertical, distance: 100},
1839+ ];
1840+ }
1841+
1842+ function test_resizeSplit(data) {
1843+ var leftObject = objectComponent.createObject(tiledView);
1844+ verifySetRootItem(leftObject);
1845+ verifySetOrientation(leftObject, data.orientation);
1846+
1847+ var rightObject = objectComponent.createObject(tiledView);
1848+ verifyAdd(leftObject, rightObject, Qt.AlignTrailing);
1849+
1850+ verifyResize(leftObject, rightObject, data.orientation, data.distance);
1851+
1852+ tiledView.setRootItem(null);
1853+ leftObject.destroy();
1854+ rightObject.destroy();
1855+ }
1856+ }
1857+}

Subscribers

People subscribed via source and target branches