import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import NERvGear 1.0 as NVG import NERvGear.Controls 1.0 import NERvGear.Templates 1.0 as T import NERvGear.Preferences 1.0 as P WidgetTemplate { id: widget title: qsTr("Ordinal Scale Top UI widget") editing: styleDialog.active version: "1.0.1" defaultValues: { "Curve": "true", "Line Color": "#fffcf9", "Line Width": 38, "Shadow Color": "#e0e0e0", "Shadow Size": 0.5, "Battle UI": false, "Circle Color": "#fffcf9", "Clock Visible": true, "Full Clock": true, "Font Color": "#f5f5f5", "Font Size": 44, "Font Name": 0, "Font Weight": 0, "Text Vertical Offset": 16 } onUpdated: { widget.settings.styles = Object.assign(defaultValues, widget.settings.styles); } onConfigsChanged: { line.requestPaint(); c0.requestPaint(); c1.requestPaint(); c2.requestPaint(); c3.requestPaint(); c4.requestPaint(); c5.requestPaint(); } readonly property var configs: widget.settings.styles property real thour: 0 property real t12hour: 0 property real tmin: -1 readonly property var fonts: Qt.fontFamilies() readonly property var fontweight: [Font.Light, Font.Normal, Font.Bold] readonly property var sfontweight: [qsTr("Light"), qsTr("Normal"), qsTr("Bold")] readonly property real size: 155 readonly property real h: Math.min(widget.width, widget.height) readonly property real w: widget.width readonly property real r: (w**2+h**2)/4/h action: T.Action { id: thiz title: qsTr("Top Line Action") description: title execute: function () { return new Promise(function (resolve, reject) { if (!styleDialog.active) { let cfg = widget.settings.styles; cfg["Battle UI"] = !cfg["Battle UI"]; widget.settings.styles = cfg; } resolve(); }); } preference: P.SelectPreference { label: qsTr("Command") model: [ qsTr("Toggle UI") ] defaultValue: 0 load: function () {} save: function () {} } } Timer { interval: 250 running: text_clock.visible repeat: true onTriggered: { var now = new Date(); if (tmin !== now.getMinutes()) { tmin = now.getMinutes(); thour = now.getHours(); t12hour = thour > 12 ? thour - 12 : thour; } } } Text { id: text_clock anchors.top: parent.top anchors.topMargin: widget.height/200*configs["Text Vertical Offset"] anchors.horizontalCenter: parent.horizontalCenter style: Text.Outline styleColor: "transparent" color: configs["Font Color"] text: configs["Full Clock"] ? ("0"+thour).slice(-2) + ":" + ("0"+tmin).slice(-2) : ("0"+t12hour).slice(-2) + ":" + ("0"+tmin).slice(-2) font.pointSize: widget.height/200*configs["Font Size"] font.family: fonts[configs["Font Name"]] font.weight: fontweight[configs["Font Weight"]] visible: widget.NVG.View.exposed && !configs["Battle UI"] && configs["Clock Visible"] } Item { id: circle anchors.centerIn: parent anchors.verticalCenterOffset: -2.5 scale: widget.height/size/1.25 visible: widget.NVG.View.exposed && configs["Battle UI"] Canvas { id: c1 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.04 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.38-configs["Shadow Size"]/2, -Math.PI/6, -Math.PI/6+Math.PI*2/3 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } rotation: -140 SequentialAnimation on rotation { running: circle.visible loops: Animation.Infinite RotationAnimation { duration: 1250 easing.type: Easing.InOutCubic from: -140 to: 220 } RotationAnimation { duration: 1250 easing.type: Easing.InOutCubic from: 220 to: -140 direction: RotationAnimation.Counterclockwise } } } Canvas { id: c2 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.08 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.30-configs["Shadow Size"]/2, -Math.PI/6, -Math.PI/24+Math.PI*2/3 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } rotation: 20 NumberAnimation on rotation { duration: 2800 easing.type: Easing.Linear from: 20 to: 380 loops: Animation.Infinite running: circle.visible } } Canvas { id: c3 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.03 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.23-configs["Shadow Size"]/2, -Math.PI/6, Math.PI/12+Math.PI*2/3 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } rotation: 90 NumberAnimation on rotation { duration: 2000 easing.type: Easing.Linear from: 90 to: -270 loops: Animation.Infinite running: circle.visible } } Canvas { id: c4 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.06 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.17-configs["Shadow Size"]/2, -Math.PI/6, Math.PI/4+Math.PI*2/3 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } rotation: -70 SequentialAnimation on rotation { running: circle.visible loops: Animation.Infinite RotationAnimation { duration: 1150 easing.type: Easing.InOutCubic from: -70 to: 290 } RotationAnimation { duration: 1150 easing.type: Easing.InOutCubic from: 290 to: -70 direction: RotationAnimation.Counterclockwise } } } Canvas { id: c5 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.1 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.05-configs["Shadow Size"]/2, 0, -Math.PI*2 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } } Canvas { id: c0 anchors.centerIn: parent width: size height: size contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,size,size); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = size*0.16 - configs["Shadow Size"]; context.beginPath(); context.arc(size/2, size/2, size*0.38-configs["Shadow Size"]/2, 0, -Math.PI/3 , true); context.strokeStyle = configs["Circle Color"]; context.stroke(); } NumberAnimation on rotation { duration: 1500 easing.type: Easing.Linear from: 0 to: 360 loops: Animation.Infinite running: circle.visible } } } Canvas { id: line anchors.centerIn: parent width: widget.width height: widget.height contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,width,height); context.shadowBlur = configs["Shadow Size"]; context.shadowColor = configs["Shadow Color"]; context.lineWidth = Math.max(0.08*configs["Line Width"], 0.1); context.strokeStyle = configs["Line Color"]; let deg = Math.asin(w/2/r)*0.95; context.beginPath(); if (configs["Curve"]) { if (circle.visible) { context.arc(w/2, -r+h/2, r, deg+Math.PI/2, Math.PI/2+circle.scale*size/1.22/r, true); context.stroke(); context.beginPath(); context.arc(w/2, -r+h/2, r, -deg+Math.PI/2, Math.PI/2-circle.scale*size/1.22/r, false); context.stroke(); } else { context.arc(w/2, -r+h/2, r, deg+Math.PI/2, -deg+Math.PI/2, true); context.stroke(); } } else { if (circle.visible) { context.moveTo(0, height/2); context.lineTo(width/2-circle.scale*size/1.22, height/2); context.stroke(); context.beginPath(); context.moveTo(width/2+circle.scale*size/1.22, height/2); context.lineTo(width, height/2); context.stroke(); } else { context.moveTo(0, height/2); context.lineTo(width, height/2); context.stroke(); } } } } menu: Menu { Action { text: qsTr("Settings") + "..." onTriggered: styleDialog.active = true } } Loader { id: styleDialog active: false sourceComponent: NVG.Window { id: window title: qsTr("Settings") visible: true minimumWidth: 380 minimumHeight: 500 width: minimumWidth height: minimumHeight transientParent: widget.NVG.View.window property var configuration Page { id: cfg_page anchors.fill: parent header: TitleBar { text: qsTr("UI Settings") standardButtons: Dialog.Save | Dialog.Reset onAccepted: { configuration = rootPreference.save(); widget.settings.styles = configuration; styleDialog.active = false; } onReset: { rootPreference.load(); let cfg = rootPreference.save(); widget.settings.styles = cfg; } } ColumnLayout { id: root anchors.fill: parent anchors.margins: 16 anchors.topMargin: 0 Flickable { Layout.fillWidth: true Layout.fillHeight: true clip: true contentWidth: preferenceLayout.implicitWidth contentHeight: preferenceLayout.implicitHeight ColumnLayout { id: preferenceLayout width: root.width P.PreferenceGroup { id: rootPreference Layout.fillWidth: true label: qsTr("Configuration") onPreferenceEdited: { widget.settings.styles = rootPreference.save(); } P.SwitchPreference { name: "Curve" label: qsTr("Curve") defaultValue: defaultValues["Curve"] } P.ColorPreference { name: "Line Color" label: qsTr("Line Color") defaultValue: defaultValues["Line Color"] } P.SliderPreference { name: "Line Width" label: qsTr("Line Width") from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Line Width"] displayValue: value + "%" } P.ColorPreference { name: "Shadow Color" label: qsTr("Shadow Color") defaultValue: defaultValues["Shadow Color"] } P.SliderPreference { name: "Shadow Size" label: qsTr("Shadow Size") from: 0 to: 3 stepSize: 0.1 defaultValue: defaultValues["Shadow Size"] displayValue: Math.round(value*10)/10 + "px" } P.Separator {} P.SwitchPreference { id: _cfg_battle_ui name: "Battle UI" label: qsTr("Battle UI") defaultValue: defaultValues["Battle UI"] } P.ColorPreference { name: "Circle Color" label: qsTr("Circle Color") visible: _cfg_battle_ui.value defaultValue: defaultValues["Circle Color"] } P.Separator {} P.SwitchPreference { id: _cfg_clock_visible name: "Clock Visible" label: qsTr("Clock Visible") visible: !_cfg_battle_ui.value enabled: visible defaultValue: defaultValues["Clock Visible"] } P.SwitchPreference { name: "Full Clock" label: qsTr("24 Hour Clock") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value defaultValue: defaultValues["Full Clock"] } P.ColorPreference { name: "Font Color" label: qsTr("Font Color") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value defaultValue: defaultValues["Font Color"] } P.SliderPreference { name: "Font Size" label: qsTr("Font Size") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Font Size"] displayValue: value + "%" } P.SelectPreference { name: "Font Name" label: qsTr("Font Style") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value icon.name: "solid:\uf1fc" defaultValue: defaultValues["Font Name"] model: fonts } P.SelectPreference { name: "Font Weight" label: qsTr("Font Weight") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value icon.name: "solid:\uf1fc" defaultValue: defaultValues["Font Weight"] model: sfontweight } P.SliderPreference { name: "Text Vertical Offset" label: qsTr("Text Vertical Offset") visible: !_cfg_battle_ui.value enabled: visible && _cfg_clock_visible.value from: -100 to: 100 stepSize: 1 defaultValue: defaultValues["Text Vertical Offset"] displayValue: value + "%" } Component.onCompleted: { if(!widget.settings.styles) { configuration = rootPreference.save(); widget.settings.styles = configuration; } rootPreference.load(widget.settings.styles); configuration = widget.settings.styles; } } } } } } onClosing: { widget.settings.styles = configuration; styleDialog.active = false; } } } }