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
+3
View File
@@ -180,6 +180,9 @@ void queue_message(MessageType type, String const& msg) {
// Only show errors in the main thread
message_queue.push_front(make_pair(type,msg));
}
void qm(String const& msg) {
queue_message(MESSAGE_ERROR, msg);
}
void handle_error(const Error& e) {
queue_message(e.is_fatal() ? MESSAGE_FATAL_ERROR : MESSAGE_ERROR, e.what());
+1
View File
@@ -154,6 +154,7 @@ enum MessageType
/** If the message is a MESSAGE_FATAL_ERROR, and show_message_box_for_fatal_errors==true, then a popup is shown
*/
void queue_message(MessageType type, String const& msg);
void qm(String const& msg);
/// Handle an error by queuing a message
void handle_error(const Error& e);
/// Handle an error by showing a message box
+78 -24
View File
@@ -25,8 +25,8 @@ wxUniChar tag_char(wxUniChar c) {
// Is a character the "end" of the tag name?
// don't mistake <tag> as <t>, only <t>, <t-stuff> and <t:stuff> are considered <t>
bool is_tag_end_char(Char c) {
return c == '>' || c == '-' || c == ':' || c == ' ';
bool is_tag_end_char(Char c, bool strict) {
return strict ? c == '>' : c == '>' || c == '-' || c == ':' || c == ' ';
}
@@ -162,11 +162,11 @@ String fix_old_tags(const String& str) {
return skip_all_tags(it, end, after_open, after_close);
}
[[nodiscard]] bool is_tag(String::const_iterator it, String::const_iterator end, const char* tag) {
[[nodiscard]] bool is_tag(String::const_iterator it, String::const_iterator end, const char* tag, bool strict) {
for (; *tag; ++it, ++tag) {
if (it == end || *it != *tag) return false;
}
if (it == end || !is_tag_end_char(*it)) return false;
if (it == end || !is_tag_end_char(*it, strict)) return false;
return true;
}
@@ -267,11 +267,25 @@ String::const_iterator find_close_tag(String::const_iterator tag, String::const_
return String::npos;
}
bool is_tag(const String& str, size_t pos, const String& tag) {
return is_substr(str, pos, tag) && pos+tag.size() < str.size() && is_tag_end_char(str[pos+tag.size()]);
bool is_tag(const String& str, size_t pos, const String& tag, bool strict) {
return is_substr(str, pos, tag) && pos+tag.size() < str.size() && is_tag_end_char(str[pos+tag.size()], strict);
}
[[nodiscard]] size_t in_tag(const String& str, const String& tag, size_t start, size_t end) {
bool is_formatting_tag(const String& str, size_t pos) {
if (str.size() < pos + 2) return false;
if (str.GetChar(pos) != '<') return false;
pos++;
return is_substr(str, pos, _("b")) || is_substr(str, pos, _("/b")) ||
is_substr(str, pos, _("i")) || is_substr(str, pos, _("/i")) ||
is_substr(str, pos, _("sym")) || is_substr(str, pos, _("/sym")) ||
is_substr(str, pos, _("u")) || is_substr(str, pos, _("/u")) ||
is_substr(str, pos, _("strike")) || is_substr(str, pos, _("/strike")) ||
is_substr(str, pos, _("font")) || is_substr(str, pos, _("/font")) ||
is_substr(str, pos, _("size")) || is_substr(str, pos, _("/size")) ||
is_substr(str, pos, _("color")) || is_substr(str, pos, _("/color"));
}
[[nodiscard]] size_t in_tag(const String& str, const String& tag, size_t start, size_t end, bool strict) {
size_t last_start = String::npos;
size_t size = str.size();
int taglevel = 0;
@@ -279,10 +293,10 @@ bool is_tag(const String& str, size_t pos, const String& tag) {
for (size_t pos = 0 ; pos < end ; ) {
Char c = str.GetChar(pos);
if (c == _('<')) {
if (is_substr(str, pos + 1, static_cast<const Char*>(tag.c_str())+1) && pos+tag.size() < str.size() && is_tag_end_char(str[pos+tag.size()])) {
if (is_substr(str, pos + 1, static_cast<const Char*>(tag.c_str())+1) && pos+tag.size() < str.size() && is_tag_end_char(str[pos+tag.size()], strict)) {
if (pos < start) last_start = pos;
++taglevel;
} else if (pos + 2 < size && str.GetChar(pos+1) == _('/') && is_substr(str, pos + 2, static_cast<const Char*>(tag.c_str())+1) && pos+1+tag.size() < str.size() && is_tag_end_char(str[pos+1+tag.size()])) {
} else if (pos + 2 < size && str.GetChar(pos+1) == _('/') && is_substr(str, pos + 2, static_cast<const Char*>(tag.c_str())+1) && pos+1+tag.size() < str.size() && is_tag_end_char(str[pos+1+tag.size()], strict)) {
--taglevel; // close tag
}
pos = skip_tag(str,pos);
@@ -296,8 +310,8 @@ bool is_tag(const String& str, size_t pos, const String& tag) {
}
return taglevel < 1 ? String::npos : last_start;
}
bool is_in_tag(const String& str, const String& tag, size_t start, size_t end) {
return in_tag(str,tag,start,end) != String::npos;
bool is_in_tag(const String& str, const String& tag, size_t start, size_t end, bool strict) {
return in_tag(str,tag,start,end,strict) != String::npos;
}
@@ -325,6 +339,17 @@ String anti_tag(const String& tag) {
// ----------------------------------------------------------------------------- : Cursor position
size_t to_untagged_pos(const String& str, size_t pos) {
return untag(str.substr(0, pos)).size();
}
size_t to_tagged_pos(const String& str, size_t pos, bool after_open, bool after_close) {
String::const_iterator it = str.begin();
const String::const_iterator end = str.end();
it = advance_untagged(it, end, pos, after_open, after_close);
return std::distance(str.begin(), it);
}
size_t index_to_cursor(const String& str, size_t index, Movement dir) {
size_t cursor = 0;
index = min(index, str.size());
@@ -335,7 +360,11 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) {
bool has_width = true;
if (c == _('<')) {
// a tag
if (is_substr(str, i, _("<atom")) || is_substr(str, i, _("<sep"))) {
if (is_substr(str, i, _("<atom")) ||
is_substr(str, i, _("<sep")) ||
is_substr(str, i, _("<bullet")) ||
is_substr(str, i, _("<soft-line")) ||
is_substr(str, i, _("<line"))) {
// skip tag contents, tag counts as a single 'character'
size_t before = i;
size_t close = match_close_tag(str, i);
@@ -426,7 +455,11 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size
bool has_width = true;
if (c == _('<')) {
// a tag
if (is_substr(str, i, _("<atom")) || is_substr(str, i, _("<sep"))) {
if (is_substr(str, i, _("<atom")) ||
is_substr(str, i, _("<sep")) ||
is_substr(str, i, _("<bullet")) ||
is_substr(str, i, _("<soft-line")) ||
is_substr(str, i, _("<line"))) {
// never move the end over an atom/sep
if (cur >= cursor) { ++i; break; }
// skip tag contents, tag counts as a single 'character'
@@ -504,6 +537,15 @@ String untag_for_cursor(const String& str) {
} else if (is_substr(str, i, _("<sep"))) {
i = match_close_tag_end(str, i);
ret += UNTAG_SEP;
} else if (is_substr(str, i, _("<bullet"))) {
i = match_close_tag_end(str, i);
ret += UNTAG_BULLET;
} else if (is_substr(str, i, _("<soft-line"))) {
i = match_close_tag_end(str, i);
ret += UNTAG_ATOM;
} else if (is_substr(str, i, _("<line"))) {
i = match_close_tag_end(str, i);
ret += UNTAG_ATOM;
} else if (i == 0 && is_substr(str, i, _("<prefix"))) {
// prefix at start of string, skip contents, index never before
i = match_close_tag_end(str,i);
@@ -555,7 +597,14 @@ size_t index_to_untagged(const String& str, size_t index) {
return p;
}
// ----------------------------------------------------------------------------- : Global operations
// ----------------------------------------------------------------------------- : Global operations
String wrap_tag(const String& str, const String& tag) {
return tag + str + close_tag(tag);
}
String anti_wrap_tag(const String& str, const String& tag) {
return close_tag(tag) + str + tag;
}
String remove_tag(const String& str, const String& tag) {
if (tag.size() < 1) return str;
@@ -641,14 +690,18 @@ String simplify_tagged(const String& str) {
// Add a tag to a stack of tags, try to cancel it out
// If </tag> is in stack remove it and returns true
// otherwise appends <tag> and returns fales
// otherwise appends <tag> and returns false
// (where </tag> is the negation of tag)
bool add_or_cancel_tag(const String& tag, String& stack, bool all = false) {
if (all || starts_with(tag, _("/")) ||
starts_with(tag, _("b")) ||
starts_with(tag, _("i")) ||
starts_with(tag, _("sym")) ||
starts_with(tag, _("u")) ||
starts_with(tag, _("sym"))) {
starts_with(tag, _("strike")) ||
starts_with(tag, _("color")) ||
starts_with(tag, _("font")) ||
starts_with(tag, _("size"))) {
// cancel out all close tags, but not all open tags,
// so <xx></xx> is always removed
// but </xx><xx> is not
@@ -669,9 +722,9 @@ bool add_or_cancel_tag(const String& tag, String& stack, bool all = false) {
}
String simplify_tagged_merge(const String& str, bool all) {
String ret; ret.reserve(str.size());
String waiting_tags; // tags that are waiting to be written to the output
size_t size = str.size();
String ret; ret.reserve(size);
String waiting_tags; // tags that are waiting to be written to the output
for (size_t i = 0 ; i < size ; ++i) {
Char c = str.GetChar(i);
if (c == _('<')) {
@@ -688,17 +741,18 @@ String simplify_tagged_merge(const String& str, bool all) {
}
String simplify_tagged_overlap(const String& str) {
String ret; ret.reserve(str.size());
String open_tags; // tags we are in
size_t size = str.size();
String ret; ret.reserve(size);
String open_tags; // tags we are in
for (size_t i = 0 ; i < size ; ++i) {
Char c = str.GetChar(i);
if (c == _('<')) {
String tag = tag_at(str, i);
if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) ||
starts_with(tag, _("i")) || starts_with(tag, _("/i")) ||
starts_with(tag, _("u")) || starts_with(tag, _("/u")) ||
starts_with(tag, _("sym")) || starts_with(tag, _("/sym"))) {
if (starts_with(tag, _("b")) || starts_with(tag, _("/b")) ||
starts_with(tag, _("i")) || starts_with(tag, _("/i")) ||
starts_with(tag, _("sym")) || starts_with(tag, _("/sym")) ||
starts_with(tag, _("u")) || starts_with(tag, _("/u")) ||
starts_with(tag, _("strike")) || starts_with(tag, _("/strike"))) {
// optimize this tag
if (open_tags.find(_("<") + tag + _(">")) == String::npos) {
// we are not already inside this tag
+27 -6
View File
@@ -74,7 +74,11 @@ String fix_old_tags(const String&);
/// Does a string contain a tag at the given location?
/** Only matches if the tag ends one of ">-: " */
[[nodiscard]] bool is_tag(const String& str, size_t pos, const String& tag);
[[nodiscard]] bool is_tag(const String& str, size_t pos, const String& tag, bool strict = false);
/// Does a string contain a tag at the given location?
/** Matches <b <i <u <strike <font <size <color <sym or their anti tags */
[[nodiscard]] bool is_formatting_tag(const String& str, size_t pos);
/// Is the given range entirely contained in a given tagged block?
/** If so: return the start position of that tag, otherwise returns String::npos
@@ -83,9 +87,9 @@ String fix_old_tags(const String&);
* <tag><tag></tag>x</tag>
* the x is in_tag
*/
[[nodiscard]] size_t in_tag(const String& str, const String& tag, size_t start, size_t end);
[[nodiscard]] size_t in_tag(const String& str, const String& tag, size_t start, size_t end, bool strict = false);
/// Boolean returning version of the above
bool is_in_tag(const String& str, const String& tag, size_t start, size_t end);
bool is_in_tag(const String& str, const String& tag, size_t start, size_t end, bool strict = false);
/// Return the tag at the given position (without the <>)
String tag_at(const String& str, size_t pos);
@@ -120,16 +124,25 @@ String anti_tag(const String& tag);
[[nodiscard]] String::const_iterator find_close_tag(String::const_iterator it, String::const_iterator end);
/// Does a string contain a tag at the given location?
/** Only matches if the tag in the text ends one of ">-: "
/** Only matches if the tag in the text ends with one of ">-: "
* If strict is set to true then it must end with exactly ">"
* tag should be "<tag" or "</tag"
*/
[[nodiscard]] bool is_tag(String::const_iterator it, String::const_iterator end, const char* tag);
[[nodiscard]] bool is_tag(String::const_iterator it, String::const_iterator end, const char* tag, bool strict = false);
// Length of a string when not counting tags
// For example: untagged_length("<b>abc</b>",_) = 3
[[nodiscard]] size_t untagged_length(String::const_iterator it, String::const_iterator end);
// ----------------------------------------------------------------------------- : Cursor position
/// Take a tagged string and a position inside it,
/// return the position if the string was untagged.
size_t to_untagged_pos(const String& str, size_t pos);
/// Take a tagged string and a position inside the untagged version,
/// return the position in the tagged version.
size_t to_tagged_pos(const String& str, size_t pos, bool after_open=false, bool after_close=false);
/// Directions of cursor movement
enum Movement
@@ -158,6 +171,7 @@ size_t cursor_to_index(const String& str, size_t cursor, Movement dir = MOVE_MID
const Char UNTAG_ATOM = _('\2');
const Char UNTAG_SEP = _('\3');
const Char UNTAG_ATOM_KWPPH = _('\4');
const Char UNTAG_BULLET = _('\5');
/// Untag a string for use with cursors, <atom>...</atom> becomes a single character.
/** This string should only be used for cursor position calculations. */
@@ -179,6 +193,11 @@ size_t untagged_to_index(const String& str, size_t pos, bool inside, size_t star
size_t index_to_untagged(const String& str, size_t index);
// ----------------------------------------------------------------------------- : Global operations
/// Add a tag and its close tag around a string. Tag must be complete.
String wrap_tag(const String& str, const String& tag);
/// Add a tag and its close tag around a string but in the opposite order.
String anti_wrap_tag(const String& str, const String& tag);
/// Remove all instances of a tag and its close tag, but keep the contents.
/** tag doesn't have to be a complete tag, for example remove_tag(str, "<kw-")
@@ -244,7 +263,9 @@ String simplify_tagged(const String& str);
/// Simplify a tagged string by merging adjecent open/close tags
/** e.g. "<tag></tag>" --> ""
*
* @param all Merge all tags, if false only merges b,i,sym, and <tag></tag> pairs. But not </tag><tag>.
* @param all Merge all tags, if false only merges b,i,u,strike,color,size,font,sym, and <tag></tag> pairs.
* But not </tag><tag>, as this could lead to structural changes. For example, merging </li><li> would
* fuse two bullet points together.
*/
String simplify_tagged_merge(const String& str, bool all = false);
+3
View File
@@ -134,9 +134,12 @@ enum ChildMenuID {
ID_FORMAT_ITALIC,
ID_FORMAT_UNDERLINE,
ID_FORMAT_STRIKETHROUGH,
ID_FORMAT_COLOR,
ID_FORMAT_BULLETPOINT,
ID_FORMAT_SYMBOL,
ID_FORMAT_REMINDER,
ID_INSERT_SYMBOL,
ID_FORMAT_FONT,
// Spelling errors
ID_SPELLING_ADD_TO_DICT = 6301,