mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Expose more information about text layout.
This commit is contained in:
+33
-6
@@ -97,11 +97,38 @@ void TextStyle::checkContentDependencies(Context& ctx, const Dependency& dep) co
|
||||
alignment.initDependencies(ctx, dep);
|
||||
}
|
||||
|
||||
template <typename T> void reflect_content(T& handler, const TextStyle& ts) {}
|
||||
template <> void reflect_content(GetMember& handler, const TextStyle& ts) {
|
||||
REFLECT_N("content_width", ts.content_width);
|
||||
REFLECT_N("content_height", ts.content_height);
|
||||
REFLECT_N("content_lines", ts.content_lines);
|
||||
template <typename T> void reflect_layout(T& handler, const TextStyle& ts) {}
|
||||
template <> void reflect_layout(GetMember& handler, const TextStyle& ts) {
|
||||
REFLECT(ts.layout);
|
||||
if (ts.layout) {
|
||||
REFLECT_N("content_width", ts.layout->width);
|
||||
REFLECT_N("content_height", ts.layout->height);
|
||||
REFLECT_N("content_lines", ts.layout->lines.size());
|
||||
} else {
|
||||
REFLECT_N("content_width", 0.);
|
||||
REFLECT_N("content_height", 0.);
|
||||
REFLECT_N("content_lines", 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <> void GetMember::handle(LineLayout const& obj) { obj.reflect(*this); }
|
||||
template <> void GetDefaultMember::handle(LineLayout const& obj) {}
|
||||
void LineLayout::reflect(GetMember& handler) const {
|
||||
REFLECT(width);
|
||||
REFLECT(top);
|
||||
REFLECT(height);
|
||||
REFLECT_N("bottom", bottom());
|
||||
REFLECT_N("middle", top + height/2);
|
||||
if (type > Type::LINE) REFLECT(lines);
|
||||
if (type > Type::PARAGRAPH) REFLECT(paragraphs);
|
||||
if (type > Type::BLOCK) REFLECT(blocks);
|
||||
}
|
||||
|
||||
template <> void GetMember::handle(TextLayout const& obj) { obj.reflect(*this); }
|
||||
template <> void GetDefaultMember::handle(TextLayout const& obj) {}
|
||||
void TextLayout::reflect(GetMember& handler) const {
|
||||
REFLECT_BASE(LineLayout);
|
||||
REFLECT(separators);
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(TextStyle) {
|
||||
@@ -127,7 +154,7 @@ IMPLEMENT_REFLECTION(TextStyle) {
|
||||
REFLECT(line_height_line_max);
|
||||
REFLECT(paragraph_height);
|
||||
REFLECT(direction);
|
||||
reflect_content(handler, *this);
|
||||
reflect_layout(handler, *this);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextValue
|
||||
|
||||
@@ -25,6 +25,8 @@ DECLARE_POINTER_TYPE(TextField);
|
||||
DECLARE_POINTER_TYPE(TextStyle);
|
||||
DECLARE_POINTER_TYPE(TextValue);
|
||||
DECLARE_POINTER_TYPE(TextBackground);
|
||||
DECLARE_POINTER_TYPE(TextLayout);
|
||||
DECLARE_POINTER_TYPE(LineLayout);
|
||||
|
||||
/// A field for values containing tagged text
|
||||
class TextField : public Field {
|
||||
@@ -44,6 +46,26 @@ class TextField : public Field {
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextStyle
|
||||
|
||||
// information coming from text rendering
|
||||
class LineLayout : public IntrusivePtrVirtualBase {
|
||||
public:
|
||||
double width, top, height;
|
||||
enum class Type { LINE, PARAGRAPH, BLOCK, ALL } type;
|
||||
vector<LineLayoutP> lines, paragraphs, blocks;
|
||||
|
||||
LineLayout() {}
|
||||
LineLayout(double width, double top, double height, Type type) : width(width), top(top), height(height), type(type) {}
|
||||
inline double bottom() const { return top+height; }
|
||||
void reflect(GetMember& gm) const;
|
||||
};
|
||||
|
||||
class TextLayout : public LineLayout {
|
||||
public:
|
||||
vector<double> separators;
|
||||
TextLayout() : LineLayout(0,0,0,Type::ALL) {}
|
||||
void reflect(GetMember& gm) const;
|
||||
};
|
||||
|
||||
/// The Style for a TextField
|
||||
class TextStyle : public Style {
|
||||
public:
|
||||
@@ -69,6 +91,7 @@ class TextStyle : public Style {
|
||||
paragraph_height; ///< Fixed height of paragraphs
|
||||
Direction direction; ///< In what direction is text layed out?
|
||||
// information from text rendering
|
||||
TextLayoutP layout;
|
||||
double content_width, content_height; ///< Size of the rendered text
|
||||
int content_lines; ///< Number of rendered lines
|
||||
|
||||
|
||||
+54
-14
@@ -344,24 +344,55 @@ void TextViewer::prepareElements(const String& text, const TextStyle& style, Con
|
||||
|
||||
// ----------------------------------------------------------------------------- : Layout
|
||||
|
||||
|
||||
void update_size(LineLayout& layout, TextViewer::Line const& l) {
|
||||
layout.width = max(layout.width, l.width());
|
||||
layout.height = max(layout.height, l.bottom() - layout.top);
|
||||
}
|
||||
|
||||
TextLayoutP TextViewer::extractLayoutInfo() const {
|
||||
// store information about the content/layout
|
||||
TextLayoutP layout = make_intrusive<TextLayout>();
|
||||
LineBreak last_break = BREAK_LINE;
|
||||
LineLayoutP paragraph, block;
|
||||
for (auto const& l : lines) {
|
||||
LineLayoutP line = make_intrusive<LineLayout>(l.width(), l.top, l.line_height, LineLayout::Type::LINE);
|
||||
if (!block) {
|
||||
block = make_intrusive<LineLayout>(*line);
|
||||
block->type = LineLayout::Type::BLOCK;
|
||||
layout->blocks.push_back(block);
|
||||
}
|
||||
if (!paragraph) {
|
||||
paragraph = make_intrusive<LineLayout>(*line);
|
||||
paragraph->type = LineLayout::Type::PARAGRAPH;
|
||||
block->paragraphs.push_back(paragraph);
|
||||
layout->paragraphs.push_back(paragraph);
|
||||
}
|
||||
paragraph->lines.push_back(line);
|
||||
block->lines.push_back(line);
|
||||
layout->lines.push_back(line);
|
||||
if (l.line_height > 0) {
|
||||
update_size(*paragraph, l);
|
||||
update_size(*block, l);
|
||||
update_size(*layout, l);
|
||||
}
|
||||
if (l.break_after == BREAK_LINE) {
|
||||
paragraph = block = nullptr;
|
||||
} else if (l.break_after == BREAK_HARD) {
|
||||
paragraph = nullptr;
|
||||
}
|
||||
}
|
||||
for (size_t i=0; i+1 < layout->blocks.size() ; ++i) {
|
||||
layout->separators.push_back((layout->blocks[i]->bottom() + layout->blocks[i]->top)/2);
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
void TextViewer::prepareLines(RotatedDC& dc, const String& text, TextStyle& style, Context& ctx) {
|
||||
vector<CharInfo> chars;
|
||||
prepareLinesTryScales(dc, text, style, chars);
|
||||
assert(!lines.empty());
|
||||
|
||||
// store information about the content/layout, allow this to change alignment
|
||||
style.content_width = 0;
|
||||
FOR_EACH(l, lines) {
|
||||
style.content_width = max(style.content_width, l.width());
|
||||
}
|
||||
style.content_height = 0;
|
||||
FOR_EACH_REVERSE(l, lines) {
|
||||
style.content_height = l.top + l.line_height;
|
||||
if (l.line_height) break; // not an empty line
|
||||
}
|
||||
style.content_lines = (int)lines.size();
|
||||
style.alignment.update(ctx); // allow this to affect the alignment
|
||||
|
||||
// no text, find a dummy height for the single line we have
|
||||
if (lines.size() == 1 && lines[0].width() < 0.0001) {
|
||||
if (style.always_symbol && style.symbol_font.valid()) {
|
||||
@@ -372,6 +403,12 @@ void TextViewer::prepareLines(RotatedDC& dc, const String& text, TextStyle& styl
|
||||
}
|
||||
}
|
||||
|
||||
// store information about the content/layout, allow this to change alignment
|
||||
if (style.alignment.isScripted()) {
|
||||
style.layout = extractLayoutInfo();
|
||||
style.alignment.update(ctx); // allow this to affect the alignment
|
||||
}
|
||||
|
||||
// align
|
||||
alignLines(dc, chars, style);
|
||||
|
||||
@@ -382,6 +419,9 @@ void TextViewer::prepareLines(RotatedDC& dc, const String& text, TextStyle& styl
|
||||
lines[0].line_height = h;
|
||||
lines[0].top -= h;
|
||||
}
|
||||
|
||||
// make layout available to scripts
|
||||
style.layout = extractLayoutInfo();
|
||||
}
|
||||
|
||||
// bound on max_scale, given that scale fits and produces the given lines
|
||||
@@ -465,7 +505,7 @@ void TextViewer::prepareLinesTryScales(RotatedDC& dc, const String& text, const
|
||||
max_scale = min(max_scale, bound_on_max_scale(dc,style,lines,scale));
|
||||
}
|
||||
|
||||
// The common case optimization fialed, try a binary search
|
||||
// The common case optimization failed, try a binary search
|
||||
// Invariant:
|
||||
// a. The text fits at min_scale (or we force it anyway)
|
||||
// b. but not at max_scale
|
||||
|
||||
@@ -145,6 +145,9 @@ class TextViewer {
|
||||
/// Align the lines of a single paragraph (a set of lines)
|
||||
void alignParagraph(size_t start_line, size_t end_line, const vector<CharInfo>& chars, const TextStyle& style, const RealRect& box);
|
||||
|
||||
/// Make layout info available to scripts
|
||||
TextLayoutP extractLayoutInfo() const;
|
||||
|
||||
/// Find the line the given index is on, returns the first line if the index is not found
|
||||
const Line& findLine(size_t index) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user