Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions manual/05-the-top-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ All of it is stored in `~/.config/omarchy/shell.json`, under the `bar` key. Here

Every widget is one entry in one of the three layout arrays, and its settings sit inline on that entry — there's no separate settings file and no `config` sub-object. The clock's `format`, `formatAlt` (what right-click cycles to), and `verticalFormat` all live right there on `{ "id": "omarchy.clock" }`.

The media widget works the same way: `scroll` set to `false` stops the now-playing text from sliding and truncates it with an ellipsis instead, `separator` changes what sits between the track and the artist, `maxLabelWidth` is how wide the label may get before either of those kicks in, and `iconGap` is the space between the play/pause control and the text.

`centerAnchor` names the one center widget that gets pinned to the exact center of the screen, with the others flanking it. That's how the clock stays dead center even as the weather and update badge come and go. Set it to an empty string and the center list is just centered as a group instead.

One rule worth internalizing: **once you have your own `shell.json`, it's canonical**. Until you customize anything, the shell reads Omarchy's default file. The moment you drag a widget, run `omarchy bar`, or edit the file yourself, you own it — there's no deep merge, so new default widgets in future Omarchy releases won't appear on your bar automatically. `omarchy bar defaults` puts the shipped layout back whenever you want a clean slate.
Expand Down
74 changes: 69 additions & 5 deletions shell/plugins/services/media/BarWidget.qml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ BarWidget {
property bool popupOpen: false

function close() { popupOpen = false }
property real maxLabelWidth: 180

// Tunables from this widget's inline shell.json entry. Defaults reproduce the
// previous hardcoded behaviour, so an entry without them is unchanged.
readonly property bool scrollLabel: setting("scroll", true)
readonly property string separator: setting("separator", " · ")
readonly property real iconGap: setting("iconGap", 6)
property real maxLabelWidth: setting("maxLabelWidth", 180)
Comment thread
robi42 marked this conversation as resolved.
Outdated

visible: hasMedia
implicitWidth: hasMedia ? row.implicitWidth + Style.space(14) : 0
Expand All @@ -28,7 +34,7 @@ BarWidget {
Row {
id: row
anchors.centerIn: parent
spacing: Style.space(6)
spacing: Style.space(root.iconGap)

Text {
id: glyph
Expand All @@ -45,15 +51,19 @@ BarWidget {

Item {
id: scrollClip
width: Math.min(root.maxLabelWidth, labelText.implicitWidth)
width: root.scrollLabel
? Math.min(root.maxLabelWidth, labelText.implicitWidth)
: staticLabel.implicitWidth
height: glyph.height
clip: true
anchors.verticalCenter: parent.verticalCenter
visible: !root.bar.vertical && root.title !== ""

// Scrolling mode: title and artist as one string, slid horizontally.
Text {
id: labelText
text: root.title + (root.artist ? " · " + root.artist : "")
visible: root.scrollLabel
text: root.title + (root.artist ? root.separator + root.artist : "")
color: root.bar.barForeground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.body
Expand All @@ -63,14 +73,68 @@ BarWidget {

NumberAnimation on x {
id: scrollAnim
running: labelText.needsScroll && !root.popupOpen && !root.bar.vertical
running: root.scrollLabel && labelText.needsScroll && !root.popupOpen && !root.bar.vertical
loops: Animation.Infinite
duration: Math.max(6000, labelText.implicitWidth * 25)
from: scrollClip.width
to: -labelText.implicitWidth
easing.type: Easing.Linear
}
}

// Static mode: title and artist elide against their own share of the
// budget, so a long title truncates instead of pushing the artist out of
// the label entirely. The shares are constants rather than being derived
// from the text metrics, which keeps each label's width binding clear of
// its own implicitWidth.
Row {
id: staticLabel
visible: !root.scrollLabel
spacing: 0
anchors.verticalCenter: parent.verticalCenter

Item {
width: Math.min(root.maxLabelWidth * 0.65, titleText.implicitWidth)
Comment thread
robi42 marked this conversation as resolved.
Outdated
height: titleText.implicitHeight
clip: true

Text {
id: titleText
text: root.title
width: parent.width
elide: Text.ElideRight
color: root.bar.barForeground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.body
}
}

Text {
id: sepText
visible: root.artist !== ""
text: root.separator
color: root.bar.barForeground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.body
}

Item {
visible: root.artist !== ""
width: Math.min(root.maxLabelWidth * 0.35, artistText.implicitWidth)
height: artistText.implicitHeight
clip: true

Text {
id: artistText
text: root.artist
width: parent.width
elide: Text.ElideRight
color: root.bar.barForeground
font.family: root.bar.fontFamily
font.pixelSize: Style.font.body
}
}
}
}
}

Expand Down