Commit 6cd57ab0 authored by twanvl's avatar twanvl

Added <prefix> and <suffix> tags;

Added prefix and suffix support to combined_editor;
'always symbol' now checks if the symbols are available in the symbol font;
Fixed parser bug in spec_sort;
A first information field is no longer used as set identification
parent 5da31292
...@@ -13,16 +13,18 @@ When the field changes the underlying values are updated and vice versa. ...@@ -13,16 +13,18 @@ When the field changes the underlying values are updated and vice versa.
Note: @forward_editor@ and @combined_editor@ are the same function. Note: @forward_editor@ and @combined_editor@ are the same function.
--Parameters-- --Parameters--
! Parameter Type Description ! Parameter Type Default Description
| @field@ [[type:value#text|text value]] Text value to edit | @field@ [[type:value#text|text value]] Text value to edit
| @field1@ [[type:value#text|text value]] Another text value to edit | @field1@ [[type:value#text|text value]] Another text value to edit
| @field2@ [[type:value#text|text value]] ''etc.'' | @field2@ [[type:value#text|text value]] ''etc.''
| @separator@ [[type:string]] Separator between field 1 and 2 | @separator@ [[type:string]] Separator between field 1 and 2
| @separator1@ [[type:string]] Multiple separators | @separator1@ [[type:string]] Multiple separators
| @separator2@ [[type:string]] Next separator, ''etc.'' | @separator2@ [[type:string]] Next separator, ''etc.''
| @hide when empty@ [[type:boolean]] Don't include separators if the entire value is empty. | @prefix@ [[type:string]] ''optional'' Prefix before the combined editor; like a separtor between the start and the first field.
| @soft before empty@ [[type:boolean]] Make separators 'soft' when the value folowing it is empty. | @suffix@ [[type:string]] ''optional'' Suffix after the combined editor; like a separtor between the last field and the end.
Soft separators are hidden by default and shown grayed when the field is selected. | @hide when empty@ [[type:boolean]] @false@ Don't include separators if the entire value is empty.
| @soft before empty@ [[type:boolean]] @false@ Make separators 'soft' when the value folowing it is empty.
Soft separators are hidden by default and shown grayed when the field is selected.
--Examples-- --Examples--
>card field: >card field:
......
...@@ -55,7 +55,8 @@ The rest of the properties depend on the type of [[type:field]] this style is fo ...@@ -55,7 +55,8 @@ The rest of the properties depend on the type of [[type:field]] this style is fo
! Type Property Type Default Description ! Type Property Type Default Description
| @"text"@ @font@ [[type:font]] ''Required'' Font to render the text. | @"text"@ @font@ [[type:font]] ''Required'' Font to render the text.
| ^^^ @symbol font@ [[type:symbol font]] Font to render symbols in the text (optional). | ^^^ @symbol font@ [[type:symbol font]] Font to render symbols in the text (optional).
| ^^^ @always symbol@ [[type:boolean]] @false@ Should all text be rendered with symbols?<br/>If set, @font@ is not needed. | ^^^ @always symbol@ [[type:boolean]] @false@ Should all text be rendered with symbols?<br/>
Text that is not supported by the symbol font is still rendered as normal text.
| ^^^ @allow formating@ [[type:boolean]] @true@ Is custom formating (bold, italic) allowed? | ^^^ @allow formating@ [[type:boolean]] @true@ Is custom formating (bold, italic) allowed?
| ^^^ @alignment@ [[type:scriptable]] [[type:alignment]] @top left@ Alignment of the text. | ^^^ @alignment@ [[type:scriptable]] [[type:alignment]] @top left@ Alignment of the text.
| ^^^ @angle@ [[type:scriptable]] [[type:int]] @0@ Rotation of the text inside the box, in degrees. | ^^^ @angle@ [[type:scriptable]] [[type:int]] @0@ Rotation of the text inside the box, in degrees.
...@@ -72,6 +73,9 @@ The rest of the properties depend on the type of [[type:field]] this style is fo ...@@ -72,6 +73,9 @@ The rest of the properties depend on the type of [[type:field]] this style is fo
A line height of @0@ means all lines are in the same position, @1@ is normal behaviour, @2@ skips a line, etc. A line height of @0@ means all lines are in the same position, @1@ is normal behaviour, @2@ skips a line, etc.
| ^^^ @line height hard@ [[type:double]] @1@ Multiplier for the line height of 'hard' line breaks. These are breaks caused by the enter key. | ^^^ @line height hard@ [[type:double]] @1@ Multiplier for the line height of 'hard' line breaks. These are breaks caused by the enter key.
| ^^^ @line height line@ [[type:double]] @1@ Multiplier for the line height of 'soft' line breaks. These are breaks caused by @"<line>\n</line>"@ tags. | ^^^ @line height line@ [[type:double]] @1@ Multiplier for the line height of 'soft' line breaks. These are breaks caused by @"<line>\n</line>"@ tags.
| ^^^ @line height soft max@ ^^^ ''disabled'' When there is still vertical room in the text box, increase the line heights to at most these values to spread the text more evenly.
| ^^^ @line height hard max@ ^^^ ^^^ ^^^
| ^^^ @line height line max@ ^^^ ^^^ ^^^
| ^^^ @mask@ [[type:filename]] ''none'' A mask that indicates where in the box text can be placed.<br/> | ^^^ @mask@ [[type:filename]] ''none'' A mask that indicates where in the box text can be placed.<br/>
Text is never put in black areas of the box:<br/> Text is never put in black areas of the box:<br/>
<img src="style-text-mask.png" alt=""/> <img src="style-text-mask.png" alt=""/>
......
...@@ -36,6 +36,8 @@ This is written as the character with code 1 in files. ...@@ -36,6 +36,8 @@ This is written as the character with code 1 in files.
| @"<sep>"@ A separator between fields. This tag is automatically inserted by the [[fun:combined_editor]] function.<br/> | @"<sep>"@ A separator between fields. This tag is automatically inserted by the [[fun:combined_editor]] function.<br/>
Inserting this tag manually will confuse that function!<br/> Inserting this tag manually will confuse that function!<br/>
This tag can never be selected, and its contents can not be edited. This tag can never be selected, and its contents can not be edited.
| @"<prefix>"@ At the beginning of a string, indicates a part that can not be selected. This tag is automatically inserted by the [[fun:combined_editor]] function.
| @"<suffix>"@ At the end of a string, indicates a part that can not be selected. This tag is automatically inserted by the [[fun:combined_editor]] function.
| @"<sep-soft>"@ Like @"<sep>"@, only hidden. This is inserted by [[fun:combined_editor]] | @"<sep-soft>"@ Like @"<sep>"@, only hidden. This is inserted by [[fun:combined_editor]]
| @"<soft>"@ Text who's width is ignored for alignment, similair to @"<sep-soft>"@, but not a separator. | @"<soft>"@ Text who's width is ignored for alignment, similair to @"<sep-soft>"@, but not a separator.
| @"<word-list-?>"@ Indicate that the text inside the tag should be selected from a [[type:word list]]. | @"<word-list-?>"@ Indicate that the text inside the tag should be selected from a [[type:word list]].
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <data/keyword.hpp> #include <data/keyword.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/field/text.hpp> // for 0.2.7 fix #include <data/field/text.hpp> // for 0.2.7 fix
#include <data/field/information.hpp>
#include <util/tagged_string.hpp> // for 0.2.7 fix #include <util/tagged_string.hpp> // for 0.2.7 fix
#include <util/order_cache.hpp> #include <util/order_cache.hpp>
#include <script/script_manager.hpp> #include <script/script_manager.hpp>
...@@ -106,12 +107,13 @@ String Set::identification() const { ...@@ -106,12 +107,13 @@ String Set::identification() const {
return v->toString(); return v->toString();
} }
} }
// otherwise the first field // otherwise the first non-information field
if (!data.empty()) { FOR_EACH_CONST(v, data) {
return data.at(0)->toString(); if (!dynamic_pointer_cast<InfoValue>(v)) {
} else { return v->toString();
return wxEmptyString; }
} }
return wxEmptyString;
} }
......
...@@ -215,6 +215,46 @@ next_symbol:; ...@@ -215,6 +215,46 @@ next_symbol:;
} }
} }
size_t SymbolFont::recognizePrefix(const String& text, size_t start) const {
size_t pos;
for (pos = start ; pos < text.size() ; ) {
// 1. check merged numbers
if (merge_numbers && pos + 1 < text.size()) {
size_t num_count = text.find_first_not_of(_("0123456789"), pos) - pos;
if (num_count >= 2) {
pos += num_count;
goto next_symbol;
}
}
// 2. check symbol list
FOR_EACH_CONST(sym, symbols) {
if (!sym->code.empty() && sym->enabled && is_substr(text, pos, sym->code)) { // symbol matches
pos += sym->code.size();
goto next_symbol; // continue two levels
}
}
// 3. draw multiple together as text?
if (!as_text.empty()) {
if (!as_text_r.IsValid()) {
as_text_r.Compile(_("^") + as_text, wxRE_ADVANCED);
}
if (as_text_r.IsValid()) {
if (as_text_r.Matches(text.substr(pos))) {
size_t start, len;
if (as_text_r.GetMatch(&start,&len) && start == 0) {
pos += len;
goto next_symbol;
}
}
}
}
// 4. failed
break;
next_symbol:;
}
return pos - start;
}
SymbolInFont* SymbolFont::defaultSymbol() const { SymbolInFont* SymbolFont::defaultSymbol() const {
FOR_EACH_CONST(sym, symbols) { FOR_EACH_CONST(sym, symbols) {
if (sym->code.empty() && sym->enabled) return sym.get(); if (sym->code.empty() && sym->enabled) return sym.get();
......
...@@ -47,10 +47,13 @@ class SymbolFont : public Packaged { ...@@ -47,10 +47,13 @@ class SymbolFont : public Packaged {
SymbolInFont* symbol; ///< Symbol to draw, if nullptr, use the default symbol and draw the text SymbolInFont* symbol; ///< Symbol to draw, if nullptr, use the default symbol and draw the text
}; };
typedef vector<DrawableSymbol> SplitSymbols; typedef vector<DrawableSymbol> SplitSymbols;
/// Split a string into separate symbols for drawing and for determining their size /// Split a string into separate symbols for drawing and for determining their size
void split(const String& text, SplitSymbols& out) const; void split(const String& text, SplitSymbols& out) const;
/// How many consecutive characters of the text, starting at start can be rendered with this symbol font?
size_t recognizePrefix(const String& text, size_t start) const;
/// Draw a piece of text /// Draw a piece of text
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text); void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
/// Get information on characters in a string /// Get information on characters in a string
......
...@@ -956,6 +956,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& ...@@ -956,6 +956,7 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
void TextValueEditor::tryAutoReplace() { void TextValueEditor::tryAutoReplace() {
size_t end = selection_start_i; size_t end = selection_start_i;
GameSettings& gs = settings.gameSettingsFor(*getSet().game); GameSettings& gs = settings.gameSettingsFor(*getSet().game);
if (!gs.use_auto_replace) return;
FOR_EACH(ar, gs.auto_replaces) { FOR_EACH(ar, gs.auto_replaces) {
if (ar->enabled && ar->match.size() <= end) { if (ar->enabled && ar->match.size() <= end) {
size_t start = end - ar->match.size(); size_t start = end - ar->match.size();
......
...@@ -75,6 +75,7 @@ struct TextElementsFromString { ...@@ -75,6 +75,7 @@ struct TextElementsFromString {
int soft, kwpph, param, line, soft_line; int soft, kwpph, param, line, soft_line;
int code, code_kw, code_string, param_ref, error; int code, code_kw, code_string, param_ref, error;
int param_id; int param_id;
/// put angle brackets around the text?
bool bracket; bool bracket;
TextElementsFromString() TextElementsFromString()
...@@ -86,10 +87,15 @@ struct TextElementsFromString { ...@@ -86,10 +87,15 @@ struct TextElementsFromString {
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) { void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
te.elements.clear(); te.elements.clear();
end = min(end, text.size()); end = min(end, text.size());
size_t text_start = start;
// for each character... // for each character...
for (size_t pos = start ; pos < end ; ) { for (size_t pos = start ; pos < end ; ) {
Char c = text.GetChar(pos); Char c = text.GetChar(pos);
if (c == _('<')) { if (c == _('<')) {
if (text_start < pos) {
// text element before this tag?
addText(te, text, text_start, pos, style, ctx);
}
size_t tag_start = pos; size_t tag_start = pos;
pos = skip_tag(text, tag_start); pos = skip_tag(text, tag_start);
if (is_substr(text, tag_start, _( "<b"))) bold += 1; if (is_substr(text, tag_start, _( "<b"))) bold += 1;
...@@ -132,70 +138,91 @@ struct TextElementsFromString { ...@@ -132,70 +138,91 @@ struct TextElementsFromString {
else if (is_substr(text, tag_start, _("<atom"))) { else if (is_substr(text, tag_start, _("<atom"))) {
// 'atomic' indicator // 'atomic' indicator
size_t end_tag = min(end, match_close_tag(text, tag_start)); size_t end_tag = min(end, match_close_tag(text, tag_start));
intrusive_ptr<AtomTextElement> e(new AtomTextElement(text, pos, end_tag)); intrusive_ptr<AtomTextElement> e(new AtomTextElement(pos, end_tag));
fromString(e->elements, text, pos, end_tag, style, ctx); fromString(e->elements, text, pos, end_tag, style, ctx);
te.elements.push_back(e); te.elements.push_back(e);
pos = skip_tag(text, end_tag); pos = skip_tag(text, end_tag);
} else if (is_substr(text, tag_start, _( "<error"))) { } else if (is_substr(text, tag_start, _( "<error"))) {
// error indicator // error indicator
size_t end_tag = min(end, match_close_tag(text, tag_start)); size_t end_tag = min(end, match_close_tag(text, tag_start));
intrusive_ptr<ErrorTextElement> e(new ErrorTextElement(text, pos, end_tag)); intrusive_ptr<ErrorTextElement> e(new ErrorTextElement(pos, end_tag));
fromString(e->elements, text, pos, end_tag, style, ctx); fromString(e->elements, text, pos, end_tag, style, ctx);
te.elements.push_back(e); te.elements.push_back(e);
pos = skip_tag(text, end_tag); pos = skip_tag(text, end_tag);
} else { } else {
// ignore other tags // ignore other tags
} }
text_start = pos;
} else { } else {
c = untag_char(c); // unescape pos += 1;
// A character of normal text, add to the last text element (if possible) }
SimpleTextElement* e = nullptr; }
if (!te.elements.empty()) e = dynamic_cast<SimpleTextElement*>(te.elements.back().get()); if (text_start < end) {
if (e && e->end == (bracket ? pos + 1 : pos)) { addText(te, text, text_start, end, style, ctx);
e->end = bracket ? pos + 2 : pos + 1; // just move the end, no need to make a new element }
e->content += c; }
if (bracket) {
// content is "<somethin>g" should be "<something>" private:
swap(e->content[e->content.size() - 2], e->content[e->content.size() - 1]); /// Create a text element for a piece of text
} void addText(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
} else { String content = untag(text.substr(start, end - start));
// add a new element for this text assert(content.size() == end-start);
if (symbol > 0 && style.symbol_font.valid()) { // use symbol font?
e = new SymbolTextElement(text, pos, pos + 1, style.symbol_font, &ctx); if (symbol > 0 && style.symbol_font.valid()) {
bracket = false; te.elements.push_back(new_intrusive5<SymbolTextElement>(content, start, end, style.symbol_font, &ctx));
} else { } else {
FontP font = style.font.make( // text, possibly mixed with symbols
(bold > 0 ? FONT_BOLD : FONT_NORMAL) | DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL;
(italic > 0 ? FONT_ITALIC : FONT_NORMAL) | LineBreak line_break = line > 0 ? BREAK_LINE :
(soft > 0 ? FONT_SOFT : FONT_NORMAL) | soft_line > 0 ? BREAK_SOFT : BREAK_HARD;
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) | if (kwpph > 0 || param > 0) {
(code > 0 ? FONT_CODE : FONT_NORMAL) | // bracket the text
(code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) | content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET;
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL), start -= 1;
param > 0 || param_ref > 0 end += 1;
? &param_colors[(param_id++) % param_colors_count] }
: nullptr); if (style.always_symbol && style.symbol_font.valid()) {
bracket = kwpph > 0 || param > 0; // mixed symbols/text, autodetected by symbol font
e = new FontTextElement( size_t text_pos = 0;
text, size_t pos = 0;
bracket ? pos - 1 : pos, FontP font;
bracket ? pos + 2 : pos + 1, while (pos < end-start) {
font, if (size_t n = style.symbol_font.font->recognizePrefix(content,pos)) {
soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL, // at 'pos' there are n symbol font characters
line > 0 ? BREAK_LINE : if (text_pos < pos) {
soft_line > 0 ? BREAK_SOFT : BREAK_HARD); // text before it?
} if (!font) font = makeFont(style);
if (bracket) { te.elements.push_back(new_intrusive6<FontTextElement>(content.substr(text_pos, pos-text_pos), start+text_pos, start+pos, font, what, line_break));
e->content = String(LEFT_ANGLE_BRACKET) + c + RIGHT_ANGLE_BRACKET; }
te.elements.push_back(new_intrusive5<SymbolTextElement>(content.substr(pos,n), start+pos, start+pos+n, style.symbol_font, &ctx));
text_pos = pos += n;
} else { } else {
e->content = c; ++pos;
} }
te.elements.push_back(TextElementP(e));
} }
pos += 1; if (text_pos < pos) {
if (!font) font = makeFont(style);
te.elements.push_back(new_intrusive6<FontTextElement>(content.substr(text_pos), start+text_pos, end, font, what, line_break));
}
} else {
te.elements.push_back(new_intrusive6<FontTextElement>(content, start, end, makeFont(style), what, line_break));
} }
} }
} }
FontP makeFont(const TextStyle& style) {
return style.font.make(
(bold > 0 ? FONT_BOLD : FONT_NORMAL) |
(italic > 0 ? FONT_ITALIC : FONT_NORMAL) |
(soft > 0 ? FONT_SOFT : FONT_NORMAL) |
(kwpph > 0 ? FONT_SOFT : FONT_NORMAL) |
(code > 0 ? FONT_CODE : FONT_NORMAL) |
(code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) |
(code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL),
param > 0 || param_ref > 0
? &param_colors[(param_id++) % param_colors_count]
: nullptr);
}
}; };
void TextElements::fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) { void TextElements::fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
......
...@@ -57,12 +57,10 @@ struct CharInfo { ...@@ -57,12 +57,10 @@ struct CharInfo {
/// A section of text that can be rendered using a TextViewer /// A section of text that can be rendered using a TextViewer
class TextElement : public IntrusivePtrBase<TextElement> { class TextElement : public IntrusivePtrBase<TextElement> {
public: public:
/// The text of which a subsection is drawn
String text;
/// What section of the input string is this element? /// What section of the input string is this element?
size_t start, end; size_t start, end;
inline TextElement(const String& text, size_t start ,size_t end) : text(text), start(start), end(end) {} inline TextElement(size_t start ,size_t end) : start(start), end(end) {}
virtual ~TextElement() {} virtual ~TextElement() {}
/// Draw a subsection section of the text in the given rectangle /// Draw a subsection section of the text in the given rectangle
...@@ -109,8 +107,8 @@ class TextElements { ...@@ -109,8 +107,8 @@ class TextElements {
/// A text element that just shows text /// A text element that just shows text
class SimpleTextElement : public TextElement { class SimpleTextElement : public TextElement {
public: public:
SimpleTextElement(const String& text, size_t start, size_t end) SimpleTextElement(const String& content, size_t start, size_t end)
: TextElement(text, start, end), content(text.substr(start,end-start)) : TextElement(start, end), content(content)
{} {}
String content; ///< Text to show String content; ///< Text to show
}; };
...@@ -118,8 +116,8 @@ class SimpleTextElement : public TextElement { ...@@ -118,8 +116,8 @@ class SimpleTextElement : public TextElement {
/// A text element that uses a normal font /// A text element that uses a normal font
class FontTextElement : public SimpleTextElement { class FontTextElement : public SimpleTextElement {
public: public:
FontTextElement(const String& text, size_t start, size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style) FontTextElement(const String& content, size_t start, size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style)
: SimpleTextElement(text, start, end) : SimpleTextElement(content, start, end)
, font(font), draw_as(draw_as), break_style(break_style) , font(font), draw_as(draw_as), break_style(break_style)
{} {}
...@@ -136,8 +134,8 @@ class FontTextElement : public SimpleTextElement { ...@@ -136,8 +134,8 @@ class FontTextElement : public SimpleTextElement {
/// A text element that uses a symbol font /// A text element that uses a symbol font
class SymbolTextElement : public SimpleTextElement { class SymbolTextElement : public SimpleTextElement {
public: public:
SymbolTextElement(const String& text, size_t start, size_t end, const SymbolFontRef& font, Context* ctx) SymbolTextElement(const String& content, size_t start, size_t end, const SymbolFontRef& font, Context* ctx)
: SimpleTextElement(text, start, end) : SimpleTextElement(content, start, end)
, font(font), ctx(*ctx) , font(font), ctx(*ctx)
{} {}
...@@ -155,7 +153,7 @@ class SymbolTextElement : public SimpleTextElement { ...@@ -155,7 +153,7 @@ class SymbolTextElement : public SimpleTextElement {
/// A TextElement consisting of sub elements /// A TextElement consisting of sub elements
class CompoundTextElement : public TextElement { class CompoundTextElement : public TextElement {
public: public:
CompoundTextElement(const String& text, size_t start, size_t end) : TextElement(text, start, end) {} CompoundTextElement(size_t start, size_t end) : TextElement(start, end) {}
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const; virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
...@@ -168,7 +166,7 @@ class CompoundTextElement : public TextElement { ...@@ -168,7 +166,7 @@ class CompoundTextElement : public TextElement {
/// A TextElement drawn using a grey background /// A TextElement drawn using a grey background
class AtomTextElement : public CompoundTextElement { class AtomTextElement : public CompoundTextElement {
public: public:
AtomTextElement(const String& text, size_t start, size_t end) : CompoundTextElement(text, start, end) {} AtomTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
}; };
...@@ -176,23 +174,10 @@ class AtomTextElement : public CompoundTextElement { ...@@ -176,23 +174,10 @@ class AtomTextElement : public CompoundTextElement {
/// A TextElement drawn using a red wavy underline /// A TextElement drawn using a red wavy underline
class ErrorTextElement : public CompoundTextElement { class ErrorTextElement : public CompoundTextElement {
public: public:
ErrorTextElement(const String& text, size_t start, size_t end) : CompoundTextElement(text, start, end) {} ErrorTextElement(size_t start, size_t end) : CompoundTextElement(start, end) {}
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
}; };
// ----------------------------------------------------------------------------- : Other text elements
/*
/// A text element that displays a horizontal separator line
class HorizontalLineTextElement : public TextElement {
public:
HorizontalLineTextElement(const String& text, size_t start ,size_t end) : TextElement(text, start, end) {}
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
virtual double minScale() const;
};
*/
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -328,12 +328,7 @@ void TextViewer::setExactScrollPosition(double pos) { ...@@ -328,12 +328,7 @@ void TextViewer::setExactScrollPosition(double pos) {
// ----------------------------------------------------------------------------- : Elements // ----------------------------------------------------------------------------- : Elements
void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) { void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) {
if (style.always_symbol) { elements.fromString(text, 0, text.size(), style, ctx);
elements.elements.clear();
elements.elements.push_back(new_intrusive5<SymbolTextElement>(text, 0, text.size(), style.symbol_font, &ctx));
} else {
elements.fromString(text, 0, text.size(), style, ctx);
}
} }
......
...@@ -52,10 +52,21 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) { ...@@ -52,10 +52,21 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) {
if (separators.size() < values.size() - 1) { if (separators.size() < values.size() - 1) {
throw ScriptError(String::Format(_("Not enough separators for combine_editor, expected %d"), values.size()-1)); throw ScriptError(String::Format(_("Not enough separators for combine_editor, expected %d"), values.size()-1));
} }
// split the value // the value
SCRIPT_PARAM(String, value); SCRIPT_PARAM(String, value);
// remove suffix/prefix
SCRIPT_OPTIONAL_PARAM_(String, prefix);
SCRIPT_OPTIONAL_PARAM_(String, suffix);
if (is_substr(value,0,_("<prefix"))) {
value = value.substr(min(value.size(), match_close_tag_end(value, 0)));
}
size_t pos = value.rfind(_("<suffix"));
if (pos != String::npos && match_close_tag_end(value,pos) >= value.size()) {
value = value.substr(0, pos);
}
// split the value
vector<pair<String,bool> > value_parts; // (value part, is empty) vector<pair<String,bool> > value_parts; // (value part, is empty)
size_t pos = value.find(_("<sep")); pos = value.find(_("<sep"));
while (pos != String::npos) { while (pos != String::npos) {
String part = value.substr(0, pos); String part = value.substr(0, pos);
value_parts.push_back(make_pair(part, false)); value_parts.push_back(make_pair(part, false));
...@@ -81,18 +92,48 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) { ...@@ -81,18 +92,48 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) {
// recombine the parts // recombine the parts
String new_value = value_parts.front().first; String new_value = value_parts.front().first;
bool new_value_empty = value_parts.front().second; bool new_value_empty = value_parts.front().second;
size_t size_before_last = 0;
for (size_t i = 1 ; i < value_parts.size() ; ++i) { for (size_t i = 1 ; i < value_parts.size() ; ++i) {
size_before_last = new_value.size();
if (value_parts[i].second && new_value_empty && hide_when_empty) { if (value_parts[i].second && new_value_empty && hide_when_empty) {
// no separator // no separator
} else if (value_parts[i].second && soft_before_empty) { } else if (value_parts[i].second && soft_before_empty) {
// soft separator // soft separator
new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>"); new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>");
new_value_empty = false;
} else { } else {
// normal separator // normal separator
new_value += _("<sep>") + separators[i - 1] + _("</sep>"); new_value += _("<sep>") + separators[i - 1] + _("</sep>");
new_value_empty = false;
} }
new_value += value_parts[i].first; new_value += value_parts[i].first;
} }
if (!new_value_empty || !hide_when_empty) {
if (!suffix.empty()) {
if (is_substr(new_value, size_before_last, _("<sep-soft>")) && value_parts.size() >= 2) {
// If the value ends in a soft separator, we have this situation:
// [blah]<sep-soft>ABC</sep-soft><suffix>XYZ</suffix>
// This renderes as:
// [blah] XYZ
// Which looks bad, so instead change the text to
// [blah]<sep>XYZ<soft>ABC</soft></sep>
// Which might be slightly incorrect, but soft text doesn't matter anyway.
size_t after = min(new_value.size(), match_close_tag_end(new_value, size_before_last));
new_value = new_value.substr(0, size_before_last)
+ _("<sep>")
+ suffix
+ _("<soft>")
+ separators[value_parts.size() - 2]
+ _("</soft></sep>")
+ new_value.substr(after);
} else {
new_value += _("<suffix>") + suffix + _("</suffix>");
}
}
if (!prefix.empty()) {
new_value = _("<prefix>") + prefix + _("</prefix>") + new_value;
}
}
SCRIPT_RETURN(new_value); SCRIPT_RETURN(new_value);
} }
......
...@@ -239,7 +239,14 @@ void in_place_sort(const String& spec, String& input, String& ret) { ...@@ -239,7 +239,14 @@ void in_place_sort(const String& spec, String& input, String& ret) {
String spec_sort(const String& spec, String& input, String& ret) { String spec_sort(const String& spec, String& input, String& ret) {
SpecIterator it(spec); SpecIterator it(spec);
while(it.nextUntil(0)) { while(it.nextUntil(0)) {
if (it.value == _('<')) { // keep only a single copy if (it.escaped) { // single character, escaped
FOR_EACH(d, input) {
if (d == it.value) {
ret += d;
d = REMOVED;
}
}
} else if (it.value == _('<')) { // keep only a single copy
while (it.nextUntil(_('>'))) { while (it.nextUntil(_('>'))) {
size_t pos = input.find_first_of(it.value); size_t pos = input.find_first_of(it.value);
if (pos != String::npos) { if (pos != String::npos) {
......
...@@ -249,6 +249,13 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) { ...@@ -249,6 +249,13 @@ size_t index_to_cursor(const String& str, size_t index, Movement dir) {
} }
} }
i = after; i = after;
} else if (i == 0 && is_substr(str, i, _("<prefix"))) {
// prefix at start of string, skip contents
i = match_close_tag_end(str, i);
has_width = false;
} else if (is_substr(str, i, _("<suffix")) && match_close_tag_end(str,i) >= str.size()) {
// suffix at end of string
break;
} else { } else {
i = skip_tag(str, i); i = skip_tag(str, i);
has_width = false; has_width = false;
...@@ -268,7 +275,8 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size ...@@ -268,7 +275,8 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size
start = end = 0; start = end = 0;
size_t cur = 0; size_t cur = 0;
size_t i = 0; size_t i = 0;
while (cur <= cursor && i < str.size()) { size_t size = str.size(); // can be changed by <suffix> tags
while (cur <= cursor && i < size) {
Char c = str.GetChar(i); Char c = str.GetChar(i);
bool has_width = true; bool has_width = true;
if (c == _('<')) { if (c == _('<')) {
...@@ -278,6 +286,14 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size ...@@ -278,6 +286,14 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size
if (cur >= cursor) { ++i; break; } if (cur >= cursor) { ++i; break; }
// skip tag contents, tag counts as a single 'character' // skip tag contents, tag counts as a single 'character'
i = match_close_tag_end(str, i); i = match_close_tag_end(str, i);
} else if (i == 0 && is_substr(str, i, _("<prefix"))) {
// prefix at start of string, skip contents, index never before
start = i = match_close_tag_end(str,i);
has_width = false;
} else if (is_substr(str, i, _("<suffix")) && match_close_tag_end(str,i) >= str.size()) {
// suffix at start of string, skip contents
size = i;
has_width = false;
} else { } else {
i = skip_tag(str, i); i = skip_tag(str, i);
has_width = false; has_width = false;
...@@ -290,8 +306,8 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size ...@@ -290,8 +306,8 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& start, size
if (cur == cursor) start = i; if (cur == cursor) start = i;
} }
} }
end = min(i, str.size()); end = min(i, size);
if (cur < cursor) start = end = str.size(); if (cur < cursor) start = end = size;
if (end <= start) end = start + 1; if (end <= start) end = start + 1;
} }
...@@ -336,6 +352,12 @@ String untag_for_cursor(const String& str) { ...@@ -336,6 +352,12 @@ String untag_for_cursor(const String& str) {
} else if (is_substr(str, i, _("<sep"))) { } else if (is_substr(str, i, _("<sep"))) {
i = match_close_tag_end(str, i); i = match_close_tag_end(str, i);
ret += _('\3'); // use a random character here ret += _('\3'); // use a random character here
} 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);
} else if (is_substr(str, i, _("<suffix")) && match_close_tag_end(str,i) >= str.size()) {
// suffix at start of string, skip contents
i = str.size();
} else { } else {
i = skip_tag(str, i); i = skip_tag(str, i);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment