diff --git a/include/modules/mpris/mpris.hpp b/include/modules/mpris/mpris.hpp index e82c929..ea5f7de 100644 --- a/include/modules/mpris/mpris.hpp +++ b/include/modules/mpris/mpris.hpp @@ -16,7 +16,7 @@ extern "C" { namespace waybar::modules::mpris { -class Mpris : public AModule { +class Mpris : public ALabel { public: Mpris(const std::string&, const Json::Value&); virtual ~Mpris(); @@ -39,21 +39,38 @@ class Mpris : public AModule { std::optional artist; std::optional album; std::optional title; - std::optional length; // as HH:MM:SS + std::optional length; // as HH:MM:SS + std::optional position; // same format }; auto getPlayerInfo() -> std::optional; - auto getIcon(const Json::Value&, const std::string&) -> std::string; - - Gtk::Box box_; - Gtk::Label label_; + auto getIconFromJson(const Json::Value&, const std::string&) -> std::string; + auto getArtistStr(const PlayerInfo&, bool) -> std::string; + auto getAlbumStr(const PlayerInfo&, bool) -> std::string; + auto getTitleStr(const PlayerInfo&, bool) -> std::string; + auto getLengthStr(const PlayerInfo&, bool) -> std::string; + auto getPositionStr(const PlayerInfo&, bool) -> std::string; + auto getDynamicStr(const PlayerInfo&, bool, bool) -> std::string; // config - std::string format_; std::string format_playing_; std::string format_paused_; std::string format_stopped_; - std::chrono::seconds interval_; + + std::string tooltip_; + std::string tooltip_playing_; + std::string tooltip_paused_; + std::string tooltip_stopped_; + + int artist_len_; + int album_len_; + int title_len_; + int dynamic_len_; + std::vector dynamic_prio_; + bool truncate_hours_; + bool tooltip_len_limits_; + std::string ellipsis_; + std::string player_; std::vector ignored_players_; diff --git a/man/waybar-mpd.5.scd b/man/waybar-mpd.5.scd index 044af98..1dde8f7 100644 --- a/man/waybar-mpd.5.scd +++ b/man/waybar-mpd.5.scd @@ -74,20 +74,20 @@ Addressed by *mpd* Tooltip information displayed when the MPD server can't be reached. *artist-len*: ++ - typeof: integer ++ - Maximum length of the Artist tag. + typeof: integer ++ + Maximum length of the Artist tag. *album-len*: ++ - typeof: integer ++ - Maximum length of the Album tag. + typeof: integer ++ + Maximum length of the Album tag. *album-artist-len*: ++ - typeof: integer ++ - Maximum length of the Album Artist tag. + typeof: integer ++ + Maximum length of the Album Artist tag. *title-len*: ++ - typeof: integer ++ - Maximum length of the Title tag. + typeof: integer ++ + Maximum length of the Title tag. *rotate*: ++ typeof: integer ++ @@ -98,12 +98,12 @@ Addressed by *mpd* The maximum length in character the module should display. *min-length*: ++ - typeof: integer ++ - The minimum length in characters the module should take up. + typeof: integer ++ + The minimum length in characters the module should take up. *align*: ++ - typeof: float ++ - The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. If the module is rotated, it will follow the flow of the text. *on-click*: ++ typeof: string ++ diff --git a/man/waybar-mpris.5.scd b/man/waybar-mpris.5.scd index d2a72d9..40f132d 100644 --- a/man/waybar-mpris.5.scd +++ b/man/waybar-mpris.5.scd @@ -33,6 +33,84 @@ The *mpris* module displays currently playing media via libplayerctl. typeof: string ++ The status-specific text format. +*tooltip*: ++ + typeof: bool ++ + default: true ++ + Option to disable tooltip on hover. + +*tooltip-format*: ++ + typeof: string ++ + default: {player} ({status}) {dynamic} ++ + The tooltip text format. + +*tooltip-format-[status]*: ++ + typeof: string ++ + The status-specific tooltip format. + +*artist-len*: ++ + typeof: integer ++ + Maximum length of the Artist tag (Wide/Fullwidth Unicode characters + count as two). Set to zero to hide the artist in `{dynamic}` tag. + +*album-len*: ++ + typeof: integer ++ + Maximum length of the Album tag (Wide/Fullwidth Unicode characters count + as two). Set to zero to hide the album in `{dynamic}` tag. + +*title-len*: ++ + typeof: integer ++ + Maximum length of the Title tag (Wide/Fullwidth Unicode characters count + as two). Set to zero to hide the title in `{dynamic}` tag. + +*dynamic-len*: ++ + typeof: integer ++ + Maximum length of the Dynamic tag (Wide/Fullwidth Unicode characters ++ + count as two). The dynamic tag will not truncate any tags beyond their ++ + set length limits, instead, it will attempt to fit as much of the ++ + available tags as possible. It is recommended you set title-len to ++ + something less than or equal to this value, so the title will always be ++ + displayed. + +*dynamic-priority*: ++ + typeof: []string ++ + default: ["title", "length", "position", "artist", "album"] ++ + Priority of the tags when truncating the Dynamic tag (absence in this + list means force inclusion). + +*truncate-hours*: ++ + typeof: bool ++ + default: true ++ + Whether to hide hours when media duration is less than an hour long. + +*enable-tooltip-len-limits*: ++ + typeof: bool ++ + default: false ++ + Option to enable the length limits for the tooltip as well. By default + the tooltip ignores all length limits. + +*ellipsis*: ++ + typeof: string ++ + default: "…" ++ + This character will be used when any of the tags exceed their maximum + length. If you don't want to use an ellipsis, set this to empty string. + +*rotate*: ++ + typeof: integer ++ + Positive value to rotate the text label. + +*max-length*: ++ + typeof: integer ++ + The maximum length in character the module should display. + +*min-length*: ++ + typeof: integer ++ + The minimum length in characters the module should take up. + +*align*: ++ + typeof: float ++ + The alignment of the text, where 0 is left-aligned and 1 is right-aligned. + If the module is rotated, it will follow the flow of the text. + *on-click*: ++ typeof: string ++ default: play-pause ++ @@ -49,11 +127,11 @@ The *mpris* module displays currently playing media via libplayerctl. Overwrite default action toggles. *player-icons*: ++ - typeof: map[string]string + typeof: map[string]string ++ Allows setting _{player-icon}_ based on player-name property. *status-icons*: ++ - typeof: map[string]string + typeof: map[string]string ++ Allows setting _{status-icon}_ based on player status (playing, paused, stopped). @@ -83,8 +161,8 @@ The *mpris* module displays currently playing media via libplayerctl. ``` "mpris": { - "format": "DEFAULT: {player_icon} {dynamic}", - "format-paused": "DEFAULT: {status_icon} {dynamic}", + "format": "{player_icon} {dynamic}", + "format-paused": "{status_icon} {dynamic}", "player-icons": { "default": "▶", "mpv": "🎵" diff --git a/src/modules/mpris/mpris.cpp b/src/modules/mpris/mpris.cpp index 7665e13..68bb7fe 100644 --- a/src/modules/mpris/mpris.cpp +++ b/src/modules/mpris/mpris.cpp @@ -1,15 +1,16 @@ -#include "modules/mpris/mpris.hpp" - #include #include #include #include +#include "modules/mpris/mpris.hpp" + extern "C" { #include } +#include #include namespace waybar::modules::mpris { @@ -17,22 +18,20 @@ namespace waybar::modules::mpris { const std::string DEFAULT_FORMAT = "{player} ({status}): {dynamic}"; Mpris::Mpris(const std::string& id, const Json::Value& config) - : AModule(config, "mpris", id), - box_(Gtk::ORIENTATION_HORIZONTAL, 0), - label_(), - format_(DEFAULT_FORMAT), - interval_(0), + : ALabel(config, "mpris", id, DEFAULT_FORMAT, 5, false, true), + tooltip_(DEFAULT_FORMAT), + artist_len_(-1), + album_len_(-1), + title_len_(-1), + dynamic_len_(-1), + dynamic_prio_({"title", "length", "position", "artist", "album"}), + truncate_hours_(true), + tooltip_len_limits_(false), + // this character is used in Gnome so it's fine to use it here + ellipsis_(u8"\u2026"), player_("playerctld"), manager(), player() { - box_.pack_start(label_); - box_.set_name(name_); - event_box_.add(box_); - event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Mpris::handleToggle)); - - if (config_["format"].isString()) { - format_ = config_["format"].asString(); - } if (config_["format-playing"].isString()) { format_playing_ = config_["format-playing"].asString(); } @@ -42,8 +41,51 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["format-stopped"].isString()) { format_stopped_ = config_["format-stopped"].asString(); } - if (config_["interval"].isUInt()) { - interval_ = std::chrono::seconds(config_["interval"].asUInt()); + if (config_["ellipsis"].isString()) { + ellipsis_ = config_["ellipsis"].asString(); + } + if (tooltipEnabled()) { + if (config_["tooltip-format"].isString()) { + tooltip_ = config_["tooltip-format"].asString(); + } + if (config_["tooltip-format-playing"].isString()) { + tooltip_playing_ = config_["tooltip-format-playing"].asString(); + } + if (config_["tooltip-format-paused"].isString()) { + tooltip_paused_ = config_["tooltip-format-paused"].asString(); + } + if (config_["tooltip-format-stopped"].isString()) { + tooltip_stopped_ = config_["tooltip-format-stopped"].asString(); + } + if (config_["enable-tooltip-len-limits"].isBool()) { + tooltip_len_limits_ = config["enable-tooltip-len-limits"].asBool(); + } + } + + if (config["artist-len"].isUInt()) { + artist_len_ = config["artist-len"].asUInt(); + } + if (config["album-len"].isUInt()) { + album_len_ = config["album-len"].asUInt(); + } + if (config["title-len"].isUInt()) { + title_len_ = config["title-len"].asUInt(); + } + if (config["dynamic-len"].isUInt()) { + dynamic_len_ = config["dynamic-len"].asUInt(); + } + if (config_["dynamic-priority"].isArray()) { + dynamic_prio_.clear(); + for (auto it = config_["dynamic-priority"].begin(); it != config_["dynamic-priority"].end(); + ++it) { + if (it->isString()) { + dynamic_prio_.push_back(it->asString()); + } + } + } + + if (config_["truncate-hours"].isBool()) { + truncate_hours_ = config["truncate-hours"].asBool(); } if (config_["player"].isString()) { player_ = config_["player"].asString(); @@ -51,7 +93,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config) if (config_["ignored-players"].isArray()) { for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end(); ++it) { - ignored_players_.push_back(it->asString()); + if (it->isString()) { + ignored_players_.push_back(it->asString()); + } } } @@ -118,7 +162,7 @@ Mpris::~Mpris() { if (player != NULL) g_object_unref(player); } -auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::string { +auto Mpris::getIconFromJson(const Json::Value& icons, const std::string& key) -> std::string { if (icons.isObject()) { if (icons[key].isString()) { return icons[key].asString(); @@ -129,6 +173,181 @@ auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::st return ""; } +// Wide characters count as two, zero-width characters count as zero +// Modifies str in-place (unless width = std::string::npos) +// Returns the total width of the string pre-truncating +size_t utf8_truncate(std::string& str, size_t width = std::string::npos) { + if (str.length() == 0) return 0; + + const gchar* trunc_end = nullptr; + + size_t total_width = 0; + + for (gchar *data = str.data(), *end = data + str.size(); data;) { + gunichar c = g_utf8_get_char_validated(data, end - data); + if (c == -1 || c == -2) { + // invalid unicode, treat string as ascii + if (width != std::string::npos && str.length() > width) str.resize(width); + return str.length(); + } else if (g_unichar_iswide(c)) { + total_width += 2; + } else if (!g_unichar_iszerowidth(c) && c != 0xAD) { // neither zero-width nor soft hyphen + total_width += 1; + } + + data = g_utf8_find_next_char(data, end); + if (width != std::string::npos && total_width <= width && !g_unichar_isspace(c)) + trunc_end = data; + } + + if (trunc_end) str.resize(trunc_end - str.data()); + + return total_width; +} + +size_t utf8_width(const std::string& str) { return utf8_truncate(const_cast(str)); } + +void truncate(std::string& s, const std::string& ellipsis, size_t max_len) { + if (max_len == 0) { + s.resize(0); + return; + } + size_t len = utf8_truncate(s, max_len); + if (len > max_len) { + size_t ellipsis_len = utf8_width(ellipsis); + if (max_len >= ellipsis_len) { + if (ellipsis_len) utf8_truncate(s, max_len - ellipsis_len); + s += ellipsis; + } else { + s.resize(0); + } + } +} + +auto Mpris::getArtistStr(const PlayerInfo& info, bool truncated) -> std::string { + auto artist = info.artist.value_or(std::string()); + if (truncated && artist_len_ >= 0) truncate(artist, ellipsis_, artist_len_); + return artist; +} + +auto Mpris::getAlbumStr(const PlayerInfo& info, bool truncated) -> std::string { + auto album = info.album.value_or(std::string()); + if (truncated && album_len_ >= 0) truncate(album, ellipsis_, album_len_); + return album; +} + +auto Mpris::getTitleStr(const PlayerInfo& info, bool truncated) -> std::string { + auto title = info.title.value_or(std::string()); + if (truncated && title_len_ >= 0) truncate(title, ellipsis_, title_len_); + return title; +} + +auto Mpris::getLengthStr(const PlayerInfo& info, bool truncated) -> std::string { + if (info.length.has_value()) { + auto length = info.length.value(); + return (truncated && length.substr(0, 3) == "00:") ? length.substr(3) : length; + } + return std::string(); +} + +auto Mpris::getPositionStr(const PlayerInfo& info, bool truncated) -> std::string { + if (info.position.has_value()) { + auto position = info.position.value(); + return (truncated && position.substr(0, 3) == "00:") ? position.substr(3) : position; + } + return std::string(); +} + +auto Mpris::getDynamicStr(const PlayerInfo& info, bool truncated, bool html) -> std::string { + auto artist = getArtistStr(info, truncated); + auto album = getAlbumStr(info, truncated); + auto title = getTitleStr(info, truncated); + auto length = getLengthStr(info, truncated && truncate_hours_); + // keep position format same as length format + auto position = getPositionStr(info, truncated && truncate_hours_ && length.length() < 6); + + size_t artistLen = utf8_width(artist); + size_t albumLen = utf8_width(album); + size_t titleLen = utf8_width(title); + size_t lengthLen = length.length(); + size_t posLen = position.length(); + + bool showArtist = artistLen != 0; + bool showAlbum = albumLen != 0; + bool showTitle = titleLen != 0; + bool showLength = lengthLen != 0; + bool showPos = posLen != 0; + + if (truncated && dynamic_len_ >= 0) { + size_t dynamicLen = dynamic_len_; + if (showArtist) artistLen += 3; + if (showAlbum) albumLen += 3; + if (showLength) lengthLen += 3; + if (showPos) posLen += 3; + + size_t totalLen = 0; + + for (auto it = dynamic_prio_.begin(); it != dynamic_prio_.end(); ++it) { + if (*it == "artist") { + if (totalLen + artistLen > dynamicLen) { + showArtist = false; + } else if (showArtist) { + totalLen += artistLen; + } + } else if (*it == "album") { + if (totalLen + albumLen > dynamicLen) { + showAlbum = false; + } else if (showAlbum) { + totalLen += albumLen; + } + } else if (*it == "title") { + if (totalLen + titleLen > dynamicLen) { + showTitle = false; + } else if (showTitle) { + totalLen += titleLen; + } + } else if (*it == "length") { + if (totalLen + lengthLen > dynamicLen) { + showLength = false; + } else if (showLength) { + totalLen += lengthLen; + posLen = std::max((size_t)2, posLen) - 2; + } + } else if (*it == "position") { + if (totalLen + posLen > dynamicLen) { + showPos = false; + } else if (showPos) { + totalLen += posLen; + lengthLen = std::max((size_t)2, lengthLen) - 2; + } + } + } + } + + std::stringstream dynamic; + if (html) { + artist = Glib::Markup::escape_text(artist); + album = Glib::Markup::escape_text(album); + title = Glib::Markup::escape_text(title); + } + if (showArtist) dynamic << artist << " - "; + if (showAlbum) dynamic << album << " - "; + if (showTitle) dynamic << title; + if (showLength || showPos) { + dynamic << ' '; + if (html) dynamic << ""; + dynamic << '['; + if (showPos) { + dynamic << position; + if (showLength) dynamic << '/'; + } + if (showLength) dynamic << length; + dynamic << ']'; + if (html) dynamic << ""; + } + return dynamic.str(); +} + auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name, gpointer data) -> void { Mpris* mpris = static_cast(data); @@ -248,21 +467,21 @@ auto Mpris::getPlayerInfo() -> std::optional { if (auto artist_ = playerctl_player_get_artist(player, &error)) { spdlog::debug("mpris[{}]: artist = {}", info.name, artist_); - info.artist = Glib::Markup::escape_text(artist_); + info.artist = artist_; g_free(artist_); } if (error) goto errorexit; if (auto album_ = playerctl_player_get_album(player, &error)) { spdlog::debug("mpris[{}]: album = {}", info.name, album_); - info.album = Glib::Markup::escape_text(album_); + info.album = album_; g_free(album_); } if (error) goto errorexit; if (auto title_ = playerctl_player_get_title(player, &error)) { spdlog::debug("mpris[{}]: title = {}", info.name, title_); - info.title = Glib::Markup::escape_text(title_); + info.title = title_; g_free(title_); } if (error) goto errorexit; @@ -272,12 +491,28 @@ auto Mpris::getPlayerInfo() -> std::optional { std::chrono::microseconds len = std::chrono::microseconds(std::strtol(length_, nullptr, 10)); auto len_h = std::chrono::duration_cast(len); auto len_m = std::chrono::duration_cast(len - len_h); - auto len_s = std::chrono::duration_cast(len - len_m); + auto len_s = std::chrono::duration_cast(len - len_h - len_m); info.length = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); g_free(length_); } if (error) goto errorexit; + { + auto position_ = playerctl_player_get_position(player, &error); + if (error) { + // it's fine to have an error here because not all players report a position + g_error_free(error); + error = nullptr; + } else { + spdlog::debug("mpris[{}]: position = {}", info.name, position_); + std::chrono::microseconds len = std::chrono::microseconds(position_); + auto len_h = std::chrono::duration_cast(len); + auto len_m = std::chrono::duration_cast(len - len_h); + auto len_s = std::chrono::duration_cast(len - len_h - len_m); + info.position = fmt::format("{:02}:{:02}:{:02}", len_h.count(), len_m.count(), len_s.count()); + } + } + return info; errorexit: @@ -296,19 +531,19 @@ bool Mpris::handleToggle(GdkEventButton* const& e) { switch (e->button) { case 1: // left-click if (config_["on-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_play_pause(player, &error); break; case 2: // middle-click if (config_["on-middle-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_previous(player, &error); break; case 3: // right-click if (config_["on-right-click"].isString()) { - return AModule::handleToggle(e); + return ALabel::handleToggle(e); } playerctl_player_next(player, &error); break; @@ -327,7 +562,7 @@ auto Mpris::update() -> void { auto opt = getPlayerInfo(); if (!opt) { event_box_.set_visible(false); - AModule::update(); + ALabel::update(); return; } auto info = *opt; @@ -339,60 +574,86 @@ auto Mpris::update() -> void { spdlog::debug("mpris[{}]: running update", info.name); - // dynamic is the auto-formatted string containing a nice out-of-the-box - // format text - std::stringstream dynamic; - if (info.artist) dynamic << *info.artist << " - "; - if (info.album) dynamic << *info.album << " - "; - if (info.title) dynamic << *info.title; - if (info.length) - dynamic << " " - << "" - << "[" << *info.length << "]" - << ""; - // set css class for player status - if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { - box_.get_style_context()->remove_class(lastStatus); + if (!lastStatus.empty() && event_box_.get_style_context()->has_class(lastStatus)) { + event_box_.get_style_context()->remove_class(lastStatus); } - if (!box_.get_style_context()->has_class(info.status_string)) { - box_.get_style_context()->add_class(info.status_string); + if (!event_box_.get_style_context()->has_class(info.status_string)) { + event_box_.get_style_context()->add_class(info.status_string); } lastStatus = info.status_string; // set css class for player name - if (!lastPlayer.empty() && box_.get_style_context()->has_class(lastPlayer)) { - box_.get_style_context()->remove_class(lastPlayer); + if (!lastPlayer.empty() && event_box_.get_style_context()->has_class(lastPlayer)) { + event_box_.get_style_context()->remove_class(lastPlayer); } - if (!box_.get_style_context()->has_class(info.name)) { - box_.get_style_context()->add_class(info.name); + if (!event_box_.get_style_context()->has_class(info.name)) { + event_box_.get_style_context()->add_class(info.name); } lastPlayer = info.name; auto formatstr = format_; + auto tooltipstr = tooltip_; switch (info.status) { case PLAYERCTL_PLAYBACK_STATUS_PLAYING: if (!format_playing_.empty()) formatstr = format_playing_; + if (!tooltip_playing_.empty()) tooltipstr = tooltip_playing_; break; case PLAYERCTL_PLAYBACK_STATUS_PAUSED: if (!format_paused_.empty()) formatstr = format_paused_; + if (!tooltip_paused_.empty()) tooltipstr = tooltip_paused_; break; case PLAYERCTL_PLAYBACK_STATUS_STOPPED: if (!format_stopped_.empty()) formatstr = format_stopped_; + if (!tooltip_stopped_.empty()) tooltipstr = tooltip_stopped_; break; } - auto label_format = - fmt::format(fmt::runtime(formatstr), fmt::arg("player", info.name), - fmt::arg("status", info.status_string), fmt::arg("artist", *info.artist), - fmt::arg("title", *info.title), fmt::arg("album", *info.album), - fmt::arg("length", *info.length), fmt::arg("dynamic", dynamic.str()), - fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)), - fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string))); - label_.set_markup(label_format); + + std::string length = getLengthStr(info, truncate_hours_); + std::string tooltipLength = + (tooltip_len_limits_ || length.length() > 5) ? length : getLengthStr(info, false); + // keep position format same as length format + std::string position = getPositionStr(info, truncate_hours_ && length.length() < 6); + std::string tooltipPosition = + (tooltip_len_limits_ || position.length() > 5) ? position : getPositionStr(info, false); + + try { + auto label_format = fmt::format( + fmt::runtime(formatstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), fmt::arg("artist", getArtistStr(info, true)), + fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)), + fmt::arg("length", length), fmt::arg("position", position), + fmt::arg("dynamic", getDynamicStr(info, true, true)), + fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); + + label_.set_markup(label_format); + } catch (fmt::format_error const& e) { + spdlog::warn("mpris: format error: {}", e.what()); + } + + if (tooltipEnabled()) { + try { + auto tooltip_text = fmt::format( + fmt::runtime(tooltipstr), fmt::arg("player", info.name), + fmt::arg("status", info.status_string), + fmt::arg("artist", getArtistStr(info, tooltip_len_limits_)), + fmt::arg("title", getTitleStr(info, tooltip_len_limits_)), + fmt::arg("album", getAlbumStr(info, tooltip_len_limits_)), + fmt::arg("length", tooltipLength), fmt::arg("position", tooltipPosition), + fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)), + fmt::arg("player_icon", getIconFromJson(config_["player-icons"], info.name)), + fmt::arg("status_icon", getIconFromJson(config_["status-icons"], info.status_string))); + + label_.set_tooltip_text(tooltip_text); + } catch (fmt::format_error const& e) { + spdlog::warn("mpris: format error (tooltip): {}", e.what()); + } + } event_box_.set_visible(true); // call parent update - AModule::update(); + ALabel::update(); } } // namespace waybar::modules::mpris