From 05c7bee6dde8e38c62c5aceb68ff07e96dfd50af Mon Sep 17 00:00:00 2001
From: MingSheng <102264706+mingsheng13@users.noreply.github.com>
Date: Mon, 28 Aug 2023 20:07:26 +0100
Subject: [PATCH] Feature: input can now use overwrite mode when toggled with
 insert key (#735)

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
---
 CHANGELOG.md                                  |  4 +++
 include/ftxui/component/component_options.hpp |  1 +
 include/ftxui/component/event.hpp             |  1 +
 src/ftxui/component/event.cpp                 |  1 +
 src/ftxui/component/input.cpp                 | 19 ++++++++++---
 src/ftxui/component/input_test.cpp            | 28 +++++++++++++++++++
 6 files changed, 50 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa4ae1bb..a094fd12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ Changelog
 current (development) 
 ---------------------
 
+### Component
+- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
+  option. Added by @mingsheng13.
+
 5.0.0
 -----
 
diff --git a/include/ftxui/component/component_options.hpp b/include/ftxui/component/component_options.hpp
index b401b6d1..c437bf05 100644
--- a/include/ftxui/component/component_options.hpp
+++ b/include/ftxui/component/component_options.hpp
@@ -177,6 +177,7 @@ struct InputOption {
   std::function<Element(InputState)> transform;
   Ref<bool> password = false;  /// < Obscure the input content using '*'.
   Ref<bool> multiline = true;  /// < Whether the input can be multiline.
+  Ref<bool> insert = true;     /// < Insert or overtype character mode.
 
   /// Called when the content changes.
   std::function<void()> on_change = [] {};
diff --git a/include/ftxui/component/event.hpp b/include/ftxui/component/event.hpp
index 504bee09..b4816225 100644
--- a/include/ftxui/component/event.hpp
+++ b/include/ftxui/component/event.hpp
@@ -55,6 +55,7 @@ struct Event {
   static const Event TabReverse;
   static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
 
+  static const Event Insert;
   static const Event Home;
   static const Event End;
 
diff --git a/src/ftxui/component/event.cpp b/src/ftxui/component/event.cpp
index b6530746..72a57f13 100644
--- a/src/ftxui/component/event.cpp
+++ b/src/ftxui/component/event.cpp
@@ -100,6 +100,7 @@ const Event Event::F10 = Event::Special("\x1B[21~");  // NOLINT
 const Event Event::F11 = Event::Special("\x1B[23~");  // NOLINT
 const Event Event::F12 = Event::Special("\x1B[24~");  // NOLINT
 
+const Event Event::Insert = Event::Special("\x1B[2~");            // NOLINT
 const Event Event::Home = Event::Special({27, 91, 72});           // NOLINT
 const Event Event::End = Event::Special({27, 91, 70});            // NOLINT
 const Event Event::PageUp = Event::Special({27, 91, 53, 126});    // NOLINT
diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp
index 12122146..caef66c1 100644
--- a/src/ftxui/component/input.cpp
+++ b/src/ftxui/component/input.cpp
@@ -100,8 +100,9 @@ class InputBase : public ComponentBase, public InputOption {
   // Component implementation:
   Element Render() override {
     const bool is_focused = Focused();
-    const auto focused =
-        (is_focused || hovered_) ? focusCursorBarBlinking : select;
+    const auto focused = (!is_focused && !hovered_) ? select
+                         : insert()                 ? focusCursorBarBlinking
+                                                    : focusCursorBlockBlinking;
 
     auto transform_func =
         transform ? transform : InputOption::Default().transform;
@@ -342,10 +343,13 @@ class InputBase : public ComponentBase, public InputOption {
   }
 
   bool HandleCharacter(const std::string& character) {
+    if (!insert() && cursor_position() < (int)content->size() &&
+        content()[cursor_position()] != '\n') {
+      HandleDelete();
+    }
     content->insert(cursor_position(), character);
     cursor_position() += character.size();
     on_change();
-
     return true;
   }
 
@@ -391,7 +395,9 @@ class InputBase : public ComponentBase, public InputOption {
     if (event == Event::ArrowRightCtrl) {
       return HandleRightCtrl();
     }
-
+    if (event == Event::Insert) {
+      return HandleInsert();
+    }
     return false;
   }
 
@@ -509,6 +515,11 @@ class InputBase : public ComponentBase, public InputOption {
     return true;
   }
 
+  bool HandleInsert() {
+    insert() = !insert();
+    return true;
+  }
+
   bool Focusable() const final { return true; }
 
   bool hovered_ = false;
diff --git a/src/ftxui/component/input_test.cpp b/src/ftxui/component/input_test.cpp
index 96a63b38..863569a2 100644
--- a/src/ftxui/component/input_test.cpp
+++ b/src/ftxui/component/input_test.cpp
@@ -754,4 +754,32 @@ TEST(InputTest, OnEnter) {
   EXPECT_TRUE(on_enter_called);
 }
 
+TEST(InputTest, InsertMode) {
+  std::string content = "abc\nefg";
+  bool insert = true;
+  int cursor_position = 1;
+  Component input = Input({
+      .content = &content,
+      .insert = &insert,
+      .cursor_position = &cursor_position,
+  });
+
+  EXPECT_TRUE(insert);
+  EXPECT_TRUE(input->OnEvent(Event::Insert));
+  EXPECT_FALSE(insert);
+
+  EXPECT_EQ(content, "abc\nefg");
+  EXPECT_TRUE(input->OnEvent(Event::Character('x')));
+  EXPECT_EQ(content, "axc\nefg");
+  EXPECT_TRUE(input->OnEvent(Event::Character('y')));
+  EXPECT_EQ(content, "axy\nefg");
+  EXPECT_TRUE(input->OnEvent(Event::Character('z')));
+  EXPECT_EQ(content, "axyz\nefg");
+
+  EXPECT_TRUE(input->OnEvent(Event::ArrowDown));
+  EXPECT_EQ(content, "axyz\nefg");
+  EXPECT_TRUE(input->OnEvent(Event::Character('X')));
+  EXPECT_EQ(content, "axyz\nefgX");
+}
+
 }  // namespace ftxui
-- 
GitLab