mpris: add tooltip and length limits
parent
9fd2703cb9
commit
a53c97f7f6
|
@ -44,6 +44,11 @@ class Mpris : public AModule {
|
||||||
|
|
||||||
auto getPlayerInfo() -> std::optional<PlayerInfo>;
|
auto getPlayerInfo() -> std::optional<PlayerInfo>;
|
||||||
auto getIcon(const Json::Value&, const std::string&) -> std::string;
|
auto getIcon(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 getDynamicStr(const PlayerInfo&, bool, bool) -> std::string;
|
||||||
|
|
||||||
Gtk::Box box_;
|
Gtk::Box box_;
|
||||||
Gtk::Label label_;
|
Gtk::Label label_;
|
||||||
|
@ -53,6 +58,19 @@ class Mpris : public AModule {
|
||||||
std::string format_playing_;
|
std::string format_playing_;
|
||||||
std::string format_paused_;
|
std::string format_paused_;
|
||||||
std::string format_stopped_;
|
std::string format_stopped_;
|
||||||
|
|
||||||
|
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<std::string> dynamic_prio_;
|
||||||
|
bool tooltip_len_limits_;
|
||||||
|
|
||||||
std::chrono::seconds interval_;
|
std::chrono::seconds interval_;
|
||||||
std::string player_;
|
std::string player_;
|
||||||
std::vector<std::string> ignored_players_;
|
std::vector<std::string> ignored_players_;
|
||||||
|
|
|
@ -74,20 +74,20 @@ Addressed by *mpd*
|
||||||
Tooltip information displayed when the MPD server can't be reached.
|
Tooltip information displayed when the MPD server can't be reached.
|
||||||
|
|
||||||
*artist-len*: ++
|
*artist-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Artist tag.
|
Maximum length of the Artist tag.
|
||||||
|
|
||||||
*album-len*: ++
|
*album-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Album tag.
|
Maximum length of the Album tag.
|
||||||
|
|
||||||
*album-artist-len*: ++
|
*album-artist-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Album Artist tag.
|
Maximum length of the Album Artist tag.
|
||||||
|
|
||||||
*title-len*: ++
|
*title-len*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
Maximum length of the Title tag.
|
Maximum length of the Title tag.
|
||||||
|
|
||||||
*rotate*: ++
|
*rotate*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
|
@ -98,12 +98,12 @@ Addressed by *mpd*
|
||||||
The maximum length in character the module should display.
|
The maximum length in character the module should display.
|
||||||
|
|
||||||
*min-length*: ++
|
*min-length*: ++
|
||||||
typeof: integer ++
|
typeof: integer ++
|
||||||
The minimum length in characters the module should take up.
|
The minimum length in characters the module should take up.
|
||||||
|
|
||||||
*align*: ++
|
*align*: ++
|
||||||
typeof: float ++
|
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.
|
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*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
|
|
|
@ -33,6 +33,48 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
The status-specific text format.
|
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 ++
|
||||||
|
default: "MPD (disconnected)" ++
|
||||||
|
The status-specif tooltip format.
|
||||||
|
|
||||||
|
*artist-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Artist tag.
|
||||||
|
|
||||||
|
*album-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Album tag.
|
||||||
|
|
||||||
|
*title-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Title tag.
|
||||||
|
|
||||||
|
*dynamic-len*: ++
|
||||||
|
typeof: integer ++
|
||||||
|
Maximum length of the Dynamic tag.
|
||||||
|
|
||||||
|
*dynamic-priority* ++
|
||||||
|
typeof: []string ++
|
||||||
|
default: ["title", "length", "artist", "album"]
|
||||||
|
Priority of the tags when truncating the Dynamic tag (absence in this
|
||||||
|
list means force inclusion).
|
||||||
|
|
||||||
|
*enable-tooltip-len-limits*: ++
|
||||||
|
typeof: bool ++
|
||||||
|
default: false ++
|
||||||
|
Option to enable the limits for the tooltip as well
|
||||||
|
|
||||||
*on-click*: ++
|
*on-click*: ++
|
||||||
typeof: string ++
|
typeof: string ++
|
||||||
default: play-pause ++
|
default: play-pause ++
|
||||||
|
@ -83,8 +125,8 @@ The *mpris* module displays currently playing media via libplayerctl.
|
||||||
|
|
||||||
```
|
```
|
||||||
"mpris": {
|
"mpris": {
|
||||||
"format": "DEFAULT: {player_icon} {dynamic}",
|
"format": "{player_icon} {dynamic}",
|
||||||
"format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
|
"format-paused": "{status_icon} <i>{dynamic}</i>",
|
||||||
"player-icons": {
|
"player-icons": {
|
||||||
"default": "▶",
|
"default": "▶",
|
||||||
"mpv": "🎵"
|
"mpv": "🎵"
|
||||||
|
|
|
@ -21,6 +21,13 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||||
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
box_(Gtk::ORIENTATION_HORIZONTAL, 0),
|
||||||
label_(),
|
label_(),
|
||||||
format_(DEFAULT_FORMAT),
|
format_(DEFAULT_FORMAT),
|
||||||
|
tooltip_(DEFAULT_FORMAT),
|
||||||
|
artist_len_(-1),
|
||||||
|
album_len_(-1),
|
||||||
|
title_len_(-1),
|
||||||
|
dynamic_len_(-1),
|
||||||
|
dynamic_prio_({"title", "length", "artist", "album"}),
|
||||||
|
tooltip_len_limits_(false),
|
||||||
interval_(0),
|
interval_(0),
|
||||||
player_("playerctld"),
|
player_("playerctld"),
|
||||||
manager(),
|
manager(),
|
||||||
|
@ -42,6 +49,46 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||||
if (config_["format-stopped"].isString()) {
|
if (config_["format-stopped"].isString()) {
|
||||||
format_stopped_ = config_["format-stopped"].asString();
|
format_stopped_ = config_["format-stopped"].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_["interval"].isUInt()) {
|
if (config_["interval"].isUInt()) {
|
||||||
interval_ = std::chrono::seconds(config_["interval"].asUInt());
|
interval_ = std::chrono::seconds(config_["interval"].asUInt());
|
||||||
}
|
}
|
||||||
|
@ -51,7 +98,9 @@ Mpris::Mpris(const std::string& id, const Json::Value& config)
|
||||||
if (config_["ignored-players"].isArray()) {
|
if (config_["ignored-players"].isArray()) {
|
||||||
for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end();
|
for (auto it = config_["ignored-players"].begin(); it != config_["ignored-players"].end();
|
||||||
++it) {
|
++it) {
|
||||||
ignored_players_.push_back(it->asString());
|
if (it->isString()) {
|
||||||
|
ignored_players_.push_back(it->asString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +178,95 @@ auto Mpris::getIcon(const Json::Value& icons, const std::string& key) -> std::st
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Mpris::getArtistStr(const PlayerInfo &info, bool truncated) -> std::string {
|
||||||
|
std::string artist = info.artist.value_or(std::string());
|
||||||
|
return (truncated && artist_len_ >= 0) ? artist.substr(0, artist_len_) : artist;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getAlbumStr(const PlayerInfo &info, bool truncated) -> std::string {
|
||||||
|
std::string album = info.album.value_or(std::string());
|
||||||
|
return (truncated && album_len_ >= 0) ? album.substr(0, album_len_) : album;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getTitleStr(const PlayerInfo &info, bool truncated) -> std::string {
|
||||||
|
std::string title = info.title.value_or(std::string());
|
||||||
|
return (truncated && title_len_ >= 0) ? title.substr(0, title_len_) : title;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getLengthStr(const PlayerInfo &info, bool from_dynamic) -> std::string {
|
||||||
|
if (info.length.has_value()) {
|
||||||
|
return from_dynamic ? ("[" + info.length.value() + "]") : info.length.value();
|
||||||
|
}
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Mpris::getDynamicStr(const PlayerInfo &info, bool truncated, bool html) -> std::string {
|
||||||
|
std::string artist = getArtistStr(info, truncated);
|
||||||
|
std::string album = getAlbumStr(info, truncated);
|
||||||
|
std::string title = getTitleStr(info, truncated);
|
||||||
|
std::string length = getLengthStr(info, true);
|
||||||
|
|
||||||
|
std::stringstream dynamic;
|
||||||
|
bool showArtist, showAlbum, showTitle, showLength;
|
||||||
|
showArtist = artist.length() != 0;
|
||||||
|
showAlbum = album.length() != 0;
|
||||||
|
showTitle = title.length() != 0;
|
||||||
|
showLength = length.length() != 0;
|
||||||
|
|
||||||
|
if (truncated && dynamic_len_ >= 0) {
|
||||||
|
size_t dynamicLen = dynamic_len_;
|
||||||
|
size_t artistLen = showArtist ? artist.length() + 3 : 0;
|
||||||
|
size_t albumLen = showAlbum ? album.length() + 3 : 0;
|
||||||
|
size_t titleLen = title.length();
|
||||||
|
size_t lengthLen = showLength ? length.length() + 1 : 0;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
totalLen += artistLen;
|
||||||
|
}
|
||||||
|
} else if (*it == "album") {
|
||||||
|
if (totalLen + albumLen > dynamicLen) {
|
||||||
|
showAlbum = false;
|
||||||
|
} else {
|
||||||
|
totalLen += albumLen;
|
||||||
|
}
|
||||||
|
} else if (*it == "title") {
|
||||||
|
if (totalLen + titleLen > dynamicLen) {
|
||||||
|
showTitle = false;
|
||||||
|
} else {
|
||||||
|
totalLen += titleLen;
|
||||||
|
}
|
||||||
|
} else if (*it == "length") {
|
||||||
|
if (totalLen + lengthLen > dynamicLen) {
|
||||||
|
showLength = false;
|
||||||
|
} else {
|
||||||
|
totalLen += lengthLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showArtist) dynamic << artist << " - ";
|
||||||
|
if (showAlbum) dynamic << album << " - ";
|
||||||
|
if (showTitle) dynamic << title;
|
||||||
|
if (showLength) {
|
||||||
|
dynamic << " ";
|
||||||
|
if (html) {
|
||||||
|
dynamic << "<small>";
|
||||||
|
}
|
||||||
|
dynamic << length;
|
||||||
|
if (html) {
|
||||||
|
dynamic << "</small>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dynamic.str();
|
||||||
|
}
|
||||||
|
|
||||||
auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
auto Mpris::onPlayerNameAppeared(PlayerctlPlayerManager* manager, PlayerctlPlayerName* player_name,
|
||||||
gpointer data) -> void {
|
gpointer data) -> void {
|
||||||
Mpris* mpris = static_cast<Mpris*>(data);
|
Mpris* mpris = static_cast<Mpris*>(data);
|
||||||
|
@ -335,18 +473,6 @@ auto Mpris::update() -> void {
|
||||||
|
|
||||||
spdlog::debug("mpris[{}]: running update", info.name);
|
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 << " "
|
|
||||||
<< "<small>"
|
|
||||||
<< "[" << *info.length << "]"
|
|
||||||
<< "</small>";
|
|
||||||
|
|
||||||
// set css class for player status
|
// set css class for player status
|
||||||
if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) {
|
if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) {
|
||||||
box_.get_style_context()->remove_class(lastStatus);
|
box_.get_style_context()->remove_class(lastStatus);
|
||||||
|
@ -366,25 +492,55 @@ auto Mpris::update() -> void {
|
||||||
lastPlayer = info.name;
|
lastPlayer = info.name;
|
||||||
|
|
||||||
auto formatstr = format_;
|
auto formatstr = format_;
|
||||||
|
auto tooltipstr = tooltip_;
|
||||||
switch (info.status) {
|
switch (info.status) {
|
||||||
case PLAYERCTL_PLAYBACK_STATUS_PLAYING:
|
case PLAYERCTL_PLAYBACK_STATUS_PLAYING:
|
||||||
if (!format_playing_.empty()) formatstr = format_playing_;
|
if (!format_playing_.empty()) formatstr = format_playing_;
|
||||||
|
if (!tooltip_playing_.empty()) tooltipstr = tooltip_playing_;
|
||||||
break;
|
break;
|
||||||
case PLAYERCTL_PLAYBACK_STATUS_PAUSED:
|
case PLAYERCTL_PLAYBACK_STATUS_PAUSED:
|
||||||
if (!format_paused_.empty()) formatstr = format_paused_;
|
if (!format_paused_.empty()) formatstr = format_paused_;
|
||||||
|
if (!tooltip_paused_.empty()) tooltipstr = tooltip_paused_;
|
||||||
break;
|
break;
|
||||||
case PLAYERCTL_PLAYBACK_STATUS_STOPPED:
|
case PLAYERCTL_PLAYBACK_STATUS_STOPPED:
|
||||||
if (!format_stopped_.empty()) formatstr = format_stopped_;
|
if (!format_stopped_.empty()) formatstr = format_stopped_;
|
||||||
|
if (!tooltip_stopped_.empty()) tooltipstr = tooltip_stopped_;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto label_format =
|
|
||||||
fmt::format(fmt::runtime(formatstr), fmt::arg("player", info.name),
|
try {
|
||||||
fmt::arg("status", info.status_string), fmt::arg("artist", *info.artist),
|
auto label_format = fmt::format(
|
||||||
fmt::arg("title", *info.title), fmt::arg("album", *info.album),
|
fmt::runtime(formatstr), fmt::arg("player", info.name),
|
||||||
fmt::arg("length", *info.length), fmt::arg("dynamic", dynamic.str()),
|
fmt::arg("status", info.status_string), fmt::arg("artist", getArtistStr(info, true)),
|
||||||
fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)),
|
fmt::arg("title", getTitleStr(info, true)), fmt::arg("album", getAlbumStr(info, true)),
|
||||||
fmt::arg("status_icon", getIcon(config_["status-icons"], info.status_string)));
|
fmt::arg("length", getLengthStr(info, false)),
|
||||||
label_.set_markup(label_format);
|
fmt::arg("dynamic", getDynamicStr(info, true, true)),
|
||||||
|
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);
|
||||||
|
} 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", getLengthStr(info, false)),
|
||||||
|
fmt::arg("dynamic", getDynamicStr(info, tooltip_len_limits_, false)),
|
||||||
|
fmt::arg("player_icon", getIcon(config_["player-icons"], info.name)),
|
||||||
|
fmt::arg("status_icon", getIcon(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);
|
event_box_.set_visible(true);
|
||||||
// call parent update
|
// call parent update
|
||||||
|
|
Loading…
Reference in New Issue