add font picker, color picker and bullet point ui tools

This commit is contained in:
GenevensiS
2026-02-05 16:02:55 +01:00
parent 11e506739a
commit e9abd1fb77
31 changed files with 519 additions and 134 deletions
+183 -21
View File
@@ -102,40 +102,202 @@ TextValue& TextValueAction::value() const {
}
unique_ptr<TextValueAction> toggle_format_action(const TextValueP& value, vector<String>& tags, size_t start_i, size_t end_i, size_t start, size_t end, const String& action_name) {
if (start > end) {
swap(start, end);
swap(start_i, end_i);
}
// Find absolute position of the selection
String new_value = value->value();
size_t untagged_start_i = to_untagged_pos(new_value, start_i);
size_t untagged_end_i = to_untagged_pos(new_value, end_i);
int offset = 0;
// Compute the changes
for (int i = 0; i < tags.size() ; ++i) {
const String& tag = tags[i];
new_value = tag.Contains(":") ? compute_new_variable_value(new_value, tag, start_i, end_i):
tag == _("li") ? compute_new_bullet_value (new_value, offset, start_i, end_i):
compute_new_simple_value (new_value, tag, start_i, end_i);
// Erase redundant tags
if (start != end) {
// don't simplify if start == end, this way we insert <b></b>, allowing the
// user to press Ctrl+B and start typing bold text
new_value = simplify_tagged(new_value);
}
// Adjust selection
start_i = to_tagged_pos(new_value, untagged_start_i, true, false);
end_i = to_tagged_pos(new_value, untagged_end_i, true, false);
}
// Build action
if (value->value() == new_value) {
return nullptr; // no changes
} else {
return make_unique<TextValueAction>(value, start+offset, end+offset, end+offset, new_value, action_name);
}
}
unique_ptr<TextValueAction> toggle_format_action(const TextValueP& value, const String& tag, size_t start_i, size_t end_i, size_t start, size_t end, const String& action_name) {
if (start > end) {
swap(start, end);
swap(start_i, end_i);
}
String new_value;
const String& str = value->value();
// Are we inside the tag we are toggling?
if (!is_in_tag(str, _("<") + tag, start_i, end_i)) {
// we are not inside this tag, add it
new_value = substr(str, 0, start_i);
new_value += _("<") + tag + _(">");
new_value += substr(str, start_i, end_i - start_i);
new_value += _("</") + tag + _(">");
new_value += substr(str, end_i);
} else {
// we are inside this tag, 'remove' it
new_value = substr(str, 0, start_i);
new_value += _("</") + tag + _(">");
new_value += substr(str, start_i, end_i - start_i);
new_value += _("<") + tag + _(">");
new_value += substr(str, end_i);
}
// Build action
}
String new_value = value->value();
int offset = 0;
// Compute the changes
new_value = tag.Contains(":") ? compute_new_variable_value(new_value, tag, start_i, end_i):
tag == _("li") ? compute_new_bullet_value (new_value, offset, start_i, end_i):
compute_new_simple_value (new_value, tag, start_i, end_i);
// Erase redundant tags
if (start != end) {
// don't simplify if start == end, this way we insert <b></b>, allowing the
// user to press Ctrl+B and start typing bold text
new_value = simplify_tagged(new_value);
}
// Build action
if (value->value() == new_value) {
return nullptr; // no changes
} else {
return make_unique<TextValueAction>(value, start, end, end, new_value, action_name);
return make_unique<TextValueAction>(value, start+offset, end+offset, end+offset, new_value, action_name);
}
}
String compute_new_simple_value(const String& str, const String& tag, size_t start_i, size_t end_i) {
String prefix(substr(str, 0, start_i));
String suffix(substr(str, end_i));
String selection(substr(str, start_i, end_i - start_i));
// if we are inside the tag we are toggling, 'remove' it
if (in_tag(str, _("<") + tag, start_i, end_i) != String::npos) selection = anti_wrap_tag(selection, _("<") + tag + _(">"));
// otherwise add it
else selection = wrap_tag(selection, _("<") + tag + _(">"));
return prefix + selection + suffix;
}
String compute_new_variable_value(const String& str, const String& tag, size_t start_i, size_t end_i) {
size_t start_tag = in_tag(str, _("<") + tag, start_i, end_i, true);
// if we are inside the tag we are toggling, 'remove' it
if (start_tag != String::npos) {
String prefix(substr(str, 0, start_i));
String suffix(substr(str, end_i));
String selection(substr(str, start_i, end_i - start_i));
selection = anti_wrap_tag(selection, _("<") + tag + _(">"));
return prefix + selection + suffix;
}
// we are not inside this tag, add it around the selection, and
// move all instances of variants of this tag to the sides of the selection
// first, include surrounding formatting tags to the selection
int start_temp = str.find_last_of("<", start_i - 1);
while (start_temp != String::npos &&
str.GetChar(start_i - 1) == '>' &&
is_formatting_tag(str, start_temp)) {
start_i = start_temp;
start_temp = str.find_last_of("<", start_i - 1);
}
while (end_i < str.size() &&
is_formatting_tag(str, end_i)) {
end_i = str.find_first_of(">", end_i) + 1;
}
String prefix(substr(str, 0, start_i));
String suffix(substr(str, end_i));
String selection(substr(str, start_i, end_i - start_i));
// tally open tags that are variants of this tag
vector<String> tag_list;
map<String, int> tag_map;
int start = 0;
int end = 0;
String tag_variants = _("<") + tag.substr(0, tag.find_first_of(":"));
size_t size = tag_variants.size();
String temp; temp.reserve(selection.size());
for (; end < selection.size() ; ++end) {
if (is_substr(selection, end, tag_variants)) {
temp += selection.substr(start, end - start);
start = end + 1;
end = selection.find(">", end + size);
String new_tag = selection.substr(start, end+1 - start);
if (tag_map.find(new_tag) != tag_map.end()) {
tag_map[new_tag] = tag_map[new_tag]+1;
}
else {
tag_map[new_tag] = 1;
tag_list.push_back(new_tag);
}
start = end+1;
}
}
temp += selection.substr(start, end - start);
// tally close tags
selection.Clear(); selection.reserve(temp.size());
String cejected_tag = close_tag(tag_variants);
size_t csize = size + 1;
for (start = 0, end = 0; end < temp.size() ; ++end) {
if (is_substr(temp, end, cejected_tag)) {
selection += temp.substr(start, end - start);
start = end + 2;
end = temp.find(">", end + csize);
String new_tag = temp.substr(start, end+1 - start);
if (tag_map.find(new_tag) != tag_map.end()) {
tag_map[new_tag] = tag_map[new_tag]-1;
}
else {
tag_map[new_tag] = -1;
tag_list.push_back(new_tag);
}
start = end+1;
}
}
selection += temp.substr(start, end - start);
// add the actual tag we are toggling
selection = wrap_tag(selection, _("<") + tag + _(">"));
// add the tallied open and close tags
for (int i = 0; i < tag_list.size() ; ++i) {
String& new_tag = tag_list[i];
int count = tag_map[new_tag];
if (count > 0) {
selection = selection + _("<") + new_tag;
}
else if (count < 0) {
selection = _("</") + new_tag + selection;
}
}
// add the stuff around the selection
return prefix + selection + suffix;
}
String compute_new_bullet_value(const String& str, int& offset, size_t start_i, size_t end_i) {
// Are we inside the tag we are toggling?
size_t start_tag = in_tag(str, _("<li"), start_i, end_i);
if (start_tag == String::npos) {
// we are not inside this tag, add it
// first, expand the selection to the end of the line
end_i = min(str.find(_("<li"), end_i),
min(str.find(_("<sep"), end_i),
min(str.find(_("<suffix"), end_i),
min(str.find(_("\n"), end_i),
min(str.find(_("<line"), end_i),
str.find(_("<soft-line"), end_i))))));
String prefix(substr(str, 0, start_i));
String suffix = end_i == String::npos ? String() : substr(str, end_i);
String selection(substr(str, start_i, end_i - start_i));
selection = _("<li><bullet>• </bullet>") + selection + _("</li>");
offset += 1;
return prefix + selection + suffix;
}
// we are inside this tag, 'remove' it
// first, expand the selection to include the tags
start_i = start_tag;
end_i = str.find(_("</li>"), start_tag);
if (end_i != String::npos) end_i += 5;
String prefix(substr(str, 0, start_i));
String suffix = end_i == String::npos ? String() : substr(str, end_i);
String selection(substr(str, start_i, end_i - start_i));
selection = remove_tag(remove_tag_contents(selection, _("<bullet>")), _("<bullet>"));
selection = remove_tag(selection, _("<li>"));
offset -= 1;
return prefix + selection + suffix;
}
unique_ptr<TextValueAction> typing_action(const TextValueP& value, size_t start_i, size_t end_i, size_t start, size_t end, const String& replacement, const String& action_name) {