Merge lp:~fboucault/ubuntu-terminal-app/tiled_view into lp:ubuntu-terminal-app
- tiled_view
- Merge into trunk
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 |
Related bugs: |
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 | +} |