orkmode/ui/qml/SettingsDialog.qml

324 lines
9.7 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
Dialog {
id: settingsDialog
title: qsTr("Settings")
modal: true
standardButtons: Dialog.Close
width: 450
height: 520
x: (parent.width - width) / 2
y: (parent.height - height) / 2
// Load the current org path from the server whenever the dialog opens.
onOpened: {
orgPathField.text = orkClient.orgPath()
orgPathStatus.clear()
}
// Native folder picker. Writes the chosen local path into the field; the
// user still presses Apply to send it to the server.
FolderDialog {
id: folderDialog
title: qsTr("Select Org Directory")
onAccepted: {
// selectedFolder is a file:// URL; convert to a local path.
orgPathField.text = decodeURIComponent(
selectedFolder.toString().replace(/^file:\/\//, ""))
}
}
// Send the field's path to the server via /cfg. On success, refresh the
// document list; on failure, show the server's error message.
function applyOrgPath() {
var path = orgPathField.text.trim()
if (path.length === 0) {
orgPathStatus.ok = false
orgPathStatus.text = qsTr("Path is empty.")
return
}
if (orkClient.setOrgPath(path)) {
orgPathStatus.ok = true
orgPathStatus.text = qsTr("Applied. Scanning %1").arg(path)
} else {
orgPathStatus.ok = false
orgPathStatus.text = qsTr("Failed: %1").arg(orkClient.error)
}
}
background: Rectangle {
color: theme.windowColor
border.color: theme.midColor
border.width: 1
radius: 6
}
header: Rectangle {
implicitHeight: 40
color: theme.buttonColor
radius: 6
Label {
anchors.centerIn: parent
text: settingsDialog.title
color: theme.buttonTextColor
font.bold: true
font.pointSize: theme.fontSize > 0 ? theme.fontSize + 1 : 13
}
// Square off bottom corners
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 6
color: theme.buttonColor
}
}
contentItem: ScrollView {
id: contentScroll
clip: true
// Only scroll vertically; the inner layout matches the viewport width.
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentWidth: availableWidth
ColumnLayout {
// Match the scroll viewport width so Layout.fillWidth children size
// correctly and no horizontal scrolling is needed.
width: contentScroll.availableWidth
spacing: 16
// --- Org Path ---
GroupBox {
title: qsTr("Org Path")
Layout.fillWidth: true
background: Rectangle {
color: "transparent"
border.color: theme.midColor
border.width: 1
radius: 4
y: 12
}
label: Label {
x: 8
text: parent.title
color: theme.textColor
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
}
ColumnLayout {
anchors.fill: parent
spacing: 8
Label {
text: qsTr("Directory the server scans for .org files. A leading ~ expands to your home directory.")
color: theme.placeholderTextColor
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 10
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
spacing: 8
TextField {
id: orgPathField
Layout.fillWidth: true
placeholderText: qsTr("e.g. ~/org")
selectByMouse: true
onAccepted: applyOrgPath()
}
Button {
text: qsTr("Browse…")
onClicked: folderDialog.open()
}
Button {
text: qsTr("Apply")
onClicked: applyOrgPath()
}
}
Label {
id: orgPathStatus
Layout.fillWidth: true
wrapMode: Text.WordWrap
visible: text.length > 0
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 10
// ok/err color set by applyOrgPath via property.
property bool ok: true
color: ok ? theme.doneColor : theme.deadlineColor
function clear() { text = "" }
}
}
}
// --- Color Scheme ---
GroupBox {
title: qsTr("Color Scheme")
Layout.fillWidth: true
background: Rectangle {
color: "transparent"
border.color: theme.midColor
border.width: 1
radius: 4
y: 12
}
label: Label {
x: 8
text: parent.title
color: theme.textColor
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
}
RowLayout {
anchors.fill: parent
spacing: 8
ComboBox {
id: schemeCombo
Layout.fillWidth: true
model: theme.availableSchemes
currentIndex: {
let idx = model.indexOf(theme.mode)
return idx >= 0 ? idx : 0
}
onActivated: theme.mode = currentText
}
}
}
// --- Font ---
GroupBox {
title: qsTr("Font")
Layout.fillWidth: true
background: Rectangle {
color: "transparent"
border.color: theme.midColor
border.width: 1
radius: 4
y: 12
}
label: Label {
x: 8
text: parent.title
color: theme.textColor
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
}
GridLayout {
columns: 2
anchors.fill: parent
columnSpacing: 12
rowSpacing: 8
Label { text: qsTr("Family:"); color: theme.textColor }
ComboBox {
id: fontFamilyCombo
Layout.fillWidth: true
model: theme.availableFamilies
editable: true
currentIndex: {
let fam = theme.fontFamily || Qt.application.font.family
let idx = model.indexOf(fam)
return idx >= 0 ? idx : 0
}
onActivated: theme.fontFamily = currentText
onAccepted: theme.fontFamily = editText
}
Label { text: qsTr("Style:"); color: theme.textColor }
ComboBox {
id: fontStyleCombo
Layout.fillWidth: true
model: theme.availableStyles
currentIndex: {
let st = theme.fontStyle
let idx = model.indexOf(st)
return idx >= 0 ? idx : 0
}
onActivated: theme.fontStyle = currentText
}
Label { text: qsTr("Size:"); color: theme.textColor }
SpinBox {
id: fontSizeSpin
from: 6
to: 72
value: theme.fontSize > 0 ? theme.fontSize : Qt.application.font.pointSize
onValueModified: theme.fontSize = value
}
}
}
// --- UI Scale ---
GroupBox {
title: qsTr("UI Scale")
Layout.fillWidth: true
background: Rectangle {
color: "transparent"
border.color: theme.midColor
border.width: 1
radius: 4
y: 12
}
label: Label {
x: 8
text: parent.title
color: theme.textColor
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.9 : 11
}
RowLayout {
anchors.fill: parent
spacing: 12
Slider {
id: scaleSlider
Layout.fillWidth: true
from: 0.5
to: 3.0
stepSize: 0.1
value: theme.uiScale
onMoved: theme.uiScale = value
}
Label {
text: (theme.uiScale * 100).toFixed(0) + "%"
color: theme.textColor
Layout.preferredWidth: 50
}
}
}
// --- Reset buttons ---
RowLayout {
Layout.fillWidth: true
spacing: 8
Button {
text: qsTr("Reset Font")
onClicked: theme.resetFont()
}
Item { Layout.fillWidth: true }
}
}
}
}