feat: add ability to specify connector as layout name and extend other layouts (#985)

* feat: add ability to specify connector as layout name and extend other layouts

Signed-off-by: davfsa <davfsa@gmail.com>

* chore: rename function

Signed-off-by: davfsa <davfsa@gmail.com>

* Update src/lib/options/types.ts

Co-authored-by: Jas Singh <jaskiratpal.singh@outlook.com>

* Update src/components/bar/utils/monitors/index.ts

Co-authored-by: Jas Singh <jaskiratpal.singh@outlook.com>

* Update src/components/bar/utils/monitors/index.ts

---------

Signed-off-by: davfsa <davfsa@gmail.com>
Co-authored-by: Jas Singh <jaskiratpal.singh@outlook.com>
This commit is contained in:
davfsa
2025-06-16 01:00:51 +02:00
committed by GitHub
parent daf45665c4
commit e03666ab5d
4 changed files with 78 additions and 16 deletions

View File

@@ -3,6 +3,13 @@ import { BarLayout, BarLayouts } from 'src/lib/options/types';
import { GdkMonitorService } from 'src/services/display/monitor'; import { GdkMonitorService } from 'src/services/display/monitor';
import { MonitorMapping } from './types'; import { MonitorMapping } from './types';
import { JSXElement } from 'src/core/types'; import { JSXElement } from 'src/core/types';
import AstalHyprland from 'gi://AstalHyprland?version=0.1';
const emptyBar = {
left: [],
middle: [],
right: [],
};
/** /**
* Returns the bar layout configuration for a specific monitor * Returns the bar layout configuration for a specific monitor
@@ -12,24 +19,80 @@ import { JSXElement } from 'src/core/types';
* @returns BarLayout configuration for the specified monitor, falling back to default if not found * @returns BarLayout configuration for the specified monitor, falling back to default if not found
*/ */
export const getLayoutForMonitor = (monitor: number, layouts: BarLayouts): BarLayout => { export const getLayoutForMonitor = (monitor: number, layouts: BarLayouts): BarLayout => {
const matchingKey = Object.keys(layouts).find((key) => key === monitor.toString()); const [rootKey, rootLayout] = _getResolveLayoutForMonitor(monitor, layouts);
const wildcard = Object.keys(layouts).find((key) => key === '*');
if (matchingKey !== undefined) { let left = rootLayout.left;
return layouts[matchingKey]; let middle = rootLayout.middle;
} let right = rootLayout.right;
if (wildcard) { let layout = rootLayout;
return layouts[wildcard]; const visited = [rootKey];
while (
layout.extends !== undefined &&
(left === undefined || middle === undefined || right === undefined)
) {
if (visited.includes(layout.extends)) {
console.error(`found circular reference in layout extensions: ${visited.join(' -> ')}`);
return emptyBar;
}
visited.push(layout.extends);
if (!(layout.extends in layouts)) {
console.error(
`failed to find layout with name '${layout.extends}' (resolved path: ${visited.join(' -> ')})`,
);
return emptyBar;
}
layout = layouts[layout.extends];
if (left === undefined) {
left = layout.left;
}
if (middle === undefined) {
middle = layout.middle;
}
if (right === undefined) {
right = layout.right;
}
} }
return { return {
left: ['dashboard', 'workspaces', 'windowtitle'], left: left ?? [],
middle: ['media'], middle: middle ?? [],
right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'], right: right ?? [],
}; };
}; };
const _getResolveLayoutForMonitor = (monitor: number, layouts: BarLayouts): [string, BarLayout] => {
const hyprlandService = AstalHyprland.get_default();
const monitorConn = hyprlandService.get_monitor(monitor).get_name();
const matchingConn = Object.keys(layouts).find((key) => key === monitorConn);
if (matchingConn !== undefined) {
return [matchingConn, layouts[matchingConn]];
}
const matchingNum = Object.keys(layouts).find((key) => key === monitor.toString());
if (matchingNum !== undefined) {
return [matchingNum, layouts[matchingNum]];
}
const wildcard = Object.keys(layouts).find((key) => key === '*');
if (wildcard) {
return [wildcard, layouts[wildcard]];
}
return [
'default',
{
left: ['dashboard', 'workspaces', 'windowtitle'],
middle: ['media'],
right: ['volume', 'network', 'bluetooth', 'battery', 'systray', 'clock', 'notifications'],
},
];
};
/** /**
* Checks if a bar layout configuration is empty * Checks if a bar layout configuration is empty
* *

View File

@@ -16,9 +16,9 @@ export function getLayoutItems(): BarModule[] {
const itemsInLayout: BarModule[] = []; const itemsInLayout: BarModule[] = [];
Object.keys(layouts.get()).forEach((monitor) => { Object.keys(layouts.get()).forEach((monitor) => {
const leftItems = layouts.get()[monitor].left; const leftItems = layouts.get()[monitor].left ?? [];
const rightItems = layouts.get()[monitor].right; const rightItems = layouts.get()[monitor].right ?? [];
const middleItems = layouts.get()[monitor].middle; const middleItems = layouts.get()[monitor].middle ?? [];
itemsInLayout.push(...leftItems); itemsInLayout.push(...leftItems);
itemsInLayout.push(...middleItems); itemsInLayout.push(...middleItems);

View File

@@ -46,6 +46,7 @@ export type BarLayout = {
left: BarModule[]; left: BarModule[];
middle: BarModule[]; middle: BarModule[];
right: BarModule[]; right: BarModule[];
extends?: string;
}; };
export type BarLayouts = { export type BarLayouts = {
[key: string]: BarLayout; [key: string]: BarLayout;

View File

@@ -60,7 +60,7 @@ export class GdkMonitorService {
const tempUsedIds = new Set<number>(); const tempUsedIds = new Set<number>();
const monitorsToUse = validMonitors.length > 0 ? validMonitors : hyprlandMonitors; const monitorsToUse = validMonitors.length > 0 ? validMonitors : hyprlandMonitors;
const result = this._matchMonitor( return this._matchMonitor(
monitorsToUse, monitorsToUse,
gdkMonitor, gdkMonitor,
monitor, monitor,
@@ -68,8 +68,6 @@ export class GdkMonitorService {
(mon, gdkMon) => this._matchMonitorKey(mon, gdkMon), (mon, gdkMon) => this._matchMonitorKey(mon, gdkMon),
tempUsedIds, tempUsedIds,
); );
return result;
} }
/** /**