Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions quickshell/agility/Core/Components/SidebarNavRow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ Rectangle {
property string labelText: ""
property string statusText: ""
property bool isActive: false
property bool hasChevron: false
property bool isExpanded: false

signal rowClicked()
signal chevronClicked()

Layout.fillWidth: true
Layout.preferredHeight: Constants.size3Xl
Expand Down Expand Up @@ -78,6 +81,24 @@ Rectangle {

}

SvgIcon {
visible: rowRoot.hasChevron
icon: "chevron-down"
iconSize: 12
flat: true
rotation: rowRoot.isExpanded ? 0 : -90
iconColor: rowRoot.isActive ? Theme.fg : (hoverHandler.hovered ? Theme.fg : Theme.muted)
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: Constants.size3Xs

Behavior on rotation {
NumberAnimation {
duration: Constants.animFast
easing.type: Easing.OutQuad
}
}
}

}

HoverHandler {
Expand Down
43 changes: 40 additions & 3 deletions quickshell/agility/Core/Services/AppState.qml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Item {
property real activeNotificationHeight: 0
readonly property bool isNiri: Quickshell.env("NIRI_SOCKET") !== undefined || Quickshell.env("XDG_CURRENT_DESKTOP") === "niri"
property bool hasActiveWindow: false
property string niriActiveTitle: ""
property string niriActiveAppId: ""
readonly property string activeWindowTitle: isNiri ? niriActiveTitle : (Hyprland.activeWindow ? (Hyprland.activeWindow.title || "") : "")
readonly property string activeWindowClass: isNiri ? niriActiveAppId : (Hyprland.activeWindow ? (Hyprland.activeWindow.class || Hyprland.activeWindow.initialClass || "") : "")
readonly property bool hasWindowOnScreen: isNiri ? hasActiveWindow : Boolean(Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.toplevelCount > 0)
readonly property var focusWidgets: ["launcher", "clipboard", "wallpaper", "screenshot", "powerMenu"]
readonly property var standaloneWindows: ["minflair_settings", "minflair_keybinds", "packagemanager"]
Expand All @@ -40,7 +44,7 @@ Item {
if (!id)
return false;

return id === "dashboard" || id === "controlCenter" || id === "notificationsCenter" || id === "music" || id === "clock" || id.startsWith("systemTray_");
return id === "dashboard" || id === "controlCenter" || id === "notificationsCenter" || id === "music" || id === "clock" || id === "weather" || id.startsWith("systemTray_");
}

function isFocusWidget(id) {
Expand Down Expand Up @@ -231,14 +235,44 @@ Item {
}
}

Process {
id: niriFocusedWindowProc

command: ["niri", "msg", "-j", "focused-window"]

stdout: SplitParser {
onRead: (data) => {
if (!data)
return;
try {
let win = JSON.parse(data.trim());
if (win && win.id !== undefined) {
appState.niriActiveTitle = win.title || "";
appState.niriActiveAppId = win.app_id || "";
} else {
appState.niriActiveTitle = "";
appState.niriActiveAppId = "";
}
} catch (e) {
appState.niriActiveTitle = "";
appState.niriActiveAppId = "";
}
}
}
}

Timer {
id: niriDebounceTimer

interval: 50
repeat: false
onTriggered: {
if (appState.isNiri && !niriQueryProc.running)
niriQueryProc.running = true;
if (appState.isNiri) {
if (!niriQueryProc.running)
niriQueryProc.running = true;
if (!niriFocusedWindowProc.running)
niriFocusedWindowProc.running = true;
}
}
}

Expand All @@ -252,12 +286,15 @@ Item {
onTriggered: {
if (!niriQueryProc.running)
niriQueryProc.running = true;
if (!niriFocusedWindowProc.running)
niriFocusedWindowProc.running = true;
}
}

Component.onCompleted: {
if (appState.isNiri) {
niriQueryProc.running = true;
niriFocusedWindowProc.running = true;
niriEventStreamProc.running = true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion quickshell/agility/Core/Services/NotificationService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Item {
property alias server: notificationServer
property alias activeList: activeListModel
property alias historyList: historyListModel
property bool dndEnabled: false
property bool dndEnabled: SettingsService.dndEnabled
property int unreadCount: historyListModel.count
property var notificationObjects: ({
})
Expand Down
66 changes: 66 additions & 0 deletions quickshell/agility/Core/Services/SettingsService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ Item {
property string pillPopupStyle: "center"
property string pillWidgetLayout: "distributed"
property bool barAutoHide: false
property string barGlassMode: "solid"
property int barGlassOpacity: 85
property bool appletActiveWindow: true
property string appletActiveWindowAction: "maximize"
property bool appletDynamicMedia: true
property bool appletMediaVisualizer: true
property bool appletWeather: true
property string appletWeatherUnit: "C"
property bool appletBattery: true
property bool appletBatteryShowTime: true
property bool appletNetwork: false
property bool appletDnd: false
property bool dndEnabled: false
readonly property int barWidgetHeight: 28
property bool edgeLightingEnabled: true
property string edgeLightingColorMode: "album_art"
Expand Down Expand Up @@ -205,6 +218,19 @@ Item {
saveSettings();

}
onBarGlassModeChanged: { if (settingsLoaded) saveSettings(); }
onBarGlassOpacityChanged: { if (settingsLoaded) saveSettings(); }
onAppletActiveWindowChanged: { if (settingsLoaded) saveSettings(); }
onAppletActiveWindowActionChanged: { if (settingsLoaded) saveSettings(); }
onAppletDynamicMediaChanged: { if (settingsLoaded) saveSettings(); }
onAppletMediaVisualizerChanged: { if (settingsLoaded) saveSettings(); }
onAppletWeatherChanged: { if (settingsLoaded) saveSettings(); }
onAppletWeatherUnitChanged: { if (settingsLoaded) saveSettings(); }
onAppletBatteryChanged: { if (settingsLoaded) saveSettings(); }
onAppletBatteryShowTimeChanged: { if (settingsLoaded) saveSettings(); }
onAppletNetworkChanged: { if (settingsLoaded) saveSettings(); }
onAppletDndChanged: { if (settingsLoaded) saveSettings(); }
onDndEnabledChanged: { if (settingsLoaded) saveSettings(); }
onEdgeLightingEnabledChanged: { if (settingsLoaded) saveSettings(); }
onEdgeLightingColorModeChanged: { if (settingsLoaded) saveSettings(); }
onEdgeLightingCustomColorChanged: { if (settingsLoaded) saveSettings(); }
Expand Down Expand Up @@ -265,6 +291,19 @@ Item {
"pillPopupStyle": settingsService.pillPopupStyle,
"pillWidgetLayout": settingsService.pillWidgetLayout,
"barAutoHide": settingsService.barAutoHide,
"barGlassMode": settingsService.barGlassMode,
"barGlassOpacity": settingsService.barGlassOpacity,
"appletActiveWindow": settingsService.appletActiveWindow,
"appletActiveWindowAction": settingsService.appletActiveWindowAction,
"appletDynamicMedia": settingsService.appletDynamicMedia,
"appletMediaVisualizer": settingsService.appletMediaVisualizer,
"appletWeather": settingsService.appletWeather,
"appletWeatherUnit": settingsService.appletWeatherUnit,
"appletBattery": settingsService.appletBattery,
"appletBatteryShowTime": settingsService.appletBatteryShowTime,
"appletNetwork": settingsService.appletNetwork,
"appletDnd": settingsService.appletDnd,
"dndEnabled": settingsService.dndEnabled,
"actionShortcuts": settingsService.actionShortcuts,
"customShortcuts": settingsService.customShortcuts,
"edgeLightingEnabled": settingsService.edgeLightingEnabled,
Expand Down Expand Up @@ -451,6 +490,33 @@ cp ~/.cache/quickshell/settings_prefs.json /var/tmp/sddm-theme/settings_prefs.js
if (prefs.barAutoHide !== undefined)
settingsService.barAutoHide = Boolean(prefs.barAutoHide);

if (prefs.barGlassMode !== undefined)
settingsService.barGlassMode = prefs.barGlassMode;
if (prefs.barGlassOpacity !== undefined)
settingsService.barGlassOpacity = prefs.barGlassOpacity;
if (prefs.appletActiveWindow !== undefined)
settingsService.appletActiveWindow = prefs.appletActiveWindow;
if (prefs.appletActiveWindowAction !== undefined)
settingsService.appletActiveWindowAction = prefs.appletActiveWindowAction;
if (prefs.appletDynamicMedia !== undefined)
settingsService.appletDynamicMedia = prefs.appletDynamicMedia;
if (prefs.appletMediaVisualizer !== undefined)
settingsService.appletMediaVisualizer = prefs.appletMediaVisualizer;
if (prefs.appletWeather !== undefined)
settingsService.appletWeather = prefs.appletWeather;
if (prefs.appletWeatherUnit !== undefined)
settingsService.appletWeatherUnit = prefs.appletWeatherUnit;
if (prefs.appletBattery !== undefined)
settingsService.appletBattery = prefs.appletBattery;
if (prefs.appletBatteryShowTime !== undefined)
settingsService.appletBatteryShowTime = prefs.appletBatteryShowTime;
if (prefs.appletNetwork !== undefined)
settingsService.appletNetwork = prefs.appletNetwork;
if (prefs.appletDnd !== undefined)
settingsService.appletDnd = prefs.appletDnd;
if (prefs.dndEnabled !== undefined)
settingsService.dndEnabled = prefs.dndEnabled;

if (prefs.actionShortcuts !== undefined)
settingsService.actionShortcuts = prefs.actionShortcuts;

Expand Down
155 changes: 155 additions & 0 deletions quickshell/agility/Core/Services/WeatherService.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Core
import qs.Core.Services
pragma Singleton

Item {
id: weatherService

property string city: "Loading..."
property int temperatureC: 22
property int apparentTemperatureC: 22
property int humidity: 50
property int windSpeed: 5
property int weatherCode: 0
property var dailyForecast: []
property bool isFetching: false

readonly property string displayTemp: {
let isF = SettingsService.appletWeatherUnit === "F";
let val = isF ? Math.round(temperatureC * 9 / 5 + 32) : temperatureC;
return val + (isF ? "°F" : "°C");
}

readonly property string displayFeelsLike: {
let isF = SettingsService.appletWeatherUnit === "F";
let val = isF ? Math.round(apparentTemperatureC * 9 / 5 + 32) : apparentTemperatureC;
return val + (isF ? "°F" : "°C");
}

function wmoToIcon(code) {
if (code === 0)
return "sun-duotone";
if (code === 1 || code === 2)
return "cloud-sun-duotone";
if (code === 3)
return "cloud-duotone";
if (code === 45 || code === 48)
return "cloud-fog-duotone";
if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82))
return "cloud-rain-duotone";
if ((code >= 71 && code <= 77) || code === 85 || code === 86)
return "cloud-snow-duotone";
if (code >= 95)
return "cloud-lightning-duotone";
return "sun-duotone";
}

function wmoToText(code) {
if (code === 0)
return "Clear sky";
if (code === 1)
return "Mainly clear";
if (code === 2)
return "Partly cloudy";
if (code === 3)
return "Overcast";
if (code === 45 || code === 48)
return "Foggy";
if (code >= 51 && code <= 55)
return "Drizzle";
if (code >= 61 && code <= 65)
return "Rain";
if (code >= 71 && code <= 77)
return "Snow";
if (code >= 80 && code <= 82)
return "Rain showers";
if (code >= 95)
return "Thunderstorm";
return "Fair";
}

readonly property string conditionText: wmoToText(weatherCode)
readonly property string conditionIcon: wmoToIcon(weatherCode)

function formatDaily(dailyList) {
if (!dailyList || !Array.isArray(dailyList))
return [];

let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
let isF = SettingsService.appletWeatherUnit === "F";
let res = [];

for (let i = 0; i < dailyList.length; i++) {
let item = dailyList[i];
let d = new Date(item.date);
let dayName = (i === 0) ? "Today" : ((i === 1) ? "Tomorrow" : days[d.getDay()]);
let maxVal = isF ? Math.round(item.max * 9 / 5 + 32) : item.max;
let minVal = isF ? Math.round(item.min * 9 / 5 + 32) : item.min;

res.push({
"day": dayName,
"icon": wmoToIcon(item.code),
"condition": wmoToText(item.code),
"max": maxVal + "°",
"min": minVal + "°"
});
}
return res;
}

function refreshWeather(force) {
if (isFetching)
return;
isFetching = true;
let scriptPath = Quickshell.shellDir + "/Scripts/system/fetch_weather.sh";
weatherProc.command = ["bash", scriptPath, force ? "--force" : ""];
weatherProc.running = true;
}

Process {
id: weatherProc

stdout: StdioCollector {
id: weatherOutput
}

onExited: function(exitCode) {
weatherService.isFetching = false;
if (exitCode === 0 && weatherOutput.text) {
try {
let data = JSON.parse(weatherOutput.text.trim());
if (data && !data.error) {
weatherService.city = data.city || "Local";
weatherService.temperatureC = data.temperature !== undefined ? data.temperature : 22;
weatherService.apparentTemperatureC = data.apparent_temperature !== undefined ? data.apparent_temperature : 22;
weatherService.humidity = data.humidity !== undefined ? data.humidity : 50;
weatherService.windSpeed = data.wind_speed !== undefined ? data.wind_speed : 5;
weatherService.weatherCode = data.weather_code !== undefined ? data.weather_code : 0;
weatherService.dailyForecast = weatherService.formatDaily(data.daily);
}
} catch (e) {
console.error("Error parsing weather JSON: " + e);
}
}
}
}

Timer {
id: weatherPollTimer

interval: 900000 // 15 mins
repeat: true
running: SettingsService.appletWeather
triggeredOnStart: true
onTriggered: {
weatherService.refreshWeather(false);
}
}

Component.onCompleted: {
weatherService.refreshWeather(false);
}
}
Loading
Loading