// Lucid bar from Sn3akyy1/lucid at bdd7f79da18efd837f79dcecbe5d2ec178abc10c (MIT). // Kept as a separate component so the other Lucid surfaces stay optional. import "./lucidbar" import "./lucidedit" import QtQuick import Quickshell import Quickshell.Io import Quickshell.Wayland import qs Variants { id: bars property var dock property var toastMod: null model: Monitors.barEverywhere ? Quickshell.screens : [""] PanelWindow { id: bar // a display from Quickshell.screens with a bar on every one, or "" for the one bar required property var modelData // the bar that speaks for the shell: it alone turns into notification // popups, raises the privacy toast and answers the modules' ipc readonly property bool primary: !Monitors.barEverywhere || bar.screen === Monitors.popupScreen // what the shell's ipc targets reach on the main bar readonly property var clockModule: clockMod readonly property var mprisModule: mprisMod readonly property var workspacesModule: workspacesMod PaletteFade { active: bar.visible } // the display Settings > Displays picks, or this one's own. gated, // because unset must leave the choice to hyprland, which null would not Binding { target: bar property: "screen" value: bar.modelData !== "" ? bar.modelData : Monitors.barPlacement when: bar.modelData !== "" || Monitors.barPlacement !== null } visible: Prefs.loaded && Prefs.barEnabled && Monitors.surfacesUp property bool laidOut: false // stand-ins take room in edit mode, so this asks after the modules themselves readonly property bool anyModuleShown: Prefs.barZones.some((zone) => { return bar.zoneKeys(zone).some((key) => { return bar.realWidthOf(key) > 0.5; }); }) // edit mode: the module in hand, how far it sits off its slot, and the // arrangement the bar shows while it is held property real editT: Edit.active ? 1 : 0 // in edit mode the modules part a little, so each gap has room for its clasp readonly property real editGap: Theme.dp(16) * bar.editT property string dragKey: "" property real dragShift: 0 property real dragLift: 0 property var dragGroups: null property bool dragHeld: false Behavior on editT { NumberAnimation { duration: Theme.barMs(320) easing.type: Easing.OutCubic } } function xOf(key) { return bar.placeFor(key) + (key === bar.dragKey ? bar.dragShift : 0); } function liftOf(key) { return key === bar.dragKey ? bar.dragLift : 0; } // a right click on a module: its sheet, on the side of the screen away from it function editModule(key) { const mod = bar.pillFor[key]; const cx = bar.xOf(key) + (mod ? mod.width : 0) / 2; Edit.openSheet("module", key, Edit.sideFor(cx, bar.width), bar.screen ? bar.screen.name : ""); } // the sheet's open button: this bar's module opens as a click would open it Connections { function onModuleOpenRequested(id, screen) { if (screen !== "" ? (!bar.screen || bar.screen.name !== screen) : !bar.primary) return ; const mod = bar.pillFor[id]; if (!mod) return ; if (id === "start") { dock.openLauncher(""); } else if (id === "media") { mod.openPanel("player"); } else { if (mod.compactClicked) mod.compactClicked(); mod.expanded = true; } } target: Edit } // keys, when given, lets modules joined to the next one sit flush against it function placeGroup(widths, originX, keys) { const gap = Theme.dp(Prefs.barSpacing) + bar.editGap; const out = []; let x = originX; let any = false; let trailing = 0; for (let i = 0; i < widths.length; i++) { out.push(x); const w = widths[i]; if (w > 0.5) { trailing = keys && bar.joinedAfter(keys, widths, i) ? 0 : (gap > 0 ? gap * Math.min(1, w / gap) : 0); x += w + trailing; any = true; } } out.push(any ? Math.max(originX, x - trailing) : originX); return out; } // whether the module at i is joined to the one right after it. both // have to be in the bar this moment: one that has emptied out (a tray // with no icons, workspaces off in the overview) parts its group there function joinedAfter(keys, widths, i) { return i + 1 < keys.length && widths[i] > 0.5 && widths[i + 1] > 0.5 && bar.realWidthOf(keys[i]) > 0.5 && bar.realWidthOf(keys[i + 1]) > 0.5 && Prefs.barJoinedNow(keys[i], keys[i + 1]); } // what a module's pill reads to square off the corners it shares function joinedLeftOf(key) { const at = bar.placeMap[key]; if (!at || at.at === 0) return false; const keys = bar.zoneKeys(at.zone); return bar.realWidthOf(key) > 0.5 && bar.realWidthOf(keys[at.at - 1]) > 0.5 && Prefs.barJoinedNow(keys[at.at - 1], key); } function joinedRightOf(key) { const at = bar.placeMap[key]; if (!at) return false; const keys = bar.zoneKeys(at.zone); return at.at + 1 < keys.length && bar.realWidthOf(key) > 0.5 && bar.realWidthOf(keys[at.at + 1]) > 0.5 && Prefs.barJoinedNow(key, keys[at.at + 1]); } // per module key, radius caps where a hanging panel meets the islands: {corners, tops, sides, fillets} readonly property var hangPlan: bar.planHangs() function surfaceSpan(mod) { const l = mod.x + (mod.surfaceX !== undefined ? mod.surfaceX : 0); return [l, l + (mod.surfaceWidth !== undefined ? mod.surfaceWidth : mod.width)]; } function planHangs() { const plan = ({ "corners": ({}), "tops": ({}), "sides": ({}), "fillets": [] }); const popups = Prefs.barPopupMode; if (popups && !Prefs.barPopupNotched) return plan; const most = Theme.dp(Prefs.barNotchFlare); const bite = 0.5; const cap = (key, corner, v) => { const c = plan.corners[key] || (plan.corners[key] = ({})); c[corner] = Math.min(c[corner] === undefined ? 1e6 : c[corner], Math.max(0, v)); }; const fillet = (x, y, size, mirrored, flipped) => { if (size >= 1) plan.fillets.push({ "x": x, "y": y, "size": size, "mirrored": mirrored, "flipped": flipped }); }; // islands: runs of joined modules as they stand const islands = []; const shelves = []; for (const zone of Prefs.barZones) { const keys = bar.zoneKeys(zone); let run = null; for (let i = 0; i < keys.length; i++) { const mod = bar.pillFor[keys[i]]; if (!mod || bar.widthOf(keys[i]) <= 0.5) { run = null; continue; } if (run && Prefs.barJoinedNow(keys[i - 1], keys[i])) { run.keys.push(keys[i]); run.r = bar.surfaceSpan(mod)[1]; } else { run = ({ "keys": [keys[i]], "l": bar.surfaceSpan(mod)[0], "r": bar.surfaceSpan(mod)[1], "top": mod.y, "bottom": mod.y + mod.compactHeight }); islands.push(run); } } } // what hangs: a notched pop-up, or a joined module opened in place for (const isle of islands) { for (let j = 0; j < isle.keys.length; j++) { const m = bar.pillFor[isle.keys[j]]; if (popups) { const pop = m.notchedPopup === true && m.popupOpen ? m.popupItem : null; if (pop && pop.height > 0.5) shelves.push({ "key": isle.keys[j], "l": m.x + pop.x, "r": m.x + pop.x + pop.width, "y": m.y + pop.y, "depth": pop.height }); } else if (isle.keys.length > 1 && m !== workspacesMod && m.height - m.compactHeight > 0.5) { const depth = m.height - m.compactHeight; const side = ({}); if (j > 0) { const run = m.x - isle.l; const f = Math.floor(Math.min(most, run / 2, depth / 2)); fillet(m.x - f + bite, isle.bottom - bite, f, false, false); cap(isle.keys[0], "bl", run - f); side.bl = depth - f; } if (j < isle.keys.length - 1) { const run = isle.r - (m.x + m.width); const f = Math.floor(Math.min(most, run / 2, depth / 2)); fillet(m.x + m.width - bite, isle.bottom - bite, f, true, false); cap(isle.keys[isle.keys.length - 1], "br", run - f); side.br = depth - f; } plan.sides[isle.keys[j]] = side; } } } // a pop-up's top is the screen edge upside down: islands stand on it or it hangs from them for (const shelf of shelves) { const over = islands.filter((isle) => { return isle.l < shelf.r - 0.5 && isle.r > shelf.l + 0.5 && Math.abs(isle.bottom - shelf.y) < 1; }); const top = ({}); plan.tops[shelf.key] = top; if (over.length === 0) continue; for (let k = 0; k < over.length; k++) { const isle = over[k]; const first = isle.keys[0]; const last = isle.keys[isle.keys.length - 1]; const tall = isle.bottom - isle.top; const dl = isle.l - shelf.l; if (k > 0) { const f = Math.floor(Math.min(most, (isle.l - over[k - 1].r) / 2, tall)); fillet(isle.l - f + bite, shelf.y - f + bite, f, false, true); cap(first, "bl", 0); } else if (dl > 0.5) { const f = Math.floor(Math.min(most, dl / 2, tall)); fillet(isle.l - f + bite, shelf.y - f + bite, f, false, true); cap(first, "bl", 0); top.tl = dl - f; } else if (dl < -0.5) { const f = Math.floor(Math.min(most, -dl / 2, shelf.depth / 2)); fillet(shelf.l - f + bite, shelf.y - bite, f, false, false); cap(first, "bl", -dl - f); top.tl = 0; } else { cap(first, "bl", 0); top.tl = 0; } const dr = shelf.r - isle.r; if (k < over.length - 1) { const f = Math.floor(Math.min(most, (over[k + 1].l - isle.r) / 2, tall)); fillet(isle.r - bite, shelf.y - f + bite, f, true, true); cap(last, "br", 0); } else if (dr > 0.5) { const f = Math.floor(Math.min(most, dr / 2, tall)); fillet(isle.r - bite, shelf.y - f + bite, f, true, true); cap(last, "br", 0); top.tr = dr - f; } else if (dr < -0.5) { const f = Math.floor(Math.min(most, -dr / 2, shelf.depth / 2)); fillet(shelf.r - bite, shelf.y - bite, f, true, false); cap(last, "br", -dr - f); top.tr = 0; } else { cap(last, "br", 0); top.tr = 0; } } } return plan; } property real wsCollapse: (workspacesMod.expanded && !Prefs.barPopupMode) ? 0 : 1 // the modules are addressed by their id in Prefs.barModules; the // groups in Prefs.barLayout decide which slot each one lands in readonly property var pillFor: ({ "workspaces": workspacesMod, "media": mprisMod, "tray": sysTrayMod, "clock": clockMod, "notifications": notifMod, "system": systemMod, "privacy": privacyMod, "power": powerMod, "window": windowMod, "start": startMod }) // what BarPill looks itself up in, for a right click to its card readonly property var moduleById: bar.pillFor readonly property var modules: [workspacesMod, mprisMod, sysTrayMod, clockMod, notifMod, systemMod, privacyMod, powerMod, windowMod, startMod] // which modules switched on have left the bar for now, for the Bar // page's arrangement Binding { target: Prefs property: "barModulesAway" when: bar.primary value: Prefs.barModules.filter((m) => { const mod = bar.pillFor[m.id]; return Prefs[m.key] === true && mod && !mod.shown; }).map((m) => { return m.id; }) } // a module on its way out of the bar keeps the slot it had until its // pill has finished emptying, so its neighbours close the gap at the // rate it shrinks rather than dropping into it function zoneKeys(zone) { const live = bar.dragGroups ? bar.dragGroups[zone === "centre" ? "center" : zone].filter((id) => { return Prefs.barShown(id); }) : Prefs.barKeysOf(zone); const r = Prefs.barRetiring; if (!r || r.zone !== zone || Prefs.barHas(r.key)) return live; const out = live.slice(); out.splice(Math.min(r.at, out.length), 0, r.key); return out; } readonly property var retiring: Prefs.barRetiring onRetiringChanged: { if (bar.retiring) retireTimer.restart(); } Timer { id: retireTimer interval: Theme.barMs(560) onTriggered: Prefs.barClearRetiring() } // key -> which zone it sits in and how far along readonly property var placeMap: { const out = ({}); for (var z = 0; z < Prefs.barZones.length; z++) { const zone = Prefs.barZones[z]; const keys = bar.zoneKeys(zone); for (var i = 0; i < keys.length; i++) out[keys[i]] = ({ "zone": zone, "at": i }) } return out; } // workspaces empties its slot as it flies to the screen centre, so the // rest of its zone closes up behind it function realWidthOf(key) { const it = bar.pillFor[key]; if (!it) return 0; return it === workspacesMod ? it.width * bar.wsCollapse : it.width; } // in edit mode a module with nothing to show keeps a stand-in's room function widthOf(key) { return Math.max(bar.realWidthOf(key), barEdit.ghostWidthOf(key)); } function widthsOf(zone) { return bar.zoneKeys(zone).map(bar.widthOf); } function placesOf(zone) { return zone === "left" ? bar.leftPlaces : (zone === "centre" ? bar.centrePlaces : bar.rightPlaces); } function alignFor(zone) { return zone === "centre" ? "center" : (zone === "right" ? "right" : "left"); } function placeFor(key) { const at = bar.placeMap[key]; if (!at) return 0; const places = bar.placesOf(at.zone); return at.at < places.length ? places[at.at] : 0; } function alignOf(key) { const at = bar.placeMap[key]; return bar.alignFor(at ? at.zone : "left"); } readonly property var leftWidths: bar.widthsOf("left") readonly property var centreWidths: bar.widthsOf("centre") readonly property var rightWidths: bar.widthsOf("right") readonly property real leftGroupWidth: bar.placeGroup(bar.leftWidths, 0, bar.zoneKeys("left"))[bar.leftWidths.length] readonly property real centreGroupWidth: bar.placeGroup(bar.centreWidths, 0, bar.zoneKeys("centre"))[bar.centreWidths.length] readonly property real rightGroupWidth: bar.placeGroup(bar.rightWidths, 0, bar.zoneKeys("right"))[bar.rightWidths.length] readonly property int zoneGap: Theme.dp(Prefs.barZoneGap) // pills ease to their new slots only while the arrangement is changing. // a Behavior left on all the time would sit on top of the width // animations a panel opening or a tray icon arriving already drive, and // one that restarts every frame never arrives readonly property int reorderTick: Prefs.barReorderTick property bool shuffling: false onReorderTickChanged: { bar.shuffling = true; shuffleTimer.restart(); } Timer { id: shuffleTimer interval: Theme.barMs(520) onTriggered: bar.shuffling = false } readonly property real leftOriginX: bar.sideMargin readonly property real rightOriginX: bar.width - bar.rightGroupWidth - bar.sideMargin readonly property real centreOriginX: Math.min(Math.max((bar.width - bar.centreGroupWidth) / 2, bar.leftOriginX + bar.leftGroupWidth + bar.zoneGap), bar.rightOriginX - bar.centreGroupWidth - bar.zoneGap) readonly property var leftPlaces: bar.placeGroup(bar.leftWidths, bar.leftOriginX, bar.zoneKeys("left")) readonly property var centrePlaces: bar.placeGroup(bar.centreWidths, bar.centreOriginX, bar.zoneKeys("centre")) readonly property var rightPlaces: bar.placeGroup(bar.rightWidths, bar.rightOriginX, bar.zoneKeys("right")) Behavior on wsCollapse { NumberAnimation { duration: Theme.barMs(380) easing.type: Easing.OutCubic } } readonly property real sideMargin: Theme.dp(Prefs.barSideMargin) // busy: a panel is open somewhere on the bar, so it stays out // regardless of the pointer. held: the pointer is on the reveal // strip or already on a module - either keeps it from hiding readonly property bool barBusy: bar.modules.some((m) => { return m.expanded === true || m.anyOpen === true; }) property bool slidingAway: false readonly property bool heldByPointer: Prefs.barAutoHide && (revealArea.containsMouse || (!bar.slidingAway && bar.modules.some((m) => { return m.visible && (m.surfaceHovered === true || m.compactHovered === true); }))) readonly property bool barRevealed: !Prefs.barAutoHide || bar.barBusy || bar.heldByPointer || Edit.active // how far the compact content sits above its resting y=0 while // hidden - past the window's own top edge, so it clips away for free property real hiddenOffset: bar.barRevealed ? 0 : -(Theme.dp(Prefs.barHeight) + Prefs.effectiveBarTopMargin + Theme.dp(4)) Behavior on hiddenOffset { NumberAnimation { duration: Theme.durLong easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } onBarRevealedChanged: { if (bar.barRevealed) { bar.slidingAway = false; slideAwayTimer.stop(); } else { bar.slidingAway = true; slideAwayTimer.restart(); } } Timer { id: slideAwayTimer interval: Theme.durLong onTriggered: bar.slidingAway = false } Component.onCompleted: laidOutTimer.start() Timer { id: laidOutTimer interval: 120 onTriggered: bar.laidOut = true } color: "transparent" implicitHeight: bar.screen ? bar.screen.height - Prefs.effectiveBarTopMargin : Theme.dp(800) exclusiveZone: (Prefs.barEnabled && bar.anyModuleShown && !Prefs.barAutoHide) ? Theme.dp(Prefs.barHeight) + Prefs.shellGap : 0 anchors { top: true bottom: false left: true right: true } margins { top: Prefs.effectiveBarTopMargin } // full bar: one strip across the whole top edge, under every module Rectangle { id: fullStrip visible: Prefs.barFull && bar.anyModuleShown x: 0 y: bar.hiddenOffset z: -2 width: bar.width height: Theme.dp(Prefs.barHeight) color: Theme.bg Behavior on color { enabled: bar.laidOut ColorAnimation { duration: Theme.barMs(260) easing.type: Easing.OutCubic } } } // rounded corners hanging off the strip where it meets the screen sides BarFlare { visible: fullStrip.visible && size > 0 mirrored: true size: Theme.dp(Prefs.barFullCorner) x: 0 y: Theme.dp(Prefs.barHeight) + bar.hiddenOffset z: -2 } BarFlare { visible: fullStrip.visible && size > 0 size: Theme.dp(Prefs.barFullCorner) x: bar.width - width y: Theme.dp(Prefs.barHeight) + bar.hiddenOffset z: -2 } // full bar: an open panel hangs off the strip, so round the two concave // corners where its sides meet the strip's lower edge. They grow with // the panel's height, so a folding panel takes them with it Repeater { model: (Prefs.barFull && !Prefs.barPopupMode) ? bar.modules.filter((m) => { return m !== workspacesMod; }) : [] Item { id: panelFlares required property var modelData // surfaceOpen, not the height alone: a pill that is only // resizing its compact face is see-through on the full bar readonly property real reach: (panelFlares.modelData.visible && panelFlares.modelData.surfaceOpen === true) ? panelFlares.modelData.height - Theme.dp(Prefs.barHeight) : 0 // never past the screen edge, and never into the screen corner // that already rounds that side function sizeFor(toTheLeft) { var m = panelFlares.modelData; var corner = Theme.dp(Prefs.barFullCorner); var toEdge = toTheLeft ? m.x : bar.width - m.x - m.width; var room = toEdge - (toEdge < corner * 2 ? corner : 0); return Math.max(0, Math.floor(Math.min(corner, panelFlares.reach, room))); } anchors.fill: parent z: -1 BarFlare { size: panelFlares.sizeFor(true) x: panelFlares.modelData.x - width + 0.5 y: Theme.dp(Prefs.barHeight) } BarFlare { mirrored: true size: panelFlares.sizeFor(false) x: panelFlares.modelData.x + panelFlares.modelData.width - 0.5 y: Theme.dp(Prefs.barHeight) } } } Mpris { id: mprisMod popupAlign: bar.alignOf("media") hostWindow: bar x: bar.xOf("media") editLifted: bar.dragKey === "media" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("media") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "media" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } SysTray { id: sysTrayMod popupAlign: bar.alignOf("tray") hostWindow: bar x: bar.xOf("tray") editLifted: bar.dragKey === "tray" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("tray") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "tray" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } Clock { id: clockMod popupAlign: bar.alignOf("clock") hostWindow: bar anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("clock") x: bar.xOf("clock") editLifted: bar.dragKey === "clock" Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "clock" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } Notifications { id: notifMod showsPopups: bar.primary popupAlign: bar.alignOf("notifications") hostWindow: bar x: bar.xOf("notifications") editLifted: bar.dragKey === "notifications" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("notifications") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "notifications" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } Privacy { id: privacyMod popupAlign: bar.alignOf("privacy") toast: bar.primary ? toastMod : null hostWindow: bar x: bar.xOf("privacy") editLifted: bar.dragKey === "privacy" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("privacy") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "privacy" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } SessionMenu { id: powerMod popupAlign: bar.alignOf("power") hostWindow: bar x: bar.xOf("power") editLifted: bar.dragKey === "power" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("power") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "power" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } StartButton { id: startMod popupAlign: bar.alignOf("start") hostWindow: bar dockMod: dock x: bar.xOf("start") editLifted: bar.dragKey === "start" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("start") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "start" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } ActiveWindow { id: windowMod popupAlign: bar.alignOf("window") hostWindow: bar x: bar.xOf("window") editLifted: bar.dragKey === "window" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("window") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "window" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } System { id: systemMod popupAlign: bar.alignOf("system") hostWindow: bar mprisMod: mprisMod x: bar.xOf("system") editLifted: bar.dragKey === "system" anchors.top: parent.top anchors.topMargin: bar.hiddenOffset + bar.liftOf("system") Behavior on x { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "system" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } Repeater { model: Prefs.barNotch ? bar.modules : [] Item { id: flares required property var modelData readonly property bool present: flares.modelData && flares.modelData.width > 0.5 && flares.modelData.visible && !(bar.dragLift > 0.5 && bar.pillFor[bar.dragKey] === flares.modelData) readonly property bool modHovered: flares.modelData ? (flares.modelData.compactHovered === true && flares.modelData !== workspacesMod) : false function flareFor(toTheLeft) { if (!flares.present) return 0; var mods = bar.modules; var edge = toTheLeft ? flares.modelData.x : flares.modelData.x + flares.modelData.width; var toEdge = toTheLeft ? edge : bar.width - edge; var toNeighbour = 100000; for (var i = 0; i < mods.length; i++) { var o = mods[i]; if (!o || o === flares.modelData || o.width <= 0.5 || !o.visible) continue; var d = toTheLeft ? edge - (o.x + o.width) : o.x - edge; if (d >= 0) toNeighbour = Math.min(toNeighbour, d); } return Math.max(0, Math.min(Theme.dp(Prefs.barNotchFlare), Math.floor(toNeighbour / 2), Math.floor(toEdge))); } anchors.fill: parent z: -1 visible: Prefs.barNotch && flares.present readonly property real bite: 0.5 BarFlare { hovered: flares.modHovered size: flares.flareFor(true) x: flares.modelData ? flares.modelData.x - width + flares.bite : 0 y: bar.hiddenOffset } BarFlare { hovered: flares.modHovered mirrored: true size: flares.flareFor(false) x: flares.modelData ? flares.modelData.x + flares.modelData.width - flares.bite : 0 y: bar.hiddenOffset } } } // fillets where a hanging panel meets the islands above it Repeater { model: 16 BarFlare { required property int index readonly property var spec: bar.hangPlan.fillets.length > index ? bar.hangPlan.fillets[index] : null z: 0 size: spec ? spec.size : 0 mirrored: spec ? spec.mirrored : false flipped: spec ? spec.flipped : false x: spec ? spec.x : 0 y: spec ? spec.y : 0 } } Workspaces { id: workspacesMod hostWindow: bar dockMod: dock restX: bar.xOf("workspaces") restY: bar.hiddenOffset + bar.liftOf("workspaces") z: bar.dragKey === "workspaces" ? 120 : (workspacesMod.popupOpen ? 100 : 1) Behavior on restX { enabled: bar.laidOut && bar.shuffling && bar.dragKey !== "workspaces" NumberAnimation { duration: Theme.barDurEnter easing.type: Easing.Bezier easing.bezierCurve: Theme.easeEmphasizedDecel } } } mask: Region { ModuleRegion { mod: workspacesMod } ModuleRegion { mod: mprisMod } ModuleRegion { mod: sysTrayMod } ModuleRegion { mod: clockMod } ModuleRegion { mod: notifMod } ModuleRegion { mod: systemMod } ModuleRegion { mod: privacyMod } ModuleRegion { mod: powerMod } ModuleRegion { mod: windowMod } ModuleRegion { mod: startMod } Region { item: Prefs.barAutoHide ? revealArea : null } Region { width: bar.dragHeld || Edit.active ? bar.width : 0 height: bar.dragHeld ? bar.height : (Edit.active ? Theme.dp(Prefs.barHeight) + Theme.dp(4) : 0) } } BarEdit { id: barEdit host: bar } // touching this strip reveals the bar MouseArea { id: revealArea x: 0 y: 0 width: bar.width height: Theme.dp(4) hoverEnabled: true acceptedButtons: Qt.NoButton enabled: Prefs.barAutoHide visible: Prefs.barAutoHide } // the overview and the dock menu shut each other Connections { function onExpandedChanged() { if (workspacesMod.expanded) dock.menuOpen = false; } target: workspacesMod } Connections { function onMenuOpenChanged() { if (dock.menuOpen) workspacesMod.expanded = false; } target: dock } BackgroundEffect.blurRegion: (Theme.blurAmount > 0 && bar.laidOut) ? barBlurRegion : null // clipped to the surface: hyprland frosts the whole layer for a region // wholly outside it, which is where a hidden bar's pills are Region { id: barBlurRegion intersection: Intersection.Intersect Region { x: 0 y: fullStrip.visible ? bar.hiddenOffset : 0 width: fullStrip.visible ? Math.round(bar.width) : 0 height: fullStrip.visible ? Theme.dp(Prefs.barHeight) : 0 } ModuleRegion { blur: true mod: workspacesMod } ModuleRegion { blur: true mod: mprisMod } ModuleRegion { blur: true mod: sysTrayMod } ModuleRegion { blur: true mod: clockMod } ModuleRegion { blur: true mod: notifMod } ModuleRegion { blur: true mod: systemMod } ModuleRegion { blur: true mod: privacyMod } ModuleRegion { blur: true mod: powerMod } ModuleRegion { blur: true mod: windowMod } ModuleRegion { blur: true mod: startMod } } } }