mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 23:34:01 -04:00
185 lines
7.3 KiB
QML
185 lines
7.3 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import "SegmentRoles.js" as SegmentRoles
|
|
import "OverflowLayout.js" as OverflowLayout
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var barContent: null
|
|
property var surfaceContext: barContent?.surfaceContext ?? null
|
|
property var widgetsModel: barContent?.[section + "WidgetsModel"] ?? null
|
|
property var components: barContent?.allComponents ?? null
|
|
property bool noBackground: barConfig?.noBackground ?? false
|
|
required property var axis
|
|
property string section: "center"
|
|
property var parentScreen: barContent?.barWindow?.screen ?? null
|
|
property real widgetThickness: barContent?.barWindow?.widgetThickness ?? 30
|
|
property real barThickness: barContent?.barWindow?.effectiveBarThickness ?? 48
|
|
property real barSpacing: barConfig?.spacing ?? 4
|
|
property var barConfig: barContent?.barConfig ?? null
|
|
property var blurBarWindow: barContent?.blurBarWindow ?? null
|
|
property real sectionAvailablePrimarySize: 0
|
|
property bool overrideAxisLayout: false
|
|
property bool forceVerticalLayout: false
|
|
property bool edgeIsScreenEdge: true
|
|
property real crossEdgeExtension: 0
|
|
property string widgetStyle: BarMetrics.widgetStyle(barConfig)
|
|
property var roles: []
|
|
property string overflowSegmentRole: "solo"
|
|
property var entryRepeater: null
|
|
property int entryRevision: 0
|
|
readonly property alias parkingHost: parkingHost
|
|
readonly property var overflowIndices: barContent?.overflowPlan?.hidden?.[section] ?? []
|
|
readonly property string overflowDefaultMode: barConfig?.[section + "OverflowMode"] === "bar" ? "bar" : "auto"
|
|
readonly property int overflowPosition: Math.max(0, Math.min(entryRepeater?.count ?? 0, barConfig?.[section + "OverflowPosition"] ?? OverflowLayout.defaultPosition(section, entryRepeater?.count ?? 0)))
|
|
readonly property var layoutEntries: {
|
|
// Delegates incubate after count changes; entryRevision re-reads them once they land.
|
|
root.entryRevision;
|
|
const entries = [];
|
|
for (let index = 0; index < (entryRepeater?.count ?? 0); index++) {
|
|
const wrapper = entryRepeater.itemAt(index);
|
|
entries.push({
|
|
size: wrapper?.preferredSize ?? 0,
|
|
min: wrapper?.minimumSize ?? 0,
|
|
mode: wrapper?.overflowMode ?? "bar"
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
readonly property var inlineEntries: {
|
|
root.entryRevision;
|
|
const entries = [];
|
|
for (let index = 0; index < (entryRepeater?.count ?? 0); index++)
|
|
entries.push({
|
|
size: entryRepeater.itemAt(index)?.primarySize ?? 0
|
|
});
|
|
return entries;
|
|
}
|
|
readonly property var allottedSizes: barContent?.overflowPlan?.sizes?.[section] ?? []
|
|
readonly property var hiddenEntries: {
|
|
root.entryRevision;
|
|
const entries = [];
|
|
for (let index = 0; index < (entryRepeater?.count ?? 0); index++) {
|
|
const wrapper = entryRepeater.itemAt(index);
|
|
if (wrapper?.available && wrapper.inOverflow)
|
|
entries.push(wrapper);
|
|
}
|
|
return entries;
|
|
}
|
|
readonly property int overflowCount: hiddenEntries.length
|
|
readonly property SectionOverflow overflowItem: overflowLoader.item as SectionOverflow
|
|
readonly property var overflowButton: overflowItem?.button ?? null
|
|
readonly property var overflowParent: overflowItem?.popupParent ?? null
|
|
readonly property var overflowSurface: overflowItem?.popout ?? null
|
|
readonly property bool overflowOpen: overflowItem?.open ?? false
|
|
property real overflowButtonPosition: 0
|
|
property bool overflowWanted: false
|
|
readonly property real overflowTriggerSize: widgetThickness
|
|
readonly property bool overflowBackground: {
|
|
root.entryRevision;
|
|
let barePills = 0;
|
|
for (let index = 0; index < (entryRepeater?.count ?? 0); index++) {
|
|
const wrapper = entryRepeater.itemAt(index);
|
|
const item = wrapper?.available ? wrapper.widgetItem : null;
|
|
if (!item || !("widgetBackground" in item))
|
|
continue;
|
|
if (item.widgetBackground)
|
|
return true;
|
|
barePills++;
|
|
}
|
|
return barePills === 0 && !noBackground;
|
|
}
|
|
readonly property var inlineOrder: {
|
|
const layout = inlineLayout();
|
|
return layout.indices.filter((index, position) => layout.sizes[position] !== null);
|
|
}
|
|
|
|
signal layoutRequested
|
|
|
|
Connections {
|
|
target: root.entryRepeater
|
|
function onItemAdded(index, item) {
|
|
root.entryRevision++;
|
|
}
|
|
function onItemRemoved(index, item) {
|
|
root.entryRevision++;
|
|
}
|
|
}
|
|
|
|
onLayoutEntriesChanged: requestOverflowLayout()
|
|
onInlineEntriesChanged: layoutRequested()
|
|
onOverflowIndicesChanged: layoutRequested()
|
|
onOverflowPositionChanged: requestOverflowLayout()
|
|
onOverflowTriggerSizeChanged: requestOverflowLayout()
|
|
onWidgetSpacingChanged: requestOverflowLayout()
|
|
|
|
function requestOverflowLayout() {
|
|
layoutRequested();
|
|
barContent?.requestOverflowLayout?.();
|
|
}
|
|
|
|
function inlineLayout() {
|
|
return OverflowLayout.section(inlineEntries, hiddenEntries.map(entry => entry.occurrenceOrder), widgetSpacing, overflowTriggerSize, overflowPosition);
|
|
}
|
|
|
|
Item {
|
|
id: parkingHost
|
|
width: 0
|
|
height: 0
|
|
clip: true
|
|
}
|
|
|
|
Loader {
|
|
id: overflowLoader
|
|
// Unloading while the popup is open destroys its window mid-flight and leaves a stale surface.
|
|
active: root.overflowCount > 0 || root.overflowWanted
|
|
sourceComponent: Component {
|
|
SectionOverflow {
|
|
sectionContext: root
|
|
}
|
|
}
|
|
onLoaded: root.layoutRequested()
|
|
}
|
|
|
|
readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false)
|
|
readonly property bool segmented: widgetStyle === "segments" && !noBackground
|
|
readonly property real outlineThickness: (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? Theme.outlineWidth) : 0
|
|
readonly property real widgetSpacing: (segmented ? BarMetrics.segmentGap : noBackground ? BarMetrics.bareGap : BarMetrics.pillGap) + outlineThickness * 2
|
|
|
|
function refreshBlur() {
|
|
blurBarWindow?.refreshBlurRegion?.();
|
|
}
|
|
|
|
function roleAt(index) {
|
|
if (!segmented)
|
|
return "solo";
|
|
return roles[index] ?? "solo";
|
|
}
|
|
|
|
function participation(wrapper, visible, item) {
|
|
if (!wrapper || !visible || wrapper.width <= 0 || wrapper.height <= 0)
|
|
return null;
|
|
return !!item && "segmentRole" in item;
|
|
}
|
|
|
|
function applyRoles(entries, participating) {
|
|
const allEntries = entries.slice();
|
|
const allStates = participating.slice();
|
|
if (overflowCount > 0) {
|
|
allEntries.splice(overflowPosition, 0, {
|
|
widgetId: "overflow"
|
|
});
|
|
allStates.splice(overflowPosition, 0, true);
|
|
}
|
|
const next = SegmentRoles.resolve(allEntries, allStates);
|
|
const buttonRole = overflowCount > 0 ? next.splice(overflowPosition, 1)[0] : "solo";
|
|
overflowSegmentRole = segmented ? buttonRole : "solo";
|
|
if (next.join() !== roles.join())
|
|
roles = next;
|
|
}
|
|
}
|