From 78966dc00419a4f5fe5fec4725062a4a0f380228 Mon Sep 17 00:00:00 2001 From: Adrian Kummerlaender Date: Sun, 5 Jul 2015 15:11:13 +0200 Subject: Embedded QML into C++ application --- qml/EmbeddedTerminal.qml | 88 +++++++++++++++++++++++++ qml/ScrollBar.qml | 78 ++++++++++++++++++++++ qml/TerminalItem.qml | 118 +++++++++++++++++++++++++++++++++ qml/main.qml | 168 +++++++++++++++++++++++++++++++++++++++++++++++ qml/ui.qrc | 8 +++ 5 files changed, 460 insertions(+) create mode 100644 qml/EmbeddedTerminal.qml create mode 100644 qml/ScrollBar.qml create mode 100644 qml/TerminalItem.qml create mode 100644 qml/main.qml create mode 100644 qml/ui.qrc (limited to 'qml') diff --git a/qml/EmbeddedTerminal.qml b/qml/EmbeddedTerminal.qml new file mode 100644 index 0000000..f4f104e --- /dev/null +++ b/qml/EmbeddedTerminal.qml @@ -0,0 +1,88 @@ +import QtQuick 2.0 +import QMLTermWidget 1.0 +import QtQuick.Controls 1.2 + +Item { + id: embeddedTerminal + property string program + property string workingDirectory + property int columns + property int lines + + width: container.width + height: container.height + + function select() { highlighter.select() } + function deselect() { highlighter.deselect() } + + Row { + id: container + + Rectangle { + id: highlighter + + width: 10 + height: terminal.height + + color: "#909636" + + Behavior on opacity { + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } + + function select() { opacity = 1 } + function deselect() { opacity = 0 } + function focus() { color = "#352F6A" } + function unfocus() { color = "#909636" } + } + + Rectangle { + width: terminal.width + height: terminal.height + + color: "#ffffff" + + QMLTermWidget { + id: terminal + + font.family: "Monospace" + font.pointSize: 8 + + width: fontMetrics.width * embeddedTerminal.columns + height: fontMetrics.height * embeddedTerminal.lines + + session: QMLTermSession { + initialWorkingDirectory: embeddedTerminal.workingDirectory + + shellProgram: { + return (embeddedTerminal.program).split(" ")[0]; + } + + shellProgramArgs: { + var elements = (embeddedTerminal.program).split(" "); + elements.shift(); + + return elements; + } + } + + onTermGetFocus: highlighter.focus() + onTermLostFocus: highlighter.unfocus() + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: { } + } + + Component.onCompleted: { + terminal.forceActiveFocus(); + session.startShellProgram(); + } + } + } + } +} diff --git a/qml/ScrollBar.qml b/qml/ScrollBar.qml new file mode 100644 index 0000000..18f149c --- /dev/null +++ b/qml/ScrollBar.qml @@ -0,0 +1,78 @@ +import QtQuick 2.0; + +Item { + id: scrollbar + width: (handleSize + 2) + visible: (flickable.visibleArea.heightRatio < 1.0) + + anchors { + top: flickable.top + right: flickable.right + bottom: flickable.bottom + } + + property Flickable flickable + property int handleSize + + Item { + id: bar + + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "black" + opacity: 0.5 + } + + MouseArea { + id: control + anchors.fill: parent + + drag { + target: handle + minimumY: 0 + maximumY: (bar.height - handle.height) + axis: Drag.YAxis + } + + onClicked: { + flickable.contentY = (mouse.y / bar.height * (flickable.contentHeight - flickable.height)); + } + } + + Item { + id: handle; + height: Math.max(20, (flickable.visibleArea.heightRatio * bar.height)) + + anchors { + left: parent.left + right: parent.right + } + + Rectangle { + id: backHandle + anchors { + fill: parent + margins: 1 + } + + color: (control.pressed ? "gray" : "white") + } + } + } + + Binding { + target: handle + property: "y" + value: (flickable.contentY * control.drag.maximumY / (flickable.contentHeight - flickable.height)) + when: (!control.drag.active) + } + + Binding { + target: flickable + property: "contentY" + value: (handle.y * (flickable.contentHeight - flickable.height) / control.drag.maximumY) + when: (control.drag.active || control.pressed) + } +} diff --git a/qml/TerminalItem.qml b/qml/TerminalItem.qml new file mode 100644 index 0000000..6c19c45 --- /dev/null +++ b/qml/TerminalItem.qml @@ -0,0 +1,118 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 + +Item { + id: terminalItem + signal executed + + height: elementList.height + + function select() { + if ( command.readOnly ) { + elementList.children[1].select(); + } else { + highlighter.select(); + } + } + + function deselect() { + if ( command.readOnly ) { + elementList.children[1].deselect(); + } else { + highlighter.deselect(); + } + } + + function forceActiveFocus() { + if ( command.readOnly ) { + scope.forceActiveFocus(); + } else { + scope.forceActiveFocus(); + highlighter.select(); + highlighter.focus(); + } + } + + function unfocus() { + if ( !command.readOnly ) { + highlighter.unfocus(); + } + } + + FocusScope { + id: scope + + Column { + id: elementList + + function createTerminal(program) { + var terminal = Qt.createComponent("qrc:/EmbeddedTerminal.qml"); + var instantiateTerminal = function() { + terminal.createObject(elementList, { + "columns": 90, + "lines": 20, + "program": program, + "workingDirectory": "$HOME", + "focus": true + }); + } + + if ( terminal.status === Component.Ready ) { + instantiateTerminal(); + } else { + terminal.statusChanged.connect(instantiateTerminal); + } + } + + RowLayout { + width: terminalItem.width + + Rectangle { + id: highlighter + + width: 10 + height: command.height + opacity: 0 + + color: "#909636" + + Behavior on opacity { + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } + + function select() { opacity = 1 } + function deselect() { opacity = 0 } + function focus() { color = "#352F6A" } + function unfocus() { color = "#909636" } + } + + TextInput { + id: command + + font.pointSize: 18 + color: "white" + selectedTextColor: "#161616" + selectionColor: "white" + selectByMouse: true + focus: true + + Layout.fillWidth: true + + onAccepted: { + if ( !readOnly ) { + readOnly = true; + focus = false; + elementList.createTerminal(text); + terminalItem.executed(); + highlighter.deselect(); + } + } + } + } + } + } +} diff --git a/qml/main.qml b/qml/main.qml new file mode 100644 index 0000000..1e0db9d --- /dev/null +++ b/qml/main.qml @@ -0,0 +1,168 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Controls 1.2 + +ApplicationWindow { + id: root + + visible: true + + color: "#161616" + + Component.onCompleted: terminalList.focusCurrent() + + Flickable { + id: terminalListFlickable + anchors.fill: parent + + boundsBehavior: Flickable.StopAtBounds + contentHeight: terminalList.height + contentWidth: terminalList.width + pixelAligned: true + + Column { + id: terminalList + spacing: 10 + + property int activeItem : 0 + + onHeightChanged: scrollTo(activeItem) + + function createItem() { + var terminalItem = Qt.createComponent("qrc:/TerminalItem.qml"); + var instantiateTerminal = function() { + var instance = terminalItem.createObject(terminalList, { + "width": terminalListFlickable.width + }); + instance.onExecuted.connect(createItem); + } + + if ( terminalItem.status === Component.Ready ) { + instantiateTerminal(); + } else { + terminalItem.statusChanged.connect(instantiateTerminal); + } + } + + function scrollTo(index) { + if ( terminalList.height >= terminalListFlickable.height ) { + var offset = children[index].y + + (children[index].height / 2) + - (terminalListFlickable.height / 2); + + var bound = terminalList.height + - terminalListFlickable.height; + + if ( offset < 0 ) { + terminalListFlickable.contentY = 0; + } else if ( offset >= bound ) { + terminalListFlickable.contentY = bound; + } else { + terminalListFlickable.contentY = offset; + } + } + } + + function selectItem(index) { + children[activeItem].deselect(); + children[index ].select(); + + activeItem = index; + + scrollTo(index); + } + + function selectNext() { + if ( activeItem < (children.length - 1) ) { + selectItem(activeItem + 1); + } else { + insertTerminalAction.trigger(); + } + } + + function selectPrev() { + if ( activeItem > 0 ) { + selectItem(activeItem - 1); + } + } + + function focusCurrent() { + children[activeItem].forceActiveFocus(); + } + + function unfocusCurrent() { + children[activeItem].unfocus(); + } + + TerminalItem { + width: terminalListFlickable.width + onExecuted: terminalList.createItem() + } + } + } + + Action { + id: insertTerminalAction + shortcut: "i" + enabled: false + onTriggered: { + escapeTerminalAction.enabled = true; + insertTerminalAction.enabled = false; + nextTerminalAction.enabled = false; + prevTerminalAction.enabled = false; + lastTerminalAction.enabled = false; + firstTerminalAction.enabled = false; + + terminalList.focusCurrent(); + } + } + + Action { + id: escapeTerminalAction + shortcut: "Shift+ESC" + onTriggered: { + escapeTerminalAction.enabled = false; + insertTerminalAction.enabled = true; + nextTerminalAction.enabled = true; + prevTerminalAction.enabled = true; + lastTerminalAction.enabled = true; + firstTerminalAction.enabled = true; + + terminalList.forceActiveFocus(); + terminalList.unfocusCurrent(); + } + } + + Action { + id: nextTerminalAction + shortcut: "j" + enabled: false + onTriggered: terminalList.selectNext() + } + + Action { + id: prevTerminalAction + shortcut: "k" + enabled: false + onTriggered: terminalList.selectPrev() + } + + Action { + id: lastTerminalAction + shortcut: "Shift+G" + enabled: false + onTriggered: terminalList.selectItem(terminalList.children.length - 1) + } + + Action { + id: firstTerminalAction + shortcut: "g" + enabled: false + onTriggered: terminalList.selectItem(0) + } + + ScrollBar { + flickable: terminalListFlickable + handleSize: 10 + } +} diff --git a/qml/ui.qrc b/qml/ui.qrc new file mode 100644 index 0000000..8339fa2 --- /dev/null +++ b/qml/ui.qrc @@ -0,0 +1,8 @@ + + + main.qml + TerminalItem.qml + EmbeddedTerminal.qml + ScrollBar.qml + + -- cgit v1.2.3