diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4dd1eb6ff1f4b178119c0a44487febeff1c1b727..2d5cffb49abb02cf7a87fd3e3f333ccabaeacc92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ current (development)
 ---------------------
 
 ### Component
+- Feature: `input` is now multi-line.
+- Feature: `input` style can now be customized.
 - Feature: Support `ResizableSplit` with customizable separator.
 - Breaking: MenuDirection enum is renamed Direction
 - Fix: Remove useless new line when using an alternative screen.
@@ -12,9 +14,7 @@ current (development)
 ### Dom
 - Feature: Add the dashed style for border and separator.
 - Feature: Add colored borders.
-- Feature: Customize with gradient color effect. Add the following decorators:
-  - `colorgrad`
-  - `bgcolorgrad`
+- Feature: Add `LinearGradient`!
 - Improvement: Color::Interpolate() uses gamma correction.
 
 ###
diff --git a/cmake/ftxui_test.cmake b/cmake/ftxui_test.cmake
index 5b62cb32837dbb51c5e10b5c87c8bea8449e940a..fbbbbfa2b525fc8002c98dcb4cdccba71e1ce8ca 100644
--- a/cmake/ftxui_test.cmake
+++ b/cmake/ftxui_test.cmake
@@ -71,3 +71,5 @@ include(GoogleTest)
 gtest_discover_tests(ftxui-tests
   DISCOVERY_TIMEOUT 600
 )
+
+set(CMAKE_CTEST_ARGUMENTS "--rerun-failed --output-on-failure")
diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt
index c08069092bfc16bb809731c665be70cbc5dcf757..6211583d82da5530483292315e046781110a5d52 100644
--- a/examples/component/CMakeLists.txt
+++ b/examples/component/CMakeLists.txt
@@ -17,6 +17,7 @@ example(focus_cursor)
 example(gallery)
 example(homescreen)
 example(input)
+example(input_style)
 example(linear_gradient_gallery)
 example(maybe)
 example(menu)
@@ -40,5 +41,6 @@ example(slider_direction)
 example(slider_rgb)
 example(tab_horizontal)
 example(tab_vertical)
+example(textarea)
 example(toggle)
 example(with_restored_io)
diff --git a/examples/component/input_style.cpp b/examples/component/input_style.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f98ae396073b64477162213490e64cf68f70b61f
--- /dev/null
+++ b/examples/component/input_style.cpp
@@ -0,0 +1,98 @@
+#include <ftxui/dom/linear_gradient.hpp>  // for LinearGradient
+#include <ftxui/screen/color.hpp>  // for Color, Color::White, Color::Red, Color::Blue, Color::Black, Color::GrayDark, ftxui
+#include <functional>              // for function
+#include <string>                  // for allocator, string
+#include <utility>                 // for move
+
+#include "ftxui/component/component.hpp"  // for Input, Horizontal, Vertical, operator|
+#include "ftxui/component/component_base.hpp"     // for Component
+#include "ftxui/component/component_options.hpp"  // for InputState, InputOption
+#include "ftxui/component/screen_interactive.hpp"  // for ScreenInteractive
+#include "ftxui/dom/elements.hpp"  // for operator|=, Element, bgcolor, operator|, separatorEmpty, color, borderEmpty, separator, text, center, dim, hbox, vbox, border, borderDouble, borderRounded
+
+int main(int argc, const char* argv[]) {
+  using namespace ftxui;
+
+  InputOption style_1 = InputOption::Default();
+
+  InputOption style_2 = InputOption::Spacious();
+
+  InputOption style_3 = InputOption::Spacious();
+  style_3.transform = [](InputState state) {
+    state.element |= borderEmpty;
+
+    if (state.is_placeholder) {
+      state.element |= dim;
+    }
+
+    if (state.focused) {
+      state.element |= borderDouble;
+      state.element |= bgcolor(Color::White);
+      state.element |= color(Color::Black);
+    } else if (state.hovered) {
+      state.element |= borderRounded;
+      state.element |= bgcolor(LinearGradient(90, Color::Blue, Color::Red));
+      state.element |= color(Color::White);
+    } else {
+      state.element |= border;
+      state.element |= bgcolor(LinearGradient(0, Color::Blue, Color::Red));
+      state.element |= color(Color::White);
+    }
+
+    return state.element;
+  };
+
+  InputOption style_4 = InputOption::Spacious();
+  style_4.transform = [](InputState state) {
+    state.element = hbox({
+        text("Theorem") | center | borderEmpty | bgcolor(Color::Red),
+        separatorEmpty(),
+        separator() | color(Color::White),
+        separatorEmpty(),
+        std::move(state.element),
+    });
+
+    state.element |= borderEmpty;
+    if (state.is_placeholder) {
+      state.element |= dim;
+    }
+
+    if (state.focused) {
+      state.element |= bgcolor(Color::Black);
+    } else {
+      state.element |= bgcolor(Color::Blue);
+    }
+
+    if (state.hovered) {
+      state.element |= bgcolor(Color::GrayDark);
+    }
+
+    return vbox({state.element, separatorEmpty()});
+  };
+
+  auto generateUiFromStyle = [&](InputOption style) {
+    auto first_name = new std::string();   // Leaked
+    auto middle_name = new std::string();  // Leaked
+    auto last_name = new std::string();    // Leaked
+    return Container::Vertical({
+               Input(first_name, "first name", style),
+               Input(middle_name, "middle name", style),
+               Input(last_name, "last name", style),
+           }) |
+           borderEmpty;
+  };
+
+  auto ui = Container::Horizontal({
+      generateUiFromStyle(style_1),
+      generateUiFromStyle(style_2),
+      generateUiFromStyle(style_3),
+      generateUiFromStyle(style_4),
+  });
+
+  auto screen = ScreenInteractive::TerminalOutput();
+  screen.Loop(ui);
+}
+
+// Copyright 2020 Arthur Sonzogni. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found in
+// the LICENSE file.
diff --git a/examples/component/textarea.cpp b/examples/component/textarea.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b8753b3e5e707fcbd969d6efd4e62331c91d2ba5
--- /dev/null
+++ b/examples/component/textarea.cpp
@@ -0,0 +1,35 @@
+#include <memory>  // for allocator, __shared_ptr_access, shared_ptr
+#include <string>  // for string
+
+#include "ftxui/component/captured_mouse.hpp"  // for ftxui
+#include "ftxui/component/component.hpp"  // for Input, Renderer, ResizableSplitLeft
+#include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
+#include "ftxui/component/screen_interactive.hpp"  // for ScreenInteractive
+#include "ftxui/dom/elements.hpp"  // for operator|, separator, text, Element, flex, vbox, border
+
+int main(int argc, const char* argv[]) {
+  using namespace ftxui;
+
+  std::string content_1;
+  std::string content_2;
+  auto textarea_1 = Input(&content_1);
+  auto textarea_2 = Input(&content_2);
+  int size = 50;
+  auto layout = ResizableSplitLeft(textarea_1, textarea_2, &size);
+
+  auto component = Renderer(layout, [&] {
+    return vbox({
+               text("Input:"),
+               separator(),
+               layout->Render() | flex,
+           }) |
+           border;
+  });
+
+  auto screen = ScreenInteractive::Fullscreen();
+  screen.Loop(component);
+}
+
+// Copyright 2020 Arthur Sonzogni. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found in
+// the LICENSE file.
diff --git a/include/ftxui/component/component.hpp b/include/ftxui/component/component.hpp
index b54f89ffbfb1c92a2599d1bb8c4a777332b1686a..a9289ae872ba852d5f64995e402e8389f9097259 100644
--- a/include/ftxui/component/component.hpp
+++ b/include/ftxui/component/component.hpp
@@ -51,8 +51,9 @@ Component Checkbox(ConstStringRef label,
                    bool* checked,
                    Ref<CheckboxOption> option = CheckboxOption::Simple());
 
+Component Input(StringRef content, Ref<InputOption> option = {});
 Component Input(StringRef content,
-                ConstStringRef placeholder,
+                StringRef placeholder,
                 Ref<InputOption> option = {});
 
 Component Menu(ConstStringListRef entries,
diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp
index 8c49bd6a6bbae8785d62c8a694f42b78bea97494..172f3c5a224cedfc5225e858bddf27bfd11b0172 100644
--- a/include/ftxui/component/component_options.hpp
+++ b/include/ftxui/component/component_options.hpp
@@ -5,7 +5,7 @@
 #include <ftxui/component/animation.hpp>  // for Duration, QuadraticInOut, Function
 #include <ftxui/dom/direction.hpp>  // for Direction, Direction::Left, Direction::Right, Direction::Down
 #include <ftxui/dom/elements.hpp>  // for Element, separator
-#include <ftxui/util/ref.hpp>      // for Ref, ConstRef
+#include <ftxui/util/ref.hpp>      // for Ref, ConstRef, StringRef
 #include <functional>              // for function
 #include <optional>                // for optional
 #include <string>                  // for string
@@ -134,20 +134,42 @@ struct CheckboxOption {
   std::function<void()> on_change = [] {};
 };
 
+/// @brief Used to define style for the Input component.
+struct InputState {
+  Element element;
+  bool hovered;         /// < Whether the input is hovered by the mouse.
+  bool focused;         /// < Whether the input is focused by the user.
+  bool is_placeholder;  /// < Whether the input is empty and displaying the
+                        /// < placeholder.
+};
+
 /// @brief Option for the Input component.
 /// @ingroup component
 struct InputOption {
+  // A set of predefined styles:
+
+  /// @brief Create the default input style:
+  static InputOption Default();
+  /// @brief A white on black style with high margins:
+  static InputOption Spacious();
+  /// @brief A style with a border:
+  static InputOption Arthur();
+
+  /// The content of the input when it's empty.
+  StringRef placeholder = "";
+
+  // Style:
+  std::function<Element(InputState)> transform;
+  Ref<bool> password = false;  /// < Obscure the input content using '*'.
+  Ref<bool> multiline = true;  /// < Whether the input can be multiline.
+
   /// Called when the content changes.
   std::function<void()> on_change = [] {};
   /// Called when the user presses enter.
   std::function<void()> on_enter = [] {};
 
-  /// Obscure the input content using '*'.
-  Ref<bool> password = false;
-
-  /// When set different from -1, this attributes is used to store the cursor
-  /// position.
-  Ref<int> cursor_position = -1;
+  // The char position of the cursor:
+  Ref<int> cursor_position = 0;
 };
 
 /// @brief Option for the Radiobox component.
diff --git a/include/ftxui/component/receiver.hpp b/include/ftxui/component/receiver.hpp
index 1d487644f7d96fa4879339395de9a5e87e708634..212341cb2c702fc6e2f100a51f54d3443dcc986b 100644
--- a/include/ftxui/component/receiver.hpp
+++ b/include/ftxui/component/receiver.hpp
@@ -1,7 +1,7 @@
 #ifndef FTXUI_COMPONENT_RECEIVER_HPP_
 #define FTXUI_COMPONENT_RECEIVER_HPP_
 
-#include <algorithm>           // for copy
+#include <algorithm>           // for copy, max
 #include <atomic>              // for atomic, __atomic_base
 #include <condition_variable>  // for condition_variable
 #include <functional>
diff --git a/include/ftxui/screen/string.hpp b/include/ftxui/screen/string.hpp
index 0475c41fff79a751a2bb8bd0f94da08be10bc07a..965733058cd47255202d2c625217417f3899a0c5 100644
--- a/include/ftxui/screen/string.hpp
+++ b/include/ftxui/screen/string.hpp
@@ -15,44 +15,10 @@ std::wstring to_wstring(T s) {
 }
 
 int string_width(const std::string&);
+
 // Split the string into a its glyphs. An empty one is inserted ater fullwidth
 // ones.
 std::vector<std::string> Utf8ToGlyphs(const std::string& input);
-// If |input| was an array of glyphs, this returns the number of char to eat
-// before reaching the glyph at index |glyph_index|.
-int GlyphPosition(const std::string& input,
-                  size_t glyph_index,
-                  size_t start = 0);
-// Returns the number of glyphs in |input|.
-int GlyphCount(const std::string& input);
-
-// Properties from:
-// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt
-enum class WordBreakProperty {
-  ALetter,
-  CR,
-  Double_Quote,
-  Extend,
-  ExtendNumLet,
-  Format,
-  Hebrew_Letter,
-  Katakana,
-  LF,
-  MidLetter,
-  MidNum,
-  MidNumLet,
-  Newline,
-  Numeric,
-  Regional_Indicator,
-  Single_Quote,
-  WSegSpace,
-  ZWJ,
-};
-WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint);
-std::vector<WordBreakProperty> Utf8ToWordBreakProperty(
-    const std::string& input);
-
-bool IsWordBreakingCharacter(const std::string& input, size_t glyph_index);
 
 // Map every cells drawn by |input| to their corresponding Glyphs. Half-size
 // Glyphs takes one cell, full-size Glyphs take two cells.
diff --git a/include/ftxui/util/ref.hpp b/include/ftxui/util/ref.hpp
index ac20b4fd4afcb07486431497e96234ad672a49c6..c650bc4222b1b14f622cb801814246d39ce1f8bb 100644
--- a/include/ftxui/util/ref.hpp
+++ b/include/ftxui/util/ref.hpp
@@ -48,6 +48,7 @@ class StringRef {
   StringRef(const wchar_t* ref) : StringRef(to_string(std::wstring(ref))) {}
   StringRef(const char* ref) : StringRef(std::string(ref)) {}
   std::string& operator*() { return address_ ? *address_ : owned_; }
+  std::string& operator()() { return address_ ? *address_ : owned_; }
   std::string* operator->() { return address_ ? address_ : &owned_; }
 
  private:
diff --git a/src/ftxui/component/button_test.cpp b/src/ftxui/component/button_test.cpp
index 4099b96165822c0176e965b4387ef1a9f7d13381..c49582c8cf9495e27bb84072113239d135dcb025 100644
--- a/src/ftxui/component/button_test.cpp
+++ b/src/ftxui/component/button_test.cpp
@@ -1,7 +1,6 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, Test, EXPECT_FALSE, EXPECT_TRUE, TestInfo (ptr only), TEST
-#include <chrono>         // for operator""s, chrono_literals
-#include <memory>         // for __shared_ptr_access, shared_ptr, allocator
-#include <string>         // for string
+#include <chrono>  // for operator""s, chrono_literals
+#include <memory>  // for __shared_ptr_access, shared_ptr, allocator
+#include <string>  // for string
 
 #include "ftxui/component/animation.hpp"          // for Duration, Params
 #include "ftxui/component/component.hpp"          // for Button, Horizontal
@@ -12,6 +11,7 @@
 #include "ftxui/dom/node.hpp"         // for Render
 #include "ftxui/screen/screen.hpp"    // for Screen
 #include "ftxui/screen/terminal.hpp"  // for SetColorSupport, Color, TrueColor
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, Test, EXPECT_FALSE, EXPECT_TRUE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/collapsible_test.cpp b/src/ftxui/component/collapsible_test.cpp
index 4492fe6c36c6abaac8fed1923086b45fb5c6f151..bbea5cc1b27b6365540a44e44c46d72063631427 100644
--- a/src/ftxui/component/collapsible_test.cpp
+++ b/src/ftxui/component/collapsible_test.cpp
@@ -1,12 +1,13 @@
-#include <gtest/gtest.h>
 #include <memory>  // for __shared_ptr_access, shared_ptr, allocator
+#include <string>  // for string
 
 #include "ftxui/component/component.hpp"       // for Collapsible, Renderer
 #include "ftxui/component/component_base.hpp"  // for ComponentBase
 #include "ftxui/component/event.hpp"  // for Event, Event::Return, Event::ArrowDown
-#include "ftxui/dom/elements.hpp"     // for text, Element
+#include "ftxui/dom/elements.hpp"     // for Element, text
 #include "ftxui/dom/node.hpp"         // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen
+#include "gtest/gtest.h"  // for AssertionResult, Message, Test, TestPartResult, EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/component_options.cpp b/src/ftxui/component/component_options.cpp
index 4c955e5c42b08e178a9c690a8f329f3daf1daab4..e36ac3585194575c77c81bab941e48fd707eaef1 100644
--- a/src/ftxui/component/component_options.cpp
+++ b/src/ftxui/component/component_options.cpp
@@ -1,11 +1,12 @@
 #include "ftxui/component/component_options.hpp"
 
-#include <ftxui/screen/color.hpp>  // for Color, Color::Black, Color::White, Color::GrayDark, Color::GrayLight
-#include <memory>   // for shared_ptr
-#include <utility>  // for move
+#include <ftxui/dom/linear_gradient.hpp>  // for LinearGradient
+#include <ftxui/screen/color.hpp>  // for Color, Color::White, Color::Black, Color::GrayDark, Color::Blue, Color::GrayLight, Color::Red
+#include <memory>                  // for shared_ptr
+#include <utility>                 // for move
 
 #include "ftxui/component/animation.hpp"  // for Function, Duration
-#include "ftxui/dom/elements.hpp"  // for operator|=, text, Element, bold, inverted, operator|, dim, hbox, automerge, borderEmpty, borderLight
+#include "ftxui/dom/elements.hpp"  // for operator|=, Element, text, bgcolor, inverted, bold, dim, operator|, color, borderEmpty, hbox, automerge, border, borderLight
 
 namespace ftxui {
 
@@ -257,6 +258,77 @@ RadioboxOption RadioboxOption::Simple() {
   return option;
 }
 
+// static
+InputOption InputOption::Default() {
+  InputOption option;
+  option.transform = [](InputState state) {
+    state.element |= color(Color::White);
+
+    if (state.is_placeholder) {
+      state.element |= dim;
+    }
+
+    if (state.focused) {
+      state.element |= inverted;
+    } else if (state.hovered) {
+      state.element |= bgcolor(Color::GrayDark);
+    }
+
+    return state.element;
+  };
+  return option;
+}
+
+// static
+InputOption InputOption::Spacious() {
+  InputOption option;
+  option.transform = [](InputState state) {
+    state.element |= borderEmpty;
+    state.element |= color(Color::White);
+
+    if (state.is_placeholder) {
+      state.element |= dim;
+    }
+
+    if (state.focused) {
+      state.element |= bgcolor(Color::Black);
+    }
+
+    if (state.hovered) {
+      state.element |= bgcolor(Color::GrayDark);
+    }
+
+    return state.element;
+  };
+  return option;
+}
+
+// static
+InputOption InputOption::Arthur() {
+  InputOption option;
+  option.transform = [](InputState state) {
+    state.element |= borderEmpty;
+    state.element |= color(Color::White);
+
+    if (state.is_placeholder) {
+      state.element |= dim;
+    }
+
+    if (state.focused) {
+      state.element |= bgcolor(Color::Black);
+    } else {
+      state.element |= bgcolor(LinearGradient(0, Color::Blue, Color::Red));
+    }
+
+    if (state.hovered) {
+      state.element |= bgcolor(Color::GrayDark);
+    }
+
+    return state.element;
+  };
+  return option;
+}
+
 }  // namespace ftxui
 
 // Copyright 2022 Arthur Sonzogni. All rights reserved.
diff --git a/src/ftxui/component/component_test.cpp b/src/ftxui/component/component_test.cpp
index 5a8b6aad53b56cc52b1f3f9392bc27cadb7a1465..4247c04a04a8ccc9233e0c5bfac21c9e12167453 100644
--- a/src/ftxui/component/component_test.cpp
+++ b/src/ftxui/component/component_test.cpp
@@ -1,8 +1,9 @@
-#include <gtest/gtest.h>
 #include <memory>  // for shared_ptr, __shared_ptr_access, allocator, __shared_ptr_access<>::element_type, make_shared
+#include <string>  // for string
 
 #include "ftxui/component/component.hpp"       // for Make
 #include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
+#include "gtest/gtest.h"  // for Message, TestPartResult, EXPECT_EQ, Test, AssertionResult, TEST, EXPECT_FALSE
 
 namespace ftxui {
 
diff --git a/src/ftxui/component/container_test.cpp b/src/ftxui/component/container_test.cpp
index 6974c4803ac1132c0caff400d8f06c695b32f5cb..9710c675b3056829f420a58e3740506acce5546b 100644
--- a/src/ftxui/component/container_test.cpp
+++ b/src/ftxui/component/container_test.cpp
@@ -1,9 +1,10 @@
-#include <gtest/gtest.h>
 #include <memory>  // for __shared_ptr_access, shared_ptr, allocator
+#include <string>  // for string
 
 #include "ftxui/component/component.hpp"  // for Horizontal, Vertical, Button, Tab
 #include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
 #include "ftxui/component/event.hpp"  // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_FALSE, Test, EXPECT_TRUE, TEST
 
 namespace ftxui {
 
diff --git a/src/ftxui/component/hoverable_test.cpp b/src/ftxui/component/hoverable_test.cpp
index 039d563adb69053b52a62a4350dee7852ab658b9..3a023a0264e59f4039b83425309553db1fd10ff0 100644
--- a/src/ftxui/component/hoverable_test.cpp
+++ b/src/ftxui/component/hoverable_test.cpp
@@ -1,6 +1,6 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, EXPECT_FALSE, EXPECT_EQ, Test, EXPECT_TRUE, TestInfo (ptr only), TEST
 #include <ftxui/dom/elements.hpp>  // for Element, text
 #include <memory>  // for shared_ptr, __shared_ptr_access, allocator
+#include <string>  // for string
 
 #include "ftxui/component/component.hpp"  // for Hoverable, Horizontal, operator|=, Renderer
 #include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
@@ -8,6 +8,7 @@
 #include "ftxui/component/mouse.hpp"  // for Mouse, Mouse::Left, Mouse::Released
 #include "ftxui/dom/node.hpp"         // for Render
 #include "ftxui/screen/screen.hpp"    // for Screen
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_FALSE, EXPECT_EQ, Test, EXPECT_TRUE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp
index c21bdd4bee99eaeafedabc7251096bf92b108a90..7275b122d5dc56d93947cd3add0f861bddad62fb 100644
--- a/src/ftxui/component/input.cpp
+++ b/src/ftxui/component/input.cpp
@@ -1,32 +1,55 @@
+#include <stdint.h>    // for uint32_t
 #include <algorithm>   // for max, min
 #include <cstddef>     // for size_t
 #include <functional>  // for function
-#include <memory>      // for shared_ptr
-#include <string>      // for string, allocator
-#include <utility>     // for move
-#include <vector>      // for vector
+#include <memory>   // for allocator, shared_ptr, allocator_traits<>::value_type
+#include <sstream>  // for basic_istream, stringstream
+#include <string>   // for string, basic_string, operator==, getline
+#include <utility>  // for move
+#include <vector>   // for vector
 
 #include "ftxui/component/captured_mouse.hpp"     // for CapturedMouse
 #include "ftxui/component/component.hpp"          // for Make, Input
 #include "ftxui/component/component_base.hpp"     // for ComponentBase
 #include "ftxui/component/component_options.hpp"  // for InputOption
-#include "ftxui/component/event.hpp"  // for Event, Event::ArrowLeft, Event::ArrowLeftCtrl, Event::ArrowRight, Event::ArrowRightCtrl, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
+#include "ftxui/component/event.hpp"  // for Event, Event::ArrowDown, Event::ArrowLeft, Event::ArrowLeftCtrl, Event::ArrowRight, Event::ArrowRightCtrl, Event::ArrowUp, Event::Backspace, Event::Delete, Event::End, Event::Home, Event::Return
 #include "ftxui/component/mouse.hpp"  // for Mouse, Mouse::Left, Mouse::Pressed
 #include "ftxui/component/screen_interactive.hpp"  // for Component
-#include "ftxui/dom/elements.hpp"  // for operator|, text, Element, reflect, operator|=, flex, inverted, hbox, size, bold, dim, focus, focusCursorBarBlinking, frame, select, Decorator, EQUAL, HEIGHT
+#include "ftxui/dom/elements.hpp"  // for operator|, reflect, text, Element, xflex, hbox, Elements, frame, operator|=, vbox, focus, focusCursorBarBlinking, select
 #include "ftxui/screen/box.hpp"    // for Box
-#include "ftxui/screen/string.hpp"  // for GlyphPosition, WordBreakProperty, GlyphCount, Utf8ToWordBreakProperty, CellToGlyphIndex, WordBreakProperty::ALetter, WordBreakProperty::CR, WordBreakProperty::Double_Quote, WordBreakProperty::Extend, WordBreakProperty::ExtendNumLet, WordBreakProperty::Format, WordBreakProperty::Hebrew_Letter, WordBreakProperty::Katakana, WordBreakProperty::LF, WordBreakProperty::MidLetter, WordBreakProperty::MidNum, WordBreakProperty::MidNumLet, WordBreakProperty::Newline, WordBreakProperty::Numeric, WordBreakProperty::Regional_Indicator, WordBreakProperty::Single_Quote, WordBreakProperty::WSegSpace, WordBreakProperty::ZWJ
-#include "ftxui/screen/util.hpp"    // for clamp
-#include "ftxui/util/ref.hpp"       // for StringRef, Ref, ConstStringRef
+#include "ftxui/screen/string.hpp"           // for string_width
+#include "ftxui/screen/string_internal.hpp"  // for GlyphNext, GlyphPrevious, WordBreakProperty, EatCodePoint, CodepointToWordBreakProperty, IsFullWidth, WordBreakProperty::ALetter, WordBreakProperty::CR, WordBreakProperty::Double_Quote, WordBreakProperty::Extend, WordBreakProperty::ExtendNumLet, WordBreakProperty::Format, WordBreakProperty::Hebrew_Letter, WordBreakProperty::Katakana, WordBreakProperty::LF, WordBreakProperty::MidLetter, WordBreakProperty::MidNum, WordBreakProperty::MidNumLet, WordBreakProperty::Newline, WordBreakProperty::Numeric, WordBreakProperty::Regional_Indicator, WordBreakProperty::Single_Quote, WordBreakProperty::WSegSpace, WordBreakProperty::ZWJ
+#include "ftxui/screen/util.hpp"             // for clamp
+#include "ftxui/util/ref.hpp"                // for StringRef, Ref
 
 namespace ftxui {
 
 namespace {
 
-// Group together several propertiej so they appear to form a similar group.
-// For instance, letters are grouped with number and form a single word.
-bool IsWordCharacter(WordBreakProperty property) {
-  switch (property) {
+std::vector<std::string> Split(const std::string& input) {
+  std::vector<std::string> output;
+  std::stringstream ss(input);
+  std::string line;
+  while (std::getline(ss, line)) {
+    output.push_back(line);
+  }
+  if (input.back() == '\n') {
+    output.push_back("");
+  }
+  return output;
+}
+
+size_t GlyphWidth(const std::string& input, size_t iter) {
+  uint32_t ucs = 0;
+  if (!EatCodePoint(input, iter, &iter, &ucs))
+    return 0;
+  if (IsFullWidth(ucs))
+    return 2;
+  return 1;
+}
+
+bool IsWordCodePoint(uint32_t codepoint) {
+  switch (CodepointToWordBreakProperty(codepoint)) {
     case WordBreakProperty::ALetter:
     case WordBreakProperty::Hebrew_Letter:
     case WordBreakProperty::Katakana:
@@ -42,7 +65,7 @@ bool IsWordCharacter(WordBreakProperty property) {
     case WordBreakProperty::Newline:
     case WordBreakProperty::Single_Quote:
     case WordBreakProperty::WSegSpace:
-    // Unsure:
+    // Unexpected/Unsure
     case WordBreakProperty::Extend:
     case WordBreakProperty::ExtendNumLet:
     case WordBreakProperty::Format:
@@ -50,216 +73,398 @@ bool IsWordCharacter(WordBreakProperty property) {
     case WordBreakProperty::ZWJ:
       return false;
   }
-  return true;  // NOT_REACHED();
+  return false;  // NOT_REACHED();
 }
 
-std::string PasswordField(size_t size) {
-  std::string out;
-  out.reserve(2 * size);
-  while (size--) {
-    out += "•";
+bool IsWordCharacter(const std::string& input, size_t iter) {
+  uint32_t ucs = 0;
+  if (!EatCodePoint(input, iter, &iter, &ucs)) {
+    return false;
   }
-  return out;
+
+  return IsWordCodePoint(ucs);
 }
 
 // An input box. The user can type text into it.
 class InputBase : public ComponentBase {
  public:
-  InputBase(StringRef content,
-            ConstStringRef placeholder,
-            Ref<InputOption> option)
-      : content_(std::move(content)),
-        placeholder_(std::move(placeholder)),
-        option_(std::move(option)) {}
-
-  int cursor_position_internal_ = 0;
-  int& cursor_position() {
-    int& opt = option_->cursor_position();
-    if (opt != -1) {
-      return opt;
-    }
-    return cursor_position_internal_;
-  }
+  // NOLINTNEXTLINE
+  InputBase(StringRef content, Ref<InputOption> option)
+      : content_(std::move(content)), option_(std::move(option)) {}
 
+ private:
   // Component implementation:
   Element Render() override {
-    std::string password_content;
-    if (option_->password()) {
-      password_content = PasswordField(content_->size());
-    }
-    const std::string& content =
-        option_->password() ? password_content : *content_;
-
-    const int size = GlyphCount(content);
-
-    cursor_position() = std::max(0, std::min<int>(size, cursor_position()));
-    auto main_decorator = flex | ftxui::size(HEIGHT, EQUAL, 1);
     const bool is_focused = Focused();
+    const auto focused =
+        (is_focused || hovered_) ? focusCursorBarBlinking : select;
+
+    auto transform = option_->transform ? option_->transform
+                                        : InputOption::Default().transform;
 
     // placeholder.
-    if (size == 0) {
-      auto element = text(*placeholder_) | dim | main_decorator | reflect(box_);
+    if (content_->empty()) {
+      auto element = text(option_->placeholder()) | xflex | frame;
       if (is_focused) {
         element |= focus;
       }
-      if (hovered_ || is_focused) {
-        element |= inverted;
+
+      return transform({
+                 std::move(element), hovered_, is_focused,
+                 true  // placeholder
+             }) |
+             reflect(box_);
+    }
+
+    Elements elements;
+    std::vector<std::string> lines = Split(*content_);
+
+    int& cursor_position = option_->cursor_position();
+    cursor_position = util::clamp(cursor_position, 0, (int)content_->size());
+
+    // Find the line and index of the cursor.
+    int cursor_line = 0;
+    int cursor_char_index = cursor_position;
+    for (const auto& line : lines) {
+      if (cursor_char_index <= (int)line.size()) {
+        break;
       }
-      return element;
+
+      cursor_char_index -= line.size() + 1;
+      cursor_line++;
     }
 
-    // Not focused.
-    if (!is_focused) {
-      auto element = text(content) | main_decorator | reflect(box_);
-      if (hovered_) {
-        element |= inverted;
+    if (lines.empty()) {
+      elements.push_back(text("") | focused);
+    }
+
+    for (size_t i = 0; i < lines.size(); ++i) {
+      const std::string& line = lines[i];
+
+      // This is not the cursor line.
+      if (int(i) != cursor_line) {
+        elements.push_back(Text(line));
+        continue;
       }
-      return element;
-    }
-
-    const int index_before_cursor = GlyphPosition(content, cursor_position());
-    const int index_after_cursor =
-        GlyphPosition(content, 1, index_before_cursor);
-    const std::string part_before_cursor =
-        content.substr(0, index_before_cursor);
-    std::string part_at_cursor = " ";
-    if (cursor_position() < size) {
-      part_at_cursor = content.substr(index_before_cursor,
-                                      index_after_cursor - index_before_cursor);
-    }
-    const std::string part_after_cursor = content.substr(index_after_cursor);
-    auto focused = (is_focused || hovered_) ? focusCursorBarBlinking : select;
-    return hbox({
-               text(part_before_cursor),
-               text(part_at_cursor) | focused | reflect(cursor_box_),
-               text(part_after_cursor),
+
+      // The cursor is at the end of the line.
+      if (cursor_char_index >= (int)line.size()) {
+        elements.push_back(hbox({
+                               Text(line),
+                               text(" ") | focused | reflect(cursor_box_),
+                           }) |
+                           xflex);
+        continue;
+      }
+
+      // The cursor is on this line.
+      const int glyph_start = cursor_char_index;
+      const int glyph_end = GlyphNext(line, glyph_start);
+      const std::string part_before_cursor = line.substr(0, glyph_start);
+      const std::string part_at_cursor =
+          line.substr(glyph_start, glyph_end - glyph_start);
+      const std::string part_after_cursor = line.substr(glyph_end);
+      auto element = hbox({
+                         Text(part_before_cursor),
+                         Text(part_at_cursor) | focused | reflect(cursor_box_),
+                         Text(part_after_cursor),
+                     }) |
+                     xflex;
+      elements.push_back(element);
+    }
+
+    auto element = vbox(std::move(elements)) | frame;
+    return transform({
+               std::move(element), hovered_, is_focused,
+               false  // placeholder
            }) |
-           flex | frame | bold | main_decorator | reflect(box_);
+           xflex | reflect(box_);
   }
 
-  bool OnEvent(Event event) override {
-    cursor_position() =
-        std::max(0, std::min<int>((int)content_->size(), cursor_position()));
+  Element Text(const std::string& input) {
+    if (!option_->password()) {
+      return text(input);
+    }
 
-    if (event.is_mouse()) {
-      return OnMouseEvent(event);
+    std::string out;
+    out.reserve(10 + input.size() * 3 / 2);
+    for (size_t i = 0; i < input.size(); ++i) {
+      out += "•";
     }
+    return text(out);
+  }
 
-    // Backspace.
-    if (event == Event::Backspace) {
-      if (cursor_position() == 0) {
-        return false;
-      }
-      const size_t start = GlyphPosition(*content_, cursor_position() - 1);
-      const size_t end = GlyphPosition(*content_, cursor_position());
-      content_->erase(start, end - start);
-      cursor_position()--;
-      option_->on_change();
-      return true;
+  bool HandleBackspace() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == 0) {
+      return false;
     }
+    const size_t start = GlyphPrevious(content_(), cursor_position);
+    const size_t end = cursor_position;
+    content_->erase(start, end - start);
+    cursor_position = start;
+    return true;
+  }
 
-    // Delete
-    if (event == Event::Delete) {
-      if (cursor_position() == int(content_->size())) {
-        return false;
-      }
-      const size_t start = GlyphPosition(*content_, cursor_position());
-      const size_t end = GlyphPosition(*content_, cursor_position() + 1);
-      content_->erase(start, end - start);
-      option_->on_change();
-      return true;
+  bool HandleDelete() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == (int)content_->size()) {
+      return false;
     }
+    const size_t start = cursor_position;
+    const size_t end = GlyphNext(content_(), cursor_position);
+    content_->erase(start, end - start);
+    return true;
+  }
 
-    // Enter.
-    if (event == Event::Return) {
-      option_->on_enter();
-      return true;
+  bool HandleArrowLeft() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == 0) {
+      return false;
     }
 
-    if (event == Event::Custom) {
+    cursor_position = GlyphPrevious(content_(), cursor_position);
+    return true;
+  }
+
+  bool HandleArrowRight() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == (int)content_->size()) {
       return false;
     }
 
-    // Arrow
-    if (event == Event::ArrowLeft && cursor_position() > 0) {
-      cursor_position()--;
-      return true;
+    cursor_position = GlyphNext(content_(), cursor_position);
+    return true;
+  }
+
+  size_t CursorColumn() {
+    int& cursor_position = option_->cursor_position();
+    size_t iter = cursor_position;
+    int width = 0;
+    while (true) {
+      if (iter == 0) {
+        break;
+      }
+      iter = GlyphPrevious(content_(), iter);
+      if (content_()[iter] == '\n') {
+        break;
+      }
+      width += GlyphWidth(content_(), iter);
     }
+    return width;
+  }
 
-    if (event == Event::ArrowRight &&
-        cursor_position() < static_cast<int>(content_->size())) {
-      cursor_position()++;
-      return true;
+  // Move the cursor `columns` on the right, if possible.
+  void MoveCursorColumn(int columns) {
+    int& cursor_position = option_->cursor_position();
+    while (columns > 0) {
+      if (cursor_position == (int)content_().size() ||
+          content_()[cursor_position] == '\n') {
+        return;
+      }
+
+      columns -= GlyphWidth(content_(), cursor_position);
+      cursor_position = GlyphNext(content_(), cursor_position);
     }
+  }
 
-    // CTRL + Arrow:
-    if (event == Event::ArrowLeftCtrl) {
-      HandleLeftCtrl();
-      return true;
+  bool HandleArrowUp() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == 0) {
+      return false;
     }
-    if (event == Event::ArrowRightCtrl) {
-      HandleRightCtrl();
-      return true;
+
+    size_t columns = CursorColumn();
+
+    // Move cursor at the beginning of 2 lines above.
+    while (true) {
+      if (cursor_position == 0) {
+        return true;
+      }
+      size_t previous = GlyphPrevious(content_(), cursor_position);
+      if (content_()[previous] == '\n') {
+        break;
+      }
+      cursor_position = previous;
+    }
+    cursor_position = GlyphPrevious(content_(), cursor_position);
+    while (true) {
+      if (cursor_position == 0) {
+        break;
+      }
+      size_t previous = GlyphPrevious(content_(), cursor_position);
+      if (content_()[previous] == '\n') {
+        break;
+      }
+      cursor_position = previous;
     }
 
-    if (event == Event::Home) {
-      cursor_position() = 0;
-      return true;
+    MoveCursorColumn(columns);
+    return true;
+  }
+
+  bool HandleArrowDown() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == (int)content_->size()) {
+      return false;
     }
 
-    if (event == Event::End) {
-      cursor_position() = GlyphCount(*content_);
-      return true;
+    size_t columns = CursorColumn();
+
+    // Move cursor at the beginning of the next line
+    while (true) {
+      if (content_()[cursor_position] == '\n') {
+        break;
+      }
+      cursor_position = GlyphNext(content_(), cursor_position);
+      if (cursor_position == (int)content_().size()) {
+        return true;
+      }
+    }
+    cursor_position = GlyphNext(content_(), cursor_position);
+
+    MoveCursorColumn(columns);
+    return true;
+  }
+
+  bool HandleHome() {
+    int& cursor_position = option_->cursor_position();
+    cursor_position = 0;
+    return true;
+  }
+
+  bool HandleEnd() {
+    int& cursor_position = option_->cursor_position();
+    cursor_position = content_->size();
+    return true;
+  }
+
+  bool HandleReturn() {
+    int& cursor_position = option_->cursor_position();
+    content_->insert(cursor_position, "\n");
+    cursor_position++;
+    option_->on_change();
+    return true;
+  }
+
+  bool HandleCharacter(const std::string& character) {
+    if (character == "\n" && !option_->multiline()) {
+      option_->on_enter();
+      return false;
     }
 
-    // Content
+    int& cursor_position = option_->cursor_position();
+    content_->insert(cursor_position, character);
+    cursor_position += character.size();
+    option_->on_change();
+
+    if (character == "\n") {
+      option_->on_enter();
+    }
+    return true;
+  }
+
+  bool OnEvent(Event event) override {
+    int& cursor_position = option_->cursor_position();
+    cursor_position = util::clamp(cursor_position, 0, (int)content_->size());
+
     if (event.is_character()) {
-      const size_t start = GlyphPosition(*content_, cursor_position());
-      content_->insert(start, event.character());
-      cursor_position()++;
-      option_->on_change();
-      return true;
+      return HandleCharacter(event.character());
+    }
+    if (event.is_mouse()) {
+      return HandleMouse(event);
+    }
+    if (event == Event::Backspace) {
+      return HandleBackspace();
+    }
+    if (event == Event::Delete) {
+      return HandleDelete();
+    }
+    if (event == Event::ArrowLeft) {
+      return HandleArrowLeft();
+    }
+    if (event == Event::ArrowRight) {
+      return HandleArrowRight();
+    }
+    if (event == Event::ArrowUp) {
+      return HandleArrowUp();
     }
+    if (event == Event::ArrowDown) {
+      return HandleArrowDown();
+    }
+    if (event == Event::Home) {
+      return HandleHome();
+    }
+    if (event == Event::End) {
+      return HandleEnd();
+    }
+    if (event == Event::ArrowLeftCtrl) {
+      return HandleLeftCtrl();
+    }
+    if (event == Event::ArrowRightCtrl) {
+      return HandleRightCtrl();
+    }
+    if (event == Event::Return) {
+      return HandleReturn();
+    }
+
     return false;
   }
 
- private:
-  void HandleLeftCtrl() {
-    auto properties = Utf8ToWordBreakProperty(*content_);
-
-    // Move left, as long as left is not a word character.
-    while (cursor_position() > 0 &&
-           !IsWordCharacter(properties[cursor_position() - 1])) {
-      cursor_position()--;
+  bool HandleLeftCtrl() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == 0) {
+      return false;
     }
 
+    // Move left, as long as left it not a word.
+    while (cursor_position) {
+      size_t previous = GlyphPrevious(content_(), cursor_position);
+      if (IsWordCharacter(content_(), previous)) {
+        break;
+      }
+      cursor_position = previous;
+    }
     // Move left, as long as left is a word character:
-    while (cursor_position() > 0 &&
-           IsWordCharacter(properties[cursor_position() - 1])) {
-      cursor_position()--;
+    while (cursor_position) {
+      size_t previous = GlyphPrevious(content_(), cursor_position);
+      if (!IsWordCharacter(content_(), previous)) {
+        break;
+      }
+      cursor_position = previous;
     }
+    return true;
   }
 
-  void HandleRightCtrl() {
-    auto properties = Utf8ToWordBreakProperty(*content_);
-    const int max = properties.size();
-
-    // Move right, as long as right is not a word character.
-    while (cursor_position() < max &&
-           !IsWordCharacter(properties[cursor_position()])) {
-      cursor_position()++;
+  bool HandleRightCtrl() {
+    int& cursor_position = option_->cursor_position();
+    if (cursor_position == (int)content_().size()) {
+      return false;
     }
 
+    // Move right, until entering a word.
+    while (cursor_position < (int)content_().size()) {
+      cursor_position = GlyphNext(content_(), cursor_position);
+      if (IsWordCharacter(content_(), cursor_position)) {
+        break;
+      }
+    }
     // Move right, as long as right is a word character:
-    while (cursor_position() < max &&
-           IsWordCharacter(properties[cursor_position()])) {
-      cursor_position()++;
+    while (cursor_position < (int)content_().size()) {
+      size_t next = GlyphNext(content_(), cursor_position);
+      if (!IsWordCharacter(content_(), cursor_position)) {
+        break;
+      }
+      cursor_position = next;
     }
+
+    return true;
   }
 
-  bool OnMouseEvent(Event event) {
-    hovered_ =
-        box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
+  bool HandleMouse(Event event) {
+    hovered_ = box_.Contain(event.mouse().x,  //
+                            event.mouse().y) &&
+               CaptureMouse(event);
     if (!hovered_) {
       return false;
     }
@@ -270,32 +475,56 @@ class InputBase : public ComponentBase {
     }
 
     TakeFocus();
+
     if (content_->empty()) {
+      option_->cursor_position() = 0;
       return true;
     }
 
-    auto mapping = CellToGlyphIndex(*content_);
-    int original_glyph = cursor_position();
-    original_glyph = util::clamp(original_glyph, 0, int(mapping.size()));
-    size_t original_cell = 0;
-    for (size_t i = 0; i < mapping.size(); i++) {
-      if (mapping[i] == original_glyph) {
-        original_cell = i;
+    // Find the line and index of the cursor.
+    std::vector<std::string> lines = Split(*content_);
+    int& cursor_position = option_->cursor_position();
+    int cursor_line = 0;
+    int cursor_char_index = cursor_position;
+    for (const auto& line : lines) {
+      if (cursor_char_index <= (int)line.size()) {
         break;
       }
+
+      cursor_char_index -= line.size() + 1;
+      cursor_line++;
     }
-    if (mapping[original_cell] != original_glyph) {
-      original_cell = mapping.size();
+    int cursor_column =
+        string_width(lines[cursor_line].substr(0, cursor_char_index));
+
+    int new_cursor_column = cursor_column + event.mouse().x - cursor_box_.x_min;
+    int new_cursor_line = cursor_line + event.mouse().y - cursor_box_.y_min;
+
+    // Fix the new cursor position:
+    new_cursor_line = std::max(std::min(new_cursor_line, (int)lines.size()), 0);
+
+    std::string empty_string;
+    const std::string& line = new_cursor_line < (int)lines.size()
+                                  ? lines[new_cursor_line]
+                                  : empty_string;
+    new_cursor_column = util::clamp(new_cursor_column, 0, string_width(line));
+
+    if (new_cursor_column == cursor_column &&  //
+        new_cursor_line == cursor_line) {
+      return false;
     }
-    const int target_cell =
-        int(original_cell) + event.mouse().x - cursor_box_.x_min;
-    int target_glyph = target_cell < int(mapping.size()) ? mapping[target_cell]
-                                                         : int(mapping.size());
-    target_glyph = util::clamp(target_glyph, 0, GlyphCount(*content_));
-    if (cursor_position() != target_glyph) {
-      cursor_position() = target_glyph;
-      option_->on_change();
+
+    // Convert back the new_cursor_{line,column} toward cursor_position:
+    cursor_position = 0;
+    for (int i = 0; i < new_cursor_line; ++i) {
+      cursor_position += lines[i].size() + 1;
+    }
+    while (new_cursor_column > 0) {
+      new_cursor_column -= GlyphWidth(content_(), cursor_position);
+      cursor_position = GlyphNext(content_(), cursor_position);
     }
+
+    option_->on_change();
     return true;
   }
 
@@ -303,7 +532,6 @@ class InputBase : public ComponentBase {
 
   bool hovered_ = false;
   StringRef content_;
-  ConstStringRef placeholder_;
 
   Box box_;
   Box cursor_box_;
@@ -314,7 +542,6 @@ class InputBase : public ComponentBase {
 
 /// @brief An input box for editing text.
 /// @param content The editable content.
-/// @param placeholder The text displayed when content is still empty.
 /// @param option Additional optional parameters.
 /// @ingroup component
 /// @see InputBase
@@ -334,15 +561,19 @@ class InputBase : public ComponentBase {
 /// ```bash
 /// placeholder
 /// ```
+Component Input(StringRef content, Ref<InputOption> option) {
+  return Make<InputBase>(std::move(content), std::move(option));
+}
+
 Component Input(StringRef content,
-                ConstStringRef placeholder,
+                StringRef placeholder,
                 Ref<InputOption> option) {
-  return Make<InputBase>(std::move(content), std::move(placeholder),
-                         std::move(option));
+  option->placeholder = placeholder;
+  return Make<InputBase>(std::move(content), std::move(option));
 }
 
 }  // namespace ftxui
 
-// Copyright 2020 Arthur Sonzogni. All rights reserved.
+// Copyright 2022 Arthur Sonzogni. All rights reserved.
 // Use of this source code is governed by the MIT license that can be found in
 // the LICENSE file.
diff --git a/src/ftxui/component/input_test.cpp b/src/ftxui/component/input_test.cpp
index f187e828ea1884c6fc873a3d78299c357133743e..057c3bd4101459b1d24b49fab2968cdaa27448de 100644
--- a/src/ftxui/component/input_test.cpp
+++ b/src/ftxui/component/input_test.cpp
@@ -1,27 +1,23 @@
-#include <gtest/gtest.h>
 #include <memory>  // for __shared_ptr_access, shared_ptr, allocator
 #include <string>  // for string
 
 #include "ftxui/component/component.hpp"       // for Input
 #include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
 #include "ftxui/component/component_options.hpp"  // for InputOption
-#include "ftxui/component/event.hpp"  // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Delete, Event::End, Event::Home
+#include "ftxui/component/event.hpp"  // for Event, Event::ArrowRightCtrl, Event::ArrowLeftCtrl, Event::ArrowLeft, Event::ArrowRight, Event::ArrowDown, Event::ArrowUp, Event::Delete, Event::Backspace, Event::Return, Event::End, Event::Home
 #include "ftxui/component/mouse.hpp"  // for Mouse, Mouse::Button, Mouse::Left, Mouse::Motion, Mouse::Pressed
 #include "ftxui/dom/elements.hpp"   // for Fit
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Fixed, Screen, Pixel
 #include "ftxui/util/ref.hpp"       // for Ref
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_TRUE, Test, EXPECT_FALSE, TEST
 
-// NOLINTBEGIN
 namespace ftxui {
 
 TEST(InputTest, Init) {
   std::string content;
-  std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  Component input = Input(&content, &placeholder, &option);
-
+  Component input = Input(&content, &option);
   EXPECT_EQ(option.cursor_position(), 0);
 }
 
@@ -29,413 +25,471 @@ TEST(InputTest, Type) {
   std::string content;
   std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  Component input = Input(&content, &placeholder, &option);
+  Component input = Input(&content, &option);
 
   input->OnEvent(Event::Character("a"));
   EXPECT_EQ(content, "a");
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_EQ(option.cursor_position(), 1);
 
   input->OnEvent(Event::Character('b'));
   EXPECT_EQ(content, "ab");
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_EQ(option.cursor_position(), 2);
+
+  input->OnEvent(Event::Return);
+  EXPECT_EQ(content, "ab\n");
+  EXPECT_EQ(option.cursor_position(), 3);
+
+  input->OnEvent(Event::Character('c'));
+  EXPECT_EQ(content, "ab\nc");
+  EXPECT_EQ(option.cursor_position(), 4);
 
   auto document = input->Render();
-  auto screen = Screen::Create(Dimension::Fit(document));
+
+  auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(2));
   Render(screen, document);
   EXPECT_EQ(screen.PixelAt(0, 0).character, "a");
   EXPECT_EQ(screen.PixelAt(1, 0).character, "b");
+  EXPECT_EQ(screen.PixelAt(0, 1).character, "c");
+  EXPECT_EQ(screen.PixelAt(1, 1).character, " ");
 }
 
-TEST(InputTest, TypePassword) {
-  std::string content;
-  std::string placeholder;
+TEST(InputTest, ArrowLeftRight) {
+  std::string content = "abc测测a测\na测\n";
   auto option = InputOption();
-  option.cursor_position = 0;
-  option.password = true;
-  Component input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
+  EXPECT_EQ(option.cursor_position(), 0);
 
-  input->OnEvent(Event::Character('a'));
-  EXPECT_EQ(content, "a");
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_EQ(option.cursor_position(), 0);
 
-  input->OnEvent(Event::Character('b'));
-  EXPECT_EQ(content, "ab");
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 1);
 
-  auto document = input->Render();
-  auto screen = Screen::Create(Dimension::Fit(document));
-  Render(screen, document);
-  EXPECT_EQ(screen.PixelAt(0, 0).character, "•");
-  EXPECT_EQ(screen.PixelAt(1, 0).character, "•");
-}
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_EQ(option.cursor_position(), 0);
 
-TEST(InputTest, Arrow) {
-  std::string content;
-  std::string placeholder;
-  auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 1);
 
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 2);
 
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 3);
 
-  input->OnEvent(Event::ArrowLeft);
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 6);
 
-  input->OnEvent(Event::ArrowLeft);
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 9);
 
-  input->OnEvent(Event::ArrowLeft);
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 10);
 
-  input->OnEvent(Event::ArrowLeft);
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 13);
 
-  input->OnEvent(Event::ArrowRight);
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 14);
 
-  input->OnEvent(Event::ArrowRight);
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 15);
 
-  input->OnEvent(Event::ArrowRight);
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 18);
 
-  input->OnEvent(Event::ArrowRight);
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 19);
+
+  EXPECT_FALSE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 19);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_EQ(option.cursor_position(), 18);
+}
+
+TEST(InputTest, ArrowUpDown) {
+  std::string content =
+      "0\n"
+      "00\n"
+      "000\n"
+      "0a0\n"
+      "00\n"
+      "0\n"
+      "";
+  auto option = InputOption();
+  auto input = Input(&content, &option);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 4);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 11);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 21);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 29);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 36);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 40);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 40);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 36);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 29);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 21);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 11);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 4);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 0);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 0);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_EQ(option.cursor_position(), 3);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 7);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 14);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 24);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 32);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 39);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 40);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(option.cursor_position(), 40);
+
+  option.cursor_position() = 39;
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 32);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 24);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 14);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_EQ(option.cursor_position(), 7);
 }
 
 TEST(InputTest, Insert) {
   std::string content;
-  std::string placeholder;
-  Component input = Input(&content, &placeholder);
+  Component input = Input(&content);
 
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
+  EXPECT_TRUE(input->OnEvent(Event::Character('a')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
   EXPECT_EQ(content, "abc");
 
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::Character('-'));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Character('-')));
   EXPECT_EQ(content, "a-bc");
 
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::Character('-'));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Character('-')));
   EXPECT_EQ(content, "a--bc");
 
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::Character('-'));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Character('-')));
   EXPECT_EQ(content, "-a--bc");
+
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_EQ(content, "-测a--bc");
+
+  EXPECT_TRUE(input->OnEvent(Event::Character("a")));
+  EXPECT_EQ(content, "-测aa--bc");
+
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_EQ(content, "-测a测a--bc");
 }
 
 TEST(InputTest, Home) {
   std::string content;
-  std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
-
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
-  EXPECT_EQ(content, "abc");
+  auto input = Input(&content, &option);
+
+  EXPECT_TRUE(input->OnEvent(Event::Character('a')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_TRUE(input->OnEvent(Event::Return));
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 9u);
 
-  EXPECT_EQ(option.cursor_position(), 3u);
-  input->OnEvent(Event::Home);
+  EXPECT_TRUE(input->OnEvent(Event::Home));
   EXPECT_EQ(option.cursor_position(), 0u);
 
-  input->OnEvent(Event::Character('-'));
-  EXPECT_EQ(content, "-abc");
+  EXPECT_TRUE(input->OnEvent(Event::Character('-')));
+  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_EQ(content, "-abc\n测bc");
 }
 
 TEST(InputTest, End) {
   std::string content;
   std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
-
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
-
-  input->OnEvent(Event::ArrowLeft);
-  input->OnEvent(Event::ArrowLeft);
+  auto input = Input(&content, &option);
+
+  EXPECT_TRUE(input->OnEvent(Event::Character('a')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_TRUE(input->OnEvent(Event::Return));
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowUp));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 2u);
 
-  EXPECT_EQ(option.cursor_position(), 1u);
   input->OnEvent(Event::End);
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_EQ(option.cursor_position(), 9u);
 }
 
 TEST(InputTest, Delete) {
   std::string content;
   std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
 
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
-  input->OnEvent(Event::ArrowLeft);
+  EXPECT_TRUE(input->OnEvent(Event::Character('a')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_TRUE(input->OnEvent(Event::Return));
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
 
-  EXPECT_EQ(content, "abc");
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 9u);
 
-  input->OnEvent(Event::Delete);
-  EXPECT_EQ(content, "ab");
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_FALSE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 9u);
 
-  input->OnEvent(Event::Delete);
-  EXPECT_EQ(content, "ab");
-  EXPECT_EQ(option.cursor_position(), 2u);
-}
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 8u);
 
-TEST(InputTest, Backspace) {
-  std::string content;
-  std::string placeholder;
-  auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abc\n测b");
+  EXPECT_EQ(option.cursor_position(), 8u);
 
-  input->OnEvent(Event::Character('a'));
-  input->OnEvent(Event::Character('b'));
-  input->OnEvent(Event::Character('c'));
-  input->OnEvent(Event::ArrowLeft);
+  EXPECT_FALSE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abc\n测b");
+  EXPECT_EQ(option.cursor_position(), 8u);
 
-  EXPECT_EQ(content, "abc");
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abc\nb");
+  EXPECT_EQ(option.cursor_position(), 4u);
 
-  input->OnEvent(Event::Backspace);
-  EXPECT_EQ(content, "ac");
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abcb");
+  EXPECT_EQ(option.cursor_position(), 3u);
 
-  input->OnEvent(Event::Backspace);
-  EXPECT_EQ(content, "c");
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "abc");
+  EXPECT_EQ(option.cursor_position(), 3u);
 
-  input->OnEvent(Event::Backspace);
-  EXPECT_EQ(content, "c");
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_TRUE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "");
+
+  EXPECT_FALSE(input->OnEvent(Event::Delete));
+  EXPECT_EQ(content, "");
 }
 
-TEST(InputTest, MouseClick) {
+TEST(InputTest, Backspace) {
   std::string content;
   std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
 
-  input->OnEvent(Event::Character("a"));
-  input->OnEvent(Event::Character("b"));
-  input->OnEvent(Event::Character("c"));
-  input->OnEvent(Event::Character("d"));
-
-  EXPECT_EQ(option.cursor_position(), 4u);
+  EXPECT_TRUE(input->OnEvent(Event::Character('a')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
+  EXPECT_TRUE(input->OnEvent(Event::Return));
+  EXPECT_TRUE(input->OnEvent(Event::Character("测")));
+  EXPECT_TRUE(input->OnEvent(Event::Character('b')));
+  EXPECT_TRUE(input->OnEvent(Event::Character('c')));
 
-  auto render = [&] {
-    auto document = input->Render();
-    auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(1));
-    Render(screen, document);
-  };
-  render();
+  EXPECT_EQ(content, "abc\n测bc");
+  EXPECT_EQ(option.cursor_position(), 9u);
 
-  Mouse mouse;
-  mouse.button = Mouse::Button::Left;
-  mouse.motion = Mouse::Motion::Pressed;
-  mouse.y = 0;
-  mouse.shift = false;
-  mouse.meta = false;
-  mouse.control = false;
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "abc\n测b");
+  EXPECT_EQ(option.cursor_position(), 8u);
 
-  mouse.x = 0;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeft));
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "abc\nb");
+  EXPECT_EQ(option.cursor_position(), 4u);
 
-  mouse.x = 2;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "abcb");
+  EXPECT_EQ(option.cursor_position(), 3u);
 
-  mouse.x = 2;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "abb");
   EXPECT_EQ(option.cursor_position(), 2u);
 
-  mouse.x = 1;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "ab");
   EXPECT_EQ(option.cursor_position(), 1u);
 
-  mouse.x = 3;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "b");
+  EXPECT_EQ(option.cursor_position(), 0u);
 
-  mouse.x = 4;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 4u);
+  EXPECT_FALSE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "b");
+  EXPECT_EQ(option.cursor_position(), 0u);
 
-  mouse.x = 5;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 4u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRight));
+  EXPECT_TRUE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "");
+  EXPECT_EQ(option.cursor_position(), 0u);
+
+  EXPECT_FALSE(input->OnEvent(Event::Backspace));
+  EXPECT_EQ(content, "");
+  EXPECT_EQ(option.cursor_position(), 0u);
 }
 
-TEST(InputTest, MouseClickComplex) {
-  std::string content;
+TEST(InputTest, CtrlArrow) {
+  std::string content =
+      "word word 测ord wo测d word\n"
+      "coucou    coucou coucou\n"
+      "coucou coucou coucou\n";
   std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  option.cursor_position = 1000;
+  auto input = Input(&content, &option);
 
-  input->OnEvent(Event::Character("测"));
-  input->OnEvent(Event::Character("试"));
-  input->OnEvent(Event::Character("a⃒"));
-  input->OnEvent(Event::Character("ā"));
+  // Use CTRL+Left several time
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 67);
 
-  EXPECT_EQ(option.cursor_position(), 4u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 60);
 
-  auto render = [&] {
-    auto document = input->Render();
-    auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(1));
-    Render(screen, document);
-  };
-  render();
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 53);
 
-  Mouse mouse;
-  mouse.button = Mouse::Button::Left;
-  mouse.motion = Mouse::Motion::Pressed;
-  mouse.y = 0;
-  mouse.shift = false;
-  mouse.meta = false;
-  mouse.control = false;
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 46);
 
-  mouse.x = 0;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 39);
 
-  mouse.x = 0;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 29);
 
-  mouse.x = 1;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 24);
 
-  mouse.x = 1;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 17);
 
-  mouse.x = 2;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 10);
 
-  mouse.x = 2;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 1u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 5);
 
-  mouse.x = 1;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 0);
 
-  mouse.x = 4;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 2u);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 0);
 
-  mouse.x = 5;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 4);
 
-  mouse.x = 6;
-  input->OnEvent(Event::Mouse("", mouse));
-  render();
-  EXPECT_EQ(option.cursor_position(), 4u);
-}
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 9);
 
-TEST(InputTest, CtrlArrowLeft) {
-  std::string content = "word word 测ord wo测d word";
-  //                     0    5    10    15    20
-  std::string placeholder;
-  auto option = InputOption();
-  option.cursor_position = 22;
-  auto input = Input(&content, &placeholder, &option);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 16);
 
-  // Use CTRL+Left several time
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 20u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 23);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 15u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 28);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 10u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 35);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 5u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 45);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 52);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 59);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 66);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 73);
 }
 
 TEST(InputTest, CtrlArrowLeft2) {
   std::string content = "   word  word  测ord  wo测d  word   ";
-  //                     0  3  6  9 12  15  18 21  24 27 30 33
-  std::string placeholder;
   auto option = InputOption();
   option.cursor_position = 33;
-  auto input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
 
   // Use CTRL+Left several time
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 27u);
+  EXPECT_EQ(option.cursor_position(), 31);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 21u);
+  EXPECT_EQ(option.cursor_position(), 23);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 15u);
+  EXPECT_EQ(option.cursor_position(), 15);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 9u);
+  EXPECT_EQ(option.cursor_position(), 9);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 3u);
+  EXPECT_EQ(option.cursor_position(), 3);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_EQ(option.cursor_position(), 0);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowLeftCtrl));
-  EXPECT_EQ(option.cursor_position(), 0u);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowLeftCtrl));
+  EXPECT_EQ(option.cursor_position(), 0);
 }
 
 TEST(InputTest, CtrlArrowRight) {
-  std::string content = "word word 测ord wo测d word";
-  //                     0    5    10    15    20
-  std::string placeholder;
+  std::string content =
+      "word word 测ord wo测d word\n"
+      "coucou dfqdsf jmlkjm";
+
   auto option = InputOption();
   option.cursor_position = 2;
-  auto input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
 
   // Use CTRL+Left several time
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
@@ -445,52 +499,231 @@ TEST(InputTest, CtrlArrowRight) {
   EXPECT_EQ(option.cursor_position(), 9);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 14u);
+  EXPECT_EQ(option.cursor_position(), 16);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 23);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 19u);
+  EXPECT_EQ(option.cursor_position(), 28);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 24u);
+  EXPECT_EQ(option.cursor_position(), 35);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 24u);
+  EXPECT_EQ(option.cursor_position(), 42);
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 49);
+
+  EXPECT_FALSE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 49);
 }
 
 TEST(InputTest, CtrlArrowRight2) {
   std::string content = "   word  word  测ord  wo测d  word   ";
-  //                     0  3  6  9 12  15  18 21  24 27 30 33
-  std::string placeholder;
   auto option = InputOption();
-  option.cursor_position = 0;
-  auto input = Input(&content, &placeholder, &option);
+  auto input = Input(&content, &option);
 
   // Use CTRL+Left several time
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 7u);
+  EXPECT_EQ(option.cursor_position(), 7);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 13u);
+  EXPECT_EQ(option.cursor_position(), 13);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 19u);
+  EXPECT_EQ(option.cursor_position(), 21);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 25u);
+  EXPECT_EQ(option.cursor_position(), 29);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 31u);
+  EXPECT_EQ(option.cursor_position(), 35);
 
   EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 34u);
+  EXPECT_EQ(option.cursor_position(), 38);
 
-  EXPECT_TRUE(input->OnEvent(Event::ArrowRightCtrl));
-  EXPECT_EQ(option.cursor_position(), 34u);
+  EXPECT_FALSE(input->OnEvent(Event::ArrowRightCtrl));
+  EXPECT_EQ(option.cursor_position(), 38);
+}
+
+TEST(InputTest, TypePassword) {
+  std::string content;
+  std::string placeholder;
+  auto option = InputOption();
+  option.cursor_position = 0;
+  option.password = true;
+  Component input = Input(&content, &placeholder, &option);
+
+  input->OnEvent(Event::Character('a'));
+  EXPECT_EQ(content, "a");
+  EXPECT_EQ(option.cursor_position(), 1u);
+
+  input->OnEvent(Event::Character('b'));
+  EXPECT_EQ(content, "ab");
+  EXPECT_EQ(option.cursor_position(), 2u);
+
+  auto document = input->Render();
+  auto screen = Screen::Create(Dimension::Fit(document));
+  Render(screen, document);
+  EXPECT_EQ(screen.PixelAt(0, 0).character, "•");
+  EXPECT_EQ(screen.PixelAt(1, 0).character, "•");
+}
+
+TEST(InputTest, MouseClick) {
+  std::string content;
+  auto option = InputOption();
+  auto input = Input(&content, &option);
+
+  input->OnEvent(Event::Character("a"));
+  input->OnEvent(Event::Character("b"));
+  input->OnEvent(Event::Character("c"));
+  input->OnEvent(Event::Character("d"));
+  input->OnEvent(Event::Return);
+  input->OnEvent(Event::Character("a"));
+  input->OnEvent(Event::Character("b"));
+  input->OnEvent(Event::Character("c"));
+  input->OnEvent(Event::Character("d"));
+  input->OnEvent(Event::Return);
+
+  EXPECT_EQ(content, "abcd\nabcd\n");
+  EXPECT_EQ(option.cursor_position(), 10u);
+
+  auto render = [&] {
+    auto document = input->Render();
+    auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(3));
+    Render(screen, document);
+  };
+  render();
+  EXPECT_EQ(option.cursor_position(), 10u);
+
+  Mouse mouse;
+  mouse.button = Mouse::Button::Left;
+  mouse.motion = Mouse::Motion::Pressed;
+  mouse.shift = false;
+  mouse.meta = false;
+  mouse.control = false;
+
+  mouse.x = 0;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 0u);
+
+  mouse.x = 2;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 2u);
+
+  mouse.x = 2;
+  mouse.y = 0;
+  EXPECT_FALSE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 2u);
+
+  mouse.x = 1;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 1u);
+
+  mouse.x = 3;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 3u);
+
+  mouse.x = 4;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 4u);
+
+  mouse.x = 5;
+  mouse.y = 0;
+  EXPECT_FALSE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 4u);
+
+  mouse.x = 5;
+  mouse.y = 1;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 9u);
+
+  mouse.x = 1;
+  mouse.y = 1;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 6u);
+
+  mouse.x = 4;
+  mouse.y = 2;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 10u);
+}
+
+TEST(InputTest, MouseClickComplex) {
+  std::string content;
+  auto option = InputOption();
+  auto input = Input(&content, &option);
+
+  input->OnEvent(Event::Character("测"));
+  input->OnEvent(Event::Character("试"));
+  input->OnEvent(Event::Character("a⃒"));
+  input->OnEvent(Event::Character("ā"));
+  input->OnEvent(Event::Return);
+  input->OnEvent(Event::Character("测"));
+  input->OnEvent(Event::Character("试"));
+  input->OnEvent(Event::Character("a⃒"));
+  input->OnEvent(Event::Character("ā"));
+
+  EXPECT_EQ(option.cursor_position(), 27u);
+
+  auto render = [&] {
+    auto document = input->Render();
+    auto screen = Screen::Create(Dimension::Fixed(100), Dimension::Fixed(4));
+    Render(screen, document);
+  };
+  render();
+
+  Mouse mouse;
+  mouse.button = Mouse::Button::Left;
+  mouse.motion = Mouse::Motion::Pressed;
+  mouse.shift = false;
+  mouse.meta = false;
+  mouse.control = false;
+
+  mouse.x = 0;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 0);
+
+  mouse.x = 0;
+  mouse.y = 1;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 14);
+
+  mouse.x = 1;
+  mouse.y = 0;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 3);
+
+  mouse.x = 1;
+  mouse.y = 1;
+  EXPECT_TRUE(input->OnEvent(Event::Mouse("", mouse)));
+  render();
+  EXPECT_EQ(option.cursor_position(), 17);
 }
 
 }  // namespace ftxui
-// NOLINTEND
 
-// Copyright 2021 Arthur Sonzogni. All rights reserved.
+// Copyright 2023 Arthur Sonzogni. All rights reserved.
 // Use of this source code is governed by the MIT license that can be found in
 // the LICENSE file.
diff --git a/src/ftxui/component/radiobox_test.cpp b/src/ftxui/component/radiobox_test.cpp
index c743a227c9ba2c84c08a9d2777a01db2b55ed96c..eca86ecf33b71d2325512a377a35f8a45efcf92b 100644
--- a/src/ftxui/component/radiobox_test.cpp
+++ b/src/ftxui/component/radiobox_test.cpp
@@ -1,4 +1,3 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_TRUE, Test, TestInfo (ptr only), EXPECT_FALSE, TEST
 #include <ftxui/dom/elements.hpp>   // for yframe
 #include <ftxui/dom/node.hpp>       // for Render
 #include <ftxui/screen/screen.hpp>  // for Screen
@@ -11,6 +10,7 @@
 #include "ftxui/component/component_options.hpp"  // for RadioboxOption
 #include "ftxui/component/event.hpp"  // for Event, Event::Return, Event::ArrowDown, Event::End, Event::Home, Event::Tab, Event::TabReverse, Event::PageDown, Event::PageUp, Event::ArrowUp
 #include "ftxui/util/ref.hpp"         // for Ref
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, EXPECT_TRUE, Test, EXPECT_FALSE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/receiver_test.cpp b/src/ftxui/component/receiver_test.cpp
index a45e9f341af2a2717001eecab52babebaa589fc7..0d83b7edeabf68a9bf5183ea86ee2f3d84c9cf92 100644
--- a/src/ftxui/component/receiver_test.cpp
+++ b/src/ftxui/component/receiver_test.cpp
@@ -1,8 +1,9 @@
-#include <gtest/gtest.h>
+#include <string>   // for string
 #include <thread>   // for thread
 #include <utility>  // for move
 
 #include "ftxui/component/receiver.hpp"
+#include "gtest/gtest.h"  // for AssertionResult, Message, Test, TestPartResult, EXPECT_EQ, EXPECT_TRUE, EXPECT_FALSE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/resizable_split_test.cpp b/src/ftxui/component/resizable_split_test.cpp
index 627d4129c1025a47d229c7133ab5b9f98a1cb99d..851f9c33e0d0cc2729373f8aa521808789995da7 100644
--- a/src/ftxui/component/resizable_split_test.cpp
+++ b/src/ftxui/component/resizable_split_test.cpp
@@ -1,6 +1,6 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, TestInfo (ptr only), TEST
 #include <ftxui/dom/direction.hpp>  // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
 #include <memory>  // for __shared_ptr_access, shared_ptr, allocator
+#include <string>  // for string
 
 #include "ftxui/component/component.hpp"  // for ResizableSplit, Renderer, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
 #include "ftxui/component/component_base.hpp"  // for ComponentBase, Component
@@ -9,6 +9,7 @@
 #include "ftxui/dom/elements.hpp"   // for Element, separatorDouble, text
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/slider_test.cpp b/src/ftxui/component/slider_test.cpp
index c6159db8f7df8e6edb72014aeb2d054cc2cdcb87..af4d7f192d428ae175082d09012a1b13c6936d84 100644
--- a/src/ftxui/component/slider_test.cpp
+++ b/src/ftxui/component/slider_test.cpp
@@ -1,17 +1,17 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, TestInfo (ptr only), EXPECT_FALSE, TEST
-#include <array>          // for array
-#include <cstddef>        // for size_t
+#include <array>                      // for array
+#include <cstddef>                    // for size_t
 #include <ftxui/component/mouse.hpp>  // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
 #include <ftxui/dom/direction.hpp>  // for Direction, Direction::Down, Direction::Left, Direction::Right, Direction::Up
 #include <ftxui/dom/elements.hpp>   // for frame
 #include <memory>  // for shared_ptr, __shared_ptr_access, allocator
-#include <string>  // for to_string
+#include <string>  // for string, to_string
 
 #include "ftxui/component/component.hpp"  // for Slider, Vertical, operator|=
 #include "ftxui/component/component_base.hpp"  // for ComponentBase
 #include "ftxui/component/event.hpp"           // for Event, Event::ArrowDown
 #include "ftxui/dom/node.hpp"                  // for Render
 #include "ftxui/screen/screen.hpp"             // for Screen
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, Test, EXPECT_EQ, EXPECT_TRUE, EXPECT_FALSE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp
index 72da0d16608aab6643fde2d145d662fc3d845b7d..9fad1ba132f5a11d8cca2b54feb83d03ab2cea4c 100644
--- a/src/ftxui/component/terminal_input_parser.cpp
+++ b/src/ftxui/component/terminal_input_parser.cpp
@@ -37,12 +37,12 @@ const std::map<std::string, std::string> g_uniformize = {
     //   Home    ESC [ H   ESC O H
     //   End     ESC [ F   ESC O F
     //
-    {"\x1BOA", "\x1B[A"}, // UP
-    {"\x1BOB", "\x1B[B"}, // DOWN
-    {"\x1BOC", "\x1B[C"}, // RIGHT
-    {"\x1BOD", "\x1B[D"}, // LEFT
-    {"\x1BOH", "\x1B[H"}, // HOME
-    {"\x1BOF", "\x1B[F"}, // END
+    {"\x1BOA", "\x1B[A"},  // UP
+    {"\x1BOB", "\x1B[B"},  // DOWN
+    {"\x1BOC", "\x1B[C"},  // RIGHT
+    {"\x1BOD", "\x1B[D"},  // LEFT
+    {"\x1BOH", "\x1B[H"},  // HOME
+    {"\x1BOF", "\x1B[F"},  // END
 
 };
 
diff --git a/src/ftxui/component/terminal_input_parser_test.cpp b/src/ftxui/component/terminal_input_parser_test.cpp
index 4eec23b45e048ec666e48da748c6317161b4db19..79ca86d9ca56fd7688ba99c3c83529d555e14aee 100644
--- a/src/ftxui/component/terminal_input_parser_test.cpp
+++ b/src/ftxui/component/terminal_input_parser_test.cpp
@@ -1,13 +1,13 @@
-#include <gtest/gtest.h>  // for AssertionResult, Test, Message, TestPartResult, SuiteApiResolver, TestInfo (ptr only), EXPECT_EQ, EXPECT_TRUE, TEST, TestFactoryImpl, EXPECT_FALSE
 #include <ftxui/component/mouse.hpp>  // for Mouse, Mouse::Left, Mouse::Middle, Mouse::Pressed, Mouse::Released, Mouse::Right
 #include <ftxui/component/task.hpp>   // for Task
 #include <initializer_list>           // for initializer_list
 #include <memory>                     // for allocator, unique_ptr
 #include <variant>                    // for get
 
-#include "ftxui/component/event.hpp"  // for Event, Event::Return, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::F10, Event::F11, Event::F12, Event::F5, Event::F6, Event::F7, Event::F8, Event::F9, Event::Home, Event::PageDown, Event::PageUp, Event::Tab, Event::TabReverse, Event::Escape
+#include "ftxui/component/event.hpp"  // for Event, Event::Return, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::Backspace, Event::End, Event::Home, Event::Custom, Event::Delete, Event::F1, Event::F10, Event::F11, Event::F12, Event::F2, Event::F3, Event::F4, Event::F5, Event::F6, Event::F7, Event::F8, Event::F9, Event::PageDown, Event::PageUp, Event::Tab, Event::TabReverse, Event::Escape
 #include "ftxui/component/receiver.hpp"  // for MakeReceiver, ReceiverImpl
 #include "ftxui/component/terminal_input_parser.hpp"
+#include "gtest/gtest.h"  // for AssertionResult, Test, Message, TestPartResult, EXPECT_EQ, EXPECT_TRUE, TEST, EXPECT_FALSE
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/component/toggle_test.cpp b/src/ftxui/component/toggle_test.cpp
index 7e61fd1aadf900cddd1b9e13ba54c8df8a16bfc2..b3dd5b056fcf56fa5751df0645c452d7f2106a05 100644
--- a/src/ftxui/component/toggle_test.cpp
+++ b/src/ftxui/component/toggle_test.cpp
@@ -1,14 +1,14 @@
-#include <gtest/gtest.h>  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, Test, EXPECT_TRUE, TestInfo (ptr only), EXPECT_FALSE, TEST
-#include <functional>     // for function
-#include <memory>         // for __shared_ptr_access, shared_ptr, allocator
-#include <string>         // for string, basic_string
-#include <vector>         // for vector
+#include <functional>  // for function
+#include <memory>      // for __shared_ptr_access, shared_ptr, allocator
+#include <string>      // for string, basic_string
+#include <vector>      // for vector
 
 #include "ftxui/component/component.hpp"          // for Menu, Toggle
 #include "ftxui/component/component_base.hpp"     // for ComponentBase
 #include "ftxui/component/component_options.hpp"  // for MenuOption
 #include "ftxui/component/event.hpp"  // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
 #include "ftxui/util/ref.hpp"         // for Ref
+#include "gtest/gtest.h"  // for AssertionResult, Message, TestPartResult, EXPECT_EQ, Test, EXPECT_TRUE, EXPECT_FALSE, TEST
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/dom/blink_test.cpp b/src/ftxui/dom/blink_test.cpp
index c0713c1ec4ea921124ef386580582f9efcf6ae58..2a96825eb2c9bc521c51cee8d5dd299cced14cc0 100644
--- a/src/ftxui/dom/blink_test.cpp
+++ b/src/ftxui/dom/blink_test.cpp
@@ -1,9 +1,9 @@
-#include <gtest/gtest.h>
-#include <string>  // for allocator
+#include <string>  // for allocator, string
 
 #include "ftxui/dom/elements.hpp"   // for operator|, text, blink, Element
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen, Pixel
+#include "gtest/gtest.h"  // for Test, AssertionResult, EXPECT_TRUE, Message, TEST, TestPartResult
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/dom/bold_test.cpp b/src/ftxui/dom/bold_test.cpp
index c5ae167821231e7139087bd0797eabca34dc8f8e..9007bb3e36f6c9c9742aa27672e6c7f60b6ac646 100644
--- a/src/ftxui/dom/bold_test.cpp
+++ b/src/ftxui/dom/bold_test.cpp
@@ -1,9 +1,9 @@
-#include <gtest/gtest.h>
-#include <string>  // for allocator
+#include <string>  // for allocator, string
 
 #include "ftxui/dom/elements.hpp"   // for operator|, text, bold, Element
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen, Pixel
+#include "gtest/gtest.h"  // for Test, AssertionResult, EXPECT_TRUE, Message, TEST, TestPartResult
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/dom/dim_test.cpp b/src/ftxui/dom/dim_test.cpp
index 2073158ba1eb058685291a63b2132b9da0a7bbbf..d1d0198b70566fe9d38076fa21beba0a6b97abf7 100644
--- a/src/ftxui/dom/dim_test.cpp
+++ b/src/ftxui/dom/dim_test.cpp
@@ -1,9 +1,9 @@
-#include <gtest/gtest.h>
-#include <string>  // for allocator
+#include <string>  // for allocator, string
 
 #include "ftxui/dom/elements.hpp"   // for operator|, text, dim, Element
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen, Pixel
+#include "gtest/gtest.h"  // for Test, AssertionResult, EXPECT_TRUE, Message, TEST, TestPartResult
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/dom/text.cpp b/src/ftxui/dom/text.cpp
index 10017e9f6f22fea9b17c84eb24abe02b0bd856b5..c598be1a6bb82589da89d32fe7dad6065e29209e 100644
--- a/src/ftxui/dom/text.cpp
+++ b/src/ftxui/dom/text.cpp
@@ -35,6 +35,9 @@ class Text : public Node {
       if (x > box_.x_max) {
         return;
       }
+      if (cell == "\n") {
+        continue;
+      }
       screen.PixelAt(x, y).character = cell;
       ++x;
     }
diff --git a/src/ftxui/dom/underlined_test.cpp b/src/ftxui/dom/underlined_test.cpp
index ba30d1a1df9d0a50aa87bc8489ca6d427e946b51..acb3947879ac54fcd2d3d546cc836277dd94a929 100644
--- a/src/ftxui/dom/underlined_test.cpp
+++ b/src/ftxui/dom/underlined_test.cpp
@@ -1,9 +1,9 @@
-#include <gtest/gtest.h>
-#include <string>  // for allocator
+#include <string>  // for allocator, string
 
 #include "ftxui/dom/elements.hpp"   // for operator|, text, underlined, Element
 #include "ftxui/dom/node.hpp"       // for Render
 #include "ftxui/screen/screen.hpp"  // for Screen, Pixel
+#include "gtest/gtest.h"  // for Test, AssertionResult, EXPECT_TRUE, Message, TEST, TestPartResult
 
 // NOLINTBEGIN
 namespace ftxui {
diff --git a/src/ftxui/screen/string.cpp b/src/ftxui/screen/string.cpp
index 1fb5c33d73f7d2d0ef56c0830690d6a076a19ed6..37305e7de146a611e6bde56705c3a78fa49686c7 100644
--- a/src/ftxui/screen/string.cpp
+++ b/src/ftxui/screen/string.cpp
@@ -7,15 +7,22 @@
 
 #include "ftxui/screen/string.hpp"
 
-#include <array>    // for array
-#include <cstdint>  // for uint32_t, uint8_t, uint16_t, int32_t
-#include <string>   // for string, basic_string, wstring
-#include <tuple>    // for _Swallow_assign, ignore
+#include <stddef.h>  // for size_t
+#include <array>     // for array
+#include <cstdint>   // for uint32_t, uint8_t, uint16_t, int32_t
+#include <string>    // for string, basic_string, wstring
+#include <tuple>     // for _Swallow_assign, ignore
 
-#include "ftxui/screen/deprecated.hpp"  // for wchar_width, wstring_width
+#include "ftxui/screen/deprecated.hpp"       // for wchar_width, wstring_width
+#include "ftxui/screen/string_internal.hpp"  // for WordBreakProperty, EatCodePoint, CodepointToWordBreakProperty, GlyphCount, GlyphIterate, GlyphNext, GlyphPrevious, IsCombining, IsControl, IsFullWidth, Utf8ToWordBreakProperty
 
 namespace {
 
+using ftxui::EatCodePoint;
+using ftxui::IsCombining;
+using ftxui::IsControl;
+using ftxui::IsFullWidth;
+
 struct Interval {
   uint32_t first;
   uint32_t last;
@@ -1411,46 +1418,26 @@ bool Bisearch(uint32_t ucs, const std::array<C, N> table, C* out) {
   return false;
 }
 
-bool IsCombining(uint32_t ucs) {
-  return ftxui::CodepointToWordBreakProperty(ucs) == WBP::Extend;
-}
-
-bool IsFullWidth(uint32_t ucs) {
-  if (ucs < 0x0300)  // Quick path: // NOLINT
-    return false;
-
-  return Bisearch(ucs, g_full_width_characters);
-}
-
-bool IsControl(uint32_t ucs) {
-  if (ucs == 0) {
-    return true;
-  }
-  if (ucs < 32) {  // NOLINT
-    return true;
-  }
-  if (ucs >= 0x7f && ucs < 0xa0) {  // NOLINT
-    return true;
-  }
-  return false;
-}
-
 int codepoint_width(uint32_t ucs) {
-  if (IsControl(ucs)) {
+  if (ftxui::IsControl(ucs)) {
     return -1;
   }
 
-  if (IsCombining(ucs)) {
+  if (ftxui::IsCombining(ucs)) {
     return 0;
   }
 
-  if (IsFullWidth(ucs)) {
+  if (ftxui::IsFullWidth(ucs)) {
     return 2;
   }
 
   return 1;
 }
 
+}  // namespace
+
+namespace ftxui {
+
 // From UTF8 encoded string |input|, eat in between 1 and 4 byte representing
 // one codepoint. Put the codepoint into |ucs|. Start at |start| and update
 // |end| to represent the beginning of the next byte to eat for consecutive
@@ -1563,9 +1550,29 @@ bool EatCodePoint(const std::wstring& input,
   return true;
 }
 
-}  // namespace
+bool IsCombining(uint32_t ucs) {
+  return ftxui::CodepointToWordBreakProperty(ucs) == WBP::Extend;
+}
 
-namespace ftxui {
+bool IsFullWidth(uint32_t ucs) {
+  if (ucs < 0x0300)  // Quick path: // NOLINT
+    return false;
+
+  return Bisearch(ucs, g_full_width_characters);
+}
+
+bool IsControl(uint32_t ucs) {
+  if (ucs == 0) {
+    return true;
+  }
+  if (ucs < 32) {      // NOLINT
+    return ucs != 10;  // 10 => Line feed.
+  }
+  if (ucs >= 0x7f && ucs < 0xa0) {  // NOLINT
+    return true;
+  }
+  return false;
+}
 
 WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint) {
   WordBreakPropertyInterval interval = {0, 0, WBP::ALetter};
@@ -1660,12 +1667,35 @@ std::vector<std::string> Utf8ToGlyphs(const std::string& input) {
   return out;
 }
 
-int GlyphPosition(const std::string& input, size_t glyph_index, size_t start) {
-  if (glyph_index <= 0) {
-    return 0;
+size_t GlyphPrevious(const std::string& input, size_t start) {
+  while (true) {
+    if (start == 0) {
+      return 0;
+    }
+    start--;
+
+    // Skip the UTF8 continuation bytes.
+    if ((input[start] & 0b1100'0000) == 0b1000'0000) {
+      continue;
+    }
+
+    uint32_t codepoint = 0;
+    size_t end = 0;
+    const bool eaten = EatCodePoint(input, start, &end, &codepoint);
+
+    // Ignore invalid, control characters and combining characters.
+    if (!eaten || IsControl(codepoint) || IsCombining(codepoint)) {
+      continue;
+    }
+
+    return start;
   }
-  size_t end = 0;
+}
+
+size_t GlyphNext(const std::string& input, size_t start) {
+  bool glyph_found = false;
   while (start < input.size()) {
+    size_t end = 0;
     uint32_t codepoint = 0;
     const bool eaten = EatCodePoint(input, start, &end, &codepoint);
 
@@ -1677,17 +1707,31 @@ int GlyphPosition(const std::string& input, size_t glyph_index, size_t start) {
 
     // We eat the beginning of the next glyph. If we are eating the one
     // requested, return its start position immediately.
-    if (glyph_index == 0) {
+    if (glyph_found) {
       return static_cast<int>(start);
     }
 
     // Otherwise, skip this glyph and iterate:
-    glyph_index--;
+    glyph_found = true;
     start = end;
   }
   return static_cast<int>(input.size());
 }
 
+size_t GlyphIterate(const std::string& input, int glyph_offset, size_t start) {
+  if (glyph_offset >= 0) {
+    for (int i = 0; i < glyph_offset; ++i) {
+      start = GlyphNext(input, start);
+    }
+    return start;
+  } else {
+    for (int i = 0; i < -glyph_offset; ++i) {
+      start = GlyphPrevious(input, start);
+    }
+    return start;
+  }
+}
+
 std::vector<int> CellToGlyphIndex(const std::string& input) {
   int x = -1;
   std::vector<int> out;
diff --git a/src/ftxui/screen/string_internal.hpp b/src/ftxui/screen/string_internal.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eab4aa2376272fdb567fa14cb58bca18c2e8ef7e
--- /dev/null
+++ b/src/ftxui/screen/string_internal.hpp
@@ -0,0 +1,64 @@
+#ifndef FTXUI_SCREEN_STRING_INTERNAL_HPP
+#define FTXUI_SCREEN_STRING_INTERNAL_HPP
+
+namespace ftxui {
+
+bool EatCodePoint(const std::string& input,
+                  size_t start,
+                  size_t* end,
+                  uint32_t* ucs);
+bool EatCodePoint(const std::wstring& input,
+                  size_t start,
+                  size_t* end,
+                  uint32_t* ucs);
+
+bool IsCombining(uint32_t ucs);
+bool IsFullWidth(uint32_t ucs);
+bool IsControl(uint32_t ucs);
+
+size_t GlyphPrevious(const std::string& input, size_t start);
+size_t GlyphNext(const std::string& input, size_t start);
+
+// Return the index in the |input| string of the glyph at |glyph_offset|,
+// starting at |start|
+size_t GlyphIterate(const std::string& input,
+                    int glyph_offset,
+                    size_t start = 0);
+
+// Returns the number of glyphs in |input|.
+int GlyphCount(const std::string& input);
+
+// Properties from:
+// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/WordBreakProperty.txt
+enum class WordBreakProperty {
+  ALetter,
+  CR,
+  Double_Quote,
+  Extend,
+  ExtendNumLet,
+  Format,
+  Hebrew_Letter,
+  Katakana,
+  LF,
+  MidLetter,
+  MidNum,
+  MidNumLet,
+  Newline,
+  Numeric,
+  Regional_Indicator,
+  Single_Quote,
+  WSegSpace,
+  ZWJ,
+};
+WordBreakProperty CodepointToWordBreakProperty(uint32_t codepoint);
+std::vector<WordBreakProperty> Utf8ToWordBreakProperty(
+    const std::string& input);
+
+bool IsWordBreakingCharacter(const std::string& input, size_t glyph_index);
+}  // namespace ftxui
+
+#endif /* end of include guard: FTXUI_SCREEN_STRING_INTERNAL_HPP */
+
+// Copyright 2023 Arthur Sonzogni. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found in
+// the LICENSE file.
diff --git a/src/ftxui/screen/string_test.cpp b/src/ftxui/screen/string_test.cpp
index 57a220ddc73f057d7927206cc207e161e1c867f4..98c8faea892e5ee1b7c02888347c1017add60d2e 100644
--- a/src/ftxui/screen/string_test.cpp
+++ b/src/ftxui/screen/string_test.cpp
@@ -1,6 +1,7 @@
 #include "ftxui/screen/string.hpp"
 #include <gtest/gtest.h>
 #include <string>  // for allocator, string
+#include "ftxui/screen/string_internal.hpp"
 
 namespace ftxui {
 
@@ -61,41 +62,41 @@ TEST(StringTest, GlyphCount) {
   EXPECT_EQ(GlyphCount("a\1a"), 2);
 }
 
-TEST(StringTest, GlyphPosition) {
+TEST(StringTest, GlyphIterate) {
   // Basic:
-  EXPECT_EQ(GlyphPosition("", -1), 0);
-  EXPECT_EQ(GlyphPosition("", 0), 0);
-  EXPECT_EQ(GlyphPosition("", 1), 0);
-  EXPECT_EQ(GlyphPosition("a", 0), 0);
-  EXPECT_EQ(GlyphPosition("a", 1), 1);
-  EXPECT_EQ(GlyphPosition("ab", 0), 0);
-  EXPECT_EQ(GlyphPosition("ab", 1), 1);
-  EXPECT_EQ(GlyphPosition("ab", 2), 2);
-  EXPECT_EQ(GlyphPosition("abc", 0), 0);
-  EXPECT_EQ(GlyphPosition("abc", 1), 1);
-  EXPECT_EQ(GlyphPosition("abc", 2), 2);
-  EXPECT_EQ(GlyphPosition("abc", 3), 3);
+  EXPECT_EQ(GlyphIterate("", -1), 0);
+  EXPECT_EQ(GlyphIterate("", 0), 0);
+  EXPECT_EQ(GlyphIterate("", 1), 0);
+  EXPECT_EQ(GlyphIterate("a", 0), 0);
+  EXPECT_EQ(GlyphIterate("a", 1), 1);
+  EXPECT_EQ(GlyphIterate("ab", 0), 0);
+  EXPECT_EQ(GlyphIterate("ab", 1), 1);
+  EXPECT_EQ(GlyphIterate("ab", 2), 2);
+  EXPECT_EQ(GlyphIterate("abc", 0), 0);
+  EXPECT_EQ(GlyphIterate("abc", 1), 1);
+  EXPECT_EQ(GlyphIterate("abc", 2), 2);
+  EXPECT_EQ(GlyphIterate("abc", 3), 3);
   // Fullwidth glyphs:
-  EXPECT_EQ(GlyphPosition("测", 0), 0);
-  EXPECT_EQ(GlyphPosition("测", 1), 3);
-  EXPECT_EQ(GlyphPosition("测试", 0), 0);
-  EXPECT_EQ(GlyphPosition("测试", 1), 3);
-  EXPECT_EQ(GlyphPosition("测试", 2), 6);
-  EXPECT_EQ(GlyphPosition("测试", 1, 3), 6);
-  EXPECT_EQ(GlyphPosition("测试", 1, 0), 3);
+  EXPECT_EQ(GlyphIterate("测", 0), 0);
+  EXPECT_EQ(GlyphIterate("测", 1), 3);
+  EXPECT_EQ(GlyphIterate("测试", 0), 0);
+  EXPECT_EQ(GlyphIterate("测试", 1), 3);
+  EXPECT_EQ(GlyphIterate("测试", 2), 6);
+  EXPECT_EQ(GlyphIterate("测试", 1, 3), 6);
+  EXPECT_EQ(GlyphIterate("测试", 1, 0), 3);
   // Combining characters:
-  EXPECT_EQ(GlyphPosition("ā", 0), 0);
-  EXPECT_EQ(GlyphPosition("ā", 1), 3);
-  EXPECT_EQ(GlyphPosition("a⃒a̗ā", 0), 0);
-  EXPECT_EQ(GlyphPosition("a⃒a̗ā", 1), 4);
-  EXPECT_EQ(GlyphPosition("a⃒a̗ā", 2), 7);
-  EXPECT_EQ(GlyphPosition("a⃒a̗ā", 3), 10);
+  EXPECT_EQ(GlyphIterate("ā", 0), 0);
+  EXPECT_EQ(GlyphIterate("ā", 1), 3);
+  EXPECT_EQ(GlyphIterate("a⃒a̗ā", 0), 0);
+  EXPECT_EQ(GlyphIterate("a⃒a̗ā", 1), 4);
+  EXPECT_EQ(GlyphIterate("a⃒a̗ā", 2), 7);
+  EXPECT_EQ(GlyphIterate("a⃒a̗ā", 3), 10);
   // Control characters:
-  EXPECT_EQ(GlyphPosition("\1", 0), 0);
-  EXPECT_EQ(GlyphPosition("\1", 1), 1);
-  EXPECT_EQ(GlyphPosition("a\1a", 0), 0);
-  EXPECT_EQ(GlyphPosition("a\1a", 1), 2);
-  EXPECT_EQ(GlyphPosition("a\1a", 2), 3);
+  EXPECT_EQ(GlyphIterate("\1", 0), 0);
+  EXPECT_EQ(GlyphIterate("\1", 1), 1);
+  EXPECT_EQ(GlyphIterate("a\1a", 0), 0);
+  EXPECT_EQ(GlyphIterate("a\1a", 1), 2);
+  EXPECT_EQ(GlyphIterate("a\1a", 2), 3);
 }
 
 TEST(StringTest, CellToGlyphIndex) {
@@ -135,7 +136,7 @@ TEST(StringTest, Utf8ToWordBreakProperty) {
   EXPECT_EQ(Utf8ToWordBreakProperty(":"), T({P::MidLetter}));
   EXPECT_EQ(Utf8ToWordBreakProperty("."), T({P::MidNumLet}));
   EXPECT_EQ(Utf8ToWordBreakProperty("\r"), T({}));  // FIXME
-  EXPECT_EQ(Utf8ToWordBreakProperty("\n"), T({}));  // FIXME
+  EXPECT_EQ(Utf8ToWordBreakProperty("\n"), T({P::LF}));
 }
 
 TEST(StringTest, to_string) {