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