mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-23 19:54:02 -04:00
@@ -115,6 +115,38 @@ Singleton {
|
||||
return (layout?.instances ?? []).filter(instance => instance.edge === side).reduce((sum, instance) => sum + instance.reservation, 0);
|
||||
}
|
||||
|
||||
// non-manual layouts let the compositor stack exclusive zones, so the bar that was mapped later is the tucked one
|
||||
function adjacentCover(screen, side, config, length) {
|
||||
const layout = forScreen(screen);
|
||||
const neighbour = adjacentBar(screen, side, config);
|
||||
if (!layout || !neighbour)
|
||||
return null;
|
||||
const opposite = {
|
||||
top: "bottom",
|
||||
bottom: "top",
|
||||
left: "right",
|
||||
right: "left"
|
||||
}[side];
|
||||
const near = layout.edges[side].reservation;
|
||||
const far = adjacentBar(screen, opposite, config) ? layout.edges[opposite].reservation : 0;
|
||||
const deficit = (side === "top" || side === "bottom" ? layout.screen.height : layout.screen.width) - length;
|
||||
const order = id => layout.instances.find(instance => instance.barId === id)?.configOrder ?? 0;
|
||||
let tucked;
|
||||
if (deficit >= near + far - 1)
|
||||
tucked = true;
|
||||
else if (deficit < near - 1)
|
||||
tucked = false;
|
||||
else if (Math.abs(near - far) > 1)
|
||||
tucked = Math.abs(deficit - near) <= 1;
|
||||
else
|
||||
tucked = order(neighbour.config.id) < order(config?.id);
|
||||
return {
|
||||
reach: dockAdjacentThickness(screen, side),
|
||||
wing: neighbour.wingSize ?? 0,
|
||||
tucked
|
||||
};
|
||||
}
|
||||
|
||||
function adjacentInfo(screen, config) {
|
||||
return Resolver.adjacentInfo(forScreen(screen), config, primaryBar);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
.pragma library
|
||||
|
||||
function borderPath(g) {
|
||||
const f = canonical(g);
|
||||
if (!f)
|
||||
return "";
|
||||
return serialize(g.alongWings ? alongOps(f) : crossOps(f), g.position, g.width, g.height);
|
||||
}
|
||||
|
||||
function canonical(g) {
|
||||
const body = g.body;
|
||||
const c = g.corners;
|
||||
switch (g.position) {
|
||||
case "top":
|
||||
return frame(g, body.x, body.x + body.width, body.height, c.topLeft, c.topRight, c.bottomLeft, c.bottomRight);
|
||||
case "bottom":
|
||||
return frame(g, body.x, body.x + body.width, body.height, c.bottomLeft, c.bottomRight, c.topLeft, c.topRight);
|
||||
case "left":
|
||||
return frame(g, body.y, body.y + body.height, body.width, c.topLeft, c.bottomLeft, c.topRight, c.bottomRight);
|
||||
case "right":
|
||||
return frame(g, body.y, body.y + body.height, body.width, c.topRight, c.bottomRight, c.topLeft, c.bottomLeft);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function frame(g, start, end, thickness, attachedStart, attachedEnd, farStart, farEnd) {
|
||||
if (end - start <= 0 || thickness <= 0)
|
||||
return null;
|
||||
const inset = g.inset;
|
||||
const shrink = radius => Math.max(0, radius - inset);
|
||||
const sides = g.alongWings || !g.open;
|
||||
const cover = reach => {
|
||||
if (g.alongWings)
|
||||
return 0;
|
||||
return Math.max(0, reach ?? 0);
|
||||
};
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
thickness,
|
||||
inset,
|
||||
wing: Math.max(0, g.wing),
|
||||
attachedStart: shrink(attachedStart),
|
||||
attachedEnd: shrink(attachedEnd),
|
||||
farStart: shrink(farStart),
|
||||
farEnd: shrink(farEnd),
|
||||
attachedVisible: !g.open,
|
||||
startSide: sides && !(g.seamStart ?? false),
|
||||
endSide: sides && !(g.seamEnd ?? false),
|
||||
coverStart: cover(g.coverStart),
|
||||
coverEnd: cover(g.coverEnd),
|
||||
coverStartWing: Math.max(0, g.coverStartWing ?? 0),
|
||||
coverEndWing: Math.max(0, g.coverEndWing ?? 0)
|
||||
};
|
||||
}
|
||||
|
||||
function crossOps(f) {
|
||||
const p = pen();
|
||||
const i = f.inset;
|
||||
const r = f.wing;
|
||||
const T = f.thickness;
|
||||
// the offset of a concave arc meets the offset side line 2*sqrt(r*i) short of the wing tip
|
||||
const tail = T + r - 2 * Math.sqrt(r * i);
|
||||
const farEdge = T - i;
|
||||
const endSideBottom = endSideStop(f, tail);
|
||||
const farEdgeStart = farEdgeStop(f);
|
||||
if (f.attachedVisible) {
|
||||
if (f.startSide)
|
||||
p.at(f.start + i + f.attachedStart, i);
|
||||
else
|
||||
p.at(f.start + i, i + f.attachedStart).corner(f.attachedStart, f.start + i + f.attachedStart, i);
|
||||
p.at(f.end - i - f.attachedEnd, i).corner(f.attachedEnd, f.end - i, i + f.attachedEnd);
|
||||
if (f.endSide)
|
||||
p.at(f.end - i, endSideBottom);
|
||||
else
|
||||
p.lift();
|
||||
}
|
||||
if (f.coverEnd > 0) {
|
||||
// carry the stroke onto the tucked neighbour's wing circle so both windows paint one band
|
||||
p.lift();
|
||||
if (f.coverEndWing > 0)
|
||||
p.at(f.end - f.coverEnd + f.coverEndWing + i, T + f.coverEndWing).arc(f.coverEndWing + i, 0, f.end - f.coverEnd, T - i);
|
||||
else
|
||||
p.at(f.end - f.coverEnd, T - i);
|
||||
} else if (r > 0) {
|
||||
if (!f.endSide)
|
||||
p.at(f.end + i, T + r);
|
||||
p.arc(r + i, 0, f.end - r, T - i);
|
||||
} else {
|
||||
if (!f.endSide)
|
||||
p.at(f.end - i, T - i - f.farEnd);
|
||||
p.corner(f.farEnd, f.end - i - f.farEnd, T - i);
|
||||
}
|
||||
p.at(farEdgeStart, farEdge);
|
||||
if (f.coverStart > 0) {
|
||||
if (f.coverStartWing > 0)
|
||||
p.arc(f.coverStartWing + i, 0, f.start + f.coverStart - f.coverStartWing - i, T + f.coverStartWing);
|
||||
p.lift();
|
||||
if (f.startSide)
|
||||
p.at(f.start + i, T - i);
|
||||
} else if (r > 0) {
|
||||
if (f.startSide)
|
||||
p.arc(r + i, 0, f.start + i, tail);
|
||||
else
|
||||
p.arc(r + i, 0, f.start - i, T + r).lift();
|
||||
} else {
|
||||
p.corner(f.farStart, f.start + i, T - i - f.farStart);
|
||||
if (!f.startSide)
|
||||
p.lift();
|
||||
}
|
||||
if (!f.startSide || !f.attachedVisible)
|
||||
return p.ops;
|
||||
p.at(f.start + i, i + f.attachedStart).corner(f.attachedStart, f.start + i + f.attachedStart, i).seal();
|
||||
return p.ops;
|
||||
}
|
||||
|
||||
function endSideStop(f, tail) {
|
||||
const farEdge = f.thickness - f.inset;
|
||||
if (f.coverEnd > 0)
|
||||
return farEdge;
|
||||
if (f.wing > 0)
|
||||
return tail;
|
||||
return farEdge - f.farEnd;
|
||||
}
|
||||
|
||||
function farEdgeStop(f) {
|
||||
if (f.coverStart > 0)
|
||||
return f.start + f.coverStart;
|
||||
if (f.wing > 0)
|
||||
return f.start + f.wing;
|
||||
return f.start + f.inset + f.farStart;
|
||||
}
|
||||
|
||||
function alongOps(f) {
|
||||
const p = pen();
|
||||
const i = f.inset;
|
||||
const r = f.wing;
|
||||
const T = f.thickness;
|
||||
// the offset of a wing arc meets the offset attached edge 2*sqrt(r*i) short of the wing tip
|
||||
const reach = 2 * Math.sqrt(r * i);
|
||||
const startTip = f.start - r;
|
||||
const endTip = f.end + r;
|
||||
if (r > 0) {
|
||||
if (f.attachedVisible)
|
||||
p.at(startTip + reach, i).at(endTip - reach, i);
|
||||
else
|
||||
p.at(endTip, -i);
|
||||
p.arc(r + i, 0, f.end - i, r);
|
||||
} else {
|
||||
if (f.attachedVisible)
|
||||
p.at(f.start + i + f.attachedStart, i);
|
||||
p.at(f.end - i - f.attachedEnd, i).corner(f.attachedEnd, f.end - i, i + f.attachedEnd);
|
||||
}
|
||||
p.at(f.end - i, T - i - f.farEnd).corner(f.farEnd, f.end - i - f.farEnd, T - i);
|
||||
p.at(f.start + i + f.farStart, T - i).corner(f.farStart, f.start + i, T - i - f.farStart);
|
||||
if (r > 0) {
|
||||
p.at(f.start + i, r);
|
||||
if (f.attachedVisible)
|
||||
p.arc(r + i, 0, startTip + reach, i);
|
||||
else
|
||||
p.arc(r + i, 0, startTip, -i);
|
||||
} else {
|
||||
p.at(f.start + i, i + f.attachedStart).corner(f.attachedStart, f.start + i + f.attachedStart, i);
|
||||
}
|
||||
if (f.attachedVisible)
|
||||
p.seal();
|
||||
return p.ops;
|
||||
}
|
||||
|
||||
function pen() {
|
||||
const self = {
|
||||
ops: [],
|
||||
down: false,
|
||||
lifted: false,
|
||||
at(a, c) {
|
||||
self.ops.push({
|
||||
op: self.down ? "L" : "M",
|
||||
a,
|
||||
c
|
||||
});
|
||||
self.down = true;
|
||||
return self;
|
||||
},
|
||||
arc(radius, sweep, a, c) {
|
||||
self.ops.push({
|
||||
op: "A",
|
||||
radius,
|
||||
sweep,
|
||||
a,
|
||||
c
|
||||
});
|
||||
return self;
|
||||
},
|
||||
corner(radius, a, c) {
|
||||
return radius > 0 ? self.arc(radius, 1, a, c) : self;
|
||||
},
|
||||
lift() {
|
||||
self.down = false;
|
||||
self.lifted = true;
|
||||
return self;
|
||||
},
|
||||
seal() {
|
||||
if (!self.lifted)
|
||||
self.ops.push({
|
||||
op: "Z"
|
||||
});
|
||||
self.down = false;
|
||||
return self;
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
function serialize(ops, position, width, height) {
|
||||
const map = mapper(position, width, height);
|
||||
const flipSweep = position === "bottom" || position === "left";
|
||||
return ops.map(op => {
|
||||
if (op.op === "Z")
|
||||
return "Z";
|
||||
const point = map(op.a, op.c);
|
||||
const target = `${num(point.x)} ${num(point.y)}`;
|
||||
if (op.op !== "A")
|
||||
return `${op.op} ${target}`;
|
||||
const sweep = flipSweep ? 1 - op.sweep : op.sweep;
|
||||
return `A ${num(op.radius)} ${num(op.radius)} 0 0 ${sweep} ${target}`;
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
function mapper(position, width, height) {
|
||||
switch (position) {
|
||||
case "bottom":
|
||||
return (a, c) => ({
|
||||
x: a,
|
||||
y: height - c
|
||||
});
|
||||
case "left":
|
||||
return (a, c) => ({
|
||||
x: c,
|
||||
y: a
|
||||
});
|
||||
case "right":
|
||||
return (a, c) => ({
|
||||
x: width - c,
|
||||
y: a
|
||||
});
|
||||
default:
|
||||
return (a, c) => ({
|
||||
x: a,
|
||||
y: c
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function num(value) {
|
||||
return String(Math.round(value * 100) / 100);
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules.SurfaceWidgets
|
||||
import "BarOutline.js" as BarOutline
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -21,6 +23,14 @@ Item {
|
||||
readonly property bool edgeAttached: (barConfig?.attachToScreenEdge ?? false) && !frameShapesBar
|
||||
readonly property bool alongWings: !(barWindow.spansEdge ?? true)
|
||||
readonly property real wing: gothEnabled ? barWindow._wingR : 0
|
||||
readonly property real windowLength: axis.isVertical ? barWindow.height : barWindow.width
|
||||
readonly property string startSide: axis.isVertical ? "top" : "left"
|
||||
readonly property string endSide: axis.isVertical ? "bottom" : "right"
|
||||
readonly property var startNeighbour: alongWings ? null : ShellLayout.adjacentCover(barWindow.screen, startSide, barConfig, windowLength)
|
||||
readonly property var endNeighbour: alongWings ? null : ShellLayout.adjacentCover(barWindow.screen, endSide, barConfig, windowLength)
|
||||
readonly property real alongOrigin: axis.isVertical ? 0 : barWindow.effectiveSpacing
|
||||
readonly property real startCover: coveredBy(startNeighbour)
|
||||
readonly property real endCover: coveredBy(endNeighbour)
|
||||
readonly property real alongWing: alongWings ? wing : 0
|
||||
readonly property real crossWing: alongWings ? 0 : wing
|
||||
readonly property real rt: {
|
||||
@@ -112,6 +122,12 @@ Item {
|
||||
anchors.topMargin: -(axis.isVertical ? alongWing : (isBottom ? crossWing : 0))
|
||||
anchors.bottomMargin: -(axis.isVertical ? alongWing : (isTop ? crossWing : 0))
|
||||
|
||||
function coveredBy(neighbour) {
|
||||
if (!neighbour || neighbour.tucked)
|
||||
return 0;
|
||||
return Math.max(0, neighbour.reach - alongOrigin) + neighbour.wing;
|
||||
}
|
||||
|
||||
function alongWingCorner(leading) {
|
||||
switch (barPos) {
|
||||
case SettingsData.Position.Bottom:
|
||||
@@ -202,7 +218,7 @@ Item {
|
||||
id: leadingWing
|
||||
radius: Math.max(0, motion.currentOffsetAlong)
|
||||
color: root.barWindow._bgColor
|
||||
visible: root.gothEnabled && radius > 0
|
||||
visible: root.gothEnabled && radius > 0 && root.startCover <= 0
|
||||
x: root.alongWings ? (root.axis.isVertical && root.isRight ? root.width - radius : 0) : (root.isLeft ? body.width : 0)
|
||||
y: root.alongWings ? (!root.axis.isVertical && root.isBottom ? root.height - radius : 0) : (root.isTop ? body.height : 0)
|
||||
corner: root.alongWings ? root.alongWingCorner(true) : root.isTop ? "bottomRight" : root.isBottom ? "topRight" : root.isLeft ? "bottomRight" : "bottomLeft"
|
||||
@@ -212,30 +228,62 @@ Item {
|
||||
id: trailingWing
|
||||
radius: Math.max(0, motion.currentOffsetAlong)
|
||||
color: root.barWindow._bgColor
|
||||
visible: root.gothEnabled && radius > 0
|
||||
visible: root.gothEnabled && radius > 0 && root.endCover <= 0
|
||||
x: root.axis.isVertical ? (root.alongWings ? (root.isRight ? root.width - radius : 0) : (root.isLeft ? body.width : 0)) : root.width - radius
|
||||
y: root.axis.isVertical ? root.height - radius : (root.alongWings ? (root.isBottom ? root.height - radius : 0) : (root.isTop ? body.height : 0))
|
||||
corner: root.alongWings ? root.alongWingCorner(false) : root.isTop ? "bottomLeft" : root.isBottom ? "topLeft" : root.isLeft ? "topRight" : "topLeft"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Loader {
|
||||
id: border
|
||||
readonly property real thickness: Theme.snap(Math.max(Theme.outlineWidth, barConfig?.borderThickness ?? Theme.outlineWidth), CompositorService.getScreenScale(root.barWindow.screen))
|
||||
readonly property string colorKey: barConfig?.borderColor || "surfaceText"
|
||||
readonly property color baseColor: colorKey === "surfaceText" ? Theme.surfaceText : colorKey === "primary" ? Theme.primary : Theme.secondary
|
||||
readonly property bool showFullBorder: (barConfig?.spacing ?? 4) > 0
|
||||
readonly property string path: BarOutline.borderPath({
|
||||
position: root.axis.edge,
|
||||
width: root.width,
|
||||
height: root.height,
|
||||
body: {
|
||||
x: body.x,
|
||||
y: body.y,
|
||||
width: body.width,
|
||||
height: body.height
|
||||
},
|
||||
corners: {
|
||||
topLeft: body.topLeftRadius,
|
||||
topRight: body.topRightRadius,
|
||||
bottomLeft: body.bottomLeftRadius,
|
||||
bottomRight: body.bottomRightRadius
|
||||
},
|
||||
wing: root.gothEnabled ? leadingWing.radius : 0,
|
||||
alongWings: root.alongWings,
|
||||
inset: thickness / 2,
|
||||
open: !showFullBorder,
|
||||
coverStart: root.startCover,
|
||||
coverStartWing: root.startNeighbour?.wing ?? 0,
|
||||
coverEnd: root.endCover,
|
||||
coverEndWing: root.endNeighbour?.wing ?? 0,
|
||||
seamStart: root.startNeighbour?.tucked ?? false,
|
||||
seamEnd: root.endNeighbour?.tucked ?? false
|
||||
})
|
||||
z: 100
|
||||
visible: barConfig?.borderEnabled ?? false
|
||||
x: body.x - (!showFullBorder && root.isRight ? thickness : 0)
|
||||
y: body.y - (!showFullBorder && root.isBottom ? thickness : 0)
|
||||
width: body.width + (!showFullBorder && (root.isLeft || root.isRight) ? thickness : 0)
|
||||
height: body.height + (!showFullBorder && (root.isTop || root.isBottom) ? thickness : 0)
|
||||
topLeftRadius: body.topLeftRadius
|
||||
topRightRadius: body.topRightRadius
|
||||
bottomLeftRadius: body.bottomLeftRadius
|
||||
bottomRightRadius: body.bottomRightRadius
|
||||
color: "transparent"
|
||||
border.width: thickness
|
||||
border.color: Theme.withAlpha(baseColor, barConfig?.borderOpacity ?? 1.0)
|
||||
anchors.fill: parent
|
||||
active: barConfig?.borderEnabled ?? false
|
||||
sourceComponent: Shape {
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: Theme.withAlpha(border.baseColor, barConfig?.borderOpacity ?? 1.0)
|
||||
strokeWidth: border.thickness
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
capStyle: ShapePath.FlatCap
|
||||
|
||||
PathSvg {
|
||||
path: border.path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,6 +321,8 @@ Item {
|
||||
|
||||
if (hasBar && barBackground.gothEnabled && barWindow._wingR > 0) {
|
||||
for (const wingItem of [barBackground.leadingWing, barBackground.trailingWing]) {
|
||||
if (!wingItem.visible)
|
||||
continue;
|
||||
const wing = blurWingRegionComp.createObject(region, {
|
||||
wing: wingItem
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user