// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import QtQuick import "../.." // One labelled key of qtmaildir.conf: a text field that saves on Enter. // Emptied, the key is unset and qtmaildir uses its default, which the // placeholder names. kind "int" or "float" checks the number and its range // before writing; a refused value shows why under the row. Column { id: s property string section property string key property string label property string placeholder: "" property string hint: "" property string kind: "text" property real min: -Infinity property real max: Infinity // A #rrggbb key: a swatch beside the field opens a colour dialog. property bool colour: false // What a search hit scrolls to. objectName: section + "/" + key width: parent ? parent.width : 0 spacing: 2 readonly property string value: Qtm.get(s.section, s.key) ?? "" property string problem: "" function commit(t) { t = t.trim(); if (t === "") { s.problem = ""; Qtm.set(s.section, s.key, null); return; } if (s.kind !== "text") { const n = Number(t); if (isNaN(n) || (s.kind === "int" && !Number.isInteger(n)) || n < s.min || n > s.max) { s.problem = s.kind === "int" ? `a whole number from ${s.min} to ${s.max}` : `a number from ${s.min} to ${s.max}`; return; } } s.problem = ""; Qtm.set(s.section, s.key, t); } Item { width: s.width height: 34 Text { id: lbl anchors { left: parent.left; verticalCenter: parent.verticalCenter } width: 160 elide: Text.ElideRight text: s.label color: Theme.subtext font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } } ColourPick { id: pick anchors { right: parent.right; verticalCenter: parent.verticalCenter } visible: s.colour width: s.colour ? 34 : 0 colour: s.value onPicked: hex => s.commit(hex) } Rectangle { anchors { left: lbl.right; leftMargin: 8; right: pick.left; rightMargin: s.colour ? 8 : 0 top: parent.top; bottom: parent.bottom } radius: 8 color: Theme.surface TextInput { id: ti anchors { fill: parent; leftMargin: 10; rightMargin: 10 } verticalAlignment: TextInput.AlignVCenter clip: true selectByMouse: true color: Theme.text font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } text: s.value onAccepted: if (text !== s.value) s.commit(text) Keys.onEscapePressed: event => { if (text !== s.value) text = s.value; else event.accepted = false; } Text { anchors.verticalCenter: parent.verticalCenter width: parent.width elide: Text.ElideRight visible: parent.text === "" text: s.placeholder color: Theme.overlay font: parent.font } } } } Text { x: 168 width: s.width - x visible: s.problem !== "" || s.hint !== "" wrapMode: Text.Wrap text: s.problem !== "" ? "Must be " + s.problem + "." : s.hint color: s.problem !== "" ? Theme.red : Theme.overlay font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } } }