import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtGraphicalEffects 1.0 import QtQuick.Shapes 1.1 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 import "." WidgetTemplate { id: widget title: qsTr("Vecto Clock") editing: styleDialog.active resizable: true version: "1.0.0" defaultValues: { "Weekday Settings": { "Color": "#ffffff", "Font Size": 43, "Font Name": Common.fonts.length - 1, "Font Weight": 2, "Letter Spacing": 4, "Y Offset": 50, "Gap": 12 }, "Line Settings": { "Color": "#ffffff", "Width": 30, "Position": -8 }, "Clock Settings": { "Full Clock": false, "Text Color": "#554e49", "Background Visible": true, "Background Color": "#ffffff", "Font Size": 33, "Font Name": Common.fonts.length - 1, "Font Weight": 1, "Letter Spacing": 0, "X Offset": 0, "Y Offset": 24, "Gap": 9 }, "Date Settings": { "Color": "#ffffff", "Font Size": 10, "Font Name": Common.fonts.length - 1, "Font Weight": 1, "Letter Spacing": 25, "X Offset": 29, "Y Offset": 12 } } menu: Menu { Action { text: qsTr("Settings") + "..." onTriggered: styleDialog.active = true } } readonly property var configs: widget.settings.styles onConfigsChanged: { weekday_mask_source.requestPaint(); divline.requestPaint(); clockbg.requestPaint(); configsFlag = true; } property bool configsFlag: false property real thour: 0 property real t12hour: 0 property real tmin: -1 readonly property var weekdays: ["SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"] property int weekday: -1 readonly property var months: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"] property string dateString: "" readonly property real whRatio: width/height readonly property real fixWHRatio: 2.1 property real clock_width: main.height*fixWHRatio * (0.5-0.005*configs["Line Settings"]["Position"]) property real left_gap_ratio: configs["Weekday Settings"]["Gap"]/100 property real right_gap_ratio: configs["Clock Settings"]["Gap"]/100 property real clock_text_size: weekday_mask.height*configs["Clock Settings"]["Font Size"]/100 property real right_topMargin: (timeinfo.height-clock_text_size)*configs["Clock Settings"]["Y Offset"]/100 Item { id: main anchors.centerIn: parent width: widget.width height: whRatio > fixWHRatio ? widget.height : widget.width/fixWHRatio Item { id: weekday_mask width: main.width - clock_width height: main.height anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter layer.enabled: true layer.effect: OpacityMask{ maskSource: weekday_mask_source } Canvas { id: weekday_mask_source visible: false anchors.fill: parent renderStrategy: Canvas.Cooperative contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,width,height); context.beginPath(); context.lineTo(weekday_mask.width-weekday_mask.height*left_gap_ratio, 0); context.lineTo(weekday_mask.width-0.45*weekday_mask.height-weekday_mask.height*left_gap_ratio, weekday_mask.height); context.lineTo(0, weekday_mask.height); context.lineTo(0, 0); context.closePath(); context.fill(); } onHeightChanged: { requestPaint(); } onWidthChanged: { requestPaint(); } } Text { id: weekday_text text: weekday>-1 ? weekdays[weekday] : "" color: configs["Weekday Settings"]["Color"] anchors.left: parent.left anchors.top: parent.top anchors.topMargin: (weekday_mask.height-height*0.75)*configs["Weekday Settings"]["Y Offset"]/100 style: Text.Outline styleColor: "transparent" font.pixelSize: weekday_mask.height*configs["Weekday Settings"]["Font Size"]/100 font.letterSpacing: weekday_mask.width/text.length*configs["Weekday Settings"]["Letter Spacing"]/100 font.family: Common.fonts[configs["Weekday Settings"]["Font Name"]] font.weight: Common.fontweight[configs["Weekday Settings"]["Font Weight"]] } } Item { id: timeinfo width: clock_width+0.45*timeinfo.height height: main.height anchors.right: parent.right anchors.top: parent.top Canvas { id: divline anchors.fill: parent renderTarget: Canvas.FramebufferObject renderStrategy: Canvas.Cooperative contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,width,height); context.lineWidth = 6*configs["Line Settings"]["Width"]/100; context.strokeStyle = configs["Line Settings"]["Color"]; context.beginPath(); context.moveTo(0, timeinfo.height); context.lineTo(0.45*timeinfo.height, 0); context.closePath(); context.stroke(); } onHeightChanged: { requestPaint(); } } Canvas { id: clockbg width: timeinfo.width height: clock_text_size visible: configs["Clock Settings"]["Background Visible"] anchors.right: parent.right anchors.top: parent.top anchors.topMargin: right_topMargin renderStrategy: Canvas.Cooperative contextType: "2d" onPaint: { context.reset(); context.clearRect(0,0,width,height); context.fillStyle = configs["Clock Settings"]["Background Color"]; context.beginPath(); context.moveTo(timeinfo.width, 0); context.lineTo(0.45*(timeinfo.height-right_topMargin)+timeinfo.height*right_gap_ratio, 0); context.lineTo(timeinfo.height*right_gap_ratio, timeinfo.height-right_topMargin); context.lineTo(timeinfo.width, timeinfo.height); context.closePath(); context.fill(); } onHeightChanged: { requestPaint(); } } Text { id: clock_text anchors.horizontalCenter: clockbg.horizontalCenter anchors.horizontalCenterOffset: -right_topMargin*0.225 + timeinfo.width*(0.225*configs["Clock Settings"]["X Offset"]+12.5)/100 + timeinfo.height*right_gap_ratio/2 anchors.verticalCenterOffset: height/100 anchors.verticalCenter: clockbg.verticalCenter text: "" color: configs["Clock Settings"]["Text Color"] style: Text.Outline styleColor: "transparent" font.pixelSize: clock_text_size/1.7 font.letterSpacing: clock_width/text.length*configs["Clock Settings"]["Letter Spacing"]/100 font.family: Common.fonts[configs["Clock Settings"]["Font Name"]] font.weight: Common.fontweight[configs["Clock Settings"]["Font Weight"]] } Text { id: date_text anchors.top: clockbg.bottom anchors.topMargin: (configs["Date Settings"]["Y Offset"]>0 ? (main.height-right_topMargin-clock_text_size-height) : right_topMargin+clock_text_size)*configs["Date Settings"]["Y Offset"]/100 anchors.left: clockbg.left anchors.leftMargin: (configs["Date Settings"]["X Offset"]>0 ? clock_width : main.width-timeinfo.width)*configs["Date Settings"]["X Offset"]/100 text: dateString color: configs["Date Settings"]["Color"] style: Text.Outline styleColor: "transparent" font.pixelSize: timeinfo.height*configs["Date Settings"]["Font Size"]/100 font.letterSpacing: clock_width/text.length*configs["Date Settings"]["Letter Spacing"]/100 font.family: Common.fonts[configs["Date Settings"]["Font Name"]] font.weight: Common.fontweight[configs["Date Settings"]["Font Weight"]] } } } Timer { interval: 250 running: true repeat: true onTriggered: { var now = new Date(); if (tmin !== now.getMinutes() || configsFlag) { configsFlag = false; tmin = now.getMinutes(); thour = now.getHours(); if (configs["Clock Settings"]["Full Clock"]) { clock_text.text = [("0"+thour).slice(-2), ("0"+tmin).slice(-2)].join(":"); } else { t12hour = thour > 12 ? thour - 12 : thour; clock_text.text = [t12hour,("0"+tmin).slice(-2)].join(":") + (thour > 11 ? "PM" : "AM"); } if (weekday !== now.getDay()) { weekday = now.getDay(); dateString = [months[now.getMonth()], now.getDate(), now.getFullYear()].join(" "); } } } } Loader { id: styleDialog active: false sourceComponent: NVG.Window { id: window title: qsTr("Settings") visible: true minimumWidth: 500 minimumHeight: 600 width: minimumWidth height: minimumHeight transientParent: widget.NVG.View.window property var configuration Page { id: cfg_page anchors.fill: parent header: TitleBar { text: qsTr("Vecto Clock") 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: 16 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.DialogPreference { name: "Weekday Settings" label: qsTr("Weekday Settings") live: true icon.name: "regular:\uf1de" P.ColorPreference { name: "Color" label: qsTr("Color") defaultValue: defaultValues["Weekday Settings"]["Color"] } P.SliderPreference { name: "Font Size" label: qsTr("Font Size") from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Weekday Settings"]["Font Size"] displayValue: value + "%" } P.SelectPreference { name: "Font Name" label: qsTr("Font Style") defaultValue: defaultValues["Weekday Settings"]["Font Name"] model: Common.fonts } P.SelectPreference { name: "Font Weight" label: qsTr("Font Weight") defaultValue: defaultValues["Weekday Settings"]["Font Weight"] model: Common.sfontweight } P.SliderPreference { name: "Letter Spacing" label: qsTr("Letter Spacing") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Weekday Settings"]["Letter Spacing"] displayValue: value + "%" } P.SliderPreference { name: "Y Offset" label: qsTr("Y Offset") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Weekday Settings"]["Y Offset"] displayValue: value + "%" } P.SliderPreference { name: "Gap" label: qsTr("Gap") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Weekday Settings"]["Gap"] displayValue: value + "%" } } P.DialogPreference { name: "Line Settings" label: qsTr("Line Settings") live: true icon.name: "regular:\uf1de" P.ColorPreference { name: "Color" label: qsTr("Color") defaultValue: defaultValues["Line Settings"]["Color"] } P.SliderPreference { name: "Width" label: qsTr("Width") from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Line Settings"]["Width"] displayValue: value + "%" } P.SliderPreference { name: "Position" label: qsTr("Position") from: -100 to: 100 stepSize: 1 defaultValue: defaultValues["Line Settings"]["Position"] displayValue: value + "%" } } P.DialogPreference { name: "Clock Settings" label: qsTr("Clock Settings") live: true icon.name: "regular:\uf1de" P.SwitchPreference { name: "Full Clock" label: qsTr("24 Hour Clock") defaultValue: defaultValues["Clock Settings"]["Full Clock"] } P.ColorPreference { name: "Text Color" label: qsTr("Text Color") defaultValue: defaultValues["Clock Settings"]["Text Color"] } P.SwitchPreference { id: _cfg_clock_background_visible name:"Background Visible" label: qsTr("Background Visible") defaultValue: defaultValues["Clock Settings"]["Background Visible"] } P.ColorPreference { name: "Background Color" label: qsTr("Background Color") enabled: _cfg_clock_background_visible.value defaultValue: defaultValues["Clock Settings"]["Background Color"] } P.SliderPreference { name: "Font Size" label: qsTr("Font Size") from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Clock Settings"]["Font Size"] displayValue: value + "%" } P.SelectPreference { name: "Font Name" label: qsTr("Font Style") defaultValue: defaultValues["Clock Settings"]["Font Name"] model: Common.fonts } P.SelectPreference { name: "Font Weight" label: qsTr("Font Weight") defaultValue: defaultValues["Clock Settings"]["Font Weight"] model: Common.sfontweight } P.SliderPreference { name: "Letter Spacing" label: qsTr("Letter Spacing") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Clock Settings"]["Letter Spacing"] displayValue: value + "%" } P.SliderPreference { name: "X Offset" label: qsTr("X Offset") from: -100 to: 100 stepSize: 1 defaultValue: defaultValues["Clock Settings"]["X Offset"] displayValue: value + "%" } P.SliderPreference { name: "Y Offset" label: qsTr("Y Offset") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Clock Settings"]["Y Offset"] displayValue: value + "%" } P.SliderPreference { name: "Gap" label: qsTr("Gap") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Clock Settings"]["Gap"] displayValue: value + "%" } } P.DialogPreference { name: "Date Settings" label: qsTr("Date Settings") live: true icon.name: "regular:\uf1de" P.ColorPreference { name: "Color" label: qsTr("Color") defaultValue: defaultValues["Date Settings"]["Color"] } P.SliderPreference { name: "Font Size" label: qsTr("Font Size") from: 1 to: 100 stepSize: 1 defaultValue: defaultValues["Date Settings"]["Font Size"] displayValue: value + "%" } P.SelectPreference { name: "Font Name" label: qsTr("Font Style") defaultValue: defaultValues["Date Settings"]["Font Name"] model: Common.fonts } P.SelectPreference { name: "Font Weight" label: qsTr("Font Weight") defaultValue: defaultValues["Date Settings"]["Font Weight"] model: Common.sfontweight } P.SliderPreference { name: "Letter Spacing" label: qsTr("Letter Spacing") from: 0 to: 100 stepSize: 1 defaultValue: defaultValues["Date Settings"]["Letter Spacing"] displayValue: value + "%" } P.SliderPreference { name: "X Offset" label: qsTr("X Offset") from: -100 to: 100 stepSize: 1 defaultValue: defaultValues["Date Settings"]["X Offset"] displayValue: value + "%" } P.SliderPreference { name: "Y Offset" label: qsTr("Y Offset") from: -100 to: 100 stepSize: 1 defaultValue: defaultValues["Date Settings"]["Y Offset"] displayValue: value + "%" } } Component.onCompleted: { rootPreference.load(widget.settings.styles); configuration = widget.settings.styles; } } } } } } onClosing: { widget.settings.styles = configuration; styleDialog.active = false; } } } }