Merge branch 'upsilon-dev-latex'

This commit is contained in:
Mino1289
2021-12-14 22:51:15 +01:00
committed by Laury
parent 7fc28f8b72
commit fdef2b5d6e
24 changed files with 2763 additions and 56 deletions

View File

@@ -27,6 +27,10 @@ ifeq (${MODEL}, n0100)
endif
endif
ifeq ($(filter reader,$(apps_list)),)
HAS_READER := 1
endif
ifeq (${MODEL}, n0110)
apps_list = ${EPSILON_APPS}
else

View File

@@ -1,12 +1,15 @@
apps += Reader::App
app_headers += apps/reader/app.h
SFLAGS += -DHAS_READER
app_sreader_src = $(addprefix apps/reader/,\
app.cpp \
list_book_controller.cpp \
utility.cpp \
read_book_controller \
word_wrap_view.cpp \
tex_parser.cpp \
)
apps_src += $(app_sreader_src)
@@ -15,4 +18,4 @@ app_images += apps/reader/reader_icon.png
i18n_files += $(call i18n_without_universal_for,reader/base)
$(eval $(call depends_on_image,apps/reader/app.cpp,apps/reader/reader_icon.png))
$(eval $(call depends_on_image,apps/reader/app.cpp,apps/reader/reader_icon.png))

View File

@@ -6,8 +6,10 @@ Thanks to [Gabriel79](https://github.com/Gabriel79) for the original reader app,
# Rich text format
Reader app supports now a rich text format :
* `$` around a mathematical expression **without spaces** to render it
* `$` around a LaTeX expression to render it
* `%` around a color-code (see below) to change the color of the text
### LaTeX expressions
You can read the documentation for the LaTeX Parser [here](TexParser.md).
### Color codes :
|code|color|
| --:| ---:|

1901
apps/reader/TexParser.html Normal file

File diff suppressed because it is too large Load Diff

51
apps/reader/TexParser.md Normal file
View File

@@ -0,0 +1,51 @@
# LaTeX Parser
In the reader app, you can read a txt file. You can also read a txt file with LaTeX expression inside of it.
All the symbols you can use are listed here :
|Command|Output||Command|Output|
|--:|:--:|--:|--:|:--:|
|Math|Expressions||||
|`\frac{ab}{cd}`|$\frac{ab}{cd}$||`\sqrt[n]{x}`|$\sqrt[n]{x}$|
|Math|Symbols|||
|`\times`|$\times$||`\div`|$\div$|
|`\forall`|$\forall$||`\exists`|$\exists$|
|`\partial`|$\partial$||`\pm`|$\pm$|
|`\infty`|$\infty$||`\approx`|$\approx$|
|`\neq`|$\neq$||`\equiv`|$\equiv$|
|`\leq`|$\leq$||`\geq`|$\geq$|
|Simple|Arrows||Double|Arrows|
|`\leftarrow`|$\leftarrow$||`\Leftarrow`|$\Leftarrow$|
|`\rightarrow`|$\rightarrow$||`\Rightarrow`|$\Rightarrow$|
|`\uparrow`|$\uparrow$||`\Uparrow`|$\Uparrow$|
|`\downarrow`|$\downarrow$||`\Downarrow`|$\Downarrow$|
|`\leftrightarrow`|$\leftrightarrow$||||
|`\updownarrow`|$\updownarrow$||||
|Greek Capital|Letters||Greek Small|Letters|
|`\Alpha`|$\Alpha$||`\alpha`|$\alpha$|
|`\Beta`|$\Beta$||`\beta`|$\beta$|
|`\Gamma`|$\Gamma$||`\gamma`|$\gamma$|
|`\Delta`|$\Delta$||`\delta`|$\delta$|
|`\Epsilon`|$\Epsilon$||`\epsilon`|$\epsilon$|
|`\Zeta`|$\Zeta$||`\zeta`|$\zeta$|
|`\Eta`|$\Eta$||`\eta`|$\eta$|
|`\Theta`|$\Theta$||`\theta`|$\theta$|
|`\Iota`|$\Iota$||`\iota`|$\iota$|
|`\Kappa`|$\Kappa$||`\kappa`|$\kappa$|
|`\Lambda`|$\Lambda$||`\lambda`|$\lambda$|
|`\Mu`|$\Mu$||`\mu`|$\mu$|
|`\Nu`|$\Nu$||`\nu`|$\nu$|
|`\Xi`|$\Xi$||`\xi`|$\xi$|
|`\Omicron`|$\Omicron$|||
|`\Pi`|$\Pi$||`\pi`|$\pi$|
|`\Rho`|$\Rho$||`\rho`|$\rho$|
|`\Sigma`|$\Sigma$||`\sigma`|$\sigma$|
|`\Tau`|$\Tau$||`\tau`|$\tau$|
|`\Upsilon`|$\Upsilon$||`\upsilon`|$\upsilon$|
|`\Phi`|$\Phi$||`\phi`|$\phi$|
|`\Chi`|$\Chi$||`\chi`|$\chi$|
|`\Psi`|$\Psi$||`\psi`|$\psi$|
|`\Omega`|$\Omega$||`\omega`|$\omega$|

268
apps/reader/tex_parser.cpp Normal file
View File

@@ -0,0 +1,268 @@
#include "tex_parser.h"
#include <ion/unicode/utf8_decoder.h>
namespace Reader {
// List of available Symbols
static constexpr char const * k_SymbolsCommands[] = {
"times", "div", "forall", "partial", "exists", "pm", "approx", "infty", "neq", "equiv", "leq", "geq",
"leftarrow", "uparrow", "rightarrow", "downarrow", "leftrightarrow", "updownarrow", "Leftarrow", "Uparrow", "Rightarrow", "Downarrow",
"Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda",
"Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi","Omega",
"alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda",
"mu", "nu", "xi", "omicron", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega",
"sim",
};
// List of the available Symbol's CodePoints in the same order of the Symbol's list
static constexpr uint32_t const k_SymbolsCodePoints[] = {
0xd7, 0xf7, 0x2200, 0x2202, 0x2203, 0xb1, 0x2248, 0x221e, 0x2260, 0x2261, 0x2264, 0x2265,
0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x21d0, 0x21d1, 0x21d2, 0x21d3,
0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b,
0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9,
0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb,
0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9,
0x7e,
};
// List of available Function Commands that don't require a specific handling
static constexpr char const * k_FunctionCommands[] = {
"arcos", "arcsin", "arctan", "arg", "cos", "cosh", "cot", "coth",
"csc", "deg", "det", "dim", "exp", "gcd", "hom", "inf",
"ker", "lg", "lim", "liminf", "limsup", "ln", "log", "max",
"min", "Pr", "sec", "sin", "sinh", "sup", "tan", "tanh"
};
TexParser::TexParser(const char * text, const char * endOfText) :
m_text(text),
m_endOfText(endOfText),
m_hasError(false)
{
}
Layout TexParser::getLayout() {
Layout layout = popText(0);
if (m_hasError) {
return CodePointLayout::Builder(CodePoint(0xfffd));
}
return layout;
}
Layout TexParser::popBlock() {
while (*m_text == ' ') {
m_text ++;
}
if (*m_text == '{') {
m_text ++;
return popText('}');
}
if (*m_text == '\\') {
m_text ++;
return popCommand();
}
if (m_text >= m_endOfText) {
m_hasError = true;
}
UTF8Decoder decoder(m_text);
m_text ++;
return CodePointLayout::Builder(decoder.nextCodePoint());
}
Layout TexParser::popText(char stop) {
HorizontalLayout layout = HorizontalLayout::Builder();
const char * start = m_text;
while (m_text < m_endOfText && *m_text != stop) {
switch (*m_text) {
// TODO: Factorize this code
case '\\':
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
layout.addOrMergeChildAtIndex(popCommand(), layout.numberOfChildren(), false);
start = m_text;
break;
case ' ':
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
start = m_text;
break;
case '^':
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
layout.addOrMergeChildAtIndex(VerticalOffsetLayout::Builder(popBlock(), VerticalOffsetLayoutNode::Position::Superscript), layout.numberOfChildren(), false);
start = m_text;
break;
case '_':
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
layout.addOrMergeChildAtIndex(VerticalOffsetLayout::Builder(popBlock(), VerticalOffsetLayoutNode::Position::Subscript), layout.numberOfChildren(), false);
start = m_text;
break;
default:
m_text ++;
}
}
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
if (layout.numberOfChildren() == 1) {
return layout.squashUnaryHierarchyInPlace();
}
return layout;
}
Layout TexParser::popCommand() {
// TODO: Factorize this code
if (strncmp(k_ceilCommand, m_text, strlen(k_ceilCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_ceilCommand)))) {
m_text += strlen(k_ceilCommand);
return popCeilCommand();
}
}
if (strncmp(k_floorCommand, m_text, strlen(k_floorCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_floorCommand)))) {
m_text += strlen(k_floorCommand);
return popFloorCommand();
}
}
if (strncmp(k_fracCommand, m_text, strlen(k_fracCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_fracCommand)))) {
m_text += strlen(k_fracCommand);
return popFracCommand();
}
}
if (strncmp(k_leftCommand, m_text, strlen(k_leftCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_leftCommand)))) {
m_text += strlen(k_leftCommand);
return popLeftCommand();
}
}
if (strncmp(k_rightCommand, m_text, strlen(k_rightCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_rightCommand)))) {
m_text += strlen(k_rightCommand);
return popRightCommand();
}
}
if (strncmp(k_sqrtCommand, m_text, strlen(k_sqrtCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_sqrtCommand)))) {
m_text += strlen(k_sqrtCommand);
return popSqrtCommand();
}
}
if (strncmp(k_spaceCommand, m_text, strlen(k_spaceCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_spaceCommand)))) {
m_text += strlen(k_spaceCommand);
return popSpaceCommand();
}
}
if (strncmp(k_overrightArrowCommand, m_text, strlen(k_overrightArrowCommand)) == 0) {
if (isCommandEnded(*(m_text + strlen(k_overrightArrowCommand)))) {
m_text += strlen(k_overrightArrowCommand);
return popOverrightarrowCommand();
}
}
for (int i = 0; i < k_NumberOfSymbols; i++) {
if (strncmp(k_SymbolsCommands[i], m_text, strlen(k_SymbolsCommands[i])) == 0) {
if (isCommandEnded(*(m_text + strlen(k_SymbolsCommands[i])))) {
m_text += strlen(k_SymbolsCommands[i]);
return popSymbolCommand(i);
}
}
}
for (int i = 0; i < k_NumberOfFunctionCommands; i++) {
if (strncmp(k_FunctionCommands[i], m_text, strlen(k_FunctionCommands[i])) == 0) {
if (isCommandEnded(*(m_text + strlen(k_FunctionCommands[i])))) {
m_text += strlen(k_FunctionCommands[i]);
return LayoutHelper::String(k_FunctionCommands[i], strlen(k_FunctionCommands[i]));
}
}
}
m_hasError = true;
return EmptyLayout::Builder();
}
// Expressions
Layout TexParser::popCeilCommand() {
Layout ceil = popBlock();
return CeilingLayout::Builder(ceil);
}
Layout TexParser::popFloorCommand() {
Layout floor = popBlock();
return FloorLayout::Builder(floor);
}
Layout TexParser::popFracCommand() {
Layout numerator = popBlock();
Layout denominator = popBlock();
FractionLayout l = FractionLayout::Builder(numerator, denominator);
return l;
}
Layout TexParser::popLeftCommand() {
m_text++;
return LeftParenthesisLayout::Builder();
}
Layout TexParser::popRightCommand() {
m_text++;
return RightParenthesisLayout::Builder();
}
Layout TexParser::popSqrtCommand() {
while (*m_text == ' ') {
m_text ++;
}
if (*m_text == '[') {
m_text ++;
Layout rootFactor = popText(']');
Layout belowRoot = popBlock();
return NthRootLayout::Builder(belowRoot, rootFactor);
}
else {
return NthRootLayout::Builder(popBlock());
}
}
Layout TexParser::popSpaceCommand() {
return LayoutHelper::String(" ", 1);
}
Layout TexParser::popOverrightarrowCommand() {
return VectorLayout::Builder(popBlock());
}
Layout TexParser::popSymbolCommand(int SymbolIndex) {
uint32_t codePoint = k_SymbolsCodePoints[SymbolIndex];
return CodePointLayout::Builder(codePoint);
}
inline bool TexParser::isCommandEnded(char c) const {
return !(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z');
}
}

57
apps/reader/tex_parser.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef __TEX_PARSER_H__
#define __TEX_PARSER_H__
#include <poincare_layouts.h>
#include <poincare/layout_helper.h>
#include <string.h>
using namespace Poincare;
namespace Reader
{
/// @brief Class used in the WordWrapTextView class to parse a Tex expression
class TexParser {
public:
TexParser(const char * text, const char * endOfText);
Layout getLayout();
private:
Layout popBlock();
Layout popText(char stop);
Layout popCommand();
// Expressions
Layout popCeilCommand();
Layout popFloorCommand();
Layout popFracCommand();
Layout popLeftCommand();
Layout popRightCommand();
Layout popSqrtCommand();
Layout popSpaceCommand();
Layout popOverrightarrowCommand();
//Symbols
Layout popSymbolCommand(int SymbolIndex);
const char * m_text;
const char * m_endOfText;
bool m_hasError;
inline bool isCommandEnded(char c) const;
// Expressions that require specific handling
static constexpr char const * k_ceilCommand = "ceil";
static constexpr char const * k_floorCommand = "floor";
static constexpr char const * k_fracCommand = "frac";
static constexpr char const * k_leftCommand = "left";
static constexpr char const * k_rightCommand = "right";
static constexpr char const * k_sqrtCommand = "sqrt";
static constexpr char const * k_spaceCommand = "space";
static constexpr char const * k_overrightArrowCommand = "overrightarrow";
static constexpr int const k_NumberOfSymbols = 71;
static constexpr int const k_NumberOfFunctionCommands = 32;
};
}
#endif

View File

@@ -117,7 +117,7 @@ const char * EndOfPrintableWord(const char * word, const char * end) {
UTF8Decoder decoder(word);
CodePoint codePoint = decoder.nextCodePoint();
const char * result = word;
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%') {
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') {
result = decoder.stringPosition();
if (result >= end) {
break;
@@ -127,4 +127,21 @@ const char * EndOfPrintableWord(const char * word, const char * end) {
return result;
}
const char * StartOfPrintableWord(const char * word, const char * start) {
if (word == start) {
return word;
}
UTF8Decoder decoder(start, word);
CodePoint codePoint = decoder.previousCodePoint();
const char * result = word;
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') {
result = decoder.stringPosition();
if (result <= start) {
break;
}
codePoint = decoder.previousCodePoint();
}
return result;
}
}

View File

@@ -12,6 +12,7 @@ bool stringEndsWith(const char* str, const char* end);
int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize);
void stringNCopy(char* dest, int max, const char* src, int len);
const char * EndOfPrintableWord(const char * word, const char * end);
const char * StartOfPrintableWord(const char * word, const char * start);
}
#endif

View File

@@ -1,6 +1,7 @@
#include "word_wrap_view.h"
#include "utility.h"
#include "tex_parser.h"
#include <poincare/expression.h>
#include "../shared/poincare_helpers.h"
#include <poincare/undefined.h>
@@ -40,16 +41,17 @@ void WordWrapTextView::previousPage() {
const int charWidth = m_font->glyphSize().width();
const int charHeight = m_font->glyphSize().height();
const char * endOfWord = text() + m_pageOffset - 1;
const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
const char * endOfFile = text() + m_length;
const char * endOfWord = text() + m_pageOffset;
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
KDSize textSize = KDSizeZero;
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
while(startOfWord>=text()) {
startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
endOfWord = UTF8Helper::EndOfWord(startOfWord);
startOfWord = StartOfPrintableWord(endOfWord-1, text());
//endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
if (*startOfWord == '%') {
if (updateTextColorBackward(startOfWord)) {
@@ -57,20 +59,28 @@ void WordWrapTextView::previousPage() {
continue;
}
}
if (*startOfWord == '$' && *(endOfWord-1) == '$') {
const int wordMaxLength = 128;
char word[wordMaxLength];
stringNCopy(word, wordMaxLength, startOfWord + 1, endOfWord-startOfWord-2);
Poincare::Expression expr = Poincare::Expression::Parse(word, nullptr);
if (expr.isUninitialized()) {
expr = Poincare::Undefined::Builder();
if (*endOfWord == '$') {
startOfWord = endOfWord - 1;
while (*startOfWord != '$') {
if (startOfWord < text()) {
break; // File isn't rightly formated
}
startOfWord --;
}
Poincare::Layout layout = Shared::PoincareHelpers::CreateLayout(expr);
textSize = layout.layoutSize();
startOfWord --;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 2);
Poincare::Layout layout = parser.getLayout();
textSize = layout.layoutSize();
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
textSize = m_font->stringSizeUntil(startOfWord + 1, endOfWord);
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
}
}
KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y());
@@ -111,7 +121,7 @@ void WordWrapTextView::previousPage() {
m_pageOffset = 0;
}
else {
m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1;
m_pageOffset = EndOfPrintableWord(startOfWord, endOfFile) - text() + 1;
}
markRectAsDirty(bounds());
}
@@ -121,7 +131,12 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
const char * endOfFile = text() + m_length;
const char * startOfWord = text() + m_pageOffset;
const char * endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
const char * endOfWord;
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
} // Else we don't need to update endOfWord
KDPoint textPosition(k_margin, k_margin);
const int wordMaxLength = 128;
@@ -154,18 +169,26 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
continue;
}
}
if (*startOfWord == '$' && *(endOfWord-1) == '$') { // Look for expression
stringNCopy(word, wordMaxLength, startOfWord + 1, endOfWord-startOfWord-2);
Poincare::Expression expr = Poincare::Expression::Parse(word, nullptr);
if (expr.isUninitialized()) {
expr = Poincare::Undefined::Builder();
if (*startOfWord == '$') { // Look for expression
endOfWord = startOfWord + 1;
while (*endOfWord != '$') {
if (endOfWord > endOfFile) {
break; // If we are here, it's bad...
}
endOfWord ++;
}
layout = Shared::PoincareHelpers::CreateLayout(expr);
endOfWord ++;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
layout = parser.getLayout();
textSize = layout.layoutSize();
toDraw = ToDraw::Expression;
}
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
startOfWord ++;
}
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
toDraw = ToDraw::Text;
@@ -187,7 +210,7 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
}
if (toDraw == ToDraw::Expression) {
layout.draw(ctx, textPosition, m_textColor);
layout.draw(ctx, textPosition, m_textColor, m_backgroundColor);
}
else if (toDraw == ToDraw::Text) {
ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor);
@@ -224,7 +247,10 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
}
textPosition = nextTextPosition;
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
} // Else we don't need to update endOfWord
}
m_nextPageOffset = startOfWord - text();

View File

@@ -39,7 +39,7 @@ openocd:
# The flasher target is defined here because otherwise $(%_src) has not been
# fully filled
flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(kandinsky_src)
flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(simple_kandinsky_src)
$(BUILD_DIR)/flasher.light.$(EXE): $(call flavored_object_for,$(flasher_src),light usbxip)
$(BUILD_DIR)/flasher.verbose.$(EXE): $(call flavored_object_for,$(flasher_src),usbxip)
$(BUILD_DIR)/flasher.verbose.flash.$(EXE): $(call flavored_object_for,$(flasher_src))
@@ -48,7 +48,7 @@ $(BUILD_DIR)/flasher.%.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld
$(BUILD_DIR)/flasher.%.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/internal_flash.ld
#TODO Do not build all apps... Put elsewhere?
bench_src = $(ion_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src)
bench_src = $(ion_src) $(liba_src) $(default_kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src)
$(BUILD_DIR)/bench.ram.$(EXE): $(call flavored_object_for,$(bench_src),consoleuart usbxip)
$(BUILD_DIR)/bench.ram.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/bench
$(BUILD_DIR)/bench.ram.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld

View File

@@ -1,7 +1,7 @@
HANDY_TARGETS += test.external_flash.write test.external_flash.read
$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src)
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src)
$(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src))
$(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src))

View File

@@ -7,7 +7,7 @@ HANDY_TARGETS_EXTENSIONS ?=
# Epsilon base target
base_src = $(ion_src) $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src)
base_src = $(ion_src) $(liba_src) $(default_kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src)
epsilon_src = $(base_src) $(apps_src)

View File

@@ -21,10 +21,8 @@ kandinsky_src += $(addprefix kandinsky/src/,\
rect.cpp \
)
kandinsky_src += $(addprefix kandinsky/fonts/, \
LargeFont.ttf \
SmallFont.ttf \
)
simple_kandinsky_src := $(kandinsky_src)
default_kandinsky_src := $(kandinsky_src)
tests_src += $(addprefix kandinsky/test/,\
color.cpp\
@@ -53,16 +51,53 @@ $(eval $(call rule_for, \
RASTERIZER := $(BUILD_DIR)/kandinsky/fonts/rasterizer
# Define a rasterizing recipe. Parameters : font name, size, packed_width, packed_height
# Define a rasterizing recipe. Parameters : font source, font name, size, packed_width, packed_height
define raster_font
$(call rule_for, \
RASTER, \
kandinsky/fonts/$(1).cpp, \
kandinsky/fonts/$(2).cpp, \
kandinsky/fonts/$(1).ttf $$(RASTERIZER), \
$$(RASTERIZER) $$< $(2) $(2) $(3) $(4) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png), \
$$(RASTERIZER) $$< $(3) $(4) $(4) $(5) $(6) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png), \
global \
)
endef
$(eval $(call raster_font,SmallFont,12,7,14))
$(eval $(call raster_font,LargeFont,16,10,18))
ifdef HAS_READER
kandinsky_src += $(addprefix kandinsky/fonts/, \
LargeFontExtended.ttf \
SmallFontExtended.ttf \
LargeFontSimple.ttf \
SmallFontSimple.ttf \
)
default_kandinsky_src += $(addprefix kandinsky/fonts/, \
LargeFontExtended.ttf \
SmallFontExtended.ttf \
)
simple_kandinsky_src += $(addprefix kandinsky/fonts/, \
LargeFontSimple.ttf \
SmallFontSimple.ttf \
)
$(eval $(call raster_font,SmallFont,SmallFontExtended,1,12,7,14))
$(eval $(call raster_font,LargeFont,LargeFontExtended,1,16,10,18))
$(eval $(call raster_font,SmallFont,SmallFontSimple,0,12,7,14))
$(eval $(call raster_font,LargeFont,LargeFontSimple,0,16,10,18))
else
kandinsky_src += $(addprefix kandinsky/fonts/, \
LargeFont.ttf \
SmallFont.ttf \
)
simple_kandinsky_src = $(kandinsky_src)
default_kandinsky_src = $(kandinsky_src)
$(eval $(call raster_font,SmallFont,SmallFontSimple,0,12,7,14))
$(eval $(call raster_font,LargeFont,LargeFontSimple,0,16,10,18))
endif

View File

@@ -8,7 +8,7 @@
/* This array lists the code points that are rasterized by rasterizer.c. We put
* most characters from the LATIN charset, and some mathematical characters. */
uint32_t CodePoints[] = {
uint32_t SimpleCodePoints[] = {
0x20, // // SPACE
0x21, // ! // EXCLAMATION MARK
0x22, // " // QUOTATION MARK
@@ -153,6 +153,206 @@ uint32_t CodePoints[] = {
0x1d422, // 𝐢 // MATHEMATICAL BOLD SMALL I"
};
int NumberOfCodePoints = sizeof(CodePoints)/sizeof(CodePoints[0]);
uint32_t ExtendedCodePoints[] = {
0x20, // // SPACE
0x21, // ! // EXCLAMATION MARK
0x22, // " // QUOTATION MARK
0x23, // # // NUMBER SIGN
0x24, // $ // DOLLAR SIGN
0x25, // % // PERCENT SIGN
0x26, // & // AMPERSAND
0x27, // ' // APOSTROPHE
0x28, // ( // LEFT PARENTHESIS
0x29, // ) // RIGHT PARENTHESIS
0x2a, // * // ASTERISK
0x2b, // + // PLUS SIGN
0x2c, // , // COMMA
0x2d, // - // HYPHEN-MINUS
0x2e, // . // FULL STOP
0x2f, // / // SOLIDUS
0x30, // 0 // DIGIT ZERO
0x31, // 1 // DIGIT ONE
0x32, // 2 // DIGIT TWO
0x33, // 3 // DIGIT THREE
0x34, // 4 // DIGIT FOUR
0x35, // 5 // DIGIT FIVE
0x36, // 6 // DIGIT SIX
0x37, // 7 // DIGIT SEVEN
0x38, // 8 // DIGIT EIGHT
0x39, // 9 // DIGIT NINE
0x3a, // : // COLON
0x3b, // ; // SEMICOLON
0x3c, // < // LESS-THAN SIGN
0x3d, // = // EQUALS SIGN
0x3e, // > // GREATER-THAN SIGN
0x3f, // ? // QUESTION MARK
0x40, // @ // COMMERCIAL AT
0x41, // A // LATIN CAPITAL LETTER A
0x42, // B // LATIN CAPITAL LETTER B
0x43, // C // LATIN CAPITAL LETTER C
0x44, // D // LATIN CAPITAL LETTER D
0x45, // E // LATIN CAPITAL LETTER E
0x46, // F // LATIN CAPITAL LETTER F
0x47, // G // LATIN CAPITAL LETTER G
0x48, // H // LATIN CAPITAL LETTER H
0x49, // I // LATIN CAPITAL LETTER I
0x4a, // J // LATIN CAPITAL LETTER J
0x4b, // K // LATIN CAPITAL LETTER K
0x4c, // L // LATIN CAPITAL LETTER L
0x4d, // M // LATIN CAPITAL LETTER M
0x4e, // N // LATIN CAPITAL LETTER N
0x4f, // O // LATIN CAPITAL LETTER O
0x50, // P // LATIN CAPITAL LETTER P
0x51, // Q // LATIN CAPITAL LETTER Q
0x52, // R // LATIN CAPITAL LETTER R
0x53, // S // LATIN CAPITAL LETTER S
0x54, // T // LATIN CAPITAL LETTER T
0x55, // U // LATIN CAPITAL LETTER U
0x56, // V // LATIN CAPITAL LETTER V
0x57, // W // LATIN CAPITAL LETTER W
0x58, // X // LATIN CAPITAL LETTER X
0x59, // Y // LATIN CAPITAL LETTER Y
0x5a, // Z // LATIN CAPITAL LETTER Z
0x5b, // [ // LEFT SQUARE BRACKET
0x5c, // \ // REVERSE SOLIDUS
0x5d, // ] // RIGHT SQUARE BRACKET
0x5e, // ^ // CIRCUMFLEX ACCENT
0x5f, // _ // LOW LINE
0x60, // ` // GRAVE ACCENT
0x61, // a // LATIN SMALL LETTER A
0x62, // b // LATIN SMALL LETTER B
0x63, // c // LATIN SMALL LETTER C
0x64, // d // LATIN SMALL LETTER D
0x65, // e // LATIN SMALL LETTER E
0x66, // f // LATIN SMALL LETTER F
0x67, // g // LATIN SMALL LETTER G
0x68, // h // LATIN SMALL LETTER H
0x69, // i // LATIN SMALL LETTER I
0x6a, // j // LATIN SMALL LETTER J
0x6b, // k // LATIN SMALL LETTER K
0x6c, // l // LATIN SMALL LETTER L
0x6d, // m // LATIN SMALL LETTER M
0x6e, // n // LATIN SMALL LETTER N
0x6f, // o // LATIN SMALL LETTER O
0x70, // p // LATIN SMALL LETTER P
0x71, // q // LATIN SMALL LETTER Q
0x72, // r // LATIN SMALL LETTER R
0x73, // s // LATIN SMALL LETTER S
0x74, // t // LATIN SMALL LETTER T
0x75, // u // LATIN SMALL LETTER U
0x76, // v // LATIN SMALL LETTER V
0x77, // w // LATIN SMALL LETTER W
0x78, // x // LATIN SMALL LETTER X
0x79, // y // LATIN SMALL LETTER Y
0x7a, // z // LATIN SMALL LETTER Z
0x7b, // { // LEFT CURLY BRACKET
0x7c, // | // VERTICAL LINE
0x7d, // } // RIGHT CURLY BRACKET
0x7e, // ~ // TILDE
0xb0, // ° // DEGREE SIGN
0xb1, // ± // PLUS OR MINUS
0xb7, // · // MIDDLE DOT
0xc6, // Æ // LATIN CAPITAL LETTER AE
0xd0, // Ð // LATIN CAPITAL LETTER ETH
0xd7, // × // MULTIPLICATION SIGN
0xd8, // Ø // LATIN CAPITAL LETTER O WITH STROKE
0xde, // Þ // LATIN CAPITAL LETTER THORN
0xdf, // ß // LATIN SMALL LETTER SHARP S
0xe6, // æ // LATIN SMALL LETTER AE
0xf0, // ð // LATIN SMALL LETTER ETH
0xf7, // ÷ // DIVISION SIGN
0xf8, // ø // LATIN SMALL LETTER O WITH STROKE
0xfe, // þ // LATIN SMALL LETTER THORN
0x300, // ̀ // COMBINING GRAVE ACCENT
0x301, // ́ // COMBINING ACUTE ACCENT
0x302, // ̂ // COMBINING CIRCUMFLEX ACCENT
0x303, // ̃ // COMBINING TILDE
0x305, // ̅ // COMBINING OVERLINE
0x308, // ̈ // COMBINING DIAERESIS
0x30a, // ̊ // COMBINING RING ABOVE
0x30b, // ˝// COMBINING DOUBLE ACUTE ACCENT
0x327, // ̧ // COMBINING CEDILLA
0x391, // Α // GREEK CAPITAL LETTER ALPHA
0x392, // Β // GREEK CAPITAL LETTER BETA
0x393, // Γ // GREEK CAPITAL LETTER GAMMA
0x394, // Δ // GREEK CAPITAL LETTER DELTA
0x395, // Ε // GREEK CAPITAL LETTER EPSILON
0x396, // Ζ // GREEK CAPITAL LETTER ZETA
0x397, // Η // GREEK CAPITAL LETTER ETA
0x398, // Θ // GREEK CAPITAL LETTER THETA
0x399, // Ι // GREEK CAPITAL LETTER IOTA
0x39a, // Κ // GREEK CAPITAL LETTER KAPPA
0x39b, // Λ // GREEK CAPITAL LETTER LAMBDA
0x39c, // Μ // GREEK CAPITAL LETTER MU
0x39d, // Ν // GREEK CAPITAL LETTER NU
0x39e, // Ξ // GREEK CAPITAL LETTER KSI
0x39f, // Ο // GREEK CAPITAL LETTER OMICRON
0x3a0, // Π // GREEK CAPITAL LETTER PI
0x3a1, // Ρ // GREEK CAPITAL LETTER RHO
0x3a3, // Σ // GREEK CAPITAL LETTER SIGMA
0x3a4, // Τ // GREEK CAPITAL LETTER TAU
0x3a5, // Υ // GREEK CAPITAL LETTER UPSILON
0x3a6, // Φ // GREEK CAPITAL LETTER PHI
0x3a7, // Χ // GREEK CAPITAL LETTER KHI
0x3a8, // Ψ // GREEK CAPITAL LETTER PSI
0x3a9, // Ω // GREEK CAPITAL LETTER OMEGA
0x3b1, // α // GREEK SMALL LETTER ALPHA
0x3b2, // β // GREEK SMALL LETTER BETA
0x3b3, // γ // GREEK SMALL LETTER GAMMA
0x3b4, // δ // GREEK SMALL LETTER DELTA
0x3b5, // ε // GREEK SMALL LETTER EPSILON
0x3b6, // ζ // GREEK SMALL LETTER ZETA
0x3b7, // η // GREEK SMALL LETTER ETA
0x3b8, // θ // GREEK SMALL LETTER THETA
0x3b9, // ι // GREEK SMALL LETTER IOTA
0x3ba, // κ // GREEK SMALL LETTER KAPPA
0x3bb, // λ // GREEK SMALL LETTER LAMBDA
0x3bc, // μ // GREEK SMALL LETTER MU
0x3bd, // ν // GREEK SMALL LETTER NU
0x3be, // ξ // GREEK SMALL LETTER KSI
0x3bf, // ο // GREEK SMALL LETTER OMICRON
0x3c0, // π // GREEK SMALL LETTER PI
0x3c1, // ρ // GREEK SMALL LETTER RHO
0x3c3, // σ // GREEK SMALL LETTER SIGMA
0x3c4, // τ // GREEK SMALL LETTER TAU
0x3c5, // υ // GREEK SMALL LETTER UPSILON
0x3c6, // φ // GREEK SMALL LETTER PHI
0x3c7, // χ // GREEK SMALL LETTER KHI
0x3c8, // ψ // GREEK SMALL LETTER PSI
0x3c9, // ω // GREEK SMALL LETTER OMEGA
0x1d07, // ᴇ // LATIN LETTER SMALL CAPITAL E
0x212f, // // SCRIPT SMALL E
0x2190, // ← // BACKWARD ARROW (leftarrow)
0x2191, // ↑ // TOP ARROW (uparrow)
0x2192, // → // FORWARD ARROW (rightarrow)
0x2193, // ↓ // BOTTOM ARROW (downarrow)
0x2194, // ↔ // BACKWARD FORWARD ARROW (leftrightarrow)
0x2195, // ↕ // TOP BOTTOM ARROW (updownarrow)
0x21d0, // ⇐ // DOUBLE BACKWARD ARROW (Leftarrow)
0x21d1, // ⇑ // DOUBLE TOP ARROW (Uparrow)
0x21d2, // ⇒ // DOUBLE FORWARD ARROW (Rightarrow)
0x21d3, // ⇓ // DOUBLE BOTTOM ARROW (Downarrow)
0x2200, // ∀ // FORALL
0x2202, // ∂ // PARTIAL
0x2203, // ∃ // EXIST
0x2211, // ∑ // N-ARY SUMMATION
0x221a, // √ // SQUARE ROOT
0x221e, // ∞ // INFINITY
0x222b, // ∫ // INTEGRAL
0x2248, // ≈ // ALMOST EQUAL TO
0x2260, // ≠ // NOT EQUAL TO
0x2261, // ≡ // IS CONGRUENT TO
0x2264, // ≤ // LESS-THAN OR EQUAL TO
0x2265, // ≥ // GREATER-THAN OR EQUAL TO
0xFFFD, // <20> // REPLACEMENT CHARACTER
0x1d422, // 𝐢 // MATHEMATICAL BOLD SMALL I"
};
int NumberOfSimpleCodePoints = sizeof(SimpleCodePoints)/sizeof(SimpleCodePoints[0]);
int NumberOfExtendedCodePoints = sizeof(ExtendedCodePoints)/sizeof(ExtendedCodePoints[0]);
#endif

View File

@@ -16,6 +16,7 @@
#include FT_FREETYPE_H
#include "code_points.h"
#include "../../ion/src/external/lz4/lz4hc.h"
@@ -49,17 +50,18 @@ int main(int argc, char * argv[]) {
FT_Face face;
image_t bitmap_image;
int expectedNumberOfArguments = 8;
int expectedNumberOfArguments = 9;
#ifdef GENERATE_PNG
expectedNumberOfArguments = 9;
expectedNumberOfArguments = 10;
#endif
if (argc != expectedNumberOfArguments) {
#ifdef GENERATE_PNG
fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_cpp output_png\n", argv[0]);
fprintf(stderr, "Usage: %s font_file extended glyph_width glyph_height font_name output_cpp output_png\n", argv[0]);
#else
fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_cpp\n", argv[0]);
fprintf(stderr, "Usage: %s font_file extended glyph_width glyph_height font_name output_cpp\n", argv[0]);
#endif
fprintf(stderr, " font_file: Path of the font file to load\n");
fprintf(stderr, " extended: 0 if simple set of code points, else 1\n");
fprintf(stderr, " glyph_width: Width of bitmap glyphs, in pixels\n");
fprintf(stderr, " glyph_height: Height of bitmap glyphs, in pixels\n");
fprintf(stderr, " packed_glyph_width: Minimal glyph width in pixels. Pass 0 if unsure.\n");
@@ -73,16 +75,20 @@ int main(int argc, char * argv[]) {
}
char * font_file = argv[1];
int requested_glyph_width = atoi(argv[2]);
int requested_glyph_height = atoi(argv[3]);
int packed_glyph_width = atoi(argv[4]);
int packed_glyph_height = atoi(argv[5]);
char * font_name = argv[6];
char * output_cpp = argv[7];
int extended_set = atoi(argv[2]);
int requested_glyph_width = atoi(argv[3]);
int requested_glyph_height = atoi(argv[4]);
int packed_glyph_width = atoi(argv[5]);
int packed_glyph_height = atoi(argv[6]);
char * font_name = argv[7];
char * output_cpp = argv[8];
#ifdef GENERATE_PNG
char * output_png = argv[8];
char * output_png = argv[9];
#endif
uint32_t * CodePoints = extended_set ? ExtendedCodePoints : SimpleCodePoints;
int NumberOfCodePoints = extended_set ? NumberOfExtendedCodePoints : NumberOfSimpleCodePoints;
ENSURE(!FT_Init_FreeType(&library), "Initializing library");
// 0 means we're picking the first face in the provided file

View File

@@ -155,7 +155,7 @@ KDFont::GlyphIndex KDFont::indexForCodePoint(CodePoint c) const {
return endPair->glyphIndex();
}
NoMatchingGlyph:
assert(CodePoints[IndexForReplacementCharacterCodePoint] == 0xFFFD);
assert(SimpleCodePoints[IndexForReplacementCharacterCodePoint] == 0xFFFD);
return IndexForReplacementCharacterCodePoint;
#endif
}

View File

@@ -26,6 +26,7 @@ poincare_src += $(addprefix poincare/src/,\
right_square_bracket_layout.cpp \
sequence_layout.cpp \
sum_layout.cpp \
vector_layout.cpp \
vertical_offset_layout.cpp \
)

View File

@@ -23,6 +23,7 @@ class LayoutCursor final {
friend class MatrixLayoutNode;
friend class NthRootLayoutNode;
friend class SequenceLayoutNode;
friend class VectorLayoutNode;
friend class VerticalOffsetLayoutNode;
public:
constexpr static KDCoordinate k_cursorWidth = 1;

View File

@@ -40,6 +40,7 @@ public:
RightParenthesisLayout,
RightSquareBracketLayout,
SumLayout,
VectorLayout,
VectorNormLayout,
VerticalOffsetLayout
};

View File

@@ -0,0 +1,53 @@
#ifndef POINCARE_VECTOR_LAYOUT_NODE_H
#define POINCARE_VECTOR_LAYOUT_NODE_H
#include <poincare/layout.h>
#include <poincare/layout_cursor.h>
#include <poincare/serialization_helper.h>
namespace Poincare {
class VectorLayoutNode final : public LayoutNode {
public:
// Layout
Type type() const override { return Type::VectorLayout; }
// SerializationHelperInterface
int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override {
return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "vector", true, 0);
}
virtual void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false);
virtual void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false);
// TreeNode
size_t size() const override { return sizeof(VectorLayoutNode); }
int numberOfChildren() const override { return 1; }
#if POINCARE_TREE_LOG
void logNodeName(std::ostream & stream) const override {
stream << "VectorLayout";
}
#endif
constexpr static KDCoordinate k_arrowWidth = 5;
constexpr static KDCoordinate k_arrowHeight = 9;
protected:
virtual KDSize computeSize();
virtual KDCoordinate computeBaseline();
virtual KDPoint positionOfChild(LayoutNode * child);
private:
virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed);
constexpr static KDCoordinate k_sideMargin = 2;
constexpr static KDCoordinate k_topMargin = 1;
constexpr static KDCoordinate k_arrowLineHeight = 1; // k_arrowHeight - k_arrowLineHeight must be even
};
class VectorLayout final : public Layout {
public:
static VectorLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder<VectorLayout, VectorLayoutNode>({child}); }
VectorLayout() = delete;
};
}
#endif

View File

@@ -24,6 +24,7 @@
#include <poincare/right_square_bracket_layout.h>
#include <poincare/square_bracket_layout.h>
#include <poincare/sum_layout.h>
#include <poincare/vector_layout.h>
#include <poincare/vector_norm_layout.h>
#include <poincare/vertical_offset_layout.h>

View File

@@ -375,6 +375,7 @@ template VectorCross TreeHandle::FixedArityBuilder<VectorCross, VectorCrossNode>
template VectorDot TreeHandle::FixedArityBuilder<VectorDot, VectorDotNode>(const Tuple &);
template VectorNorm TreeHandle::FixedArityBuilder<VectorNorm, VectorNormNode>(const Tuple &);
template VectorNormLayout TreeHandle::FixedArityBuilder<VectorNormLayout, VectorNormLayoutNode>(const Tuple &);
template VectorLayout TreeHandle::FixedArityBuilder<VectorLayout, VectorLayoutNode>(const Tuple &);
template MatrixLayout TreeHandle::NAryBuilder<MatrixLayout, MatrixLayoutNode>(const Tuple &);
}

View File

@@ -0,0 +1,78 @@
#include <poincare/vector_layout.h>
namespace Poincare
{
const uint8_t arrowMask[VectorLayoutNode::k_arrowHeight][VectorLayoutNode::k_arrowWidth] = {
{0xff, 0xf7, 0xff, 0xff, 0xff},
{0xf3, 0x2c, 0xd9, 0xff, 0xff},
{0xff, 0x93, 0x46, 0xfb, 0xff},
{0xff, 0xfb, 0x46, 0x93, 0xff},
{0x13, 0x13, 0x13, 0x13, 0xf0},
{0xff, 0xfb, 0x46, 0x93, 0xff},
{0xff, 0x93, 0x46, 0xfb, 0xff},
{0xf3, 0x2c, 0xd9, 0xff, 0xff},
{0xff, 0xf7, 0xff, 0xff, 0xff}
};
void VectorLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) {
if (cursor->layoutNode() == childAtIndex(0)
&& cursor->position() == LayoutCursor::Position::Left)
{
// Case: Left of the operand. Go Left of the brackets.
cursor->setLayout(this);
return;
}
assert(cursor->layoutNode() == this);
if (cursor->position() == LayoutCursor::Position::Right) {
// Case: Right of the brackets. Go Right of the operand.
cursor->setLayout(childAtIndex(0));
return;
}
assert(cursor->position() == LayoutCursor::Position::Left);
// Case: Left of the brackets. Ask the parent.
LayoutNode * parentNode = parent();
if (parentNode != nullptr) {
parentNode->moveCursorLeft(cursor, shouldRecomputeLayout);
}
}
void VectorLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) {
if (cursor->layoutNode() == childAtIndex(0)
&& cursor->position() == LayoutCursor::Position::Right)
{
// Case: Right of the operand. Go Right of the brackets.
cursor->setLayout(this);
return;
}
assert(cursor->layoutNode() == this);
if (cursor->position() == LayoutCursor::Position::Left) {
// Case: Left of the brackets. Go Left of the operand.
cursor->setLayout(childAtIndex(0));
return;
}
assert(cursor->position() == LayoutCursor::Position::Right);
// Case: Right of the brackets. Ask the parent.
LayoutNode * parentNode = parent();
if (parentNode != nullptr) {
parentNode->moveCursorRight(cursor, shouldRecomputeLayout);
}
}
KDSize VectorLayoutNode::computeSize() {
KDSize size = childAtIndex(0)->layoutSize();
return KDSize(2 * k_sideMargin + size.width() + k_arrowWidth + k_sideMargin, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + size.height());
}
KDCoordinate VectorLayoutNode::computeBaseline() {
return childAtIndex(0)->baseline() + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight + k_topMargin;
}
KDPoint VectorLayoutNode::positionOfChild(LayoutNode * child) {
assert(child == childAtIndex(0));
return KDPoint(k_sideMargin * 2, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight);
}
void VectorLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart, Layout * selectionEnd, KDColor selectionColor) {
KDColor workingBuffer[k_arrowWidth * k_arrowHeight];
ctx->fillRect(KDRect(p.x() + k_sideMargin, p.y() + k_topMargin + (k_arrowHeight-k_arrowLineHeight)/2, 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), k_arrowLineHeight), expressionColor);
ctx->blendRectWithMask(KDRect(p.x() + 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), p.y() + k_topMargin, k_arrowWidth, k_arrowHeight), expressionColor, (const uint8_t *)arrowMask, workingBuffer);
}
}