You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

509 lines
20 KiB

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Shapes 1.1
import QtGraphicalEffects 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
import "utils.js" as Utils
T.Widget {
id: widget
title: qsTr("Weather Widget")
solid: true
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 var configs: widget.settings.styles ?? {"Location":"","Display Location":"","Update Interval":{"Value":1,"Unit":1},"Background Color":"#ffa502","Background Opacity":60,"Area Opacity Difference":17,"Icon Color":"#fefefe","Temperature Text Settings":{"Font Color":"#f5f5f5","Font Size":90,"Font Name":fonts.length-1,"Font Weight":1,"X Offset":33,"Y Offset":32},"Area Text Settings":{"Font Color":"#f5f5f5","Font Size":60,"Font Name":fonts.length-1,"Font Weight":1,"X Offset":0,"Y Offset":-56,"Border Margin":40}}
readonly property real w: widget.width
readonly property real h: 0.46*widget.width
Item {
id: main
width: w
height: h
anchors.centerIn: parent
layer.enabled: true
layer.effect: OpacityMask{
maskSource: Rectangle {
width: w
height: h
color: "black"
radius: h/15
}
}
Rectangle {
id: weather_box
width: w
height: h
anchors.left: parent.left
anchors.top: parent.top
opacity: configs["Background Opacity"]/100
color: configs["Background Color"]
}
Item {
id: sdialog
width: h/8
height: h/8
anchors.left: parent.left
anchors.leftMargin: h/16
anchors.topMargin: h/14
anchors.top: parent.top
Shape {
anchors.fill: parent
ShapePath {
strokeWidth: parent.height/120
strokeColor: configs["Icon Color"]
startX: h/44; startY: h/800
PathLine { x: h*0.10227272727272727; y: h/800 }
}
ShapePath {
strokeWidth: parent.height/120
strokeColor: configs["Icon Color"]
startX: h/44; startY: h/800 + h/32
PathLine { x: h*0.10227272727272727; y: h/800 + h/32 }
}
ShapePath {
strokeWidth: parent.height/120
strokeColor: configs["Icon Color"]
startX: h/44; startY: h/800 + h/16
PathLine { x: h*0.10227272727272727; y: h/800 + h/16 }
}
}
MouseArea {
anchors.fill: parent
enabled: !styleDialog.active
onClicked: {
styleDialog.active = true;
}
}
}
Item {
id: weather
width: 0.655*w
height: h
anchors.left: parent.left
anchors.top: parent.top
Image {
id: weather_mask
anchors.centerIn: weather
autoTransform: true
visible: false
source: "../Images/Weather/Unknown.png"
}
Rectangle {
id: wcolor
width: h/1.1
height: h/1.1
anchors.centerIn: weather
visible: false
color: configs["Icon Color"]
}
OpacityMask {
anchors.fill: wcolor
source: wcolor
maskSource: weather_mask
}
}
Rectangle {
id: temper_box
width: 0.345*w
height: h
anchors.right: parent.right
anchors.top: parent.top
opacity: configs["Background Opacity"]*configs["Area Opacity Difference"]/10000
color: "black"
}
Text {
id: area
anchors.centerIn: temper_box
anchors.horizontalCenterOffset: temper_box.width*configs["Area Text Settings"]["X Offset"]/200
anchors.verticalCenterOffset: h*configs["Area Text Settings"]["Y Offset"]/200
color: configs["Area Text Settings"]["Font Color"]
text: ""
font.pixelSize: w*0.0009*configs["Area Text Settings"]["Font Size"]
font.family: fonts[configs["Area Text Settings"]["Font Name"]]
font.weight: fontweight[configs["Area Text Settings"]["Font Weight"]]
Rectangle {
anchors.fill: parent
anchors.margins: -area.font.pixelSize*configs["Area Text Settings"]["Border Margin"]/100
color: "transparent"
border.color: configs["Area Text Settings"]["Font Color"]
border.width: area.font.pixelSize/15
radius: area.font.pixelSize/3.5
visible: Boolean(area.text)
}
}
Text {
id: temperature
anchors.centerIn: temper_box
anchors.horizontalCenterOffset: temper_box.width*configs["Temperature Text Settings"]["X Offset"]/200
anchors.verticalCenterOffset: h*configs["Temperature Text Settings"]["Y Offset"]/200
color: configs["Temperature Text Settings"]["Font Color"]
text: "--°"
font.pixelSize: w*0.002*configs["Temperature Text Settings"]["Font Size"]
font.family: fonts[configs["Temperature Text Settings"]["Font Name"]]
font.weight: fontweight[configs["Temperature Text Settings"]["Font Weight"]]
}
}
function setWeather() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState === XMLHttpRequest.DONE) {
let data = xhr.responseText.toString();
if (data) {
let jdata = JSON.parse(data);
temperature.text = jdata["current_condition"][0]["temp_C"] + "°";
if (!configs["Display Location"] && !configs["Location"]) {
area.text = jdata["nearest_area"][0]["areaName"][0]["value"];
}
weather_mask.source = "../Images/Weather/" + Utils.weather_codes[jdata["current_condition"][0]["weatherCode"]] ?? "Unknown" + ".png";
} else {
weather_mask.source = "../Images/Weather/Unknown.png";
}
}
}
if (configs["Location"])
xhr.open("GET", 'https://wttr.in/'+configs["Location"]+'?format=j1');
else
xhr.open("GET", 'https://wttr.in/?format=j1');
xhr.send();
if (configs["Display Location"]) {
area.text = configs["Display Location"];
} else if (configs["Location"]) {
area.text = configs["Location"]
}
}
Timer {
id: timer
interval: 600000
running: widget.NVG.View.exposed
repeat: true
triggeredOnStart: true
onTriggered: {
setWeather();
}
}
menu: Menu {
Action {
text: qsTr("Settings") + "..."
onTriggered: styleDialog.active = true
}
Action {
text: qsTr("Refresh")
onTriggered: timer.restart()
}
}
Loader {
id: styleDialog
active: false
sourceComponent: NVG.Window {
id: window
title: qsTr("Settings")
visible: true
minimumWidth: 450
minimumHeight: 550
width: minimumWidth
height: minimumHeight
transientParent: widget.NVG.View.window
property var configuration
Page {
id: cfg_page
anchors.fill: parent
header: TitleBar {
text: qsTr("Settings")
standardButtons: Dialog.Save | Dialog.Reset
onAccepted: {
configuration = rootPreference.save();
widget.settings.styles = configuration;
styleDialog.active = false;
timer.interval = 60000*configs["Update Interval"]["Value"]*(1+59*configs["Update Interval"]["Unit"])
setWeather();
}
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.TextFieldPreference {
name: "Location"
label: qsTr("Location")
message: qsTr("Search address by location, latitude and longitude.")
}
P.TextFieldPreference {
name: "Display Location"
label: qsTr("Display Location")
message: "The location to display in widget."
}
P.DialogPreference {
name: "Update Interval"
label: qsTr("Update Interval")
live: true
displayValue: _cfg_update_interval_value.value + " " + [qsTr("Minutes"), qsTr("Hours")][_cfg_update_interval_unit.value]
P.SpinPreference {
id: _cfg_update_interval_value
name: "Value"
from: 1
to: 1440
editable: true
defaultValue: 1
}
P.SelectPreference {
id: _cfg_update_interval_unit
name: "Unit"
label: qsTr("Unit")
defaultValue: 1
model: [qsTr("Minutes"), qsTr("Hours")]
}
}
P.ColorPreference {
name: "Background Color"
label: qsTr("Background Color")
defaultValue: "#ffa502"
}
P.SliderPreference {
name: "Background Opacity"
label: qsTr("Background Opacity")
from: 0
to: 100
stepSize: 1
defaultValue: 60
displayValue: value + "%"
}
P.SliderPreference {
name: "Area Opacity Difference"
label: qsTr("Area Opacity Difference")
from: 0
to: 100
stepSize: 1
defaultValue: 17
displayValue: value + "%"
}
P.ColorPreference {
name: "Icon Color"
label: qsTr("Icon Color")
defaultValue: "#fefefe"
}
P.DialogPreference {
name: "Temperature Text Settings"
label: qsTr("Temperature Text Settings")
icon.name: "solid:\uf1fc"
live: true
P.ColorPreference {
name: "Font Color"
label: qsTr("Font Color")
defaultValue: "#f5f5f5"
}
P.SliderPreference {
name: "Font Size"
label: qsTr("Font Size")
from: 1
to: 100
stepSize: 1
defaultValue: 90
displayValue: value + "%"
}
P.SelectPreference {
name: "Font Name"
label: qsTr("Font Style")
defaultValue: fonts.length-1
model: fonts
}
P.SelectPreference {
name: "Font Weight"
label: qsTr("Font Weight")
defaultValue: 1
model: sfontweight
}
P.SliderPreference {
name: "X Offset"
label: qsTr("X Offset")
from: -100
to: 100
stepSize: 1
defaultValue: 33
displayValue: value + "%"
}
P.SliderPreference {
name: "Y Offset"
label: qsTr("Y Offset")
from: -100
to: 100
stepSize: 1
defaultValue: 32
displayValue: value + "%"
}
}
P.DialogPreference {
name: "Area Text Settings"
label: qsTr("Area Text Settings")
icon.name: "solid:\uf1fc"
live: true
P.ColorPreference {
name: "Font Color"
label: qsTr("Font Color")
defaultValue: "#f5f5f5"
}
P.SliderPreference {
name: "Font Size"
label: qsTr("Font Size")
from: 1
to: 100
stepSize: 1
defaultValue: 60
displayValue: value + "%"
}
P.SelectPreference {
name: "Font Name"
label: qsTr("Font Style")
defaultValue: fonts.length-1
model: fonts
}
P.SelectPreference {
name: "Font Weight"
label: qsTr("Font Weight")
defaultValue: 1
model: sfontweight
}
P.SliderPreference {
name: "X Offset"
label: qsTr("X Offset")
from: -100
to: 100
stepSize: 1
defaultValue: 0
displayValue: value + "%"
}
P.SliderPreference {
name: "Y Offset"
label: qsTr("Y Offset")
from: -100
to: 100
stepSize: 1
defaultValue: -56
displayValue: value + "%"
}
P.SliderPreference {
name: "Border Margin"
label: qsTr("Border Margin")
from: 0
to: 100
stepSize: 1
defaultValue: 40
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;
}
}
}
}