refactor: extract utf8 truncation logic to utility
Move the UTF-8 visual-width measurement and truncation helper functions from the mpris module to a common utility (include/util/utf8_string.hpp and src/util/utf8_string.cpp). This decouples string truncation and width measurement from the mpris module, allowing other modules (like mpd) to reuse. The helper function `utf8_truncate` (formerly `truncate` in mpris) and `utf8_width` is exported under the `waybar::util` namespace, while the low-level `measure_and_truncate` (formerly `utf8_truncate`) is encapsulated in an anonymous namespace in src/util/utf8_string.cpp to avoid unnecessary API exposure. No functional changes were made to the mpris module's behavior.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include <string>
|
||||
#include <util/utf8_string.hpp>
|
||||
|
||||
namespace waybar::util {
|
||||
|
||||
namespace {
|
||||
// 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 measure_and_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 != nullptr;) {
|
||||
gunichar c = g_utf8_get_char_validated(data, end - data);
|
||||
if (c == -1U || c == -2U) {
|
||||
// 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;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
size_t utf8_width(const std::string& str) {
|
||||
return measure_and_truncate(const_cast<std::string&>(str));
|
||||
}
|
||||
|
||||
void utf8_truncate(std::string& s, const std::string& ellipsis, size_t max_len) {
|
||||
if (max_len == 0) {
|
||||
s.resize(0);
|
||||
return;
|
||||
}
|
||||
size_t len = measure_and_truncate(s, max_len);
|
||||
if (len > max_len) {
|
||||
size_t ellipsis_len = utf8_width(ellipsis);
|
||||
if (max_len >= ellipsis_len) {
|
||||
if (ellipsis_len) measure_and_truncate(s, max_len - ellipsis_len);
|
||||
s += ellipsis;
|
||||
} else {
|
||||
s.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace waybar::util
|
||||
Reference in New Issue
Block a user