blob: 0443750a3bc3c664220bfb57f3b665ccc3a7f7ff [file] [log] [blame]
#include "rive/text/text_style.hpp"
#include "rive/text/text_style_axis.hpp"
#include "rive/text/text_style_feature.hpp"
#include "rive/text/text_variation_helper.hpp"
#include "rive/renderer.hpp"
#include "rive/shapes/paint/shape_paint.hpp"
#include "rive/backboard.hpp"
#include "rive/importers/backboard_importer.hpp"
#include "rive/text/text.hpp"
#include "rive/artboard.hpp"
using namespace rive;
// satisfy unique_ptr
TextStyle::TextStyle() = default;
void TextStyle::addVariation(TextStyleAxis* axis)
{
m_variations.push_back(axis);
}
void TextStyle::addFeature(TextStyleFeature* feature)
{
m_styleFeatures.push_back(feature);
}
void TextStyle::onDirty(ComponentDirt dirt)
{
// This can happen if the Text is in a Solo which is propagating its
// collapsed state during its onAddedClean (which could be called before
// ours, so m_text might still be null).
if (m_text != nullptr)
{
if ((dirt & ComponentDirt::TextShape) == ComponentDirt::TextShape)
{
m_text->markShapeDirty();
if (m_variationHelper != nullptr)
{
m_variationHelper->addDirt(ComponentDirt::TextShape);
}
}
}
}
StatusCode TextStyle::onAddedClean(CoreContext* context)
{
// We know this is good because we validated it during validate().
m_text = TextInterface::from(parent());
auto code = Super::onAddedClean(context);
if (code != StatusCode::Ok)
{
return code;
}
// This ensures context propagates to variation helper too.
if (!m_variations.empty() || !m_styleFeatures.empty())
{
m_variationHelper = rivestd::make_unique<TextVariationHelper>(this);
}
if (m_variationHelper != nullptr)
{
if ((code = m_variationHelper->onAddedDirty(context)) != StatusCode::Ok)
{
return code;
}
if ((code = m_variationHelper->onAddedClean(context)) != StatusCode::Ok)
{
return code;
}
}
return StatusCode::Ok;
}
const rcp<Font> TextStyle::font() const
{
if (m_variableFont != nullptr)
{
return m_variableFont;
}
auto asset = fontAsset();
return asset == nullptr ? nullptr : asset->font();
}
void TextStyle::updateVariableFont()
{
auto asset = fontAsset();
rcp<Font> baseFont = asset == nullptr ? nullptr : asset->font();
if (baseFont == nullptr)
{
// Not ready yet.
return;
}
if (!m_variations.empty() || !m_styleFeatures.empty())
{
m_coords.clear();
for (TextStyleAxis* axis : m_variations)
{
m_coords.push_back({axis->tag(), axis->axisValue()});
}
m_features.clear();
for (TextStyleFeature* styleFeature : m_styleFeatures)
{
m_features.push_back(
{styleFeature->tag(), styleFeature->featureValue()});
}
m_variableFont = baseFont->withOptions(m_coords, m_features);
}
else
{
m_variableFont = nullptr;
}
}
void TextStyle::buildDependencies()
{
if (m_variationHelper != nullptr)
{
m_variationHelper->buildDependencies();
}
parent()->addDependent(this);
Super::buildDependencies();
}
uint32_t TextStyle::assetId() { return this->fontAssetId(); }
void TextStyle::setAsset(rcp<FileAsset> asset)
{
if (asset->is<FontAsset>())
{
FileAssetReferencer::setAsset(asset);
}
}
StatusCode TextStyle::import(ImportStack& importStack)
{
auto result = registerReferencer(importStack);
if (result != StatusCode::Ok)
{
return result;
}
return Super::import(importStack);
}
void TextStyle::fontSizeChanged() { m_text->markShapeDirty(); }
void TextStyle::lineHeightChanged() { m_text->markShapeDirty(); }
void TextStyle::letterSpacingChanged() { m_text->markShapeDirty(); }
Core* TextStyle::clone() const
{
TextStyle* twin = TextStyleBase::clone()->as<TextStyle>();
if (m_fileAsset != nullptr)
{
twin->setAsset(m_fileAsset);
}
return twin;
}
bool TextStyle::validate(CoreContext* context)
{
return TextInterface::from(context->resolve(parentId())) != nullptr;
}