diff options
Diffstat (limited to 'xT/xTq.qml')
| -rw-r--r-- | xT/xTq.qml | 411 |
1 files changed, 377 insertions, 34 deletions
@@ -1,90 +1,421 @@ import QtQuick import QtQuick.Controls.Fusion -//import QtQuick.Controls import QtQuick.Layouts +import QtQuick.Dialogs +import QtMultimedia ApplicationWindow { id: window - width: 640 - height: 480 + width: 960 + height: 720 visible: true - title: qsTr("xT") + title: qsTr("xTq") - property RelayConnection connection + RelayConnection { + id: connection + + onErrorOccurred: (message) => { + errorDialog.text = message + errorDialog.open() + } + + onBeepRequested: { + beepSound.play() + } + + onConnectedChanged: { + if (!connected) { + connectDialog.open() + } + } + + onAboutToChangeBuffer: (oldBuffer) => { + // Save the current buffer's input before switching + connection.saveBufferInput( + oldBuffer, + inputText.text, + inputText.selectionStart, + inputText.selectionEnd + ) + } + + onIconChanged: { + // Window icon changes would need QML/Window integration, + // probably need to add a custom property to our window. + // TODO: Check highlightedness status and somehow change icon. + } + + onBufferTextChanged: { + connection.populateBufferDocument(bufferText.textDocument) + scrollToBottom() + } + + onCompletionResult: (completion) => { + inputText.text = completion + inputText.cursorPosition = inputText.text.length + } + + onLogViewChanged: (logText) => { + logView.text = logText + logView.visible = true + bufferText.visible = false + logButton.checked = true + var vbar = bufferScroll.ScrollBar.vertical + vbar.position = 1.0 - vbar.size + } + } + + SoundEffect { + id: beepSound + source: "qrc:/beep.wav" + volume: 0.5 + } ColumnLayout { - id: column anchors.fill: parent anchors.margins: 6 + spacing: 6 - ScrollView { - id: bufferScroll + Label { + id: topicLabel + Layout.fillWidth: true + text: connection.topic + textFormat: Text.RichText + wrapMode: Text.Wrap + onLinkActivated: (link) => { + Qt.openUrlExternally(link) + } + visible: text.length > 0 + padding: 4 + background: Rectangle { + color: palette.base + border.color: palette.mid + border.width: 1 + } + } + + SplitView { + id: mainSplit Layout.fillWidth: true Layout.fillHeight: true - TextArea { - id: buffer - text: qsTr("Buffer text") + orientation: Qt.Horizontal + + Rectangle { + SplitView.preferredWidth: 200 + SplitView.minimumWidth: 150 + color: "transparent" + border.color: palette.mid + border.width: 1 + + ListView { + id: bufferList + anchors.fill: parent + anchors.margins: 1 + clip: true + + model: connection.bufferListModel + + Connections { + target: connection.bufferListModel + onBufferActivated: { + bufferList.currentIndex = + connection.bufferListModel. + getCurrentBufferIndex() + } + } + + delegate: ItemDelegate { + width: bufferList.width + text: model.displayText + font.bold: model.isBold + highlighted: ListView.isCurrentItem + contentItem: Label { + text: parent.text + font: parent.font + color: model.highlightColor.valid ? + model.highlightColor : palette.text + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + onClicked: { + bufferList.currentIndex = index + connection.activateBuffer(model.bufferName) + } + } + + ScrollBar.vertical: ScrollBar {} + } + } + + ScrollView { + id: bufferScroll + SplitView.fillWidth: true + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + TextArea { + id: bufferText + textFormat: Text.RichText + readOnly: true + wrapMode: Text.Wrap + selectByMouse: true + onLinkActivated: (link) => { + Qt.openUrlExternally(link) + } + + Connections { + target: connection + function onCurrentBufferChanged() { + connection.populateBufferDocument(bufferText.textDocument) + bufferText.visible = true + logView.visible = false + logButton.checked = false + scrollToBottom() + + // Restore input for the new buffer + inputText.text = connection.getBufferInput() + inputText.cursorPosition = connection.getBufferInputStart() + inputText.select(connection.getBufferInputStart(), connection.getBufferInputEnd()) + } + } + } + + TextArea { + id: logView + visible: false + textFormat: Text.RichText + readOnly: true + wrapMode: Text.Wrap + selectByMouse: true + font.family: "monospace" + onLinkActivated: (link) => { + Qt.openUrlExternally(link) + } + } } } RowLayout { - id: row Layout.fillWidth: true + spacing: 6 Label { + id: promptLabel + text: connection.prompt + } + + Item { Layout.fillWidth: true - id: prompt - text: qsTr("Prompt") } - Label { + ToolButton { + id: boldButton + text: "B" + font.bold: true + checkable: true + ToolTip.text: "Bold" + ToolTip.visible: hovered + onClicked: { + inputText.font.bold = checked + } + } + + ToolButton { + id: italicButton + text: "I" + font.italic: true + checkable: true + ToolTip.text: "Italic" + ToolTip.visible: hovered + onClicked: { + inputText.font.italic = checked + } + } + + ToolButton { + id: underlineButton + text: "U" + font.underline: true + checkable: true + ToolTip.text: "Underline" + ToolTip.visible: hovered + onClicked: { + inputText.font.underline = checked + } + } + + Item { Layout.fillWidth: true - id: status + } + + Label { + id: statusLabel + text: connection.status horizontalAlignment: Text.AlignRight - text: qsTr("Status") + } + + ToolButton { + id: logButton + text: "Log" + checkable: true + ToolTip.text: "View buffer log" + ToolTip.visible: hovered + onClicked: { + if (checked) { + connection.toggleBufferLog() + } else { + logView.visible = false + bufferText.visible = true + logView.text = "" + } + } + } + + ToolButton { + text: "↓" + ToolTip.text: "Scroll to bottom" + ToolTip.visible: hovered + enabled: { + var vbar = bufferScroll.ScrollBar.vertical + return vbar.position + vbar.size < 1.0 + } + onClicked: scrollToBottom() } } - TextArea { - id: input + ScrollView { Layout.fillWidth: true - text: qsTr("Input") + Layout.preferredHeight: Math.min(inputText.contentHeight + 12, 120) + + TextArea { + id: inputText + wrapMode: Text.Wrap + selectByMouse: true + textFormat: Text.RichText + + Keys.onReturnPressed: (event) => { + event.accepted = false + if (event.modifiers & Qt.ShiftModifier) + return + + event.accepted = true + if (text.length > 0) { + connection.sendInput(text) + text = "" + } + } + + Keys.onUpPressed: { + var history = connection.getInputHistoryUp() + if (history.length > 0) { + text = history + } + } + + Keys.onDownPressed: { + var history = connection.getInputHistoryDown() + if (history.length > 0) { + text = history + } + } + + Keys.onTabPressed: { + connection.requestCompletion(text, cursorPosition) + } + } } } - Component.onCompleted: {} + Shortcut { + sequences: ["F5", "Alt+PgUp", "Ctrl+PgUp"] + onActivated: connection.activatePreviousBuffer() + } + + Shortcut { + sequences: ["F6", "Alt+PgDown", "Ctrl+PgDown"] + onActivated: connection.activateNextBuffer() + } + + Shortcut { + sequences: ["Ctrl+Tab", "Alt+Tab"] + onActivated: connection.activateLastBuffer() + } + + Shortcut { + sequence: "Alt+A" + onActivated: connection.activateNextWithActivity() + } + + Shortcut { + sequence: "Alt+!" + onActivated: connection.activateNextHighlighted() + } + + Shortcut { + sequence: "Alt+H" + onActivated: connection.toggleUnimportant() + } + + Shortcut { + sequence: "PgUp" + onActivated: { + var vbar = bufferScroll.ScrollBar.vertical + vbar.position = Math.max(0, vbar.position - vbar.size) + } + } + + Shortcut { + sequence: "PgDown" + onActivated: { + var vbar = bufferScroll.ScrollBar.vertical + vbar.position = Math.min(1 - vbar.size, vbar.position + vbar.size) + } + } + + function scrollToBottom() { + var vbar = bufferScroll.ScrollBar.vertical + vbar.position = 1.0 - vbar.size + } Dialog { - id: connect + id: connectDialog title: "Connect to relay" anchors.centerIn: parent modal: true - visible: true + closePolicy: Popup.NoAutoClose + + onOpened: connectHost.forceActiveFocus() - onRejected: Qt.quit() onAccepted: { - // TODO(p): Store the host, store the port, initiate connection. + connection.host = connectHost.text + connection.port = connectPort.text + connection.connectToRelay() } + onRejected: Qt.quit() + GridLayout { anchors.fill: parent - anchors.margins: 6 columns: 2 - // It is a bit silly that one has to do everything manually. - Keys.onReturnPressed: connect.accept() - Label { text: "Host:" } TextField { id: connectHost Layout.fillWidth: true - // And if this doesn't work reliably, do it after open(). + text: connection.host || "localhost" focus: true + selectByMouse: true + onAccepted: connectDialog.accept() } + Label { text: "Port:" } TextField { id: connectPort Layout.fillWidth: true + text: connection.port || "" + selectByMouse: true + validator: IntValidator { bottom: 0; top: 65535 } + onAccepted: connectDialog.accept() } } @@ -92,14 +423,26 @@ ApplicationWindow { Button { text: qsTr("Connect") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole - Keys.onReturnPressed: connect.accept() highlighted: true } Button { - text: qsTr("Close") - DialogButtonBox.buttonRole: DialogButtonBox.DestructiveRole - Keys.onReturnPressed: connect.reject() + text: qsTr("Exit") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole } } } + + MessageDialog { + id: errorDialog + title: "Error" + buttons: MessageDialog.Ok + } + + Component.onCompleted: { + if (!connection.connected) { + connectDialog.open() + } else { + inputText.forceActiveFocus() + } + } } |
