orkmode/ui/qml/Main.qml

268 lines
10 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
ApplicationWindow {
id: root
width: 1000
height: 700
visible: true
title: qsTr("orkmode")
// Apply theme font globally
font.family: theme.fontFamily || Qt.application.font.family
font.pointSize: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
// Apply theme palette to Qt Quick Controls.
// theme.* colors return system palette when mode="system", override colors otherwise.
palette.active.window: theme.windowColor
palette.active.windowText: theme.windowTextColor
palette.active.base: theme.baseColor
palette.active.text: theme.textColor
palette.active.button: theme.buttonColor
palette.active.buttonText: theme.buttonTextColor
palette.active.highlight: theme.highlightColor
palette.active.highlightedText: theme.highlightedTextColor
palette.active.mid: theme.midColor
palette.active.dark: theme.darkColor
palette.active.light: theme.lightColor
palette.active.shadow: theme.shadowColor
palette.active.link: theme.linkColor
palette.active.toolTipBase: theme.tooltipBaseColor
palette.active.toolTipText: theme.tooltipTextColor
palette.active.placeholderText: theme.placeholderTextColor
palette.active.alternateBase: theme.alternateBaseColor
palette.inactive.window: theme.windowColor
palette.inactive.windowText: theme.windowTextColor
palette.inactive.base: theme.baseColor
palette.inactive.text: theme.textColor
palette.inactive.button: theme.buttonColor
palette.inactive.buttonText: theme.buttonTextColor
palette.inactive.highlight: theme.highlightColor
palette.inactive.highlightedText: theme.highlightedTextColor
palette.inactive.mid: theme.midColor
palette.inactive.dark: theme.darkColor
palette.inactive.light: theme.lightColor
palette.inactive.shadow: theme.shadowColor
palette.inactive.link: theme.linkColor
palette.inactive.toolTipBase: theme.tooltipBaseColor
palette.inactive.toolTipText: theme.tooltipTextColor
palette.inactive.placeholderText: theme.placeholderTextColor
palette.inactive.alternateBase: theme.alternateBaseColor
palette.disabled.window: theme.windowColor
palette.disabled.windowText: theme.midColor
palette.disabled.base: theme.baseColor
palette.disabled.text: theme.midColor
palette.disabled.button: theme.buttonColor
palette.disabled.buttonText: theme.midColor
palette.disabled.highlight: theme.highlightColor
palette.disabled.highlightedText: theme.highlightedTextColor
palette.disabled.mid: theme.midColor
palette.disabled.dark: theme.darkColor
palette.disabled.light: theme.lightColor
palette.disabled.shadow: theme.shadowColor
color: theme.windowColor
// Currently selected document name (namespace directory).
property string currentDoc: ""
function refreshDocs() {
docList.model = orkClient.docs()
if (docList.model.length > 0 && root.currentDoc === "")
root.selectDoc(docList.model[0].name)
}
function selectDoc(name) {
root.currentDoc = name
docModel.document = name
}
Component.onCompleted: refreshDocs()
Connections {
target: orkClient
function onConnectedChanged() { root.refreshDocs() }
// When the org path changes via Settings, the server has rescanned a
// new directory: drop the current selection and reload the doc list.
function onOrgPathChanged() {
root.currentDoc = ""
docModel.document = ""
docModel.reload()
root.refreshDocs()
}
}
// Settings dialog
SettingsDialog {
id: settingsDialog
}
// Reference font size for scalable UI
readonly property int baseFontSize: theme.fontSize > 0 ? theme.fontSize : 12
header: ToolBar {
background: Rectangle { color: theme.buttonColor }
RowLayout {
anchors.fill: parent
anchors.leftMargin: root.baseFontSize
anchors.rightMargin: root.baseFontSize
Label {
text: "orkmode"
color: theme.buttonTextColor
font.pointSize: root.baseFontSize + 2
font.bold: true
}
Item { Layout.fillWidth: true }
Label {
text: orkClient.connected
? "● " + orkClient.address
: "○ disconnected"
color: orkClient.connected ? theme.doneColor : theme.deadlineColor
font.pointSize: root.baseFontSize - 1
}
ToolButton {
text: "⟳"
onClicked: { docModel.reload(); root.refreshDocs() }
ToolTip.text: qsTr("Reload")
ToolTip.visible: hovered
}
ToolButton {
text: "⚙"
onClicked: settingsDialog.open()
ToolTip.text: qsTr("Settings")
ToolTip.visible: hovered
}
}
}
RowLayout {
anchors.fill: parent
spacing: 0
// Document sidebar.
Rectangle {
Layout.preferredWidth: root.baseFontSize * 16
Layout.fillHeight: true
color: theme.baseColor
ColumnLayout {
anchors.fill: parent
spacing: 0
Label {
text: qsTr("Documents")
color: theme.placeholderTextColor
font.pointSize: root.baseFontSize - 1
font.bold: true
Layout.margins: root.baseFontSize * 0.8
}
ListView {
id: docList
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
spacing: root.baseFontSize * 0.25
model: []
delegate: ItemDelegate {
id: docDelegate
required property var modelData
width: docList.width
highlighted: root.currentDoc === modelData.name
onClicked: root.selectDoc(modelData.name)
leftPadding: root.baseFontSize * 0.8
rightPadding: root.baseFontSize * 0.8
topPadding: root.baseFontSize * 0.5
bottomPadding: root.baseFontSize * 0.5
background: Rectangle {
color: (docDrop.containsDrag) ? theme.highlightColor
: (docDelegate.highlighted ? theme.highlightColor : "transparent")
opacity: docDrop.containsDrag ? 0.5
: (docDelegate.highlighted ? 0.3 : 1.0)
// While a section is dragged over a non-current doc,
// show a border that fills as the spring timer runs.
border.color: theme.linkColor
border.width: docDrop.containsDrag
&& modelData.name !== root.currentDoc ? 1 : 0
}
// Spring-loaded cross-file drop target: hovering a
// dragged section over another document for a moment
// switches the view to it; the drop then lands in that
// file via the outline's normal in-file resolution.
DropArea {
id: docDrop
anchors.fill: parent
keys: ["ork-section"]
onEntered: {
if (docDelegate.modelData.name !== root.currentDoc)
springTimer.restart()
}
onExited: springTimer.stop()
Timer {
id: springTimer
interval: 450
onTriggered: {
if (docDrop.containsDrag)
root.selectDoc(docDelegate.modelData.name)
}
}
}
contentItem: ColumnLayout {
spacing: root.baseFontSize * 0.3
Label {
text: modelData.title
color: theme.textColor
font.pointSize: root.baseFontSize
elide: Text.ElideRight
Layout.fillWidth: true
}
Label {
text: modelData.name
color: theme.placeholderTextColor
font.pointSize: root.baseFontSize - 2
elide: Text.ElideRight
Layout.fillWidth: true
}
}
}
}
}
}
Rectangle { Layout.preferredWidth: 1; Layout.fillHeight: true; color: theme.darkColor }
// Outline view.
Item {
Layout.fillWidth: true
Layout.fillHeight: true
OutlineView {
anchors.fill: parent
anchors.margins: root.baseFontSize * 0.6
model: docModel
// Hidden until a document is selected so its own
// "No sections" empty state does not overlap the
// "Select a document" placeholder below.
visible: root.currentDoc !== ""
}
Label {
anchors.centerIn: parent
width: Math.min(parent.width - root.baseFontSize * 4,
root.baseFontSize * 30)
visible: root.currentDoc === ""
text: orkClient.connected
? qsTr("Select a document")
: qsTr("Not connected: %1").arg(orkClient.error)
color: theme.placeholderTextColor
font.pointSize: root.baseFontSize + 1
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
lineHeight: 1.3
}
}
}
}