From 60cbc57a0782336ac7b0fa255ca1a166dd7894de Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 13:36:18 +0200 Subject: [PATCH 001/295] [ion/device] Fix unresponsive keyboard --- ion/src/device/events_keyboard_platform.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ion/src/device/events_keyboard_platform.cpp b/ion/src/device/events_keyboard_platform.cpp index 4c2b1a605..0ea71ee61 100644 --- a/ion/src/device/events_keyboard_platform.cpp +++ b/ion/src/device/events_keyboard_platform.cpp @@ -13,7 +13,7 @@ Event getPlatformEvent() { bool usbPlugged = USB::isPlugged(); if (usbPlugged != sLastUSBPlugged) { sLastUSBPlugged = usbPlugged; - return Events::USBPlug; + return USBPlug; } // Second, check if the USB device has been connected to an USB host @@ -21,9 +21,11 @@ Event getPlatformEvent() { if (usbEnumerated != sLastUSBEnumerated) { sLastUSBEnumerated = usbEnumerated; if (usbEnumerated) { - return Events::USBEnumeration; + return USBEnumeration; } } + + return None; } } From bb89ae9211e37a5fd249d6e66ee3a34410d37ee9 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 09:41:54 +0200 Subject: [PATCH 002/295] [python/kandinsky] Color can now be passed as a tuple The values are expected to be integers between 0 and 255 --- python/port/mod/kandinsky/modkandinsky.cpp | 43 +++++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index e36b5a462..db688ae29 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -1,9 +1,35 @@ extern "C" { #include "modkandinsky.h" +#include +#include } #include #include "port.h" +static KDColor ColorForTuple(mp_obj_t tuple) { + size_t len; + mp_obj_t * elem; + + mp_obj_get_array(tuple, &len, &elem); + if (len != 3) { + mp_raise_TypeError("color needs 3 components"); + } + + return KDColor::RGB888( + mp_obj_get_int(elem[0]), + mp_obj_get_int(elem[1]), + mp_obj_get_int(elem[2]) + ); +} + +static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) { + mp_obj_tuple_t * t = static_cast(MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL))); + t->items[0] = MP_OBJ_NEW_SMALL_INT(r); + t->items[1] = MP_OBJ_NEW_SMALL_INT(g); + t->items[2] = MP_OBJ_NEW_SMALL_INT(b); + return MP_OBJ_FROM_PTR(t); +} + /* KDIonContext::sharedContext needs to be set to the wanted Rect before * calling kandinsky_get_pixel, kandinsky_set_pixel and kandinsky_draw_string. * We do this here with displaySandbox(), which pushes the SandboxController on @@ -11,28 +37,25 @@ extern "C" { * KDIonContext::sharedContext is set to the frame of the last object drawn. */ mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) { - return - MP_OBJ_NEW_SMALL_INT( - KDColor::RGB888( - mp_obj_get_int(red), - mp_obj_get_int(green), - mp_obj_get_int(blue) - ) - ); + return TupleForRGB( + mp_obj_get_int(red), + mp_obj_get_int(green), + mp_obj_get_int(blue) + ); } mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) { KDColor c = KDIonContext::sharedContext()->getPixel( KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)) ); - return MP_OBJ_NEW_SMALL_INT(c); + return TupleForRGB(c.red(), c.green(), c.blue()); } mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) { MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->setPixel( KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)), - KDColor::RGB16(mp_obj_get_int(color)) + ColorForTuple(color) ); return mp_const_none; } From 926b96cafbf7176bb39e78d7fa1617f25ddc254f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 09:57:28 +0200 Subject: [PATCH 003/295] [python/kandinsky] Expose the fill_rect method Which allows for *much* faster graphics --- python/port/genhdr/qstrdefs.in.h | 1 + python/port/mod/kandinsky/modkandinsky.cpp | 13 +++++++++++++ python/port/mod/kandinsky/modkandinsky.h | 1 + python/port/mod/kandinsky/modkandinsky_table.c | 2 ++ 4 files changed, 17 insertions(+) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index f905e1dea..540a17da4 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -21,6 +21,7 @@ QCFG(BYTES_IN_HASH, (2)) Q(kandinsky) Q(color) Q(draw_string) +Q(fill_rect) Q(get_pixel) Q(set_pixel) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index db688ae29..3878f33a3 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -69,3 +69,16 @@ mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y) { return mp_const_none; } +mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + KDIonContext::sharedContext()->fillRect( + KDRect( + mp_obj_get_int(args[0]), + mp_obj_get_int(args[1]), + mp_obj_get_int(args[2]), + mp_obj_get_int(args[3]) + ), + ColorForTuple(args[4]) + ); + return mp_const_none; +} diff --git a/python/port/mod/kandinsky/modkandinsky.h b/python/port/mod/kandinsky/modkandinsky.h index c9dc84989..11bc43b0b 100644 --- a/python/port/mod/kandinsky/modkandinsky.h +++ b/python/port/mod/kandinsky/modkandinsky.h @@ -4,3 +4,4 @@ mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue); mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y); mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color); mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y); +mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t *args); diff --git a/python/port/mod/kandinsky/modkandinsky_table.c b/python/port/mod/kandinsky/modkandinsky_table.c index ee402fd09..2a8e19c5c 100644 --- a/python/port/mod/kandinsky/modkandinsky_table.c +++ b/python/port/mod/kandinsky/modkandinsky_table.c @@ -4,6 +4,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_color_obj, modkandinsky_color); STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_draw_string_obj, modkandinsky_draw_string); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_rect_obj, 5, 5, modkandinsky_fill_rect); STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) }, @@ -11,6 +12,7 @@ STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_get_pixel), (mp_obj_t)&modkandinsky_get_pixel_obj }, { MP_ROM_QSTR(MP_QSTR_set_pixel), (mp_obj_t)&modkandinsky_set_pixel_obj }, { MP_ROM_QSTR(MP_QSTR_draw_string), (mp_obj_t)&modkandinsky_draw_string_obj }, + { MP_ROM_QSTR(MP_QSTR_fill_rect), (mp_obj_t)&modkandinsky_fill_rect_obj }, }; STATIC MP_DEFINE_CONST_DICT(modkandinsky_module_globals, modkandinsky_module_globals_table); From ec50e75a3aae275abe8c0d811f4f9c92a0f50e3d Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 10:12:39 +0200 Subject: [PATCH 004/295] [python/kandinsky] Bubble-up input errors before switching to draw mode --- python/port/mod/kandinsky/modkandinsky.cpp | 41 +++++++++++----------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 3878f33a3..3fceb11d9 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -44,41 +44,42 @@ mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) { ); } +/* Calling ExecutionEnvironment::displaySandbox() hides the console and switches + * to another mode. So it's a good idea to retrieve and handle input parameters + * before calling displaySandbox, otherwise error messages (such as TypeError) + * won't be visible until the user comes back to the console screen. */ + mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) { - KDColor c = KDIonContext::sharedContext()->getPixel( - KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)) - ); + KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); + KDColor c = KDIonContext::sharedContext()->getPixel(point); return TupleForRGB(c.red(), c.green(), c.blue()); } mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) { + KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); + KDColor kdColor = ColorForTuple(color); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); - KDIonContext::sharedContext()->setPixel( - KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)), - ColorForTuple(color) - ); + KDIonContext::sharedContext()->setPixel(point, kdColor); return mp_const_none; } mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y) { + const char * kdText = mp_obj_str_get_str(text); + KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); - KDIonContext::sharedContext()->drawString( - mp_obj_str_get_str(text), - KDPoint(mp_obj_get_int(x), mp_obj_get_int(y)) - ); + KDIonContext::sharedContext()->drawString(kdText, point); return mp_const_none; } mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { - MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); - KDIonContext::sharedContext()->fillRect( - KDRect( - mp_obj_get_int(args[0]), - mp_obj_get_int(args[1]), - mp_obj_get_int(args[2]), - mp_obj_get_int(args[3]) - ), - ColorForTuple(args[4]) + KDRect rect( + mp_obj_get_int(args[0]), + mp_obj_get_int(args[1]), + mp_obj_get_int(args[2]), + mp_obj_get_int(args[3]) ); + KDColor color = ColorForTuple(args[4]); + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + KDIonContext::sharedContext()->fillRect(rect, color); return mp_const_none; } From 42f72eee7757172629848793c0efeeddff6e5aa6 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 10:24:38 +0200 Subject: [PATCH 005/295] [python/kandinsky] draw_string takes two optional colors (text/bg) --- python/port/mod/kandinsky/modkandinsky.cpp | 10 ++++++---- python/port/mod/kandinsky/modkandinsky.h | 2 +- python/port/mod/kandinsky/modkandinsky_table.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 3fceb11d9..eacc184ae 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -63,11 +63,13 @@ mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) { return mp_const_none; } -mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y) { - const char * kdText = mp_obj_str_get_str(text); - KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); +mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { + const char * text = mp_obj_str_get_str(args[0]); + KDPoint point(mp_obj_get_int(args[1]), mp_obj_get_int(args[2])); + KDColor textColor = (n_args >= 4) ? ColorForTuple(args[3]) : KDColorBlack; + KDColor backgroundColor = (n_args >= 5) ? ColorForTuple(args[4]) : KDColorWhite; MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); - KDIonContext::sharedContext()->drawString(kdText, point); + KDIonContext::sharedContext()->drawString(text, point, KDFont::LargeFont, textColor, backgroundColor); return mp_const_none; } diff --git a/python/port/mod/kandinsky/modkandinsky.h b/python/port/mod/kandinsky/modkandinsky.h index 11bc43b0b..c346650cd 100644 --- a/python/port/mod/kandinsky/modkandinsky.h +++ b/python/port/mod/kandinsky/modkandinsky.h @@ -3,5 +3,5 @@ mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue); mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y); mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color); -mp_obj_t modkandinsky_draw_string(mp_obj_t text, mp_obj_t x, mp_obj_t y); +mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t *args); diff --git a/python/port/mod/kandinsky/modkandinsky_table.c b/python/port/mod/kandinsky/modkandinsky_table.c index 2a8e19c5c..802bee555 100644 --- a/python/port/mod/kandinsky/modkandinsky_table.c +++ b/python/port/mod/kandinsky/modkandinsky_table.c @@ -3,7 +3,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_color_obj, modkandinsky_color); STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_draw_string_obj, modkandinsky_draw_string); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_string_obj, 3, 5, modkandinsky_draw_string); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_rect_obj, 5, 5, modkandinsky_fill_rect); STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { From 8308068b694b397bbcc92ca3552541ec55ffa233 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 17 Apr 2019 11:20:09 +0200 Subject: [PATCH 006/295] [apps/code] Add catalog entry for kandinsky.fill_rect --- apps/code/catalog.de.i18n | 1 + apps/code/catalog.en.i18n | 1 + apps/code/catalog.es.i18n | 1 + apps/code/catalog.fr.i18n | 1 + apps/code/catalog.pt.i18n | 1 + apps/code/catalog.universal.i18n | 1 + apps/code/python_toolbox.cpp | 4 +++- 7 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 2bbcd18ff..e5a0be097 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -36,6 +36,7 @@ PythonEval = "Returns the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" PythonFmod = "a modulo b" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index d9ec7bfad..4fd635083 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -36,6 +36,7 @@ PythonEval = "Returns the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" PythonFmod = "a modulo b" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index d9ec7bfad..4fd635083 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -36,6 +36,7 @@ PythonEval = "Returns the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" PythonFmod = "a modulo b" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 109faebe0..30a6247b1 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -36,6 +36,7 @@ PythonEval = "Evalue l'expression en argument " PythonExp = "Fonction exponentielle" PythonExpm1 = "Calcul de exp(x)-1" PythonFabs = "Valeur absolue" +PythonFillRect = "Remplit un rectangle" PythonFloat = "Conversion en flottant" PythonFloor = "Partie entière" PythonFmod = "a modulo b" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 2d0210552..fd30d2b90 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -36,6 +36,7 @@ PythonEval = "Returns the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" PythonFmod = "a modulo b" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index d1e47d336..9052cee93 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -38,6 +38,7 @@ PythonCommandExp = "exp(x)" PythonCommandExpComplex = "exp(z)" PythonCommandExpm1 = "expm1(x)" PythonCommandFabs = "fabs(x)" +PythonCommandFillRect = "fill_rect(x,y,width,height,color)" PythonCommandFloat = "float(x)" PythonCommandFloor = "floor(x)" PythonCommandFmod = "fmod(a,b)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 1a3ed9916..771f5624b 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -99,7 +99,8 @@ const ToolboxMessageTree KandinskyModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSetPixel, I18n::Message::PythonSetPixel), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect) }; const ToolboxMessageTree RandomModuleChildren[] = { @@ -216,6 +217,7 @@ const ToolboxMessageTree catalogChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExp, I18n::Message::PythonExp), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExpm1, I18n::Message::PythonExpm1), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFabs, I18n::Message::PythonFabs), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloat, I18n::Message::PythonFloat), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloor, I18n::Message::PythonFloor), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandForward, I18n::Message::PythonTurtleForward), From e7d2c61cec8c6233ced5651e2493bc07b4e23816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 29 Apr 2019 16:33:52 +0200 Subject: [PATCH 007/295] [apps/regression] Increase the iterations limit to compute a regression The exponential regression on the following data now works: 1 120000 3 130000 6 150000 8 160000 --- apps/regression/model/model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h index 86e586ee5..60a4e5470 100644 --- a/apps/regression/model/model.h +++ b/apps/regression/model/model.h @@ -47,7 +47,7 @@ private: virtual double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const = 0; // Levenberg-Marquardt - static constexpr double k_maxIterations = 100; + static constexpr double k_maxIterations = 300; static constexpr double k_maxMatrixInversionFixIterations = 10; static constexpr double k_initialLambda = 0.001; static constexpr double k_lambdaFactor = 10; From 25456b7973386dd2f4d4e39e1d12b2ed56950547 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 31 Oct 2018 10:28:57 +0100 Subject: [PATCH 008/295] [kandinsky] Assume UTF-8 encoding for strings --- kandinsky/include/kandinsky/font.h | 67 +++++---- .../include/kandinsky/unicode/codepoint.h | 23 ++++ .../include/kandinsky/unicode/utf8decoder.h | 14 ++ kandinsky/src/context_text.cpp | 35 +++-- kandinsky/src/font.cpp | 130 +++++++++++++++--- kandinsky/src/unicode/utf8decoder.cpp | 26 ++++ 6 files changed, 237 insertions(+), 58 deletions(-) create mode 100644 kandinsky/include/kandinsky/unicode/codepoint.h create mode 100644 kandinsky/include/kandinsky/unicode/utf8decoder.h create mode 100644 kandinsky/src/unicode/utf8decoder.cpp diff --git a/kandinsky/include/kandinsky/font.h b/kandinsky/include/kandinsky/font.h index a4ae9c786..5d2a571e9 100644 --- a/kandinsky/include/kandinsky/font.h +++ b/kandinsky/include/kandinsky/font.h @@ -2,13 +2,15 @@ #define KANDINSKY_FONT_H #include +#include #include #include +#include #include "palette.h" class KDFont { private: - static constexpr int k_bitsPerPixel = 4; + static constexpr int k_bitsPerPixel = 4; // TODO: Should be generated by the rasterizer static const KDFont privateLargeFont; static const KDFont privateSmallFont; public: @@ -17,38 +19,57 @@ public: KDSize stringSize(const char * text) const; + union GlyphBuffer { + public: + GlyphBuffer() {} // Don't initialize either buffer + KDColor * colorBuffer() { return m_colors; } + uint8_t * greyscaleBuffer() { return m_greyscales; } + uint8_t * secondaryGreyscaleBuffer() { return m_greyscales + k_maxGlyphPixelCount; } + private: + private: + static constexpr int k_maxGlyphPixelCount = 180; //TODO: Should be generated by the rasterizer + uint8_t m_greyscales[2*k_maxGlyphPixelCount]; + KDColor m_colors[k_maxGlyphPixelCount]; + }; + + using GlyphIndex = uint8_t; + class CodepointIndexPair { + public: + constexpr CodepointIndexPair(Codepoint c, GlyphIndex i) : m_codepoint(c), m_glyphIndex(i) {} + Codepoint codepoint() const { return m_codepoint; } + GlyphIndex glyphIndex() const { return m_glyphIndex; } + private: + Codepoint m_codepoint; + GlyphIndex m_glyphIndex; + }; + GlyphIndex indexForCodepoint(Codepoint c) const; + + void setGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const; + void accumulateGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const; + using RenderPalette = KDPalette<(1<; - void fetchGlyphForChar(char c, const RenderPalette * renderPalette, KDColor * pixelBuffer) const; + void colorizeGlyphBuffer(const RenderPalette * renderPalette, GlyphBuffer * glyphBuffer) const; + RenderPalette renderPalette(KDColor textColor, KDColor backgroundColor) const { return RenderPalette::Gradient(textColor, backgroundColor); } KDSize glyphSize() const { return m_glyphSize; } - constexpr KDFont(KDCoordinate glyphWidth, KDCoordinate glyphHeight, const uint16_t * glyphDataOffset, const uint8_t * data) : - m_glyphSize(glyphWidth, glyphHeight), m_glyphDataOffset(glyphDataOffset), m_data(data) { } + constexpr KDFont(size_t tableLength, const CodepointIndexPair * table, KDCoordinate glyphWidth, KDCoordinate glyphHeight, const uint16_t * glyphDataOffset, const uint8_t * data) : + m_tableLength(tableLength), m_table(table), m_glyphSize(glyphWidth, glyphHeight), m_glyphDataOffset(glyphDataOffset), m_data(data) { } private: - void fetchGreyscaleGlyphForChar(char c, uint8_t * greyscaleBuffer) const; + void fetchGreyscaleGlyphAtIndex(GlyphIndex index, uint8_t * greyscaleBuffer) const; - const uint8_t * compressedGlyphData(char c) const { - return m_data + m_glyphDataOffset[charAsIndex(c)]; - } - uint16_t compressedGlyphDataSize(char c) const { - return m_glyphDataOffset[charAsIndex(c)+1] - m_glyphDataOffset[charAsIndex(c)]; - } - uint8_t charAsIndex(char c) const { - // FIXME: This is most likely false for chars greater than 127 - return static_cast(c) - k_magicCharOffsetValue; - } - int signedCharAsIndex(char c) const { - int cInt = c; - if (cInt < 0) { - /* A char casted as int takes its value between -127 and +128, but we want - * a positive value. -127 is thus 129, -126 is 130, etc. */ - cInt=128+(cInt-(-127)+1); - } - return cInt - k_magicCharOffsetValue; + const uint8_t * compressedGlyphData(GlyphIndex index) const { + return m_data + m_glyphDataOffset[index]; } + uint16_t compressedGlyphDataSize(GlyphIndex index) const { + return m_glyphDataOffset[index+1] - m_glyphDataOffset[index]; + } + + size_t m_tableLength; + const CodepointIndexPair * m_table; KDSize m_glyphSize; const uint16_t * m_glyphDataOffset; const uint8_t * m_data; diff --git a/kandinsky/include/kandinsky/unicode/codepoint.h b/kandinsky/include/kandinsky/unicode/codepoint.h new file mode 100644 index 000000000..323647cfe --- /dev/null +++ b/kandinsky/include/kandinsky/unicode/codepoint.h @@ -0,0 +1,23 @@ +#ifndef KANDINSKY_UNICODE_CODEPOINT_H +#define KANDINSKY_UNICODE_CODEPOINT_H + +#include + +class Codepoint { +public: + constexpr Codepoint(uint32_t c) : m_code(c) {} + operator uint16_t() const { return m_code; } + + + bool isCombining() const { + return (m_code >= 0x300 && m_code <= 0x036F); + } +private: + uint32_t m_code; +}; + +static constexpr Codepoint Null = 0x0; +static constexpr Codepoint Tabulation = 0x9; +static constexpr Codepoint LineFeed = 0xA; + +#endif diff --git a/kandinsky/include/kandinsky/unicode/utf8decoder.h b/kandinsky/include/kandinsky/unicode/utf8decoder.h new file mode 100644 index 000000000..f9badc77f --- /dev/null +++ b/kandinsky/include/kandinsky/unicode/utf8decoder.h @@ -0,0 +1,14 @@ +#ifndef KANDINSKY_UNICODE_UTF8DECODER_H +#define KANDINSKY_UNICODE_UTF8DECODER_H + +#include "codepoint.h" + +class UTF8Decoder { +public: + UTF8Decoder(const char * string) : m_string(string) {} + Codepoint nextCodepoint(); +private: + const char * m_string; +}; + +#endif diff --git a/kandinsky/src/context_text.cpp b/kandinsky/src/context_text.cpp index 8d401d773..6a4282f90 100644 --- a/kandinsky/src/context_text.cpp +++ b/kandinsky/src/context_text.cpp @@ -1,35 +1,44 @@ #include #include #include +#include -constexpr int maxGlyphPixelCount = 180; constexpr static int k_tabCharacterWidth = 4; KDPoint KDContext::drawString(const char * text, KDPoint p, const KDFont * font, KDColor textColor, KDColor backgroundColor, int maxLength) { - KDPoint position = p; KDSize glyphSize = font->glyphSize(); KDFont::RenderPalette palette = font->renderPalette(textColor, backgroundColor); - KDColor glyph[maxGlyphPixelCount]; + KDFont::GlyphBuffer glyphBuffer; - const char * end = text + maxLength; - while(*text != 0 && text != end) { - if (*text == '\n') { + UTF8Decoder decoder(text); + Codepoint codepoint = decoder.nextCodepoint(); + while (codepoint != Null) { + if (codepoint == LineFeed) { position = KDPoint(0, position.y() + glyphSize.height()); - } else if (*text == '\t') { + codepoint = decoder.nextCodepoint(); + } else if (codepoint == Tabulation) { position = position.translatedBy(KDPoint(k_tabCharacterWidth * glyphSize.width(), 0)); + codepoint = decoder.nextCodepoint(); } else { - // Fetch and draw glyph for current char - font->fetchGlyphForChar(*text, &palette, glyph); + assert(!codepoint.isCombining()); + font->setGlyphGreyscalesForCodepoint(codepoint, &glyphBuffer); + codepoint = decoder.nextCodepoint(); + while (codepoint.isCombining()) { + font->accumulateGlyphGreyscalesForCodepoint(codepoint, &glyphBuffer); + codepoint = decoder.nextCodepoint(); + } + font->colorizeGlyphBuffer(&palette, &glyphBuffer); + // Flush accumulated content fillRectWithPixels( - KDRect(position, glyphSize.width(), glyphSize.height()), - glyph, - glyph // It's OK to trash the content of glyph since we'll re-fetch it for the next char anyway + KDRect(position, glyphSize), + glyphBuffer.colorBuffer(), + glyphBuffer.colorBuffer() // It's OK to trash the content of the color buffer since we'll re-fetch it for the next char anyway ); position = position.translatedBy(KDPoint(glyphSize.width(), 0)); } - text++; } + return position; } diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 38f947d7e..2e5a88321 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -1,6 +1,7 @@ #include #include #include +#include constexpr static int k_tabCharacterWidth = 4; @@ -9,39 +10,48 @@ KDSize KDFont::stringSize(const char * text) const { return KDSizeZero; } KDSize stringSize = KDSize(0, m_glyphSize.height()); - while (*text != 0) { + + UTF8Decoder decoder(text); + Codepoint codepoint = decoder.nextCodepoint(); + while (codepoint != Null) { KDSize cSize = KDSize(m_glyphSize.width(), 0); - if (*text == '\t') { - cSize = KDSize(k_tabCharacterWidth*m_glyphSize.width(), 0); - } - if (*text == '\n') { + if (codepoint == LineFeed) { cSize = KDSize(0, m_glyphSize.height()); + codepoint = decoder.nextCodepoint(); + } else if (codepoint == Tabulation) { + cSize = KDSize(k_tabCharacterWidth*m_glyphSize.width(), 0); + } else if (codepoint.isCombining()) { + cSize = KDSizeZero; } stringSize = KDSize(stringSize.width()+cSize.width(), stringSize.height()+cSize.height()); - text++; + codepoint = decoder.nextCodepoint(); } return stringSize; } -void KDFont::fetchGreyscaleGlyphForChar(char c, uint8_t * greyscaleBuffer) const { +void KDFont::setGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const { + fetchGreyscaleGlyphAtIndex(indexForCodepoint(codepoint), glyphBuffer->greyscaleBuffer()); +} + +void KDFont::accumulateGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const { + uint8_t * greyscaleBuffer = glyphBuffer->greyscaleBuffer(); + uint8_t * accumulationGreyscaleBuffer = glyphBuffer->secondaryGreyscaleBuffer(); + fetchGreyscaleGlyphAtIndex(indexForCodepoint(codepoint), accumulationGreyscaleBuffer); + for (int i=0; i= k_numberOfGlyphs) { - // There is no data for this glyph - for (int i = 0; i < pixelCount; i++) { - pixelBuffer[i] = KDColorBlack; - } - return; - } +void KDFont::colorizeGlyphBuffer(const RenderPalette * renderPalette, GlyphBuffer * glyphBuffer) const { /* Since a greyscale value is smaller than a color value (see assertion), we * can store the temporary greyscale values in the output pixel buffer. * What's great is that now, if we fill the pixel buffer right-to-left with @@ -49,8 +59,9 @@ void KDFont::fetchGlyphForChar(char c, const KDFont::RenderPalette * renderPalet * the remaining grayscale values since those are smaller. So we can avoid a * separate buffer for the temporary greyscale values. */ assert(k_bitsPerPixel < 8*sizeof(KDColor)); - uint8_t * greyscaleBuffer = reinterpret_cast(pixelBuffer); - fetchGreyscaleGlyphForChar(c, greyscaleBuffer); + + uint8_t * greyscaleBuffer = glyphBuffer->greyscaleBuffer(); + KDColor * colorBuffer = glyphBuffer->colorBuffer(); uint8_t mask = (0xFF >> (8-k_bitsPerPixel)); int pixelIndex = pixelCount; // Let's start at the final pixel @@ -62,7 +73,82 @@ void KDFont::fetchGlyphForChar(char c, const KDFont::RenderPalette * renderPalet uint8_t greyscale = greyscaleByte & mask; greyscaleByte = greyscaleByte >> k_bitsPerPixel; assert(pixelIndex >= 0); - pixelBuffer[pixelIndex--] = renderPalette->colorAtIndex(greyscale); + colorBuffer[pixelIndex--] = renderPalette->colorAtIndex(greyscale); } } } + +KDFont::GlyphIndex KDFont::indexForCodepoint(Codepoint c) const { +#define USE_BINARY_SEARCH 0 +#if USE_BINARY_SEARCH + int lowerBound = 0; + int upperBound = m_tableLength; + while (true) { + int currentIndex = (lowerBound+upperBound)/2; + // printf("Considering %d in [%d,%d]\n", currentIndex, lowerBound, upperBound); + const CodepointIndexPair * currentPair = m_table + currentIndex; + const CodepointIndexPair * nextPair = currentIndex + 1 < m_tableLength ? currentPair + 1 : nullptr; + // printf("At this point, currentPair->codepoint() = %d and c = %d\n", currentPair->codepoint(), c); + if (currentPair->codepoint() == c) { + return currentPair->glyphIndex(); + } else if (currentPair->codepoint() > c) { + // We need to look below + if (upperBound == currentIndex) { + // There's nothing below. Error out. + return 0; + } + upperBound = currentIndex; + continue; + } else if (nextPair == nullptr) { + return 0; + } else if (nextPair->codepoint() == c) { + return nextPair->glyphIndex(); + } else if (nextPair->codepoint() < c) { + // We need to look above + if (lowerBound == currentIndex) { + // There's nothing above. Error out. + return 0; + } + lowerBound = currentIndex; + continue; + } else { + // At this point, + // currentPair->codepoint < c && nextPair != nullptr && nextPair->codepoint > c + // Yay, it's over! + // There can be an empty space between the currentPair and the nextPair + // e.g. currentPair(3,1) and nextPair(9, 4) + // means value at codepoints 3, 4, 5, 6, 7, 8, 9 + // are glyph identifiers 1, ?, ?, ?, ?, ?, 4 + // solved as 1, 2, 3, 0, 0, 0, 4 + + // Let's hunt down the zeroes + Codepoint lastCodepointOfCurrentPair = currentPair->codepoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); + if (c > lastCodepointOfCurrentPair) { + return 0; + } + return currentPair->glyphIndex() + (c - currentPair->codepoint()); + } + } +#else + const CodepointIndexPair * currentPair = m_table; + if (c < currentPair->codepoint()) { + return 0; + } + const CodepointIndexPair * endPair = m_table + m_tableLength - 1; + while (currentPair < endPair) { + const CodepointIndexPair * nextPair = currentPair + 1; + if (c < nextPair->codepoint()) { + Codepoint lastCodepointOfCurrentPair = currentPair->codepoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); + if (c > lastCodepointOfCurrentPair) { + return 0; + } + return currentPair->glyphIndex() + (c - currentPair->codepoint()); + } + currentPair = nextPair; + } + if (endPair->codepoint() == c) { + return endPair->glyphIndex(); + } + return 0; +#endif +} diff --git a/kandinsky/src/unicode/utf8decoder.cpp b/kandinsky/src/unicode/utf8decoder.cpp new file mode 100644 index 000000000..e8585e9f8 --- /dev/null +++ b/kandinsky/src/unicode/utf8decoder.cpp @@ -0,0 +1,26 @@ +#include +#include + +static inline int leading_ones(uint8_t value) { + for (int i=0; i<8; i++) { + if (!(value & 0x80)) { + return i; + } + value = value << 1; + } + assert(false); +} + +static inline uint8_t last_k_bits(uint8_t value, uint8_t bits) { + return (value & ((1< Date: Wed, 31 Oct 2018 10:30:34 +0100 Subject: [PATCH 009/295] [kandinsky] The rasterizer encodes Unicode codepoints --- kandinsky/fonts/codepoints.h | 149 +++++++++++++++++++++++++++++++++++ kandinsky/fonts/rasterizer.c | 118 +++++++++++++-------------- 2 files changed, 204 insertions(+), 63 deletions(-) create mode 100644 kandinsky/fonts/codepoints.h diff --git a/kandinsky/fonts/codepoints.h b/kandinsky/fonts/codepoints.h new file mode 100644 index 000000000..f039df1d8 --- /dev/null +++ b/kandinsky/fonts/codepoints.h @@ -0,0 +1,149 @@ +// [0x30a].map{|i| "0x" + i.to_s(16) +", // " + [i].pack("U") + " // " + Unicode::Name.of([i].pack("U"))}.join("|") +#include + +uint32_t Codepoints[] = { + 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 + 0xb5, // µ // MICRO SIGN + 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 + 0x304, // ̄ // COMBINING MACRON + 0x308, // ̈ // COMBINING DIAERESIS + 0x30a, // ̊ // COMBINING RING ABOVE + 0x327, // ̧ // COMBINING CEDILLA + + 0x393, // Γ // GREEK CAPITAL LETTER GAMMA + 0x394, // Δ // GREEK CAPITAL LETTER DELTA + 0x3b8, // θ // GREEK SMALL LETTER THETA + 0x3bb, // λ // GREEK SMALL LETTER LAMDA + 0x3c0, // π // GREEK SMALL LETTER PI + 0x3c3, // σ // GREEK SMALL LETTER SIGMA + 0x1d07, // ᴇ // LATIN LETTER SMALL CAPITAL E + 0x212f, // ℯ // SCRIPT SMALL E + 0x2192, // → // RIGHTWARDS ARROW + 0x2211, // ∑ // N-ARY SUMMATION + 0x221a, // √ // SQUARE ROOT + 0x222b, // ∫ // INTEGRAL + 0x2248, // ≈ // ALMOST EQUAL TO + 0x2264, // ≤ // LESS-THAN OR EQUAL TO + 0x2265, // ≥ // GREATER-THAN OR EQUAL TO + 0x1d422, // 𝐢 // MATHEMATICAL BOLD SMALL I" + + // Apostrophe + // Xbar + // Ybar + +}; + +int NumberOfCodepoints = sizeof(Codepoints)/sizeof(Codepoints[0]); diff --git a/kandinsky/fonts/rasterizer.c b/kandinsky/fonts/rasterizer.c index baafd3b4e..5903adfdf 100644 --- a/kandinsky/fonts/rasterizer.c +++ b/kandinsky/fonts/rasterizer.c @@ -16,6 +16,7 @@ #include FT_FREETYPE_H #include "unicode_for_symbol.h" +#include "codepoints.h" #include "../../ion/src/external/lz4/lz4hc.h" @@ -40,18 +41,6 @@ typedef struct { void writeImageToPNGFile(image_t * image, char * filename); #endif -#define CHARACTER_RANGE_START 0x20 -#define CHARACTER_RANGE_END 0x7E -#define CHARACTER_COUNT (CHARACTER_RANGE_END-CHARACTER_RANGE_START+1) -#define GLYPH_COUNT (CHARACTER_COUNT + NUMBER_OF_SYMBOLS) - -#define GRID_WIDTH 19 -#define GRID_HEIGHT 8 - -#if (GRID_WIDTH*GRID_HEIGHT < GLYPH_COUNT) -#error Grid too small. Consider increasing GRID_WIDTH or GRID_HEIGHT -#endif - void drawGlyphInImage(FT_Bitmap * glyphBitmap, image_t * image, int x, int y); static void prettyPrintArray(FILE * stream, int maxWidth, int typeSize, void * array, int numberOfElements); @@ -105,24 +94,9 @@ int main(int argc, char * argv[]) { int maxWidth = 0; int maxAboveBaseline = 0; int maxBelowBaseline = 0; - for (unsigned char character = CHARACTER_RANGE_START; character <= CHARACTER_RANGE_END; character++) { - ENSURE(!FT_Load_Char(face, character, FT_LOAD_RENDER), "Loading character 0x%02x", character); - int aboveBaseline = face->glyph->bitmap_top; - int belowBaseline = face->glyph->bitmap.rows - face->glyph->bitmap_top; - int width = face->glyph->bitmap_left + face->glyph->bitmap.width; - if (width > maxWidth) { - maxWidth = width; - } - if (aboveBaseline > maxAboveBaseline) { - maxAboveBaseline = aboveBaseline; - } - if (belowBaseline > maxBelowBaseline) { - maxBelowBaseline = belowBaseline; - } - } - for (int charIndex = 0; charIndex < NUMBER_OF_SYMBOLS; charIndex++) { - wchar_t wideChar = codePointForSymbol[charIndex]; - ENSURE(!FT_Load_Char(face, wideChar, FT_LOAD_RENDER), "Loading character 0x%02x", wideChar); + for (int i=0; i < NumberOfCodepoints; i++) { + wchar_t codepoint = Codepoints[i]; + ENSURE(!FT_Load_Char(face, codepoint, FT_LOAD_RENDER), "Loading character 0x%02x", codepoint); int aboveBaseline = face->glyph->bitmap_top; int belowBaseline = face->glyph->bitmap.rows - face->glyph->bitmap_top; int width = face->glyph->bitmap_left + face->glyph->bitmap.width; @@ -142,9 +116,11 @@ int main(int argc, char * argv[]) { //printf("Actual glyph size = %dx%d\n", glyph_width, glyph_height); int grid_size = 1; + int grid_width = 20; + int grid_height = ((NumberOfCodepoints-1)/grid_width)+1; - bitmap_image.width = GRID_WIDTH*glyph_width+(GRID_WIDTH-1)*grid_size; - bitmap_image.height = GRID_HEIGHT*glyph_height+(GRID_HEIGHT-1)*grid_size; + bitmap_image.width = grid_width*glyph_width+(grid_width-1)*grid_size; + bitmap_image.height = grid_height*glyph_height+(grid_height-1)*grid_size; bitmap_image.pixels = malloc(sizeof(pixel_t)*bitmap_image.width*bitmap_image.height); ENSURE(bitmap_image.pixels != NULL, "Allocating bitmap image of size %dx%d at %ld bytes per pixel", bitmap_image.width, bitmap_image.height, sizeof(pixel_t)); @@ -159,30 +135,23 @@ int main(int argc, char * argv[]) { } } - // We're doing the ASCII table, so characters from 0 to 255 inclusive - for (unsigned char character = CHARACTER_RANGE_START; character <= CHARACTER_RANGE_END; character++) { - int x = (character-CHARACTER_RANGE_START)%(GRID_WIDTH); - int y = (character-CHARACTER_RANGE_START)/(GRID_WIDTH); + for (int i=0; iglyph->bitmap_left, face->glyph->bitmap_top); + while (face->glyph->bitmap_left < 0) { + // This is a workaround for combining glyphs. + // For some reason, FreeType does a fun hack and yields a negative bitmap_left + // This way, the glyph automagically combines with the previous one. That's neat, + // but we don't want to do that. + face->glyph->bitmap_left += glyph_width; + } drawGlyphInImage(&face->glyph->bitmap, &bitmap_image, - x*(glyph_width+grid_size) + face->glyph->bitmap_left, - y*(glyph_height+grid_size) + maxAboveBaseline - face->glyph->bitmap_top - ); - } - // We are now using unicode to access non-ASCII characters - for (int charIndex = 0; charIndex < NUMBER_OF_SYMBOLS; charIndex++) { - wchar_t wideChar = codePointForSymbol[charIndex]; - int x = (charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)%(GRID_WIDTH); - int y = (charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)/(GRID_WIDTH); - // FT_LOAD_RENDER: Render the glyph upon load - ENSURE(!FT_Load_Char(face, wideChar, FT_LOAD_RENDER), "Loading character 0x%02x", wideChar); - //printf("Advances = %dx%d\n", face->glyph->bitmap_left, face->glyph->bitmap_top); - drawGlyphInImage(&face->glyph->bitmap, - &bitmap_image, - x*(glyph_width+grid_size) + face->glyph->bitmap_left, + x*(glyph_width+grid_size) + face->glyph->bitmap_left, y*(glyph_height+grid_size) + maxAboveBaseline - face->glyph->bitmap_top ); } @@ -195,6 +164,29 @@ int main(int argc, char * argv[]) { fprintf(sourceFile, "/* This file is auto-generated by the rasterizer */\n\n"); fprintf(sourceFile, "#include \n\n"); + + // Step 1 - Build the GlyphIndex <-> UnicodeCodepoint correspondance table + + int previousIndex = -1; + uint32_t previousCodepoint = 0; + int numberOfPairs = 0; + + fprintf(sourceFile, "static constexpr KDFont::CodepointIndexPair table[] = {\n"); + for (int i=0; i Date: Wed, 31 Oct 2018 10:37:41 +0100 Subject: [PATCH 010/295] [i18n] Encode strings as NFKD-normalized UTF-8 strings --- apps/i18n.py | 62 +++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/apps/i18n.py b/apps/i18n.py index 93ec034c9..dd307b8ba 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -1,4 +1,6 @@ -#coding=utf-8 +# This script gather all .i18n files and aggregates them as a pair of .h/.cpp file +# In practice, it enforces a NFKD normalization +# It works with Python 2 and Python 3 import sys import re @@ -6,42 +8,8 @@ import unicodedata import argparse import io -ion_special_characters = { - u'Δ': "Ion::Charset::CapitalDelta", - u'Σ': "Ion::Charset::CapitalSigma", - u'λ': "Ion::Charset::SmallLambda", - u'μ': "Ion::Charset::SmallMu", - u'σ': "Ion::Charset::SmallSigma", - u'≤': "Ion::Charset::LessEqual", - u'≈': "Ion::Charset::AlmostEqual", - u'ø': "Ion::Charset::Empty", - u'•': "Ion::Charset::MiddleDot" -} - -def ion_char(i18n_letter): - if i18n_letter == '\'': - return "'\\\''" - if ord(i18n_letter) < 128: - return "'" + i18n_letter + "'" - if i18n_letter in ion_special_characters: - return ion_special_characters[i18n_letter] - normalized = unicodedata.normalize("NFD", i18n_letter).encode('ascii', 'ignore') - #sys.stderr.write("Warning: Normalizing unicode character \"" + i18n_letter + "\" -> \"" + normalized + "\"\n") - return "'" + normalized.decode() + "'" - def source_definition(i18n_string): - ion_characters = [] - i = 0 - while i < len(i18n_string): - if i18n_string[i] == '\\': - i = i+1 - newChar = "'\\"+i18n_string[i]+"'" - ion_characters.append(newChar) - else: - ion_characters.append(ion_char(i18n_string[i])) - i = i+1 - ion_characters.append("0") - return "{" + ", ".join(ion_characters) + "}" + return (u"\"" + unicodedata.normalize("NFKD", i18n_string) + u"\"").encode("utf-8") def split_line(line): match = re.match(r"^(\w+)\s*=\s*\"(.*)\"$", line) @@ -78,7 +46,7 @@ def parse_files(files): return {"messages": sorted(messages), "universal_messages": sorted(universal_messages), "data": data} def print_header(data, path, locales): - f = open(path, 'w') + f = open(path, "w") f.write("#ifndef APPS_I18N_H\n") f.write("#define APPS_I18N_H\n\n") f.write("// This file is auto-generated by i18n.py\n\n") @@ -114,7 +82,7 @@ def print_header(data, path, locales): f.close() def print_implementation(data, path, locales): - f = open(path, 'w') + f = open(path, "w") f.write("#include \"i18n.h\"\n") f.write("#include \n") f.write("#include \n\n"); @@ -141,7 +109,11 @@ def print_implementation(data, path, locales): if not message in data["data"][locale]: sys.stderr.write("Error: Undefined key \"" + message + "\" for locale \"" + locale + "\"\n") sys.exit(-1) - f.write("constexpr static char " + locale + message + "[] = " + data["data"][locale][message] + ";\n") + f.write("constexpr static char " + locale + message + "[] = ") + f = open(path, "ab") # Re-open the file as binary to output raw UTF-8 bytes + f.write(data["data"][locale][message]) + f = open(path, "a") # Re-open the file as text + f.write(";\n") f.write("\n") f.write("constexpr static const char * messages[%d][%d] = {\n" % (len(data["messages"]), len(locales))) for message in data["messages"]: @@ -152,6 +124,18 @@ def print_implementation(data, path, locales): f.write("};\n\n") # Write the translate method + for message in data["universal_messages"]: + f.write("constexpr static char universal" + message + "[] = ") + f = open(path, "ab") # Re-open the file as binary to output raw UTF-8 bytes + f.write(data["data"]["universal"][message]) + f = open(path, "a") # Re-open the file as text + f.write(";\n") + f.write("\n") + f.write("constexpr static const char * universalMessages[%d] = {\n" % len(data["universal_messages"])) + for message in data["universal_messages"]: + f.write(" universal" + message + ",\n") + f.write("};\n") + f.write("\n") f.write("const char * translate(Message m, Language l) {\n") f.write(" assert(m != Message::LocalizedMessageMarker);\n") f.write(" int localizedMessageOffset = (int)Message::LocalizedMessageMarker+1;\n") From c23e5a47bc1464c4ae8bd92cd862ae6aa815266a Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 31 Oct 2018 10:38:25 +0100 Subject: [PATCH 011/295] [kandinsky] Add unit tests for UTF-8 decoding and CodepointToIndex --- kandinsky/Makefile | 3 +++ kandinsky/test/font.cpp | 24 ++++++++++++++++++++++++ kandinsky/test/utf8decoder.cpp | 15 +++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 kandinsky/test/font.cpp create mode 100644 kandinsky/test/utf8decoder.cpp diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 9705de6fe..af4804052 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -13,6 +13,7 @@ src += $(addprefix kandinsky/src/,\ ion_context.cpp \ point.cpp \ rect.cpp \ + unicode/utf8decoder.cpp\ ) src += $(addprefix kandinsky/fonts/, \ @@ -22,7 +23,9 @@ src += $(addprefix kandinsky/fonts/, \ tests += $(addprefix kandinsky/test/,\ color.cpp\ + font.cpp\ rect.cpp\ + utf8decoder.cpp\ ) RASTERIZER_CFLAGS := -std=c99 `pkg-config freetype2 --cflags` diff --git a/kandinsky/test/font.cpp b/kandinsky/test/font.cpp new file mode 100644 index 000000000..96a2acfed --- /dev/null +++ b/kandinsky/test/font.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +static constexpr KDFont::CodepointIndexPair table[] = { + KDFont::CodepointIndexPair(3, 1), // Codepoint, identifier + KDFont::CodepointIndexPair(9, 4), + KDFont::CodepointIndexPair(12, 5), + KDFont::CodepointIndexPair(14, 7) +}; + +constexpr KDFont testFont(4, table, 10, 10, nullptr, nullptr); + +const KDFont::GlyphIndex index_for_codepoint[] = { +/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ + 0, 0, 0, 1, 2, 3, 0, 0, 0, 4, 0, 0, 5, 6, 7, 0 +}; + +QUIZ_CASE(kandinsky_font_index_for_codepoint) { + for (int i=0; i<16; i++) { + KDFont::GlyphIndex result = testFont.indexForCodepoint(i); + quiz_assert(result == index_for_codepoint[i]); + } +} diff --git a/kandinsky/test/utf8decoder.cpp b/kandinsky/test/utf8decoder.cpp new file mode 100644 index 000000000..3e858f526 --- /dev/null +++ b/kandinsky/test/utf8decoder.cpp @@ -0,0 +1,15 @@ +#include +#include + +void assert_decodes_to(const char * string, Codepoint c) { + UTF8Decoder d(string); + quiz_assert(d.nextCodepoint() == c); + quiz_assert(d.nextCodepoint() == 0); +} + +QUIZ_CASE(kandinsky_utf8_decoder) { + assert_decodes_to("\x20", 0x20); + assert_decodes_to("\xC2\xA2", 0xA2); + assert_decodes_to("\xED\x9F\xBF", 0xD7FF); + assert_decodes_to("\xCC\x81", 0x301); +} From e57995712be058b7e9e165a64116f850a67373d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 10 Jan 2019 14:07:36 +0100 Subject: [PATCH 012/295] [kandinsky/font] Fix rebase on master --- apps/i18n.py | 23 +++++++++-------------- kandinsky/src/font.cpp | 2 +- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/i18n.py b/apps/i18n.py index dd307b8ba..8aee99be6 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -85,14 +85,20 @@ def print_implementation(data, path, locales): f = open(path, "w") f.write("#include \"i18n.h\"\n") f.write("#include \n") - f.write("#include \n\n"); + f.write("#include \n\n") f.write("namespace I18n {\n\n") + # Write the default message f.write("constexpr static char universalDefault[] = {0};\n") + # Write the universal messages for message in data["universal_messages"]: - f.write("constexpr static char universal" + message + "[] = " + data["data"]["universal"][message] + ";\n") + f.write("constexpr static char universal" + message + "[] = ") + f = open(path, "ab") # Re-open the file as binary to output raw UTF-8 bytes + f.write(data["data"]["universal"][message]) + f = open(path, "a") # Re-open the file as text + f.write(";\n") f.write("\n") f.write("constexpr static const char * universalMessages[%d] = {\n" % (len(data["universal_messages"])+1)) f.write(" universalDefault,\n") @@ -123,19 +129,8 @@ def print_implementation(data, path, locales): f.write("},\n") f.write("};\n\n") + # Write the translate method - for message in data["universal_messages"]: - f.write("constexpr static char universal" + message + "[] = ") - f = open(path, "ab") # Re-open the file as binary to output raw UTF-8 bytes - f.write(data["data"]["universal"][message]) - f = open(path, "a") # Re-open the file as text - f.write(";\n") - f.write("\n") - f.write("constexpr static const char * universalMessages[%d] = {\n" % len(data["universal_messages"])) - for message in data["universal_messages"]: - f.write(" universal" + message + ",\n") - f.write("};\n") - f.write("\n") f.write("const char * translate(Message m, Language l) {\n") f.write(" assert(m != Message::LocalizedMessageMarker);\n") f.write(" int localizedMessageOffset = (int)Message::LocalizedMessageMarker+1;\n") diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 2e5a88321..b6c43151d 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -64,7 +64,7 @@ void KDFont::colorizeGlyphBuffer(const RenderPalette * renderPalette, GlyphBuffe KDColor * colorBuffer = glyphBuffer->colorBuffer(); uint8_t mask = (0xFF >> (8-k_bitsPerPixel)); - int pixelIndex = pixelCount; // Let's start at the final pixel + int pixelIndex = m_glyphSize.width() * m_glyphSize.height() - 1; // Let's start at the final pixel int greyscaleByteIndex = pixelIndex * k_bitsPerPixel / 8; while (pixelIndex >= 0) { assert(greyscaleByteIndex == pixelIndex * k_bitsPerPixel / 8); From 1f80e63bea202d501a339fe6fa999667542e8b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 10 Jan 2019 14:36:06 +0100 Subject: [PATCH 013/295] [kandinsky] Coding style --- kandinsky/include/kandinsky/font.h | 1 - kandinsky/src/font.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/kandinsky/include/kandinsky/font.h b/kandinsky/include/kandinsky/font.h index 5d2a571e9..a45a1e283 100644 --- a/kandinsky/include/kandinsky/font.h +++ b/kandinsky/include/kandinsky/font.h @@ -25,7 +25,6 @@ public: KDColor * colorBuffer() { return m_colors; } uint8_t * greyscaleBuffer() { return m_greyscales; } uint8_t * secondaryGreyscaleBuffer() { return m_greyscales + k_maxGlyphPixelCount; } - private: private: static constexpr int k_maxGlyphPixelCount = 180; //TODO: Should be generated by the rasterizer uint8_t m_greyscales[2*k_maxGlyphPixelCount]; diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index b6c43151d..40fbebd00 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -87,7 +87,7 @@ KDFont::GlyphIndex KDFont::indexForCodepoint(Codepoint c) const { int currentIndex = (lowerBound+upperBound)/2; // printf("Considering %d in [%d,%d]\n", currentIndex, lowerBound, upperBound); const CodepointIndexPair * currentPair = m_table + currentIndex; - const CodepointIndexPair * nextPair = currentIndex + 1 < m_tableLength ? currentPair + 1 : nullptr; + const CodepointIndexPair * nextPair = (currentIndex + 1) < m_tableLength ? currentPair + 1 : nullptr; // printf("At this point, currentPair->codepoint() = %d and c = %d\n", currentPair->codepoint(), c); if (currentPair->codepoint() == c) { return currentPair->glyphIndex(); From 6380ba6cea5ac0a6457c4524d2bb7ed52f5177ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 10 Jan 2019 14:40:45 +0100 Subject: [PATCH 014/295] [poincare/tokenizer] Coding style --- poincare/src/parsing/tokenizer.cpp | 19 ++++++++++--------- poincare/src/parsing/tokenizer.h | 13 ++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 1ad7dfd99..00698188a 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -16,7 +16,7 @@ const char Tokenizer::popChar() { const char nextChar = *m_nextCharP; m_nextCharP++; return nextChar; - // Note that, after returning, m_nextCharP points to the character after nextChar. + // After returning, m_nextCharP points to the character after nextChar. } bool Tokenizer::canPopChar (const char c) { @@ -28,8 +28,8 @@ bool Tokenizer::canPopChar (const char c) { } size_t Tokenizer::popIdentifier() { - // Since this method is only called by popToken, - // currentChar is necessary a letter. + /* Since this method is only called by popToken, currentChar is necessarily a + * letter. */ size_t length = 1; char nextChar = *m_nextCharP; while (isLetter(nextChar) || isDigit(nextChar) || nextChar == '_') { @@ -50,7 +50,7 @@ size_t Tokenizer::popDigits() { Token Tokenizer::popNumber() { /* This method is only called by popToken, after popping a dot or a digit. - * Hence one needs to get one character back. */ + * Hence the need to get one character back. */ m_nextCharP--; const char * integralPartText = m_nextCharP; @@ -88,7 +88,8 @@ Token Tokenizer::popToken() { // Skip whitespaces while (canPopChar(' ')) {} - // Save for later use (since m_nextCharP is altered by popChar, popNumber, popIdentifier). + /* Save for later use (since m_nextCharP is altered by popChar, popNumber, + * popIdentifier). */ const char * start = m_nextCharP; const char currentChar = popChar(); @@ -102,8 +103,8 @@ Token Tokenizer::popToken() { return result; } if ('(' <= currentChar && currentChar <= '/') { - // Those characters form a contiguous range in the ascii character set, - // so one can make searching faster with this lookup table. + /* Those characters form a contiguous range in the ascii character set, we + * make searching faster with this lookup table. */ constexpr Token::Type typeForChar[] = { Token::LeftParenthesis, Token::RightParenthesis, @@ -114,8 +115,8 @@ Token Tokenizer::popToken() { Token::Undefined, Token::Slash }; - // The dot character is the second last of that range, - // but it is matched before (with popNumber). + /* The dot character is the second last of that range, but it is matched + * before (with popNumber). */ assert(currentChar != '.'); return Token(typeForChar[currentChar - '(']); } diff --git a/poincare/src/parsing/tokenizer.h b/poincare/src/parsing/tokenizer.h index 7e3cd452e..05e2017d9 100644 --- a/poincare/src/parsing/tokenizer.h +++ b/poincare/src/parsing/tokenizer.h @@ -1,12 +1,11 @@ #ifndef POINCARE_PARSING_TOKENIZER_H #define POINCARE_PARSING_TOKENIZER_H -/* In order to parse a text input into an Expression, - * (an instance of) the Tokenizer reads the successive - * characters of the input, pops the Tokens it recognizes, - * which are then consumed by the Parser. - * For each Token, the Tokenizer determines a Type and - * may save other relevant data intended for the Parser. */ +/* In order to parse a text input into an Expression, (an instance of) the + * Tokenizer reads the successive characters of the input, pops the Tokens it + * recognizes, which are then consumed by the Parser. For each Token, the + * Tokenizer determines a Type and may save other relevant data intended for the + * Parser. */ #include "token.h" @@ -14,7 +13,7 @@ namespace Poincare { class Tokenizer { public: - Tokenizer(const char * text) : m_nextCharP(text) {}; + Tokenizer(const char * text) : m_nextCharP(text) {} Token popToken(); private: const char popChar(); From 46f2cc29dcaeb8b90080a71e6b67b6a69b3f2d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 10 Jan 2019 15:38:36 +0100 Subject: [PATCH 015/295] [poincare/parser] Use the UTF8Decoder in the parser --- poincare/src/parsing/tokenizer.cpp | 95 ++++++++++++++++++------------ poincare/src/parsing/tokenizer.h | 7 ++- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 00698188a..bd2c8e823 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -1,6 +1,7 @@ #include "tokenizer.h" #include #include +#include namespace Poincare { @@ -12,54 +13,70 @@ static inline bool isDigit(const char c) { return '0' <= c && c <= '9'; } +const char Tokenizer::nextChar(PopTest popTest, char context, bool * testResult) { + // Beware of chars spaning over more than one byte: use the UTF8Decoder. + UTF8Decoder decoder(m_text); + Codepoint firstCodepoint = decoder.nextCodepoint(); + int numberOfBytesForChar = 1; + if (firstCodepoint != Null) { + Codepoint codepoint = decoder.nextCodepoint(); + while (codepoint.isCombining()) { + numberOfBytesForChar++; + codepoint = decoder.nextCodepoint(); + } + } + char c = *m_text; // TODO handle combined chars? + bool shouldPop = popTest(c, context); + if (testResult != nullptr) { + *testResult = shouldPop; + } + if (shouldPop) { + m_text+= numberOfBytesForChar; + } + return c; +} + const char Tokenizer::popChar() { - const char nextChar = *m_nextCharP; - m_nextCharP++; - return nextChar; - // After returning, m_nextCharP points to the character after nextChar. + return nextChar([](char c, char context) { return true; }); + // m_text now points to the start of the character after the returned char. } bool Tokenizer::canPopChar (const char c) { - if (*m_nextCharP == c) { - m_nextCharP++; - return true; + bool didPop = false; + nextChar([](char nextC, char context) { return nextC == context; }, c, &didPop); + return didPop; +} + +size_t Tokenizer::popWhile(PopTest popTest, char context) { + size_t length = 0; + bool didPop = true; + while (didPop) { + nextChar(popTest, context, &didPop); + if (didPop) { + length++; + } } - return false; + return length; } size_t Tokenizer::popIdentifier() { - /* Since this method is only called by popToken, currentChar is necessarily a - * letter. */ - size_t length = 1; - char nextChar = *m_nextCharP; - while (isLetter(nextChar) || isDigit(nextChar) || nextChar == '_') { - length++; - nextChar = *++m_nextCharP; - } - return length; + return popWhile([](char c, char context) { return isLetter(c) || isDigit(c) || c == context; }, '_'); } size_t Tokenizer::popDigits() { - size_t length = 0; - while (isDigit(*m_nextCharP)) { - length++; - m_nextCharP++; - } - return length; + return popWhile([](char c, char context) { return isDigit(c); }); } Token Tokenizer::popNumber() { - /* This method is only called by popToken, after popping a dot or a digit. - * Hence the need to get one character back. */ - m_nextCharP--; - - const char * integralPartText = m_nextCharP; + const char * integralPartText = m_text; size_t integralPartLength = popDigits(); - const char * fractionalPartText = m_nextCharP; + const char * fractionalPartText = m_text; size_t fractionalPartLength = 0; + + assert(integralPartLength > 0 || *m_text == '.'); if (canPopChar('.')) { - fractionalPartText = m_nextCharP; + fractionalPartText = m_text; fractionalPartLength = popDigits(); } @@ -67,12 +84,12 @@ Token Tokenizer::popNumber() { return Token(Token::Undefined); } - const char * exponentPartText = m_nextCharP; + const char * exponentPartText = m_text; size_t exponentPartLength = 0; bool exponentIsNegative = false; if (canPopChar(Ion::Charset::Exponent)) { exponentIsNegative = canPopChar('-'); - exponentPartText = m_nextCharP; + exponentPartText = m_text; exponentPartLength = popDigits(); if (exponentPartLength == 0) { return Token(Token::Undefined); @@ -88,18 +105,22 @@ Token Tokenizer::popToken() { // Skip whitespaces while (canPopChar(' ')) {} - /* Save for later use (since m_nextCharP is altered by popChar, popNumber, + /* Save for later use (since m_text is altered by popChar, popNumber, * popIdentifier). */ - const char * start = m_nextCharP; + const char * start = m_text; + + /* If the next char is the start of a number, we do not want to pop it because + * popNumber needs this char. */ + bool nextCharIsNeitherDotNorDigit = true; + const char currentChar = nextChar([](char c, char context) { return c != context && !isDigit(c); }, '.', &nextCharIsNeitherDotNorDigit); - const char currentChar = popChar(); // According to currentChar, recognize the Token::Type. - if (currentChar == '.' || isDigit(currentChar)) { + if (!nextCharIsNeitherDotNorDigit) { return popNumber(); } if (isLetter(currentChar)) { Token result(Token::Identifier); - result.setString(start, popIdentifier()); + result.setString(start, 1 + popIdentifier()); // We already popped 1 char return result; } if ('(' <= currentChar && currentChar <= '/') { diff --git a/poincare/src/parsing/tokenizer.h b/poincare/src/parsing/tokenizer.h index 05e2017d9..b75d6e863 100644 --- a/poincare/src/parsing/tokenizer.h +++ b/poincare/src/parsing/tokenizer.h @@ -13,16 +13,19 @@ namespace Poincare { class Tokenizer { public: - Tokenizer(const char * text) : m_nextCharP(text) {} + Tokenizer(const char * text) : m_text(text) {} Token popToken(); private: + typedef bool (*PopTest)(char c, char context); + const char nextChar(PopTest popTest, char context = 0, bool * testResult = nullptr); const char popChar(); bool canPopChar(const char c); + size_t popWhile(PopTest popTest, char context = 0); size_t popDigits(); size_t popIdentifier(); Token popNumber(); - const char * m_nextCharP; + const char * m_text; }; } From c8da3bb9e7b8c56365ba6f9260e87c8d73858f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 11:09:07 +0100 Subject: [PATCH 016/295] [kandinsky/font] Add comment about UTF-8 --- kandinsky/include/kandinsky/font.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kandinsky/include/kandinsky/font.h b/kandinsky/include/kandinsky/font.h index a45a1e283..7f716ba70 100644 --- a/kandinsky/include/kandinsky/font.h +++ b/kandinsky/include/kandinsky/font.h @@ -8,6 +8,21 @@ #include #include "palette.h" +/* We use UTF-8 encoding. This means that a character is encoded as a code point + * that uses between 1 and 4 bytes. Code points can be "combining", in which + * case their glyph should be superimposed to the glyph of the previous code + * point in the string. This is for instance used to print accents: the string + * for the glyph 'è' is composed of the code point for 'e' followed by the + * combining code point for '`'. + * ASCII characters have the same encoding in ASCII and in UTF-8. + * + * We do not provide a glyph for each of the 1,112,064 valid UTF-8 code points. + * We thus have a table of the glyphs we can draw (uint32_t Codepoints[] in + * kandinsky/fonts/codepoints.h). To easily compute the index of a code point in + * the Codepoints table, we use the m_table matching table: it contains the + * CodepointIndexPairs of the first code point of each series of consecutive + * code points in the Codepoints table. */ + class KDFont { private: static constexpr int k_bitsPerPixel = 4; // TODO: Should be generated by the rasterizer From 50ac72107b80742136a45e16239b6e2a8f8de2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 11:16:14 +0100 Subject: [PATCH 017/295] Rename Codepoint Code point --- .../fonts/{codepoints.h => code_points.h} | 4 +- kandinsky/fonts/rasterizer.c | 48 +++++++------- kandinsky/include/kandinsky/font.h | 30 ++++----- .../include/kandinsky/unicode/code_point.h | 23 +++++++ .../include/kandinsky/unicode/codepoint.h | 23 ------- .../include/kandinsky/unicode/utf8decoder.h | 4 +- kandinsky/src/context_text.cpp | 24 +++---- kandinsky/src/font.cpp | 66 +++++++++---------- kandinsky/src/unicode/utf8decoder.cpp | 4 +- kandinsky/test/font.cpp | 18 ++--- kandinsky/test/utf8decoder.cpp | 6 +- poincare/src/parsing/tokenizer.cpp | 10 +-- 12 files changed, 130 insertions(+), 130 deletions(-) rename kandinsky/fonts/{codepoints.h => code_points.h} (98%) create mode 100644 kandinsky/include/kandinsky/unicode/code_point.h delete mode 100644 kandinsky/include/kandinsky/unicode/codepoint.h diff --git a/kandinsky/fonts/codepoints.h b/kandinsky/fonts/code_points.h similarity index 98% rename from kandinsky/fonts/codepoints.h rename to kandinsky/fonts/code_points.h index f039df1d8..44195cf96 100644 --- a/kandinsky/fonts/codepoints.h +++ b/kandinsky/fonts/code_points.h @@ -1,7 +1,7 @@ // [0x30a].map{|i| "0x" + i.to_s(16) +", // " + [i].pack("U") + " // " + Unicode::Name.of([i].pack("U"))}.join("|") #include -uint32_t Codepoints[] = { +uint32_t CodePoints[] = { 0x20, // // SPACE 0x21, // ! // EXCLAMATION MARK 0x22, // " // QUOTATION MARK @@ -146,4 +146,4 @@ uint32_t Codepoints[] = { }; -int NumberOfCodepoints = sizeof(Codepoints)/sizeof(Codepoints[0]); +int NumberOfCodePoints = sizeof(CodePoints)/sizeof(CodePoints[0]); diff --git a/kandinsky/fonts/rasterizer.c b/kandinsky/fonts/rasterizer.c index 5903adfdf..1990ba56c 100644 --- a/kandinsky/fonts/rasterizer.c +++ b/kandinsky/fonts/rasterizer.c @@ -16,7 +16,7 @@ #include FT_FREETYPE_H #include "unicode_for_symbol.h" -#include "codepoints.h" +#include "code_points.h" #include "../../ion/src/external/lz4/lz4hc.h" @@ -94,9 +94,9 @@ int main(int argc, char * argv[]) { int maxWidth = 0; int maxAboveBaseline = 0; int maxBelowBaseline = 0; - for (int i=0; i < NumberOfCodepoints; i++) { - wchar_t codepoint = Codepoints[i]; - ENSURE(!FT_Load_Char(face, codepoint, FT_LOAD_RENDER), "Loading character 0x%02x", codepoint); + for (int i=0; i < NumberOfCodePoints; i++) { + wchar_t codePoint = CodePoints[i]; + ENSURE(!FT_Load_Char(face, codePoint, FT_LOAD_RENDER), "Loading character 0x%02x", codePoint); int aboveBaseline = face->glyph->bitmap_top; int belowBaseline = face->glyph->bitmap.rows - face->glyph->bitmap_top; int width = face->glyph->bitmap_left + face->glyph->bitmap.width; @@ -117,7 +117,7 @@ int main(int argc, char * argv[]) { int grid_size = 1; int grid_width = 20; - int grid_height = ((NumberOfCodepoints-1)/grid_width)+1; + int grid_height = ((NumberOfCodePoints-1)/grid_width)+1; bitmap_image.width = grid_width*glyph_width+(grid_width-1)*grid_size; bitmap_image.height = grid_height*glyph_height+(grid_height-1)*grid_size; @@ -135,12 +135,12 @@ int main(int argc, char * argv[]) { } } - for (int i=0; iglyph->bitmap_left, face->glyph->bitmap_top); while (face->glyph->bitmap_left < 0) { // This is a workaround for combining glyphs. @@ -165,18 +165,18 @@ int main(int argc, char * argv[]) { fprintf(sourceFile, "/* This file is auto-generated by the rasterizer */\n\n"); fprintf(sourceFile, "#include \n\n"); - // Step 1 - Build the GlyphIndex <-> UnicodeCodepoint correspondance table + // Step 1 - Build the GlyphIndex <-> UnicodeCodePoint correspondance table int previousIndex = -1; - uint32_t previousCodepoint = 0; + uint32_t previousCodePoint = 0; int numberOfPairs = 0; - fprintf(sourceFile, "static constexpr KDFont::CodepointIndexPair table[] = {\n"); - for (int i=0; i #include #include -#include +#include #include "palette.h" /* We use UTF-8 encoding. This means that a character is encoded as a code point @@ -17,11 +17,11 @@ * ASCII characters have the same encoding in ASCII and in UTF-8. * * We do not provide a glyph for each of the 1,112,064 valid UTF-8 code points. - * We thus have a table of the glyphs we can draw (uint32_t Codepoints[] in - * kandinsky/fonts/codepoints.h). To easily compute the index of a code point in - * the Codepoints table, we use the m_table matching table: it contains the - * CodepointIndexPairs of the first code point of each series of consecutive - * code points in the Codepoints table. */ + * We thus have a table of the glyphs we can draw (uint32_t CodePoints[] in + * kandinsky/fonts/code_points.h). To easily compute the index of a code point in + * the CodePoints table, we use the m_table matching table: it contains the + * CodePointIndexPairs of the first code point of each series of consecutive + * code points in the CodePoints table. */ class KDFont { private: @@ -47,19 +47,19 @@ public: }; using GlyphIndex = uint8_t; - class CodepointIndexPair { + class CodePointIndexPair { public: - constexpr CodepointIndexPair(Codepoint c, GlyphIndex i) : m_codepoint(c), m_glyphIndex(i) {} - Codepoint codepoint() const { return m_codepoint; } + constexpr CodePointIndexPair(CodePoint c, GlyphIndex i) : m_codePoint(c), m_glyphIndex(i) {} + CodePoint codePoint() const { return m_codePoint; } GlyphIndex glyphIndex() const { return m_glyphIndex; } private: - Codepoint m_codepoint; + CodePoint m_codePoint; GlyphIndex m_glyphIndex; }; - GlyphIndex indexForCodepoint(Codepoint c) const; + GlyphIndex indexForCodePoint(CodePoint c) const; - void setGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const; - void accumulateGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const; + void setGlyphGreyscalesForCodePoint(CodePoint codePoint, GlyphBuffer * glyphBuffer) const; + void accumulateGlyphGreyscalesForCodePoint(CodePoint codePoint, GlyphBuffer * glyphBuffer) const; using RenderPalette = KDPalette<(1<; void colorizeGlyphBuffer(const RenderPalette * renderPalette, GlyphBuffer * glyphBuffer) const; @@ -69,7 +69,7 @@ public: } KDSize glyphSize() const { return m_glyphSize; } - constexpr KDFont(size_t tableLength, const CodepointIndexPair * table, KDCoordinate glyphWidth, KDCoordinate glyphHeight, const uint16_t * glyphDataOffset, const uint8_t * data) : + constexpr KDFont(size_t tableLength, const CodePointIndexPair * table, KDCoordinate glyphWidth, KDCoordinate glyphHeight, const uint16_t * glyphDataOffset, const uint8_t * data) : m_tableLength(tableLength), m_table(table), m_glyphSize(glyphWidth, glyphHeight), m_glyphDataOffset(glyphDataOffset), m_data(data) { } private: void fetchGreyscaleGlyphAtIndex(GlyphIndex index, uint8_t * greyscaleBuffer) const; @@ -83,7 +83,7 @@ private: } size_t m_tableLength; - const CodepointIndexPair * m_table; + const CodePointIndexPair * m_table; KDSize m_glyphSize; const uint16_t * m_glyphDataOffset; const uint8_t * m_data; diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h new file mode 100644 index 000000000..0487efb97 --- /dev/null +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -0,0 +1,23 @@ +#ifndef KANDINSKY_UNICODE_CODE_POINT_H +#define KANDINSKY_UNICODE_CODE_POINT_H + +#include + +class CodePoint { +public: + constexpr CodePoint(uint32_t c) : m_code(c) {} + operator uint16_t() const { return m_code; } + + + bool isCombining() const { + return (m_code >= 0x300 && m_code <= 0x036F); + } +private: + uint32_t m_code; +}; + +static constexpr CodePoint Null = 0x0; +static constexpr CodePoint Tabulation = 0x9; +static constexpr CodePoint LineFeed = 0xA; + +#endif diff --git a/kandinsky/include/kandinsky/unicode/codepoint.h b/kandinsky/include/kandinsky/unicode/codepoint.h deleted file mode 100644 index 323647cfe..000000000 --- a/kandinsky/include/kandinsky/unicode/codepoint.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef KANDINSKY_UNICODE_CODEPOINT_H -#define KANDINSKY_UNICODE_CODEPOINT_H - -#include - -class Codepoint { -public: - constexpr Codepoint(uint32_t c) : m_code(c) {} - operator uint16_t() const { return m_code; } - - - bool isCombining() const { - return (m_code >= 0x300 && m_code <= 0x036F); - } -private: - uint32_t m_code; -}; - -static constexpr Codepoint Null = 0x0; -static constexpr Codepoint Tabulation = 0x9; -static constexpr Codepoint LineFeed = 0xA; - -#endif diff --git a/kandinsky/include/kandinsky/unicode/utf8decoder.h b/kandinsky/include/kandinsky/unicode/utf8decoder.h index f9badc77f..36aed2c48 100644 --- a/kandinsky/include/kandinsky/unicode/utf8decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8decoder.h @@ -1,12 +1,12 @@ #ifndef KANDINSKY_UNICODE_UTF8DECODER_H #define KANDINSKY_UNICODE_UTF8DECODER_H -#include "codepoint.h" +#include "code_point.h" class UTF8Decoder { public: UTF8Decoder(const char * string) : m_string(string) {} - Codepoint nextCodepoint(); + CodePoint nextCodePoint(); private: const char * m_string; }; diff --git a/kandinsky/src/context_text.cpp b/kandinsky/src/context_text.cpp index 6a4282f90..d4654a6d0 100644 --- a/kandinsky/src/context_text.cpp +++ b/kandinsky/src/context_text.cpp @@ -13,21 +13,21 @@ KDPoint KDContext::drawString(const char * text, KDPoint p, const KDFont * font, KDFont::GlyphBuffer glyphBuffer; UTF8Decoder decoder(text); - Codepoint codepoint = decoder.nextCodepoint(); - while (codepoint != Null) { - if (codepoint == LineFeed) { + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != Null) { + if (codePoint == LineFeed) { position = KDPoint(0, position.y() + glyphSize.height()); - codepoint = decoder.nextCodepoint(); - } else if (codepoint == Tabulation) { + codePoint = decoder.nextCodePoint(); + } else if (codePoint == Tabulation) { position = position.translatedBy(KDPoint(k_tabCharacterWidth * glyphSize.width(), 0)); - codepoint = decoder.nextCodepoint(); + codePoint = decoder.nextCodePoint(); } else { - assert(!codepoint.isCombining()); - font->setGlyphGreyscalesForCodepoint(codepoint, &glyphBuffer); - codepoint = decoder.nextCodepoint(); - while (codepoint.isCombining()) { - font->accumulateGlyphGreyscalesForCodepoint(codepoint, &glyphBuffer); - codepoint = decoder.nextCodepoint(); + assert(!codePoint.isCombining()); + font->setGlyphGreyscalesForCodePoint(codePoint, &glyphBuffer); + codePoint = decoder.nextCodePoint(); + while (codePoint.isCombining()) { + font->accumulateGlyphGreyscalesForCodePoint(codePoint, &glyphBuffer); + codePoint = decoder.nextCodePoint(); } font->colorizeGlyphBuffer(&palette, &glyphBuffer); // Flush accumulated content diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 40fbebd00..15173836b 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -12,31 +12,31 @@ KDSize KDFont::stringSize(const char * text) const { KDSize stringSize = KDSize(0, m_glyphSize.height()); UTF8Decoder decoder(text); - Codepoint codepoint = decoder.nextCodepoint(); - while (codepoint != Null) { + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != Null) { KDSize cSize = KDSize(m_glyphSize.width(), 0); - if (codepoint == LineFeed) { + if (codePoint == LineFeed) { cSize = KDSize(0, m_glyphSize.height()); - codepoint = decoder.nextCodepoint(); - } else if (codepoint == Tabulation) { + codePoint = decoder.nextCodePoint(); + } else if (codePoint == Tabulation) { cSize = KDSize(k_tabCharacterWidth*m_glyphSize.width(), 0); - } else if (codepoint.isCombining()) { + } else if (codePoint.isCombining()) { cSize = KDSizeZero; } stringSize = KDSize(stringSize.width()+cSize.width(), stringSize.height()+cSize.height()); - codepoint = decoder.nextCodepoint(); + codePoint = decoder.nextCodePoint(); } return stringSize; } -void KDFont::setGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const { - fetchGreyscaleGlyphAtIndex(indexForCodepoint(codepoint), glyphBuffer->greyscaleBuffer()); +void KDFont::setGlyphGreyscalesForCodePoint(CodePoint codePoint, GlyphBuffer * glyphBuffer) const { + fetchGreyscaleGlyphAtIndex(indexForCodePoint(codePoint), glyphBuffer->greyscaleBuffer()); } -void KDFont::accumulateGlyphGreyscalesForCodepoint(Codepoint codepoint, GlyphBuffer * glyphBuffer) const { +void KDFont::accumulateGlyphGreyscalesForCodePoint(CodePoint codePoint, GlyphBuffer * glyphBuffer) const { uint8_t * greyscaleBuffer = glyphBuffer->greyscaleBuffer(); uint8_t * accumulationGreyscaleBuffer = glyphBuffer->secondaryGreyscaleBuffer(); - fetchGreyscaleGlyphAtIndex(indexForCodepoint(codepoint), accumulationGreyscaleBuffer); + fetchGreyscaleGlyphAtIndex(indexForCodePoint(codePoint), accumulationGreyscaleBuffer); for (int i=0; icodepoint() = %d and c = %d\n", currentPair->codepoint(), c); - if (currentPair->codepoint() == c) { + const CodePointIndexPair * currentPair = m_table + currentIndex; + const CodePointIndexPair * nextPair = (currentIndex + 1) < m_tableLength ? currentPair + 1 : nullptr; + // printf("At this point, currentPair->codePoint() = %d and c = %d\n", currentPair->codePoint(), c); + if (currentPair->codePoint() == c) { return currentPair->glyphIndex(); - } else if (currentPair->codepoint() > c) { + } else if (currentPair->codePoint() > c) { // We need to look below if (upperBound == currentIndex) { // There's nothing below. Error out. @@ -101,9 +101,9 @@ KDFont::GlyphIndex KDFont::indexForCodepoint(Codepoint c) const { continue; } else if (nextPair == nullptr) { return 0; - } else if (nextPair->codepoint() == c) { + } else if (nextPair->codePoint() == c) { return nextPair->glyphIndex(); - } else if (nextPair->codepoint() < c) { + } else if (nextPair->codePoint() < c) { // We need to look above if (lowerBound == currentIndex) { // There's nothing above. Error out. @@ -113,40 +113,40 @@ KDFont::GlyphIndex KDFont::indexForCodepoint(Codepoint c) const { continue; } else { // At this point, - // currentPair->codepoint < c && nextPair != nullptr && nextPair->codepoint > c + // currentPair->codePoint < c && nextPair != nullptr && nextPair->codePoint > c // Yay, it's over! // There can be an empty space between the currentPair and the nextPair // e.g. currentPair(3,1) and nextPair(9, 4) - // means value at codepoints 3, 4, 5, 6, 7, 8, 9 + // means value at codePoints 3, 4, 5, 6, 7, 8, 9 // are glyph identifiers 1, ?, ?, ?, ?, ?, 4 // solved as 1, 2, 3, 0, 0, 0, 4 // Let's hunt down the zeroes - Codepoint lastCodepointOfCurrentPair = currentPair->codepoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); - if (c > lastCodepointOfCurrentPair) { + CodePoint lastCodePointOfCurrentPair = currentPair->codePoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); + if (c > lastCodePointOfCurrentPair) { return 0; } - return currentPair->glyphIndex() + (c - currentPair->codepoint()); + return currentPair->glyphIndex() + (c - currentPair->codePoint()); } } #else - const CodepointIndexPair * currentPair = m_table; - if (c < currentPair->codepoint()) { + const CodePointIndexPair * currentPair = m_table; + if (c < currentPair->codePoint()) { return 0; } - const CodepointIndexPair * endPair = m_table + m_tableLength - 1; + const CodePointIndexPair * endPair = m_table + m_tableLength - 1; while (currentPair < endPair) { - const CodepointIndexPair * nextPair = currentPair + 1; - if (c < nextPair->codepoint()) { - Codepoint lastCodepointOfCurrentPair = currentPair->codepoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); - if (c > lastCodepointOfCurrentPair) { + const CodePointIndexPair * nextPair = currentPair + 1; + if (c < nextPair->codePoint()) { + CodePoint lastCodePointOfCurrentPair = currentPair->codePoint() + (nextPair->glyphIndex() - currentPair->glyphIndex() - 1); + if (c > lastCodePointOfCurrentPair) { return 0; } - return currentPair->glyphIndex() + (c - currentPair->codepoint()); + return currentPair->glyphIndex() + (c - currentPair->codePoint()); } currentPair = nextPair; } - if (endPair->codepoint() == c) { + if (endPair->codePoint() == c) { return endPair->glyphIndex(); } return 0; diff --git a/kandinsky/src/unicode/utf8decoder.cpp b/kandinsky/src/unicode/utf8decoder.cpp index e8585e9f8..7990c28bb 100644 --- a/kandinsky/src/unicode/utf8decoder.cpp +++ b/kandinsky/src/unicode/utf8decoder.cpp @@ -15,12 +15,12 @@ static inline uint8_t last_k_bits(uint8_t value, uint8_t bits) { return (value & ((1< #include -static constexpr KDFont::CodepointIndexPair table[] = { - KDFont::CodepointIndexPair(3, 1), // Codepoint, identifier - KDFont::CodepointIndexPair(9, 4), - KDFont::CodepointIndexPair(12, 5), - KDFont::CodepointIndexPair(14, 7) +static constexpr KDFont::CodePointIndexPair table[] = { + KDFont::CodePointIndexPair(3, 1), // CodePoint, identifier + KDFont::CodePointIndexPair(9, 4), + KDFont::CodePointIndexPair(12, 5), + KDFont::CodePointIndexPair(14, 7) }; constexpr KDFont testFont(4, table, 10, 10, nullptr, nullptr); -const KDFont::GlyphIndex index_for_codepoint[] = { +const KDFont::GlyphIndex index_for_code_point[] = { /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */ 0, 0, 0, 1, 2, 3, 0, 0, 0, 4, 0, 0, 5, 6, 7, 0 }; -QUIZ_CASE(kandinsky_font_index_for_codepoint) { +QUIZ_CASE(kandinsky_font_index_for_code_point) { for (int i=0; i<16; i++) { - KDFont::GlyphIndex result = testFont.indexForCodepoint(i); - quiz_assert(result == index_for_codepoint[i]); + KDFont::GlyphIndex result = testFont.indexForCodePoint(i); + quiz_assert(result == index_for_code_point[i]); } } diff --git a/kandinsky/test/utf8decoder.cpp b/kandinsky/test/utf8decoder.cpp index 3e858f526..1198166a6 100644 --- a/kandinsky/test/utf8decoder.cpp +++ b/kandinsky/test/utf8decoder.cpp @@ -1,10 +1,10 @@ #include #include -void assert_decodes_to(const char * string, Codepoint c) { +void assert_decodes_to(const char * string, CodePoint c) { UTF8Decoder d(string); - quiz_assert(d.nextCodepoint() == c); - quiz_assert(d.nextCodepoint() == 0); + quiz_assert(d.nextCodePoint() == c); + quiz_assert(d.nextCodePoint() == 0); } QUIZ_CASE(kandinsky_utf8_decoder) { diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index bd2c8e823..84c3a2d7e 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -16,13 +16,13 @@ static inline bool isDigit(const char c) { const char Tokenizer::nextChar(PopTest popTest, char context, bool * testResult) { // Beware of chars spaning over more than one byte: use the UTF8Decoder. UTF8Decoder decoder(m_text); - Codepoint firstCodepoint = decoder.nextCodepoint(); + CodePoint firstCodePoint = decoder.nextCodePoint(); int numberOfBytesForChar = 1; - if (firstCodepoint != Null) { - Codepoint codepoint = decoder.nextCodepoint(); - while (codepoint.isCombining()) { + if (firstCodePoint != Null) { + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint.isCombining()) { numberOfBytesForChar++; - codepoint = decoder.nextCodepoint(); + codePoint = decoder.nextCodePoint(); } } char c = *m_text; // TODO handle combined chars? From c31fe3856a89fa17bb77b47b3ff29172f9bb9946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 11:40:32 +0100 Subject: [PATCH 018/295] [kandinsky] Add KDCodePoint prefix static code points --- kandinsky/include/kandinsky/unicode/code_point.h | 6 +++--- kandinsky/src/context_text.cpp | 6 +++--- kandinsky/src/font.cpp | 6 +++--- poincare/src/parsing/tokenizer.cpp | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h index 0487efb97..033e96407 100644 --- a/kandinsky/include/kandinsky/unicode/code_point.h +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -16,8 +16,8 @@ private: uint32_t m_code; }; -static constexpr CodePoint Null = 0x0; -static constexpr CodePoint Tabulation = 0x9; -static constexpr CodePoint LineFeed = 0xA; +static constexpr CodePoint KDCodePointNull = 0x0; +static constexpr CodePoint KDCodePointTabulation = 0x9; +static constexpr CodePoint KDCodePointLineFeed = 0xA; #endif diff --git a/kandinsky/src/context_text.cpp b/kandinsky/src/context_text.cpp index d4654a6d0..05728dbba 100644 --- a/kandinsky/src/context_text.cpp +++ b/kandinsky/src/context_text.cpp @@ -14,11 +14,11 @@ KDPoint KDContext::drawString(const char * text, KDPoint p, const KDFont * font, UTF8Decoder decoder(text); CodePoint codePoint = decoder.nextCodePoint(); - while (codePoint != Null) { - if (codePoint == LineFeed) { + while (codePoint != KDCodePointNull) { + if (codePoint == KDCodePointLineFeed) { position = KDPoint(0, position.y() + glyphSize.height()); codePoint = decoder.nextCodePoint(); - } else if (codePoint == Tabulation) { + } else if (codePoint == KDCodePointTabulation) { position = position.translatedBy(KDPoint(k_tabCharacterWidth * glyphSize.width(), 0)); codePoint = decoder.nextCodePoint(); } else { diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 15173836b..6f48d7814 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -13,12 +13,12 @@ KDSize KDFont::stringSize(const char * text) const { UTF8Decoder decoder(text); CodePoint codePoint = decoder.nextCodePoint(); - while (codePoint != Null) { + while (codePoint != KDCodePointNull) { KDSize cSize = KDSize(m_glyphSize.width(), 0); - if (codePoint == LineFeed) { + if (codePoint == KDCodePointLineFeed) { cSize = KDSize(0, m_glyphSize.height()); codePoint = decoder.nextCodePoint(); - } else if (codePoint == Tabulation) { + } else if (codePoint == KDCodePointTabulation) { cSize = KDSize(k_tabCharacterWidth*m_glyphSize.width(), 0); } else if (codePoint.isCombining()) { cSize = KDSizeZero; diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 84c3a2d7e..0dfa5e0fa 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -18,7 +18,7 @@ const char Tokenizer::nextChar(PopTest popTest, char context, bool * testResult) UTF8Decoder decoder(m_text); CodePoint firstCodePoint = decoder.nextCodePoint(); int numberOfBytesForChar = 1; - if (firstCodePoint != Null) { + if (firstCodePoint != KDCodePointNull) { CodePoint codePoint = decoder.nextCodePoint(); while (codePoint.isCombining()) { numberOfBytesForChar++; From 1928e267a97c7e9cc38a09802d2dcb588a2e691c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 11:52:20 +0100 Subject: [PATCH 019/295] [kandinsky] UTF8Decoder::CodePointToChars --- .../include/kandinsky/unicode/utf8decoder.h | 13 ++++++++++++ kandinsky/src/unicode/utf8decoder.cpp | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/kandinsky/include/kandinsky/unicode/utf8decoder.h b/kandinsky/include/kandinsky/unicode/utf8decoder.h index 36aed2c48..fcabe859a 100644 --- a/kandinsky/include/kandinsky/unicode/utf8decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8decoder.h @@ -1,12 +1,25 @@ #ifndef KANDINSKY_UNICODE_UTF8DECODER_H #define KANDINSKY_UNICODE_UTF8DECODER_H +#include #include "code_point.h" +/* UTF-8 encodes all valid code points using at most 4 bytes (= 28 bits), the + * lowest codes being equal to ASCII codes. There are less than 2^21 different + * UTF-8 valid code points. + * + * The encoding is the following: + * For code points between ... -> The corresponding bits are ... + * 0 and 7F -> 0xxxxxxx + * 80 and 7FF -> 110xxxxx 10xxxxxx + * 800 and FFFF -> 1110xxxx 10xxxxxx 10xxxxxx + * 10000 and 10FFFF -> 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + class UTF8Decoder { public: UTF8Decoder(const char * string) : m_string(string) {} CodePoint nextCodePoint(); + static size_t CodePointToChars(CodePoint c, char * buffer, int bufferSize); private: const char * m_string; }; diff --git a/kandinsky/src/unicode/utf8decoder.cpp b/kandinsky/src/unicode/utf8decoder.cpp index 7990c28bb..5648df4df 100644 --- a/kandinsky/src/unicode/utf8decoder.cpp +++ b/kandinsky/src/unicode/utf8decoder.cpp @@ -24,3 +24,24 @@ CodePoint UTF8Decoder::nextCodePoint() { } return CodePoint(result); } + +size_t UTF8Decoder::CodePointToChars(CodePoint c, char * buffer, int bufferSize) { + assert(bufferSize >= sizeof(CodePoint)/sizeof(char)); + size_t i = 0; + if (c <= 0x7F) { + buffer[i++] = c; + } else if (c <= 0x7FF) { + buffer[i++] = 0b11000000 | (c >> 6); + buffer[i++] = 0b10000000 | (c & 0b111111); + } else if (c <= 0xFFFF) { + buffer[i++] = 0b11100000 | (c >> 12); + buffer[i++] = 0b10000000 | ((c >> 6) & 0b111111); + buffer[i++] = 0b10000000 | (c & 0b111111); + } else { + buffer[i++] = 0b11110000 | (c >> 18); + buffer[i++] = 0b10000000 | ((c >> 12) & 0b111111); + buffer[i++] = 0b10000000 | ((c >> 6) & 0b111111); + buffer[i++] = 0b10000000 | (c & 0b111111); + } + return i; +} From 1a55c2023b0a8a98bd878030c8e975c3cc3ce759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 12:13:22 +0100 Subject: [PATCH 020/295] [kandinsly] Rename utf8decoder as utf8_decoder --- kandinsky/Makefile | 4 ++-- .../kandinsky/unicode/{utf8decoder.h => utf8_decoder.h} | 4 ++-- kandinsky/src/context_text.cpp | 2 +- kandinsky/src/font.cpp | 2 +- kandinsky/src/unicode/{utf8decoder.cpp => utf8_decoder.cpp} | 2 +- kandinsky/test/utf8decoder.cpp | 2 +- poincare/src/parsing/tokenizer.cpp | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename kandinsky/include/kandinsky/unicode/{utf8decoder.h => utf8_decoder.h} (90%) rename kandinsky/src/unicode/{utf8decoder.cpp => utf8_decoder.cpp} (96%) diff --git a/kandinsky/Makefile b/kandinsky/Makefile index af4804052..368948437 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -13,7 +13,7 @@ src += $(addprefix kandinsky/src/,\ ion_context.cpp \ point.cpp \ rect.cpp \ - unicode/utf8decoder.cpp\ + unicode/utf8_decoder.cpp\ ) src += $(addprefix kandinsky/fonts/, \ @@ -25,7 +25,7 @@ tests += $(addprefix kandinsky/test/,\ color.cpp\ font.cpp\ rect.cpp\ - utf8decoder.cpp\ + utf8_decoder.cpp\ ) RASTERIZER_CFLAGS := -std=c99 `pkg-config freetype2 --cflags` diff --git a/kandinsky/include/kandinsky/unicode/utf8decoder.h b/kandinsky/include/kandinsky/unicode/utf8_decoder.h similarity index 90% rename from kandinsky/include/kandinsky/unicode/utf8decoder.h rename to kandinsky/include/kandinsky/unicode/utf8_decoder.h index fcabe859a..f4dab5269 100644 --- a/kandinsky/include/kandinsky/unicode/utf8decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8_decoder.h @@ -1,5 +1,5 @@ -#ifndef KANDINSKY_UNICODE_UTF8DECODER_H -#define KANDINSKY_UNICODE_UTF8DECODER_H +#ifndef KANDINSKY_UNICODE_UTF8_DECODER_H +#define KANDINSKY_UNICODE_UTF8_DECODER_H #include #include "code_point.h" diff --git a/kandinsky/src/context_text.cpp b/kandinsky/src/context_text.cpp index 05728dbba..860d9da65 100644 --- a/kandinsky/src/context_text.cpp +++ b/kandinsky/src/context_text.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include constexpr static int k_tabCharacterWidth = 4; diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 6f48d7814..a921b4a4b 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include constexpr static int k_tabCharacterWidth = 4; diff --git a/kandinsky/src/unicode/utf8decoder.cpp b/kandinsky/src/unicode/utf8_decoder.cpp similarity index 96% rename from kandinsky/src/unicode/utf8decoder.cpp rename to kandinsky/src/unicode/utf8_decoder.cpp index 5648df4df..bdb6ed370 100644 --- a/kandinsky/src/unicode/utf8decoder.cpp +++ b/kandinsky/src/unicode/utf8_decoder.cpp @@ -1,4 +1,4 @@ -#include +#include #include static inline int leading_ones(uint8_t value) { diff --git a/kandinsky/test/utf8decoder.cpp b/kandinsky/test/utf8decoder.cpp index 1198166a6..3c649f3b7 100644 --- a/kandinsky/test/utf8decoder.cpp +++ b/kandinsky/test/utf8decoder.cpp @@ -1,5 +1,5 @@ #include -#include +#include void assert_decodes_to(const char * string, CodePoint c) { UTF8Decoder d(string); diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 0dfa5e0fa..9b7d440f7 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -1,7 +1,7 @@ #include "tokenizer.h" #include #include -#include +#include namespace Poincare { From 3f56cb70415a13b09090f258fdff49449ecaa425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 14:02:50 +0100 Subject: [PATCH 021/295] [poincare] CodePointLayout --- .../include/kandinsky/unicode/code_point.h | 10 +- poincare/Makefile | 1 + poincare/include/poincare/code_point_layout.h | 76 ++++++++++++ poincare/include/poincare/layout.h | 1 + poincare/include/poincare/layout_node.h | 1 + .../include/poincare/serialization_helper.h | 9 +- poincare/include/poincare_layouts.h | 1 + poincare/src/code_point_layout.cpp | 108 ++++++++++++++++++ poincare/src/serialization_helper.cpp | 18 +++ 9 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 poincare/include/poincare/code_point_layout.h create mode 100644 poincare/src/code_point_layout.cpp diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h index 033e96407..fc32a7b1a 100644 --- a/kandinsky/include/kandinsky/unicode/code_point.h +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -16,8 +16,12 @@ private: uint32_t m_code; }; -static constexpr CodePoint KDCodePointNull = 0x0; -static constexpr CodePoint KDCodePointTabulation = 0x9; -static constexpr CodePoint KDCodePointLineFeed = 0xA; +static constexpr CodePoint KDCodePointNull = 0x0; +static constexpr CodePoint KDCodePointTabulation = 0x9; +static constexpr CodePoint KDCodePointLineFeed = 0xA; +static constexpr CodePoint KDCodePointMiddleDot = 0xB7; +static constexpr CodePoint KDCodePointMultiplicationSign = 0xD7; +static constexpr CodePoint KDCodePointLatinLetterSmallCapitalE = 0x1d07; +static constexpr CodePoint KDCodePointRightwardsArrow = 0x2192; #endif diff --git a/poincare/Makefile b/poincare/Makefile index 4a41680f9..cc7f7e2df 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -5,6 +5,7 @@ src += $(addprefix poincare/src/,\ bracket_layout.cpp \ bracket_pair_layout.cpp \ char_layout.cpp \ + code_point_layout.cpp\ condensed_sum_layout.cpp \ conjugate_layout.cpp \ empty_layout.cpp \ diff --git a/poincare/include/poincare/code_point_layout.h b/poincare/include/poincare/code_point_layout.h new file mode 100644 index 000000000..06f5adb91 --- /dev/null +++ b/poincare/include/poincare/code_point_layout.h @@ -0,0 +1,76 @@ +#ifndef POINCARE_CODEPOINT_LAYOUT_NODE_H +#define POINCARE_CODEPOINT_LAYOUT_NODE_H + +#include +#include +#include + +namespace Poincare { + +/* TODO: Make several code point classes depending on codepoint size? + * (m_codePoint sometimes fits in a char, no need for a whole CodePoint */ + +class CodePointLayoutNode final : public LayoutNode { +public: + static constexpr const KDFont * k_defaultFont = KDFont::LargeFont; + CodePointLayoutNode(CodePoint c = KDCodePointNull, const KDFont * font = k_defaultFont) : + LayoutNode(), + m_codePoint(c), + m_font(font) + {} + + // CodePointLayout + CodePoint codePoint() const { return m_codePoint; } + const KDFont * font() const { return m_font; } + + // LayoutNode + void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + bool isCodePoint() const override { return true; } + bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const override; + bool canBeOmittedMultiplicationLeftFactor() const override; + bool canBeOmittedMultiplicationRightFactor() const override; + + // TreeNode + size_t size() const override { return sizeof(CodePointLayoutNode); } + int numberOfChildren() const override { return 0; } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "CodePointLayout"; + } + virtual void logAttributes(std::ostream & stream) const override { + stream << " CodePoint=\"" << m_codePoint << "\""; + } +#endif + +protected: + // LayoutNode + KDSize computeSize() override; + KDCoordinate computeBaseline() override; + KDPoint positionOfChild(LayoutNode * child) override { + assert(false); + return KDPointZero; + } + +private: + void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; + bool isMultiplicationCodePoint() const; + CodePoint m_codePoint; + const KDFont * m_font; +}; + +class CodePointLayout final : public Layout { +public: + CodePointLayout(const CodePointLayoutNode * n) : Layout(n) {} + static CharLayout Builder(CodePoint c, const KDFont * font = KDFont::LargeFont); + const KDFont * font() const { return const_cast(this)->node()->font(); } + CodePoint codePoint() const { return const_cast(this)->node()->codePoint(); } +private: + using Layout::node; + CodePointLayoutNode * node() { return static_cast(Layout::node()); } +}; + +} + +#endif diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 2dfffdcd9..bf70fba41 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -50,6 +50,7 @@ public: bool isVerticalOffset() const { return const_cast(this)->node()->isVerticalOffset(); } bool isLeftParenthesis() const { return const_cast(this)->node()->isLeftParenthesis(); } bool isChar() const { return const_cast(this)->node()->isChar(); } + bool isCodePoint() const { return const_cast(this)->node()->isCodePoint(); } bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return const_cast(this)->node()->isCollapsable(numberOfOpenParenthesis, goingLeft); } int leftCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->leftCollapsingAbsorbingChildIndex(); } int rightCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->rightCollapsingAbsorbingChildIndex(); } diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 40a2afe41..c10120091 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -105,6 +105,7 @@ public: virtual bool isEmpty() const { return false; } virtual bool isMatrix() const { return false; } virtual bool isChar() const { return false; } + virtual bool isCodePoint() const { return false; } virtual bool hasUpperLeftIndex() const { return false; } virtual char XNTChar() const { LayoutNode * p = parent(); diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index 2135d0aea..c2406302d 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -2,11 +2,12 @@ #define POINCARE_SERIALIZATION_HELPER_H #include +#include namespace Poincare { namespace SerializationHelper { - /* SerializableReference to Text */ + // SerializableReference to text int Infix( const TreeNode * node, char * buffer, @@ -26,8 +27,10 @@ namespace SerializationHelper { const char * operatorName, bool writeFirstChild = true); - /* Write one char in buffer */ - int Char(char * buffer, int bufferSize, char charToWrite); + // Write one char in buffer + int Char(char * buffer, int bufferSize, char charToWrite); // TODO REMOVE + // Write one code point in a buffer + int CodePoint(char * buffer, int bufferSize, CodePoint c); }; } diff --git a/poincare/include/poincare_layouts.h b/poincare/include/poincare_layouts.h index 743d99bac..3cf5ee953 100644 --- a/poincare/include/poincare_layouts.h +++ b/poincare/include/poincare_layouts.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/src/code_point_layout.cpp b/poincare/src/code_point_layout.cpp new file mode 100644 index 000000000..2e052164c --- /dev/null +++ b/poincare/src/code_point_layout.cpp @@ -0,0 +1,108 @@ +#include +#include +#include + +namespace Poincare { + +// LayoutNode +void CodePointLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Right) { + cursor->setPosition(LayoutCursor::Position::Left); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } +} + +void CodePointLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) { + if (cursor->position() == LayoutCursor::Position::Left) { + cursor->setPosition(LayoutCursor::Position::Right); + return; + } + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } +} + +int CodePointLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return SerializationHelper::CodePoint(buffer, bufferSize, m_codePoint); +} + +bool CodePointLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { + if (*numberOfOpenParenthesis <= 0) { + if (m_codePoint == '+' + || m_codePoint == KDCodePointRightwardsArrow + || m_codePoint == '=' + || m_codePoint == ',') + { + return false; + } + if (m_codePoint == '-') { + /* If the expression is like 3ᴇ-200, we want '-' to be collapsable. + * Otherwise, '-' is not collapsable. */ + Layout thisRef = CodePointLayout(this); + Layout parent = thisRef.parent(); + if (!parent.isUninitialized()) { + int indexOfThis = parent.indexOfChild(thisRef); + if (indexOfThis > 0) { + Layout leftBrother = parent.childAtIndex(indexOfThis-1); + if (leftBrother.isCodePoint() + && static_cast(leftBrother).codePoint() == KDCodePointLatinLetterSmallCapitalE) + { + return true; + } + } + } + return false; + } + } + return true; +} + +bool CodePointLayoutNode::canBeOmittedMultiplicationLeftFactor() const { + if (isMultiplicationCodePoint()) { + return false; + } + return LayoutNode::canBeOmittedMultiplicationRightFactor(); +} + +bool CodePointLayoutNode::canBeOmittedMultiplicationRightFactor() const { + if (m_codePoint == '!' || isMultiplicationCodePoint()) { + return false; + } + return LayoutNode::canBeOmittedMultiplicationRightFactor(); +} + +// Sizing and positioning +KDSize CodePointLayoutNode::computeSize() { + return m_font->glyphSize(); +} + +KDCoordinate CodePointLayoutNode::computeBaseline() { + return m_font->glyphSize().height()/2; +} + +void CodePointLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { + constexpr int bufferSize = sizeof(CodePoint)/sizeof(char) + 1; // Null-terminating char + char buffer[bufferSize]; + SerializationHelper::CodePoint(buffer, bufferSize, m_codePoint); + ctx->drawString(buffer, p, m_font, expressionColor, backgroundColor); +} + +bool CodePointLayoutNode::isMultiplicationCodePoint() const { + return m_codePoint == '*' + || m_codePoint == KDCodePointMultiplicationSign + || m_codePoint == KDCodePointMiddleDot; +} + +CodePointLayout CodePointLayout::Builder(CodePoint c, const KDFont * font) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(CodePointLayoutNode)); + CodePointLayoutNode * node = new (bufferNode) CodePointLayoutNode(c, font); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + +} diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index 3375d3b6f..c255bd160 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -149,4 +150,21 @@ int SerializationHelper::Char(char * buffer, int bufferSize, char charToWrite) { return 1; } +int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoint c) { + if (bufferSize == 0) { + return -1; + } + if (bufferSize == 1) { + buffer[0] = 0; + return 0; + } + constexpr int maxCodePointSize = sizeof(class CodePoint)/sizeof(char) + 1; // Null-terminating char + char helpBuffer[maxCodePointSize]; + size_t size = UTF8Decoder::CodePointToChars(c, helpBuffer, maxCodePointSize); + assert(size < maxCodePointSize); + helpBuffer[size] = 0; + strlcpy(buffer, helpBuffer, bufferSize); + return strlen(buffer); +} + } From f576e31ff11326a8dbc8409971b807ccbe4021dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 14:09:45 +0100 Subject: [PATCH 022/295] [apps/poincare] Replace CharLayouts with CodePointLayouts --- apps/regression/calculation_controller.cpp | 4 +- apps/regression/model/cubic_model.cpp | 32 ++--- apps/regression/model/exponential_model.cpp | 14 +-- apps/regression/model/linear_model.cpp | 12 +- apps/regression/model/logarithmic_model.cpp | 20 ++-- apps/regression/model/logistic_model.cpp | 22 ++-- apps/regression/model/power_model.cpp | 10 +- apps/regression/model/quadratic_model.cpp | 22 ++-- apps/regression/model/quartic_model.cpp | 42 +++---- apps/regression/model/trigonometric_model.cpp | 30 ++--- apps/sequence/graph/term_sum_controller.cpp | 6 +- apps/sequence/list/sequence_toolbox.cpp | 8 +- .../list/type_parameter_controller.cpp | 4 +- apps/sequence/sequence.cpp | 16 +-- apps/sequence/sequence_title_cell.h | 2 +- .../sub_menu/preferences_controller.cpp | 4 +- apps/solver/list_controller.cpp | 4 +- apps/solver/solutions_controller.cpp | 4 +- poincare/Makefile | 1 - poincare/include/poincare/char_layout.h | 73 ------------ poincare/include/poincare/layout.h | 1 - poincare/include/poincare/layout_cursor.h | 2 +- poincare/include/poincare/layout_node.h | 5 +- .../include/poincare/serialization_helper.h | 4 +- poincare/include/poincare_layouts.h | 1 - poincare/src/char_layout.cpp | 106 ----------------- poincare/src/constant.cpp | 2 +- poincare/src/equal.cpp | 4 +- poincare/src/factorial.cpp | 4 +- poincare/src/integral_layout.cpp | 2 +- poincare/src/layout.cpp | 2 +- poincare/src/layout_cursor.cpp | 20 ++-- poincare/src/layout_helper.cpp | 6 +- poincare/src/opposite.cpp | 4 +- poincare/src/product_layout.cpp | 2 +- poincare/src/rational.cpp | 2 +- poincare/src/sequence_layout.cpp | 2 +- poincare/src/serialization_helper.cpp | 6 +- poincare/src/store.cpp | 4 +- poincare/src/sum_layout.cpp | 2 +- poincare/src/symbol.cpp | 16 +-- poincare/test/fraction_layout.cpp | 8 +- poincare/test/layouts.cpp | 110 +++++++++--------- poincare/test/parentheses_layout.cpp | 12 +- poincare/test/vertical_offset_layout.cpp | 2 +- 45 files changed, 238 insertions(+), 421 deletions(-) delete mode 100644 poincare/include/poincare/char_layout.h delete mode 100644 poincare/src/char_layout.cpp diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 895256377..02446c0f3 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -1,7 +1,7 @@ #include "calculation_controller.h" #include "../apps_container.h" #include "../shared/poincare_helpers.h" -#include +#include #include #include @@ -25,7 +25,7 @@ CalculationController::CalculationController(Responder * parentResponder, Button m_hideableCell(), m_store(store) { - m_r2Layout = HorizontalLayout::Builder(CharLayout::Builder('r', KDFont::SmallFont), VerticalOffsetLayout::Builder(CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript)); + m_r2Layout = HorizontalLayout::Builder(CodePointLayout::Builder('r', KDFont::SmallFont), VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript)); m_selectableTableView.setVerticalCellOverlap(0); m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); m_selectableTableView.setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp index 5147ff5f5..39cf2139b 100644 --- a/apps/regression/model/cubic_model.cpp +++ b/apps/regression/model/cubic_model.cpp @@ -2,7 +2,7 @@ #include "../../shared/poincare_helpers.h" #include #include -#include +#include #include #include #include @@ -20,27 +20,27 @@ namespace Regression { Layout CubicModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('3', KDFont::SmallFont), + CodePointLayout::Builder('3', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('2', KDFont::SmallFont), + CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('c', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('d', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('c', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('d', KDFont::SmallFont), }; m_layout = HorizontalLayout::Builder(layoutChildren, 15); } diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp index 320d99b45..4b2c3fa5d 100644 --- a/apps/regression/model/exponential_model.cpp +++ b/apps/regression/model/exponential_model.cpp @@ -1,7 +1,7 @@ #include "exponential_model.h" #include #include -#include +#include #include #include @@ -12,14 +12,14 @@ namespace Regression { Layout ExponentialModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('e', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('e', KDFont::SmallFont), VerticalOffsetLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont) + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont) ), VerticalOffsetLayoutNode::Type::Superscript ) diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 92e03af93..116126fc5 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -2,7 +2,7 @@ #include "../store.h" #include #include -#include +#include #include using namespace Poincare; @@ -12,11 +12,11 @@ namespace Regression { Layout LinearModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), }; m_layout = HorizontalLayout::Builder(layoutChildren, 5); } diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp index fb7563928..255ef5268 100644 --- a/apps/regression/model/logarithmic_model.cpp +++ b/apps/regression/model/logarithmic_model.cpp @@ -2,7 +2,7 @@ #include "../store.h" #include #include -#include +#include #include using namespace Poincare; @@ -12,15 +12,15 @@ namespace Regression { Layout LogarithmicModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('l', KDFont::SmallFont), - CharLayout::Builder('n', KDFont::SmallFont), - CharLayout::Builder('(', KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder(')', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont) + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('l', KDFont::SmallFont), + CodePointLayout::Builder('n', KDFont::SmallFont), + CodePointLayout::Builder('(', KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder(')', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont) }; m_layout = HorizontalLayout::Builder(layoutChildren, 9); } diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp index ad0ea7503..5f637e798 100644 --- a/apps/regression/model/logistic_model.cpp +++ b/apps/regression/model/logistic_model.cpp @@ -1,7 +1,7 @@ #include "logistic_model.h" #include #include -#include +#include #include #include #include @@ -13,24 +13,24 @@ namespace Regression { Layout LogisticModel::layout() { if (m_layout.isUninitialized()) { Layout exponentLayoutChildren[] = { - CharLayout::Builder('-', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont) + CodePointLayout::Builder('-', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont) }; Layout layoutChildren[] = { - CharLayout::Builder('1', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('e', KDFont::SmallFont), + CodePointLayout::Builder('1', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('e', KDFont::SmallFont), VerticalOffsetLayout::Builder( HorizontalLayout::Builder(exponentLayoutChildren, 4), VerticalOffsetLayoutNode::Type::Superscript ) }; m_layout = FractionLayout::Builder( - CharLayout::Builder('c', KDFont::SmallFont), + CodePointLayout::Builder('c', KDFont::SmallFont), HorizontalLayout::Builder(layoutChildren, 6) ); } diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp index bd790d815..7a5dcc992 100644 --- a/apps/regression/model/power_model.cpp +++ b/apps/regression/model/power_model.cpp @@ -2,7 +2,7 @@ #include "../store.h" #include #include -#include +#include #include #include @@ -13,11 +13,11 @@ namespace Regression { Layout PowerModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), }; diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp index 0ee0b1764..16d9fd6c9 100644 --- a/apps/regression/model/quadratic_model.cpp +++ b/apps/regression/model/quadratic_model.cpp @@ -2,7 +2,7 @@ #include "../../shared/poincare_helpers.h" #include #include -#include +#include #include #include #include @@ -20,19 +20,19 @@ namespace Regression { Layout QuadraticModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('2', KDFont::SmallFont), + CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('c', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('c', KDFont::SmallFont), }; m_layout = HorizontalLayout::Builder(layoutChildren, 10); } diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp index 3f7a4395a..163880fae 100644 --- a/apps/regression/model/quartic_model.cpp +++ b/apps/regression/model/quartic_model.cpp @@ -2,7 +2,7 @@ #include "../../shared/poincare_helpers.h" #include #include -#include +#include #include #include #include @@ -20,35 +20,35 @@ namespace Regression { Layout QuarticModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('4', KDFont::SmallFont), + CodePointLayout::Builder('4', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('3', KDFont::SmallFont), + CodePointLayout::Builder('3', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('c', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('c', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('2', KDFont::SmallFont), + CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('d', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('e', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('d', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('e', KDFont::SmallFont), }; m_layout = HorizontalLayout::Builder(layoutChildren, 20); } diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp index 2a605cdde..8fceb306f 100644 --- a/apps/regression/model/trigonometric_model.cpp +++ b/apps/regression/model/trigonometric_model.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -21,20 +21,20 @@ namespace Regression { Layout TrigonometricModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { - CharLayout::Builder('a', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('s', KDFont::SmallFont), - CharLayout::Builder('i', KDFont::SmallFont), - CharLayout::Builder('n', KDFont::SmallFont), - CharLayout::Builder('(', KDFont::SmallFont), - CharLayout::Builder('b', KDFont::SmallFont), - CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout::Builder('X', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('c', KDFont::SmallFont), - CharLayout::Builder(')', KDFont::SmallFont), - CharLayout::Builder('+', KDFont::SmallFont), - CharLayout::Builder('d', KDFont::SmallFont) + CodePointLayout::Builder('a', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('s', KDFont::SmallFont), + CodePointLayout::Builder('i', KDFont::SmallFont), + CodePointLayout::Builder('n', KDFont::SmallFont), + CodePointLayout::Builder('(', KDFont::SmallFont), + CodePointLayout::Builder('b', KDFont::SmallFont), + CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder('X', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('c', KDFont::SmallFont), + CodePointLayout::Builder(')', KDFont::SmallFont), + CodePointLayout::Builder('+', KDFont::SmallFont), + CodePointLayout::Builder('d', KDFont::SmallFont) }; m_layout = HorizontalLayout::Builder(layoutChildren, 14); } diff --git a/apps/sequence/graph/term_sum_controller.cpp b/apps/sequence/graph/term_sum_controller.cpp index 35fcfb49f..17b59c943 100644 --- a/apps/sequence/graph/term_sum_controller.cpp +++ b/apps/sequence/graph/term_sum_controller.cpp @@ -1,7 +1,7 @@ #include "term_sum_controller.h" #include "../../shared/text_field_delegate.h" #include "../app.h" -#include +#include #include #include @@ -51,9 +51,9 @@ double TermSumController::cursorNextStep(double x, int direction) { Layout TermSumController::createFunctionLayout(const char * functionName) { return HorizontalLayout::Builder( - CharLayout::Builder(functionName[0], KDFont::SmallFont), + CodePointLayout::Builder(functionName[0], KDFont::SmallFont), VerticalOffsetLayout::Builder( - CharLayout::Builder('n', KDFont::SmallFont), + CodePointLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript ) ); diff --git a/apps/sequence/list/sequence_toolbox.cpp b/apps/sequence/list/sequence_toolbox.cpp index 5dbcd3380..0742fc4bd 100644 --- a/apps/sequence/list/sequence_toolbox.cpp +++ b/apps/sequence/list/sequence_toolbox.cpp @@ -2,7 +2,7 @@ #include "../sequence_store.h" #include #include -#include +#include #include using namespace Poincare; @@ -77,18 +77,18 @@ void SequenceToolbox::buildExtraCellsLayouts(const char * sequenceName, int recu for (int j = 0; j < recurrenceDepth; j++) { const char * indice = j == 0 ? "n" : "n+1"; m_addedCellLayout[j] = HorizontalLayout::Builder( - CharLayout::Builder(sequenceName[0], KDFont::LargeFont), + CodePointLayout::Builder(sequenceName[0], KDFont::LargeFont), VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); m_addedCellLayout[j+recurrenceDepth] = HorizontalLayout::Builder( - CharLayout::Builder(otherSequenceName[0], KDFont::LargeFont), + CodePointLayout::Builder(otherSequenceName[0], KDFont::LargeFont), VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); } if (recurrenceDepth < 2) { const char * indice = recurrenceDepth == 0 ? "n" : (recurrenceDepth == 1 ? "n+1" : "n+2"); m_addedCellLayout[2*recurrenceDepth] = HorizontalLayout::Builder( - CharLayout::Builder(otherSequenceName[0], KDFont::LargeFont), + CodePointLayout::Builder(otherSequenceName[0], KDFont::LargeFont), VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); } diff --git a/apps/sequence/list/type_parameter_controller.cpp b/apps/sequence/list/type_parameter_controller.cpp index 71bace67e..75bbd12e0 100644 --- a/apps/sequence/list/type_parameter_controller.cpp +++ b/apps/sequence/list/type_parameter_controller.cpp @@ -3,7 +3,7 @@ #include "../app.h" #include #include -#include +#include #include using namespace Poincare; @@ -116,7 +116,7 @@ void TypeParameterController::willDisplayCellAtLocation(HighlightCell * cell, in } const char * subscripts[3] = {"n", "n+1", "n+2"}; m_layouts[j] = HorizontalLayout::Builder( - CharLayout::Builder(nextName[0], font), + CodePointLayout::Builder(nextName[0], font), VerticalOffsetLayout::Builder(LayoutHelper::String(subscripts[j], strlen(subscripts[j]), font), VerticalOffsetLayoutNode::Type::Subscript) ); ExpressionTableCellWithPointer * myCell = (ExpressionTableCellWithPointer *)cell; diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index d413343b0..e108f1b25 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -2,7 +2,7 @@ #include "sequence_store.h" #include "cache_context.h" #include -#include +#include #include #include #include "../shared/poincare_helpers.h" @@ -147,8 +147,8 @@ int Sequence::numberOfElements() { Poincare::Layout Sequence::nameLayout() { if (m_nameLayout.isUninitialized()) { m_nameLayout = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], KDFont::SmallFont), - VerticalOffsetLayout::Builder(CharLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) + CodePointLayout::Builder(name()[0], KDFont::SmallFont), + VerticalOffsetLayout::Builder(CodePointLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) ); } return m_nameLayout; @@ -158,16 +158,16 @@ Poincare::Layout Sequence::definitionName() { if (m_definitionName.isUninitialized()) { if (m_type == Type::Explicit) { m_definitionName = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], k_layoutFont), + CodePointLayout::Builder(name()[0], k_layoutFont), VerticalOffsetLayout::Builder(LayoutHelper::String("n", 1, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); } else if (m_type == Type::SingleRecurrence) { m_definitionName = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], k_layoutFont), + CodePointLayout::Builder(name()[0], k_layoutFont), VerticalOffsetLayout::Builder(LayoutHelper::String("n+1", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); } else { assert(m_type == Type::DoubleRecurrence); m_definitionName = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], k_layoutFont), + CodePointLayout::Builder(name()[0], k_layoutFont), VerticalOffsetLayout::Builder(LayoutHelper::String("n+2", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); } } @@ -183,7 +183,7 @@ Poincare::Layout Sequence::firstInitialConditionName() { { Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); m_firstInitialConditionName = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], k_layoutFont), + CodePointLayout::Builder(name()[0], k_layoutFont), VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); } return m_firstInitialConditionName; @@ -196,7 +196,7 @@ Poincare::Layout Sequence::secondInitialConditionName() { if (m_type == Type::DoubleRecurrence) { Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); m_secondInitialConditionName = HorizontalLayout::Builder( - CharLayout::Builder(name()[0], k_layoutFont), + CodePointLayout::Builder(name()[0], k_layoutFont), VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); } } diff --git a/apps/sequence/sequence_title_cell.h b/apps/sequence/sequence_title_cell.h index 1f6b92c67..bcebbd4a9 100644 --- a/apps/sequence/sequence_title_cell.h +++ b/apps/sequence/sequence_title_cell.h @@ -15,7 +15,7 @@ public: void setColor(KDColor color) override; void setOrientation(Orientation orientation) override; const KDFont * font() const override { - return Poincare::CharLayoutNode::k_defaultFont; + return Poincare::CodePointLayoutNode::k_defaultFont; } Poincare::Layout layout() const override { return m_titleTextView.layout(); diff --git a/apps/settings/sub_menu/preferences_controller.cpp b/apps/settings/sub_menu/preferences_controller.cpp index bb96f02cf..65baa91eb 100644 --- a/apps/settings/sub_menu/preferences_controller.cpp +++ b/apps/settings/sub_menu/preferences_controller.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -84,7 +84,7 @@ Layout layoutForPreferences(I18n::Message message) { // Complex format case I18n::Message::Real: { - return CharLayout::Builder('x', KDFont::SmallFont); + return CodePointLayout::Builder('x', KDFont::SmallFont); } case I18n::Message::Cartesian: { diff --git a/apps/solver/list_controller.cpp b/apps/solver/list_controller.cpp index d46095f21..1f8fc7434 100644 --- a/apps/solver/list_controller.cpp +++ b/apps/solver/list_controller.cpp @@ -1,6 +1,6 @@ #include "list_controller.h" #include "app.h" -#include +#include #include using namespace Shared; @@ -124,7 +124,7 @@ bool textRepresentsAnEquality(const char * text) { bool layoutRepresentsAnEquality(Poincare::Layout l) { Poincare::Layout match = l.recursivelyMatches( [](Poincare::Layout layout) { - return layout.isChar() && static_cast(layout).character() == '='; }); + return layout.isCodePoint() && static_cast(layout).codePoint() == '='; }); return !match.isUninitialized(); } diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index f9b97405b..1f01562a7 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -75,7 +75,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt m_delta2Layout(), m_contentView(this) { - m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); + m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); char deltaB[] = {Ion::Charset::CapitalDelta, '=', 'b'}; static_cast(m_delta2Layout).addOrMergeChildAtIndex(LayoutHelper::String(deltaB, 3, KDFont::SmallFont), 0, false); for (int i = 0; i < EquationStore::k_maxNumberOfExactSolutions; i++) { diff --git a/poincare/Makefile b/poincare/Makefile index cc7f7e2df..d6a13b974 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -4,7 +4,6 @@ src += $(addprefix poincare/src/,\ binomial_coefficient_layout.cpp \ bracket_layout.cpp \ bracket_pair_layout.cpp \ - char_layout.cpp \ code_point_layout.cpp\ condensed_sum_layout.cpp \ conjugate_layout.cpp \ diff --git a/poincare/include/poincare/char_layout.h b/poincare/include/poincare/char_layout.h deleted file mode 100644 index c206d39b8..000000000 --- a/poincare/include/poincare/char_layout.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef POINCARE_CHAR_LAYOUT_NODE_H -#define POINCARE_CHAR_LAYOUT_NODE_H - -#include -#include -#include - -namespace Poincare { - -class CharLayoutNode final : public LayoutNode { -public: - static constexpr const KDFont * k_defaultFont = KDFont::LargeFont; - CharLayoutNode(char c = Ion::Charset::Empty, const KDFont * font = k_defaultFont) : - LayoutNode(), - m_char(c), - m_font(font) - {} - - // CharLayout - char character() const { return m_char; } - const KDFont * font() const { return m_font; } - - // LayoutNode - void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; - void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; - int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - bool isChar() const override { return true; } - bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const override; - bool canBeOmittedMultiplicationLeftFactor() const override; - bool canBeOmittedMultiplicationRightFactor() const override; - - // TreeNode - size_t size() const override { return sizeof(CharLayoutNode); } - int numberOfChildren() const override { return 0; } -#if POINCARE_TREE_LOG - virtual void logNodeName(std::ostream & stream) const override { - stream << "CharLayout"; - } - virtual void logAttributes(std::ostream & stream) const override { - stream << " char=\"" << m_char << "\""; - } -#endif - -protected: - // LayoutNode - KDSize computeSize() override; - KDCoordinate computeBaseline() override; - KDPoint positionOfChild(LayoutNode * child) override { - assert(false); - return KDPointZero; - } - -private: - void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; - bool isMultiplicationChar() const; - char m_char; - const KDFont * m_font; -}; - -class CharLayout final : public Layout { -public: - CharLayout(const CharLayoutNode * n) : Layout(n) {} - static CharLayout Builder(char c, const KDFont * font = KDFont::LargeFont); - const KDFont * font() const { return const_cast(this)->node()->font(); } - char character() const {return const_cast(this)->node()->character();} -private: - using Layout::node; - CharLayoutNode * node() { return static_cast(Layout::node());} -}; - -} - -#endif diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index bf70fba41..306b93355 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -49,7 +49,6 @@ public: bool isMatrix() const { return const_cast(this)->node()->isMatrix(); } bool isVerticalOffset() const { return const_cast(this)->node()->isVerticalOffset(); } bool isLeftParenthesis() const { return const_cast(this)->node()->isLeftParenthesis(); } - bool isChar() const { return const_cast(this)->node()->isChar(); } bool isCodePoint() const { return const_cast(this)->node()->isCodePoint(); } bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return const_cast(this)->node()->isCollapsable(numberOfOpenParenthesis, goingLeft); } int leftCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->leftCollapsingAbsorbingChildIndex(); } diff --git a/poincare/include/poincare/layout_cursor.h b/poincare/include/poincare/layout_cursor.h index 17f99143c..764023e04 100644 --- a/poincare/include/poincare/layout_cursor.h +++ b/poincare/include/poincare/layout_cursor.h @@ -109,7 +109,7 @@ public: void addEmptySquarePowerLayout(); void addEmptyTenPowerLayout(); void addFractionLayoutAndCollapseSiblings(); - void addXNTCharLayout(); + void addXNTCodePointLayout(); void insertText(const char * text); void addLayoutAndMoveCursor(Layout l); bool showEmptyLayoutIfNeeded() { return privateShowHideEmptyLayoutIfNeeded(true); } diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index c10120091..37c8899f9 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -84,14 +84,14 @@ public: virtual bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return true; } /* isCollapsable is used when adding a sibling fraction: should the layout be * inserted in the numerator (or denominator)? For instance, 1+2|3-4 should - * become 1+ 2/3 - 4 when pressing "Divide": a CharLayout is collapsable if + * become 1+ 2/3 - 4 when pressing "Divide": a CodePointLayout is collapsable if * its char is not +, -, or *. */ virtual bool canBeOmittedMultiplicationLeftFactor() const; virtual bool canBeOmittedMultiplicationRightFactor() const; /* canBeOmittedMultiplicationLeftFactor and RightFactor return true if the * layout, next to another layout, might be the factor of a multiplication * with an omitted multiplication sign. For instance, an absolute value layout - * returns true, because |3|2 means |3|*2. A '+' CharLayout returns false, + * returns true, because |3|2 means |3|*2. A '+' CodePointLayout returns false, * because +'something' nevers means +*'something'. */ virtual bool mustHaveLeftSibling() const { return false; } virtual bool isVerticalOffset() const { return false; } @@ -104,7 +104,6 @@ public: virtual bool isRightBracket() const { return false; } virtual bool isEmpty() const { return false; } virtual bool isMatrix() const { return false; } - virtual bool isChar() const { return false; } virtual bool isCodePoint() const { return false; } virtual bool hasUpperLeftIndex() const { return false; } virtual char XNTChar() const { diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index c2406302d..6d2a0de39 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -27,8 +27,8 @@ namespace SerializationHelper { const char * operatorName, bool writeFirstChild = true); - // Write one char in buffer - int Char(char * buffer, int bufferSize, char charToWrite); // TODO REMOVE + // Write one char in a buffer + int Char(char * buffer, int bufferSize, char c); // Write one code point in a buffer int CodePoint(char * buffer, int bufferSize, CodePoint c); }; diff --git a/poincare/include/poincare_layouts.h b/poincare/include/poincare_layouts.h index 3cf5ee953..ef9d39212 100644 --- a/poincare/include/poincare_layouts.h +++ b/poincare/include/poincare_layouts.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/poincare/src/char_layout.cpp b/poincare/src/char_layout.cpp deleted file mode 100644 index c938c06ff..000000000 --- a/poincare/src/char_layout.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include - -namespace Poincare { - -// LayoutNode -void CharLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) { - if (cursor->position() == LayoutCursor::Position::Right) { - cursor->setPosition(LayoutCursor::Position::Left); - return; - } - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); - } -} - -void CharLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) { - if (cursor->position() == LayoutCursor::Position::Left) { - cursor->setPosition(LayoutCursor::Position::Right); - return; - } - LayoutNode * parentNode = parent(); - if (parentNode != nullptr) { - parentNode->moveCursorRight(cursor, shouldRecomputeLayout); - } -} - -int CharLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return SerializationHelper::Char(buffer, bufferSize, m_char); -} - -bool CharLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { - if (*numberOfOpenParenthesis <= 0) { - if (m_char == '+' - || m_char == Ion::Charset::Sto - || m_char == '=' - || m_char == ',') - { - return false; - } - if (m_char == '-') { - /* If the expression is like 3E-200, we want '-' to be collapsable. - * Otherwise, '-' is not collapsable. */ - Layout thisRef = CharLayout(this); - Layout parent = thisRef.parent(); - if (!parent.isUninitialized()) { - int indexOfThis = parent.indexOfChild(thisRef); - if (indexOfThis > 0) { - Layout leftBrother = parent.childAtIndex(indexOfThis-1); - if (leftBrother.isChar() - && static_cast(leftBrother).character() == Ion::Charset::Exponent) - { - return true; - } - } - } - return false; - } - } - return true; -} - -bool CharLayoutNode::canBeOmittedMultiplicationLeftFactor() const { - if (isMultiplicationChar()) { - return false; - } - return LayoutNode::canBeOmittedMultiplicationRightFactor(); -} - -bool CharLayoutNode::canBeOmittedMultiplicationRightFactor() const { - if (m_char == '!' || isMultiplicationChar()) { - return false; - } - return LayoutNode::canBeOmittedMultiplicationRightFactor(); -} - -// Sizing and positioning -KDSize CharLayoutNode::computeSize() { - return m_font->glyphSize(); -} - -KDCoordinate CharLayoutNode::computeBaseline() { - return m_font->glyphSize().height()/2; -} - -void CharLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) { - char string[2] = {m_char, 0}; - ctx->drawString(string, p, m_font, expressionColor, backgroundColor); -} - -bool CharLayoutNode::isMultiplicationChar() const { - return m_char == '*' - || m_char == Ion::Charset::MultiplicationSign - || m_char == Ion::Charset::MiddleDot; -} - -CharLayout CharLayout::Builder(char c, const KDFont * font) { - void * bufferNode = TreePool::sharedPool()->alloc(sizeof(CharLayoutNode)); - CharLayoutNode * node = new (bufferNode) CharLayoutNode(c, font); - TreeHandle h = TreeHandle::BuildWithGhostChildren(node); - return static_cast(h); -} - -} diff --git a/poincare/src/constant.cpp b/poincare/src/constant.cpp index 6c4039c03..2f971598c 100644 --- a/poincare/src/constant.cpp +++ b/poincare/src/constant.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 0f8c758fe..338ac935f 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -27,7 +27,7 @@ Expression EqualNode::shallowReduce(Context & context, Preferences::ComplexForma Layout EqualNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(CharLayout::Builder('='), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(CodePointLayout::Builder('='), result.numberOfChildren(), result.numberOfChildren(), nullptr); result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false); return result; } diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index 643d24001..9c777a44a 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -62,7 +62,7 @@ Layout FactorialNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); int childrenCount = result.numberOfChildren(); - result.addChildAtIndex(CharLayout::Builder('!'), childrenCount, childrenCount, nullptr); + result.addChildAtIndex(CodePointLayout::Builder('!'), childrenCount, childrenCount, nullptr); return result; } diff --git a/poincare/src/integral_layout.cpp b/poincare/src/integral_layout.cpp index b53035fa6..907ee5c01 100644 --- a/poincare/src/integral_layout.cpp +++ b/poincare/src/integral_layout.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/poincare/src/layout.cpp b/poincare/src/layout.cpp index 103f5fa6d..d55906954 100644 --- a/poincare/src/layout.cpp +++ b/poincare/src/layout.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index 07b205b4c..a5f9346e5 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -75,7 +75,7 @@ void LayoutCursor::move(MoveDirection direction, bool * shouldRecomputeLayout) { void LayoutCursor::addEmptyExponentialLayout() { EmptyLayout emptyLayout = EmptyLayout::Builder(); HorizontalLayout sibling = HorizontalLayout::Builder( - CharLayout::Builder(Ion::Charset::Exponential), + CodePointLayout::Builder(Ion::Charset::Exponential), VerticalOffsetLayout::Builder(emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); m_layout.addSibling(this, sibling, false); m_layout = emptyLayout; @@ -107,16 +107,16 @@ void LayoutCursor::addEmptyPowerLayout() { } void LayoutCursor::addEmptySquarePowerLayout() { - VerticalOffsetLayout offsetLayout = VerticalOffsetLayout::Builder(CharLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript); + VerticalOffsetLayout offsetLayout = VerticalOffsetLayout::Builder(CodePointLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript); privateAddEmptyPowerLayout(offsetLayout); } void LayoutCursor::addEmptyTenPowerLayout() { EmptyLayout emptyLayout = EmptyLayout::Builder(); HorizontalLayout sibling = HorizontalLayout::Builder( - CharLayout::Builder(Ion::Charset::MiddleDot), - CharLayout::Builder('1'), - CharLayout::Builder('0'), + CodePointLayout::Builder(Ion::Charset::MiddleDot), + CodePointLayout::Builder('1'), + CodePointLayout::Builder('0'), VerticalOffsetLayout::Builder( emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); @@ -132,8 +132,8 @@ void LayoutCursor::addFractionLayoutAndCollapseSiblings() { Layout(newChild.node()).collapseSiblings(this); } -void LayoutCursor::addXNTCharLayout() { - m_layout.addSibling(this, CharLayout::Builder(m_layout.XNTChar()), true); +void LayoutCursor::addXNTCodePointLayout() { + m_layout.addSibling(this, CodePointLayout::Builder(m_layout.XNTChar()), true); } void LayoutCursor::insertText(const char * text) { @@ -148,7 +148,7 @@ void LayoutCursor::insertText(const char * text) { continue; } if (text[i] == Ion::Charset::MultiplicationSign) { - newChild = CharLayout::Builder(Ion::Charset::MiddleDot); + newChild = CodePointLayout::Builder(Ion::Charset::MiddleDot); } else if (text[i] == '(') { newChild = LeftParenthesisLayout::Builder(); if (pointedChild.isUninitialized()) { @@ -167,7 +167,7 @@ void LayoutCursor::insertText(const char * text) { } #endif else { - newChild = CharLayout::Builder(text[i]); + newChild = CodePointLayout::Builder(text[i]); } m_layout.addSibling(this, newChild, true); } diff --git a/poincare/src/layout_helper.cpp b/poincare/src/layout_helper.cpp index 7a61da0b8..d713ec1eb 100644 --- a/poincare/src/layout_helper.cpp +++ b/poincare/src/layout_helper.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ Layout LayoutHelper::Prefix(const Expression & expression, Preferences::PrintFlo if (numberOfChildren > 0) { args.addOrMergeChildAtIndex(expression.childAtIndex(0).createLayout(floatDisplayMode, numberOfSignificantDigits), 0, true); for (int i = 1; i < numberOfChildren; i++) { - args.addChildAtIndex(CharLayout::Builder(','), args.numberOfChildren(), args.numberOfChildren(), nullptr); + args.addChildAtIndex(CodePointLayout::Builder(','), args.numberOfChildren(), args.numberOfChildren(), nullptr); args.addOrMergeChildAtIndex(expression.childAtIndex(i).createLayout(floatDisplayMode, numberOfSignificantDigits), args.numberOfChildren(), true); } } @@ -57,7 +57,7 @@ HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const assert(bufferLen > 0); HorizontalLayout resultLayout = HorizontalLayout::Builder(); for (int i = 0; i < bufferLen; i++) { - resultLayout.addChildAtIndex(CharLayout::Builder(buffer[i], font), i, i, nullptr); + resultLayout.addChildAtIndex(CodePointLayout::Builder(buffer[i], font), i, i, nullptr); } return resultLayout; } diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp index ec4665c05..071f7b8c8 100644 --- a/poincare/src/opposite.cpp +++ b/poincare/src/opposite.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -41,7 +41,7 @@ bool OppositeNode::childNeedsParenthesis(const TreeNode * child) const { } Layout OppositeNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - HorizontalLayout result = HorizontalLayout::Builder(CharLayout::Builder('-')); + HorizontalLayout result = HorizontalLayout::Builder(CodePointLayout::Builder('-')); if (childAtIndex(0)->type() == Type::Opposite) { result.addOrMergeChildAtIndex(LayoutHelper::Parentheses(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), false), 1, false); } else { diff --git a/poincare/src/product_layout.cpp b/poincare/src/product_layout.cpp index 8be05d651..70abbfb8f 100644 --- a/poincare/src/product_layout.cpp +++ b/poincare/src/product_layout.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include namespace Poincare { diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp index fa86aba96..3379dec01 100644 --- a/poincare/src/rational.cpp +++ b/poincare/src/rational.cpp @@ -9,7 +9,7 @@ extern "C" { #include #include #include -#include +#include namespace Poincare { diff --git a/poincare/src/sequence_layout.cpp b/poincare/src/sequence_layout.cpp index b1c0e96eb..ed209b147 100644 --- a/poincare/src/sequence_layout.cpp +++ b/poincare/src/sequence_layout.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index c255bd160..f66c02ec6 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -137,15 +137,15 @@ int SerializationHelper::Prefix( return numberOfChar; } -int SerializationHelper::Char(char * buffer, int bufferSize, char charToWrite) { +int SerializationHelper::Char(char * buffer, int bufferSize, char c) { if (bufferSize == 0) { return -1; } - buffer[bufferSize-1] = 0; if (bufferSize == 1) { + buffer[0] = 0; return 0; } - buffer[0] = charToWrite; + buffer[0] = c; buffer[1] = 0; return 1; } diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index 0c085733d..76e646653 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ int StoreNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM Layout StoreNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(CharLayout::Builder(Ion::Charset::Sto), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(CodePointLayout::Builder(Ion::Charset::Sto), result.numberOfChildren(), result.numberOfChildren(), nullptr); result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false); return result; } diff --git a/poincare/src/sum_layout.cpp b/poincare/src/sum_layout.cpp index 9c9a079e8..1d7a60d74 100644 --- a/poincare/src/sum_layout.cpp +++ b/poincare/src/sum_layout.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include namespace Poincare { diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 0428218eb..de844615a 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -81,32 +81,32 @@ bool SymbolNode::isReal(Context & context) const { Layout SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { if (m_name[0] == Symbol::SpecialSymbols::UnknownX) { assert(m_name[1] == 0); - return CharLayout::Builder(Symbol::k_unknownXReadableChar); + return CodePointLayout::Builder(Symbol::k_unknownXReadableChar); } if (strcmp(m_name, "u(n)") == 0) { return HorizontalLayout::Builder( - CharLayout::Builder('u'), + CodePointLayout::Builder('u'), VerticalOffsetLayout::Builder( - CharLayout::Builder('n'), + CodePointLayout::Builder('n'), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "u(n+1)") == 0) { return HorizontalLayout::Builder( - CharLayout::Builder('u'), + CodePointLayout::Builder('u'), VerticalOffsetLayout::Builder( LayoutHelper::String("n+1", 3), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "v(n)") == 0) { return HorizontalLayout::Builder( - CharLayout::Builder('v'), + CodePointLayout::Builder('v'), VerticalOffsetLayout::Builder( - CharLayout::Builder('n'), + CodePointLayout::Builder('n'), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "v(n+1)") == 0) { return HorizontalLayout::Builder( - CharLayout::Builder('v'), + CodePointLayout::Builder('v'), VerticalOffsetLayout::Builder( LayoutHelper::String("n+1", 3), VerticalOffsetLayoutNode::Type::Subscript)); diff --git a/poincare/test/fraction_layout.cpp b/poincare/test/fraction_layout.cpp index 51417ed3f..e17ec7cf6 100644 --- a/poincare/test/fraction_layout.cpp +++ b/poincare/test/fraction_layout.cpp @@ -41,11 +41,11 @@ QUIZ_CASE(poincare_fraction_layout_delete) { * |3 * */ HorizontalLayout layout2 = HorizontalLayout::Builder( - CharLayout::Builder('1'), - CharLayout::Builder('+'), + CodePointLayout::Builder('1'), + CodePointLayout::Builder('+'), FractionLayout::Builder( EmptyLayout::Builder(), - CharLayout::Builder('3') + CodePointLayout::Builder('3') ) ); LayoutCursor cursor2(layout2.childAtIndex(2).childAtIndex(1), LayoutCursor::Position::Left); @@ -56,7 +56,7 @@ QUIZ_CASE(poincare_fraction_layout_delete) { QUIZ_CASE(poincare_fraction_layout_serialize) { FractionLayout layout = FractionLayout::Builder( - CharLayout::Builder('1'), + CodePointLayout::Builder('1'), LayoutHelper::String("2+3", 3) ); assert_expression_layout_serialize_to(layout, "(1)/(2+3)"); diff --git a/poincare/test/layouts.cpp b/poincare/test/layouts.cpp index c8ee32a97..7e7172748 100644 --- a/poincare/test/layouts.cpp +++ b/poincare/test/layouts.cpp @@ -44,7 +44,7 @@ void assert_parsed_layout_is(Layout l, Poincare::Expression r) { QUIZ_CASE(poincare_create_all_layouts) { EmptyLayout e0 = EmptyLayout::Builder(); AbsoluteValueLayout e1 = AbsoluteValueLayout::Builder(e0); - CharLayout e2 = CharLayout::Builder('a'); + CodePointLayout e2 = CodePointLayout::Builder('a'); BinomialCoefficientLayout e3 = BinomialCoefficientLayout::Builder(e1, e2); CeilingLayout e4 = CeilingLayout::Builder(e3); RightParenthesisLayout e5 = RightParenthesisLayout::Builder(); @@ -82,20 +82,20 @@ QUIZ_CASE(poincare_parse_layouts) { // 1+2 l = HorizontalLayout::Builder( - CharLayout::Builder('1'), - CharLayout::Builder('+'), - CharLayout::Builder('2')); + CodePointLayout::Builder('1'), + CodePointLayout::Builder('+'), + CodePointLayout::Builder('2')); e = Addition::Builder(Rational::Builder(1), Rational::Builder(2)); assert_parsed_layout_is(l, e); // |3+3/6| l = AbsoluteValueLayout:: Builder( HorizontalLayout::Builder( - CharLayout::Builder('3'), - CharLayout::Builder('+'), + CodePointLayout::Builder('3'), + CodePointLayout::Builder('+'), FractionLayout::Builder( - CharLayout::Builder('3'), - CharLayout::Builder('6')))); + CodePointLayout::Builder('3'), + CodePointLayout::Builder('6')))); e = AbsoluteValue::Builder( Addition::Builder( Rational::Builder(3), @@ -106,8 +106,8 @@ QUIZ_CASE(poincare_parse_layouts) { // binCoef(4,5) l = BinomialCoefficientLayout::Builder( - CharLayout::Builder('4'), - CharLayout::Builder('5')); + CodePointLayout::Builder('4'), + CodePointLayout::Builder('5')); e = BinomialCoefficient::Builder( Rational::Builder(4), Rational::Builder(5)); @@ -116,9 +116,9 @@ QUIZ_CASE(poincare_parse_layouts) { // ceil(4.6) l = CeilingLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('4'), - CharLayout::Builder('.'), - CharLayout::Builder('6'))); + CodePointLayout::Builder('4'), + CodePointLayout::Builder('.'), + CodePointLayout::Builder('6'))); e = Ceiling::Builder( Decimal::Builder(4.6)); assert_parsed_layout_is(l, e); @@ -126,21 +126,21 @@ QUIZ_CASE(poincare_parse_layouts) { // floor(7.2) l = FloorLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('7'), - CharLayout::Builder('.'), - CharLayout::Builder('2'))); + CodePointLayout::Builder('7'), + CodePointLayout::Builder('.'), + CodePointLayout::Builder('2'))); e = Floor::Builder( Decimal::Builder(7.2)); assert_parsed_layout_is(l, e); // 2^(3+4) l = HorizontalLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('3'), - CharLayout::Builder('+'), - CharLayout::Builder('4')), + CodePointLayout::Builder('3'), + CodePointLayout::Builder('+'), + CodePointLayout::Builder('4')), VerticalOffsetLayoutNode::Type::Superscript)); e = Power::Builder( Rational::Builder(2), @@ -151,12 +151,12 @@ QUIZ_CASE(poincare_parse_layouts) { // log_3(2) HorizontalLayout l1 = HorizontalLayout::Builder(); - l1.addChildAtIndex(CharLayout::Builder('l'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout::Builder('o'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout::Builder('g'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(VerticalOffsetLayout::Builder(CharLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Subscript), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CodePointLayout::Builder('l'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CodePointLayout::Builder('o'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CodePointLayout::Builder('g'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(VerticalOffsetLayout::Builder(CodePointLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Subscript), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); l1.addChildAtIndex(LeftParenthesisLayout::Builder(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout::Builder('2'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CodePointLayout::Builder('2'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); l1.addChildAtIndex(RightParenthesisLayout::Builder(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); l = l1; e = Logarithm::Builder( @@ -166,17 +166,17 @@ QUIZ_CASE(poincare_parse_layouts) { // root(5,3) l = NthRootLayout::Builder( - CharLayout::Builder('5'), - CharLayout::Builder('3')); + CodePointLayout::Builder('5'), + CodePointLayout::Builder('3')); e = NthRoot::Builder(Rational::Builder(5), Rational::Builder(3)); assert_parsed_layout_is(l, e); // int(7, x, 4, 5) l = IntegralLayout::Builder( - CharLayout::Builder('7'), - CharLayout::Builder('x'), - CharLayout::Builder('4'), - CharLayout::Builder('5')); + CodePointLayout::Builder('7'), + CodePointLayout::Builder('x'), + CodePointLayout::Builder('4'), + CodePointLayout::Builder('5')); e = Integral::Builder( Rational::Builder(7), Symbol::Builder('x'), @@ -186,11 +186,11 @@ QUIZ_CASE(poincare_parse_layouts) { // 2^2 ! l = HorizontalLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript), - CharLayout::Builder('!')); + CodePointLayout::Builder('!')); e = Factorial::Builder( Power::Builder( Rational::Builder(2), @@ -199,14 +199,14 @@ QUIZ_CASE(poincare_parse_layouts) { // 5* 6/(7+5) *3 l = HorizontalLayout::Builder( - CharLayout::Builder('5'), + CodePointLayout::Builder('5'), FractionLayout::Builder( - CharLayout::Builder('6'), + CodePointLayout::Builder('6'), HorizontalLayout::Builder( - CharLayout::Builder('7'), - CharLayout::Builder('+'), - CharLayout::Builder('5'))), - CharLayout::Builder('3')); + CodePointLayout::Builder('7'), + CodePointLayout::Builder('+'), + CodePointLayout::Builder('5'))), + CodePointLayout::Builder('3')); e = Multiplication::Builder( Rational::Builder(5), Division::Builder( @@ -220,14 +220,14 @@ QUIZ_CASE(poincare_parse_layouts) { // [[3^2!, 7][4,5] l = MatrixLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('3'), + CodePointLayout::Builder('3'), VerticalOffsetLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript), - CharLayout::Builder('!')), - CharLayout::Builder('7'), - CharLayout::Builder('4'), - CharLayout::Builder('5')); + CodePointLayout::Builder('!')), + CodePointLayout::Builder('7'), + CodePointLayout::Builder('4'), + CodePointLayout::Builder('5')); Matrix m = BuildOneChildMatrix( Factorial::Builder( Power::Builder( @@ -242,15 +242,15 @@ QUIZ_CASE(poincare_parse_layouts) { // 2^det([[3!, 7][4,5]) l = HorizontalLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayout::Builder( MatrixLayout::Builder( HorizontalLayout::Builder( - CharLayout::Builder('3'), - CharLayout::Builder('!')), - CharLayout::Builder('7'), - CharLayout::Builder('4'), - CharLayout::Builder('5')), + CodePointLayout::Builder('3'), + CodePointLayout::Builder('!')), + CodePointLayout::Builder('7'), + CodePointLayout::Builder('4'), + CodePointLayout::Builder('5')), VerticalOffsetLayoutNode::Type::Superscript)); m = BuildOneChildMatrix( Factorial::Builder( @@ -264,10 +264,10 @@ QUIZ_CASE(poincare_parse_layouts) { // 2e^3 l = HorizontalLayout::Builder( - CharLayout::Builder('2'), - CharLayout::Builder(Ion::Charset::Exponential), + CodePointLayout::Builder('2'), + CodePointLayout::Builder(Ion::Charset::Exponential), VerticalOffsetLayout::Builder( - CharLayout::Builder('3'), + CodePointLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Superscript)); e = Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3)))); assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3))))); diff --git a/poincare/test/parentheses_layout.cpp b/poincare/test/parentheses_layout.cpp index 1cb82c0d3..241ac6f02 100644 --- a/poincare/test/parentheses_layout.cpp +++ b/poincare/test/parentheses_layout.cpp @@ -16,16 +16,16 @@ QUIZ_CASE(poincare_parenthesis_layout_size) { LeftParenthesisLayout leftPar = LeftParenthesisLayout::Builder(); RightParenthesisLayout rightPar = RightParenthesisLayout::Builder(); layout.addChildAtIndex(leftPar, 0, 0, nullptr); - layout.addChildAtIndex(CharLayout::Builder('2'), 1, 1, nullptr); - layout.addChildAtIndex(CharLayout::Builder('+'), 2, 2, nullptr); + layout.addChildAtIndex(CodePointLayout::Builder('2'), 1, 1, nullptr); + layout.addChildAtIndex(CodePointLayout::Builder('+'), 2, 2, nullptr); layout.addChildAtIndex(LeftParenthesisLayout::Builder(), 3, 3, nullptr); layout.addChildAtIndex(FractionLayout::Builder( - CharLayout::Builder('3'), - CharLayout::Builder('4')), + CodePointLayout::Builder('3'), + CodePointLayout::Builder('4')), 4, 4, nullptr); layout.addChildAtIndex(RightParenthesisLayout::Builder(), 4, 4, nullptr); - layout.addChildAtIndex(CharLayout::Builder('6'), 5, 5, nullptr); + layout.addChildAtIndex(CodePointLayout::Builder('6'), 5, 5, nullptr); layout.addChildAtIndex(rightPar, 7, 7, nullptr); - layout.addChildAtIndex(CharLayout::Builder('1'), 8, 8, nullptr); + layout.addChildAtIndex(CodePointLayout::Builder('1'), 8, 8, nullptr); quiz_assert(leftPar.layoutSize().height() == rightPar.layoutSize().height()); } diff --git a/poincare/test/vertical_offset_layout.cpp b/poincare/test/vertical_offset_layout.cpp index 4c4e71750..72bc3ae09 100644 --- a/poincare/test/vertical_offset_layout.cpp +++ b/poincare/test/vertical_offset_layout.cpp @@ -8,7 +8,7 @@ using namespace Poincare; QUIZ_CASE(poincare_vertical_offset_layout_serialize) { HorizontalLayout layout = HorizontalLayout::Builder( - CharLayout::Builder('2'), + CodePointLayout::Builder('2'), VerticalOffsetLayout::Builder( LayoutHelper::String("x+5", 3), VerticalOffsetLayoutNode::Type::Superscript From 65e5adafac519f6f35b4725149e998eb61acfd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 11 Jan 2019 17:37:23 +0100 Subject: [PATCH 023/295] Remove Ion::Charset --- apps/calculation/test/calculation_store.cpp | 13 +- apps/code/helpers.cpp | 15 +-- apps/code/script_node_cell.h | 5 +- .../graph/graph/integral_graph_controller.cpp | 2 +- apps/regression/graph_controller.cpp | 4 +- apps/regression/model/cubic_model.cpp | 6 +- apps/regression/model/exponential_model.cpp | 4 +- apps/regression/model/linear_model.cpp | 2 +- apps/regression/model/logarithmic_model.cpp | 2 +- apps/regression/model/logistic_model.cpp | 4 +- apps/regression/model/power_model.cpp | 2 +- apps/regression/model/quadratic_model.cpp | 4 +- apps/regression/model/quartic_model.cpp | 8 +- apps/regression/model/trigonometric_model.cpp | 4 +- apps/sequence/graph/term_sum_controller.cpp | 2 +- .../sub_menu/preferences_controller.cpp | 22 ++-- apps/shared/storage_sum_graph_controller.cpp | 15 +-- apps/shared/storage_sum_graph_controller.h | 6 +- apps/shared/sum_graph_controller.cpp | 15 +-- apps/shared/sum_graph_controller.h | 6 +- apps/shared/text_field_delegate_app.cpp | 10 +- apps/shared/toolbox_helpers.cpp | 4 +- apps/solver/equation.cpp | 2 +- apps/solver/equation_store.cpp | 2 +- apps/solver/solutions_controller.cpp | 2 +- apps/solver/test/equation_store.cpp | 14 +-- apps/variable_box_controller.cpp | 2 +- apps/variable_box_empty_controller.cpp | 8 +- escher/include/escher/editable_field.h | 3 +- escher/include/escher/layout_field.h | 2 +- escher/include/escher/text_field.h | 2 +- escher/src/layout_field.cpp | 8 +- escher/src/text_area.cpp | 3 +- escher/src/text_field.cpp | 12 +- escher/src/text_input_helpers.cpp | 6 +- ion/include/ion.h | 1 - ion/include/ion/charset.h | 37 ------ ion/src/shared/events.cpp | 34 ++---- .../include/kandinsky/unicode/code_point.h | 44 +++++-- .../include/kandinsky/unicode/utf8_decoder.h | 1 + kandinsky/src/unicode/utf8_decoder.cpp | 8 +- poincare/include/poincare/code_point_layout.h | 2 +- poincare/include/poincare/constant.h | 14 ++- poincare/include/poincare/integral_layout.h | 2 +- poincare/include/poincare/layout.h | 2 +- poincare/include/poincare/layout_helper.h | 1 + poincare/include/poincare/layout_node.h | 5 +- poincare/include/poincare/print_float.h | 2 +- poincare/include/poincare/sequence_layout.h | 2 +- .../include/poincare/serialization_helper.h | 1 + poincare/include/poincare/square_root.h | 8 +- poincare/include/poincare/symbol.h | 2 +- .../poincare/trigonometry_cheat_table.h | 2 +- poincare/src/complex_argument.cpp | 2 +- poincare/src/complex_cartesian.cpp | 6 +- poincare/src/constant.cpp | 40 +++++-- poincare/src/decimal.cpp | 4 +- poincare/src/empty_expression.cpp | 3 +- poincare/src/expression.cpp | 17 +-- poincare/src/expression_debug.cpp | 10 +- poincare/src/fraction_layout.cpp | 5 +- poincare/src/layout_cursor.cpp | 18 +-- poincare/src/layout_helper.cpp | 10 ++ poincare/src/logarithm.cpp | 2 +- poincare/src/multiplication.cpp | 12 +- poincare/src/naperian_logarithm.cpp | 2 +- poincare/src/nth_root_layout.cpp | 3 +- poincare/src/parsing/tokenizer.cpp | 112 +++++++++--------- poincare/src/parsing/tokenizer.h | 10 +- poincare/src/power.cpp | 12 +- poincare/src/print_float.cpp | 36 +++--- poincare/src/serialization_helper.cpp | 9 +- poincare/src/store.cpp | 7 +- poincare/src/trigonometry.cpp | 46 +++---- poincare/src/trigonometry_cheat_table.cpp | 74 ++++++------ poincare/src/vertical_offset_layout.cpp | 7 +- poincare/test/convert_expression_to_text.cpp | 2 - poincare/test/expression_order.cpp | 14 +-- poincare/test/float.cpp | 1 - poincare/test/helper.cpp | 37 ++---- poincare/test/helper.h | 2 - poincare/test/layouts.cpp | 6 +- poincare/test/parser.cpp | 25 ++-- 83 files changed, 453 insertions(+), 470 deletions(-) delete mode 100644 ion/include/ion/charset.h diff --git a/apps/calculation/test/calculation_store.cpp b/apps/calculation/test/calculation_store.cpp index 151d6254a..c64fdd9ac 100644 --- a/apps/calculation/test/calculation_store.cpp +++ b/apps/calculation/test/calculation_store.cpp @@ -70,10 +70,7 @@ QUIZ_CASE(calculation_ans) { } void assertCalculationDisplay(const char * input, bool displayExactOutput, bool displayApproximateOutput, ::Calculation::Calculation::EqualSign sign, const char * exactOutput, const char * approximateOutput, Context * context, CalculationStore * store) { - char buffer[500]; - strlcpy(buffer, input, sizeof(buffer)); - translate_in_special_chars(buffer); - store->push(buffer, context); + store->push(input, context); ::Calculation::Calculation * lastCalculation = store->calculationAtIndex(1); quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == displayExactOutput); quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(context) == displayApproximateOutput); @@ -81,14 +78,10 @@ void assertCalculationDisplay(const char * input, bool displayExactOutput, bool quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(context) == sign); } if (exactOutput) { - strlcpy(buffer, exactOutput, sizeof(buffer)); - translate_in_special_chars(buffer); - quiz_assert(strcmp(lastCalculation->exactOutputText(), buffer) == 0); + quiz_assert(strcmp(lastCalculation->exactOutputText(), exactOutput) == 0); } if (approximateOutput) { - strlcpy(buffer, approximateOutput, sizeof(buffer)); - translate_in_special_chars(buffer); - quiz_assert(strcmp(lastCalculation->approximateOutputText(),buffer) == 0); + quiz_assert(strcmp(lastCalculation->approximateOutputText(), approximateOutput) == 0); } store->deleteAll(); } diff --git a/apps/code/helpers.cpp b/apps/code/helpers.cpp index 12582729c..8a9735de4 100644 --- a/apps/code/helpers.cpp +++ b/apps/code/helpers.cpp @@ -1,5 +1,6 @@ #include "helpers.h" #include +#include #include namespace Code { @@ -15,20 +16,16 @@ private: const char * m_text; }; -static constexpr const char k_exponential[7] = {'e', 'x', 'p', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_logarithm[7] = {'l', 'o', 'g', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_logarithm10[9] = {'l', 'o', 'g', '1', '0', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_sqrt[8] = {'s', 'q', 'r', 't', '(', Ion::Charset::Empty, ')', 0}; - +static_assert('\x11' == KDCodePointEmpty, "Unicode error"); static constexpr EventTextPair sEventTextMap[] = { EventTextPair(Ion::Events::XNT, "x"), - EventTextPair(Ion::Events::Exp, k_exponential), - EventTextPair(Ion::Events::Ln, k_logarithm), - EventTextPair(Ion::Events::Log, k_logarithm10), + EventTextPair(Ion::Events::Exp, "exp(\x11)"), + EventTextPair(Ion::Events::Ln, "log(\x11)"), + EventTextPair(Ion::Events::Log, "log10(\x11)"), EventTextPair(Ion::Events::Imaginary, "1j"), EventTextPair(Ion::Events::Power, "**"), EventTextPair(Ion::Events::Pi, "pi"), - EventTextPair(Ion::Events::Sqrt, k_sqrt), + EventTextPair(Ion::Events::Sqrt, "sqrt(\x11)"), EventTextPair(Ion::Events::Square, "**2"), EventTextPair(Ion::Events::Multiplication, "*"), EventTextPair(Ion::Events::EE, "e"), diff --git a/apps/code/script_node_cell.h b/apps/code/script_node_cell.h index ae1c252ba..29e9f8bbf 100644 --- a/apps/code/script_node_cell.h +++ b/apps/code/script_node_cell.h @@ -4,7 +4,6 @@ #include "script_node.h" #include "script_store.h" #include -#include #include namespace Code { @@ -23,8 +22,10 @@ public: void reloadCell() override; const char * text() const override { return m_scriptNodeView.text(); } + static_assert('\x11' == KDCodePointEmpty, "Unicode error"); constexpr static char k_parentheses[] = "()"; - constexpr static char k_parenthesesWithEmpty[] = {'(', Ion::Charset::Empty, ')', 0}; + constexpr static char k_parenthesesWithEmpty[] = "(\x11)"; + protected: class ScriptNodeView : public HighlightCell { public: diff --git a/apps/graph/graph/integral_graph_controller.cpp b/apps/graph/graph/integral_graph_controller.cpp index a2ff51402..6dc3fe6bd 100644 --- a/apps/graph/graph/integral_graph_controller.cpp +++ b/apps/graph/graph/integral_graph_controller.cpp @@ -13,7 +13,7 @@ using namespace Poincare; namespace Graph { IntegralGraphController::IntegralGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, GraphView * graphView, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor) : - StorageSumGraphController(parentResponder, inputEventHandlerDelegate, graphView, graphRange, cursor, Ion::Charset::Integral) + StorageSumGraphController(parentResponder, inputEventHandlerDelegate, graphView, graphRange, cursor, KDCodePointIntegral) { } diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index be3298f72..3d8a8a807 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -121,7 +121,7 @@ void GraphController::reloadBannerView() { double x = m_cursor->x(); // Display a specific legend if the mean dot is selected if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { - constexpr static char legX[] = {Ion::Charset::XBar, '=', 0}; + constexpr static char legX[] = {'X'/*TODO LEA Ion::Charset::XBar*/, '=', 0}; legend = legX; x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0); } @@ -138,7 +138,7 @@ void GraphController::reloadBannerView() { legend = "y="; double y = m_cursor->y(); if (*m_selectedDotIndex == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { - constexpr static char legY[] = {Ion::Charset::YBar, '=', 0}; + constexpr static char legY[] = {'Y' /*TODO LEA Ion::Charset::YBar*/, '=', 0}; legend = legY; y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1); } diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp index 39cf2139b..e8bbc5734 100644 --- a/apps/regression/model/cubic_model.cpp +++ b/apps/regression/model/cubic_model.cpp @@ -21,7 +21,7 @@ Layout CubicModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('3', KDFont::SmallFont), @@ -29,7 +29,7 @@ Layout CubicModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('2', KDFont::SmallFont), @@ -37,7 +37,7 @@ Layout CubicModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('c', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('d', KDFont::SmallFont), diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp index 4b2c3fa5d..a5f5b937f 100644 --- a/apps/regression/model/exponential_model.cpp +++ b/apps/regression/model/exponential_model.cpp @@ -13,12 +13,12 @@ Layout ExponentialModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('e', KDFont::SmallFont), VerticalOffsetLayout::Builder( HorizontalLayout::Builder( CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont) ), VerticalOffsetLayoutNode::Type::Superscript diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 116126fc5..15b811181 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -13,7 +13,7 @@ Layout LinearModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp index 255ef5268..c0402bac9 100644 --- a/apps/regression/model/logarithmic_model.cpp +++ b/apps/regression/model/logarithmic_model.cpp @@ -13,7 +13,7 @@ Layout LogarithmicModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('l', KDFont::SmallFont), CodePointLayout::Builder('n', KDFont::SmallFont), CodePointLayout::Builder('(', KDFont::SmallFont), diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp index 5f637e798..f7744f916 100644 --- a/apps/regression/model/logistic_model.cpp +++ b/apps/regression/model/logistic_model.cpp @@ -15,14 +15,14 @@ Layout LogisticModel::layout() { Layout exponentLayoutChildren[] = { CodePointLayout::Builder('-', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont) }; Layout layoutChildren[] = { CodePointLayout::Builder('1', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('e', KDFont::SmallFont), VerticalOffsetLayout::Builder( HorizontalLayout::Builder(exponentLayoutChildren, 4), diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp index 7a5dcc992..2241eb4fa 100644 --- a/apps/regression/model/power_model.cpp +++ b/apps/regression/model/power_model.cpp @@ -14,7 +14,7 @@ Layout PowerModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('b', KDFont::SmallFont), diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp index 16d9fd6c9..feaf176e6 100644 --- a/apps/regression/model/quadratic_model.cpp +++ b/apps/regression/model/quadratic_model.cpp @@ -21,7 +21,7 @@ Layout QuadraticModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('2', KDFont::SmallFont), @@ -29,7 +29,7 @@ Layout QuadraticModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('c', KDFont::SmallFont), diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp index 163880fae..710bb970a 100644 --- a/apps/regression/model/quartic_model.cpp +++ b/apps/regression/model/quartic_model.cpp @@ -21,7 +21,7 @@ Layout QuarticModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('4', KDFont::SmallFont), @@ -29,7 +29,7 @@ Layout QuarticModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('3', KDFont::SmallFont), @@ -37,7 +37,7 @@ Layout QuarticModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('c', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), VerticalOffsetLayout::Builder( CodePointLayout::Builder('2', KDFont::SmallFont), @@ -45,7 +45,7 @@ Layout QuarticModel::layout() { ), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('d', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('e', KDFont::SmallFont), diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp index 8fceb306f..46ba81386 100644 --- a/apps/regression/model/trigonometric_model.cpp +++ b/apps/regression/model/trigonometric_model.cpp @@ -22,13 +22,13 @@ Layout TrigonometricModel::layout() { if (m_layout.isUninitialized()) { Layout layoutChildren[] = { CodePointLayout::Builder('a', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('s', KDFont::SmallFont), CodePointLayout::Builder('i', KDFont::SmallFont), CodePointLayout::Builder('n', KDFont::SmallFont), CodePointLayout::Builder('(', KDFont::SmallFont), CodePointLayout::Builder('b', KDFont::SmallFont), - CodePointLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CodePointLayout::Builder(KDCodePointMiddleDot, KDFont::SmallFont), CodePointLayout::Builder('X', KDFont::SmallFont), CodePointLayout::Builder('+', KDFont::SmallFont), CodePointLayout::Builder('c', KDFont::SmallFont), diff --git a/apps/sequence/graph/term_sum_controller.cpp b/apps/sequence/graph/term_sum_controller.cpp index 17b59c943..aa636e422 100644 --- a/apps/sequence/graph/term_sum_controller.cpp +++ b/apps/sequence/graph/term_sum_controller.cpp @@ -18,7 +18,7 @@ using namespace Poincare; namespace Sequence { TermSumController::TermSumController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, GraphView * graphView, CurveViewRange * graphRange, CurveViewCursor * cursor) : - SumGraphController(parentResponder, inputEventHandlerDelegate, graphView, graphRange, cursor, Ion::Charset::CapitalSigma) + SumGraphController(parentResponder, inputEventHandlerDelegate, graphView, graphRange, cursor, KDCodePointNArySummation) { } diff --git a/apps/settings/sub_menu/preferences_controller.cpp b/apps/settings/sub_menu/preferences_controller.cpp index 65baa91eb..a20c3b45f 100644 --- a/apps/settings/sub_menu/preferences_controller.cpp +++ b/apps/settings/sub_menu/preferences_controller.cpp @@ -54,12 +54,12 @@ Layout layoutForPreferences(I18n::Message message) { // Angle Unit case I18n::Message::Degres: { - const char degEx[] = {'9', '0', Ion::Charset::Degree}; - return LayoutHelper::String(degEx, sizeof(degEx), KDFont::SmallFont); + const char * degEx = "90°"; + return LayoutHelper::String("90°", strlen(degEx), KDFont::SmallFont); } case I18n::Message::Radian: { - const char pi[] = {Ion::Charset::SmallPi}; + const char * pi = "π"; return FractionLayout::Builder( LayoutHelper::String(pi, sizeof(pi), KDFont::SmallFont), LayoutHelper::String("2", 1, KDFont::SmallFont) @@ -70,8 +70,8 @@ Layout layoutForPreferences(I18n::Message message) { return LayoutHelper::String("12.34", 5, KDFont::SmallFont); case I18n::Message::Scientific: { - const char text[] = {'1','.', '2', '3', '4', Ion::Charset::Exponent, '1'}; - return LayoutHelper::String(text, sizeof(text), KDFont::SmallFont); + const char * text = "1.234ᴇ1"; + return LayoutHelper::String(text, strlen(text), KDFont::SmallFont); } // Edition mode case I18n::Message::Edition2D: @@ -88,16 +88,16 @@ Layout layoutForPreferences(I18n::Message message) { } case I18n::Message::Cartesian: { - const char text[] = {'a','+', Ion::Charset::IComplex, 'b'}; - return LayoutHelper::String(text, sizeof(text), KDFont::SmallFont); + const char * text = "a+𝐢b"; + return LayoutHelper::String(text, strlen(text), KDFont::SmallFont); } case I18n::Message::Polar: { - const char base[] = {'r', Ion::Charset::Exponential}; - const char superscript[] = {Ion::Charset::IComplex, Ion::Charset::SmallTheta}; + const char * base = "rℯ"; + const char * superscript = "𝐢θ"; return HorizontalLayout::Builder( - LayoutHelper::String(base, sizeof(base), KDFont::SmallFont), - VerticalOffsetLayout::Builder(LayoutHelper::String(superscript, sizeof(superscript), KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript) + LayoutHelper::String(base, strlen(base), KDFont::SmallFont), + VerticalOffsetLayout::Builder(LayoutHelper::String(superscript, strlen(superscript), KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript) ); } default: diff --git a/apps/shared/storage_sum_graph_controller.cpp b/apps/shared/storage_sum_graph_controller.cpp index bc164fb5a..7582995f6 100644 --- a/apps/shared/storage_sum_graph_controller.cpp +++ b/apps/shared/storage_sum_graph_controller.cpp @@ -13,7 +13,7 @@ using namespace Poincare; namespace Shared { -StorageSumGraphController::StorageSumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageFunctionGraphView * graphView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol) : +StorageSumGraphController::StorageSumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageFunctionGraphView * graphView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, CodePoint sumSymbol) : SimpleInteractiveCurveViewController(parentResponder, range, graphView, cursor), m_step(Step::FirstParameter), m_startSum(NAN), @@ -203,7 +203,7 @@ bool StorageSumGraphController::handleEnter() { /* Legend View */ -StorageSumGraphController::LegendView::LegendView(StorageSumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, char sumSymbol) : +StorageSumGraphController::LegendView::LegendView(StorageSumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol) : m_sum(0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), m_sumLayout(), m_legend(k_font, I18n::Message::Default, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), @@ -234,25 +234,26 @@ void StorageSumGraphController::LegendView::setEditableZone(double d) { void StorageSumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) { assert(step == Step::Result || functionLayout.isUninitialized()); - const char sigma[] = {' ', m_sumSymbol}; + constexpr int sigmaLength = 2; + const CodePoint sigma[sigmaLength] = {KDCodePointSpace, m_sumSymbol}; if (step == Step::FirstParameter) { - m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); + m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength); } else if (step == Step::SecondParameter) { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::String(sigma, sizeof(sigma)), + LayoutHelper::CodePointString(sigma, sizeof(sigma)), LayoutHelper::String(buffer, strlen(buffer), k_font), EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false)); } else { - m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); + m_sumLayout = LayoutHelper::CodePointString(sigma, sizeof(sigma)); char buffer[2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); Layout start = LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont); PrintFloat::convertFloatToText(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); Layout end = LayoutHelper::String(buffer, strlen(buffer), k_font); m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::String(sigma, sizeof(sigma)), + LayoutHelper::CodePointString(sigma, sizeof(sigma)), start, end); strlcpy(buffer, "= ", 3); diff --git a/apps/shared/storage_sum_graph_controller.h b/apps/shared/storage_sum_graph_controller.h index 0f5189133..7b9aceecc 100644 --- a/apps/shared/storage_sum_graph_controller.h +++ b/apps/shared/storage_sum_graph_controller.h @@ -15,7 +15,7 @@ namespace Shared { class StorageSumGraphController : public SimpleInteractiveCurveViewController, public TextFieldDelegate { public: - StorageSumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageFunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol); + StorageSumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageFunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, CodePoint sumSymbol); void viewWillAppear() override; void didEnterResponderChain(Responder * previousFirstResponder) override; bool handleEvent(Ion::Events::Event event) override; @@ -49,7 +49,7 @@ private: bool handleEnter() override; class LegendView : public View { public: - LegendView(StorageSumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, char sumSymbol); + LegendView(StorageSumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol); LegendView(const LegendView& other) = delete; LegendView(LegendView&& other) = delete; LegendView& operator=(const LegendView& other) = delete; @@ -76,7 +76,7 @@ private: MessageTextView m_legend; TextField m_editableZone; char m_draftText[TextField::maxBufferSize()]; - char m_sumSymbol; + CodePoint m_sumSymbol; }; StorageFunctionGraphView * m_graphView; LegendView m_legendView; diff --git a/apps/shared/sum_graph_controller.cpp b/apps/shared/sum_graph_controller.cpp index ee03dbc2b..e45984d5b 100644 --- a/apps/shared/sum_graph_controller.cpp +++ b/apps/shared/sum_graph_controller.cpp @@ -13,7 +13,7 @@ using namespace Poincare; namespace Shared { -SumGraphController::SumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, FunctionGraphView * graphView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol) : +SumGraphController::SumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, FunctionGraphView * graphView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, CodePoint sumSymbol) : SimpleInteractiveCurveViewController(parentResponder, range, graphView, cursor), m_step(Step::FirstParameter), m_startSum(NAN), @@ -202,7 +202,7 @@ bool SumGraphController::handleEnter() { /* Legend View */ -SumGraphController::LegendView::LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, char sumSymbol) : +SumGraphController::LegendView::LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol) : m_sum(0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), m_sumLayout(), m_legend(KDFont::SmallFont, I18n::Message::Default, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle), @@ -233,18 +233,19 @@ void SumGraphController::LegendView::setEditableZone(double d) { void SumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) { assert(step == Step::Result || functionLayout.isUninitialized()); - const char sigma[] = {' ', m_sumSymbol}; + constexpr int sigmaSize = 2; + const CodePoint sigma[sigmaSize] = {KDCodePointSpace, m_sumSymbol}; if (step == Step::FirstParameter) { - m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); + m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaSize); } else if (step == Step::SecondParameter) { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::String(sigma, sizeof(sigma)), + LayoutHelper::CodePointString(sigma, sizeof(sigma)), LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont), EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, KDFont::SmallFont, false)); } else { - m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); + m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaSize); constexpr size_t bufferSize = 2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits); char buffer[bufferSize]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); @@ -252,7 +253,7 @@ void SumGraphController::LegendView::setSumSymbol(Step step, double start, doubl PrintFloat::convertFloatToText(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); Layout end = LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont); m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::String(sigma, sizeof(sigma)), + LayoutHelper::CodePointString(sigma, sizeof(sigma)), start, end); strlcpy(buffer, "= ", bufferSize); diff --git a/apps/shared/sum_graph_controller.h b/apps/shared/sum_graph_controller.h index 9def6e084..595f9f4bc 100644 --- a/apps/shared/sum_graph_controller.h +++ b/apps/shared/sum_graph_controller.h @@ -14,7 +14,7 @@ namespace Shared { class SumGraphController : public SimpleInteractiveCurveViewController, public TextFieldDelegate { public: - SumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, FunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol); + SumGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, FunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, CodePoint sumSymbol); void viewWillAppear() override; void didEnterResponderChain(Responder * previousFirstResponder) override; bool handleEvent(Ion::Events::Event event) override; @@ -48,7 +48,7 @@ private: bool handleEnter() override; class LegendView : public View { public: - LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, char sumSymbol); + LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol); LegendView(const LegendView& other) = delete; LegendView(LegendView&& other) = delete; LegendView& operator=(const LegendView& other) = delete; @@ -72,7 +72,7 @@ private: MessageTextView m_legend; TextField m_editableZone; char m_draftText[TextField::maxBufferSize()]; - char m_sumSymbol; + CodePoint m_sumSymbol; }; FunctionGraphView * m_graphView; LegendView m_legendView; diff --git a/apps/shared/text_field_delegate_app.cpp b/apps/shared/text_field_delegate_app.cpp index b9062c352..94aacf698 100644 --- a/apps/shared/text_field_delegate_app.cpp +++ b/apps/shared/text_field_delegate_app.cpp @@ -1,4 +1,5 @@ #include "text_field_delegate_app.h" +#include #include #include #include @@ -53,8 +54,13 @@ bool TextFieldDelegateApp::fieldDidReceiveEvent(EditableField * field, Responder if (!field->isEditing()) { field->setEditing(true); } - const char xnt[2] = {field->XNTChar(XNT()), 0}; - return field->handleEventWithText(xnt); + /* TODO decode here to encode again in handleEventWithText? */ + constexpr int bufferSize = CodePoint::MaxCodePointCharLength+1; + char buffer[bufferSize]; + size_t length = UTF8Decoder::CodePointToChars(XNT(), buffer, bufferSize); + assert(length < bufferSize - 1); + buffer[length] = 0; + return field->handleEventWithText(buffer); } return false; } diff --git a/apps/shared/toolbox_helpers.cpp b/apps/shared/toolbox_helpers.cpp index 0a0e50035..99c89cd3b 100644 --- a/apps/shared/toolbox_helpers.cpp +++ b/apps/shared/toolbox_helpers.cpp @@ -1,5 +1,4 @@ #include "toolbox_helpers.h" -#include #include #include #include @@ -8,6 +7,7 @@ namespace Shared { namespace ToolboxHelpers { int CursorIndexInCommandText(const char * text) { + // TODO LEA size_t textLength = strlen(text); for (size_t i = 0; i < textLength; i++) { if (text[i] == '(' || text[i] == '\'') { @@ -49,7 +49,7 @@ void TextToInsertForCommandText(const char * command, char * buffer, int bufferS buffer[currentNewTextIndex++] = command[i]; } else { if (replaceArgsWithEmptyChar && !argumentAlreadyReplaced) { - buffer[currentNewTextIndex++] = Ion::Charset::Empty; + // TODO LEA buffer[currentNewTextIndex++] = Ion::Charset::Empty; argumentAlreadyReplaced = true; } } diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp index 15c9e1578..04edc3eb8 100644 --- a/apps/solver/equation.cpp +++ b/apps/solver/equation.cpp @@ -50,7 +50,7 @@ Expression Equation::standardForm(Context * context) const { } bool Equation::containsIComplex() const { - return strchr(text(), Ion::Charset::IComplex) != nullptr; + return false; //TODO LEA strchr(text(), KDCodePointMathematicalBoldSmallI) != nullptr; } void Equation::tidyStandardForm() { diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index b81de3552..c629302a3 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -325,7 +325,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact // C = Root((delta1+sqrt(-27a^2*delta))/2, 3) Expression * mult11Operands[3] = {new Rational::Builder(-27), new Power::Builder(a->clone(), new Rational::Builder(2), false), (*delta)->clone()}; Expression * c = new Power::Builder(new Division::Builder(new Addition(delta1, new SquareRoot(new Multiplication::Builder(mult11Operands, 3, false), false), false), new Rational::Builder(2), false), new Rational::Builder(1,3), false); - Expression * unary3roots[2] = {new Addition(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(Ion::Charset::IComplex), false), new Rational::Builder(2), false), false), new Subtraction::Builder(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(Ion::Charset::IComplex), false), new Rational::Builder(2), false), false)}; + Expression * unary3roots[2] = {new Addition(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(KDCodePointMathematicalBoldSmallI), false), new Rational::Builder(2), false), false), new Subtraction::Builder(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(KDCodePointMathematicalBoldSmallI), false), new Rational::Builder(2), false), false)}; // x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root for (int k = 0; k < 3; k++) { Expression * ccopy = c; diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index 1f01562a7..c3b9ae56a 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -76,7 +76,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt m_contentView(this) { m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); - char deltaB[] = {Ion::Charset::CapitalDelta, '=', 'b'}; + const char * deltaB = "Δ=b"; static_cast(m_delta2Layout).addOrMergeChildAtIndex(LayoutHelper::String(deltaB, 3, KDFont::SmallFont), 0, false); for (int i = 0; i < EquationStore::k_maxNumberOfExactSolutions; i++) { m_exactValueCells[i].setParentResponder(m_contentView.selectableTableView()); diff --git a/apps/solver/test/equation_store.cpp b/apps/solver/test/equation_store.cpp index fd2e3e9c5..2053defb6 100644 --- a/apps/solver/test/equation_store.cpp +++ b/apps/solver/test/equation_store.cpp @@ -12,15 +12,12 @@ using namespace Poincare; namespace Solver { void assert_equation_system_exact_solve_to(const char * equations[], EquationStore::Error error, EquationStore::Type type, const char * variables[], const char * solutions[], int numberOfSolutions) { - char buffer[200]; Shared::GlobalContext globalContext; EquationStore equationStore; int index = 0; while (equations[index] != 0) { Shared::ExpressionModel * e = equationStore.addEmptyModel(); - strlcpy(buffer, equations[index++], 200); - translate_in_special_chars(buffer); - e->setContent(buffer); + e->setContent(equations[index++]); } EquationStore::Error err = equationStore.exactSolve(&globalContext); quiz_assert(err == error); @@ -40,20 +37,15 @@ void assert_equation_system_exact_solve_to(const char * equations[], EquationSto quiz_assert(strcmp(equationStore.variableAtIndex(0), variables[0]) == 0); } for (int i = 0; i < numberOfSolutions; i++) { - equationStore.exactSolutionLayoutAtIndex(i, true).serializeForParsing(buffer, 200); - translate_in_ASCII_chars(buffer); - quiz_assert(strcmp(buffer, solutions[i]) == 0); + quiz_assert(strcmp(equationStore.exactSolutionLayoutAtIndex(i, true), solutions[i]) == 0); } } void assert_equation_approximate_solve_to(const char * equations, double xMin, double xMax, const char * variable, double solutions[], int numberOfSolutions, bool hasMoreSolutions) { - char buffer[200]; Shared::GlobalContext globalContext; EquationStore equationStore; Shared::ExpressionModel * e = equationStore.addEmptyModel(); - strlcpy(buffer, equations, 200); - translate_in_special_chars(buffer); - e->setContent(buffer); + e->setContent(equations); EquationStore::Error err = equationStore.exactSolve(&globalContext); quiz_assert(err == EquationStore::Error::RequireApproximateSolution); equationStore.setIntervalBound(0, xMin); diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index adc6f61bd..54a6367c4 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -199,7 +199,7 @@ bool VariableBoxController::selectLeaf(int selectedRow) { assert(nameLength < nameToHandleMaxSize); nameToHandle[nameLength++] = '('; assert(nameLength < nameToHandleMaxSize); - nameToHandle[nameLength++] = Ion::Charset::Empty; + // TODO LEA nameToHandle[nameLength++] = Ion::Charset::Empty; assert(nameLength < nameToHandleMaxSize); nameToHandle[nameLength++] = ')'; assert(nameLength < nameToHandleMaxSize); diff --git a/apps/variable_box_empty_controller.cpp b/apps/variable_box_empty_controller.cpp index 0c810bdd8..1f0d4fd7e 100644 --- a/apps/variable_box_empty_controller.cpp +++ b/apps/variable_box_empty_controller.cpp @@ -84,8 +84,8 @@ void VariableBoxEmptyController::setType(Type type) { messages[0] = I18n::Message::EmptyExpressionBox0; messages[1] = I18n::Message::EmptyExpressionBox1; messages[2] = I18n::Message::EmptyExpressionBox2; - char storeExpression[] = {'3', Ion::Charset::Sto, 'A'}; - layout = LayoutHelper::String(storeExpression, sizeof(storeExpression), VariableBoxEmptyView::k_font); + const char * storeExpression = "3→A"; + layout = LayoutHelper::String(storeExpression, strlen(storeExpression), VariableBoxEmptyView::k_font); break; } case Type::Functions: @@ -93,8 +93,8 @@ void VariableBoxEmptyController::setType(Type type) { messages[0] = I18n::Message::EmptyFunctionBox0; messages[1] = I18n::Message::EmptyFunctionBox1; messages[2] = I18n::Message::EmptyFunctionBox2; - char storeFunction[] = {'3', '+', Graph::StorageCartesianFunctionStore::Symbol(), Ion::Charset::Sto, 'f', '(', Graph::StorageCartesianFunctionStore::Symbol(), ')'}; - layout = LayoutHelper::String(storeFunction, sizeof(storeFunction), VariableBoxEmptyView::k_font); + const char * storeFunction = "3+x→f(x)"; + layout = LayoutHelper::String(storeFunction, strlen(storeFunction), VariableBoxEmptyView::k_font); break; } default: diff --git a/escher/include/escher/editable_field.h b/escher/include/escher/editable_field.h index 0343d183e..53671bd62 100644 --- a/escher/include/escher/editable_field.h +++ b/escher/include/escher/editable_field.h @@ -3,13 +3,14 @@ #include #include +#include class EditableField : public InputEventHandler { public: using InputEventHandler::InputEventHandler; virtual bool isEditing() const = 0; virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) = 0; - virtual char XNTChar(char defaultXNTChar) = 0; + virtual CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) = 0; virtual bool shouldFinishEditing(Ion::Events::Event event) = 0; }; diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index bb8a38927..f04ed133b 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -28,7 +28,7 @@ public: } bool hasText() const { return layout().hasText(); } Poincare::Layout layout() const { return m_contentView.expressionView()->layout(); } - char XNTChar(char defaultXNTChar) override; + CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) override; // ScrollableView void setBackgroundColor(KDColor c) override { diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index 49b488747..46a9c73f2 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -20,7 +20,7 @@ public: void setText(const char * text); void setAlignment(float horizontalAlignment, float verticalAlignment); virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) override; - char XNTChar(char defaultXNTChar) override; + CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) override; bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override; bool handleEvent(Ion::Events::Event event) override; constexpr static int maxBufferSize() { diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index b31123263..ecac2dc63 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -68,12 +68,12 @@ void LayoutField::ContentView::layoutCursorSubview() { m_cursorView.setFrame(KDRect(cursorTopLeftPosition, LayoutCursor::k_cursorWidth, m_cursor.cursorHeight())); } -char LayoutField::XNTChar(char defaultXNTChar) { - char xnt = m_contentView.cursor()->layoutReference().XNTChar(); - if (xnt != Ion::Charset::Empty) { +CodePoint LayoutField::XNTCodePoint(CodePoint defaultXNTCodePoint) { + CodePoint xnt = m_contentView.cursor()->layoutReference().XNTCodePoint(); + if (xnt != KDCodePointNull) { return xnt; } - return defaultXNTChar; + return defaultXNTCodePoint; } void LayoutField::reload(KDSize previousSize) { diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index e9084892c..e7bccb24d 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -30,11 +30,12 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for // Remove EmptyChars for (size_t i = bufferIndex; i < eventTextSize; i++) { + /* TODO LEA if (text[i] != Ion::Charset::Empty) { buffer[bufferIndex++] = text[i]; } else if (i < cursorIndexInCommand) { cursorIndexInCommand--; - } + } */ } if ((indentation && insertTextWithIndentation(buffer, cursorLocation())) || insertTextAtLocation(buffer, cursorLocation())) { diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index 7f21bcfc6..1a2190a42 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include /* TextField::ContentView */ @@ -312,13 +311,14 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { return false; } -char TextField::XNTChar(char defaultXNTChar) { +CodePoint TextField::XNTCodePoint(CodePoint defaultXNTCodePoint) { static constexpr struct { const char *name; char xnt; } sFunctions[] = { { "diff", 'x' }, { "int", 'x' }, { "product", 'n' }, { "sum", 'n' } }; // Let's assume everything before the cursor is nested correctly, which is reasonable if the expression is being entered left-to-right. const char * text = this->text(); + /* TODO LEA size_t location = cursorLocation(); unsigned level = 0; while (location >= 1) { @@ -355,8 +355,9 @@ char TextField::XNTChar(char defaultXNTChar) { break; } } + */ // Fallback to the default - return defaultXNTChar; + return defaultXNTCodePoint; } bool TextField::handleEvent(Ion::Events::Event event) { @@ -404,6 +405,7 @@ bool TextField::privateHandleMoveEvent(Ion::Events::Event event) { } bool TextField::handleEventWithText(const char * eventText, bool indentation, bool forceCursorRightOfText) { +//TODO LEA size_t previousTextLength = strlen(text()); size_t eventTextLength = strlen(eventText); @@ -421,11 +423,11 @@ bool TextField::handleEventWithText(const char * eventText, bool indentation, bo int newBufferIndex = 0; // Remove EmptyChars - for (size_t i = 0; i < eventTextSize; i++) { + /* TODO for (size_t i = 0; i < eventTextSize; i++) { if (eventText[i] != Ion::Charset::Empty) { buffer[newBufferIndex++] = eventText[i]; } - } + }*/ int nextCursorLocation = draftTextLength(); if (insertTextAtLocation(buffer, cursorLocation())) { diff --git a/escher/src/text_input_helpers.cpp b/escher/src/text_input_helpers.cpp index a9600b62b..c210c33a0 100644 --- a/escher/src/text_input_helpers.cpp +++ b/escher/src/text_input_helpers.cpp @@ -1,17 +1,17 @@ #include -#include #include namespace TextInputHelpers { size_t CursorIndexInCommand(const char * text) { + // TODO LEA size_t index = 0; while (text[index] != 0) { if (text[index] == '\'' && text[index+1] == '\'') { return index + 1; - } else if (text[index] == Ion::Charset::Empty) { + } /* TODO else if (text[index] == Ion::Charset::Empty) { return index; - } + }*/ index++; } return index; diff --git a/ion/include/ion.h b/ion/include/ion.h index 8c66af6bf..74e62514b 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include diff --git a/ion/include/ion/charset.h b/ion/include/ion/charset.h deleted file mode 100644 index 1a18bbfec..000000000 --- a/ion/include/ion/charset.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ION_CHARSET_H -#define ION_CHARSET_H - -namespace Ion { - -enum Charset : char { - Integral = (char)127, - XBar = (char)128, - YBar = (char)130, - CapitalGamma = (char)132, - CapitalDelta = (char)133, - CapitalSigma = (char)134, - SmallTheta = (char)135, - SmallLambda = (char)136, - SmallMu = (char)137, - SmallPi = (char)138, - SmallSigma = (char)139, - IComplex = (char)140, - Exponent = (char)141, - Prime = (char)142, - Exponential = (char)143, - Sto = (char)144, - Root = (char)145, - LessEqual = (char)146, - GreaterEqual = (char)147, - MultiplicationSign = (char)148, - MiddleDot = (char)149, - AlmostEqual = (char)150, - Degree = (char)151, - Empty = (char)152, // This char is used to be parsed into EmptyExpression - LeftSuperscript = (char)153, // This char is used to parse Power - RightSuperscript = (char)154 // This char is used to parse Power -}; - -} - -#endif diff --git a/ion/src/shared/events.cpp b/ion/src/shared/events.cpp index b5300ec37..89fb1a736 100644 --- a/ion/src/shared/events.cpp +++ b/ion/src/shared/events.cpp @@ -1,5 +1,5 @@ #include -#include +#include extern "C" { #include @@ -25,42 +25,24 @@ private: #define U() EventData::Undefined() #define T(x) EventData::Text(x) -static constexpr const char k_exponential[6] = {Ion::Charset::Exponential, '^', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_naperianLogarithm[6] = {'l', 'n', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_logarithm[7] = {'l', 'o', 'g', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_complexI[2] = {Ion::Charset::IComplex, 0}; - -static constexpr const char k_sine[7] = {'s', 'i', 'n', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_cosine[7] = {'c', 'o', 's', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_tangent[7] = {'t', 'a', 'n', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_pi[2] = {Ion::Charset::SmallPi, 0}; -static constexpr const char k_root[5] = {Ion::Charset::Root, '(', Ion::Charset::Empty, ')', 0}; - -static constexpr const char k_multiplicationSign[2] = {Ion::Charset::MultiplicationSign, 0}; -static constexpr const char k_exponent[2] = {Ion::Charset::Exponent, 0}; -static constexpr const char k_sto[2] = {Ion::Charset::Sto, 0}; - -static constexpr const char k_arcSine[8] = {'a', 's', 'i', 'n', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_arcCosine[8] = {'a', 'c', 'o', 's', '(', Ion::Charset::Empty, ')', 0}; -static constexpr const char k_arcTangent[8] = {'a', 't', 'a', 'n', '(', Ion::Charset::Empty, ')', 0}; - +static_assert('\x11' == KDCodePointEmpty, "Unicode error"); static constexpr EventData s_dataForEvent[4*Event::PageSize] = { // Plain TL(), TL(), TL(), TL(), TL(), TL(), TL(), TL(), U(), U(), U(), U(), TL(), TL(), TL(), TL(), TL(), TL(), - T(k_exponential), T(k_naperianLogarithm), T(k_logarithm), T(k_complexI), T(","), T("^"), - T(k_sine), T(k_cosine), T(k_tangent), T(k_pi), T(k_root), T("^2"), + T("ℯ^(\x11)"), T("ln(\x11)"), T("log(\x11)"), T("𝐢"), T(","), T("^"), + T("sin(\x11)"), T("cos(\x11)"), T("tan(\x11)"), T("π"), T("√(\x11)"), T("^2"), T("7"), T("8"), T("9"), T("("), T(")"), U(), - T("4"), T("5"), T("6"), T(k_multiplicationSign), T("/"), U(), + T("4"), T("5"), T("6"), T("×"), T("/"), U(), T("1"), T("2"), T("3"), T("+"), T("-"), U(), - T("0"), T("."), T(k_exponent), TL(), TL(), U(), + T("0"), T("."), T("ℯ"), TL(), TL(), U(), // Shift TL(), U(), U(), TL(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), TL(), TL(), TL(), TL(), - T("["), T("]"), T("{"), T("}"), T("_"), T(k_sto), - T(k_arcSine), T(k_arcCosine), T(k_arcTangent), T("="), T("<"), T(">"), + T("["), T("]"), T("{"), T("}"), T("_"), T("→"), + T("arcsin(\x11)"), T("arccos(\x11)"), T("arctan(\x11)"), T("="), T("<"), T(">"), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h index fc32a7b1a..e3648384d 100644 --- a/kandinsky/include/kandinsky/unicode/code_point.h +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -5,9 +5,9 @@ class CodePoint { public: + constexpr static int MaxCodePointCharLength = sizeof(uint32_t) / sizeof(char); constexpr CodePoint(uint32_t c) : m_code(c) {} - operator uint16_t() const { return m_code; } - + constexpr operator uint16_t() const { return m_code; } bool isCombining() const { return (m_code >= 0x300 && m_code <= 0x036F); @@ -16,12 +16,38 @@ private: uint32_t m_code; }; -static constexpr CodePoint KDCodePointNull = 0x0; -static constexpr CodePoint KDCodePointTabulation = 0x9; -static constexpr CodePoint KDCodePointLineFeed = 0xA; -static constexpr CodePoint KDCodePointMiddleDot = 0xB7; -static constexpr CodePoint KDCodePointMultiplicationSign = 0xD7; -static constexpr CodePoint KDCodePointLatinLetterSmallCapitalE = 0x1d07; -static constexpr CodePoint KDCodePointRightwardsArrow = 0x2192; + +// TODO LEA Remove unneeded values +static constexpr CodePoint KDCodePointNull = 0x0; +static constexpr CodePoint KDCodePointTabulation = 0x9; +static constexpr CodePoint KDCodePointLineFeed = 0xa; + +/* 0x11, 0x12, 0x13, 0x14 represent DEVICE CONTROL ONE TO FOUR. They are not + * used, so we can use them for another purpose */ +static constexpr CodePoint KDCodePointEmpty = 0x11; // Used to be parsed into EmptyExpression +static constexpr CodePoint KDCodePointLeftSuperscript = 0x12; // Used to parse Power +static constexpr CodePoint KDCodePointRightSuperscript = 0x13; // Used to parse Power +static constexpr CodePoint KDCodePointUnknownX = 0x14; // Used to store expressions + +static constexpr CodePoint KDCodePointSpace = 0x20; // +static constexpr CodePoint KDCodePointDegree = 0xb0; // ° +static constexpr CodePoint KDCodePointMiddleDot = 0xb7; // · +static constexpr CodePoint KDCodePointMultiplicationSign = 0xd7; // × +static constexpr CodePoint KDCodePointGreekCapitalLetterGamma = 0x393; // Γ +static constexpr CodePoint KDCodePointGreekCapitalLetterDelta = 0x394; // Δ +static constexpr CodePoint KDCodePointGreekSmallLetterTheta = 0x3b8; // θ +static constexpr CodePoint KDCodePointGreekSmallLetterLambda = 0x3bb; // λ +static constexpr CodePoint KDCodePointGreekSmallLetterPi = 0x3c0; // π +static constexpr CodePoint KDCodePointGreekSmallLetterSigma = 0x3c3; // σ +static constexpr CodePoint KDCodePointLatinLetterSmallCapitalE = 0x1d07; // ᴇ +static constexpr CodePoint KDCodePointScriptSmallE = 0x212f; // ℯ +static constexpr CodePoint KDCodePointRightwardsArrow = 0x2192; // → +static constexpr CodePoint KDCodePointNArySummation = 0x2211; // ∑ +static constexpr CodePoint KDCodePointSquareRoot = 0x221a; // √ +static constexpr CodePoint KDCodePointIntegral = 0x222b; // ∫ +static constexpr CodePoint KDCodePointAlmostEqualTo = 0x2248; // ≈ +static constexpr CodePoint KDCodePointLessThanOrEqualTo = 0x2264; // ≤ +static constexpr CodePoint KDCodePointGreaterThanOrEqualTo = 0x2265; // ≥ +static constexpr CodePoint KDCodePointMathematicalBoldSmallI = 0x1d422; // 𝐢 #endif diff --git a/kandinsky/include/kandinsky/unicode/utf8_decoder.h b/kandinsky/include/kandinsky/unicode/utf8_decoder.h index f4dab5269..abb768d7f 100644 --- a/kandinsky/include/kandinsky/unicode/utf8_decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8_decoder.h @@ -19,6 +19,7 @@ class UTF8Decoder { public: UTF8Decoder(const char * string) : m_string(string) {} CodePoint nextCodePoint(); + static size_t CharSizeOfCodePoint(CodePoint c); static size_t CodePointToChars(CodePoint c, char * buffer, int bufferSize); private: const char * m_string; diff --git a/kandinsky/src/unicode/utf8_decoder.cpp b/kandinsky/src/unicode/utf8_decoder.cpp index bdb6ed370..cb6f449d3 100644 --- a/kandinsky/src/unicode/utf8_decoder.cpp +++ b/kandinsky/src/unicode/utf8_decoder.cpp @@ -25,8 +25,14 @@ CodePoint UTF8Decoder::nextCodePoint() { return CodePoint(result); } +size_t UTF8Decoder::CharSizeOfCodePoint(CodePoint c) { + constexpr int bufferSize = CodePoint::MaxCodePointCharLength; + char buffer[bufferSize]; + return CodePointToChars(c, buffer, bufferSize); +} + size_t UTF8Decoder::CodePointToChars(CodePoint c, char * buffer, int bufferSize) { - assert(bufferSize >= sizeof(CodePoint)/sizeof(char)); + assert(bufferSize >= CodePoint::MaxCodePointCharLength); size_t i = 0; if (c <= 0x7F) { buffer[i++] = c; diff --git a/poincare/include/poincare/code_point_layout.h b/poincare/include/poincare/code_point_layout.h index 06f5adb91..28d3970e7 100644 --- a/poincare/include/poincare/code_point_layout.h +++ b/poincare/include/poincare/code_point_layout.h @@ -63,7 +63,7 @@ private: class CodePointLayout final : public Layout { public: CodePointLayout(const CodePointLayoutNode * n) : Layout(n) {} - static CharLayout Builder(CodePoint c, const KDFont * font = KDFont::LargeFont); + static CodePointLayout Builder(CodePoint c, const KDFont * font = KDFont::LargeFont); const KDFont * font() const { return const_cast(this)->node()->font(); } CodePoint codePoint() const { return const_cast(this)->node()->codePoint(); } private: diff --git a/poincare/include/poincare/constant.h b/poincare/include/poincare/constant.h index 9f09fbbe1..e0a9c5626 100644 --- a/poincare/include/poincare/constant.h +++ b/poincare/include/poincare/constant.h @@ -5,6 +5,9 @@ namespace Poincare { +/* TODO: Also keep a m_codePoint ? Redundant with m_name, but faster constants + * comparison */ + class ConstantNode final : public SymbolAbstractNode { public: ConstantNode(const char * newName, int length); @@ -35,9 +38,10 @@ public: Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } /* Symbol properties */ - bool isPi() const { return isConstantChar(Ion::Charset::SmallPi); } - bool isExponential() const { return isConstantChar(Ion::Charset::Exponential); } - bool isIComplex() const { return isConstantChar(Ion::Charset::IComplex); } + bool isPi() const { return isConstantCodePoint(KDCodePointGreekSmallLetterPi); } + bool isExponential() const { return isConstantCodePoint(KDCodePointScriptSmallE); } + bool isIComplex() const { return isConstantCodePoint(KDCodePointMathematicalBoldSmallI); } + CodePoint codePoint() const; // Comparison int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; @@ -49,13 +53,13 @@ private: size_t nodeSize() const override { return sizeof(ConstantNode); } template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; - bool isConstantChar(char c) const { const char constantName[2] = {c, 0}; return strcmp(m_name, constantName) == 0; } + bool isConstantCodePoint(CodePoint c) const; }; class Constant final : public SymbolAbstract { public: Constant(const ConstantNode * node) : SymbolAbstract(node) {} - static Constant Builder(char name) { return SymbolAbstract::Builder(&name, 1); } + static Constant Builder(CodePoint c); // Constant properties bool isPi() const { return node()->isPi(); } diff --git a/poincare/include/poincare/integral_layout.h b/poincare/include/poincare/integral_layout.h index 30b8901cf..9b33b2c40 100644 --- a/poincare/include/poincare/integral_layout.h +++ b/poincare/include/poincare/integral_layout.h @@ -22,7 +22,7 @@ public: void deleteBeforeCursor(LayoutCursor * cursor) override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } - char XNTChar() const override { return 'x'; } + CodePoint XNTCodePoint() const override { return 'x'; } // TreeNode size_t size() const override { return sizeof(IntegralLayoutNode); } diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 306b93355..7cffc3ded 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -54,7 +54,7 @@ public: int leftCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->leftCollapsingAbsorbingChildIndex(); } int rightCollapsingAbsorbingChildIndex() const { return const_cast(this)->node()->rightCollapsingAbsorbingChildIndex(); } bool hasText() { return node()->hasText(); } - char XNTChar() const { return const_cast(this)->node()->XNTChar(); } + CodePoint XNTCodePoint() const { return const_cast(this)->node()->XNTCodePoint(); } // Layout modification void deleteBeforeCursor(LayoutCursor * cursor) { return node()->deleteBeforeCursor(cursor); } diff --git a/poincare/include/poincare/layout_helper.h b/poincare/include/poincare/layout_helper.h index d9b3a37c8..92a2036f8 100644 --- a/poincare/include/poincare/layout_helper.h +++ b/poincare/include/poincare/layout_helper.h @@ -15,6 +15,7 @@ namespace LayoutHelper { /* Create special layouts */ Layout Parentheses(Layout layout, bool cloneLayout); HorizontalLayout String(const char * buffer, int bufferLen, const KDFont * font = KDFont::LargeFont); + HorizontalLayout CodePointString(const CodePoint * buffer, int bufferLen, const KDFont * font = KDFont::LargeFont); Layout Logarithm(Layout argument, Layout index); }; diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 37c8899f9..d73188a7d 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -3,7 +3,6 @@ #include #include -#include namespace Poincare { @@ -106,9 +105,9 @@ public: virtual bool isMatrix() const { return false; } virtual bool isCodePoint() const { return false; } virtual bool hasUpperLeftIndex() const { return false; } - virtual char XNTChar() const { + virtual CodePoint XNTCodePoint() const { LayoutNode * p = parent(); - return p == nullptr ? Ion::Charset::Empty : p->XNTChar(); + return p == nullptr ? KDCodePointNull : p->XNTCodePoint(); } virtual bool willAddChildAtIndex(LayoutNode * l, int * index, int * currentNumberOfChildren, LayoutCursor * cursor) { return true; } diff --git a/poincare/include/poincare/print_float.h b/poincare/include/poincare/print_float.h index 7509878b0..26dfb0a2d 100644 --- a/poincare/include/poincare/print_float.h +++ b/poincare/include/poincare/print_float.h @@ -47,7 +47,7 @@ namespace PrintFloat { template int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, bool allowRounding = true); template - static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros); + static int convertFloatToTextPrivate(T f, char * buffer, int bufferSize, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros); } } diff --git a/poincare/include/poincare/sequence_layout.h b/poincare/include/poincare/sequence_layout.h index 59ef8d34d..28d2a48d5 100644 --- a/poincare/include/poincare/sequence_layout.h +++ b/poincare/include/poincare/sequence_layout.h @@ -20,7 +20,7 @@ public: void moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void deleteBeforeCursor(LayoutCursor * cursor) override; LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } - char XNTChar() const override { return 'n'; } + CodePoint XNTCodePoint() const override { return 'n'; } // TreeNode int numberOfChildren() const override { return 4; } diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index 6d2a0de39..e0ed71193 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -30,6 +30,7 @@ namespace SerializationHelper { // Write one char in a buffer int Char(char * buffer, int bufferSize, char c); // Write one code point in a buffer + constexpr int MaxSerializedCodePointSize = CodePoint::MaxCodePointCharLength + 1; // Null-terminating char int CodePoint(char * buffer, int bufferSize, CodePoint c); }; diff --git a/poincare/include/poincare/square_root.h b/poincare/include/poincare/square_root.h index 4ea8737d1..0cc02f5d0 100644 --- a/poincare/include/poincare/square_root.h +++ b/poincare/include/poincare/square_root.h @@ -4,7 +4,6 @@ #include #include #include -#include namespace Poincare { @@ -42,13 +41,8 @@ class SquareRoot final : public Expression { public: SquareRoot(const SquareRootNode * n) : Expression(n) {} static SquareRoot Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - - static_assert('\x91' == Ion::Charset::Root, "Charset error"); - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("\x91", 1, &UntypedBuilderOneChild); - + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("√", 1, &UntypedBuilderOneChild); Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - static const char k_name[2]; }; } diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index 3c4c93ec4..e7715e65e 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -61,7 +61,7 @@ public: enum SpecialSymbols : char { /* We can use characters from 1 to 31 as they do not correspond to usual * characters but events as 'end of text', 'backspace'... */ - UnknownX = 1, + UnknownX = 1 //TODO LEA make sure there is no collision with the code points }; Symbol(const SymbolNode * node) : SymbolAbstract(node) {} static Symbol Builder(const char * name, int length) { return SymbolAbstract::Builder(name, length); } diff --git a/poincare/include/poincare/trigonometry_cheat_table.h b/poincare/include/poincare/trigonometry_cheat_table.h index eb81172d5..0ae4f678a 100644 --- a/poincare/include/poincare/trigonometry_cheat_table.h +++ b/poincare/include/poincare/trigonometry_cheat_table.h @@ -7,7 +7,7 @@ namespace Poincare { /* We use the cheat table to look for known simplifications (e.g. cos(0)=1, - * cos(Pi/2)=1...). For each entry of the table, we store its expression and + * cos(π/2)=1...). For each entry of the table, we store its expression and * its float approximation in order to quickly scan the table looking for our * input approximation. If one entry matches the float approximation, we then * check that the actual expression of our input is equivalent to the table diff --git a/poincare/src/complex_argument.cpp b/poincare/src/complex_argument.cpp index eb6983ec3..b803c66eb 100644 --- a/poincare/src/complex_argument.cpp +++ b/poincare/src/complex_argument.cpp @@ -57,7 +57,7 @@ Expression ComplexArgument::shallowReduce(Context & context, Preferences::Comple return result; } else if (!std::isnan(app) && app <= -Expression::Epsilon()) { // arg(x) = Pi if x < 0 - Expression result = Constant::Builder(Ion::Charset::SmallPi); + Expression result = Constant::Builder(KDCodePointGreekSmallLetterPi); replaceWithInPlace(result); return result; } diff --git a/poincare/src/complex_cartesian.cpp b/poincare/src/complex_cartesian.cpp index ce0416d42..70b027169 100644 --- a/poincare/src/complex_cartesian.cpp +++ b/poincare/src/complex_cartesian.cpp @@ -152,7 +152,7 @@ Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFor } // Then, compute sign(b) * Pi/2 - arctan(a/b) Expression signb = SignFunction::Builder(b); - Expression signbPi2 = Multiplication::Builder(Rational::Builder(1,2), signb, Constant::Builder(Ion::Charset::SmallPi)); + Expression signbPi2 = Multiplication::Builder(Rational::Builder(1,2), signb, Constant::Builder(KDCodePointGreekSmallLetterPi)); signb.shallowReduce(context, complexFormat, angleUnit, target); Expression sub = Subtraction::Builder(signbPi2, arcTangent); signbPi2.shallowReduce(context, complexFormat, angleUnit, target); @@ -163,7 +163,7 @@ Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFor Expression signa = SignFunction::Builder(a).shallowReduce(context, complexFormat, angleUnit, target); Subtraction sub = Subtraction::Builder(Rational::Builder(1), signa); signa.shallowReduce(context, complexFormat, angleUnit, target); - Multiplication mul = Multiplication::Builder(Rational::Builder(1,2), Constant::Builder(Ion::Charset::SmallPi), sub); + Multiplication mul = Multiplication::Builder(Rational::Builder(1,2), Constant::Builder(KDCodePointGreekSmallLetterPi), sub); sub.shallowReduce(context, complexFormat, angleUnit, target); return mul; } @@ -330,7 +330,7 @@ ComplexCartesian ComplexCartesian::power(ComplexCartesian & other, Context & con rclone.shallowReduce(context, complexFormat, angleUnit, target); Expression thmuld = Multiplication::Builder(Rational::Builder(-1), thclone, d.clone()); thclone.shallowReduce(context, complexFormat, angleUnit, target); - Expression exp = Power::Builder(Constant::Builder(Ion::Charset::Exponential), thmuld); + Expression exp = Power::Builder(Constant::Builder(KDCodePointScriptSmallE), thmuld); thmuld.shallowReduce(context, complexFormat, angleUnit, target); Multiplication norm = Multiplication::Builder(rpowc, exp); rpowc.shallowReduce(context, complexFormat, angleUnit, target); diff --git a/poincare/src/constant.cpp b/poincare/src/constant.cpp index 2f971598c..788cf5135 100644 --- a/poincare/src/constant.cpp +++ b/poincare/src/constant.cpp @@ -4,10 +4,13 @@ #include #include #include +#include +#include #include #include #include #include +#include namespace Poincare { @@ -26,13 +29,13 @@ bool ConstantNode::isReal(Context & context) const { return !isIComplex(); } -int rankOfConstant(char c) { +int rankOfConstant(CodePoint c) { switch (c) { - case Ion::Charset::IComplex: + case KDCodePointMathematicalBoldSmallI : return 0; - case Ion::Charset::SmallPi: + case KDCodePointGreekSmallLetterPi : return 1; - case Ion::Charset::Exponential: + case KDCodePointScriptSmallE : return 2; default: assert(false); @@ -40,12 +43,19 @@ int rankOfConstant(char c) { } } +CodePoint ConstantNode::codePoint() const { + UTF8Decoder decoder = UTF8Decoder(m_name); + CodePoint result = decoder.nextCodePoint(); + assert(decoder.nextCodePoint() == KDCodePointNull); + return result; +} + int ConstantNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { if (!ascending) { return e->simplificationOrderSameType(this, true, canBeInterrupted); } assert(type() == e->type()); - return (rankOfConstant(name()[0]) - rankOfConstant(static_cast(e)->name()[0])); + return rankOfConstant(codePoint()) - rankOfConstant(static_cast(e)->codePoint()); } Layout ConstantNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -62,7 +72,6 @@ int ConstantNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo template Evaluation ConstantNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { if (isIComplex()) { - assert(m_name[1] == 0); return Complex::Builder(0.0, 1.0); } if (isPi()) { @@ -76,11 +85,26 @@ Expression ConstantNode::shallowReduce(Context & context, Preferences::ComplexFo return Constant(this).shallowReduce(context, complexFormat, angleUnit, target); } +bool ConstantNode::isConstantCodePoint(CodePoint c) const { + UTF8Decoder decoder(m_name); + bool result = (decoder.nextCodePoint() == c); + assert(decoder.nextCodePoint() == KDCodePointNull); + return result; +} + +Constant Constant::Builder(CodePoint c) { + constexpr int bufferSize = SerializationHelper::MaxSerializedCodePointSize; + char buffer[bufferSize]; + size_t codePointSize = SerializationHelper::CodePoint(buffer, bufferSize, c); + return SymbolAbstract::Builder(buffer, codePointSize); +} + Expression Constant::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { Expression result; - if (complexFormat == Preferences::ComplexFormat::Real && isIComplex()) { + bool isI = isIComplex(); + if (complexFormat == Preferences::ComplexFormat::Real && isI) { result = Unreal::Builder(); - } else if (target == ExpressionNode::ReductionTarget::User && isIComplex()) { + } else if (target == ExpressionNode::ReductionTarget::User && isI) { result = ComplexCartesian::Builder(Rational::Builder(0), Rational::Builder(1)); } if (!result.isUninitialized()) { diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 1e88397e9..fcad08c9a 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -176,7 +177,8 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print return currentChar; } if (currentChar >= bufferSize-1) { return bufferSize-1; } - buffer[currentChar++] = Ion::Charset::Exponent; + currentChar += SerializationHelper::CodePoint(buffer + currentChar, bufferSize - currentChar, KDCodePointScriptSmallE); + if (currentChar >= bufferSize-1) { return bufferSize-1; } currentChar += Integer(exponent).serialize(buffer+currentChar, bufferSize-currentChar); return currentChar; } diff --git a/poincare/src/empty_expression.cpp b/poincare/src/empty_expression.cpp index 882d6311f..88bd12e1f 100644 --- a/poincare/src/empty_expression.cpp +++ b/poincare/src/empty_expression.cpp @@ -2,12 +2,11 @@ #include #include #include -#include namespace Poincare { int EmptyExpressionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return SerializationHelper::Char(buffer, bufferSize, Ion::Charset::Empty); + return SerializationHelper::CodePoint(buffer, bufferSize, KDCodePointEmpty); } Layout EmptyExpressionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index ec19edd2f..b08f8e3ff 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -342,9 +342,10 @@ void Expression::SetEncounteredComplex(bool encounterComplex) { } Preferences::ComplexFormat Expression::UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput) { - if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, Ion::Charset::IComplex) != nullptr) { + /* TODO LEA if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, KDCodePointMathematicalBoldSmallI) != nullptr) { return Preferences::ComplexFormat::Cartesian; } + */ return complexFormat; } @@ -519,12 +520,12 @@ Expression Expression::ExpressionWithoutSymbols(Expression e, Context & context) Expression Expression::radianToDegree() { // e*180/Pi - return Multiplication::Builder(*this, Rational::Builder(180), Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Rational::Builder(-1))); + return Multiplication::Builder(*this, Rational::Builder(180), Power::Builder(Constant::Builder(KDCodePointGreekSmallLetterPi), Rational::Builder(-1))); } Expression Expression::degreeToRadian() { // e*Pi/180 - return Multiplication::Builder(*this, Rational::Builder(1, 180), Constant::Builder(Ion::Charset::SmallPi)); + return Multiplication::Builder(*this, Rational::Builder(1, 180), Constant::Builder(KDCodePointGreekSmallLetterPi)); } Expression Expression::reduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { @@ -626,9 +627,9 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre } if (!isZeroTb) { if (isOneTb) { - imag = Constant::Builder(Ion::Charset::IComplex); + imag = Constant::Builder(KDCodePointMathematicalBoldSmallI); } else { - imag = Multiplication::Builder(tb , Constant::Builder(Ion::Charset::IComplex)); + imag = Multiplication::Builder(tb , Constant::Builder(KDCodePointMathematicalBoldSmallI)); } } if (imag.isUninitialized()) { @@ -657,14 +658,14 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre if (!isZeroRa && !isZeroTb) { Expression arg; if (isOneTb) { - arg = Constant::Builder(Ion::Charset::IComplex); + arg = Constant::Builder(KDCodePointMathematicalBoldSmallI); } else { - arg = Multiplication::Builder(tb, Constant::Builder(Ion::Charset::IComplex)); + arg = Multiplication::Builder(tb, Constant::Builder(KDCodePointMathematicalBoldSmallI)); } if (isNegativeTb) { arg = Opposite::Builder(arg); } - exp = Power::Builder(Constant::Builder(Ion::Charset::Exponential), arg); + exp = Power::Builder(Constant::Builder(KDCodePointScriptSmallE), arg); } if (exp.isUninitialized()) { return norm; diff --git a/poincare/src/expression_debug.cpp b/poincare/src/expression_debug.cpp index f99d9523f..34eaa839d 100644 --- a/poincare/src/expression_debug.cpp +++ b/poincare/src/expression_debug.cpp @@ -213,14 +213,16 @@ void print_expression(const Expression e, int indentationLevel) { break; case ExpressionNode::Type::Symbol: std::cout << "Symbol("; - switch (static_cast(e).name()) { - case Ion::Charset::SmallPi: + UTF8Decoder decoder(static_cast(e).name()); + CodePoint firstCodePoint = decoder.nextCodePoint(); + switch (firstCodePoint) { + case KDCodePointGreekSmallLetterPi: std::cout << "PI"; break; - case Ion::Charset::IComplex: + case KDCodePointMathematicalBoldSmallI: std::cout << "i"; break; - case Ion::Charset::Exponential: + case KDCodePointScriptSmallE: std::cout << "e"; break; default: diff --git a/poincare/src/fraction_layout.cpp b/poincare/src/fraction_layout.cpp index 2b615d11a..4443c13fd 100644 --- a/poincare/src/fraction_layout.cpp +++ b/poincare/src/fraction_layout.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -135,7 +134,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr // Add a multiplication if omitted. if (idxInParent > 0 && p->isHorizontal() && p->childAtIndex(idxInParent - 1)->canBeOmittedMultiplicationLeftFactor()) { - buffer[numberOfChar++] = Ion::Charset::MiddleDot; + numberOfChar+= SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, KDCodePointMiddleDot); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} } @@ -159,7 +158,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr // Add a multiplication if omitted. if (idxInParent >= 0 && idxInParent < (p->numberOfChildren() - 1) && p->isHorizontal() && p->childAtIndex(idxInParent + 1)->canBeOmittedMultiplicationRightFactor()) { - buffer[numberOfChar++] = Ion::Charset::MiddleDot; + numberOfChar+= SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, KDCodePointMiddleDot); if (numberOfChar >= bufferSize-1) { return bufferSize-1;} } diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index a5f9346e5..9c29bd146 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace Poincare { @@ -75,7 +74,7 @@ void LayoutCursor::move(MoveDirection direction, bool * shouldRecomputeLayout) { void LayoutCursor::addEmptyExponentialLayout() { EmptyLayout emptyLayout = EmptyLayout::Builder(); HorizontalLayout sibling = HorizontalLayout::Builder( - CodePointLayout::Builder(Ion::Charset::Exponential), + CodePointLayout::Builder(KDCodePointScriptSmallE), VerticalOffsetLayout::Builder(emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); m_layout.addSibling(this, sibling, false); m_layout = emptyLayout; @@ -114,7 +113,7 @@ void LayoutCursor::addEmptySquarePowerLayout() { void LayoutCursor::addEmptyTenPowerLayout() { EmptyLayout emptyLayout = EmptyLayout::Builder(); HorizontalLayout sibling = HorizontalLayout::Builder( - CodePointLayout::Builder(Ion::Charset::MiddleDot), + CodePointLayout::Builder(KDCodePointMiddleDot), CodePointLayout::Builder('1'), CodePointLayout::Builder('0'), VerticalOffsetLayout::Builder( @@ -133,10 +132,12 @@ void LayoutCursor::addFractionLayoutAndCollapseSiblings() { } void LayoutCursor::addXNTCodePointLayout() { - m_layout.addSibling(this, CodePointLayout::Builder(m_layout.XNTChar()), true); + m_layout.addSibling(this, CodePointLayout::Builder(m_layout.XNTCodePoint()), true); } void LayoutCursor::insertText(const char * text) { +// TODO LEA +#if 0 int textLength = strlen(text); if (textLength <= 0) { return; @@ -144,12 +145,12 @@ void LayoutCursor::insertText(const char * text) { Layout newChild; Layout pointedChild; for (int i = 0; i < textLength; i++) { - if (text[i] == Ion::Charset::Empty) { + if (text[i] == //TODO Ion::Charset::Empty) { continue; } - if (text[i] == Ion::Charset::MultiplicationSign) { - newChild = CodePointLayout::Builder(Ion::Charset::MiddleDot); - } else if (text[i] == '(') { + if (text[i] == //TODO Ion::Charset::MultiplicationSign) { + newChild = CodePointLayout::Builder(KDCodePointMiddleDot); + } else*/ if (text[i] == '(') { newChild = LeftParenthesisLayout::Builder(); if (pointedChild.isUninitialized()) { pointedChild = newChild; @@ -175,6 +176,7 @@ void LayoutCursor::insertText(const char * text) { m_layout = pointedChild; m_position = Position::Right; } +#endif } void LayoutCursor::addLayoutAndMoveCursor(Layout l) { diff --git a/poincare/src/layout_helper.cpp b/poincare/src/layout_helper.cpp index d713ec1eb..9555eab3e 100644 --- a/poincare/src/layout_helper.cpp +++ b/poincare/src/layout_helper.cpp @@ -54,6 +54,16 @@ Layout LayoutHelper::Parentheses(Layout layout, bool cloneLayout) { } HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const KDFont * font) { + assert(bufferLen > 0); + HorizontalLayout resultLayout = HorizontalLayout::Builder(); + /* TODO LEA */ + for (int i = 0; i < bufferLen; i++) { + resultLayout.addChildAtIndex(CodePointLayout::Builder(buffer[i], font), i, i, nullptr); + } + return resultLayout; +} + +HorizontalLayout LayoutHelper::CodePointString(const CodePoint * buffer, int bufferLen, const KDFont * font) { assert(bufferLen > 0); HorizontalLayout resultLayout = HorizontalLayout::Builder(); for (int i = 0; i < bufferLen; i++) { diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index 4301d3c07..1e911c6e3 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -341,7 +341,7 @@ Expression Logarithm::splitLogarithmInteger(Integer i, bool isDenominator, Conte Expression Logarithm::shallowBeautify() { assert(numberOfChildren() == 2); - Constant e = Constant::Builder(Ion::Charset::Exponential); + Constant e = Constant::Builder(KDCodePointScriptSmallE); if (childAtIndex(1).isIdenticalTo(e)) { NaperianLogarithm np = NaperianLogarithm::Builder(childAtIndex(0)); replaceWithInPlace(np); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 29aceb262..edc28d20e 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -85,13 +85,17 @@ bool MultiplicationNode::childNeedsParenthesis(const TreeNode * child) const { } Layout MultiplicationNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - const char middleDotString[] = {Ion::Charset::MiddleDot, 0}; - return LayoutHelper::Infix(Multiplication(this), floatDisplayMode, numberOfSignificantDigits, middleDotString); + constexpr int stringMaxSize = CodePoint::MaxCodePointCharLength + 1; + char string[stringMaxSize]; + SerializationHelper::CodePoint(string, stringMaxSize, KDCodePointMiddleDot); + return LayoutHelper::Infix(Multiplication(this), floatDisplayMode, numberOfSignificantDigits, string); } int MultiplicationNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - const char multiplicationString[] = {Ion::Charset::MultiplicationSign, 0}; - return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, multiplicationString); + constexpr int stringMaxSize = CodePoint::MaxCodePointCharLength + 1; + char string[stringMaxSize]; + SerializationHelper::CodePoint(string, stringMaxSize, KDCodePointMultiplicationSign); + return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, string); } Expression MultiplicationNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { diff --git a/poincare/src/naperian_logarithm.cpp b/poincare/src/naperian_logarithm.cpp index 29ea6cb9b..56fb53063 100644 --- a/poincare/src/naperian_logarithm.cpp +++ b/poincare/src/naperian_logarithm.cpp @@ -35,7 +35,7 @@ Expression NaperianLogarithm::shallowReduce(Context & context, Preferences::Comp return SimplificationHelper::Map(*this, context, angleUnit); } #endif - Logarithm l = Logarithm::Builder(childAtIndex(0), Constant::Builder(Ion::Charset::Exponential)); + Logarithm l = Logarithm::Builder(childAtIndex(0), Constant::Builder(KDCodePointScriptSmallE)); replaceWithInPlace(l); return l.shallowReduce(context, complexFormat, angleUnit, target); } diff --git a/poincare/src/nth_root_layout.cpp b/poincare/src/nth_root_layout.cpp index 4e7dd4573..898641318 100644 --- a/poincare/src/nth_root_layout.cpp +++ b/poincare/src/nth_root_layout.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace Poincare { @@ -169,7 +168,7 @@ int NthRootLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pri buffer[bufferSize-1] = 0; int numberOfChar = 0; - buffer[numberOfChar++] = Ion::Charset::Root; + numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, KDCodePointSquareRoot); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 9b7d440f7..f5143f235 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -1,57 +1,56 @@ #include "tokenizer.h" -#include #include #include namespace Poincare { -static inline bool isLetter(const char c) { +static inline bool isLetter(const CodePoint c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); } -static inline bool isDigit(const char c) { +static inline bool isDigit(const CodePoint c) { return '0' <= c && c <= '9'; } -const char Tokenizer::nextChar(PopTest popTest, char context, bool * testResult) { - // Beware of chars spaning over more than one byte: use the UTF8Decoder. +const CodePoint Tokenizer::nextCodePoint(PopTest popTest, CodePoint context, bool * testResult) { UTF8Decoder decoder(m_text); CodePoint firstCodePoint = decoder.nextCodePoint(); - int numberOfBytesForChar = 1; + size_t numberOfBytesForCodePoint = UTF8Decoder::CharSizeOfCodePoint(firstCodePoint); if (firstCodePoint != KDCodePointNull) { CodePoint codePoint = decoder.nextCodePoint(); while (codePoint.isCombining()) { - numberOfBytesForChar++; + numberOfBytesForCodePoint = UTF8Decoder::CharSizeOfCodePoint(codePoint); codePoint = decoder.nextCodePoint(); } } - char c = *m_text; // TODO handle combined chars? - bool shouldPop = popTest(c, context); + // TODO handle combined code points? + bool shouldPop = popTest(firstCodePoint, context); if (testResult != nullptr) { *testResult = shouldPop; } if (shouldPop) { - m_text+= numberOfBytesForChar; + m_text+= numberOfBytesForCodePoint; } - return c; + return firstCodePoint; } -const char Tokenizer::popChar() { - return nextChar([](char c, char context) { return true; }); - // m_text now points to the start of the character after the returned char. +const CodePoint Tokenizer::popCodePoint() { + return nextCodePoint([](CodePoint c, CodePoint context) { return true; }); + /* m_text now points to the start of the first non combining code point after + * the returned code point. */ } -bool Tokenizer::canPopChar (const char c) { +bool Tokenizer::canPopCodePoint(const CodePoint c) { bool didPop = false; - nextChar([](char nextC, char context) { return nextC == context; }, c, &didPop); + nextCodePoint([](CodePoint nextC, CodePoint context) { return nextC == context; }, c, &didPop); return didPop; } -size_t Tokenizer::popWhile(PopTest popTest, char context) { +size_t Tokenizer::popWhile(PopTest popTest, CodePoint context) { size_t length = 0; bool didPop = true; while (didPop) { - nextChar(popTest, context, &didPop); + nextCodePoint(popTest, context, &didPop); if (didPop) { length++; } @@ -60,11 +59,11 @@ size_t Tokenizer::popWhile(PopTest popTest, char context) { } size_t Tokenizer::popIdentifier() { - return popWhile([](char c, char context) { return isLetter(c) || isDigit(c) || c == context; }, '_'); + return popWhile([](CodePoint c, CodePoint context) { return isLetter(c) || isDigit(c) || c == context; }, '_'); } size_t Tokenizer::popDigits() { - return popWhile([](char c, char context) { return isDigit(c); }); + return popWhile([](CodePoint c, CodePoint context) { return isDigit(c); }); } Token Tokenizer::popNumber() { @@ -75,7 +74,7 @@ Token Tokenizer::popNumber() { size_t fractionalPartLength = 0; assert(integralPartLength > 0 || *m_text == '.'); - if (canPopChar('.')) { + if (canPopCodePoint('.')) { fractionalPartText = m_text; fractionalPartLength = popDigits(); } @@ -87,8 +86,8 @@ Token Tokenizer::popNumber() { const char * exponentPartText = m_text; size_t exponentPartLength = 0; bool exponentIsNegative = false; - if (canPopChar(Ion::Charset::Exponent)) { - exponentIsNegative = canPopChar('-'); + if (canPopCodePoint(KDCodePointLatinLetterSmallCapitalE)) { + exponentIsNegative = canPopCodePoint('-'); exponentPartText = m_text; exponentPartLength = popDigits(); if (exponentPartLength == 0) { @@ -103,30 +102,30 @@ Token Tokenizer::popNumber() { Token Tokenizer::popToken() { // Skip whitespaces - while (canPopChar(' ')) {} + while (canPopCodePoint(' ')) {} - /* Save for later use (since m_text is altered by popChar, popNumber, + /* Save for later use (since m_text is altered by popCodePoint, popNumber, * popIdentifier). */ const char * start = m_text; - /* If the next char is the start of a number, we do not want to pop it because - * popNumber needs this char. */ - bool nextCharIsNeitherDotNorDigit = true; - const char currentChar = nextChar([](char c, char context) { return c != context && !isDigit(c); }, '.', &nextCharIsNeitherDotNorDigit); + /* If the next code point is the start of a number, we do not want to pop it + * because popNumber needs this code point. */ + bool nextCodePointIsNeitherDotNorDigit = true; + const CodePoint c = nextCodePoint([](CodePoint cp, CodePoint context) { return cp != context && !isDigit(cp); }, '.', &nextCodePointIsNeitherDotNorDigit); - // According to currentChar, recognize the Token::Type. - if (!nextCharIsNeitherDotNorDigit) { + // According to c, recognize the Token::Type. + if (!nextCodePointIsNeitherDotNorDigit) { return popNumber(); } - if (isLetter(currentChar)) { + if (isLetter(c)) { Token result(Token::Identifier); - result.setString(start, 1 + popIdentifier()); // We already popped 1 char + result.setString(start, 1 + popIdentifier()); // We already popped 1 code point return result; } - if ('(' <= currentChar && currentChar <= '/') { - /* Those characters form a contiguous range in the ascii character set, we - * make searching faster with this lookup table. */ - constexpr Token::Type typeForChar[] = { + if ('(' <= c && c <= '/') { + /* Those code points form a contiguous range in the utf-8 code points set, + * we can thus search faster with this lookup table. */ + constexpr Token::Type typeForCodePoint[] = { Token::LeftParenthesis, Token::RightParenthesis, Token::Times, @@ -136,58 +135,61 @@ Token Tokenizer::popToken() { Token::Undefined, Token::Slash }; - /* The dot character is the second last of that range, but it is matched + /* The dot code point is the second last of that range, but it is matched * before (with popNumber). */ - assert(currentChar != '.'); - return Token(typeForChar[currentChar - '(']); + assert(c != '.'); + return Token(typeForCodePoint[c - '(']); } - if (currentChar == Ion::Charset::MultiplicationSign || currentChar == Ion::Charset::MiddleDot) { + if (c == KDCodePointMultiplicationSign || c == KDCodePointMiddleDot) { return Token(Token::Times); } - if (currentChar == '^') { + if (c == '^') { return Token(Token::Caret); } - if (currentChar == Ion::Charset::LeftSuperscript) { + if (c == KDCodePointLeftSuperscript) { return Token(Token::LeftSuperscript); } - if (currentChar == Ion::Charset::RightSuperscript) { + if (c == KDCodePointRightSuperscript) { return Token(Token::RightSuperscript); } - if (currentChar == '!') { + if (c == '!') { return Token(Token::Bang); } - if (currentChar == '=') { + if (c == '=') { return Token(Token::Equal); } - if (currentChar == '[') { + if (c == '[') { return Token(Token::LeftBracket); } - if (currentChar == ']') { + if (c == ']') { return Token(Token::RightBracket); } - if (currentChar == '{') { + if (c == '{') { return Token(Token::LeftBrace); } - if (currentChar == '}') { + if (c == '}') { return Token(Token::RightBrace); } - if (currentChar == Ion::Charset::SmallPi || currentChar == Ion::Charset::IComplex || currentChar == Ion::Charset::Exponential) { + if (c == KDCodePointGreekSmallLetterPi + || c == KDCodePointMathematicalBoldSmallI + || c == KDCodePointScriptSmallE) + { Token result(Token::Constant); result.setString(start, 1); return result; } - if (currentChar == Ion::Charset::Root) { + if (c == KDCodePointSquareRoot) { Token result(Token::Identifier); result.setString(start, 1); return result; } - if (currentChar == Ion::Charset::Empty) { + if (c == KDCodePointEmpty) { return Token(Token::Empty); } - if (currentChar == Ion::Charset::Sto) { + if (c == KDCodePointRightwardsArrow) { return Token(Token::Store); } - if (currentChar == 0) { + if (c == 0) { return Token(Token::EndOfStream); } return Token(Token::Undefined); diff --git a/poincare/src/parsing/tokenizer.h b/poincare/src/parsing/tokenizer.h index b75d6e863..ef475f6b0 100644 --- a/poincare/src/parsing/tokenizer.h +++ b/poincare/src/parsing/tokenizer.h @@ -16,11 +16,11 @@ public: Tokenizer(const char * text) : m_text(text) {} Token popToken(); private: - typedef bool (*PopTest)(char c, char context); - const char nextChar(PopTest popTest, char context = 0, bool * testResult = nullptr); - const char popChar(); - bool canPopChar(const char c); - size_t popWhile(PopTest popTest, char context = 0); + typedef bool (*PopTest)(CodePoint c, CodePoint context); + const CodePoint nextCodePoint(PopTest popTest, CodePoint context = KDCodePointNull, bool * testResult = nullptr); + const CodePoint popCodePoint(); + bool canPopCodePoint(const CodePoint c); + size_t popWhile(PopTest popTest, CodePoint context = KDCodePointNull); size_t popDigits(); size_t popIdentifier(); Token popNumber(); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 91302c26b..f19b4954b 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -521,7 +521,7 @@ Expression Power::shallowReduce(Context & context, Preferences::ComplexFormat co Multiplication m1 = Multiplication::Builder(); replaceWithInPlace(m1); // Multiply m1 by i complex - Constant i = Constant::Builder(Ion::Charset::IComplex); + Constant i = Constant::Builder(KDCodePointMathematicalBoldSmallI); m1.addChildAtIndexInPlace(i, 0, 0); i.shallowReduce(context, complexFormat, angleUnit, target); m1.addChildAtIndexInPlace(*this, 1, 1); @@ -1136,17 +1136,17 @@ Expression Power::equivalentExpressionUsingStandardExpression() const { Expression Power::CreateComplexExponent(const Expression & r, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { // Returns e^(i*pi*r) - const Constant exp = Constant::Builder(Ion::Charset::Exponential); - Constant iComplex = Constant::Builder(Ion::Charset::IComplex); - const Constant pi = Constant::Builder(Ion::Charset::SmallPi); + const Constant exp = Constant::Builder(KDCodePointScriptSmallE); + Constant iComplex = Constant::Builder(KDCodePointMathematicalBoldSmallI); + const Constant pi = Constant::Builder(KDCodePointGreekSmallLetterPi); Multiplication mExp = Multiplication::Builder(iComplex, pi, r.clone()); iComplex.shallowReduce(context, complexFormat, angleUnit, target); Power p = Power::Builder(exp, mExp); mExp.shallowReduce(context, complexFormat, angleUnit, target); return p; #if 0 - const Constant iComplex = Constant::Builder(Ion::Charset::IComplex); - const Constant pi = Constant::Builder(Ion::Charset::SmallPi); + const Constant iComplex = Constant::Builder(KDCodePointMathematicalBoldSmallI); + const Constant pi = Constant::Builder(KDCodePointGreekSmallLetterPi); Expression op = Multiplication::Builder(pi, r).shallowReduce(context, complexFormat, angleUnit, false); Cosine cos = Cosine(op).shallowReduce(context, complexFormat, angleUnit, false);; Sine sin = Sine(op).shallowReduce(context, complexFormat, angleUnit, false); diff --git a/poincare/src/print_float.cpp b/poincare/src/print_float.cpp index 10171fe93..af65a5d59 100644 --- a/poincare/src/print_float.cpp +++ b/poincare/src/print_float.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include extern "C" { #include @@ -20,8 +21,9 @@ void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLe * in first position. When called by convertFloatToText, the buffer length is * always > 0 as we asserted a minimal number of available chars. */ assert(bufferLength > 0 && decimalMarkerPosition != 0); - char tempBuffer[PrintFloat::k_maxFloatBufferLength]; - int intLength = i.serialize(tempBuffer, PrintFloat::k_maxFloatBufferLength); + constexpr int tempBufferSize = PrintFloat::k_maxFloatBufferLength; + char tempBuffer[tempBufferSize]; + int intLength = i.serialize(tempBuffer, tempBufferSize); int firstDigitChar = tempBuffer[0] == '-' ? 1 : 0; for (int k = bufferLength-1; k >= firstDigitChar; k--) { if (k == decimalMarkerPosition) { @@ -46,14 +48,15 @@ int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize, assert(numberOfSignificantDigits > 0); assert(bufferSize > 0); - char tempBuffer[PrintFloat::k_maxFloatBufferLength]; + constexpr int tempBufferSize = PrintFloat::k_maxFloatBufferLength; + char tempBuffer[tempBufferSize]; int numberOfZerosRemoved = 0; - int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode, &numberOfZerosRemoved); + int requiredLength = convertFloatToTextPrivate(f, tempBuffer, tempBufferSize, numberOfSignificantDigits, mode, &numberOfZerosRemoved); /* If the required buffer size overflows the buffer size, we first force the * display mode to scientific and decrease the number of significant digits to * fit the buffer size. */ if (mode == Preferences::PrintFloatMode::Decimal && requiredLength >= bufferSize) { - requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); + requiredLength = convertFloatToTextPrivate(f, tempBuffer, tempBufferSize, numberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); } if (requiredLength >= bufferSize) { /* If the buffer size is still too small and rounding is allowed, we only @@ -65,7 +68,7 @@ int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize, } int adjustedNumberOfSignificantDigits = numberOfSignificantDigits - numberOfZerosRemoved - requiredLength + bufferSize - 1; adjustedNumberOfSignificantDigits = adjustedNumberOfSignificantDigits < 1 ? 1 : adjustedNumberOfSignificantDigits; - requiredLength = convertFloatToTextPrivate(f, tempBuffer, adjustedNumberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); + requiredLength = convertFloatToTextPrivate(f, tempBuffer, tempBufferSize, adjustedNumberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); } requiredLength = requiredLength < bufferSize ? requiredLength : bufferSize-1; strlcpy(buffer, tempBuffer, bufferSize); @@ -73,21 +76,21 @@ int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize, } template -int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros) { +int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int bufferSize, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros) { assert(numberOfSignificantDigits > 0); if (std::isinf(f)) { - assert(Infinity::NameSize()+1 < PrintFloat::k_maxFloatBufferLength); + assert(Infinity::NameSize()+1 < bufferSize); int currentChar = 0; if (f < 0) { buffer[currentChar++] = '-'; } - strlcpy(&buffer[currentChar], Infinity::Name(), PrintFloat::k_maxFloatBufferLength-1); + strlcpy(&buffer[currentChar], Infinity::Name(), bufferSize-1); return currentChar + Infinity::NameSize() - 1; } if (std::isnan(f)) { - assert(Undefined::NameSize() < PrintFloat::k_maxFloatBufferLength); - strlcpy(buffer, Undefined::Name(), PrintFloat::k_maxFloatBufferLength); + assert(Undefined::NameSize() < bufferSize); + strlcpy(buffer, Undefined::Name(), bufferSize); return Undefined::NameSize() - 1; } @@ -180,23 +183,24 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif int numberOfCharsForMantissaWithSign = f >= 0 ? numberOfCharsForMantissaWithoutSign : numberOfCharsForMantissaWithoutSign + 1; // Print mantissa assert(!dividend.isOverflow()); - if (numberOfCharsForMantissaWithSign >= PrintFloat::k_maxFloatBufferLength) { + if (numberOfCharsForMantissaWithSign >= bufferSize) { /* Exception 3: if we are about to overflow the buffer, we escape by * returning a big int. This will be caught by 'convertFloatToText' which * will force displayMode to Scientific. */ assert(mode == Preferences::PrintFloatMode::Decimal); return INT_MAX; } - assert(numberOfCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength); + assert(numberOfCharsForMantissaWithSign < bufferSize); PrintFloat::printBase10IntegerWithDecimalMarker(buffer, numberOfCharsForMantissaWithSign, dividend, decimalMarkerPosition); if (mode == Preferences::PrintFloatMode::Decimal || exponentInBase10 == 0) { buffer[numberOfCharsForMantissaWithSign] = 0; return numberOfCharsForMantissaWithSign; } // Print exponent - assert(numberOfCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength); - buffer[numberOfCharsForMantissaWithSign] = Ion::Charset::Exponent; - assert(numberOfCharExponent+numberOfCharsForMantissaWithSign+1 < PrintFloat::k_maxFloatBufferLength); + assert(numberOfCharsForMantissaWithSign < bufferSize); + int currentNumberOfChar = numberOfCharsForMantissaWithSign; + currentNumberOfChar+= SerializationHelper::CodePoint(buffer + currentNumberOfChar, bufferSize - currentNumberOfChar, KDCodePointScriptSmallE); + assert(numberOfCharExponent+currentNumberOfChar < bufferSize); PrintFloat::printBase10IntegerWithDecimalMarker(buffer+numberOfCharsForMantissaWithSign+1, numberOfCharExponent, Integer(exponentInBase10), -1); buffer[numberOfCharsForMantissaWithSign+1+numberOfCharExponent] = 0; return (numberOfCharsForMantissaWithSign+1+numberOfCharExponent); diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index f66c02ec6..953013948 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -158,10 +158,9 @@ int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoin buffer[0] = 0; return 0; } - constexpr int maxCodePointSize = sizeof(class CodePoint)/sizeof(char) + 1; // Null-terminating char - char helpBuffer[maxCodePointSize]; - size_t size = UTF8Decoder::CodePointToChars(c, helpBuffer, maxCodePointSize); - assert(size < maxCodePointSize); + char helpBuffer[MaxSerializedCodePointSize]; + size_t size = UTF8Decoder::CodePointToChars(c, helpBuffer, MaxSerializedCodePointSize); + assert(size < MaxSerializedCodePointSize); helpBuffer[size] = 0; strlcpy(buffer, helpBuffer, bufferSize); return strlen(buffer); diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index 76e646653..755b11b4a 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -28,13 +28,16 @@ Expression StoreNode::shallowReduce(Context & context, Preferences::ComplexForma } int StoreNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "\x90"); + constexpr int stringMaxSize = CodePoint::MaxCodePointCharLength + 1; + char string[stringMaxSize]; + SerializationHelper::CodePoint(string, stringMaxSize, KDCodePointRightwardsArrow); + return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, string); } Layout StoreNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(CodePointLayout::Builder(Ion::Charset::Sto), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(CodePointLayout::Builder(KDCodePointRightwardsArrow), result.numberOfChildren(), result.numberOfChildren(), nullptr); result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false); return result; } diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 71e174ca0..aa4e9c574 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -32,7 +32,7 @@ float Trigonometry::characteristicXRange(const Expression & e, Context & context if (d == 0) { return 0.0f; } - // e has the form cos/sin/tan(ax+b) so it is periodic of period 2*Pi/a + // e has the form cos/sin/tan(ax+b) so it is periodic of period 2*π/a assert(d == 1); /* To compute a, the slope of the expression child(0), we compute the * derivative of child(0) for any x value. */ @@ -175,8 +175,8 @@ Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& co } } - /* Step 6. Look for an expression of type "cos(p/q * Pi)" in radians or - * "cos(p/q)" in degrees, put the argument in [0, Pi/2[ or [0, 90[ and + /* Step 6. Look for an expression of type "cos(p/q * π)" in radians or + * "cos(p/q)" in degrees, put the argument in [0, π/2[ or [0, 90[ and * multiply the cos/sin/tan by -1 if needed. * We know thanks to Step 3 that p/q > 0. */ if ((angleUnit == Preferences::AngleUnit::Radian @@ -190,25 +190,25 @@ Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& co { Rational r = angleUnit == Preferences::AngleUnit::Radian ? e.childAtIndex(0).childAtIndex(0).convert() : e.childAtIndex(0).convert(); /* Step 4.1. In radians: - * We first check if p/q * Pi is already in the right quadrant: - * p/q * Pi < Pi/2 => p/q < 2 => 2p < q */ + * We first check if p/q * π is already in the right quadrant: + * p/q * π < π/2 => p/q < 2 => 2p < q */ Integer dividand = angleUnit == Preferences::AngleUnit::Radian ? Integer::Addition(r.unsignedIntegerNumerator(), r.unsignedIntegerNumerator()) : r.unsignedIntegerNumerator(); Integer divisor = angleUnit == Preferences::AngleUnit::Radian ? r.integerDenominator() : Integer::Multiplication(r.integerDenominator(), Integer(90)); if (divisor.isLowerThan(dividand)) { - /* Step 4.2. p/q * Pi is not in the wanted trigonometrical quadrant. - * We could subtract n*Pi to p/q with n an integer. + /* Step 4.2. p/q * π is not in the wanted trigonometrical quadrant. + * We could subtract n*π to p/q with n an integer. * Given p/q = (q'*q+r')/q, we have - * (p/q * Pi - q'*Pi) < Pi/2 => r'/q < 1/2 => 2*r' r'/q < 1/2 => 2*r'().isMinusOne()) { Expression x = e.childAtIndex(0).childAtIndex(0); /* This equality is not true if x = 0. We apply it under certain conditions: @@ -278,7 +278,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c * reduced to undef) */ if (target == ExpressionNode::ReductionTarget::User || x.isNumber()) { Expression sign = SignFunction::Builder(x.clone()); - Multiplication m0 = Multiplication::Builder(Rational::Builder(1,2), sign, Constant::Builder(Ion::Charset::SmallPi)); + Multiplication m0 = Multiplication::Builder(Rational::Builder(1,2), sign, Constant::Builder(KDCodePointGreekSmallLetterPi)); sign.shallowReduce(context, complexFormat, angleUnit, target); e.replaceChildAtIndexInPlace(0, x); Addition a = Addition::Builder(m0); @@ -305,16 +305,16 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c */ Expression p = e.parent(); bool letArcFunctionAtRoot = !p.isUninitialized() && isDirectTrigonometryFunction(p); - /* Step 5. Handle opposite argument: arccos(-x) = Pi-arcos(x), + /* Step 5. Handle opposite argument: arccos(-x) = π-arcos(x), * arcsin(-x) = -arcsin(x), arctan(-x)= -arctan(x) * */ if (!letArcFunctionAtRoot) { Expression positiveArg = e.childAtIndex(0).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); if (!positiveArg.isUninitialized()) { // The argument was made positive - // acos(-x) = pi-acos(x) + // acos(-x) = π-acos(x) if (e.type() == ExpressionNode::Type::ArcCosine) { - Expression pi = angleUnit == Preferences::AngleUnit::Radian ? static_cast(Constant::Builder(Ion::Charset::SmallPi)) : static_cast(Rational::Builder(180)); + Expression pi = angleUnit == Preferences::AngleUnit::Radian ? static_cast(Constant::Builder(KDCodePointGreekSmallLetterPi)) : static_cast(Rational::Builder(180)); Subtraction s = Subtraction::Builder(); e.replaceWithInPlace(s); s.replaceChildAtIndexInPlace(0, pi); @@ -355,11 +355,11 @@ template T Trigonometry::RoundToMeaningfulDigits(T result, T input) { /* Cheat: openbsd trigonometric functions are numerical implementation and * thus are approximative. - * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to - * avoid weird results as acos(1) = 6E-17 or cos(Pi/2) = 4E-17, we round - * the result to its 1E-6 or 1E-14 precision when its ratio with the - * argument (pi/2 in the exemple) is smaller than epsilon. This way, we - * have sin(pi) ~ 0 and sin(1E-15)=1E-15. + * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid + * weird results as acos(1) = 6E-17 or cos(π/2) = 4E-17, we round the result + * to its 1E-6 or 1E-14 precision when its ratio with the argument (π/2 in the + * example) is smaller than epsilon. This way, we have sin(π) ~ 0 and + * sin(1E-15)=1E-15. * We can't do that for all evaluation as the user can operate on values as * small as 1E-308 (in double) and most results still be correct. */ if (input == 0.0 || std::fabs(result/input) <= Expression::Epsilon()) { diff --git a/poincare/src/trigonometry_cheat_table.cpp b/poincare/src/trigonometry_cheat_table.cpp index 62810698f..58b2cba17 100644 --- a/poincare/src/trigonometry_cheat_table.cpp +++ b/poincare/src/trigonometry_cheat_table.cpp @@ -72,70 +72,68 @@ Expression TrigonometryCheatTable::simplify(const Expression e, ExpressionNode:: return Expression(); } -static_assert('\x8A' == Ion::Charset::SmallPi, "Unicode error"); - /* Some cheat tables values were not entered because they would never be needed * For instance, when simplfy a Cosine, we always compute the value for an angle * in the top right trigonometric quadrant. */ const TrigonometryCheatTable * TrigonometryCheatTable::Table() { static Row sTableRows[] = { Row(Row::Pair("-90", -90.0f), - Row::Pair("\x8A*(-2)^(-1)", -1.5707963267948966f), + Row::Pair("π*(-2)^(-1)", -1.5707963267948966f), Row::Pair(""), Row::Pair("-1",-1.0f), Row::Pair("undef")), Row(Row::Pair("-75",-75.0), - Row::Pair("\x8A*(-5)*12^(-1)",-1.3089969389957472f), + Row::Pair("π*(-5)*12^(-1)",-1.3089969389957472f), Row::Pair(""), Row::Pair("(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)",-0.9659258262890683f), Row::Pair("-(3^(1/2)+2)",-3.7320508075688776f)), Row(Row::Pair("-72",-72.0), - Row::Pair("\x8A*2*(-5)^(-1)",-1.2566370614359172f), + Row::Pair("π*2*(-5)^(-1)",-1.2566370614359172f), Row::Pair(""), Row::Pair("-(5/8+5^(1/2)/8)^(1/2)",-0.9510565162951535f), Row::Pair("-(5+2*5^(1/2))^(1/2)",-3.077683537175253f)), Row(Row::Pair("-135/2",67.5f), - Row::Pair("\x8A*(-3)*8^(-1)",-1.1780972450961724f), + Row::Pair("π*(-3)*8^(-1)",-1.1780972450961724f), Row::Pair(""), Row::Pair("-(2+2^(1/2))^(1/2)*2^(-1)",-0.9238795325112867f), Row::Pair("-1-2^(1/2)",-2.4142135623730945f)), Row(Row::Pair("-60",-60.0f), - Row::Pair("\x8A*(-3)^(-1)",-1.0471975511965976f), + Row::Pair("π*(-3)^(-1)",-1.0471975511965976f), Row::Pair(""), Row::Pair("-3^(1/2)*2^(-1)",-0.8660254037844386f), Row::Pair("-3^(1/2)",-1.7320508075688767f)), Row(Row::Pair("-54",-54.0f), - Row::Pair("\x8A*(-3)*10^(-1)",-0.9424777960769379), + Row::Pair("π*(-3)*10^(-1)",-0.9424777960769379), Row::Pair(""), Row::Pair("4^(-1)*(-1-5^(1/2))",-0.8090169943749473f), Row::Pair("-(1+2*5^(-1/2))^(1/2)",-1.3763819204711731f)), Row(Row::Pair("-45",-45.0f), - Row::Pair("\x8A*(-4)^(-1)",-0.7853981633974483f), + Row::Pair("π*(-4)^(-1)",-0.7853981633974483f), Row::Pair(""), Row::Pair("(-1)*(2^(-1/2))",-0.7071067811865475f), Row::Pair("-1",-1.0f)), Row(Row::Pair("-36",-36.0f), - Row::Pair("\x8A*(-5)^(-1)",-0.6283185307179586f), + Row::Pair("π*(-5)^(-1)",-0.6283185307179586f), Row::Pair(""), Row::Pair("-(5/8-5^(1/2)/8)^(1/2)",-0.5877852522924731f), Row::Pair("-(5-2*5^(1/2))^(1/2)",-0.7265425280053609f)), Row(Row::Pair("-30",-30.0f), - Row::Pair("\x8A*(-6)^(-1)",-0.5235987755982988f), + Row::Pair("π*(-6)^(-1)",-0.5235987755982988f), Row::Pair(""), Row::Pair("-0.5",-0.5f), Row::Pair("-3^(-1/2)",-0.5773502691896256f)), Row(Row::Pair("-45/2",-22.5f), - Row::Pair("\x8A*(-8)^(-1)",-0.39269908169872414f), + Row::Pair("π*(-8)^(-1)",-0.39269908169872414f), Row::Pair(""), Row::Pair("(2-2^(1/2))^(1/2)*(-2)^(-1)",-0.3826834323650898f), Row::Pair("1-2^(1/2)",-0.4142135623730951f)), Row(Row::Pair("-18",-18.0f), - Row::Pair("\x8A*(-10)^(-1)",-0.3141592653589793f), + Row::Pair("π*(-10)^(-1)",-0.3141592653589793f), Row::Pair(""), Row::Pair("4^(-1)*(1-5^(1/2))",-0.3090169943749474f), Row::Pair("-(1-2*5^(-1/2))^(1/2)",-0.3249196962329063f)), Row(Row::Pair("-15",-15.0f), - Row::Pair("\x8A*(-12)^(-1)",-0.2617993877991494f), + Row::Pair("π*(-12)^(-1)",-0.2617993877991494f), Row::Pair(""), Row::Pair("-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",-0.25881904510252074f), Row::Pair("3^(1/2)-2",-0.2679491924311227f)), @@ -145,122 +143,122 @@ const TrigonometryCheatTable * TrigonometryCheatTable::Table() { Row::Pair("0",0.0f), Row::Pair("0",0.0f)), Row(Row::Pair("15",15.0f), - Row::Pair("\x8A*12^(-1)",0.2617993877991494f), + Row::Pair("π*12^(-1)",0.2617993877991494f), Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",0.9659258262890683f), Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)",0.25881904510252074f), Row::Pair("-(3^(1/2)-2)",0.2679491924311227f)), Row(Row::Pair("18",18.0f), - Row::Pair("\x8A*10^(-1)",0.3141592653589793f), + Row::Pair("π*10^(-1)",0.3141592653589793f), Row::Pair("(5/8+5^(1/2)/8)^(1/2)",0.9510565162951535f), Row::Pair("4^(-1)*(5^(1/2)-1)",0.3090169943749474f), Row::Pair("(1-2*5^(-1/2))^(1/2)",0.3249196962329063f)), Row(Row::Pair("45/2",22.5f), - Row::Pair("\x8A*8^(-1)",0.39269908169872414f), + Row::Pair("π*8^(-1)",0.39269908169872414f), Row::Pair("(2+2^(1/2))^(1/2)*2^(-1)",0.9238795325112867f), Row::Pair("(2-2^(1/2))^(1/2)*2^(-1)",0.3826834323650898f), Row::Pair("2^(1/2)-1",0.4142135623730951f)), Row(Row::Pair("30",30.0f), - Row::Pair("\x8A*6^(-1)",0.5235987755982988f), + Row::Pair("π*6^(-1)",0.5235987755982988f), Row::Pair("3^(1/2)*2^(-1)",0.8660254037844387f), Row::Pair("0.5",0.5f), Row::Pair("3^(-1/2)",0.5773502691896256f)), Row(Row::Pair("36",36.0f), - Row::Pair("\x8A*5^(-1)",0.6283185307179586f), + Row::Pair("π*5^(-1)",0.6283185307179586f), Row::Pair("(5^(1/2)+1)*4^(-1)",0.8090169943749475f), Row::Pair("(5/8-5^(1/2)/8)^(1/2)",0.5877852522924731f), Row::Pair("(5-2*5^(1/2))^(1/2)",0.7265425280053609f)), Row(Row::Pair("45",45.0f), - Row::Pair("\x8A*4^(-1)",0.7853981633974483f), + Row::Pair("π*4^(-1)",0.7853981633974483f), Row::Pair("2^(-1/2)",0.7071067811865476f), Row::Pair("2^(-1/2)",0.7071067811865475f), Row::Pair("1",1.0f)), Row(Row::Pair("54",54.0f), - Row::Pair("\x8A*3*10^(-1)",0.9424777960769379f), + Row::Pair("π*3*10^(-1)",0.9424777960769379f), Row::Pair("(5/8-5^(1/2)/8)^(1/2)",0.5877852522924732f), Row::Pair("4^(-1)*(5^(1/2)+1)",0.8090169943749473f), Row::Pair("(1+2*5^(-1/2))^(1/2)",1.3763819204711731f)), Row(Row::Pair("60",60.0f), - Row::Pair("\x8A*3^(-1)",1.0471975511965976f), + Row::Pair("π*3^(-1)",1.0471975511965976f), Row::Pair("0.5",0.5f), Row::Pair("3^(1/2)*2^(-1)",0.8660254037844386f), Row::Pair("3^(1/2)",1.7320508075688767f)), Row(Row::Pair("135/2",67.5f), - Row::Pair("\x8A*3*8^(-1)",1.1780972450961724f), + Row::Pair("π*3*8^(-1)",1.1780972450961724f), Row::Pair("(2-2^(1/2))^(1/2)*2^(-1)",0.38268343236508984f), Row::Pair("(2+2^(1/2))^(1/2)*2^(-1)",0.9238795325112867f), Row::Pair("1+2^(1/2)",2.4142135623730945f)), Row(Row::Pair("72",72.0f), - Row::Pair("\x8A*2*5^(-1)",1.2566370614359172f), + Row::Pair("π*2*5^(-1)",1.2566370614359172f), Row::Pair("(5^(1/2)-1)*4^(-1)",0.30901699437494745f), Row::Pair("(5/8+5^(1/2)/8)^(1/2)",0.9510565162951535f), Row::Pair("(5+2*5^(1/2))^(1/2)",3.077683537175253f)), Row(Row::Pair("75",75.0f), - Row::Pair("\x8A*5*12^(-1)",1.3089969389957472f), + Row::Pair("π*5*12^(-1)",1.3089969389957472f), Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)",0.25881904510252074f), Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",0.9659258262890683f), Row::Pair("3^(1/2)+2",3.7320508075688776f)), Row(Row::Pair("90",90.0f), - Row::Pair("\x8A*2^(-1)",1.5707963267948966f), + Row::Pair("π*2^(-1)",1.5707963267948966f), Row::Pair("0",0.0f), Row::Pair("1",1.0f), Row::Pair("undef")), Row(Row::Pair("105",105.0f), - Row::Pair("\x8A*7*12^(-1)",1.832595714594046f), + Row::Pair("π*7*12^(-1)",1.832595714594046f), Row::Pair("-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",-0.25881904510252063f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("108",108.0f), - Row::Pair("\x8A*3*5^(-1)",1.8849555921538759f), + Row::Pair("π*3*5^(-1)",1.8849555921538759f), Row::Pair("(1-5^(1/2))*4^(-1)",-0.30901699437494734f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("225/2",112.5f), - Row::Pair("\x8A*5*8^(-1)",1.9634954084936207f), + Row::Pair("π*5*8^(-1)",1.9634954084936207f), Row::Pair("(2-2^(1/2))^(1/2)*(-2)^(-1)",-0.3826834323650897f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("120",120.0f), - Row::Pair("\x8A*2*3^(-1)",2.0943951023931953f), + Row::Pair("π*2*3^(-1)",2.0943951023931953f), Row::Pair("-0.5",-0.5f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("126",126.0f), - Row::Pair("\x8A*7*10^(-1)",2.199114857512855f), + Row::Pair("π*7*10^(-1)",2.199114857512855f), Row::Pair("-(5*8^(-1)-5^(1/2)*8^(-1))^(1/2)",-0.587785252292473f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("135",135.0f), - Row::Pair("\x8A*3*4^(-1)",2.356194490192345f), + Row::Pair("π*3*4^(-1)",2.356194490192345f), Row::Pair("(-1)*(2^(-1/2))",-0.7071067811865475f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("144",144.0f), - Row::Pair("\x8A*4*5^(-1)",2.5132741228718345f), + Row::Pair("π*4*5^(-1)",2.5132741228718345f), Row::Pair("(-5^(1/2)-1)*4^(-1)",-0.8090169943749473f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("150",150.0f), - Row::Pair("\x8A*5*6^(-1)",2.6179938779914944f), + Row::Pair("π*5*6^(-1)",2.6179938779914944f), Row::Pair("-3^(1/2)*2^(-1)",-0.8660254037844387f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("315/2",157.5f), - Row::Pair("\x8A*7*8^(-1)",2.748893571891069f), + Row::Pair("π*7*8^(-1)",2.748893571891069f), Row::Pair("-(2+2^(1/2))^(1/2)*2^(-1)",-0.9238795325112867f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("162",162.0f), - Row::Pair("\x8A*9*10^(-1)",2.827433388230814f), + Row::Pair("π*9*10^(-1)",2.827433388230814f), Row::Pair("-(5*8^(-1)+5^(1/2)*8^(-1))^(1/2)",-0.9510565162951535f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("165",165.0f), - Row::Pair("\x8A*11*12^(-1)",2.8797932657906435f), + Row::Pair("π*11*12^(-1)",2.8797932657906435f), Row::Pair("(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)",-0.9659258262890682f), Row::Pair(""), Row::Pair("")), Row(Row::Pair("180",180.0f), - Row::Pair("\x8A",3.141592653589793f), + Row::Pair("π",3.141592653589793f), Row::Pair("-1",-1.0f), Row::Pair("0",0.0f), Row::Pair("0",0.0f)) diff --git a/poincare/src/vertical_offset_layout.cpp b/poincare/src/vertical_offset_layout.cpp index 5b41e6282..5bd450783 100644 --- a/poincare/src/vertical_offset_layout.cpp +++ b/poincare/src/vertical_offset_layout.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -175,12 +174,12 @@ int VerticalOffsetLayoutNode::serialize(char * buffer, int bufferSize, Preferenc } assert(m_type == Type::Superscript); /* If the layout is a superscript, write: - * "Ion::Charset::LeftSuperscript indice Ion::Charset::RightSuperscript" */ - int numberOfChar = SerializationHelper::Char(buffer, bufferSize, Ion::Charset::LeftSuperscript); + * "KDCodePointLeftSuperscript indice KDCodePointRightSuperscript" */ + int numberOfChar = SerializationHelper::CodePoint(buffer, bufferSize, KDCodePointLeftSuperscript); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } numberOfChar += const_cast(this)->indiceLayout()->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - numberOfChar += SerializationHelper::Char(buffer+numberOfChar, bufferSize-numberOfChar, Ion::Charset::RightSuperscript); + numberOfChar += SerializationHelper::CodePoint(buffer+numberOfChar, bufferSize-numberOfChar, KDCodePointRightSuperscript); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } buffer[numberOfChar] = 0; diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index 85d81c602..37702f9b8 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -30,7 +30,6 @@ void assert_float_prints_to(T a, const char * result, Preferences::PrintFloatMod for (int i=tagSize+strlen(buffer)+1; i f, const char * result) { int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; char buffer[500]; f.template approximate(globalContext, Cartesian, Radian).serialize(buffer, sizeof(buffer), DecimalMode, numberOfDigits); - translate_in_ASCII_chars(buffer); quiz_assert(strcmp(buffer, result) == 0); } diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index e0e6d41be..e8afad338 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -41,33 +41,17 @@ bool expressions_are_equal(Poincare::Expression expected, Poincare::Expression g return identical; } -void translate_in_special_chars(char * expression) { - for (char *c = expression; *c; c++) { - switch (*c) { - case 'E': *c = Ion::Charset::Exponent; break; - case 'X': *c = Ion::Charset::Exponential; break; - case 'I': *c = Ion::Charset::IComplex; break; - case 'R': *c = Ion::Charset::Root; break; - case 'P': *c = Ion::Charset::SmallPi; break; - case '*': *c = Ion::Charset::MultiplicationSign; break; - case '>': *c = Ion::Charset::Sto; break; - case '?': *c = Poincare::Symbol::SpecialSymbols::UnknownX; break; - case '$': *c = Ion::Charset::LeftSuperscript; break; - case '#': *c = Ion::Charset::RightSuperscript; break; - } - } -} - -void translate_in_ASCII_chars(char * expression) { +/*TODO LEA + *void translate_in_ASCII_chars(char * expression) { for (char *c = expression; *c; c++) { switch (*c) { case Ion::Charset::Exponent: *c = 'E'; break; - case Ion::Charset::Exponential: *c = 'X'; break; - case Ion::Charset::IComplex: *c = 'I'; break; + case KDCodePointScriptSmallE: *c = 'X'; break; + case KDCodePointMathematicalBoldSmallI: *c = 'I'; break; case Ion::Charset::Root: *c = 'R'; break; - case Ion::Charset::SmallPi: *c = 'P'; break; + case KDCodePointGreekSmallLetterPi: *c = 'P'; break; case Ion::Charset::MultiplicationSign: *c = '*'; break; - case Ion::Charset::MiddleDot: *c = '*'; break; + case KDCodePointMiddleDot: *c = '*'; break; case Ion::Charset::Sto: *c = '>'; break; case Poincare::Symbol::SpecialSymbols::UnknownX: *c = '?'; break; case Ion::Charset::LeftSuperscript: *c = '$'; break; @@ -75,13 +59,11 @@ void translate_in_ASCII_chars(char * expression) { } } } +*/ Expression parse_expression(const char * expression, bool canBeUnparsable) { quiz_print(expression); - char buffer[500]; - strlcpy(buffer, expression, sizeof(buffer)); - translate_in_special_chars(buffer); - Expression result = Expression::Parse(buffer); + Expression result = Expression::Parse(expression); if (!canBeUnparsable) { quiz_assert(!result.isUninitialized()); } @@ -138,7 +120,6 @@ void assert_parsed_expression_process_to(const char * expression, const char * r Expression m = process(e, globalContext, target, complexFormat, angleUnit); char buffer[500]; m.serialize(buffer, sizeof(buffer), DecimalMode, numberOfSignifiantDigits); - translate_in_ASCII_chars(buffer); #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "---- serialize to: " << buffer << " ----" << endl; cout << "----- compared to: " << result << " ----\n" << endl; @@ -214,7 +195,6 @@ void assert_parsed_expression_serialize_to(Expression expression, const char * s #endif char buffer[500]; expression.serialize(buffer, sizeof(buffer), mode, numberOfSignifiantDigits); - translate_in_ASCII_chars(buffer); quiz_assert(strcmp(buffer, serializedExpression) == 0); } @@ -237,7 +217,6 @@ void assert_expression_layout_serialize_to(Poincare::Layout layout, const char * constexpr int bufferSize = 255; char buffer[bufferSize]; layout.serializeForParsing(buffer, bufferSize); - translate_in_ASCII_chars(buffer); #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "---- Serialize: " << serialization << "----" << endl; cout << "---- serialized to: " << buffer << " ----" << endl; diff --git a/poincare/test/helper.h b/poincare/test/helper.h index de0205017..91c8bcb46 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -19,8 +19,6 @@ constexpr Poincare::Preferences::PrintFloatMode DecimalMode = Poincare::Preferen constexpr Poincare::Preferences::PrintFloatMode ScientificMode = Poincare::Preferences::PrintFloatMode::Scientific; bool expressions_are_equal(Poincare::Expression expected, Poincare::Expression got); -void translate_in_special_chars(char * expression); -void translate_in_ASCII_chars(char * expression); Poincare::Expression parse_expression(const char * expression, bool canBeUnparsable = false); Poincare::Expression parse_and_simplify(const char * expression); diff --git a/poincare/test/layouts.cpp b/poincare/test/layouts.cpp index 7e7172748..05886d548 100644 --- a/poincare/test/layouts.cpp +++ b/poincare/test/layouts.cpp @@ -265,11 +265,11 @@ QUIZ_CASE(poincare_parse_layouts) { // 2e^3 l = HorizontalLayout::Builder( CodePointLayout::Builder('2'), - CodePointLayout::Builder(Ion::Charset::Exponential), + CodePointLayout::Builder(KDCodePointScriptSmallE), VerticalOffsetLayout::Builder( CodePointLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Superscript)); - e = Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3)))); - assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3))))); + e = Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(KDCodePointScriptSmallE),Parenthesis::Builder(Rational::Builder(3)))); + assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(KDCodePointScriptSmallE),Parenthesis::Builder(Rational::Builder(3))))); assert_parsed_layout_is(l, e); } diff --git a/poincare/test/parser.cpp b/poincare/test/parser.cpp index 2c50972f9..762100eae 100644 --- a/poincare/test/parser.cpp +++ b/poincare/test/parser.cpp @@ -9,10 +9,7 @@ using namespace Poincare; void assert_tokenizes_as(const Token::Type * tokenTypes, const char * string) { - char buffer[500]; - strlcpy(buffer, string, sizeof(buffer)); - translate_in_special_chars(buffer); - Tokenizer tokenizer(buffer); + Tokenizer tokenizer(string); while (true) { Token token = tokenizer.popToken(); quiz_assert(token.type() == *tokenTypes); @@ -29,10 +26,7 @@ void assert_tokenizes_as_number(const char * string) { } void assert_tokenizes_as_undefined_token(const char * string) { - char buffer[500]; - strlcpy(buffer, string, sizeof(buffer)); - translate_in_special_chars(buffer); - Tokenizer tokenizer(buffer); + Tokenizer tokenizer(string); while (true) { Token token = tokenizer.popToken(); if (token.type() == Token::Undefined) { @@ -45,10 +39,7 @@ void assert_tokenizes_as_undefined_token(const char * string) { } void assert_raises_parsing_error(const char * text) { - char buffer[500]; - strlcpy(buffer, text, sizeof(buffer)); - translate_in_special_chars(buffer); - Parser p(buffer); + Parser p(text); Expression result = p.parse(); quiz_assert(p.getStatus() != Parser::Status::Success); } @@ -266,9 +257,9 @@ QUIZ_CASE(poincare_parser_symbols_and_functions) { // Reserved symbols assert_parsed_expression_is("ans", Symbol::Builder("ans", 3)); - assert_parsed_expression_is("I", Constant::Builder(Ion::Charset::IComplex)); - assert_parsed_expression_is("P", Constant::Builder(Ion::Charset::SmallPi)); - assert_parsed_expression_is("X", Constant::Builder(Ion::Charset::Exponential)); + assert_parsed_expression_is("I", Constant::Builder(KDCodePointMathematicalBoldSmallI)); + assert_parsed_expression_is("P", Constant::Builder(KDCodePointGreekSmallLetterPi)); + assert_parsed_expression_is("X", Constant::Builder(KDCodePointScriptSmallE)); assert_parsed_expression_is(Infinity::Name(), Infinity::Builder(false)); assert_parsed_expression_is(Undefined::Name(), Undefined::Builder()); @@ -373,7 +364,7 @@ QUIZ_CASE(poincare_parser_implicit_multiplication) { assert_parsed_expression_is("1ans", Multiplication::Builder(Rational::Builder(1),Symbol::Builder("ans", 3))); assert_parsed_expression_is("x1", Symbol::Builder("x1", 2)); assert_parsed_expression_is("1x+2", Addition::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)),Rational::Builder(2))); - assert_parsed_expression_is("1P", Multiplication::Builder(Rational::Builder(1),Constant::Builder(Ion::Charset::SmallPi))); + assert_parsed_expression_is("1P", Multiplication::Builder(Rational::Builder(1),Constant::Builder(KDCodePointGreekSmallLetterPi))); assert_parsed_expression_is("1x-2", Subtraction::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)),Rational::Builder(2))); assert_parsed_expression_is("-1x", Opposite::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)))); assert_parsed_expression_is("2*1x", Multiplication::Builder(Rational::Builder(2),Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)))); @@ -386,7 +377,7 @@ QUIZ_CASE(poincare_parser_implicit_multiplication) { assert_parsed_expression_is("sin(1)2", Multiplication::Builder(Sine::Builder(Rational::Builder(1)),Rational::Builder(2))); assert_parsed_expression_is("1cos(2)", Multiplication::Builder(Rational::Builder(1),Cosine::Builder(Rational::Builder(2)))); assert_parsed_expression_is("1!2", Multiplication::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); - assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3))))); + assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(KDCodePointScriptSmallE),Parenthesis::Builder(Rational::Builder(3))))); Expression m1[] = {Rational::Builder(1)}; Matrix M1 = BuildMatrix(1,1,m1); Expression m2[] = {Rational::Builder(2)}; Matrix M2 = BuildMatrix(1,1,m2); assert_parsed_expression_is("[[1]][[2]]", Multiplication::Builder(M1,M2)); From 41afa92f10f3e4364ffc87d919b0d88a449a1ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 16 Jan 2019 17:03:30 +0100 Subject: [PATCH 024/295] [unicode] Use the UTF8Decoder to scan const char * --- apps/shared/toolbox_helpers.cpp | 26 ++++++---- apps/solver/equation.cpp | 4 +- apps/variable_box_controller.cpp | 3 +- escher/src/text_area.cpp | 19 +++---- escher/src/text_field.cpp | 19 +++---- escher/src/text_input_helpers.cpp | 31 ++++++++--- kandinsky/Makefile | 1 + .../include/kandinsky/unicode/utf8_decoder.h | 3 ++ .../include/kandinsky/unicode/utf8_helper.h | 17 +++++++ kandinsky/src/unicode/utf8_decoder.cpp | 14 ++++- kandinsky/src/unicode/utf8_helper.cpp | 51 +++++++++++++++++++ poincare/src/expression.cpp | 4 +- poincare/src/layout_cursor.cpp | 38 ++++++++------ poincare/src/layout_helper.cpp | 24 +++++++-- 14 files changed, 189 insertions(+), 65 deletions(-) create mode 100644 kandinsky/include/kandinsky/unicode/utf8_helper.h create mode 100644 kandinsky/src/unicode/utf8_helper.cpp diff --git a/apps/shared/toolbox_helpers.cpp b/apps/shared/toolbox_helpers.cpp index 99c89cd3b..9952847f6 100644 --- a/apps/shared/toolbox_helpers.cpp +++ b/apps/shared/toolbox_helpers.cpp @@ -1,5 +1,6 @@ #include "toolbox_helpers.h" #include +#include #include #include @@ -7,17 +8,24 @@ namespace Shared { namespace ToolboxHelpers { int CursorIndexInCommandText(const char * text) { - // TODO LEA - size_t textLength = strlen(text); - for (size_t i = 0; i < textLength; i++) { - if (text[i] == '(' || text[i] == '\'') { - return i + 1; + UTF8Decoder decoder(text); + size_t index = 0; + const char * currentPointer = text; + const char * nextPointer = decoder.nextCodePointPointer(); + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != KDCodePointNull) { + if (codePoint == '(' || codePoint == '\'') { + return index + 1; } - if (text[i] == ']') { - return i; + if (codePoint == '[') { + return index; } + index+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); } - return textLength; + return index; } void TextToInsertForCommandMessage(I18n::Message message, char * buffer, int bufferSize, bool replaceArgsWithEmptyChar) { @@ -49,7 +57,7 @@ void TextToInsertForCommandText(const char * command, char * buffer, int bufferS buffer[currentNewTextIndex++] = command[i]; } else { if (replaceArgsWithEmptyChar && !argumentAlreadyReplaced) { - // TODO LEA buffer[currentNewTextIndex++] = Ion::Charset::Empty; + currentNewTextIndex += UTF8Decoder::CodePointToChars(KDCodePointEmpty, buffer + currentNewTextIndex, bufferSize - currentNewTextIndex); argumentAlreadyReplaced = true; } } diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp index 04edc3eb8..8f9d4aa7e 100644 --- a/apps/solver/equation.cpp +++ b/apps/solver/equation.cpp @@ -1,9 +1,9 @@ #include "equation.h" - #include #include #include #include +#include using namespace Poincare; @@ -50,7 +50,7 @@ Expression Equation::standardForm(Context * context) const { } bool Equation::containsIComplex() const { - return false; //TODO LEA strchr(text(), KDCodePointMathematicalBoldSmallI) != nullptr; + return UTF8Helper::CodePointSearch(text(), KDCodePointMathematicalBoldSmallI) != nullptr; } void Equation::tidyStandardForm() { diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index 54a6367c4..d4c2610e5 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using namespace Poincare; using namespace Shared; @@ -199,7 +200,7 @@ bool VariableBoxController::selectLeaf(int selectedRow) { assert(nameLength < nameToHandleMaxSize); nameToHandle[nameLength++] = '('; assert(nameLength < nameToHandleMaxSize); - // TODO LEA nameToHandle[nameLength++] = Ion::Charset::Empty; + nameLength+= UTF8Decoder::CodePointToChars(KDCodePointEmpty, nameToHandle+nameLength, nameToHandleMaxSize - nameLength); assert(nameLength < nameToHandleMaxSize); nameToHandle[nameLength++] = ')'; assert(nameLength < nameToHandleMaxSize); diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index e7bccb24d..408dd2dbf 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -24,21 +25,15 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for size_t cursorIndexInCommand = TextInputHelpers::CursorIndexInCommand(text); - size_t eventTextSize = min(strlen(text) + 1, TextField::maxBufferSize()); - char buffer[TextField::maxBufferSize()]; - size_t bufferIndex = 0; + constexpr int bufferSize = TextField::maxBufferSize(); + char buffer[bufferSize]; - // Remove EmptyChars - for (size_t i = bufferIndex; i < eventTextSize; i++) { - /* TODO LEA - if (text[i] != Ion::Charset::Empty) { - buffer[bufferIndex++] = text[i]; - } else if (i < cursorIndexInCommand) { - cursorIndexInCommand--; - } */ - } + // Remove the Empty code points + UTF8Helper::CopyAndRemoveCodePoint(buffer, bufferSize, text, KDCodePointEmpty, &cursorIndexInCommand); + // Insert the text if ((indentation && insertTextWithIndentation(buffer, cursorLocation())) || insertTextAtLocation(buffer, cursorLocation())) { + // Set the cursor location if (forceCursorRightOfText) { nextCursorLocation += strlen(buffer); } else { diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index 1a2190a42..00322a610 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include /* TextField::ContentView */ @@ -405,29 +406,21 @@ bool TextField::privateHandleMoveEvent(Ion::Events::Event event) { } bool TextField::handleEventWithText(const char * eventText, bool indentation, bool forceCursorRightOfText) { -//TODO LEA size_t previousTextLength = strlen(text()); - size_t eventTextLength = strlen(eventText); if (!isEditing()) { setEditing(true); } - if (eventTextLength == 0) { + if (eventText[0] == 0) { setCursorLocation(0); return m_delegate->textFieldDidHandleEvent(this, true, previousTextLength != 0); } - size_t eventTextSize = min(eventTextLength + 1, TextField::maxBufferSize()); - char buffer[TextField::maxBufferSize()]; - - int newBufferIndex = 0; - // Remove EmptyChars - /* TODO for (size_t i = 0; i < eventTextSize; i++) { - if (eventText[i] != Ion::Charset::Empty) { - buffer[newBufferIndex++] = eventText[i]; - } - }*/ + // Remove the Empty code points + constexpr int bufferSize = TextField::maxBufferSize(); + char buffer[bufferSize]; + UTF8Helper::CopyAndRemoveCodePoint(buffer, bufferSize, eventText, KDCodePointEmpty); int nextCursorLocation = draftTextLength(); if (insertTextAtLocation(buffer, cursorLocation())) { diff --git a/escher/src/text_input_helpers.cpp b/escher/src/text_input_helpers.cpp index c210c33a0..d6d39c632 100644 --- a/escher/src/text_input_helpers.cpp +++ b/escher/src/text_input_helpers.cpp @@ -1,18 +1,35 @@ #include +#include #include namespace TextInputHelpers { size_t CursorIndexInCommand(const char * text) { - // TODO LEA size_t index = 0; - while (text[index] != 0) { - if (text[index] == '\'' && text[index+1] == '\'') { - return index + 1; - } /* TODO else if (text[index] == Ion::Charset::Empty) { + UTF8Decoder decoder(text); + const char * currentPointer = text; + const char * nextPointer = decoder.nextCodePointPointer(); + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != KDCodePointNull) { + if (codePoint == KDCodePointEmpty) { return index; - }*/ - index++; + } + //TODO make sure changing empty / ' order was OK + if (codePoint == '\'') { + index+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); + if (codePoint == '\'') { + return index; + } + // Continue because we already incremented codePoint + continue; + } + index+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); } return index; } diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 368948437..74c6772e7 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -14,6 +14,7 @@ src += $(addprefix kandinsky/src/,\ point.cpp \ rect.cpp \ unicode/utf8_decoder.cpp\ + unicode/utf8_helper.cpp\ ) src += $(addprefix kandinsky/fonts/, \ diff --git a/kandinsky/include/kandinsky/unicode/utf8_decoder.h b/kandinsky/include/kandinsky/unicode/utf8_decoder.h index abb768d7f..1f52cccb4 100644 --- a/kandinsky/include/kandinsky/unicode/utf8_decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8_decoder.h @@ -18,7 +18,10 @@ class UTF8Decoder { public: UTF8Decoder(const char * string) : m_string(string) {} + /* TODO: Rename methods? nextCodePoint increases m_string but + * nextCodePointPointer does not */ CodePoint nextCodePoint(); + const char * nextCodePointPointer(); static size_t CharSizeOfCodePoint(CodePoint c); static size_t CodePointToChars(CodePoint c, char * buffer, int bufferSize); private: diff --git a/kandinsky/include/kandinsky/unicode/utf8_helper.h b/kandinsky/include/kandinsky/unicode/utf8_helper.h new file mode 100644 index 000000000..70f4edc2a --- /dev/null +++ b/kandinsky/include/kandinsky/unicode/utf8_helper.h @@ -0,0 +1,17 @@ +#ifndef KANDINSKY_UNICODE_UTF8_HELPER_H +#define KANDINSKY_UNICODE_UTF8_HELPER_H + +#include "code_point.h" +#include + +namespace UTF8Helper { + +const char * CodePointSearch(const char * s, CodePoint c); +/* CopyAndRemoveCodePoint copies src into dst while removing all code points c. + * It also updates an index that should be lower if code points where removed + * before it. */ +void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c, size_t * indexToDUpdate = nullptr); + +}; + +#endif diff --git a/kandinsky/src/unicode/utf8_decoder.cpp b/kandinsky/src/unicode/utf8_decoder.cpp index cb6f449d3..288fcea92 100644 --- a/kandinsky/src/unicode/utf8_decoder.cpp +++ b/kandinsky/src/unicode/utf8_decoder.cpp @@ -25,6 +25,10 @@ CodePoint UTF8Decoder::nextCodePoint() { return CodePoint(result); } +const char * UTF8Decoder::nextCodePointPointer() { + return m_string + leading_ones(*m_string); +} + size_t UTF8Decoder::CharSizeOfCodePoint(CodePoint c) { constexpr int bufferSize = CodePoint::MaxCodePointCharLength; char buffer[bufferSize]; @@ -32,21 +36,29 @@ size_t UTF8Decoder::CharSizeOfCodePoint(CodePoint c) { } size_t UTF8Decoder::CodePointToChars(CodePoint c, char * buffer, int bufferSize) { - assert(bufferSize >= CodePoint::MaxCodePointCharLength); + if (bufferSize <= 0) { + return 0; + } size_t i = 0; if (c <= 0x7F) { buffer[i++] = c; } else if (c <= 0x7FF) { buffer[i++] = 0b11000000 | (c >> 6); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | (c & 0b111111); } else if (c <= 0xFFFF) { buffer[i++] = 0b11100000 | (c >> 12); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | ((c >> 6) & 0b111111); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | (c & 0b111111); } else { buffer[i++] = 0b11110000 | (c >> 18); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | ((c >> 12) & 0b111111); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | ((c >> 6) & 0b111111); + if (bufferSize <= i) { return i; } buffer[i++] = 0b10000000 | (c & 0b111111); } return i; diff --git a/kandinsky/src/unicode/utf8_helper.cpp b/kandinsky/src/unicode/utf8_helper.cpp new file mode 100644 index 000000000..3293e14fd --- /dev/null +++ b/kandinsky/src/unicode/utf8_helper.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include + +namespace UTF8Helper { + +static inline int min(int x, int y) { return x < y ? x : y; } + +const char * CodePointSearch(const char * s, CodePoint c) { + UTF8Decoder decoder(s); + const char * currentPointer = s; + const char * nextPointer = decoder.nextCodePointPointer(); + CodePoint codePoint = decoder.nextCodePoint(); + while (codePoint != KDCodePointNull && codePoint != c) { + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); + } + if (codePoint == c) { + return currentPointer; + } + return nullptr; +} + +void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c, size_t * indexToUpdate) { + UTF8Decoder decoder(src); + const char * currentPointer = src; + const char * nextPointer = decoder.nextCodePointPointer(); + const char * maxPointer = src + strlen(src) + 1; + CodePoint codePoint = decoder.nextCodePoint(); + size_t bufferIndex = 0; + size_t codePointCharSize = UTF8Decoder::CharSizeOfCodePoint(c); + + // Remove CodePoint c + while (currentPointer < maxPointer && bufferIndex < dstSize) { + if (codePoint != c) { + int copySize = min(nextPointer - currentPointer, dstSize - bufferIndex); + memcpy(dst + bufferIndex, currentPointer, copySize); + bufferIndex+= copySize; + } else if (indexToUpdate != nullptr && currentPointer - src < *indexToUpdate) { + assert(*indexToUpdate >= codePointCharSize); + *indexToUpdate-= codePointCharSize; + } + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); + } +} + +}; diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index b08f8e3ff..044e6f9b5 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -342,10 +343,9 @@ void Expression::SetEncounteredComplex(bool encounterComplex) { } Preferences::ComplexFormat Expression::UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput) { - /* TODO LEA if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, KDCodePointMathematicalBoldSmallI) != nullptr) { + if (complexFormat == Preferences::ComplexFormat::Real && UTF8Helper::CodePointSearch(textInput, KDCodePointMathematicalBoldSmallI) != nullptr) { return Preferences::ComplexFormat::Cartesian; } - */ return complexFormat; } diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index 9c29bd146..bdbfcb44b 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Poincare { @@ -136,47 +137,54 @@ void LayoutCursor::addXNTCodePointLayout() { } void LayoutCursor::insertText(const char * text) { -// TODO LEA -#if 0 - int textLength = strlen(text); - if (textLength <= 0) { - return; - } Layout newChild; Layout pointedChild; - for (int i = 0; i < textLength; i++) { - if (text[i] == //TODO Ion::Charset::Empty) { + UTF8Decoder decoder(text); + CodePoint codePoint = decoder.nextCodePoint(); + if (codePoint == KDCodePointNull) { + return; + } + assert(!codePoint.isCombining()); + while (codePoint != KDCodePointNull) { + if (codePoint == KDCodePointEmpty) { + codePoint = decoder.nextCodePoint(); + assert(!codePoint.isCombining()); continue; } - if (text[i] == //TODO Ion::Charset::MultiplicationSign) { + if (codePoint == KDCodePointMultiplicationSign) { newChild = CodePointLayout::Builder(KDCodePointMiddleDot); - } else*/ if (text[i] == '(') { + } else if (codePoint == '(') { newChild = LeftParenthesisLayout::Builder(); if (pointedChild.isUninitialized()) { pointedChild = newChild; } - } else if (text[i] == ')') { + } else if (codePoint == ')') { newChild = RightParenthesisLayout::Builder(); } /* We never insert text with brackets for now. Removing this code saves the * binary file 2K. */ #if 0 - else if (text[i] == '[') { + else if (codePoint == '[') { newChild = LeftSquareBracketLayout(); - } else if (text[i] == ']') { + } else if (codePoint == ']') { newChild = RightSquareBracketLayout(); } #endif else { - newChild = CodePointLayout::Builder(text[i]); + newChild = CodePointLayout::Builder(codePoint); } m_layout.addSibling(this, newChild, true); + + // Get the next code point + codePoint = decoder.nextCodePoint(); + while (codePoint.isCombining()) { + codePoint = decoder.nextCodePoint(); + } } if (!pointedChild.isUninitialized() && !pointedChild.parent().isUninitialized()) { m_layout = pointedChild; m_position = Position::Right; } -#endif } void LayoutCursor::addLayoutAndMoveCursor(Layout l) { diff --git a/poincare/src/layout_helper.cpp b/poincare/src/layout_helper.cpp index 9555eab3e..fd7919243 100644 --- a/poincare/src/layout_helper.cpp +++ b/poincare/src/layout_helper.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace Poincare { @@ -56,9 +57,26 @@ Layout LayoutHelper::Parentheses(Layout layout, bool cloneLayout) { HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const KDFont * font) { assert(bufferLen > 0); HorizontalLayout resultLayout = HorizontalLayout::Builder(); - /* TODO LEA */ - for (int i = 0; i < bufferLen; i++) { - resultLayout.addChildAtIndex(CodePointLayout::Builder(buffer[i], font), i, i, nullptr); + UTF8Decoder decoder(buffer); + const char * currentPointer = buffer; + const char * nextPointer = decoder.nextCodePointPointer(); + CodePoint codePoint = decoder.nextCodePoint(); + assert(!codePoint.isCombining()); + int layoutIndex = 0; + int bufferIndex = 0; + while (codePoint != KDCodePointNull && bufferIndex < bufferLen) { + resultLayout.addChildAtIndex(CodePointLayout::Builder(codePoint, font), layoutIndex, layoutIndex, nullptr); + layoutIndex++; + bufferIndex+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); + while (codePoint.isCombining()) { + bufferIndex+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); + codePoint = decoder.nextCodePoint(); + } } return resultLayout; } From f4e887309ded2f8cd9c7bbf31183d0e829345a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 17 Jan 2019 15:35:43 +0100 Subject: [PATCH 025/295] [parsing] Fix constant code point parsing --- poincare/src/parsing/parser.cpp | 3 ++- poincare/src/parsing/token.h | 5 ++++- poincare/src/parsing/tokenizer.cpp | 7 ++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index 0925f4f4a..4dee65c2f 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -1,4 +1,5 @@ #include "parser.h" +#include namespace Poincare { @@ -323,7 +324,7 @@ bool Parser::currentTokenIsSpecialIdentifier() const { } void Parser::parseConstant(Expression & leftHandSide, Token::Type stoppingType) { - leftHandSide = Constant::Builder(m_currentToken.text()[0]); + leftHandSide = Constant::Builder(m_currentToken.codePoint()); isThereImplicitMultiplication(); } diff --git a/poincare/src/parsing/token.h b/poincare/src/parsing/token.h index ea1eff7ab..83079df49 100644 --- a/poincare/src/parsing/token.h +++ b/poincare/src/parsing/token.h @@ -53,7 +53,7 @@ public: Undefined }; - Token(Type type) : m_type(type), m_text(0) {}; + Token(Type type) : m_type(type), m_text(0), m_codePoint(KDCodePointNull) {}; Type type() const { return m_type; } bool is(Type t) const { return m_type == t; } @@ -62,12 +62,14 @@ public: Expression expression() const { return m_expression; } const char * text() const { return m_text; } size_t length() const { return m_length; } + CodePoint codePoint() const { return m_codePoint; } void setExpression(Expression e) { m_expression = e; } void setString(const char * text, size_t length) { m_text = text; m_length = length; } + void setCodePoint(CodePoint c) { m_codePoint = c; } static int CompareNonNullTerminatedName(const char * nonNullTerminatedName, size_t nonNullTerminatedNameLength, const char * nullTerminatedName) { /* Compare m_text to name, similarly to strcmp, assuming * - m_text is not null-terminated @@ -84,6 +86,7 @@ private: Expression m_expression; const char * m_text; size_t m_length; + CodePoint m_codePoint; }; } diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index f5143f235..3ec666548 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -19,7 +19,7 @@ const CodePoint Tokenizer::nextCodePoint(PopTest popTest, CodePoint context, boo if (firstCodePoint != KDCodePointNull) { CodePoint codePoint = decoder.nextCodePoint(); while (codePoint.isCombining()) { - numberOfBytesForCodePoint = UTF8Decoder::CharSizeOfCodePoint(codePoint); + numberOfBytesForCodePoint+= UTF8Decoder::CharSizeOfCodePoint(codePoint); codePoint = decoder.nextCodePoint(); } } @@ -175,12 +175,13 @@ Token Tokenizer::popToken() { || c == KDCodePointScriptSmallE) { Token result(Token::Constant); - result.setString(start, 1); + result.setCodePoint(c); return result; } if (c == KDCodePointSquareRoot) { Token result(Token::Identifier); - result.setString(start, 1); + // TODO compute size manually? + result.setString(start, UTF8Decoder::CharSizeOfCodePoint(KDCodePointSquareRoot)); return result; } if (c == KDCodePointEmpty) { From f304ad854f9d35513a4db7b4782f297b3a616c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 17 Jan 2019 17:14:34 +0100 Subject: [PATCH 026/295] [kandinsky] Remove obsolete values --- kandinsky/fonts/unicode_for_symbol.c | 3 --- kandinsky/fonts/unicode_for_symbol.h | 10 ---------- kandinsky/include/kandinsky/font.h | 2 -- 3 files changed, 15 deletions(-) delete mode 100644 kandinsky/fonts/unicode_for_symbol.c delete mode 100644 kandinsky/fonts/unicode_for_symbol.h diff --git a/kandinsky/fonts/unicode_for_symbol.c b/kandinsky/fonts/unicode_for_symbol.c deleted file mode 100644 index 9a1ad769e..000000000 --- a/kandinsky/fonts/unicode_for_symbol.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "unicode_for_symbol.h" - -wchar_t codePointForSymbol[NUMBER_OF_SYMBOLS] = {0x222b, 0x0078, 0x0305, 0x0079, 0x0305, 0x0393, 0x0394, 0x03a3, 0x03b8, 0x03bb, 0x03bc, 0x03c0, 0x03c3, 0x0456, 0x1D07, 0x2032, 0x212e, 0x2192, 0x221A, 0x2264, 0x2265, 0x00D7, 0x00B7, 0x2248, 0x00B0}; diff --git a/kandinsky/fonts/unicode_for_symbol.h b/kandinsky/fonts/unicode_for_symbol.h deleted file mode 100644 index 1f1476c9d..000000000 --- a/kandinsky/fonts/unicode_for_symbol.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef KANDINSKY_FONT_UNICODE_H -#define KANDINSKY_FONT_UNICODE_H - -#include - -#define NUMBER_OF_SYMBOLS 25 - -extern wchar_t codePointForSymbol[NUMBER_OF_SYMBOLS]; - -#endif diff --git a/kandinsky/include/kandinsky/font.h b/kandinsky/include/kandinsky/font.h index 4af33758c..1e5c296be 100644 --- a/kandinsky/include/kandinsky/font.h +++ b/kandinsky/include/kandinsky/font.h @@ -87,8 +87,6 @@ private: KDSize m_glyphSize; const uint16_t * m_glyphDataOffset; const uint8_t * m_data; - static constexpr uint8_t k_magicCharOffsetValue = 0x20; // FIXME: Value from kandinsky/fonts/rasterizer.c (CHARACTER_RANGE_START). 0x20 because we do not want have a glyph for the first 20 ASCII characters - static constexpr uint8_t k_numberOfGlyphs = 120; // FIXME: Value from kandinsky/fonts/rasterizer.c (GLYPH_COUNT) }; #endif From 4bd2f6b1561bc35c033be5269418d1c26d0b2898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 17 Jan 2019 17:15:48 +0100 Subject: [PATCH 027/295] [kandinsky] Fix CodePoint uint32_t cast operator --- kandinsky/include/kandinsky/unicode/code_point.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kandinsky/include/kandinsky/unicode/code_point.h b/kandinsky/include/kandinsky/unicode/code_point.h index e3648384d..7e885d028 100644 --- a/kandinsky/include/kandinsky/unicode/code_point.h +++ b/kandinsky/include/kandinsky/unicode/code_point.h @@ -7,7 +7,7 @@ class CodePoint { public: constexpr static int MaxCodePointCharLength = sizeof(uint32_t) / sizeof(char); constexpr CodePoint(uint32_t c) : m_code(c) {} - constexpr operator uint16_t() const { return m_code; } + constexpr operator uint32_t() const { return m_code; } bool isCombining() const { return (m_code >= 0x300 && m_code <= 0x036F); From 501aa01fe787ced432950e6d2197471d4ba1e749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 17 Jan 2019 17:28:01 +0100 Subject: [PATCH 028/295] [kandinsky] Remove obsolete calls to files --- kandinsky/Makefile | 2 +- kandinsky/fonts/rasterizer.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 74c6772e7..65d536af8 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -41,7 +41,7 @@ endif $(eval $(call rule_for, \ HOSTCC, \ kandinsky/fonts/rasterizer, \ - kandinsky/fonts/rasterizer.c kandinsky/fonts/unicode_for_symbol.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ + kandinsky/fonts/rasterizer.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ $$(HOSTCC) $$(RASTERIZER_CFLAGS) $$^ $$(RASTERIZER_LDFLAGS) -o $$@ \ )) diff --git a/kandinsky/fonts/rasterizer.c b/kandinsky/fonts/rasterizer.c index 1990ba56c..708d027ca 100644 --- a/kandinsky/fonts/rasterizer.c +++ b/kandinsky/fonts/rasterizer.c @@ -15,7 +15,6 @@ #include #include FT_FREETYPE_H -#include "unicode_for_symbol.h" #include "code_points.h" #include "../../ion/src/external/lz4/lz4hc.h" From 28545869beaba215f352450d823ba67f4590ad2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 17 Jan 2019 17:43:27 +0100 Subject: [PATCH 029/295] [kandinsky] Use new unicodes in fonts for Exponential, mu and i complex --- kandinsky/fonts/LargeSourcePixel.ttf | Bin 219468 -> 220768 bytes kandinsky/fonts/SmallSourcePixel.ttf | Bin 213592 -> 214268 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/kandinsky/fonts/LargeSourcePixel.ttf b/kandinsky/fonts/LargeSourcePixel.ttf index 9def301b4e5a05efc3c4589701a79058dc70cc81..6271613a06a8cebfa01146cd1be4651b8d5b267c 100644 GIT binary patch literal 220768 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

dG3Fn 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 Nip;+eW?1J~{}&yWxn2MO literal 219468 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

+3aHWQ14L+dB5Q$f(Oz%(%e>5^Ppx82lBl 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≪+K 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 LNdK`4lhpqL32ip9 diff --git a/kandinsky/fonts/SmallSourcePixel.ttf b/kandinsky/fonts/SmallSourcePixel.ttf index 766d3ff79a8d10d1011256c95ce94757e971c323..24a6760a83ac995a2360b5b31f9b4071fdcb91b4 100644 GIT binary patch literal 214268 zcmce<31D2+c`rWa&S*v&jb>l(+}ZbCvuj^%Nw#Ep8QU0d@x~G&j3I^)5-1QzQc5-+ z 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±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

u@7tl*xy%~ZxwdfQtO!SrC?|K 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 DCr2EJ From d57e28bdc737d44d46727602ec8fdc2372748546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 09:41:07 +0100 Subject: [PATCH 030/295] [poincare] Clean parsing with unicodes --- poincare/src/parsing/parser.cpp | 23 ++++++++++++++--------- poincare/src/parsing/tokenizer.cpp | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index 4dee65c2f..308c507f5 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -361,12 +361,14 @@ void Parser::parseSequence(Expression & leftHandSide, const char name, Token::Ty if (m_status != Status::Progress) { } else if (!popTokenIfType(rightDelimiter)) { m_status = Status::Error; // Right delimiter missing. - } else if (rank.isIdenticalTo(Symbol::Builder("n",1))) { - char sym[5] = {name, '(', 'n', ')', 0}; - leftHandSide = Symbol::Builder(sym, 4); - } else if (rank.isIdenticalTo(Addition::Builder(Symbol::Builder("n",1),Rational::Builder("1")))) { - char sym[7] = {name, '(', 'n', '+', '1', ')', 0}; - leftHandSide = Symbol::Builder(sym, 6); + } else if (rank.isIdenticalTo(Symbol::Builder('n'))) { + constexpr int symbolNameSize = 5; + char sym[symbolNameSize] = {name, '(', 'n', ')', 0}; + leftHandSide = Symbol::Builder(sym, symbolNameSize); + } else if (rank.isIdenticalTo(Addition::Builder(Symbol::Builder('n'), Rational::Builder("1")))) { + constexpr int symbolNameSize = 7; + char sym[symbolNameSize] = {name, '(', 'n', '+', '1', ')', 0}; + leftHandSide = Symbol::Builder(sym, symbolNameSize); } else { m_status = Status::Error; // Unexpected parameter. } @@ -383,8 +385,12 @@ void Parser::parseSpecialIdentifier(Expression & leftHandSide) { } else if (m_currentToken.compareTo(Unreal::Name()) == 0) { leftHandSide = Unreal::Builder(); } else if (m_currentToken.compareTo("u_") == 0 || m_currentToken.compareTo("v_") == 0) { // Special case for sequences (e.g. "u_{n}") + /* We now that m_currentToken.text()[0] is either 'u' or 'v', so we do not + * need to pass a code point to parseSequence. */ parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftBrace, Token::RightBrace); } else if (m_currentToken.compareTo("u") == 0 || m_currentToken.compareTo("v") == 0) { // Special case for sequences (e.g. "u(n)") + /* We now that m_currentToken.text()[0] is either 'u' or 'v', so we do not + * need to pass a code point to parseSequence. */ parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftParenthesis, Token::RightParenthesis); } else if (m_currentToken.compareTo("log_") == 0) { // Special case for the log function (e.g. "log_{2}(8)") if (!popTokenIfType(Token::LeftBrace)) { @@ -426,7 +432,7 @@ void Parser::parseCustomIdentifier(Expression & leftHandSide, const char * name, return; } parameter = parameter.childAtIndex(0); - if (parameter.type() == ExpressionNode::Type::Symbol && strncmp(static_cast(parameter).name(),name, length) == 0) { + if (parameter.type() == ExpressionNode::Type::Symbol && strncmp(static_cast(parameter).name(), name, length) == 0) { m_status = Status::Error; // Function and variable must have distinct names. } else if (!popTokenIfType(Token::RightParenthesis)) { m_status = Status::Error; // Right parenthesis missing. @@ -487,8 +493,7 @@ void Parser::parseMatrix(Expression & leftHandSide, Token::Type stoppingType) { return; } if ((numberOfRows == 0 && (numberOfColumns = row.numberOfChildren()) == 0) - || - (numberOfColumns != row.numberOfChildren())) { + || (numberOfColumns != row.numberOfChildren())) { m_status = Status::Error; // Incorrect matrix. return; } else { diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index 3ec666548..d1812f48d 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -14,16 +14,22 @@ static inline bool isDigit(const CodePoint c) { const CodePoint Tokenizer::nextCodePoint(PopTest popTest, CodePoint context, bool * testResult) { UTF8Decoder decoder(m_text); + const char * currentPointer = m_text; + const char * nextPointer = decoder.nextCodePointPointer(); CodePoint firstCodePoint = decoder.nextCodePoint(); - size_t numberOfBytesForCodePoint = UTF8Decoder::CharSizeOfCodePoint(firstCodePoint); + size_t numberOfBytesForCodePoint = nextPointer - currentPointer; if (firstCodePoint != KDCodePointNull) { + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); while (codePoint.isCombining()) { - numberOfBytesForCodePoint+= UTF8Decoder::CharSizeOfCodePoint(codePoint); + numberOfBytesForCodePoint+= nextPointer - currentPointer; + currentPointer = nextPointer; + nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); } } - // TODO handle combined code points? + // TODO handle combined code points? For now the combining codepoints get dropped. bool shouldPop = popTest(firstCodePoint, context); if (testResult != nullptr) { *testResult = shouldPop; @@ -181,7 +187,9 @@ Token Tokenizer::popToken() { if (c == KDCodePointSquareRoot) { Token result(Token::Identifier); // TODO compute size manually? - result.setString(start, UTF8Decoder::CharSizeOfCodePoint(KDCodePointSquareRoot)); + constexpr int squareRootCharLength = 3; + assert(UTF8Decoder::CharSizeOfCodePoint(KDCodePointSquareRoot) == squareRootCharLength); + result.setString(start, squareRootCharLength); return result; } if (c == KDCodePointEmpty) { From 948f62ddffb0abd89ac5347e328c9097abe0f00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 10:07:16 +0100 Subject: [PATCH 031/295] [poincare/serialization_helper] Clean CodePoint --- poincare/src/serialization_helper.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index 953013948..71cb9dc39 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -158,12 +158,10 @@ int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoin buffer[0] = 0; return 0; } - char helpBuffer[MaxSerializedCodePointSize]; - size_t size = UTF8Decoder::CodePointToChars(c, helpBuffer, MaxSerializedCodePointSize); - assert(size < MaxSerializedCodePointSize); - helpBuffer[size] = 0; - strlcpy(buffer, helpBuffer, bufferSize); - return strlen(buffer); + size_t size = UTF8Decoder::CodePointToChars(c, buffer, bufferSize); + int nullTerminatingIndex = min(size, bufferSize - 1); + buffer[nullTerminatingIndex] = 0; + return nullTerminatingIndex; } } From fdfb568805857144c722952af3c0cafdea254eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 09:58:24 +0100 Subject: [PATCH 032/295] [poincare/serialization_helper] Clean --- .../include/poincare/serialization_helper.h | 7 +- poincare/src/constant.cpp | 2 +- poincare/src/serialization_helper.cpp | 110 +++++++++++------- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/poincare/include/poincare/serialization_helper.h b/poincare/include/poincare/serialization_helper.h index e0ed71193..061e587ce 100644 --- a/poincare/include/poincare/serialization_helper.h +++ b/poincare/include/poincare/serialization_helper.h @@ -6,6 +6,12 @@ namespace Poincare { +/* The serialization methods write their argument as a string in the given + * buffer, with a null-terminating 0. The return value is: + * -> -1 if the buffer size is 0 + * -> Otherwise, the number of chars written, without the null terminating 0 + */ + namespace SerializationHelper { // SerializableReference to text int Infix( @@ -30,7 +36,6 @@ namespace SerializationHelper { // Write one char in a buffer int Char(char * buffer, int bufferSize, char c); // Write one code point in a buffer - constexpr int MaxSerializedCodePointSize = CodePoint::MaxCodePointCharLength + 1; // Null-terminating char int CodePoint(char * buffer, int bufferSize, CodePoint c); }; diff --git a/poincare/src/constant.cpp b/poincare/src/constant.cpp index 788cf5135..329babeda 100644 --- a/poincare/src/constant.cpp +++ b/poincare/src/constant.cpp @@ -93,7 +93,7 @@ bool ConstantNode::isConstantCodePoint(CodePoint c) const { } Constant Constant::Builder(CodePoint c) { - constexpr int bufferSize = SerializationHelper::MaxSerializedCodePointSize; + constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1; char buffer[bufferSize]; size_t codePointSize = SerializationHelper::CodePoint(buffer, bufferSize, c); return SymbolAbstract::Builder(buffer, codePointSize); diff --git a/poincare/src/serialization_helper.cpp b/poincare/src/serialization_helper.cpp index 71cb9dc39..f77ddd247 100644 --- a/poincare/src/serialization_helper.cpp +++ b/poincare/src/serialization_helper.cpp @@ -5,24 +5,57 @@ namespace Poincare { -static void serializeChild(const TreeNode * childNode, const TreeNode * parentNode, char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfDigits, int * numberOfChar) { +static inline int min(int x, int y) { return x < y ? x : y; } + +static bool checkBufferSize(char * buffer, int bufferSize, int * result) { + // If buffer has size 0 or 1, put a zero if it fits and return + if (bufferSize == 0) { + *result = -1; + return true; + } + + buffer[bufferSize-1] = 0; // Null-terminate the buffer + if (bufferSize == 1) { + *result = 0; + return true; + } + return false; +} + +static int serializeChild( + const TreeNode * childNode, + const TreeNode * parentNode, + char * buffer, + int bufferSize, + Preferences::PrintFloatMode floatDisplayMode, + int numberOfDigits) +{ + { + int result = 0; + if (checkBufferSize(buffer, bufferSize, &result)) { + return result; + } + } + + int numberOfChar = 0; // Write the child with parentheses if needed bool addParentheses = parentNode->childNeedsParenthesis(childNode); if (addParentheses) { - buffer[*numberOfChar] = '('; - *numberOfChar = *numberOfChar + 1; - if (*numberOfChar >= bufferSize-1) { - return; + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { + return bufferSize-1; } } - *numberOfChar += childNode->serialize(buffer + *numberOfChar, bufferSize - *numberOfChar, floatDisplayMode, numberOfDigits); - if (*numberOfChar >= bufferSize-1) { - return; + numberOfChar+= childNode->serialize(buffer + numberOfChar, bufferSize - numberOfChar, floatDisplayMode, numberOfDigits); + if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); + return bufferSize-1; } if (addParentheses) { - buffer[*numberOfChar] = ')'; - *numberOfChar = *numberOfChar + 1; + buffer[numberOfChar++] = ')'; } + buffer[numberOfChar] = 0; + return numberOfChar; } int SerializationHelper::Infix( @@ -35,14 +68,11 @@ int SerializationHelper::Infix( int firstChildIndex, int lastChildIndex) { - // If buffer has size 0 or 1, put a zero if it fits and return - if (bufferSize == 0) { - return -1; - } - - buffer[bufferSize-1] = 0; // Null-terminate the buffer - if (bufferSize == 1) { - return 0; + { + int result = 0; + if (checkBufferSize(buffer, bufferSize, &result)) { + return result; + } } // Get some information on the node @@ -51,8 +81,9 @@ int SerializationHelper::Infix( assert(numberOfChildren > 0); // Write the first child, with parentheses if needed - serializeChild(node->childAtIndex(firstChildIndex), node, buffer, bufferSize, floatDisplayMode, numberOfDigits, &numberOfChar); + numberOfChar+= serializeChild(node->childAtIndex(firstChildIndex), node, buffer + numberOfChar, bufferSize - numberOfChar, floatDisplayMode, numberOfDigits); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } // For all remaining children: @@ -61,11 +92,13 @@ int SerializationHelper::Infix( // Write the operator numberOfChar += strlcpy(buffer+numberOfChar, operatorName, bufferSize-numberOfChar); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } // Write the child, with parentheses if needed - serializeChild(node->childAtIndex(i), node, buffer, bufferSize, floatDisplayMode, numberOfDigits, &numberOfChar); + numberOfChar+= serializeChild(node->childAtIndex(i), node, buffer + numberOfChar, bufferSize - numberOfChar, floatDisplayMode, numberOfDigits); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } } @@ -84,18 +117,17 @@ int SerializationHelper::Prefix( const char * operatorName, bool writeFirstChild) { - // If buffer has size 0 or 1, put a zero if it fits and return - if (bufferSize == 0) { - return -1; - } - buffer[bufferSize-1] = 0; - if (bufferSize == 1) { - return 0; + { + int result = 0; + if (checkBufferSize(buffer, bufferSize, &result)) { + return result; + } } // Copy the operator name int numberOfChar = strlcpy(buffer, operatorName, bufferSize); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } @@ -115,6 +147,7 @@ int SerializationHelper::Prefix( // Write the first child numberOfChar += node->childAtIndex(firstChildIndex)->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfDigits); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } @@ -126,6 +159,7 @@ int SerializationHelper::Prefix( } numberOfChar += node->childAtIndex(i)->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfDigits); if (numberOfChar >= bufferSize-1) { + assert(buffer[bufferSize - 1] == 0); return bufferSize-1; } } @@ -138,12 +172,11 @@ int SerializationHelper::Prefix( } int SerializationHelper::Char(char * buffer, int bufferSize, char c) { - if (bufferSize == 0) { - return -1; - } - if (bufferSize == 1) { - buffer[0] = 0; - return 0; + { + int result = 0; + if (checkBufferSize(buffer, bufferSize, &result)) { + return result; + } } buffer[0] = c; buffer[1] = 0; @@ -151,12 +184,11 @@ int SerializationHelper::Char(char * buffer, int bufferSize, char c) { } int SerializationHelper::CodePoint(char * buffer, int bufferSize, class CodePoint c) { - if (bufferSize == 0) { - return -1; - } - if (bufferSize == 1) { - buffer[0] = 0; - return 0; + { + int result = 0; + if (checkBufferSize(buffer, bufferSize, &result)) { + return result; + } } size_t size = UTF8Decoder::CodePointToChars(c, buffer, bufferSize); int nullTerminatingIndex = min(size, bufferSize - 1); From 7a1fd686265f04ad860922bc8e068a1ddf301025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 10:29:10 +0100 Subject: [PATCH 033/295] [poincare/utf8_decoder] nextCodePointPointer is now stringPosition --- apps/shared/toolbox_helpers.cpp | 4 ++-- escher/src/text_input_helpers.cpp | 6 +++--- kandinsky/include/kandinsky/unicode/utf8_decoder.h | 4 +--- kandinsky/src/unicode/utf8_decoder.cpp | 4 ---- kandinsky/src/unicode/utf8_helper.cpp | 8 ++++---- poincare/src/layout_helper.cpp | 6 +++--- poincare/src/parsing/tokenizer.cpp | 6 +++--- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/apps/shared/toolbox_helpers.cpp b/apps/shared/toolbox_helpers.cpp index 9952847f6..10fb4a95b 100644 --- a/apps/shared/toolbox_helpers.cpp +++ b/apps/shared/toolbox_helpers.cpp @@ -11,8 +11,8 @@ int CursorIndexInCommandText(const char * text) { UTF8Decoder decoder(text); size_t index = 0; const char * currentPointer = text; - const char * nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); while (codePoint != KDCodePointNull) { if (codePoint == '(' || codePoint == '\'') { return index + 1; @@ -22,8 +22,8 @@ int CursorIndexInCommandText(const char * text) { } index+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } return index; } diff --git a/escher/src/text_input_helpers.cpp b/escher/src/text_input_helpers.cpp index d6d39c632..5a653c3d3 100644 --- a/escher/src/text_input_helpers.cpp +++ b/escher/src/text_input_helpers.cpp @@ -8,8 +8,8 @@ size_t CursorIndexInCommand(const char * text) { size_t index = 0; UTF8Decoder decoder(text); const char * currentPointer = text; - const char * nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); while (codePoint != KDCodePointNull) { if (codePoint == KDCodePointEmpty) { return index; @@ -18,8 +18,8 @@ size_t CursorIndexInCommand(const char * text) { if (codePoint == '\'') { index+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); if (codePoint == '\'') { return index; } @@ -28,8 +28,8 @@ size_t CursorIndexInCommand(const char * text) { } index+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } return index; } diff --git a/kandinsky/include/kandinsky/unicode/utf8_decoder.h b/kandinsky/include/kandinsky/unicode/utf8_decoder.h index 1f52cccb4..084765b9f 100644 --- a/kandinsky/include/kandinsky/unicode/utf8_decoder.h +++ b/kandinsky/include/kandinsky/unicode/utf8_decoder.h @@ -18,10 +18,8 @@ class UTF8Decoder { public: UTF8Decoder(const char * string) : m_string(string) {} - /* TODO: Rename methods? nextCodePoint increases m_string but - * nextCodePointPointer does not */ CodePoint nextCodePoint(); - const char * nextCodePointPointer(); + const char * stringPosition() const { return m_string; } static size_t CharSizeOfCodePoint(CodePoint c); static size_t CodePointToChars(CodePoint c, char * buffer, int bufferSize); private: diff --git a/kandinsky/src/unicode/utf8_decoder.cpp b/kandinsky/src/unicode/utf8_decoder.cpp index 288fcea92..5b23cbdb2 100644 --- a/kandinsky/src/unicode/utf8_decoder.cpp +++ b/kandinsky/src/unicode/utf8_decoder.cpp @@ -25,10 +25,6 @@ CodePoint UTF8Decoder::nextCodePoint() { return CodePoint(result); } -const char * UTF8Decoder::nextCodePointPointer() { - return m_string + leading_ones(*m_string); -} - size_t UTF8Decoder::CharSizeOfCodePoint(CodePoint c) { constexpr int bufferSize = CodePoint::MaxCodePointCharLength; char buffer[bufferSize]; diff --git a/kandinsky/src/unicode/utf8_helper.cpp b/kandinsky/src/unicode/utf8_helper.cpp index 3293e14fd..da2d47f2b 100644 --- a/kandinsky/src/unicode/utf8_helper.cpp +++ b/kandinsky/src/unicode/utf8_helper.cpp @@ -10,12 +10,12 @@ static inline int min(int x, int y) { return x < y ? x : y; } const char * CodePointSearch(const char * s, CodePoint c) { UTF8Decoder decoder(s); const char * currentPointer = s; - const char * nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); while (codePoint != KDCodePointNull && codePoint != c) { currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } if (codePoint == c) { return currentPointer; @@ -26,9 +26,9 @@ const char * CodePointSearch(const char * s, CodePoint c) { void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePoint c, size_t * indexToUpdate) { UTF8Decoder decoder(src); const char * currentPointer = src; - const char * nextPointer = decoder.nextCodePointPointer(); const char * maxPointer = src + strlen(src) + 1; CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); size_t bufferIndex = 0; size_t codePointCharSize = UTF8Decoder::CharSizeOfCodePoint(c); @@ -43,8 +43,8 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo *indexToUpdate-= codePointCharSize; } currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } } diff --git a/poincare/src/layout_helper.cpp b/poincare/src/layout_helper.cpp index fd7919243..2b41a2b45 100644 --- a/poincare/src/layout_helper.cpp +++ b/poincare/src/layout_helper.cpp @@ -59,8 +59,8 @@ HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const HorizontalLayout resultLayout = HorizontalLayout::Builder(); UTF8Decoder decoder(buffer); const char * currentPointer = buffer; - const char * nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); assert(!codePoint.isCombining()); int layoutIndex = 0; int bufferIndex = 0; @@ -69,13 +69,13 @@ HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const layoutIndex++; bufferIndex+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); while (codePoint.isCombining()) { bufferIndex+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } } return resultLayout; diff --git a/poincare/src/parsing/tokenizer.cpp b/poincare/src/parsing/tokenizer.cpp index d1812f48d..ea30387a4 100644 --- a/poincare/src/parsing/tokenizer.cpp +++ b/poincare/src/parsing/tokenizer.cpp @@ -15,18 +15,18 @@ static inline bool isDigit(const CodePoint c) { const CodePoint Tokenizer::nextCodePoint(PopTest popTest, CodePoint context, bool * testResult) { UTF8Decoder decoder(m_text); const char * currentPointer = m_text; - const char * nextPointer = decoder.nextCodePointPointer(); CodePoint firstCodePoint = decoder.nextCodePoint(); + const char * nextPointer = decoder.stringPosition(); size_t numberOfBytesForCodePoint = nextPointer - currentPointer; if (firstCodePoint != KDCodePointNull) { currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); CodePoint codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); while (codePoint.isCombining()) { numberOfBytesForCodePoint+= nextPointer - currentPointer; currentPointer = nextPointer; - nextPointer = decoder.nextCodePointPointer(); codePoint = decoder.nextCodePoint(); + nextPointer = decoder.stringPosition(); } } // TODO handle combined code points? For now the combining codepoints get dropped. From 07a910b455b84413620282a5dce4e31fab3c3922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 10:54:21 +0100 Subject: [PATCH 034/295] [escher] Clean text_input --- escher/include/escher/text_input.h | 20 ++++++++++----- escher/src/text_input.cpp | 40 ++++++++---------------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/escher/include/escher/text_input.h b/escher/include/escher/text_input.h index 826b92d21..f4a705c77 100644 --- a/escher/include/escher/text_input.h +++ b/escher/include/escher/text_input.h @@ -1,14 +1,14 @@ #ifndef ESCHER_TEXT_INPUT_H #define ESCHER_TEXT_INPUT_H -#include -#include #include #include +#include +#include class TextInput : public ScrollableView, public ScrollViewDataSource { public: - TextInput(Responder * parentResponder, View * contentView); + TextInput(Responder * parentResponder, View * contentView) : ScrollableView(parentResponder, contentView, this) {} void setFont(const KDFont * font) { contentView()->setFont(font); } const char * text() const { return nonEditableContentView()->text(); } bool removeChar(); @@ -18,7 +18,12 @@ public: protected: class ContentView : public View { public: - ContentView(const KDFont * font); + ContentView(const KDFont * font) : + View(), + m_cursorView(), + m_font(font), + m_cursorIndex(0) + {} void setFont(const KDFont * font); const KDFont * font() const { return m_font; } size_t cursorLocation() const { return m_cursorIndex; } @@ -37,8 +42,11 @@ protected: const KDFont * m_font; size_t m_cursorIndex; private: - int numberOfSubviews() const override; - View * subviewAtIndex(int index) override; + int numberOfSubviews() const override { return 1; } + View * subviewAtIndex(int index) override { + assert(index == 1); + return &m_cursorView; + } virtual size_t editedTextLength() const = 0; }; protected: diff --git a/escher/src/text_input.cpp b/escher/src/text_input.cpp index 51c7d6dfd..dee7c1f79 100644 --- a/escher/src/text_input.cpp +++ b/escher/src/text_input.cpp @@ -3,14 +3,6 @@ /* TextInput::ContentView */ -TextInput::ContentView::ContentView(const KDFont * font) : - View(), - m_cursorView(), - m_font(font), - m_cursorIndex(0) -{ -} - void TextInput::ContentView::setCursorLocation(int location) { assert(location >= 0); int adjustedLocation = location > (signed int)editedTextLength() ? (signed int)editedTextLength() : location; @@ -27,14 +19,6 @@ KDRect TextInput::ContentView::cursorRect() { return characterFrameAtIndex(m_cursorIndex); } -int TextInput::ContentView::numberOfSubviews() const { - return 1; -} - -View * TextInput::ContentView::subviewAtIndex(int index) { - return &m_cursorView; -} - void TextInput::ContentView::layoutSubviews() { m_cursorView.setFrame(cursorRect()); } @@ -43,7 +27,7 @@ KDRect TextInput::ContentView::dirtyRectFromCursorPosition(size_t index, bool li KDRect charRect = characterFrameAtIndex(index); KDRect dirtyRect = KDRect(charRect.x(), charRect.y(), bounds().width() - charRect.x(), charRect.height()); if (lineBreak) { - dirtyRect = dirtyRect.unionedWith(KDRect(0, charRect.bottom()+1, bounds().width(), bounds().height()-charRect.bottom()-1)); + dirtyRect = dirtyRect.unionedWith(KDRect(0, charRect.bottom()+1, bounds().width(), bounds().height()-charRect.bottom()-1)); } return dirtyRect; } @@ -54,11 +38,6 @@ void TextInput::ContentView::reloadRectFromCursorPosition(size_t index, bool lin /* TextInput */ -TextInput::TextInput(Responder * parentResponder, View * contentView) : - ScrollableView(parentResponder, contentView, this) -{ -} - bool TextInput::removeChar() { contentView()->removeChar(); scrollToCursor(); @@ -66,13 +45,14 @@ bool TextInput::removeChar() { } void TextInput::scrollToCursor() { - /* Technically, we do not need to overscroll in text input. However, - * logically, we should layout the scroll view before calling - * scrollToContentRect in case the size of the scroll view has changed and - * then call scrollToContentRect which call another layout of the scroll view - * if the offset has evolved. In order to avoid requiring two layouts, we - * allow overscrolling in scrollToContentRect and the last layout of the - * scroll view corrects the size of the scroll view only once. */ + /* Technically, we do not need to overscroll in text input. However, we should + * layout the scroll view before calling scrollToContentRect (in case the size + * of the scroll view has changed) and then call scrollToContentRect which + * calls another layout of the scroll view if the offset has evolved. + * + * In order to avoid requiring two layouts, we allow overscrolling in + * scrollToContentRect, and the last layout of the scroll view corrects the + * size of the scroll view only once. */ scrollToContentRect(contentView()->cursorRect(), true); } @@ -87,7 +67,7 @@ bool TextInput::setCursorLocation(int location) { bool TextInput::insertTextAtLocation(const char * text, int location) { if (contentView()->insertTextAtLocation(text, location)) { /* We layout the scrollable view before scrolling to cursor because the - * content size might have changed. */ + * content size might have changed. */ layoutSubviews(); scrollToCursor(); return true; From 5c39cab73e0a6aae8e0bcf0784b795444895a488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 18 Jan 2019 16:41:39 +0100 Subject: [PATCH 035/295] [escher] Fix text inputs so they use UTF8 --- apps/code/editor_controller.cpp | 61 ++-- apps/code/editor_controller.h | 2 +- apps/code/editor_view.h | 4 +- apps/code/menu_controller.cpp | 15 +- apps/code/python_text_area.cpp | 6 +- apps/code/python_text_area.h | 2 +- apps/probability/calculation_controller.cpp | 7 +- .../editable_cell_table_view_controller.cpp | 4 +- apps/shared/text_field_with_extension.cpp | 23 +- apps/shared/text_field_with_extension.h | 2 +- escher/include/escher/text_area.h | 28 +- escher/include/escher/text_field.h | 19 +- escher/include/escher/text_input.h | 30 +- escher/include/escher/text_input_helpers.h | 10 +- escher/src/text_area.cpp | 304 +++++++++++------- escher/src/text_field.cpp | 202 +++++++----- escher/src/text_input.cpp | 53 +-- escher/src/text_input_helpers.cpp | 18 +- kandinsky/include/kandinsky/font.h | 5 +- .../include/kandinsky/unicode/utf8_decoder.h | 16 +- .../include/kandinsky/unicode/utf8_helper.h | 2 +- kandinsky/src/font.cpp | 11 +- kandinsky/src/unicode/utf8_decoder.cpp | 38 ++- kandinsky/src/unicode/utf8_helper.cpp | 8 +- 24 files changed, 510 insertions(+), 360 deletions(-) diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index 51f1f2397..11a22cb07 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -44,7 +44,7 @@ void EditorController::didBecomeFirstResponder() { void EditorController::viewWillAppear() { m_editorView.loadSyntaxHighlighter(); - m_editorView.setCursorLocation(strlen(m_editorView.text())); + m_editorView.setCursorTextLocation(m_editorView.text() + strlen(m_editorView.text())); } void EditorController::viewDidDisappear() { @@ -53,7 +53,8 @@ void EditorController::viewDidDisappear() { bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) { if (event == Ion::Events::Var) { - // We save script before displaying the Variable box to add new functions or variables + /* We save the script before displaying the Variable box to add new + * functions or variables. */ saveScript(); return false; } @@ -61,60 +62,38 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events: return true; } if (event == Ion::Events::EXE) { - // Auto-Indent - char * text = const_cast(textArea->text()); - int charBeforeCursorIndex = textArea->cursorLocation()-1; - int indentationSize = 0; - // Indent more if the previous line ends with ':'. - if (charBeforeCursorIndex >= 0 && text[charBeforeCursorIndex] == ':') { - indentationSize += k_indentationSpacesNumber; - } - // Compute the indentation of the current line. - int indentationIndex = charBeforeCursorIndex; - while (indentationIndex >= 0 && text[indentationIndex] != '\n') { - indentationIndex--; - } - if (indentationIndex >= 0) { - indentationIndex++; - while (text[indentationIndex] == ' ') { - indentationSize++; - indentationIndex++; - } - } - textArea->handleEventWithText("\n"); - for (int i = 0; i < indentationSize; i++) { - textArea->handleEventWithText(" "); - } + textArea->handleEventWithText("\n", true, false); return true; } if (event == Ion::Events::Backspace) { - // If the cursor is on the left of the text of a line, - // backspace one intentation space at a time. + /* If the cursor is on the left of the text of a line, backspace one + * indentation space at a time. */ char * text = const_cast(textArea->text()); - int charBeforeCursorIndex = textArea->cursorLocation()-1; + const char * charBeforeCursorPointer = textArea->cursorTextLocation()-1; int indentationSize = 0; - while (charBeforeCursorIndex >= 0 && text[charBeforeCursorIndex] == ' ') { - charBeforeCursorIndex--; + while (charBeforeCursorPointer >= text && *charBeforeCursorPointer == ' ') { + charBeforeCursorPointer--; indentationSize++; } - if (charBeforeCursorIndex >= 0 && text[charBeforeCursorIndex] == '\n' + if (charBeforeCursorPointer >= text + && *charBeforeCursorPointer == '\n' && indentationSize >= k_indentationSpacesNumber) { for (int i = 0; i < k_indentationSpacesNumber; i++) { - textArea->removeChar(); + textArea->removeCodePoint(); } return true; } } else if (event == Ion::Events::Space) { - // If the cursor is on the left of the text of a line, - // a space triggers an indentation. + /* If the cursor is on the left of the text of a line, a space triggers an + * indentation. */ char * text = const_cast(textArea->text()); - int charBeforeCursorIndex = textArea->cursorLocation()-1; - while (charBeforeCursorIndex >= 0 && text[charBeforeCursorIndex] == ' ') { - charBeforeCursorIndex--; + const char * charBeforeCursorPointer = textArea->cursorTextLocation()-1; + while (charBeforeCursorPointer >= text && *charBeforeCursorPointer == ' ') { + charBeforeCursorPointer--; } - if (charBeforeCursorIndex >= 0 && text[charBeforeCursorIndex] == '\n') { + if (charBeforeCursorPointer >= text && *charBeforeCursorPointer == '\n') { char indentationBuffer[k_indentationSpacesNumber+1]; for (int i = 0; i < k_indentationSpacesNumber; i++) { indentationBuffer[i] = ' '; @@ -144,9 +123,7 @@ StackViewController * EditorController::stackController() { void EditorController::saveScript() { size_t sizeOfValue = strlen(m_areaBuffer+1)+1+1; // size of scriptContent + size of importation status Script::ErrorStatus err = m_script.setValue({.buffer=m_areaBuffer, .size=sizeOfValue}); - if (err == Script::ErrorStatus::NotEnoughSpaceAvailable || err == Script::ErrorStatus::RecordDoesNotExist) { - assert(false); // This should not happen as we set the text area according to the available space in the Kallax - } + assert(err != Script::ErrorStatus::NotEnoughSpaceAvailable && err != Script::ErrorStatus::RecordDoesNotExist); // This should not happen as we set the text area according to the available space in the Kallax } } diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h index 34e3eadab..93e3adb60 100644 --- a/apps/code/editor_controller.h +++ b/apps/code/editor_controller.h @@ -34,7 +34,7 @@ public: private: Shared::InputEventHandlerDelegateApp * inputEventHandlerDelegateApp() override; - static constexpr int k_indentationSpacesNumber = 2; + static constexpr int k_indentationSpacesNumber = 2; //TODO LEA merge with text area k_indentationSpaces StackViewController * stackController(); void saveScript(); EditorView m_editorView; diff --git a/apps/code/editor_view.h b/apps/code/editor_view.h index 16cb0749c..170fc9b54 100644 --- a/apps/code/editor_view.h +++ b/apps/code/editor_view.h @@ -16,8 +16,8 @@ public: void setText(char * textBuffer, size_t textBufferSize) { m_textArea.setText(textBuffer, textBufferSize); } - bool setCursorLocation(int location) { - return m_textArea.setCursorLocation(location); + bool setCursorTextLocation(const char * location) { + return m_textArea.setCursorTextLocation(location); } void loadSyntaxHighlighter() { m_textArea.loadSyntaxHighlighter(); }; void unloadSyntaxHighlighter() { m_textArea.unloadSyntaxHighlighter(); }; diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index bf4533456..363cdce52 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -126,7 +126,7 @@ void MenuController::renameSelectedScript() { app()->setFirstResponder(myCell); myCell->setHighlighted(false); myCell->textField()->setEditing(true, false); - myCell->textField()->setCursorLocation(strlen(myCell->textField()->text())); + myCell->textField()->setCursorTextLocation(myCell->textField()->text() + strlen(myCell->textField()->text())); } void MenuController::deleteScript(Script script) { @@ -283,7 +283,9 @@ bool MenuController::textFieldShouldFinishEditing(TextField * textField, Ion::Ev } bool MenuController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { - if (event == Ion::Events::Right && textField->isEditing() && textField->cursorLocation() == textField->draftTextLength()) { + if (event == Ion::Events::Right + && textField->isEditing() + && textField->cursorTextLocation() == textField->text() + textField->draftTextLength()) { return true; } if (event == Ion::Events::Clear && textField->isEditing()) { @@ -292,7 +294,7 @@ bool MenuController::textFieldDidReceiveEvent(TextField * textField, Ion::Events assert(k_bufferSize >= 1 + strlen(ScriptStore::k_scriptExtension) + 1); strlcpy(&buffer[1], ScriptStore::k_scriptExtension, strlen(ScriptStore::k_scriptExtension) + 1); textField->setText(buffer); - textField->setCursorLocation(0); + textField->setCursorTextLocation(textField->text()); return true; } return false; @@ -316,7 +318,7 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char * default name and let the user modify it. */ if (!foundDefaultName) { textField->setText(numberedDefaultName); - textField->setCursorLocation(defaultNameLength); + textField->setCursorTextLocation(textField->draftTextBuffer() + defaultNameLength); } newName = const_cast(numberedDefaultName); } @@ -348,8 +350,9 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char bool MenuController::textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textSizeDidChange) { int scriptExtensionLength = 1 + strlen(ScriptStore::k_scriptExtension); - if (textField->isEditing() && textField->cursorLocation() > textField->draftTextLength() - scriptExtensionLength) { - textField->setCursorLocation(textField->draftTextLength() - scriptExtensionLength); + const char * maxPointerLocation = textField->text() + textField->draftTextLength() - scriptExtensionLength; + if (textField->isEditing() && textField->cursorTextLocation() > maxPointerLocation) { + textField->setCursorTextLocation(maxPointerLocation); } return returnValue; } diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp index e8764be2d..d99d26e60 100644 --- a/apps/code/python_text_area.cpp +++ b/apps/code/python_text_area.cpp @@ -18,7 +18,7 @@ constexpr KDColor OperatorColor = KDColor::RGB24(0xd73a49); constexpr KDColor StringColor = KDColor::RGB24(0x032f62); constexpr KDColor BackgroundColor = KDColorWhite; -static inline int min(int x, int y) { return (xcursorLocation() == textField->draftTextLength() && selectedColumn() < m_calculation->numberOfParameters()) - || (event == Ion::Events::Left && textField->cursorLocation() == 0); + || (event == Ion::Events::Right + && textField->cursorTextLocation() == textField->text() + textField->draftTextLength() + && selectedColumn() < m_calculation->numberOfParameters()) + || (event == Ion::Events::Left + && textField->cursorTextLocation() == textField->text()); } bool CalculationController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { diff --git a/apps/shared/editable_cell_table_view_controller.cpp b/apps/shared/editable_cell_table_view_controller.cpp index b9a5af534..a6c411bd8 100644 --- a/apps/shared/editable_cell_table_view_controller.cpp +++ b/apps/shared/editable_cell_table_view_controller.cpp @@ -19,8 +19,8 @@ bool EditableCellTableViewController::textFieldShouldFinishEditing(TextField * t return TextFieldDelegate::textFieldShouldFinishEditing(textField, event) || (event == Ion::Events::Down && selectedRow() < numberOfRows()-1) || (event == Ion::Events::Up && selectedRow() > 0) - || (event == Ion::Events::Right && textField->cursorLocation() == textField->draftTextLength() && selectedColumn() < numberOfColumns()-1) - || (event == Ion::Events::Left && textField->cursorLocation() == 0 && selectedColumn() > 0); + || (event == Ion::Events::Right && (textField->cursorTextLocation() == textField->draftTextBuffer() + textField->draftTextLength()) && selectedColumn() < numberOfColumns()-1) + || (event == Ion::Events::Left && textField->cursorTextLocation() == textField->draftTextBuffer() && selectedColumn() > 0); } bool EditableCellTableViewController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { diff --git a/apps/shared/text_field_with_extension.cpp b/apps/shared/text_field_with_extension.cpp index b2a3ca9fe..5d74cb531 100644 --- a/apps/shared/text_field_with_extension.cpp +++ b/apps/shared/text_field_with_extension.cpp @@ -2,11 +2,11 @@ namespace Shared { -void TextFieldWithExtension::willSetCursorLocation(int * location) { +void TextFieldWithExtension::willSetCursorTextLocation(const char * * location) { size_t textLength = strlen(text()); assert(textLength >= m_extensionLength); - size_t maxLocation = textLength - m_extensionLength; - if (*location > (int)maxLocation) { + const char * maxLocation = m_contentView.draftTextBuffer() + (textLength - m_extensionLength); + if (*location > maxLocation) { *location = maxLocation; } } @@ -21,17 +21,18 @@ void TextFieldWithExtension::removeWholeText() { } bool TextFieldWithExtension::removeTextBeforeExtension(bool whole) { - int extensionIndex = strlen(text()) - m_extensionLength; - assert(extensionIndex >= 0 && extensionIndex < ContentView::k_maxBufferSize - m_extensionLength); - int destinationIndex = whole ? 0 : cursorLocation(); - if (destinationIndex == extensionIndex) { + assert(isEditing()); + const char * extension = m_contentView.draftTextBuffer() + (strlen(text()) - m_extensionLength); + assert(extension >= m_contentView.draftTextBuffer() && extension < m_contentView.draftTextBuffer() + (ContentView::k_maxBufferSize - m_extensionLength)); + char * destination = whole ? m_contentView.draftTextBuffer() : const_cast(cursorTextLocation()); + if (destination == extension) { return false; } - assert(destinationIndex >= 0); - assert(destinationIndex < extensionIndex); + assert(destination >= m_contentView.draftTextBuffer()); + assert(destination < extension); m_contentView.willModifyTextBuffer(); - strlcpy(&(m_contentView.textBuffer()[destinationIndex]), &(m_contentView.textBuffer()[extensionIndex]), ContentView::k_maxBufferSize); - m_contentView.setCursorLocation(destinationIndex); + strlcpy(destination, extension, ContentView::k_maxBufferSize - (destination - m_contentView.draftTextBuffer())); + m_contentView.setCursorTextLocation(destination); m_contentView.didModifyTextBuffer(); layoutSubviews(); return true; diff --git a/apps/shared/text_field_with_extension.h b/apps/shared/text_field_with_extension.h index 12e087ef5..fa2264be4 100644 --- a/apps/shared/text_field_with_extension.h +++ b/apps/shared/text_field_with_extension.h @@ -24,7 +24,7 @@ public: m_extensionLength(extensionLength) {} private: - void willSetCursorLocation(int * location) override; + void willSetCursorTextLocation(const char * * location) override; bool privateRemoveEndOfLine() override; void removeWholeText() override; bool removeTextBeforeExtension(bool whole); diff --git a/escher/include/escher/text_area.h b/escher/include/escher/text_area.h index 94a663e9f..2e60c9d52 100644 --- a/escher/include/escher/text_area.h +++ b/escher/include/escher/text_area.h @@ -17,7 +17,7 @@ public: protected: int indentationBeforeCursor() const; - bool insertTextWithIndentation(const char * textBuffer, int location); + bool insertTextWithIndentation(const char * textBuffer, const char * location); class Text { public: @@ -36,11 +36,12 @@ protected: public: Line(const char * text); const char * text() const { return m_text; } - size_t length() const { return m_length; } + size_t charLength() const { return m_charLength; } + KDCoordinate glyphWidth(const KDFont * const font) const; bool contains(const char * c) const; private: const char * m_text; - size_t m_length; + size_t m_charLength; }; class LineIterator { @@ -66,14 +67,16 @@ protected: LineIterator begin() const { return LineIterator(m_buffer); }; LineIterator end() const { return LineIterator(nullptr); }; - Position span() const; + KDSize span(const KDFont * const font) const; - Position positionAtIndex(size_t index) const; - size_t indexAtPosition(Position p); + Position positionAtPointer(const char * pointer) const; + const char * pointerAtPosition(Position p); - void insertChar(char c, size_t index); - char removeChar(size_t index); - size_t removeRemainingLine(size_t index, int direction); + void insertText(const char * s, int textLength, char * location); + void insertSpacesAtLocation(int numberOfSpaces, char * location); + + CodePoint removeCodePoint(const char * * position); + size_t removeRemainingLine(const char * position, int direction); char operator[](size_t index) { assert(index < m_bufferSize); return m_buffer[index]; @@ -105,18 +108,19 @@ protected: const char * text() const override { return m_text.text(); } size_t editedTextLength() const override { return m_text.textLength(); } const Text * getText() const { return &m_text; } - bool insertTextAtLocation(const char * text, int location) override; + bool insertTextAtLocation(const char * text, const char * location) override; void moveCursorGeo(int deltaX, int deltaY); - bool removeChar() override; + bool removeCodePoint() override; bool removeEndOfLine() override; bool removeStartOfLine(); protected: - KDRect characterFrameAtIndex(size_t index) const override; + KDRect glyphFrameAtPosition(const char * position) const override; Text m_text; }; ContentView * contentView() { return static_cast(TextInput::contentView()); } private: + static constexpr int k_indentationSpaces = 2; TextAreaDelegate * m_delegate; }; diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index 46a9c73f2..9077e2332 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -16,7 +16,8 @@ public: void setDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) { m_inputEventHandlerDelegate = inputEventHandlerDelegate; m_delegate = delegate; } void setDraftTextBuffer(char * draftTextBuffer); bool isEditing() const override; - size_t draftTextLength() const; + const char * draftTextBuffer() const { return const_cast(this)->m_contentView.draftTextBuffer(); } + size_t draftTextLength() const; //TODO keep ? void setText(const char * text); void setAlignment(float horizontalAlignment, float verticalAlignment); virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) override; @@ -40,7 +41,7 @@ protected: void drawRect(KDContext * ctx, KDRect rect) const override; bool isEditing() const { return m_isEditing; } const char * text() const override; - size_t editedTextLength() const override; + size_t editedTextLength() const override { return m_currentDraftTextLength; } //TODO keep ? char * textBuffer() { return m_textBuffer; } char * draftTextBuffer() { return m_draftTextBuffer; } int bufferSize() { return k_maxBufferSize; } @@ -48,12 +49,12 @@ protected: void setAlignment(float horizontalAlignment, float verticalAlignment); void setEditing(bool isEditing, bool reinitDraftBuffer); void reinitDraftTextBuffer(); - /* If the text to be appended is too long to be added without overflowing the - * buffer, nothing is done (not even adding few letters from the text to reach - * the maximum buffer capacity) and false is returned. */ - bool insertTextAtLocation(const char * text, int location) override; + /* If the text to be appended is too long to be added without overflowing the + * buffer, nothing is done (not even adding few letters from the text to reach + * the maximum buffer capacity) and false is returned. */ + bool insertTextAtLocation(const char * text, const char * location) override; // TODO KDSize minimalSizeForOptimalDisplay() const override; - bool removeChar() override; + bool removeCodePoint() override; bool removeEndOfLine() override; void willModifyTextBuffer(); void didModifyTextBuffer(); @@ -65,11 +66,11 @@ protected: constexpr static int k_maxBufferSize = 152; private: void layoutSubviews() override; - KDRect characterFrameAtIndex(size_t index) const override; + KDRect glyphFrameAtPosition(const char * position) const override; bool m_isEditing; char * m_textBuffer; char * m_draftTextBuffer; - size_t m_currentDraftTextLength; + size_t m_currentDraftTextLength; //TODO keep ? size_t m_textBufferSize; float m_horizontalAlignment; float m_verticalAlignment; diff --git a/escher/include/escher/text_input.h b/escher/include/escher/text_input.h index f4a705c77..283d3db90 100644 --- a/escher/include/escher/text_input.h +++ b/escher/include/escher/text_input.h @@ -11,9 +11,9 @@ public: TextInput(Responder * parentResponder, View * contentView) : ScrollableView(parentResponder, contentView, this) {} void setFont(const KDFont * font) { contentView()->setFont(font); } const char * text() const { return nonEditableContentView()->text(); } - bool removeChar(); - size_t cursorLocation() const { return nonEditableContentView()->cursorLocation(); } - bool setCursorLocation(int location); + bool removeCodePoint(); + const char * cursorTextLocation() const { return nonEditableContentView()->cursorTextLocation(); } + bool setCursorTextLocation(const char * location); virtual void scrollToCursor(); protected: class ContentView : public View { @@ -22,29 +22,29 @@ protected: View(), m_cursorView(), m_font(font), - m_cursorIndex(0) + m_cursorTextLocation(nullptr) {} void setFont(const KDFont * font); const KDFont * font() const { return m_font; } - size_t cursorLocation() const { return m_cursorIndex; } - void setCursorLocation(int cursorLocation); + const char * cursorTextLocation() const { assert(m_cursorTextLocation != nullptr); return m_cursorTextLocation; } + void setCursorTextLocation(const char * cursorTextLocation); virtual const char * text() const = 0; - virtual bool insertTextAtLocation(const char * text, int location) = 0; - virtual bool removeChar() = 0; + virtual bool insertTextAtLocation(const char * text, const char * location) = 0; + virtual bool removeCodePoint() = 0; virtual bool removeEndOfLine() = 0; KDRect cursorRect(); protected: virtual void layoutSubviews() override; - virtual KDRect dirtyRectFromCursorPosition(size_t index, bool lineBreak) const; - void reloadRectFromCursorPosition(size_t index, bool lineBreak = false); - virtual KDRect characterFrameAtIndex(size_t index) const = 0; + void reloadRectFromPosition(const char * position, bool lineBreak = false); + virtual KDRect glyphFrameAtPosition(const char * position) const = 0; TextCursorView m_cursorView; const KDFont * m_font; - size_t m_cursorIndex; + const char * m_cursorTextLocation; + virtual KDRect dirtyRectFromPosition(const char * position, bool lineBreak) const; private: int numberOfSubviews() const override { return 1; } View * subviewAtIndex(int index) override { - assert(index == 1); + assert(index == 0); return &m_cursorView; } virtual size_t editedTextLength() const = 0; @@ -53,14 +53,14 @@ protected: /* If the text to be appended is too long to be added without overflowing the * buffer, nothing is done (not even adding few letters from the text to reach * the maximum buffer capacity) and false is returned. */ - bool insertTextAtLocation(const char * textBuffer, int location); + bool insertTextAtLocation(const char * textBuffer, const char * location); bool removeEndOfLine(); ContentView * contentView() { return const_cast(nonEditableContentView()); } virtual const ContentView * nonEditableContentView() const = 0; private: - virtual void willSetCursorLocation(int * location) {} + virtual void willSetCursorTextLocation(const char * * location) {} virtual bool privateRemoveEndOfLine(); }; diff --git a/escher/include/escher/text_input_helpers.h b/escher/include/escher/text_input_helpers.h index 8eab24830..04718b4b5 100644 --- a/escher/include/escher/text_input_helpers.h +++ b/escher/include/escher/text_input_helpers.h @@ -6,11 +6,11 @@ namespace TextInputHelpers { -size_t CursorIndexInCommand(const char * text); -/* Returns the index of the cursor position in a Command, which is the smallest - * index between : - * - The first EmptyChar index (which is the position of the first argument) - * - The first empty quote +const char * CursorPositionInCommand(const char * text); +/* Returns the pointer to the char that should be right of the cursor, which is + * the first char between : + * - The first EmptyChar (which is the position of the first argument) + * - The char after the first empty quote * - The end of the text */ } diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index 408dd2dbf..997227404 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -1,15 +1,15 @@ #include #include #include +#include #include #include #include #include -static inline size_t min(size_t a, size_t b) { - return (a>b ? b : a); -} +static inline size_t minSize(size_t a, size_t b) { return a < b ? a : b; } +static inline size_t maxSize(size_t a, size_t b) { return a > b ? a : b; } /* TextArea */ @@ -21,26 +21,26 @@ TextArea::TextArea(Responder * parentResponder, View * contentView, const KDFont } bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { - int nextCursorLocation = cursorLocation(); + const char * nextCursorLocation = cursorTextLocation(); - size_t cursorIndexInCommand = TextInputHelpers::CursorIndexInCommand(text); + const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(text); constexpr int bufferSize = TextField::maxBufferSize(); char buffer[bufferSize]; // Remove the Empty code points - UTF8Helper::CopyAndRemoveCodePoint(buffer, bufferSize, text, KDCodePointEmpty, &cursorIndexInCommand); + UTF8Helper::CopyAndRemoveCodePoint(buffer, bufferSize, text, KDCodePointEmpty, &cursorPositionInCommand); // Insert the text - if ((indentation && insertTextWithIndentation(buffer, cursorLocation())) || insertTextAtLocation(buffer, cursorLocation())) { + if ((indentation && insertTextWithIndentation(buffer, cursorTextLocation())) || insertTextAtLocation(buffer, cursorTextLocation())) { // Set the cursor location if (forceCursorRightOfText) { nextCursorLocation += strlen(buffer); } else { - nextCursorLocation += cursorIndexInCommand; + nextCursorLocation += cursorPositionInCommand - text; } } - setCursorLocation(nextCursorLocation); + setCursorTextLocation(nextCursorLocation); return true; } @@ -50,9 +50,20 @@ bool TextArea::handleEvent(Ion::Events::Event event) { } else if (handleBoxEvent(app(), event)) { return true; } else if (event == Ion::Events::Left) { - return setCursorLocation(cursorLocation()-1); + if (cursorTextLocation() <= text()) { + assert(cursorTextLocation() == text()); + return false; + } + UTF8Decoder decoder(text(), cursorTextLocation()); + decoder.previousCodePoint(); + return setCursorTextLocation(decoder.stringPosition()); } else if (event == Ion::Events::Right) { - return setCursorLocation(cursorLocation()+1); + if (*cursorTextLocation() == 0) { + return false; + } + UTF8Decoder decoder(text(), cursorTextLocation()); + decoder.nextCodePoint(); + return setCursorTextLocation(decoder.stringPosition()); } else if (event == Ion::Events::Up) { contentView()->moveCursorGeo(0, -1); } else if (event == Ion::Events::Down) { @@ -62,7 +73,7 @@ bool TextArea::handleEvent(Ion::Events::Event event) { } else if (event == Ion::Events::ShiftRight) { contentView()->moveCursorGeo(INT_MAX/2, 0); } else if (event == Ion::Events::Backspace) { - return removeChar(); + return removeCodePoint(); } else if (event.hasText()) { return handleEventWithText(event.text()); } else if (event == Ion::Events::EXE) { @@ -85,80 +96,75 @@ void TextArea::setText(char * textBuffer, size_t textBufferSize) { contentView()->moveCursorGeo(0, 0); } -bool TextArea::insertTextWithIndentation(const char * textBuffer, int location) { +bool TextArea::insertTextWithIndentation(const char * textBuffer, const char * location) { int indentation = indentationBeforeCursor(); - char spaceString[indentation+1]; - for (int i = 0; i < indentation; i++) { - spaceString[i] = ' '; + const char * previousChar = cursorTextLocation()-1; + if (previousChar >= const_cast