Files
DankMaterialShell/quickshell/Common/ShellLayout.qml
T

162 lines
6.8 KiB
QML

pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Services
import "LayoutResolver.js" as Resolver
Singleton {
id: root
readonly property var screens: Quickshell.screens.map(screen => ({
name: screen.name,
model: screen.model,
x: screen.x,
y: screen.y,
width: screen.width,
height: screen.height,
scale: CompositorService.getScreenScale(screen)
}))
readonly property var primaryBar: SettingsData.getPrimaryBarConfig()
readonly property var layouts: screens.map(screen => Resolver.resolveScreen(SettingsData.barConfigs.map(config => ({
config,
barThickness: Theme.barThickness(config.innerPadding ?? 4, screen.scale),
wingSize: config.gothCornersEnabled && root.barSpansEdge(config) ? Math.max(0, config.gothCornerRadiusOverride ? config.gothCornerRadiusValue ?? 12 : Theme.windowRadius) : 0,
popupThickness: Theme.barThickness(Resolver.option(config, "innerPadding", root.primaryBar, 4), screen.scale),
islandThickness: Resolver.islandThickness(config, SettingsData.islandDefaults),
islandFloating: SettingsData.islandSetting(config, "islandFloating")
})), screen, {
screens: root.screens,
displayNameMode: SettingsData.displayNameMode,
framePreferences: SettingsData.frameScreenPreferences,
effectiveFrameEnabled: FrameTransitionState.effectiveFrameEnabled,
effectiveConnected: FrameTransitionState.effectiveConnectedFrameModeActive,
frameThickness: SettingsData.frameThickness,
frameBarSize: SettingsData.frameBarSize
}))
readonly property var islandKeys: layouts.reduce((keys, layout) => keys.concat(layout.instances.filter(instance => instance.kind === "island").map(instance => instance.key)), [])
function barSpansEdge(config) {
return (config?.barLengthMode ?? "full") === "full" && (config?.barLengthPadding ?? 0) <= 0;
}
function forScreen(screen) {
const name = typeof screen === "string" ? screen : screen?.name;
return layouts.find(layout => layout.screen.name === name) ?? null;
}
function screenForName(name) {
return Quickshell.screens.find(screen => screen.name === name) ?? null;
}
function instance(key) {
const [screenName, barId] = JSON.parse(key);
return forScreen(screenName)?.instances.find(instance => instance.barId === barId) ?? null;
}
function forConfig(screen, barId) {
return forScreen(screen)?.instances.find(instance => instance.barId === barId) ?? null;
}
function edge(screen, side) {
return forScreen(screen)?.edges[side] ?? null;
}
function assignedScreens(config) {
return Quickshell.screens.filter(screen => coversScreen(config, screen));
}
function coversScreen(config, screen) {
return Resolver.coversScreen(config, screen, screens, SettingsData.displayNameMode);
}
function standaloneScreens(barId) {
return Quickshell.screens.filter(screen => forScreen(screen)?.instances.some(instance => instance.barId === barId && instance.kind === "bar"));
}
function hostedScreens(barId) {
return Quickshell.screens.filter(screen => forScreen(screen)?.instances.some(instance => instance.barId === barId && (instance.kind === "bar" || instance.kind === "island")));
}
function frameKeys(screen) {
const instances = forScreen(screen)?.instances ?? [];
const order = ["left", "right", "top", "bottom"];
return instances.filter(instance => instance.kind === "frame").sort((a, b) => order.indexOf(a.edge) - order.indexOf(b.edge)).map(instance => instance.key);
}
function barEdges(screen, overlayOnly) {
return Resolver.edges.filter(side => edge(screen, side)?.bars.some(config => !overlayOnly || config.useOverlayLayer));
}
function islandConfigs(screen) {
return forScreen(screen)?.islands.map(input => input.config) ?? [];
}
function frameReservation(screen, side) {
return edge(screen, side)?.frameReservation ?? 0;
}
function frameContentInset(screen, barId, side) {
const layout = forScreen(screen);
const band = layout?.edges[side];
if (!band?.occupancy)
return null;
const instance = forConfig(screen, barId);
return Math.max(0, band.occupancy - (instance?.kind === "frame" ? 0 : instance?.margins[side] ?? 0));
}
function adjacentBar(screen, side, config) {
return Resolver.adjacentBar(forScreen(screen), side, config);
}
function dockAdjacentThickness(screen, side) {
const layout = forScreen(screen);
return (layout?.instances ?? []).filter(instance => instance.edge === side).reduce((sum, instance) => sum + instance.reservation, 0);
}
function adjacentInfo(screen, config) {
return Resolver.adjacentInfo(forScreen(screen), config, primaryBar);
}
function barBounds(screen, thickness, position, config) {
const appearance = config ?? primaryBar;
const radius = (appearance?.gothCornerRadiusOverride ?? false) ? (appearance?.gothCornerRadiusValue ?? 12) : Theme.windowRadius;
const wing = (appearance?.gothCornersEnabled ?? false) && barSpansEdge(appearance) ? Math.max(0, radius) : 0;
const edge = position === undefined ? (primaryBar?.position ?? 0) : position;
return Resolver.barBounds(forScreen(screen), thickness, edge, config, primaryBar, SettingsData.connectedFrameModeActive, wing);
}
function surfaceOrigin(screen, width, height, anchors, margins) {
return Resolver.surfaceOrigin(forScreen(screen) ?? {
screen
}, width, height, anchors, margins);
}
function popupTrigger(pos, screen, thickness, width, spacing, position, config) {
const edge = position === undefined ? (primaryBar?.position ?? 0) : position;
const gap = spacing === undefined ? (primaryBar?.spacing ?? 4) : spacing;
const trigger = Resolver.popupTrigger(pos, screen, thickness, width, gap, edge, config, primaryBar, SettingsData.connectedFrameModeActive);
const offset = forScreen(screen)?.instances.find(instance => instance.barId === config?.id)?.rowOffset ?? 0;
switch (edge) {
case 0:
trigger.y += offset;
break;
case 1:
trigger.y -= offset;
break;
case 2:
trigger.x += offset;
break;
case 3:
trigger.x -= offset;
break;
}
return trigger;
}
function positionChoices(config) {
return Resolver.availablePositions(layouts.filter(layout => coversScreen(config, layout.screen)), config);
}
}