forked from Shinonome/dots-hyprland
feat(modules/bar): add weather bar
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
// credits: calestia
|
||||
// this snippet is taken from
|
||||
// https://github.com/caelestia-dots/shell
|
||||
readonly property var codeToName: ({
|
||||
"113": "clear_day",
|
||||
"116": "partly_cloudy_day",
|
||||
"119": "cloud",
|
||||
"122": "cloud",
|
||||
"143": "foggy",
|
||||
"176": "rainy",
|
||||
"179": "rainy",
|
||||
"182": "rainy",
|
||||
"185": "rainy",
|
||||
"200": "thunderstorm",
|
||||
"227": "cloudy_snowing",
|
||||
"230": "snowing_heavy",
|
||||
"248": "foggy",
|
||||
"260": "foggy",
|
||||
"263": "rainy",
|
||||
"266": "rainy",
|
||||
"281": "rainy",
|
||||
"284": "rainy",
|
||||
"293": "rainy",
|
||||
"296": "rainy",
|
||||
"299": "rainy",
|
||||
"302": "weather_hail",
|
||||
"305": "rainy",
|
||||
"308": "weather_hail",
|
||||
"311": "rainy",
|
||||
"314": "rainy",
|
||||
"317": "rainy",
|
||||
"320": "cloudy_snowing",
|
||||
"323": "cloudy_snowing",
|
||||
"326": "cloudy_snowing",
|
||||
"329": "snowing_heavy",
|
||||
"332": "snowing_heavy",
|
||||
"335": "snowing",
|
||||
"338": "snowing_heavy",
|
||||
"350": "rainy",
|
||||
"353": "rainy",
|
||||
"356": "rainy",
|
||||
"359": "weather_hail",
|
||||
"362": "rainy",
|
||||
"365": "rainy",
|
||||
"368": "cloudy_snowing",
|
||||
"371": "snowing",
|
||||
"374": "rainy",
|
||||
"377": "rainy",
|
||||
"386": "thunderstorm",
|
||||
"389": "thunderstorm",
|
||||
"392": "thunderstorm",
|
||||
"395": "snowing"
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import "root:/services"
|
||||
import "root:/modules/common/"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import "root:/modules/bar/weather"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -27,7 +28,8 @@ Scope {
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
Variants { // For each monitor
|
||||
Variants {
|
||||
// For each monitor
|
||||
model: {
|
||||
const screens = Quickshell.screens;
|
||||
const list = ConfigOptions.bar.screenList;
|
||||
@@ -42,12 +44,8 @@ Scope {
|
||||
|
||||
property ShellScreen modelData
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(modelData)
|
||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen.width) ? 2 :
|
||||
(Appearance.sizes.barShortenScreenWidthThreshold >= screen.width) ? 1 : 0
|
||||
readonly property int centerSideModuleWidth:
|
||||
(useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened :
|
||||
(useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened :
|
||||
Appearance.sizes.barCenterSideModuleWidth
|
||||
property real useShortenedForm: (Appearance.sizes.barHellaShortenScreenWidthThreshold >= screen.width) ? 2 : (Appearance.sizes.barShortenScreenWidthThreshold >= screen.width) ? 1 : 0
|
||||
readonly property int centerSideModuleWidth: (useShortenedForm == 2) ? Appearance.sizes.barCenterSideModuleWidthHellaShortened : (useShortenedForm == 1) ? Appearance.sizes.barCenterSideModuleWidthShortened : Appearance.sizes.barCenterSideModuleWidth
|
||||
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
implicitHeight: barHeight + Appearance.rounding.screenRounding
|
||||
@@ -74,7 +72,7 @@ Scope {
|
||||
}
|
||||
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
|
||||
height: barHeight
|
||||
|
||||
|
||||
MouseArea { // Left side | scroll to change brightness
|
||||
id: barLeftSideMouseArea
|
||||
anchors.left: parent.left
|
||||
@@ -87,21 +85,21 @@ Scope {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: (event) => {
|
||||
barLeftSideMouseArea.hovered = true
|
||||
onEntered: event => {
|
||||
barLeftSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: (event) => {
|
||||
barLeftSideMouseArea.hovered = false
|
||||
barLeftSideMouseArea.trackingScroll = false
|
||||
onExited: event => {
|
||||
barLeftSideMouseArea.hovered = false;
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: (event) => {
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftOpen')
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftOpen');
|
||||
}
|
||||
}
|
||||
// Scroll to change brightness
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
onWheel: event => {
|
||||
if (event.angleDelta.y < 0)
|
||||
barRoot.brightnessMonitor.setBrightness(barRoot.brightnessMonitor.brightness - 0.05);
|
||||
else if (event.angleDelta.y > 0)
|
||||
@@ -113,17 +111,18 @@ Scope {
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: (mouse) => {
|
||||
onPositionChanged: mouse => {
|
||||
if (barLeftSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barLeftSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barLeftSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx*dx + dy*dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdBrightnessHide')
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdBrightnessHide');
|
||||
barLeftSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Item { // Left section
|
||||
Item {
|
||||
// Left section
|
||||
anchors.fill: parent
|
||||
implicitHeight: leftSectionRowLayout.implicitHeight
|
||||
implicitWidth: leftSectionRowLayout.implicitWidth
|
||||
@@ -135,22 +134,22 @@ Scope {
|
||||
side: "left"
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
}
|
||||
|
||||
|
||||
RowLayout { // Content
|
||||
id: leftSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
RippleButton { // Left sidebar button
|
||||
RippleButton {
|
||||
// Left sidebar button
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: false
|
||||
property real buttonPadding: 5
|
||||
implicitWidth: distroIcon.width + buttonPadding * 2
|
||||
implicitHeight: distroIcon.height + buttonPadding * 2
|
||||
|
||||
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
@@ -159,10 +158,10 @@ Scope {
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarLeftOpen
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
property color colText: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer0
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftToggle')
|
||||
Hyprland.dispatch('global quickshell:sidebarLeftToggle');
|
||||
}
|
||||
|
||||
CustomIcon {
|
||||
@@ -170,10 +169,9 @@ Scope {
|
||||
anchors.centerIn: parent
|
||||
width: 19.5
|
||||
height: 19.5
|
||||
source: ConfigOptions.bar.topLeftIcon == 'distro' ?
|
||||
SystemInfo.distroIcon : "spark-symbolic"
|
||||
source: ConfigOptions.bar.topLeftIcon == 'distro' ? SystemInfo.distroIcon : "spark-symbolic"
|
||||
}
|
||||
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: distroIcon
|
||||
source: distroIcon
|
||||
@@ -211,34 +209,38 @@ Scope {
|
||||
visible: barRoot.useShortenedForm < 2
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
|
||||
VerticalBarSeparator {
|
||||
visible: ConfigOptions?.bar.borderless
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: middleCenterGroup
|
||||
padding: workspacesWidget.widgetPadding
|
||||
Layout.fillHeight: true
|
||||
|
||||
|
||||
Workspaces {
|
||||
id: workspacesWidget
|
||||
bar: barRoot
|
||||
Layout.fillHeight: true
|
||||
MouseArea { // Right-click to toggle overview
|
||||
MouseArea {
|
||||
// Right-click to toggle overview
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onPressed: (event) => {
|
||||
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.RightButton) {
|
||||
Hyprland.dispatch('global quickshell:overviewToggle')
|
||||
Hyprland.dispatch('global quickshell:overviewToggle');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
|
||||
VerticalBarSeparator {
|
||||
visible: ConfigOptions?.bar.borderless
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightCenterGroup
|
||||
@@ -248,13 +250,13 @@ Scope {
|
||||
Layout.fillHeight: true
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle')
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
BarGroup {
|
||||
id: rightCenterGroupContent
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
ClockWidget {
|
||||
showDate: (ConfigOptions.bar.verbose && barRoot.useShortenedForm < 2)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
@@ -273,6 +275,19 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBarSeparator {
|
||||
visible: ConfigOptions?.bar.borderless
|
||||
}
|
||||
|
||||
// Weather
|
||||
BarGroup {
|
||||
id: weatherGroupContent
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
WeatherBar {
|
||||
visible: ConfigOptions.bar.weather.show
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea { // Right side | scroll to change volume
|
||||
@@ -286,28 +301,27 @@ Scope {
|
||||
property real lastScrollX: 0
|
||||
property real lastScrollY: 0
|
||||
property bool trackingScroll: false
|
||||
|
||||
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
onEntered: (event) => {
|
||||
barRightSideMouseArea.hovered = true
|
||||
onEntered: event => {
|
||||
barRightSideMouseArea.hovered = true;
|
||||
}
|
||||
onExited: (event) => {
|
||||
barRightSideMouseArea.hovered = false
|
||||
barRightSideMouseArea.trackingScroll = false
|
||||
onExited: event => {
|
||||
barRightSideMouseArea.hovered = false;
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
onPressed: (event) => {
|
||||
onPressed: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightOpen')
|
||||
}
|
||||
else if (event.button === Qt.RightButton) {
|
||||
MprisController.activePlayer.next()
|
||||
Hyprland.dispatch('global quickshell:sidebarRightOpen');
|
||||
} else if (event.button === Qt.RightButton) {
|
||||
MprisController.activePlayer.next();
|
||||
}
|
||||
}
|
||||
// Scroll to change volume
|
||||
WheelHandler {
|
||||
onWheel: (event) => {
|
||||
onWheel: event => {
|
||||
const currentVolume = Audio.value;
|
||||
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
|
||||
if (event.angleDelta.y < 0)
|
||||
@@ -321,12 +335,12 @@ Scope {
|
||||
}
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
}
|
||||
onPositionChanged: (mouse) => {
|
||||
onPositionChanged: mouse => {
|
||||
if (barRightSideMouseArea.trackingScroll) {
|
||||
const dx = mouse.x - barRightSideMouseArea.lastScrollX;
|
||||
const dy = mouse.y - barRightSideMouseArea.lastScrollY;
|
||||
if (Math.sqrt(dx*dx + dy*dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdVolumeHide')
|
||||
if (Math.sqrt(dx * dx + dy * dy) > osdHideMouseMoveThreshold) {
|
||||
Hyprland.dispatch('global quickshell:osdVolumeHide');
|
||||
barRightSideMouseArea.trackingScroll = false;
|
||||
}
|
||||
}
|
||||
@@ -336,7 +350,7 @@ Scope {
|
||||
anchors.fill: parent
|
||||
implicitHeight: rightSectionRowLayout.implicitHeight
|
||||
implicitWidth: rightSectionRowLayout.implicitWidth
|
||||
|
||||
|
||||
ScrollHint {
|
||||
reveal: barRightSideMouseArea.hovered
|
||||
icon: "volume_up"
|
||||
@@ -351,13 +365,13 @@ Scope {
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
layoutDirection: Qt.RightToLeft
|
||||
|
||||
|
||||
RippleButton { // Right sidebar button
|
||||
id: rightSidebarButton
|
||||
Layout.margins: 4
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10*2
|
||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10 * 2
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
@@ -373,7 +387,7 @@ Scope {
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle')
|
||||
Hyprland.dispatch('global quickshell:sidebarRightToggle');
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -381,7 +395,7 @@ Scope {
|
||||
anchors.centerIn: parent
|
||||
property real realSpacing: 15
|
||||
spacing: 0
|
||||
|
||||
|
||||
Revealer {
|
||||
reveal: Audio.sink?.audio?.muted ?? false
|
||||
Layout.fillHeight: true
|
||||
@@ -475,9 +489,6 @@ Scope {
|
||||
opacity: 1.0 - Appearance.transparency
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/constants"
|
||||
import "root:/services"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real margin: 5
|
||||
implicitHeight: 32
|
||||
implicitWidth: mouseArea.implicitWidth + margin * 2
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
property bool hovered: false
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
anchors.centerIn: root
|
||||
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
popupLoader.item.visible = true;
|
||||
}
|
||||
onExited: {
|
||||
popupLoader.item.visible = false;
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: WeatherIcons.codeToName[WeatherService.data.wCode]
|
||||
iconSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: true
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: WeatherService.data.temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: popupLoader
|
||||
active: true
|
||||
|
||||
component: PopupWindow {
|
||||
id: popupWindow
|
||||
implicitWidth: weatherPopup.implicitWidth
|
||||
implicitHeight: weatherPopup.implicitHeight
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Bottom
|
||||
anchor.rect.x: (root.implicitWidth - popupWindow.implicitWidth) / 2
|
||||
anchor.rect.y: root.implicitHeight + 10
|
||||
color: "transparent"
|
||||
WeatherPopup {
|
||||
id: weatherPopup
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
radius: Appearance.rounding.verysmall
|
||||
color: Appearance.colors.colLayer1
|
||||
border.color: Appearance.colors.colShadow
|
||||
border.width: 1
|
||||
implicitWidth: columnLayout.implicitWidth * 2
|
||||
implicitHeight: columnLayout.implicitHeight * 2
|
||||
Layout.fillWidth: parent
|
||||
|
||||
property alias title: title.text
|
||||
property alias value: value.text
|
||||
property alias symbol: symbol.text
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
spacing: -10
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
MaterialSymbol {
|
||||
id: symbol
|
||||
fill: 0
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
Text {
|
||||
id: title
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: value
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
readonly property real margin: 10
|
||||
implicitWidth: columnLayout.implicitWidth + margin * 2
|
||||
implicitHeight: columnLayout.implicitHeight + margin * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: 12
|
||||
clip: true
|
||||
border.color: Appearance.colors.colShadow
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
spacing: 5
|
||||
anchors.centerIn: root
|
||||
implicitWidth: Math.max(header.implicitWidth, gridLayout.implicitWidth)
|
||||
implicitHeight: gridLayout.implicitHeight
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
id: header
|
||||
spacing: 5
|
||||
Layout.fillWidth: parent
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
MaterialSymbol {
|
||||
fill: 0
|
||||
text: "location_on"
|
||||
iconSize: Appearance.font.pixelSize.huge
|
||||
}
|
||||
|
||||
Text {
|
||||
text: WeatherService.data.city
|
||||
font.pixelSize: Appearance.font.pixelSize.large
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics grid
|
||||
GridLayout {
|
||||
id: gridLayout
|
||||
columns: 2
|
||||
rowSpacing: 5
|
||||
columnSpacing: 5
|
||||
uniformCellWidths: true
|
||||
|
||||
WeatherCard {
|
||||
title: "UV Index"
|
||||
symbol: "wb_sunny"
|
||||
value: WeatherService.data.uv
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Wind"
|
||||
symbol: "air"
|
||||
value: `(${WeatherService.data.windDir}) ${WeatherService.data.wind}`
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Precipitation"
|
||||
symbol: "rainy_light"
|
||||
value: WeatherService.data.precip
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Humidity"
|
||||
symbol: "humidity_low"
|
||||
value: WeatherService.data.humidity
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Visibility"
|
||||
symbol: "visibility"
|
||||
value: WeatherService.data.visib
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Pressure"
|
||||
symbol: "readiness_score"
|
||||
value: WeatherService.data.press
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Sunrise"
|
||||
symbol: "wb_twilight"
|
||||
value: WeatherService.data.sunrise
|
||||
}
|
||||
WeatherCard {
|
||||
title: "Sunset"
|
||||
symbol: "bedtime"
|
||||
value: WeatherService.data.sunset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,16 @@ Singleton {
|
||||
property bool alwaysShowNumbers: false
|
||||
property int showNumberDelay: 300 // milliseconds
|
||||
}
|
||||
property QtObject weather: QtObject {
|
||||
property bool show: true
|
||||
// for specific location checkout gps setting
|
||||
property string city: "Istanbul"
|
||||
// use uscs units
|
||||
// by default use metric (SI) units
|
||||
property bool useUSCS: false
|
||||
// in minutes
|
||||
property int fetchInterval: 10
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject battery: QtObject {
|
||||
@@ -161,4 +171,11 @@ Singleton {
|
||||
property QtObject hacks: QtObject {
|
||||
property int arbitraryRaceConditionDelay: 20 // milliseconds
|
||||
}
|
||||
|
||||
// this is for weather and feature apis
|
||||
property QtObject gps: QtObject {
|
||||
property bool active: false
|
||||
property real latitude: 41.27830580591624
|
||||
property real longitude: 28.730357071149154
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
import "root:/modules/common"
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
// 10 minute
|
||||
readonly property int fetchInterval: ConfigOptions.bar.weather.fetchInterval * 60 * 1000
|
||||
property var data: ({
|
||||
uv: 0,
|
||||
humidity: 0,
|
||||
sunrise: 0,
|
||||
sunset: 0,
|
||||
windDir: 0,
|
||||
wCode: 0,
|
||||
city: 0,
|
||||
wind: 0,
|
||||
precip: 0,
|
||||
visib: 0,
|
||||
press: 0,
|
||||
temp: 0
|
||||
})
|
||||
|
||||
function refineData(data) {
|
||||
let temp = {};
|
||||
temp.uv = data?.current?.uvIndex || 0;
|
||||
temp.humidity = (data?.current?.humidity || 0) + "%";
|
||||
temp.sunrise = data?.astronomy?.sunrise || "0.0";
|
||||
temp.sunset = data?.astronomy?.sunset || "0.0";
|
||||
temp.windDir = data?.current?.winddir16Point || "N";
|
||||
temp.wCode = data?.current?.weatherCode || "113";
|
||||
temp.city = data?.location?.areaName[0].value || "Istanbul";
|
||||
temp.temp = "";
|
||||
if (ConfigOptions.bar.weather.useUSCS) {
|
||||
temp.wind = (data?.current?.windspeedMiles || 0) + " mph";
|
||||
temp.precip = (data?.current?.precipInches || 0) + " in";
|
||||
temp.visib = (data?.current?.visibilityMiles || 0) + " m";
|
||||
temp.press = (data?.current?.pressureInches || 0) + " psi";
|
||||
temp.temp += (data?.current?.temp_F || 0);
|
||||
temp.temp += " (" + (data?.current?.FeelsLikeF || 0) + ") ";
|
||||
temp.temp += "\u{02109}";
|
||||
} else {
|
||||
temp.wind = (data?.current?.windspeedKmph || 0) + " km/h";
|
||||
temp.precip = (data?.current?.precipMM || 0) + " mm";
|
||||
temp.visib = (data?.current?.visibility || 0) + " km";
|
||||
temp.press = (data?.current?.pressure || 0) + " hPa";
|
||||
temp.temp += (data?.current?.temp_C || 0);
|
||||
temp.temp += " (" + (data?.current?.FeelsLikeC || 0) + ") ";
|
||||
temp.temp += "\u{02103}";
|
||||
}
|
||||
root.data = temp;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
let command = "curl -s wttr.in";
|
||||
if (ConfigOptions.gps.active) {
|
||||
command += `/${ConfigOptions.gps.latitude},${Config.gps.longitude}`;
|
||||
} else {
|
||||
command += `/${formatCityName(ConfigOptions.bar.weather.city)}`;
|
||||
}
|
||||
|
||||
// format as json
|
||||
command += "?format=j1";
|
||||
command += " | ";
|
||||
// only take the current weather, location, asytronmy data
|
||||
command += "jq '{current: .current_condition[0], location: .nearest_area[0], astronomy: .weather[0].astronomy[0]}'";
|
||||
fetcher.command[2] = command;
|
||||
fetcher.running = true;
|
||||
}
|
||||
|
||||
function formatCityName(cityName) {
|
||||
return cityName.trim().split(/\s+/).join('+');
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fetcher
|
||||
command: ["bash", "-c", ""]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.length === 0)
|
||||
return;
|
||||
try {
|
||||
const parsedData = JSON.parse(text);
|
||||
root.refineData(parsedData);
|
||||
// console.info(`[ data: ${JSON.stringify(parsedData)}`);
|
||||
} catch (e) {
|
||||
console.error(`[WeatherService] ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: true
|
||||
repeat: true
|
||||
interval: root.fetchInterval
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.getData()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user