295 lines
7.1 KiB
QML
295 lines
7.1 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import Quickshell
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import ZShell.Config
|
|
import ZShell.Components
|
|
import qs.Components
|
|
import qs.Modules.Bar.Components
|
|
import qs.Modules.Bar.Popouts
|
|
|
|
SnapLayout {
|
|
id: root
|
|
|
|
required property bool fullscreen
|
|
required property bool horizontal
|
|
required property bool isHovered
|
|
required property Wrapper popouts
|
|
required property ClipWrapper popoutsWrapper
|
|
required property ShellScreen screen
|
|
required property ScreenState screenState
|
|
readonly property int vPadding: 6
|
|
|
|
function axisCenterOf(item: Item): real {
|
|
const c = item.mapToItem(root, item.implicitWidth / 2, item.implicitHeight / 2);
|
|
return horizontal ? Math.floor(c.x) : Math.floor(c.y);
|
|
}
|
|
|
|
function checkPopout(pos: real): void {
|
|
const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper;
|
|
const id = ch?.entryId;
|
|
|
|
if (!ch || id === undefined) {
|
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover)
|
|
popouts.hasCurrent = false;
|
|
return;
|
|
}
|
|
|
|
if (screenState.sidebar || screenState.dashboard || screenState.resources || screenState.settings)
|
|
return;
|
|
|
|
if (id === "statusIcons" && Config.bar.popouts.statusIcons) {
|
|
const items = (ch.item as StatusIcons).items;
|
|
const icon = horizontal ? items.childAt(mapToItem(items, pos, 0).x, items.height / 2) : items.childAt(items.width / 2, mapToItem(items, 0, pos).y);
|
|
if (icon) {
|
|
popouts.currentName = icon.name;
|
|
popouts.currentCenter = Qt.binding(() => axisCenterOf(icon));
|
|
popouts.hasCurrent = true;
|
|
}
|
|
} else if (id === "tray" && Config.bar.popouts.tray && Config.bar.tray.showOnHover) {
|
|
const tray = ch.item as TrayIcons;
|
|
const layout = tray.layout;
|
|
const trayItem = horizontal ? layout.childAt(mapToItem(layout, pos, 0).x, layout.height / 2) : layout.childAt(tray.width / 2, mapToItem(layout, 0, pos).y);
|
|
|
|
if (trayItem) {
|
|
const idx = trayItem.index;
|
|
popouts.currentName = `traymenu${idx}`;
|
|
popouts.currentCenter = Qt.binding(() => axisCenterOf(trayItem));
|
|
popouts.hasCurrent = true;
|
|
}
|
|
|
|
if (!popouts.currentName.startsWith("traymenu"))
|
|
popouts.hasCurrent = false;
|
|
}
|
|
|
|
if (id === "updates") {
|
|
popouts.currentName = "updates";
|
|
popouts.currentCenter = Qt.binding(() => axisCenterOf(ch.item as Item));
|
|
popouts.hasCurrent = true;
|
|
}
|
|
}
|
|
|
|
function entryAt(pos: real): string {
|
|
const ch = (horizontal ? childAt(pos, height / 2) : childAt(width / 2, pos)) as EntryWrapper;
|
|
return ch?.entryId ?? "";
|
|
}
|
|
|
|
readonly property var sectionRects: {
|
|
const count = repeater.count; // dependency: re-run when items are added/removed
|
|
const names = ["start", "middle", "end"]; // constant ids by position
|
|
const bounds = [null];
|
|
let spacers = 0;
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const it = repeater.itemAt(i) as EntryWrapper;
|
|
if (!it)
|
|
continue;
|
|
|
|
if (it.entryId === "spacer") {
|
|
bounds.push(null);
|
|
spacers++;
|
|
continue;
|
|
}
|
|
|
|
const l = it.x, t = it.y, r = it.x + it.width, b = it.y + it.height;
|
|
const cur = bounds[spacers];
|
|
|
|
bounds[spacers] = cur ? {
|
|
l: Math.min(cur.l, l),
|
|
t: Math.min(cur.t, t),
|
|
r: Math.max(cur.r, r),
|
|
b: Math.max(cur.b, b)
|
|
} : {
|
|
l,
|
|
t,
|
|
r,
|
|
b
|
|
};
|
|
}
|
|
|
|
return bounds.map((c, n) => ({
|
|
id: names[n] ?? `section${n}`,
|
|
rect: c ? Qt.rect(c.l, c.t, c.r - c.l, c.b - c.t) : Qt.rect(0, 0, 0, 0)
|
|
}));
|
|
}
|
|
|
|
spacing: Tokens.spacing.small
|
|
orientation: horizontal ? Qt.Horizontal : Qt.Vertical
|
|
|
|
Repeater {
|
|
id: repeater
|
|
|
|
model: ScriptModel {
|
|
values: Config.bar.entries.values.filter(e => e.enabled)
|
|
}
|
|
|
|
DelegateChooser {
|
|
role: "id"
|
|
|
|
DelegateChoice {
|
|
roleValue: "nightlight"
|
|
|
|
delegate: EntryWrapper {
|
|
NightlightWidget {
|
|
horizontal: root.horizontal
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "spacer"
|
|
|
|
delegate: EntryWrapper {
|
|
Layout.fillHeight: !root.horizontal
|
|
Layout.fillWidth: root.horizontal
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "workspaces"
|
|
|
|
delegate: EntryWrapper {
|
|
Workspaces {
|
|
horizontal: root.horizontal
|
|
screen: root.screen
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "tray"
|
|
|
|
delegate: EntryWrapper {
|
|
TrayIcons {
|
|
horizontal: root.horizontal
|
|
loader: root
|
|
popouts: root.popouts
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "statusIcons"
|
|
|
|
delegate: EntryWrapper {
|
|
StatusIcons {
|
|
horizontal: root.horizontal
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "resources"
|
|
|
|
delegate: EntryWrapper {
|
|
Resources {
|
|
horizontal: root.horizontal
|
|
screenState: root.screenState
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "updates"
|
|
|
|
delegate: EntryWrapper {
|
|
UpdatesWidget {
|
|
horizontal: root.horizontal
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "notifBell"
|
|
|
|
delegate: EntryWrapper {
|
|
NotifBell {
|
|
horizontal: root.horizontal
|
|
popouts: root.popouts
|
|
screenState: root.screenState
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "clock"
|
|
|
|
delegate: EntryWrapper {
|
|
Clock {
|
|
horizontal: root.horizontal
|
|
loader: root
|
|
popouts: root.popouts
|
|
screenState: root.screenState
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "activeWindow"
|
|
|
|
delegate: EntryWrapper {
|
|
WindowTitle {
|
|
bar: root
|
|
horizontal: root.horizontal
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "network"
|
|
|
|
delegate: EntryWrapper {
|
|
NetworkWidget {}
|
|
}
|
|
}
|
|
|
|
DelegateChoice {
|
|
roleValue: "media"
|
|
|
|
delegate: EntryWrapper {
|
|
MediaWidget {
|
|
horizontal: root.horizontal
|
|
visible: !root.fullscreen || root.isHovered
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
component EntryWrapper: Item {
|
|
readonly property int section: {
|
|
let n = 0;
|
|
for (let i = 0; i < index; i++) {
|
|
if (repeater.model.values[i].id === "spacer")
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
readonly property string entryId: modelData.id
|
|
required property int index
|
|
required property var modelData
|
|
|
|
default property Item item
|
|
|
|
Layout.alignment: root.horizontal ? Qt.AlignVCenter : Qt.AlignHCenter
|
|
Layout.bottomMargin: !root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
|
|
Layout.leftMargin: root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
|
|
Layout.rightMargin: root.horizontal && index === repeater.count - 1 ? root.vPadding : 0
|
|
Layout.topMargin: !root.horizontal && index === 0 ? root.vPadding : (entryId === "statusIcons" && !root.horizontal) ? -root.spacing + Tokens.spacing.extraSmall : 0
|
|
children: item
|
|
implicitHeight: item?.implicitHeight ?? 0
|
|
implicitWidth: item?.implicitWidth ?? 0
|
|
}
|
|
}
|