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
82 changes: 82 additions & 0 deletions quickshell/agility/Core/Services/AppState.qml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Core
import qs.Core.Services
pragma Singleton
Expand All @@ -25,6 +27,9 @@ Item {
property bool hasActiveNotification: false
property real activeNotificationWidth: 0
property real activeNotificationHeight: 0
readonly property bool isNiri: Quickshell.env("NIRI_SOCKET") !== undefined || Quickshell.env("XDG_CURRENT_DESKTOP") === "niri"
property bool hasActiveWindow: false
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 Down Expand Up @@ -179,4 +184,81 @@ Item {
onOpenWidget: (widgetId) => {
openWidgetInternal(widgetId);
}

// ─── Niri IPC Engine for Window Presence Tracking ───
Process {
id: niriQueryProc

command: ["niri", "msg", "-j", "workspaces"]

stdout: SplitParser {
onRead: (data) => {
if (!data)
return;
try {
let list = JSON.parse(data.trim());
if (!Array.isArray(list))
return;

let hasWin = false;
for (let i = 0; i < list.length; i++) {
let ws = list[i];
if (ws.is_active || ws.is_focused) {
if (ws.active_window_id !== null && ws.active_window_id !== undefined)
hasWin = true;
}
}
appState.hasActiveWindow = hasWin;
} catch (e) {
}
}
}
}

Process {
id: niriEventStreamProc

command: ["niri", "msg", "event-stream"]

stdout: SplitParser {
onRead: (line) => {
if (!line)
return;
if (line.includes("Workspace") || line.includes("Window")) {
niriDebounceTimer.restart();
}
}
}
}

Timer {
id: niriDebounceTimer

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

Timer {
id: niriPollTimer

interval: 2000
repeat: true
running: appState.isNiri
triggeredOnStart: true
onTriggered: {
if (!niriQueryProc.running)
niriQueryProc.running = true;
}
}

Component.onCompleted: {
if (appState.isNiri) {
niriQueryProc.running = true;
niriEventStreamProc.running = true;
}
}
}
47 changes: 47 additions & 0 deletions quickshell/agility/Core/Services/SettingsService.qml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Item {
property bool barConvexMode: barStyle === "convex"
property bool barPillMode: barStyle === "pill"
property string islandRightMode: "auto"
property int pillBarWidthPercent: 60
property string pillPopupStyle: "center"
property string pillWidgetLayout: "distributed"
property bool barAutoHide: false
readonly property int barWidgetHeight: 28
property bool edgeLightingEnabled: true
property string edgeLightingColorMode: "album_art"
Expand Down Expand Up @@ -180,6 +184,26 @@ Item {
if (settingsLoaded)
saveSettings();

}
onPillBarWidthPercentChanged: {
if (settingsLoaded)
saveSettings();

}
onPillPopupStyleChanged: {
if (settingsLoaded)
saveSettings();

}
onPillWidgetLayoutChanged: {
if (settingsLoaded)
saveSettings();

}
onBarAutoHideChanged: {
if (settingsLoaded)
saveSettings();

}
onEdgeLightingEnabledChanged: { if (settingsLoaded) saveSettings(); }
onEdgeLightingColorModeChanged: { if (settingsLoaded) saveSettings(); }
Expand Down Expand Up @@ -237,6 +261,10 @@ Item {
"cursorSize": settingsService.cursorSize,
"barStyle": settingsService.barStyle,
"islandRightMode": settingsService.islandRightMode,
"pillBarWidthPercent": settingsService.pillBarWidthPercent,
"pillPopupStyle": settingsService.pillPopupStyle,
"pillWidgetLayout": settingsService.pillWidgetLayout,
"barAutoHide": settingsService.barAutoHide,
"actionShortcuts": settingsService.actionShortcuts,
"customShortcuts": settingsService.customShortcuts,
"edgeLightingEnabled": settingsService.edgeLightingEnabled,
Expand Down Expand Up @@ -404,6 +432,25 @@ cp ~/.cache/quickshell/settings_prefs.json /var/tmp/sddm-theme/settings_prefs.js
if (prefs.islandRightMode !== undefined)
settingsService.islandRightMode = prefs.islandRightMode;

if (prefs.pillBarWidthPercent !== undefined) {
let pbw = parseInt(prefs.pillBarWidthPercent);
if (!isNaN(pbw) && pbw >= 30 && pbw <= 100)
settingsService.pillBarWidthPercent = pbw;
}

if (prefs.pillPopupStyle !== undefined) {
if (prefs.pillPopupStyle === "sided" || prefs.pillPopupStyle === "top" || prefs.pillPopupStyle === "center")
settingsService.pillPopupStyle = prefs.pillPopupStyle;
}

if (prefs.pillWidgetLayout !== undefined) {
if (prefs.pillWidgetLayout === "distributed" || prefs.pillWidgetLayout === "centered")
settingsService.pillWidgetLayout = prefs.pillWidgetLayout;
}

if (prefs.barAutoHide !== undefined)
settingsService.barAutoHide = Boolean(prefs.barAutoHide);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,27 @@ Item {
AppState.isIslandOpen = true;
let nw = (AppState.hasActiveNotification && AppState.activeNotificationWidth > 0) ? AppState.activeNotificationWidth : 0;
let nh = (AppState.hasActiveNotification && AppState.activeNotificationHeight > 0) ? AppState.activeNotificationHeight : 0;
mainContainer.fromWidth = nw > 0 ? nw : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : mainContainer.fromWidth));
mainContainer.fromHeight = nh > 0 ? nh : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : mainContainer.fromHeight));
let pillW = (SettingsService.barPillMode && root.width > 0) ? Math.round(root.width * (SettingsService.pillBarWidthPercent / 100)) : 0;
let baseW = pillW > 0 ? pillW : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : mainContainer.fromWidth));
let baseH = SettingsService.barPillMode ? 38 : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : mainContainer.fromHeight));
mainContainer.fromWidth = nw > 0 ? nw : baseW;
mainContainer.fromHeight = nh > 0 ? nh : baseH;
mainContainer.fromRadius = nh > 0 ? Constants.sizeSm : mainContainer.fromHeight / 2;
} else {
let nw = (AppState.hasActiveNotification && AppState.activeNotificationWidth > 0) ? AppState.activeNotificationWidth : 0;
let nh = (AppState.hasActiveNotification && AppState.activeNotificationHeight > 0) ? AppState.activeNotificationHeight : 0;
mainContainer.fromWidth = nw > 0 ? nw : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : mainContainer.fromWidth));
mainContainer.fromHeight = nh > 0 ? nh : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : mainContainer.fromHeight));
let pillW = (SettingsService.barPillMode && root.width > 0) ? Math.round(root.width * (SettingsService.pillBarWidthPercent / 100)) : 0;
let baseW = pillW > 0 ? pillW : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : mainContainer.fromWidth));
let baseH = SettingsService.barPillMode ? 38 : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : mainContainer.fromHeight));
mainContainer.fromWidth = nw > 0 ? nw : baseW;
mainContainer.fromHeight = nh > 0 ? nh : baseH;
mainContainer.fromRadius = nh > 0 ? Constants.sizeSm : mainContainer.fromHeight / 2;
}
}

Connections {
function onFullyClosed() {
if (SettingsService.barIslandMode && !widget.positionAtBottom) {
if ((SettingsService.barIslandMode || (SettingsService.barPillMode && SettingsService.pillPopupStyle === "top")) && !widget.positionAtBottom) {
isHandover = true;
AppState.isIslandOpen = false;
if (AppState.activeWidget !== "")
Expand Down Expand Up @@ -73,8 +79,8 @@ Item {

readonly property real notifW: (AppState.hasActiveNotification && AppState.activeNotificationWidth > 0) ? AppState.activeNotificationWidth : 0
readonly property real notifH: (AppState.hasActiveNotification && AppState.activeNotificationHeight > 0) ? AppState.activeNotificationHeight : 0
property real fromWidth: notifW > 0 ? notifW : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : 180))
property real fromHeight: notifH > 0 ? notifH : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : Constants.size3Xl))
property real fromWidth: notifW > 0 ? notifW : (SettingsService.barPillMode && root.width > 0 ? Math.round(root.width * (SettingsService.pillBarWidthPercent / 100)) : ((AppState.barIslandWidth > 0) ? AppState.barIslandWidth : ((AppState.islandWidth > 0) ? AppState.islandWidth : 180)))
property real fromHeight: notifH > 0 ? notifH : (SettingsService.barPillMode ? 38 : ((AppState.barIslandHeight > 0) ? AppState.barIslandHeight : ((AppState.islandHeight > 0) ? AppState.islandHeight : Constants.size3Xl)))
property real fromRadius: notifH > 0 ? Constants.sizeXl : fromHeight / 2
readonly property real openY: 8
readonly property real currentWidth: Math.round(fromWidth + (preferredWidth - fromWidth) * bounceProgress)
Expand All @@ -91,7 +97,7 @@ Item {
IslandStyle.IslandBackground {
anchors.fill: parent
radius: mainContainer.currentRadius
enableShadow: SettingsService.barIslandMode && !isHandover
enableShadow: (SettingsService.barIslandMode || SettingsService.barPillMode) && !isHandover
}

Item {
Expand Down
2 changes: 1 addition & 1 deletion quickshell/agility/Core/Windows/OverlayWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ PanelWindow {
property bool windowFocusable: true
// Determine the active style mode cleanly
readonly property string activeStyle: {
if (SettingsService.barIslandMode && !positionAtBottom)
if ((SettingsService.barIslandMode || (SettingsService.barPillMode && SettingsService.pillPopupStyle === "top")) && !positionAtBottom)
return "island";

return "floating";
Expand Down
2 changes: 1 addition & 1 deletion quickshell/agility/Core/Windows/SidebarWindow.qml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Item {
property int flareHeight: Constants.size2Xl + 4
property int flareWidth: Constants.size2Xl + 4
property bool alignTop: false
property int topOffset: (SettingsService.barStyle === "convex" ? 48 : 24)
property int topOffset: (SettingsService.barStyle === "convex" || (SettingsService.barPillMode && SettingsService.pillPopupStyle === "sided") ? 48 : 24)
readonly property int verticalPadding: flareHeight + contentPadding
default property alias content: innerLayout.data
property int _screenHeight: 1080
Expand Down
3 changes: 2 additions & 1 deletion quickshell/agility/Core/Windows/WidgetLoader.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Item {
property bool _isInternalActive: false
readonly property bool isBarOverlayHandled: AppState.isBarOverlayWidget(widgetId)
readonly property bool isIslandHandled: !DisplayProfileService.gameModeActive && SettingsService.barIslandMode && isBarOverlayHandled
readonly property bool isConvexHandled: !DisplayProfileService.gameModeActive && SettingsService.barConvexMode && isBarOverlayHandled
readonly property bool isSidedPillHandled: SettingsService.barPillMode && SettingsService.pillPopupStyle === "sided" && (widgetId !== "music") && isBarOverlayHandled
readonly property bool isConvexHandled: !DisplayProfileService.gameModeActive && ((SettingsService.barConvexMode && isBarOverlayHandled) || isSidedPillHandled)
readonly property bool isGamingHandled: DisplayProfileService.gameModeActive && isBarOverlayHandled
readonly property bool shouldLoadWindow: enabled && !isIslandHandled && !isConvexHandled && !isGamingHandled
readonly property bool _isActive: shouldLoadWindow && (exclusive ? AppState.isWidgetOpen(widgetId) : _isInternalActive)
Expand Down
Loading
Loading