Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
text_overflow.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2//
3// pulp #1407 — CSS `text-overflow: ellipsis` truncation helper.
4//
5// Translates a string to its ellipsised form when its measured width
6// exceeds the available content-box. Operates on UTF-8 codepoints so
7// truncation never splits a multibyte sequence, and uses U+2026 (…)
8// rather than three ASCII dots, matching CSS behavior.
9
10#pragma once
11
13#include <string>
14#include <string_view>
15
16namespace pulp::view {
17
20inline constexpr const char* kEllipsis = "\xe2\x80\xa6";
21
24inline std::size_t utf8_advance(std::string_view text, std::size_t count) {
25 std::size_t i = 0;
26 while (count > 0 && i < text.size()) {
27 unsigned char c = static_cast<unsigned char>(text[i]);
28 if (c < 0x80) i += 1;
29 else if (c < 0xC0) {
30 i += 1; // stray continuation byte -> skip
31 continue;
32 }
33 else if (c < 0xE0) i += 2;
34 else if (c < 0xF0) i += 3;
35 else i += 4;
36 if (i > text.size()) i = text.size();
37 --count;
38 }
39 return i;
40}
41
44inline std::size_t utf8_codepoint_count(std::string_view text) {
45 std::size_t n = 0;
46 for (std::size_t i = 0; i < text.size();) {
47 unsigned char c = static_cast<unsigned char>(text[i]);
48 if (c < 0x80) i += 1;
49 else if (c < 0xC0) {
50 i += 1;
51 continue;
52 }
53 else if (c < 0xE0) i += 2;
54 else if (c < 0xF0) i += 3;
55 else i += 4;
56 if (i > text.size()) i = text.size();
57 ++n;
58 }
59 return n;
60}
61
76 std::string_view text,
77 float available_width,
78 bool force_ellipsis = false) {
79 if (available_width <= 0.0f) return std::string(kEllipsis);
80 if (!force_ellipsis &&
81 canvas.measure_text(std::string(text)) <= available_width)
82 return std::string(text);
83
84 const std::size_t total = utf8_codepoint_count(text);
85 if (total == 0) return std::string(text);
86
87 // Reserve at least the ellipsis itself.
88 if (canvas.measure_text(kEllipsis) > available_width) {
89 // Even "…" doesn't fit — return "…" anyway so callers can still
90 // signal truncation. The widget will visually clip via its
91 // bounding box.
92 return std::string(kEllipsis);
93 }
94
95 // Binary search for the largest codepoint prefix that fits with "…".
96 std::size_t lo = 0; // always fits (with ellipsis) — invariant: 0-prefix + "…"
97 std::size_t hi = total; // never fits at hi (we know total doesn't fit)
98 while (lo + 1 < hi) {
99 std::size_t mid = (lo + hi) / 2;
100 std::string candidate(text.substr(0, utf8_advance(text, mid)));
101 candidate.append(kEllipsis);
102 if (canvas.measure_text(candidate) <= available_width) lo = mid;
103 else hi = mid;
104 }
105
106 std::string out(text.substr(0, utf8_advance(text, lo)));
107 out.append(kEllipsis);
108 return out;
109}
110
111} // namespace pulp::view
Definition canvas.hpp:240
Definition affine_transform.hpp:24
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
std::size_t utf8_codepoint_count(std::string_view text)
Count the number of UTF-8 leading-byte codepoints in text.
Definition text_overflow.hpp:44
std::string truncate_to_width(canvas::Canvas &canvas, std::string_view text, float available_width, bool force_ellipsis=false)
If text already fits within available_width, return it as-is unless force_ellipsis is true.
Definition text_overflow.hpp:75
@ text
Plain text (default color).
Definition code_editor_tokenizer.hpp:37
constexpr const char * kEllipsis
UTF-8 horizontal ellipsis character (U+2026, "…").
Definition text_overflow.hpp:20
@ c
Definition input_events.hpp:39
@ n
Definition input_events.hpp:40
@ i
Definition input_events.hpp:40
@ candidate
Definition waveform_editor_primitives.hpp:193
std::size_t utf8_advance(std::string_view text, std::size_t count)
Walk forward over UTF-8 leading bytes.
Definition text_overflow.hpp:24