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,15 +13,17 @@ When the field changes the underlying values are updated and vice versa. ...@@ -13,15 +13,17 @@ 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.
| @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. Soft separators are hidden by default and shown grayed when the field is selected.
--Examples-- --Examples--
......
...@@ -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();
......
...@@ -51,6 +51,9 @@ class SymbolFont : public Packaged { ...@@ -51,6 +51,9 @@ class SymbolFont : public Packaged {
/// 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,39 +138,80 @@ struct TextElementsFromString { ...@@ -132,39 +138,80 @@ 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 (e && e->end == (bracket ? pos + 1 : pos)) {
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>"
swap(e->content[e->content.size() - 2], e->content[e->content.size() - 1]);
} }
} else { }
// add a new element for this text if (text_start < end) {
addText(te, text, text_start, end, style, ctx);
}
}
private:
/// 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) {
String content = untag(text.substr(start, end - start));
assert(content.size() == end-start);
// use symbol font?
if (symbol > 0 && style.symbol_font.valid()) { if (symbol > 0 && style.symbol_font.valid()) {
e = new SymbolTextElement(text, pos, pos + 1, style.symbol_font, &ctx); te.elements.push_back(new_intrusive5<SymbolTextElement>(content, start, end, style.symbol_font, &ctx));
bracket = false; } else {
// text, possibly mixed with symbols
DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL;
LineBreak line_break = line > 0 ? BREAK_LINE :
soft_line > 0 ? BREAK_SOFT : BREAK_HARD;
if (kwpph > 0 || param > 0) {
// bracket the text
content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET;
start -= 1;
end += 1;
}
if (style.always_symbol && style.symbol_font.valid()) {
// mixed symbols/text, autodetected by symbol font
size_t text_pos = 0;
size_t pos = 0;
FontP font;
while (pos < end-start) {
if (size_t n = style.symbol_font.font->recognizePrefix(content,pos)) {
// at 'pos' there are n symbol font characters
if (text_pos < pos) {
// text before it?
if (!font) font = makeFont(style);
te.elements.push_back(new_intrusive6<FontTextElement>(content.substr(text_pos, pos-text_pos), start+text_pos, start+pos, font, what, line_break));
}
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 {
++pos;
}
}
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 { } else {
FontP font = style.font.make( 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) | (bold > 0 ? FONT_BOLD : FONT_NORMAL) |
(italic > 0 ? FONT_ITALIC : FONT_NORMAL) | (italic > 0 ? FONT_ITALIC : FONT_NORMAL) |
(soft > 0 ? FONT_SOFT : FONT_NORMAL) | (soft > 0 ? FONT_SOFT : FONT_NORMAL) |
...@@ -175,26 +222,6 @@ struct TextElementsFromString { ...@@ -175,26 +222,6 @@ struct TextElementsFromString {
param > 0 || param_ref > 0 param > 0 || param_ref > 0
? &param_colors[(param_id++) % param_colors_count] ? &param_colors[(param_id++) % param_colors_count]
: nullptr); : nullptr);
bracket = kwpph > 0 || param > 0;
e = new FontTextElement(
text,
bracket ? pos - 1 : pos,
bracket ? pos + 2 : pos + 1,
font,
soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL,
line > 0 ? BREAK_LINE :
soft_line > 0 ? BREAK_SOFT : BREAK_HARD);
}
if (bracket) {
e->content = String(LEFT_ANGLE_BRACKET) + c + RIGHT_ANGLE_BRACKET;
} else {
e->content = c;
}
te.elements.push_back(TextElementP(e));
}
pos += 1;
}
}
} }
}; };
......
...@@ -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.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); 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