nosignal-shell/modules/bar/components/Tray.qml
28allday b1e2bb3bc6 feat(bar): horizontal top bar — Phase A geometry (NoSignal layout)
Convert the bar from a vertical left-edge strip to a horizontal top bar. The
shell had no orientation abstraction, so this flips the geometry across the bar
and the drawer/region/interaction layer:

- Bar.qml: ColumnLayout -> RowLayout; hit-testing (checkPopout/handleWheel),
  scroll halves and popout-center anchoring all keyed off x instead of y;
  first/last padding -> left/right margins.
- BarWrapper.qml: the bar's "thickness" is now its height (clampedHeight/
  contentHeight/implicitHeight); content anchors left/right/top.
- drawers: Exclusions (exclusive zone moved left->top), Panels (content inset
  topMargin = bar height), Regions + ContentWindow (panel->window coordinate
  mapping and the thick border flip left->top), Interactions (bar-region check
  y<barHeight, panel Y-origin uses bar height, scroll passes x, bar drag-reveal
  is vertical).
- widgets: Clock (Row, HH:MM with a colon), StatusIcons (Row), Workspaces +
  Workspace (Row), Tray (Row, expand chevron at the right) — each sizes to the
  bar height instead of width.

Verified live in the dev VM: top bar renders with centered-clock entries, the
exclusion zone keeps windows below the bar, and the dashboard drops down
correctly. Popout fly-outs still anchor sideways (Phase B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 23:26:55 +01:00

122 lines
3.2 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Services.SystemTray
import Caelestia.Config
import qs.components
import qs.services
StyledRect {
id: root
readonly property alias layout: layout
readonly property alias items: items
readonly property alias expandIcon: expandIcon
readonly property int padding: Config.bar.tray.background ? Tokens.padding.medium : Tokens.padding.extraSmall
readonly property int spacing: Config.bar.tray.background ? Tokens.spacing.small : 0
property bool expanded
readonly property real nonAnimWidth: {
if (!Config.bar.tray.compact)
return layout.implicitWidth + padding * 2;
return (expanded ? expandIcon.implicitWidth + layout.implicitWidth + spacing : expandIcon.implicitWidth) + padding * 2;
}
clip: true
visible: width > 0
implicitHeight: Tokens.sizes.bar.innerWidth
implicitWidth: nonAnimWidth
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (Config.bar.tray.background && items.count > 0) ? Colours.tPalette.m3surfaceContainer.a : 0)
radius: Tokens.rounding.full
Row {
id: layout
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.padding
spacing: Tokens.spacing.small
opacity: root.expanded || !Config.bar.tray.compact ? 1 : 0
add: Transition {
Anim {
properties: "scale"
from: 0
to: 1
easing: Tokens.anim.standardDecel
}
}
move: Transition {
Anim {
properties: "scale"
to: 1
easing: Tokens.anim.standardDecel
}
Anim {
properties: "x,y"
}
}
Repeater {
id: items
model: ScriptModel {
values: SystemTray.items.values.filter(i => !GlobalConfig.bar.tray.hiddenIcons.includes(i.id))
}
TrayItem {}
}
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
Loader {
id: expandIcon
asynchronous: true
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
active: Config.bar.tray.compact && items.count > 0
sourceComponent: Item {
implicitHeight: expandIconInner.implicitHeight
implicitWidth: expandIconInner.implicitWidth - Tokens.padding.small
MaterialIcon {
id: expandIconInner
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Config.bar.tray.background ? Tokens.padding.extraSmall : -Tokens.padding.extraSmall
text: "expand_less"
fontStyle: Tokens.font.icon.large
rotation: root.expanded ? 270 : 90
Behavior on rotation {
Anim {}
}
Behavior on anchors.rightMargin {
Anim {}
}
}
}
}
Behavior on implicitWidth {
Anim {}
}
}