HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
markup.hpp
Go to the documentation of this file.
1
2
3#pragma once
4
5#include "grapheme.hpp"
6#include "phrasing.hpp"
7#include "gstring.hpp"
8#include "../i18n/i18n.hpp"
9#include "../utility/utility.hpp"
10#include <string>
11#include <string_view>
12#include <algorithm>
13#include <iterator>
14#include <ranges>
15#include <concepts>
16
17hi_export_module(hikogui.unicode.markup);
18
19hi_export namespace hi { inline namespace v1 {
20
57hi_export template<std::input_or_output_iterator It, std::sentinel_for<It> ItEnd>
58constexpr It
59apply_markup(It first, ItEnd last, language_tag default_language = language_tag{"en-US"}, phrasing default_phrasing = phrasing::regular) noexcept
60 requires std::same_as<typename It::value_type, grapheme>
61{
62 enum class state_type { idle, command };
63
64 auto src_it = first;
65 auto dst_it = first;
66 auto command_start = first;
67
68 default_language = default_language.expand();
69 auto current_language = default_language;
70 auto current_phrasing = default_phrasing;
71
72 auto write_character = [&](grapheme c) {
73 c.set_language_tag(current_language);
74 c.set_phrasing(current_phrasing);
75 *dst_it++ = c;
76 };
77
78 auto write_command = [&](gstring_view command) {
79 write_character('[');
80 for (auto c : command) {
81 write_character(c);
82 }
83 write_character(']');
84 };
85
86 auto state = state_type::idle;
87 while (src_it != last) {
88 auto c = *src_it++;
89
90 if (state == state_type::idle) {
91 if (c == '[') {
92 command_start = src_it;
93 state = state_type::command;
94 } else {
95 write_character(c);
96 }
97
98 } else if (state == state_type::command) {
99 if (c == '[') {
100 // Escaped open bracket.
101 write_character(c);
102 state = state_type::idle;
103
104 } else if (c == ']') {
105 // End of command.
106 auto command = gstring_view{command_start, src_it - 1};
107 if (command.empty()) {
108 // An empty command is an error, keep the command in the text.
109 write_command(command);
110
111 } else if (command.size() == 1) {
112 if (auto const command_g = command.front(); command_g.is_ascii()) {
113 auto const command_c = char_cast<char>(command_g.starter());
114 if (command_c == '.') {
115 current_language = default_language;
116 current_phrasing = default_phrasing;
117
118 } else if (auto p = to_phrasing(command_c)) {
119 current_phrasing = *p;
120
121 } else {
122 // Unknown command, leep the command in the text.
123 write_command(command);
124 }
125
126 } else {
127 // Unknown command, keep the command in the text.
128 write_command(command);
129 }
130
131 } else {
132 try {
133 current_language = language_tag{to_string(command)}.expand();
134 } catch (...) {
135 // Leave the full command in the output.
136 write_command(command);
137 }
138 }
139 state = state_type::idle;
140 }
141 }
142 }
143
144 return dst_it;
145}
146
154hi_export [[nodiscard]] constexpr gstring
155apply_markup(gstring str, language_tag default_language = language_tag{"en-US"}, phrasing default_phrasing = phrasing::regular) noexcept
156{
157 auto it = apply_markup(str.begin(), str.end(), default_language, default_phrasing);
158 str.erase(it, str.end());
159 return str;
160}
161
169hi_export [[nodiscard]] constexpr gstring
170apply_markup(std::string_view str, language_tag default_language = language_tag{"en-US"}, phrasing default_phrasing = phrasing::regular) noexcept
171{
172 return apply_markup(to_gstring(str), default_language, default_phrasing);
173}
174
175}} // namespace hi::v1
@ grapheme
The gui_event has grapheme data.
The HikoGUI namespace.
Definition array_generic.hpp:20
hi_export constexpr It apply_markup(It first, ItEnd last, language_tag default_language=language_tag{"en-US"}, phrasing default_phrasing=phrasing::regular) noexcept
Inplace-apply markup to a string of graphemes.
Definition markup.hpp:59
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
The IETF BCP 47 language tag.
Definition language_tag_intf.hpp:30
T to_string(T... args)