fix bullet indent, add clauses

This commit is contained in:
GenevensiS
2026-03-06 19:05:43 +01:00
parent 8945cd162c
commit 1210dcdf16
7 changed files with 147 additions and 89 deletions
+11 -5
View File
@@ -40,14 +40,19 @@ IMPLEMENT_REFLECTION(TextField) {
TextLayoutP dummy_layout() { TextLayoutP dummy_layout() {
auto layout = make_intrusive<TextLayout>(); auto layout = make_intrusive<TextLayout>();
auto line = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::LINE); auto line = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::LINE);
auto clause = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::CLAUSE);
auto paragraph = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::PARAGRAPH); auto paragraph = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::PARAGRAPH);
auto block = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::BLOCK); auto block = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::BLOCK);
clause ->lines.push_back(line);
paragraph->lines.push_back(line); paragraph->lines.push_back(line);
block->lines.push_back(line); block ->lines.push_back(line);
layout->lines.push_back(line); layout ->lines.push_back(line);
block->paragraphs.push_back(paragraph); paragraph->clauses.push_back(clause);
layout->paragraphs.push_back(paragraph); block ->clauses.push_back(clause);
layout->blocks.push_back(block); layout ->clauses.push_back(clause);
block ->paragraphs.push_back(paragraph);
layout ->paragraphs.push_back(paragraph);
layout ->blocks.push_back(block);
return layout; return layout;
} }
@@ -129,6 +134,7 @@ void LineLayout::reflect(GetMember& handler) const {
REFLECT_N("bottom", bottom()); REFLECT_N("bottom", bottom());
REFLECT_N("middle", middle()); REFLECT_N("middle", middle());
if (type > Type::LINE) REFLECT(lines); if (type > Type::LINE) REFLECT(lines);
if (type > Type::CLAUSE) REFLECT(clauses);
if (type > Type::PARAGRAPH) REFLECT(paragraphs); if (type > Type::PARAGRAPH) REFLECT(paragraphs);
if (type > Type::BLOCK) REFLECT(blocks); if (type > Type::BLOCK) REFLECT(blocks);
} }
+2 -2
View File
@@ -50,8 +50,8 @@ public:
class LineLayout : public IntrusivePtrVirtualBase { class LineLayout : public IntrusivePtrVirtualBase {
public: public:
double width, top, height; double width, top, height;
enum class Type { LINE, PARAGRAPH, BLOCK, ALL } type; enum class Type { LINE, CLAUSE, PARAGRAPH, BLOCK, ALL } type;
vector<LineLayoutP> lines, paragraphs, blocks; vector<LineLayoutP> lines, clauses, paragraphs, blocks;
LineLayout() {} LineLayout() {}
LineLayout(double width, double top, double height, Type type) : width(width), top(top), height(height), type(type) {} LineLayout(double width, double top, double height, Type type) : width(width), top(top), height(height), type(type) {}
+28 -17
View File
@@ -46,16 +46,20 @@ struct TextElementsFromString {
const TextStyle& style; const TextStyle& style;
Context& ctx; Context& ctx;
vector<TextClause>& clauses;
vector<TextParagraph>& paragraphs; vector<TextParagraph>& paragraphs;
TextElementsFromString(TextElements& out, const String& text, const TextStyle& style, Context& ctx) TextElementsFromString(TextElements& out, const String& text, const TextStyle& style, Context& ctx)
: style(style), ctx(ctx), paragraphs(out.paragraphs) : style(style), ctx(ctx), clauses(out.clauses), paragraphs(out.paragraphs)
{ {
out.start = 0; out.start = 0;
out.end = text.size(); out.end = text.size();
clauses.emplace_back();
clauses.back().start = 0;
paragraphs.emplace_back(); paragraphs.emplace_back();
paragraphs.back().start = 0; paragraphs.back().start = 0;
fromString(out.children, text, 0, text.size()); fromString(out.children, text, 0, text.size());
clauses.back().end = text.size();
paragraphs.back().end = text.size(); paragraphs.back().end = text.size();
} }
@@ -70,7 +74,7 @@ private:
if (text_start < pos) { if (text_start < pos) {
// text element before this tag? // text element before this tag?
addText(elements, text, text_start, pos); addText(elements, text, text_start, pos);
addParagraphs(text, text_start, pos); addClausesAndParagraphs(text, text_start, pos);
} }
// a (formatting) tag // a (formatting) tag
size_t tag_start = pos; size_t tag_start = pos;
@@ -175,7 +179,8 @@ private:
} else if (is_tag(text, tag_start, _("<bullet"))) { } else if (is_tag(text, tag_start, _("<bullet"))) {
// start of bullet point, set margin before bullet here, but only once // start of bullet point, set margin before bullet here, but only once
// subsequent times will align to this one // subsequent times will align to this one
if (paragraphs.back().margin_before_bullet == paragraphs.back().start) { if (!paragraphs.back().before_bullet_found) {
paragraphs.back().before_bullet_found = true;
paragraphs.back().margin_before_bullet = tag_start; paragraphs.back().margin_before_bullet = tag_start;
} }
if (li <= 0) { if (li <= 0) {
@@ -184,7 +189,8 @@ private:
} else if (is_tag(text, tag_start, _("</bullet"))) { } else if (is_tag(text, tag_start, _("</bullet"))) {
// end of bullet point, set margin after bullet here, but only once // end of bullet point, set margin after bullet here, but only once
// subsequent times will align to this one // subsequent times will align to this one
if (paragraphs.back().margin_after_bullet == paragraphs.back().start) { if (!paragraphs.back().after_bullet_found) {
paragraphs.back().after_bullet_found = true;
paragraphs.back().margin_after_bullet = pos; paragraphs.back().margin_after_bullet = pos;
} }
if (li <= 0) { if (li <= 0) {
@@ -205,9 +211,9 @@ private:
m.top += margins.back().top; m.top += margins.back().top;
} }
margins.emplace_back(m); margins.emplace_back(m);
paragraphs.back().margin_left = m.left; clauses.back().margin_left = m.left;
paragraphs.back().margin_right = m.right; clauses.back().margin_right = m.right;
paragraphs.back().margin_top = m.top; clauses.back().margin_top = m.top;
} }
} else if (is_tag(text, tag_start, _("</margin"))) { } else if (is_tag(text, tag_start, _("</margin"))) {
if (!margins.empty()) margins.pop_back(); if (!margins.empty()) margins.pop_back();
@@ -233,7 +239,7 @@ private:
if (text_start < end) { if (text_start < end) {
// remaining text at the end // remaining text at the end
addText(elements, text, text_start, end); addText(elements, text, text_start, end);
addParagraphs(text, text_start, end); addClausesAndParagraphs(text, text_start, end);
} }
} }
@@ -249,7 +255,8 @@ private:
// text, possibly mixed with symbols // text, possibly mixed with symbols
DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL; DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL;
LineBreak line_break = line > 0 ? LineBreak::LINE : LineBreak line_break = line > 0 ? LineBreak::LINE :
soft_line > 0 ? LineBreak::SOFT : LineBreak::HARD; soft_line > 0 ? LineBreak::SOFT :
LineBreak::HARD;
if (kwpph > 0 || param > 0) { if (kwpph > 0 || param > 0) {
// bracket the text // bracket the text
content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET; content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET;
@@ -285,22 +292,25 @@ private:
} }
} }
} }
// Find paragraph breaks in text // Find clause and paragraph breaks in text
void addParagraphs(const String& text, size_t start, size_t end) { void addClausesAndParagraphs(const String& text, size_t start, size_t end) {
if (line == 0 && soft_line > 0) return;
for (size_t i = start; i < end; ++i) { for (size_t i = start; i < end; ++i) {
wxUniChar c = text.GetChar(i); wxUniChar c = text.GetChar(i);
if (c == '\n') { if (c == '\n') {
clauses.back().end = i + 1;
clauses.emplace_back();
clauses.back().start = i + 1;
if (!margins.empty()) {
clauses.back().margin_left = margins.back().left;
clauses.back().margin_right = margins.back().right;
clauses.back().margin_top = margins.back().top;
}
if (line < 1 && soft_line > 0) continue;
paragraphs.back().end = i + 1; paragraphs.back().end = i + 1;
paragraphs.emplace_back(); paragraphs.emplace_back();
paragraphs.back().start = i + 1; paragraphs.back().start = i + 1;
paragraphs.back().margin_before_bullet = i + 1; paragraphs.back().margin_before_bullet = i + 1;
paragraphs.back().margin_after_bullet = i + 1; paragraphs.back().margin_after_bullet = i + 1;
if (!margins.empty()) {
paragraphs.back().margin_left = margins.back().left;
paragraphs.back().margin_right = margins.back().right;
paragraphs.back().margin_top = margins.back().top;
}
if (!aligns.empty()) { if (!aligns.empty()) {
paragraphs.back().alignment = aligns.back(); paragraphs.back().alignment = aligns.back();
} }
@@ -333,6 +343,7 @@ private:
void TextElements::clear() { void TextElements::clear() {
children.clear(); children.clear();
clauses.clear();
paragraphs.clear(); paragraphs.clear();
} }
+22 -11
View File
@@ -25,17 +25,18 @@ class SymbolFontRef;
/// Information on a linebreak /// Information on a linebreak
enum class LineBreak { enum class LineBreak {
NO, // no line break ever NO, // no line break ever
MAYBE, // break here when in "direction:vertical" mode MAYBE, // break here when in "direction:vertical" mode (break as WRAP)
SPACE, // optional line break (' ') SPACE, // optional line break, spacing as a soft break, ends a line, ( )
SOFT, // always a line break, spacing as a soft break, doesn't end paragraphs WRAP, // always a line break, spacing as a soft break, ends a line, ( )
HARD, // always a line break ('\n') SOFT, // always a line break, spacing as a soft break, ends a clause, (<soft-line>\n</soft-line>)
LINE, // line break with a separator line (<line>) HARD, // always a line break, spacing as a hard break, ends a paragraph, (\n)
LINE, // always a line break, spacing as a line break, ends a block, (<line>\n</line>), has separator
}; };
/// Information on a character in a TextElement /// Information on a character in a TextElement
struct CharInfo { struct CharInfo {
RealSize size; ///< Size of this character RealSize size; ///< Size of this character
LineBreak break_after : 16; ///< How/when to break after it? LineBreak break_after; ///< How/when to break after it?
bool soft : 1; ///< Is this a 'soft' character? soft characters are ignored for alignment bool soft : 1; ///< Is this a 'soft' character? soft characters are ignored for alignment
bool bullet : 1; ///< Is this a bullet point? bool bullet : 1; ///< Is this a bullet point?
@@ -146,14 +147,20 @@ public:
// ----------------------------------------------------------------------------- : TextElements // ----------------------------------------------------------------------------- : TextElements
class TextClause {
public:
double margin_left = 0., margin_right = 0., margin_top = 0.;
size_t start = String::npos, end = String::npos;
};
class TextParagraph { class TextParagraph {
public: public:
optional<Alignment> alignment; optional<Alignment> alignment;
double margin_left = 0., margin_right = 0.; bool before_bullet_found = false;
double margin_top = 0.; //, margin_bottom = 0.; // TODO: more margin options? bool after_bullet_found = false;
size_t start = String::npos, end = String::npos;
size_t margin_before_bullet = 0; // position of the bullet tag size_t margin_before_bullet = 0; // position of the bullet tag
size_t margin_after_bullet = 0; // position of the first character after the bullet tag size_t margin_after_bullet = 0; // position of the first character after the bullet tag
size_t start = String::npos, end = String::npos;
}; };
/// A list of text elements extracted from a string /// A list of text elements extracted from a string
@@ -161,8 +168,12 @@ class TextElements : public CompoundTextElement {
public: public:
TextElements() : CompoundTextElement(String::npos,String::npos) {} TextElements() : CompoundTextElement(String::npos,String::npos) {}
/// Information on the paragraphs/blocks in the string /// Information on the clauses/paragraphs/blocks in the string
/// Text segments separated by newlines are considered paragraphs /// Text segments separated by wrapping are considered lines
/// Text segments separated by soft newlines are considered clauses
/// Text segments separated by hard newlines are considered paragraphs
/// Text segments separated by line newlines are considered blocks
vector<TextClause> clauses;
vector<TextParagraph> paragraphs; vector<TextParagraph> paragraphs;
void clear(); void clear();
+61 -32
View File
@@ -21,9 +21,9 @@ struct TextViewer::Line {
LineBreak break_after; ///< Is there a saparator after this line? LineBreak break_after; ///< Is there a saparator after this line?
optional<Alignment> alignment; ///< Alignment of this line optional<Alignment> alignment; ///< Alignment of this line
bool justifying; ///< Is the text justified? Only true when *really* justifying. bool justifying; ///< Is the text justified? Only true when *really* justifying.
double margin_left; ///< Left margin including the margin tag and bullet point double margin_left_after_bullet; ///< Left margin including the margin tag and bullet point
double margin_left_before_bullet; ///< Left margin but just before the bullet point double margin_left_before_bullet; ///< Left margin including the margin tag but just before the bullet point
double margin_right; ///< Rightmargin double margin_right; ///< Right margin
bool bullet; ///< Does this line start with a bullet point? bool bullet; ///< Does this line start with a bullet point?
Line() Line()
@@ -357,7 +357,7 @@ void update_size(LineLayout& layout, TextViewer::Line const& l) {
TextLayoutP TextViewer::extractLayoutInfo() const { TextLayoutP TextViewer::extractLayoutInfo() const {
// store information about the content/layout // store information about the content/layout
TextLayoutP layout = make_intrusive<TextLayout>(); TextLayoutP layout = make_intrusive<TextLayout>();
LineLayoutP paragraph, block; LineLayoutP clause, paragraph, block;
for (auto const& l : lines) { for (auto const& l : lines) {
LineLayoutP line = make_intrusive<LineLayout>(l.width(), l.top, l.line_height, LineLayout::Type::LINE); LineLayoutP line = make_intrusive<LineLayout>(l.width(), l.top, l.line_height, LineLayout::Type::LINE);
if (!block) { if (!block) {
@@ -371,18 +371,29 @@ TextLayoutP TextViewer::extractLayoutInfo() const {
block->paragraphs.push_back(paragraph); block->paragraphs.push_back(paragraph);
layout->paragraphs.push_back(paragraph); layout->paragraphs.push_back(paragraph);
} }
if (!clause) {
clause = make_intrusive<LineLayout>(*line);
clause->type = LineLayout::Type::CLAUSE;
paragraph->clauses.push_back(clause);
block->clauses.push_back(clause);
layout->clauses.push_back(clause);
}
clause->lines.push_back(line);
paragraph->lines.push_back(line); paragraph->lines.push_back(line);
block->lines.push_back(line); block->lines.push_back(line);
layout->lines.push_back(line); layout->lines.push_back(line);
if (l.line_height > 0) { if (l.line_height > 0) {
update_size(*clause, l);
update_size(*paragraph, l); update_size(*paragraph, l);
update_size(*block, l); update_size(*block, l);
update_size(*layout, l); update_size(*layout, l);
} }
if (l.break_after == LineBreak::LINE) { if (l.break_after == LineBreak::LINE) {
paragraph = block = nullptr; clause = paragraph = block = nullptr;
} else if (l.break_after == LineBreak::HARD) { } else if (l.break_after == LineBreak::HARD) {
paragraph = nullptr; clause = paragraph = nullptr;
} else if (l.break_after == LineBreak::SOFT) {
clause = nullptr;
} }
} }
for (size_t i=0; i+1 < layout->blocks.size() ; ++i) { for (size_t i=0; i+1 < layout->blocks.size() ; ++i) {
@@ -547,7 +558,7 @@ void TextViewer::prepareLinesTryScales(RotatedDC& dc, const String& text, const
// Try to fit a blank line in the masked image, move down until it fits // Try to fit a blank line in the masked image, move down until it fits
RealSize TextViewer::fitLineWidth(Line& line, RotatedDC& dc, const TextStyle& style) const { RealSize TextViewer::fitLineWidth(Line& line, RotatedDC& dc, const TextStyle& style) const {
double margin_left = line.bullet ? line.margin_left_before_bullet : line.margin_left; double margin_left = line.bullet ? line.margin_left_before_bullet : line.margin_left_after_bullet;
RealSize line_size(margin_left + lineLeft(dc, style, line.top), 0); RealSize line_size(margin_left + lineLeft(dc, style, line.top), 0);
while (line.top < dc.getHeight() && line_size.width + 1 >= dc.getWidth() - style.padding_right - line.margin_right) { while (line.top < dc.getHeight() && line_size.width + 1 >= dc.getWidth() - style.padding_right - line.margin_right) {
// nothing fits on this line, move down one pixel // nothing fits on this line, move down one pixel
@@ -561,16 +572,20 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
// Try to layout the text at the current scale // Try to layout the text at the current scale
lines.clear(); lines.clear();
// The current "clause" in the input string
size_t i_clause = 0;
assert(elements.clauses.size() > 0);
// The current "paragraph" in the input string // The current "paragraph" in the input string
size_t i_para = 0; size_t i_para = 0;
assert(elements.paragraphs.size() > 0); assert(elements.paragraphs.size() > 0);
// first line // first line
Line line; Line line;
line.top = style.padding_top; line.top = style.padding_top + elements.clauses[0].margin_top;
line.margin_left = elements.paragraphs[0].margin_left; line.margin_left_after_bullet = elements.clauses[0].margin_left;
line.margin_left_before_bullet = elements.paragraphs[0].margin_left; line.margin_left_before_bullet = elements.clauses[0].margin_left;
line.margin_right = elements.paragraphs[0].margin_right; line.margin_right = elements.clauses[0].margin_right;
line.alignment = elements.paragraphs[0].alignment; line.alignment = elements.paragraphs[0].alignment;
// size of the line so far // size of the line so far
RealSize line_size = fitLineWidth(line, dc, style); RealSize line_size = fitLineWidth(line, dc, style);
@@ -584,14 +599,26 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
// For each character ... // For each character ...
for (size_t i = 0 ; i < chars.size() ; ++i) { for (size_t i = 0 ; i < chars.size() ; ++i) {
const CharInfo& c = chars[i]; const CharInfo& c = chars[i];
assert(i_clause < elements.clauses.size());
assert(i_para < elements.paragraphs.size()); assert(i_para < elements.paragraphs.size());
assert(c.size.width == 0 || elements.paragraphs[i_para].start <= i && i < elements.paragraphs[i_para].end); assert(c.size.width == 0 || elements.paragraphs[i_para].start <= i && i < elements.paragraphs[i_para].end);
// If we found the paragraph's bullet point, calculate the margins
if (i == elements.paragraphs[i_para].margin_after_bullet) {
for (size_t j = line.start; j < elements.paragraphs[i_para].margin_after_bullet; ++j) {
line.margin_left_after_bullet += chars[j].size.width;
}
}
if (i == elements.paragraphs[i_para].margin_before_bullet) {
for (size_t j = line.start; j < elements.paragraphs[i_para].margin_before_bullet; ++j) {
line.margin_left_before_bullet += chars[j].size.width;
}
}
// Should we break? // Should we break?
bool word_too_long = false; bool word_too_long = false;
bool break_now = false; bool break_now = false;
bool accept_word = false; // the current word should be added to the line bool accept_word = false; // the current word should be added to the line
bool hide_breaker = true; // hide the \n or _(' ') that caused a line break bool hide_breaker = true; // hide the \n or _(' ') that caused a line break
if (c.break_after == LineBreak::SOFT || c.break_after == LineBreak::HARD || c.break_after == LineBreak::LINE) { if (c.break_after >= LineBreak::WRAP) {
break_now = true; break_now = true;
accept_word = true; accept_word = true;
line.break_after = c.break_after; line.break_after = c.break_after;
@@ -602,7 +629,7 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
break_now = true; break_now = true;
accept_word = true; accept_word = true;
hide_breaker = false; hide_breaker = false;
line.break_after = LineBreak::SOFT; line.break_after = LineBreak::WRAP;
} }
// Add size of the character // Add size of the character
if (c.break_after != LineBreak::LINE) { if (c.break_after != LineBreak::LINE) {
@@ -612,12 +639,6 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
} }
positions_word.push_back(word_size.width); positions_word.push_back(word_size.width);
if (!c.soft) word_end_or_soft = i + 1; if (!c.soft) word_end_or_soft = i + 1;
if (i < elements.paragraphs[i_para].margin_after_bullet) {
line.margin_left += c.size.width; // character in left margin
if (i < elements.paragraphs[i_para].margin_before_bullet) {
line.margin_left_before_bullet += c.size.width;
}
}
// Did the word become too long? // Did the word become too long?
if (!break_now) { if (!break_now) {
double max_width = lineRight(dc, style, line.top) - line.margin_right; double max_width = lineRight(dc, style, line.top) - line.margin_right;
@@ -635,12 +656,12 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
accept_word = true; accept_word = true;
hide_breaker = false; hide_breaker = false;
word_too_long = true; word_too_long = true;
line.break_after = LineBreak::SOFT; line.break_after = LineBreak::WRAP;
} }
} else { } else {
// line would become too long, break before the current word // line would become too long, break before the current word
break_now = true; break_now = true;
line.break_after = LineBreak::SOFT; line.break_after = LineBreak::WRAP;
} }
} }
} }
@@ -688,20 +709,28 @@ bool TextViewer::prepareLinesAtScale(RotatedDC& dc, const vector<CharInfo>& char
line.top += line.line_height * line_height_multiplier; line.top += line.line_height * line_height_multiplier;
line.start = word_start; line.start = word_start;
line.positions.clear(); line.positions.clear();
if (line.break_after == LineBreak::LINE) line.line_height = 0; if (line.break_after >= LineBreak::SOFT) {
// end of clause
assert(elements.clauses[i_clause].end == i + 1);
assert(i_clause + 1 < elements.clauses.size());
if (i_clause + 1 < elements.clauses.size()) ++i_clause;
assert(elements.clauses[i_clause].start == i + 1);
line.margin_right = elements.clauses[i_clause].margin_right;
//if (line.start == elements.clauses[i_clause].start) {
line.top += elements.clauses[i_clause].margin_top;
//}
}
if (line.break_after >= LineBreak::HARD) { if (line.break_after >= LineBreak::HARD) {
// end of paragraph // end of paragraph
// look at next paragraph
assert(elements.paragraphs[i_para].end == i + 1); assert(elements.paragraphs[i_para].end == i + 1);
assert(i_para + 1 < elements.paragraphs.size()); assert(i_para + 1 < elements.paragraphs.size());
if (i_para+1 < elements.paragraphs.size()) ++i_para; if (i_para + 1 < elements.paragraphs.size()) ++i_para;
assert(elements.paragraphs[i_para].start == i + 1); assert(elements.paragraphs[i_para].start == i + 1);
line.margin_left = elements.paragraphs[i_para].margin_left; line.margin_left_after_bullet = elements.clauses[i_clause].margin_left;
line.margin_left_before_bullet = elements.paragraphs[i_para].margin_left; line.margin_left_before_bullet = elements.clauses[i_clause].margin_left;
line.margin_right = elements.paragraphs[i_para].margin_right;
line.top += elements.paragraphs[i_para].margin_top;
line.alignment = elements.paragraphs[i_para].alignment; line.alignment = elements.paragraphs[i_para].alignment;
} }
if (line.break_after == LineBreak::LINE) line.line_height = 0;
line.break_after = LineBreak::NO; line.break_after = LineBreak::NO;
// is the first visible character of the line a bullet point? // is the first visible character of the line a bullet point?
line.bullet = false; line.bullet = false;
@@ -811,7 +840,7 @@ void TextViewer::alignParagraph(size_t start_line, size_t end_line, const vector
double sum = 0; double sum = 0;
for (size_t li = start_line ; li < end_line ; ++li) { for (size_t li = start_line ; li < end_line ; ++li) {
const Line& l = lines[li]; const Line& l = lines[li];
if ((soft && l.break_after == LineBreak::SOFT) if ((soft && (l.break_after == LineBreak::SOFT || l.break_after == LineBreak::WRAP))
|| (hard && l.break_after == LineBreak::HARD) || (hard && l.break_after == LineBreak::HARD)
|| (line && l.break_after == LineBreak::LINE)) sum += l.line_height; || (line && l.break_after == LineBreak::LINE)) sum += l.line_height;
} }
@@ -824,7 +853,7 @@ void TextViewer::alignParagraph(size_t start_line, size_t end_line, const vector
Line& l = lines[li]; Line& l = lines[li];
l.top += add; l.top += add;
// adjust next line by.. // adjust next line by..
if ((soft && l.break_after == LineBreak::SOFT) if ((soft && (l.break_after == LineBreak::SOFT || l.break_after == LineBreak::WRAP))
|| (hard && l.break_after == LineBreak::HARD) || (hard && l.break_after == LineBreak::HARD)
|| (line && l.break_after == LineBreak::LINE)) add += to_add * l.line_height; || (line && l.break_after == LineBreak::LINE)) add += to_add * l.line_height;
} }
@@ -847,12 +876,12 @@ void TextViewer::alignParagraph(size_t start_line, size_t end_line, const vector
} }
void TextViewer::Line::alignHorizontal(const vector<CharInfo>& chars, const TextStyle& style, const RealRect& s) { void TextViewer::Line::alignHorizontal(const vector<CharInfo>& chars, const TextStyle& style, const RealRect& s) {
double margin_bullet = bullet ? margin_left_before_bullet : margin_left; double margin_bullet = bullet ? margin_left_before_bullet : margin_left_after_bullet;
double width = this->width() - margin_bullet; double width = this->width() - margin_bullet;
double target_width = s.width - margin_bullet - margin_right; double target_width = s.width - margin_bullet - margin_right;
Alignment alignment = this->alignment.value_or(style.alignment); Alignment alignment = this->alignment.value_or(style.alignment);
bool should_fill = (alignment & ALIGN_IF_OVERFLOW ? width > target_width : true) bool should_fill = (alignment & ALIGN_IF_OVERFLOW ? width > target_width : true)
&& (alignment & ALIGN_IF_SOFTBREAK ? break_after == LineBreak::SOFT || !style.field().multi_line : true); && (alignment & ALIGN_IF_SOFTBREAK ? break_after == LineBreak::WRAP || break_after == LineBreak::SOFT || !style.field().multi_line : true);
if ((alignment & ALIGN_JUSTIFY_ALL) && should_fill) { if ((alignment & ALIGN_JUSTIFY_ALL) && should_fill) {
// justify text, by characters // justify text, by characters
justifying = true; justifying = true;
+1
View File
@@ -591,6 +591,7 @@ boost::json::object mse_to_json(const StyleP& style) {
layoutv.emplace("content_width", wxString::Format(wxT("%.2f"), s->layout->width)); layoutv.emplace("content_width", wxString::Format(wxT("%.2f"), s->layout->width));
layoutv.emplace("content_height", wxString::Format(wxT("%.2f"), s->layout->height)); layoutv.emplace("content_height", wxString::Format(wxT("%.2f"), s->layout->height));
layoutv.emplace("content_lines", wxString::Format(wxT("%i"), s->layout->lines.size())); layoutv.emplace("content_lines", wxString::Format(wxT("%i"), s->layout->lines.size()));
layoutv.emplace("content_clauses", wxString::Format(wxT("%i"), s->layout->clauses.size()));
layoutv.emplace("content_paragraphs", wxString::Format(wxT("%i"), s->layout->paragraphs.size())); layoutv.emplace("content_paragraphs", wxString::Format(wxT("%i"), s->layout->paragraphs.size()));
layoutv.emplace("content_blocks", wxString::Format(wxT("%i"), s->layout->blocks.size())); layoutv.emplace("content_blocks", wxString::Format(wxT("%i"), s->layout->blocks.size()));
boost::json::array separatorsv; boost::json::array separatorsv;
+1 -1
View File
@@ -27,7 +27,7 @@ enum Alignment {
ALIGN_FILL = ALIGN_STRETCH | ALIGN_JUSTIFY_WORDS | ALIGN_JUSTIFY_ALL, ALIGN_FILL = ALIGN_STRETCH | ALIGN_JUSTIFY_WORDS | ALIGN_JUSTIFY_ALL,
// horizontal fill modifiers // horizontal fill modifiers
ALIGN_IF_OVERFLOW = 0x1000, // only fill if text_width > box_width ALIGN_IF_OVERFLOW = 0x1000, // only fill if text_width > box_width
ALIGN_IF_SOFTBREAK = 0x2000, // only fill before soft line breaks ALIGN_IF_SOFTBREAK = 0x2000, // only fill before soft line breaks and wraps
// vertical // vertical
ALIGN_TOP = 0x100, ALIGN_TOP = 0x100,
ALIGN_MIDDLE = 0x200, ALIGN_MIDDLE = 0x200,