Files
MagicSetEditor2/src/data/field/text.cpp
T
2026-04-19 19:06:52 +02:00

237 lines
8.1 KiB
C++

//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make card games |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/field/text.hpp>
#include <util/tagged_string.hpp>
#include <script/script.hpp>
// ----------------------------------------------------------------------------- : TextField
TextField::TextField()
: multi_line(false)
, default_name(_("Default"))
{}
IMPLEMENT_FIELD_TYPE(Text, "text");
void TextField::initDependencies(Context& ctx, const Dependency& dep) const {
Field ::initDependencies(ctx, dep);
script .initDependencies(ctx, dep);
default_script.initDependencies(ctx, dep);
}
IMPLEMENT_REFLECTION(TextField) {
REFLECT_BASE(Field);
REFLECT(multi_line);
REFLECT(script);
REFLECT_N("default", default_script);
REFLECT(default_name);
}
// ----------------------------------------------------------------------------- : TextStyle
TextLayoutP dummy_layout() {
auto layout = make_intrusive<TextLayout>();
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 block = make_intrusive<LineLayout>(0, 0, 0, LineLayout::Type::BLOCK);
clause ->lines.push_back(line);
paragraph->lines.push_back(line);
block ->lines.push_back(line);
layout ->lines.push_back(line);
paragraph->clauses.push_back(clause);
block ->clauses.push_back(clause);
layout ->clauses.push_back(clause);
block ->paragraphs.push_back(paragraph);
layout ->paragraphs.push_back(paragraph);
layout ->blocks.push_back(block);
return layout;
}
TextStyle::TextStyle(const TextFieldP& field)
: Style(field)
, always_symbol(false), allow_formating(true)
, alignment(ALIGN_TOP_LEFT)
, padding_left (0), padding_left_min (10000)
, padding_right (0), padding_right_min (10000)
, padding_top (0), padding_top_min (10000)
, padding_bottom(0), padding_bottom_min(10000)
, line_height_soft(1.0)
, line_height_hard(1.0)
, line_height_line(1.0)
, line_height_soft_max(0.0)
, line_height_hard_max(0.0)
, line_height_line_max(0.0)
, paragraph_height(-1)
, direction(LEFT_TO_RIGHT)
, layout(dummy_layout())
{}
double TextStyle::getStretch() const {
if (layout->width > 0 && (alignment() & ALIGN_STRETCH)) {
double factor = (width - padding_left - padding_right) / layout->width;
if (!(alignment() & ALIGN_IF_OVERFLOW) || factor < 1.0) {
return factor;
}
}
return 1.0;
}
int TextStyle::update(Context& ctx) {
int changes = Style::update(ctx);
changes |= font .update(ctx) * CHANGE_OTHER;
changes |= symbol_font.update(ctx) * CHANGE_OTHER;
changes |= alignment .update(ctx) * CHANGE_OTHER;
changes |= padding_left .update(ctx) * CHANGE_OTHER;
changes |= padding_left_min .update(ctx) * CHANGE_OTHER;
changes |= padding_right .update(ctx) * CHANGE_OTHER;
changes |= padding_right_min .update(ctx) * CHANGE_OTHER;
changes |= padding_top .update(ctx) * CHANGE_OTHER;
changes |= padding_top_min .update(ctx) * CHANGE_OTHER;
changes |= padding_bottom .update(ctx) * CHANGE_OTHER;
changes |= padding_bottom_min .update(ctx) * CHANGE_OTHER;
changes |= line_height_soft .update(ctx) * CHANGE_OTHER;
changes |= line_height_hard .update(ctx) * CHANGE_OTHER;
changes |= line_height_line .update(ctx) * CHANGE_OTHER;
changes |= line_height_soft_max.update(ctx) * CHANGE_OTHER;
changes |= line_height_hard_max.update(ctx) * CHANGE_OTHER;
changes |= line_height_line_max.update(ctx) * CHANGE_OTHER;
return changes;
}
void TextStyle::initDependencies(Context& ctx, const Dependency& dep) const {
Style ::initDependencies(ctx, dep);
// font .initDependencies(ctx, dep);
// symbol_font.initDependencies(ctx, dep);
}
void TextStyle::checkContentDependencies(Context& ctx, const Dependency& dep) const {
Style ::checkContentDependencies(ctx, dep);
alignment.initDependencies(ctx, dep);
}
template <typename T> void reflect_layout(T& handler, const TextLayoutP& layout) {}
template <> void reflect_layout(GetMember& handler, const TextLayoutP& layout) {
assert(layout);
REFLECT_N("layout", layout);
REFLECT_N("content_width", layout->width);
REFLECT_N("content_height", layout->height);
REFLECT_N("content_lines", layout->lines.size());
}
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", middle());
if (type > Type::LINE) REFLECT(lines);
if (type > Type::CLAUSE) REFLECT(clauses);
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) {
REFLECT_BASE(Style);
REFLECT(font);
REFLECT(symbol_font);
REFLECT(always_symbol);
REFLECT(allow_formating);
REFLECT(alignment);
REFLECT(padding_left);
REFLECT(padding_right);
REFLECT(padding_top);
REFLECT(padding_bottom);
REFLECT(padding_left_min);
REFLECT(padding_right_min);
REFLECT(padding_top_min);
REFLECT(padding_bottom_min);
REFLECT(line_height_soft);
REFLECT(line_height_hard);
REFLECT(line_height_line);
REFLECT(line_height_soft_max);
REFLECT(line_height_hard_max);
REFLECT(line_height_line_max);
REFLECT(paragraph_height);
REFLECT(direction);
reflect_layout(handler, layout);
}
// ----------------------------------------------------------------------------- : TextValue
String TextValue::toString() const {
return untag_hide_sep(value());
}
bool TextValue::isDefault() {
return value.isDefault();
}
bool TextValue::update(Context& ctx) {
updateAge();
WITH_DYNAMIC_ARG(last_update_age, last_update.get());
WITH_DYNAMIC_ARG(value_being_updated, this);
bool change = field().default_script.invokeOnDefault(ctx, value)
| field(). script.invokeOn(ctx, value);
if (change) last_update.update();
updateSortValue(ctx);
return change;
}
IMPLEMENT_REFLECTION_NAMELESS(TextValue) {
if (fieldP->save_value || !handler.isWriting) REFLECT_NAMELESS(value);
}
// ----------------------------------------------------------------------------- : FakeTextValue
FakeTextValue::FakeTextValue(const TextFieldP& field, String* underlying, bool editable, bool untagged)
: TextValue(field), underlying(underlying)
, editable(editable), untagged(untagged)
{}
void FakeTextValue::store() {
if (underlying) {
if (editable) {
*underlying = untagged ? untag(value) : value();
} else {
retrieve();
}
}
}
void FakeTextValue::retrieve() {
if (underlying) {
value.assign(untagged ? escape(*underlying) : *underlying);
} else {
value.assign(_(""));
}
}
void FakeTextValue::onAction(Action& a, bool undone) {
store();
}
bool FakeTextValue::equals(const Value* that) {
if (this == that) return true;
if (!underlying) return false;
const FakeTextValue* thatT = dynamic_cast<const FakeTextValue*>(that);
if (!thatT || underlying != thatT->underlying) return false;
// update the value
retrieve();
return true;
}