2190871a(perf(label): skip redundant markup updates) caches the last label/tooltip markup as Glib::ustring and skips set_markup() when the new markup compares equal. Glib::ustring::operator== goes through g_utf8_collate(), and under the UTF-8 locale GTK sets at startup, Unicode private-use codepoints carry no collation weight. All nerd-font icons live in the PUA, so two labels that differ only in their icon glyph collate as equal and the visual update is silently dropped. idle_inhibitor is the visible victim (#5169): clicking toggles the state class (highlight changes) but the {icon} glyph never switches between the activated/deactivated icons, while plain-text icons like "YES"/"NO" work. Any ALabel module whose consecutive updates differ only by a PUA glyph is affected. The module only started routing through this cache whendb4941efmigrated it onto the shared setLabelMarkup()/setTooltipMarkup() helpers, which is why it broke in the latest batch of refactors. Store the cache as raw UTF-8 bytes (std::string via ustring::raw()) and compare those instead, so the skip only triggers on byte-identical markup. Reproduced and verified under a nested niri session: before the fix RTMIN+n toggled the state class but left the sleep glyph unchanged; after it the glyph flips as expected, and byte-identical updates are still skipped.
98 lines
4.0 KiB
C++
98 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <fmt/args.h>
|
|
#include <fmt/format.h>
|
|
#include <glibmm/markup.h>
|
|
#include <gtkmm/label.h>
|
|
#include <json/json.h>
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "AModule.hpp"
|
|
|
|
namespace waybar {
|
|
|
|
class ALabel : public AModule {
|
|
public:
|
|
ALabel(const Json::Value&, const std::string&, const std::string&, const std::string& format,
|
|
uint16_t interval = 0, bool ellipsize = false, bool enable_click = false,
|
|
bool enable_scroll = false);
|
|
virtual ~ALabel() = default;
|
|
auto update() -> void override;
|
|
virtual std::string getIcon(uint16_t, const std::string& alt = "", uint16_t max = 0);
|
|
virtual std::string getIcon(uint16_t, const std::vector<std::string>& alts, uint16_t max = 0);
|
|
|
|
protected:
|
|
Gtk::Label label_;
|
|
std::string format_;
|
|
const std::chrono::milliseconds interval_;
|
|
bool alt_ = false;
|
|
std::string default_format_;
|
|
|
|
bool setLabelMarkup(const Glib::ustring& markup);
|
|
bool setTooltipMarkup(const Glib::ustring& markup);
|
|
|
|
// resolveTooltipFormat() / resolveFormat() are inherited from AModule.
|
|
|
|
// Combined label + tooltip helper. Builds a single fmt argument store from
|
|
// `args`, renders `labelFormat` into the label and the resolved tooltip format
|
|
// into the tooltip, both through the dedup-aware setters. Honors the `tooltip`
|
|
// toggle. This replaces the label/tooltip formatting boilerplate that modules
|
|
// used to duplicate. `state` selects `tooltip-format-<state>` when non-empty.
|
|
template <typename... Args>
|
|
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
|
|
const std::string& tooltipDefault, Args&&... args) {
|
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
(store.push_back(std::forward<Args>(args)), ...);
|
|
setLabelMarkup(fmt::vformat(labelFormat, store));
|
|
if (tooltipEnabled()) {
|
|
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
|
|
}
|
|
}
|
|
|
|
template <typename... Args>
|
|
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
|
|
Args&&... args) {
|
|
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, std::forward<Args>(args)...);
|
|
}
|
|
|
|
// Overloads accepting a pre-built argument store, for modules that must
|
|
// assemble a dynamic set of format arguments (e.g. per-core CPU stats) that
|
|
// cannot be expressed through a fixed variadic call.
|
|
// A non-const reference is used so this overload is preferred over the
|
|
// variadic template above (which would otherwise bind the store as a single
|
|
// forwarded argument).
|
|
void updateLabelAndTooltipForState(const std::string& state, const std::string& labelFormat,
|
|
const std::string& tooltipDefault,
|
|
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
|
|
setLabelMarkup(fmt::vformat(labelFormat, store));
|
|
if (tooltipEnabled()) {
|
|
setTooltipMarkup(fmt::vformat(resolveTooltipFormat(tooltipDefault, state), store));
|
|
}
|
|
}
|
|
|
|
void updateLabelAndTooltip(const std::string& labelFormat, const std::string& tooltipDefault,
|
|
fmt::dynamic_format_arg_store<fmt::format_context>& store) {
|
|
updateLabelAndTooltipForState("", labelFormat, tooltipDefault, store);
|
|
}
|
|
|
|
bool handleToggle(GdkEventButton* const& e) override;
|
|
void copyToClipboard(const std::string&);
|
|
virtual std::string getState(uint8_t value, bool lesser = false);
|
|
|
|
std::map<std::string, GtkMenuItem*> submenus_;
|
|
std::map<std::string, std::string> menuActionsMap_;
|
|
static void handleGtkMenuEvent(GtkMenuItem* menuitem, gpointer data);
|
|
|
|
private:
|
|
// Raw UTF-8 bytes, not Glib::ustring: ustring::operator== collates with
|
|
// g_utf8_collate(), which gives private-use codepoints (nerd-font icons)
|
|
// no collation weight, so two different icons compare equal.
|
|
std::optional<std::string> last_label_markup_;
|
|
std::optional<std::string> last_tooltip_markup_;
|
|
};
|
|
|
|
} // namespace waybar
|