blob: f25331869ba96ba69b4013435d8afca33c352f51 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/text/paragraph_builder.h"
#include "flutter/common/threads.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/sky/engine/core/rendering/RenderInline.h"
#include "flutter/sky/engine/core/rendering/RenderParagraph.h"
#include "flutter/sky/engine/core/rendering/RenderText.h"
#include "flutter/sky/engine/core/rendering/style/RenderStyle.h"
#include "flutter/sky/engine/platform/text/LocaleToScriptMapping.h"
#include "lib/ftl/tasks/task_runner.h"
#include "lib/tonic/converter/dart_converter.h"
#include "lib/tonic/dart_args.h"
#include "lib/tonic/dart_binding_macros.h"
#include "lib/tonic/dart_library_natives.h"
namespace blink {
namespace {
// TextStyle
const int tsColorIndex = 1;
const int tsTextDecorationIndex = 2;
const int tsTextDecorationColorIndex = 3;
const int tsTextDecorationStyleIndex = 4;
const int tsFontWeightIndex = 5;
const int tsFontStyleIndex = 6;
const int tsTextBaselineIndex = 7;
const int tsFontFamilyIndex = 8;
const int tsFontSizeIndex = 9;
const int tsLetterSpacingIndex = 10;
const int tsWordSpacingIndex = 11;
const int tsHeightIndex = 12;
const int tsColorMask = 1 << tsColorIndex;
const int tsTextDecorationMask = 1 << tsTextDecorationIndex;
const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex;
const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex;
const int tsFontWeightMask = 1 << tsFontWeightIndex;
const int tsFontStyleMask = 1 << tsFontStyleIndex;
const int tsTextBaselineMask = 1 << tsTextBaselineIndex;
const int tsFontFamilyMask = 1 << tsFontFamilyIndex;
const int tsFontSizeMask = 1 << tsFontSizeIndex;
const int tsLetterSpacingMask = 1 << tsLetterSpacingIndex;
const int tsWordSpacingMask = 1 << tsWordSpacingIndex;
const int tsHeightMask = 1 << tsHeightIndex;
// ParagraphStyle
const int psTextAlignIndex = 1;
const int psFontWeightIndex = 2;
const int psFontStyleIndex = 3;
const int psMaxLinesIndex = 4;
const int psFontFamilyIndex = 5;
const int psFontSizeIndex = 6;
const int psLineHeightIndex = 7;
const int psEllipsisIndex = 8;
const int psTextAlignMask = 1 << psTextAlignIndex;
const int psFontWeightMask = 1 << psFontWeightIndex;
const int psFontStyleMask = 1 << psFontStyleIndex;
const int psMaxLinesMask = 1 << psMaxLinesIndex;
const int psFontFamilyMask = 1 << psFontFamilyIndex;
const int psFontSizeMask = 1 << psFontSizeIndex;
const int psLineHeightMask = 1 << psLineHeightIndex;
const int psEllipsisMask = 1 << psEllipsisIndex;
float getComputedSizeFromSpecifiedSize(float specifiedSize) {
if (specifiedSize < std::numeric_limits<float>::epsilon())
return 0.0f;
return specifiedSize;
}
void createFontForDocument(RenderStyle* style) {
FontDescription fontDescription = FontDescription();
fontDescription.setScript(
localeToScriptCodeForFontSelection(style->locale()));
// Using 14px default to match Material Design English Body1:
// http://www.google.com/design/spec/style/typography.html#typography-typeface
const float defaultFontSize = 14.0;
fontDescription.setSpecifiedSize(defaultFontSize);
fontDescription.setComputedSize(defaultFontSize);
FontOrientation fontOrientation = Horizontal;
NonCJKGlyphOrientation glyphOrientation = NonCJKGlyphOrientationVerticalRight;
fontDescription.setOrientation(fontOrientation);
fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
style->setFontDescription(fontDescription);
style->font().update(UIDartState::Current()->font_selector());
}
PassRefPtr<RenderStyle> decodeParagraphStyle(
RenderStyle* parentStyle,
tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double lineHeight,
const std::string& ellipsis) {
FTL_DCHECK(encoded.num_elements() == 5);
RefPtr<RenderStyle> style = RenderStyle::create();
style->inheritFrom(parentStyle);
style->setDisplay(PARAGRAPH);
int32_t mask = encoded[0];
if (mask & psTextAlignMask)
style->setTextAlign(static_cast<ETextAlign>(encoded[psTextAlignIndex]));
if (mask & (psFontWeightMask | psFontStyleMask | psFontFamilyMask |
psFontSizeMask)) {
FontDescription fontDescription = style->fontDescription();
if (mask & psFontWeightMask)
fontDescription.setWeight(
static_cast<FontWeight>(encoded[psFontWeightIndex]));
if (mask & psFontStyleMask)
fontDescription.setStyle(
static_cast<FontStyle>(encoded[psFontStyleIndex]));
if (mask & psFontFamilyMask) {
FontFamily family;
family.setFamily(String::fromUTF8(fontFamily));
fontDescription.setFamily(family);
}
if (mask & psFontSizeMask) {
fontDescription.setSpecifiedSize(fontSize);
fontDescription.setIsAbsoluteSize(true);
fontDescription.setComputedSize(
getComputedSizeFromSpecifiedSize(fontSize));
}
style->setFontDescription(fontDescription);
style->font().update(UIDartState::Current()->font_selector());
}
if (mask & psLineHeightMask)
style->setLineHeight(Length(lineHeight * 100.0, Percent));
if (mask & psMaxLinesMask)
style->setMaxLines(encoded[psMaxLinesIndex]);
if (mask & psEllipsisMask)
style->setEllipsis(AtomicString::fromUTF8(ellipsis.c_str()));
return style.release();
}
Color getColorFromARGB(int argb) {
return Color((argb & 0x00FF0000) >> 16, (argb & 0x0000FF00) >> 8,
(argb & 0x000000FF) >> 0, (argb & 0xFF000000) >> 24);
}
} // namespace
static void ParagraphBuilder_constructor(Dart_NativeArguments args) {
DartCallConstructor(&ParagraphBuilder::create, args);
}
IMPLEMENT_WRAPPERTYPEINFO(ui, ParagraphBuilder);
#define FOR_EACH_BINDING(V) \
V(ParagraphBuilder, pushStyle) \
V(ParagraphBuilder, pop) \
V(ParagraphBuilder, addText) \
V(ParagraphBuilder, build)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register(
{{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 6, true},
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
ftl::RefPtr<ParagraphBuilder> ParagraphBuilder::create(
tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double lineHeight,
const std::string& ellipsis) {
return ftl::MakeRefCounted<ParagraphBuilder>(
encoded, fontFamily, fontSize, lineHeight, ellipsis);
}
ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double lineHeight,
const std::string& ellipsis) {
createRenderView();
RefPtr<RenderStyle> paragraphStyle = decodeParagraphStyle(
m_renderView->style(), encoded, fontFamily, fontSize, lineHeight, ellipsis);
encoded.Release();
m_renderParagraph = new RenderParagraph();
m_renderParagraph->setStyle(paragraphStyle.release());
m_currentRenderObject = m_renderParagraph;
m_renderView->addChild(m_currentRenderObject);
}
ParagraphBuilder::~ParagraphBuilder() {
if (m_renderView) {
RenderView* renderView = m_renderView.leakPtr();
Threads::UI()->PostTask(
[renderView]() { renderView->destroy(); });
}
}
void ParagraphBuilder::pushStyle(tonic::Int32List& encoded,
const std::string& fontFamily,
double fontSize,
double letterSpacing,
double wordSpacing,
double height) {
FTL_DCHECK(encoded.num_elements() == 8);
RefPtr<RenderStyle> style = RenderStyle::create();
style->inheritFrom(m_currentRenderObject->style());
int32_t mask = encoded[0];
if (mask & tsColorMask)
style->setColor(getColorFromARGB(encoded[tsColorIndex]));
if (mask & tsTextDecorationMask) {
style->setTextDecoration(
static_cast<TextDecoration>(encoded[tsTextDecorationIndex]));
style->applyTextDecorations();
}
if (mask & tsTextDecorationColorMask)
style->setTextDecorationColor(
StyleColor(getColorFromARGB(encoded[tsTextDecorationColorIndex])));
if (mask & tsTextDecorationStyleMask)
style->setTextDecorationStyle(
static_cast<TextDecorationStyle>(encoded[tsTextDecorationStyleIndex]));
if (mask & tsTextBaselineMask) {
// TODO(abarth): Implement TextBaseline. The CSS version of this
// property wasn't wired up either.
}
if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontFamilyMask |
tsFontSizeMask | tsLetterSpacingMask | tsWordSpacingMask)) {
FontDescription fontDescription = style->fontDescription();
if (mask & tsFontWeightMask)
fontDescription.setWeight(
static_cast<FontWeight>(encoded[tsFontWeightIndex]));
if (mask & tsFontStyleMask)
fontDescription.setStyle(
static_cast<FontStyle>(encoded[tsFontStyleIndex]));
if (mask & tsFontFamilyMask) {
FontFamily family;
family.setFamily(String::fromUTF8(fontFamily));
fontDescription.setFamily(family);
}
if (mask & tsFontSizeMask) {
fontDescription.setSpecifiedSize(fontSize);
fontDescription.setIsAbsoluteSize(true);
fontDescription.setComputedSize(
getComputedSizeFromSpecifiedSize(fontSize));
}
if (mask & tsLetterSpacingMask)
fontDescription.setLetterSpacing(letterSpacing);
if (mask & tsWordSpacingMask)
fontDescription.setWordSpacing(wordSpacing);
style->setFontDescription(fontDescription);
style->font().update(UIDartState::Current()->font_selector());
}
if (mask & tsHeightMask) {
style->setLineHeight(Length(height * 100.0, Percent));
}
encoded.Release();
RenderObject* span = new RenderInline();
span->setStyle(style.release());
m_currentRenderObject->addChild(span);
m_currentRenderObject = span;
}
void ParagraphBuilder::pop() {
if (m_currentRenderObject)
m_currentRenderObject = m_currentRenderObject->parent();
}
void ParagraphBuilder::addText(const std::string& text) {
if (!m_currentRenderObject)
return;
RenderText* renderText = new RenderText(String::fromUTF8(text).impl());
RefPtr<RenderStyle> style = RenderStyle::create();
style->inheritFrom(m_currentRenderObject->style());
renderText->setStyle(style.release());
m_currentRenderObject->addChild(renderText);
}
ftl::RefPtr<Paragraph> ParagraphBuilder::build() {
m_currentRenderObject = nullptr;
return Paragraph::create(m_renderView.release());
}
void ParagraphBuilder::createRenderView() {
RefPtr<RenderStyle> style = RenderStyle::create();
style->setRTLOrdering(LogicalOrder);
style->setZIndex(0);
style->setUserModify(READ_ONLY);
createFontForDocument(style.get());
m_renderView = adoptPtr(new RenderView());
m_renderView->setStyle(style.release());
}
} // namespace blink