mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-27 05:25:19 -04:00
48f6a0c632
* feat(bar): separate workspace appearance for unfocused displays Add a toggle in the Workspace Appearance card to style workspace indicators independently on displays that aren't currently focused. The card is split into Focused Display and Unfocused Display(s) tabs. When the new master toggle is off (default), unfocused displays inherit the focused settings, preserving existing behavior. When on, unfocused displays use their own colors (focused/occupied/unfocused/urgent) and focused border (enable, color, thickness). WorkspaceSwitcher resolves the focused monitor per compositor and routes color/border resolution through isFocusedMonitor/useUnfocusedAppearance. * refactor(bar): address review feedback for unfocused workspace appearance - Consolidate focused-monitor lookup into BarWidgetService.getFocusedScreenName(), adding Mango support, and drop the duplicate compositor switches in WorkspaceSwitcher (effectiveScreenName + isFocusedMonitor now use the helper). - Extract the shared color and border options into reusable WorkspaceAppearanceColorOptions and WorkspaceAppearanceBorderFields components so the focused and unfocused tabs no longer duplicate the layout. - Gate the unfocused-display options on BarWidgetService.focusedScreenDetectionSupported and show an explanatory note on compositors without focus detection (e.g. labwc), instead of silently no-opping.
185 lines
6.4 KiB
QML
185 lines
6.4 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Hyprland
|
|
import Quickshell.I3
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
property var widgetRegistry: ({})
|
|
property var dankBarRepeater: null
|
|
|
|
signal widgetRegistered(string widgetId, string screenName)
|
|
signal widgetUnregistered(string widgetId, string screenName)
|
|
|
|
function registerWidget(widgetId, screenName, widgetRef) {
|
|
if (!widgetId || !screenName || !widgetRef)
|
|
return;
|
|
|
|
const nextRegistry = (typeof widgetRegistry === "object" && widgetRegistry !== null) ? Object.assign({}, widgetRegistry) : {};
|
|
const screenMap = (typeof nextRegistry[widgetId] === "object" && nextRegistry[widgetId] !== null) ? Object.assign({}, nextRegistry[widgetId]) : {};
|
|
screenMap[screenName] = widgetRef;
|
|
nextRegistry[widgetId] = screenMap;
|
|
widgetRegistry = nextRegistry;
|
|
widgetRegistered(widgetId, screenName);
|
|
}
|
|
|
|
function unregisterWidget(widgetId, screenName) {
|
|
if (!widgetId || !screenName)
|
|
return;
|
|
if (typeof widgetRegistry !== "object" || widgetRegistry === null)
|
|
return;
|
|
if (!widgetRegistry[widgetId])
|
|
return;
|
|
|
|
const nextRegistry = Object.assign({}, widgetRegistry);
|
|
const screenMap = (typeof nextRegistry[widgetId] === "object" && nextRegistry[widgetId] !== null) ? Object.assign({}, nextRegistry[widgetId]) : {};
|
|
delete screenMap[screenName];
|
|
if (Object.keys(screenMap).length === 0) {
|
|
delete nextRegistry[widgetId];
|
|
} else {
|
|
nextRegistry[widgetId] = screenMap;
|
|
}
|
|
widgetRegistry = nextRegistry;
|
|
|
|
widgetUnregistered(widgetId, screenName);
|
|
}
|
|
|
|
function getWidget(widgetId, screenName) {
|
|
if (typeof widgetRegistry !== "object" || widgetRegistry === null || !widgetRegistry[widgetId])
|
|
return null;
|
|
if (screenName)
|
|
return widgetRegistry[widgetId][screenName] || null;
|
|
|
|
const screens = Object.keys(widgetRegistry[widgetId]);
|
|
return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null;
|
|
}
|
|
|
|
function getWidgetOnFocusedScreen(widgetId) {
|
|
if (typeof widgetRegistry !== "object" || widgetRegistry === null || !widgetRegistry[widgetId])
|
|
return null;
|
|
|
|
const focusedScreen = getFocusedScreenName();
|
|
if (focusedScreen && widgetRegistry[widgetId][focusedScreen])
|
|
return widgetRegistry[widgetId][focusedScreen];
|
|
|
|
const screens = Object.keys(widgetRegistry[widgetId]);
|
|
return screens.length > 0 ? widgetRegistry[widgetId][screens[0]] : null;
|
|
}
|
|
|
|
readonly property bool focusedScreenDetectionSupported: CompositorService.isHyprland || CompositorService.isNiri || CompositorService.isMango || CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle
|
|
|
|
function getFocusedScreenName() {
|
|
if (CompositorService.isHyprland && Hyprland.focusedWorkspace?.monitor)
|
|
return Hyprland.focusedWorkspace.monitor.name;
|
|
if (CompositorService.isNiri && NiriService.currentOutput)
|
|
return NiriService.currentOutput;
|
|
if (CompositorService.isMango && MangoService.activeOutput)
|
|
return MangoService.activeOutput;
|
|
if (CompositorService.isSway || CompositorService.isScroll || CompositorService.isMiracle) {
|
|
const focusedWs = I3.workspaces?.values?.find(ws => ws.focused === true);
|
|
return focusedWs?.monitor?.name || "";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function getRegisteredWidgetIds() {
|
|
if (typeof widgetRegistry !== "object" || widgetRegistry === null)
|
|
return [];
|
|
return Object.keys(widgetRegistry);
|
|
}
|
|
|
|
function hasWidget(widgetId) {
|
|
if (typeof widgetRegistry !== "object" || widgetRegistry === null)
|
|
return false;
|
|
return widgetRegistry[widgetId] && Object.keys(widgetRegistry[widgetId]).length > 0;
|
|
}
|
|
|
|
function triggerWidgetPopout(widgetId) {
|
|
const widget = getWidgetOnFocusedScreen(widgetId);
|
|
if (!widget)
|
|
return false;
|
|
|
|
if (typeof widget.triggerPopout === "function") {
|
|
widget.triggerPopout();
|
|
return true;
|
|
}
|
|
|
|
const signalMap = {
|
|
"battery": "toggleBatteryPopup",
|
|
"vpn": "toggleVpnPopup",
|
|
"layout": "toggleLayoutPopup",
|
|
"clock": "clockClicked",
|
|
"cpuUsage": "cpuClicked",
|
|
"memUsage": "ramClicked",
|
|
"cpuTemp": "cpuTempClicked",
|
|
"gpuTemp": "gpuTempClicked"
|
|
};
|
|
|
|
const signalName = signalMap[widgetId];
|
|
if (signalName && typeof widget[signalName] === "function") {
|
|
widget[signalName]();
|
|
return true;
|
|
}
|
|
|
|
if (typeof widget.clicked === "function") {
|
|
widget.clicked();
|
|
return true;
|
|
}
|
|
|
|
if (widget.popoutTarget?.toggle) {
|
|
widget.popoutTarget.toggle();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function getBarWindowForScreen(screenName) {
|
|
if (!dankBarRepeater)
|
|
return null;
|
|
|
|
for (var i = 0; i < dankBarRepeater.count; i++) {
|
|
const loader = dankBarRepeater.itemAt(i);
|
|
if (!loader?.item)
|
|
continue;
|
|
|
|
const barItem = loader.item;
|
|
if (!barItem.barVariants?.instances)
|
|
continue;
|
|
|
|
for (var j = 0; j < barItem.barVariants.instances.length; j++) {
|
|
const barInstance = barItem.barVariants.instances[j];
|
|
if (barInstance.modelData?.name === screenName)
|
|
return barInstance;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function getBarWindowOnFocusedScreen() {
|
|
const focusedScreen = getFocusedScreenName();
|
|
if (!focusedScreen)
|
|
return getFirstBarWindow();
|
|
return getBarWindowForScreen(focusedScreen) || getFirstBarWindow();
|
|
}
|
|
|
|
function getFirstBarWindow() {
|
|
if (!dankBarRepeater || dankBarRepeater.count === 0)
|
|
return null;
|
|
|
|
const loader = dankBarRepeater.itemAt(0);
|
|
if (!loader?.item)
|
|
return null;
|
|
|
|
const barItem = loader.item;
|
|
if (!barItem.barVariants?.instances || barItem.barVariants.instances.length === 0)
|
|
return null;
|
|
|
|
return barItem.barVariants.instances[0];
|
|
}
|
|
}
|