Skip to content

Conversation

@postsolar
Copy link
Contributor

Changes behavior to keep popup open when switching tracks.

  • Popup no longer closes on next/prev track
  • Widget remains visible but empty when player is stopped (e.g. closed)

Note: Does not distinguish between stop and track change events. As a consequence, the bar widget is always visible (even if empty). This could be improved with a delay-based approach if needed.

🤖 Generated with Claude Code

Changes behavior to keep popup open when switching tracks.

- Popup no longer closes on next/prev track
- Widget remains visible but empty when player is stopped (e.g. closed)

Note: Does not distinguish between stop and track change events.
As a consequence, **the bar widget is always visible (even if empty)**.
This could be improved with a delay-based approach if needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@github-actions github-actions bot added the Z:Review Required Pull request pending review label Dec 26, 2025
@postsolar
Copy link
Contributor Author

Issue: playing next/prev track hides the popup, regardless of whether it's next/prev via ironbar or another way.

Why: next/prev track events are coupled with a transient PlayerState::Stopped, which just hid the widget and thus the popup too.

So we just don't hide the widget. The caveat is that we cannot distinguish between a transient stop on next/prev and an "actual" stop, e.g. closing the player. So we just empty the label. As a consequence the popup doesn't close anymore on actual stop either, but I'd say this is actually more predictable UX. Less good is that there is now constantly an empty-label bar widget – this could maybe be addressed with adding a delay (say 2000ms) to distinguish between transient and actual stops, but I chose to go with a simpler solution for now and hear your opinion @JakeStanger.

@JakeStanger
Copy link
Owner

I think the button needs to be properly hidden when nothing is playing, as otherwise styling is going to cause it to show as a small empty box in many cases.

The timeout approach sounds like the right way to go to me, although 2000ms sounds excessive (how long is the delay between the events, I'd imagine almost instant?). You should just be able to hold onto a glib timeout which hides the button on stop, and clear that timeout on any other relevant event.

Implements timeout-based detection to keep popup open during track changes
while properly hiding the button when the player is actually closed.

When PlayerState::Stopped is received:
- Clear label and icons immediately (visual feedback)
- Schedule button hide after 500ms timeout
- If new track/state arrives before timeout, cancel and stay visible
- If timeout fires, hide button and close popup

This prevents the popup from closing during track changes (transient stops)
while still hiding the button when the player is truly closed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@postsolar
Copy link
Contributor Author

I set it to 500ms for now, but it would occasionally still hide unintended, which sort of… defeats the whole point, leading to unpredictable UX. I think ideally we would just decouple popup visibility from button visibility, but it's not something I looked into yet.

@JakeStanger
Copy link
Owner

it would occasionally still hide unintended, which sort of… defeats the whole point, leading to unpredictable UX

Yeah okay agreed, this sounds worse.

The popup is closed here, inside the module code:

} else {
button.set_visible(false);
tx.send_spawn(ModuleUpdateEvent::ClosePopup);

To me this feels bad already - why is the bar widget code responsible for hiding the popup? That should probably be in the controller. Moving the check up to there is probably most of the work in decoupling the two.

@postsolar
Copy link
Contributor Author

postsolar commented Dec 28, 2025

If I just comment out all ModuleUpdateEvent::ClosePopup, nothing changes. My understanding is: it's a GTK-level limitation, a Popover can't be displayed if its parent (widget button in this case) is unmapped. This is what I see from getting the stacktrace inside here:

ironbar/src/popup.rs

Lines 79 to 81 in ca391df

popover.connect_closed(|popover| {
popover.unparent();
});

There is no popup.hide() getting called at all, it seems to go straight through GTK.

@postsolar
Copy link
Contributor Author

Also as for delays, I now realize there might simply not be any "correct" value because next/prev track metadata would be preceded by I/O 😆 :

1. Press next track
2.1. Backend: ends current track, sends stopped state
2.2. Ironbar: PlayerState::Stopped received
3. Backend/player: **loads up next track via IO** (network, disk, etc)
4. Ironbar: PlayerState::Playing received

And IO operation duration is unpredictable.

@JakeStanger
Copy link
Owner

Popover can't be displayed if its parent (widget button in this case) is unmapped

Ah right of course. Gone are the days of popups being free floating separate windows.

I now realize there might simply not be any "correct" value because next/prev track metadata would be preceded by I/O 😆

Do we know whether the Stopped event comes from Ironbar (there's a couple of places it can in the MPRIS module) or from players themselves? If it is Ironbar, we might be able to do something about it.

Otherwise, as much as I don't like it, I think a delay is the best possible answer. We'd need to pick a value large enough that any reasonable I/O would have kicked off in that time, and then worst-case if I/O takes an unusually long period of time, the (currently empty anyway) popup closes and has to be reopened.

@JakeStanger JakeStanger added Z:Changes Requested Pull request awaiting changes from author and removed Z:Review Required Pull request pending review labels Jan 5, 2026
@postsolar
Copy link
Contributor Author

I will most likely be away from Linux for quite some time, so this one is on hold for now :)

@JakeStanger
Copy link
Owner

No worries

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z:Changes Requested Pull request awaiting changes from author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants