mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 21:06:59 -04:00
Basic text rendering working;
Added Font (done) and SymbolFont (skeleton); Added styling to set; Added CountourMap; Some script fixes git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@73 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <render/text/element.hpp>
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <data/field/text.hpp>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(TextElementP);
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextElements
|
||||
|
||||
void TextElements::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
size_t start_ = max(start, e->start);
|
||||
size_t end_ = min(end, e->end);
|
||||
if (start_ < end_) {
|
||||
e->draw(dc, scale, rect, what, start_, end_);
|
||||
}
|
||||
if (end <= e->end) return; // nothing can be after this
|
||||
}
|
||||
}
|
||||
|
||||
void TextElements::getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector<CharInfo>& out) const {
|
||||
FOR_EACH_CONST(e, elements) {
|
||||
// characters before this element, after the previous
|
||||
for (size_t i = start ; i < e->start ; ++i) {
|
||||
out.push_back(CharInfo(RealSize(0,0), BREAK_NO));
|
||||
}
|
||||
e->getCharInfo(dc, scale, out);
|
||||
start = min(end, e->end);
|
||||
}
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
out.push_back(CharInfo(RealSize(0,0), BREAK_NO));
|
||||
}
|
||||
}
|
||||
|
||||
/*//@@
|
||||
RealSize TextElements::charSize(const Rotation& rot, double scale, size_t index) const {
|
||||
vector<TextElementP>::const_iterator e = findByIndex(index);
|
||||
if (e != elements.end()) {
|
||||
return (*e)->charSize(rot, scale, index);
|
||||
} else {
|
||||
return RealSize(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ends_before_index(const TextElementP& e, size_t index) {
|
||||
return index < e->end;
|
||||
}
|
||||
|
||||
vector<TextElementP>::const_iterator TextElements::findByIndex(size_t index) const {
|
||||
// Note: slightly abusing lower_bound, since typeof(index) != elements.element_type
|
||||
vector<TextElementP>::const_iterator it = lower_bound(elements.begin(), elements.end(), index, ends_before_index);
|
||||
if ((*it)->start <= index && (*it)->end > index) return it;
|
||||
else return elements.end();
|
||||
}*/
|
||||
|
||||
// Helper class for TextElements::fromString, to allow persistent formating state accross recusive calls
|
||||
struct TextElementsFromString {
|
||||
// What formatting is enabled?
|
||||
int bold, italic, symbol;
|
||||
int soft, kwpph;
|
||||
|
||||
TextElementsFromString()
|
||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0) {}
|
||||
|
||||
// read TextElements from a string
|
||||
void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
te.elements.clear();
|
||||
// for each character...
|
||||
for (size_t pos = start ; pos < end ; ) {
|
||||
Char c = text.GetChar(pos);
|
||||
if (c == _('<')) {
|
||||
size_t tag_start = pos;
|
||||
pos = skip_tag(text, tag_start);
|
||||
if (is_substr(text, tag_start, _( "<b"))) bold += 1;
|
||||
else if (is_substr(text, tag_start, _("</b"))) bold -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<i"))) italic += 1;
|
||||
else if (is_substr(text, tag_start, _("</i"))) italic -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sym"))) symbol += 1;
|
||||
else if (is_substr(text, tag_start, _("</sym"))) symbol -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<sep-soft"))) soft += 1;
|
||||
else if (is_substr(text, tag_start, _("</sep-soft"))) soft -= 1;
|
||||
else if (is_substr(text, tag_start, _( "<atom-kwpph"))) kwpph += 1;
|
||||
else if (is_substr(text, tag_start, _("</atom-kwpph"))) kwpph -= 1;
|
||||
else if (is_substr(text, tag_start, _("<line"))) {
|
||||
// horizontal line
|
||||
te.elements.push_back(new_shared3<HorizontalLineTextElement>(text, tag_start, pos));
|
||||
/* } else if (is_substr(text, start, _("<error"))) {
|
||||
// underline with wavy 'error' indicator
|
||||
size_t end = match_close_tag(text, tag_start);
|
||||
shared_ptr<ErrorTextElement> e(new ErrorTextElement(text, pos, end));
|
||||
fromString(e->elements, text, pos, end, style, ctx);
|
||||
pos = skip_tag(text, end);
|
||||
} else if (is_substr(text, start, _("<atom"))) {
|
||||
// 'atomic' indicator
|
||||
size_t end = match_close_tag(text, tag_start);
|
||||
shared_ptr<AtomTextElement> e(new AtomTextElement(text, pos, end));
|
||||
fromString(e->elements, text, pos, end, style, ctx);
|
||||
pos = skip_tag(text, end);
|
||||
*/ } else {
|
||||
// ignore other tags
|
||||
}
|
||||
} else {
|
||||
// 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 == pos) {
|
||||
e->end = pos + 1; // just move the end, no need to make a new element
|
||||
} else {
|
||||
// add a new element for this text
|
||||
if (symbol > 0) {
|
||||
te.elements.push_back(new_shared3<SymbolTextElement>(text, pos, pos + 1));
|
||||
} else {
|
||||
te.elements.push_back(new_shared4<FontTextElement> (text, pos, pos + 1, style.font.make(bold > 0, italic > 0)));
|
||||
}
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void TextElements::fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) {
|
||||
TextElementsFromString f;
|
||||
f.fromString(*this, text, start, end, style, ctx);
|
||||
}
|
||||
/*
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
|
||||
void CompoundTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
elements.draw(dc, scale, rect, what, start, end);
|
||||
}
|
||||
RealSize CompoundTextElement::charSize(RotatedDC& dc, double scale, size_t index) const {
|
||||
return elements.charSize(rot, scale, index);
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,186 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_RENDER_TEXT_ELEMENT
|
||||
#define HEADER_RENDER_TEXT_ELEMENT
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <util/rotation.hpp>
|
||||
#include <util/real_point.hpp>
|
||||
|
||||
DECLARE_POINTER_TYPE(TextElement);
|
||||
DECLARE_POINTER_TYPE(Font);
|
||||
class TextStyle;
|
||||
class Context;
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextElement
|
||||
|
||||
/// What should be drawn?
|
||||
enum DrawWhat
|
||||
{ DRAW_NOTHING = 0x00
|
||||
, DRAW_NORMAL = 0x01 // draw normal things, like the text
|
||||
, DRAW_EDITOR = 0x02 // draw editor stuff, such as borders/lines
|
||||
, DRAW_ACTIVE = 0x04 // draw active editor stuff, such as hidden separators and atom highlights
|
||||
};
|
||||
|
||||
/// Information on a linebreak
|
||||
enum LineBreak
|
||||
{ BREAK_NO // no line break
|
||||
, BREAK_SOFT // optional line break (' ')
|
||||
, BREAK_HARD // always a line break ('\n')
|
||||
, BREAK_LINE // line break with a separator line (<line>)
|
||||
};
|
||||
|
||||
/// Information on a character in a TextElement
|
||||
struct CharInfo {
|
||||
RealSize size;
|
||||
LineBreak break_after;
|
||||
|
||||
inline CharInfo(RealSize size, LineBreak break_after = BREAK_NO) : size(size), break_after(break_after) {}
|
||||
};
|
||||
|
||||
/// A section of text that can be rendered using a TextViewer
|
||||
class TextElement {
|
||||
public:
|
||||
/// What section of the input string is this element?
|
||||
size_t start, end;
|
||||
/// The text of which a subsection is drawn
|
||||
String text;
|
||||
|
||||
inline TextElement(const String& text, size_t start ,size_t end) : text(text), start(start), end(end) {}
|
||||
virtual ~TextElement() {}
|
||||
|
||||
/// Draw a subsection section of the text in the given rectangle
|
||||
/** this->start <= start < end <= this->end <= text.size() */
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const = 0;
|
||||
// /// The size of a single character at position index
|
||||
// /** index is in the range [start..end) */
|
||||
// virtual RealSize charSize(const Rotation& rot, double scale, size_t index) const = 0;
|
||||
/// Get information on all characters in the range [start...end) and store them in out
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const = 0;
|
||||
|
||||
/*
|
||||
// draw the section <start...end)
|
||||
// drawSeparators indicates what we should draw, separators or normal text
|
||||
// h is the height of the current line
|
||||
virtual void draw(RotatedDC& dc, UInt shrink, RealRect rect, size_t start, size_t end, DrawWhat draw) const = 0;
|
||||
/// Returns the width and height of the character at charId
|
||||
virtual RealSize charSize(RotatedDC& dc, double scale, size_t charId) const = 0;
|
||||
/// May the text be broken after char_id?
|
||||
virtual BreakAfter breakAfter(size_t char_id) const = 0;
|
||||
// number of characters in this object
|
||||
abstract size_t size() const;
|
||||
// maximum shrink factor allowed
|
||||
// shrink indicates by how much the thing to render should be shrunk, there is no indication
|
||||
// what it means (probably pixels or points), but size(shrink = k+1) <= size(shrink = k)
|
||||
abstract UInt maxShrink() const;
|
||||
// Size of an entire section
|
||||
RealSize sectionSize(RotatedDC& dc, double scale, size_t start, size_t end) const;
|
||||
RealSize for::sectionSize(RotatedDC& dc, double scale, size_t start, size_t end) const {
|
||||
RealSize size;
|
||||
for(i = start ; i < end ; ++i) {
|
||||
size = addHorizontal(size, charSize(dc, scale, i));
|
||||
}
|
||||
return size;
|
||||
}*/
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextElements
|
||||
|
||||
/// A list of text elements
|
||||
class TextElements : public vector<TextElementP> {
|
||||
public:
|
||||
/// Draw all the elements (as need to show the range start..end)
|
||||
void draw (RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const;
|
||||
// RealSize charSize(const Rotation& rot, double scale, size_t index) const;
|
||||
// Get information on all characters in the range [start...end) and store them in out
|
||||
void getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector<CharInfo>& out) const;
|
||||
|
||||
/// The actual elements
|
||||
/** They must be in order of positions and not overlap, i.e.
|
||||
* i < j ==> elements[i].end <= elements[j].start
|
||||
*/
|
||||
vector<TextElementP> elements;
|
||||
|
||||
/// Find the element that contains the given index, if there is any
|
||||
vector<TextElementP>::const_iterator findByIndex(size_t index) const;
|
||||
|
||||
/// Read the elements from a string
|
||||
void fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : SimpleTextElement
|
||||
|
||||
/// A text element that just shows text
|
||||
class SimpleTextElement : public TextElement {
|
||||
public:
|
||||
SimpleTextElement(const String& text, size_t start ,size_t end) : TextElement(text, start, end) {}
|
||||
};
|
||||
|
||||
/// A text element that uses a normal font
|
||||
class FontTextElement : public SimpleTextElement {
|
||||
public:
|
||||
FontTextElement(const String& text, size_t start ,size_t end, const FontP& font)
|
||||
: SimpleTextElement(text, start, end)
|
||||
, font(font)
|
||||
{}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
private:
|
||||
FontP font;
|
||||
DrawWhat draw_as;
|
||||
};
|
||||
|
||||
/// A text element that uses a symbol font
|
||||
class SymbolTextElement : public SimpleTextElement {
|
||||
public:
|
||||
SymbolTextElement(const String& text, size_t start ,size_t end) : SimpleTextElement(text, start, end) {}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
|
||||
/// A TextElement consisting of sub elements
|
||||
class CompoundTextElement : public TextElement {
|
||||
public:
|
||||
CompoundTextElement(const String& text, size_t start ,size_t end) : TextElement(text, start, end) {}
|
||||
|
||||
TextElements elements; ///< the elements
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
|
||||
};
|
||||
|
||||
/*
|
||||
// ----------------------------------------------------------------------------- : CompoundTextElement
|
||||
|
||||
/// A TextElement consisting of sub elements
|
||||
class CompoundTextElement : public TextElement {
|
||||
public:
|
||||
CompoundTextElement(const String& text, size_t start ,size_t end) : TextElement(text, start, end) {}
|
||||
|
||||
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const;
|
||||
virtual RealSize charSize(RotatedDC& dc, double scale, size_t index) const;
|
||||
|
||||
private:
|
||||
TextElements elements;
|
||||
};
|
||||
*/
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <render/text/element.hpp>
|
||||
#include <data/font.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : FontTextElement
|
||||
|
||||
void FontTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
dc.SetFont(font->font, font->size * scale);
|
||||
|
||||
if (end != start && text.substr(end-1, 1) == _("\n")) end -= 1; // don't draw the newline character at the end
|
||||
/* if ((draw & DRAW_NORMAL) != DRAW_NORMAL) {
|
||||
// don't draw
|
||||
if (what == DRAW_ACTIVE) {
|
||||
// we are drawing a separator
|
||||
dc.SetTextForeground(font->separator_color);
|
||||
dc.DrawText(text.substr(start, end-start), rect.position);
|
||||
}
|
||||
} else {*/
|
||||
// draw normally
|
||||
// draw shadow
|
||||
if (font->hasShadow()) {
|
||||
dc.SetTextForeground(font->shadow_color);
|
||||
dc.DrawText(text.substr(start, end - start), rect.position + font->shadow_displacement);
|
||||
}
|
||||
// draw
|
||||
dc.SetTextForeground(font->color);
|
||||
dc.DrawText(text.substr(start, end - start), rect.position);
|
||||
// }
|
||||
}
|
||||
|
||||
void FontTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
// font
|
||||
dc.SetFont(font->font, font->size * scale);
|
||||
// find sizes & breaks
|
||||
double prev_width = 0;
|
||||
for (size_t i = start ; i < end ; ++i) {
|
||||
Char c = text.GetChar(i);
|
||||
RealSize s = dc.GetTextExtent(text.substr(start, i - start));
|
||||
out.push_back(CharInfo(RealSize(s.width - prev_width, s.height),
|
||||
c == _('\n') ? BREAK_HARD :
|
||||
c == _(' ') ? BREAK_SOFT : BREAK_NO
|
||||
));
|
||||
prev_width = s.width;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <render/text/element.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : HorizontalLineTextElement
|
||||
|
||||
void HorizontalLineTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HorizontalLineTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <render/text/element.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolTextElement
|
||||
|
||||
void SymbolTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void SymbolTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const {
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <render/text/viewer.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(TextViewer::Line);
|
||||
DECLARE_TYPEOF_COLLECTION(double);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Line
|
||||
|
||||
struct TextViewer::Line {
|
||||
size_t start; ///< Index of the first character in this line
|
||||
vector<double> positions; ///< x position of each character in this line, gives the number of characters + 1, never empty
|
||||
double top; ///< y position of (the top of) this line
|
||||
double line_height; ///< The height of this line in pixels
|
||||
bool separator_after; ///< Is there a saparator after this line?
|
||||
|
||||
Line()
|
||||
: start(0), top(0), line_height(0), separator_after(false)
|
||||
{}
|
||||
|
||||
/// The position (just beyond) the bottom of this line
|
||||
double bottom() const { return top + line_height; }
|
||||
/// The width of this line
|
||||
double width() const { return positions.back() - positions.front(); }
|
||||
/// Index just beyond the last character on this line
|
||||
size_t end() const { return start + positions.size() - 1; }
|
||||
/// Find the index of the character at the given position on this line
|
||||
/** Always returns a value in the range [start..end()) */
|
||||
size_t posToIndex(double x) const;
|
||||
|
||||
/// Is this line visible using the given rectangle?
|
||||
bool visible(const Rotation& rot) const {
|
||||
return top + line_height > 0 && top < rot.getInternalSize().height;
|
||||
}
|
||||
|
||||
/// Draws a selection indicator on this line from start to end
|
||||
/** start and end need not be in this line */
|
||||
void drawSelection(RotatedDC& dc, size_t start, size_t end);
|
||||
};
|
||||
|
||||
size_t TextViewer::Line::posToIndex(double x) const {
|
||||
// largest index with pos <= x
|
||||
vector<double>::const_iterator it1 = lower_bound(positions.begin(), positions.end(), x);
|
||||
if (it1 == positions.end()) return end();
|
||||
// first index with pos > x
|
||||
vector<double>::const_iterator it2 = it1 + 1;
|
||||
if (it2 == positions.end()) return it1 - positions.begin();
|
||||
if (x - *it1 <= *it2 - x) return it1 - positions.begin(); // it1 is closer
|
||||
else return it2 - positions.begin(); // it2 is closer
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextViewer
|
||||
|
||||
// can't be declared in header because we need to know sizeof(Line)
|
||||
TextViewer:: TextViewer() {}
|
||||
TextViewer::~TextViewer() {}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing
|
||||
|
||||
void TextViewer::draw(RotatedDC& dc, const String& text, const TextStyle& style, Context& ctx, DrawWhat what) {
|
||||
Rotater r(dc, Rotation(style.angle, style.getRect()));
|
||||
if (lines.empty()) {
|
||||
// not prepared yet
|
||||
prepareElements(text, style, ctx);
|
||||
prepareLines(dc, text, style);
|
||||
}
|
||||
// Draw the text line by line
|
||||
FOR_EACH(l, lines) {
|
||||
if (l.visible(dc)) {
|
||||
RealRect rect(l.positions.front(), l.top, l.width(), l.line_height);
|
||||
elements.draw(dc, scale, rect, what, l.start, l.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end) {
|
||||
Rotater r(dc, Rotation(style.angle, style.getRect()));
|
||||
if (sel_start == sel_end) return;
|
||||
if (sel_end < sel_start) swap(sel_start, sel_end);
|
||||
dc.SetBrush(*wxBLACK_BRUSH);
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetLogicalFunction(wxINVERT);
|
||||
FOR_EACH(l, lines) {
|
||||
l.drawSelection(dc, sel_start, sel_end);
|
||||
}
|
||||
dc.SetLogicalFunction(wxCOPY);
|
||||
}
|
||||
|
||||
void TextViewer::Line::drawSelection(RotatedDC& dc, size_t sel_start, size_t sel_end) {
|
||||
if (!visible(dc)) return;
|
||||
if (sel_start < end() && sel_end > start) {
|
||||
double x1 = positions[sel_start];
|
||||
double x2 = positions[max(end(), sel_end)];
|
||||
dc.DrawRectangle(RealRect(x1, top, x2 - x1, line_height));
|
||||
}
|
||||
}
|
||||
|
||||
void TextViewer::reset() {
|
||||
elements.clear();
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Positions
|
||||
|
||||
size_t TextViewer::lineStart(size_t index) const {
|
||||
if (lines.empty()) return 0;
|
||||
return findLine(index).start;
|
||||
}
|
||||
|
||||
size_t TextViewer::lineEnd(size_t index) const {
|
||||
if (lines.empty()) return 0;
|
||||
return findLine(index).end();
|
||||
}
|
||||
|
||||
const TextViewer::Line& TextViewer::findLine(size_t index) const {
|
||||
FOR_EACH_CONST(l, lines) {
|
||||
if (l.end() > index) return l;
|
||||
}
|
||||
return lines.front();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Elements
|
||||
|
||||
void TextViewer::prepareElements(const String& text, const TextStyle& style, Context& ctx) {
|
||||
elements.fromString(text, 0, text.size(), style, ctx);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Layout
|
||||
|
||||
void TextViewer::prepareLines(RotatedDC& dc, const String& text, const TextStyle& style) {
|
||||
scale = 1;
|
||||
prepareLinesScale(dc, text, style, false);
|
||||
}
|
||||
|
||||
bool TextViewer::prepareLinesScale(RotatedDC& dc, const String& text, const TextStyle& style, bool stop_if_too_long) {
|
||||
// Try to layout the text at the current scale
|
||||
// find character sizes
|
||||
vector<CharInfo> chars;
|
||||
elements.getCharInfo(dc, scale, 0, text.size(), chars);
|
||||
// first line
|
||||
lines.clear();
|
||||
Line line;
|
||||
// size of the line so far
|
||||
RealSize line_size(lineLeft(dc, style, 0), 0);
|
||||
line.positions.push_back(line_size.width);
|
||||
// The word we are currently reading
|
||||
RealSize word_size;
|
||||
vector<double> positions_word; // positios for this word
|
||||
size_t word_start = 0;
|
||||
// For each character ...
|
||||
for(size_t i = 0 ; i < chars.size() ; ++i) {
|
||||
CharInfo& c = chars[i];
|
||||
// Should we break?
|
||||
bool break_now = false;
|
||||
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
|
||||
double line_height_multiplier = 1; // multiplier for line height for next line top
|
||||
if (c.break_after == BREAK_HARD) {
|
||||
break_now = true;
|
||||
accept_word = true;
|
||||
line_height_multiplier = style.line_height_hard;
|
||||
} else if (c.break_after == BREAK_LINE) {
|
||||
line.separator_after = true;
|
||||
break_now = true;
|
||||
accept_word = true;
|
||||
line_height_multiplier = style.line_height_line;
|
||||
} else if (c.break_after == BREAK_SOFT && style.field().multi_line) {
|
||||
// Soft break == end of word
|
||||
accept_word = true;
|
||||
}
|
||||
// Add size of the character
|
||||
word_size = addHorizontal(word_size, c.size);
|
||||
positions_word.push_back(word_size.width);
|
||||
// Did the word become too long?
|
||||
if (style.field().multi_line && !break_now) {
|
||||
double max_width = lineRight(dc, style, line.top);
|
||||
if (word_start == line.start && word_size.width > max_width) {
|
||||
// single word on this line; the word is too long
|
||||
if (stop_if_too_long) {
|
||||
return false; // just give up
|
||||
} else {
|
||||
// force a word break
|
||||
break_now = true;
|
||||
accept_word = true;
|
||||
hide_breaker = false;
|
||||
line_height_multiplier = style.line_height_soft;
|
||||
}
|
||||
} else if (line_size.width + word_size.width > max_width) {
|
||||
// line would become too long, break before the current word
|
||||
break_now = true;
|
||||
line_height_multiplier = style.line_height_soft;
|
||||
}
|
||||
}
|
||||
// Ending the current word
|
||||
if (accept_word) {
|
||||
// move word pos to line
|
||||
FOR_EACH(p, positions_word) {
|
||||
line.positions.push_back(line_size.width + p);
|
||||
}
|
||||
// add size; next word
|
||||
line_size = addHorizontal(line_size, word_size);
|
||||
word_size = RealSize(0, 0);
|
||||
word_start = i + 1;
|
||||
positions_word.clear();
|
||||
}
|
||||
// Breaking (ending the current line)
|
||||
if (break_now) {
|
||||
// remove the _('\n') or _(' ') that caused the break
|
||||
if (hide_breaker && line.positions.size() > 1) {
|
||||
line.positions.pop_back();
|
||||
}
|
||||
// height of the line
|
||||
if (line_size.height < 0.01 && !lines.empty()) {
|
||||
// if a line has 0 height, use the height of the line above it, but at most once
|
||||
} else {
|
||||
line.line_height = line_size.height;
|
||||
}
|
||||
// push
|
||||
lines.push_back(line);
|
||||
// reset line object for next line
|
||||
line.top += line.line_height * line_height_multiplier;
|
||||
line.start = word_start;
|
||||
line.positions.clear();
|
||||
if (line.separator_after) line.line_height = 0;
|
||||
line.separator_after = false;
|
||||
// reset line_size
|
||||
line_size = RealSize(lineLeft(dc, style, line.top), 0);
|
||||
line.positions.push_back(line_size.width); // start position
|
||||
}
|
||||
}
|
||||
// the last word
|
||||
FOR_EACH(p, positions_word) {
|
||||
line.positions.push_back(line_size.width + p);
|
||||
}
|
||||
line_size = addHorizontal(line_size, word_size);
|
||||
// the last line
|
||||
if (line_size.height < 0.01 && !lines.empty()) {
|
||||
// if a line has 0 height, use the height of the line above it, but at most once
|
||||
} else {
|
||||
line.line_height = line_size.height;
|
||||
}
|
||||
lines.push_back(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
double TextViewer::lineLeft(RotatedDC& dc, const TextStyle& style, double y) {
|
||||
return 0;
|
||||
// return style.mask.rowLeft(y, dc.getInternalSize()) + style.padding_left;
|
||||
}
|
||||
double TextViewer::lineRight(RotatedDC& dc, const TextStyle& style, double y) {
|
||||
return style.width;
|
||||
// return style.mask.rowRight(y, dc.getInternalSize()) - style.padding_right;
|
||||
}
|
||||
ContourMask::ContourMask() {} // MOVEME //@@
|
||||
ContourMask::~ContourMask() {}
|
||||
@@ -0,0 +1,97 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_RENDER_TEXT_VIEWER
|
||||
#define HEADER_RENDER_TEXT_VIEWER
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <util/rotation.hpp>
|
||||
#include <data/field/text.hpp>
|
||||
#include <render/text/element.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : TextViewer
|
||||
|
||||
/// Class for viewing and determining positions in formated text
|
||||
/** It can:
|
||||
* - Draw text to a DC
|
||||
* - Draw selection to a DC
|
||||
* - Convert between screen coordinates and cursor position
|
||||
*
|
||||
* To refer to positions in the text this class uses several concepts:
|
||||
* - index An index in the input string.
|
||||
* For cursor positions char_id = 0 means the cursor is BEFORE character 0
|
||||
* - pos The position of a character in real world x,y coordinates (in pixels)
|
||||
* The position is the top-left of the character.
|
||||
* A char_pos is often only the x coordinate.
|
||||
* A line_pos is often only the y coordinate.
|
||||
* - Line A line on the screen, this does not neccessarly correspond to explicit linebreaks,
|
||||
* since textwrapping also leads to a new line.
|
||||
* - line_id The index of a line, 0 is the first line.
|
||||
*/
|
||||
class TextViewer {
|
||||
public:
|
||||
/// Information on a line in the textbox
|
||||
struct Line;
|
||||
|
||||
TextViewer();
|
||||
~TextViewer();
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
/// Draw the given text with the given text style
|
||||
/** The drawing information is cached,
|
||||
* before calling draw again with different text/style reset() should be called
|
||||
*/
|
||||
void draw(RotatedDC& dc, const String& text, const TextStyle& style, Context&, DrawWhat);
|
||||
/// Draw an indicator for selected text
|
||||
void drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end);
|
||||
|
||||
/// Reset the cached data, at a new call to draw it will be recalculated
|
||||
void reset();
|
||||
|
||||
// --------------------------------------------------- : Positions
|
||||
|
||||
/// Find the character index that is before the given index, and which has a nonzero width
|
||||
size_t moveLeft(size_t index) const;
|
||||
/// Find the character index that is on a line above/below index
|
||||
/** If this would move outisde the text, returns the input index */
|
||||
size_t moveLine(size_t index, int delta) const;
|
||||
/// The character index of the start of the line that character #index is on
|
||||
size_t lineStart(size_t index) const;
|
||||
/// The character index past the end of the line that character #index is on
|
||||
size_t lineEnd (size_t index) const;
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : More drawing
|
||||
double scale; /// < Scale when drawing
|
||||
|
||||
// --------------------------------------------------- : Elements
|
||||
TextElements elements; ///< The elements of the prepared text
|
||||
|
||||
/// Find the elements in a string and add them to elements
|
||||
void prepareElements(const String&, const TextStyle& style, Context& ctx);
|
||||
|
||||
// --------------------------------------------------- : Lines
|
||||
vector<Line> lines; ///< The lines in the text box
|
||||
|
||||
/// Prepare the lines, layout the text
|
||||
void prepareLines(RotatedDC& dc, const String& text, const TextStyle& style);
|
||||
/// Prepare the lines, layout the text; at a specific scale
|
||||
bool prepareLinesScale(RotatedDC& dc, const String& text, const TextStyle& style, bool stop_if_too_long);
|
||||
/// 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;
|
||||
|
||||
// helper : get the start coordinate of a line, this is 0 unless there is a contour mask
|
||||
double lineLeft (RotatedDC& dc, const TextStyle& style, double y);
|
||||
// helper : get the end coordinate of a line, this is width unless there is a contour mask
|
||||
double lineRight(RotatedDC& dc, const TextStyle& style, double y);
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
Reference in New Issue
Block a user