@@ -1605,6 +1605,68 @@ class Widget : public SignalEmitter {
16051605 Widget& operator =(const Widget&) = delete ;
16061606};
16071607
1608+ class Cursor {
1609+ private:
1610+ // 1x1 texture stretched to font's single character dimensions
1611+ SDL_Texture* texture_;
1612+ SDL_Renderer* renderer_;
1613+ size_t position_ { 0 };
1614+ SDL_Rect rect_ {};
1615+ public:
1616+ Cursor (SDL_Renderer* renderer) : renderer_(renderer)
1617+ {
1618+ texture_ = sdl_tsd.CreateTexture (renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 1 , 1 );
1619+ if (texture_ == nullptr ) {
1620+ throw (std::runtime_error (sdl_console::SDL_GetError ()));
1621+ }
1622+
1623+ // FFFFFF = rgb white, 7F = 50% transparant
1624+ Uint32 pixel = 0xFFFFFF7F ;
1625+ if (sdl_console::SDL_UpdateTexture (texture_, NULL , &pixel, sizeof (Uint32)) != 0 ) {
1626+ throw (std::runtime_error (sdl_console::SDL_GetError ()));
1627+ }
1628+ // For transparancy
1629+ sdl_console::SDL_SetTextureBlendMode (texture_, SDL_BLENDMODE_BLEND);
1630+ }
1631+
1632+ size_t position () const
1633+ {
1634+ return position_;
1635+ }
1636+
1637+ void reset_position ()
1638+ {
1639+ position_ = 0 ;
1640+ }
1641+
1642+ void set_rect (const SDL_Rect& r)
1643+ {
1644+ rect_ = r;
1645+ }
1646+
1647+ void render () const
1648+ {
1649+ render_texture (renderer_, texture_, rect_);
1650+ }
1651+
1652+ Cursor& operator =(size_t position) {
1653+ position_ = position;
1654+ return *this ;
1655+ }
1656+
1657+ Cursor& operator ++() { ++position_; return *this ; }
1658+ Cursor& operator --() { if (position_ > 0 ) { --position_; } return *this ; }
1659+
1660+ Cursor& operator +=(std::size_t n) { position_ += n; return *this ; }
1661+ Cursor& operator -=(std::size_t n) { position_ = (n > position_) ? 0 : position_ - n; return *this ; }
1662+
1663+ Cursor (const Cursor&) = delete ;
1664+ Cursor& operator =(const Cursor&) = delete ;
1665+ Cursor (Cursor&&) = delete ;
1666+ Cursor& operator =(Cursor&&) = delete ;
1667+
1668+ };
1669+
16081670class Prompt : public Widget {
16091671public:
16101672 // Holds wrapped lines from input
@@ -1614,9 +1676,7 @@ class Prompt : public Widget {
16141676 // The input portion of the prompt.
16151677 std::u32string* input;
16161678 std::u32string saved_input;
1617- size_t cursor { 0 }; // position of cursor within an entry
1618- // 1x1 texture stretched to font's single character dimensions
1619- SDL_Texture* cursor_texture;
1679+ Cursor cursor;
16201680 /*
16211681 * For input history.
16221682 * use deque to hold a stable reference.
@@ -1625,14 +1685,12 @@ class Prompt : public Widget {
16251685 int history_index;
16261686
16271687 Prompt (Widget* parent)
1628- : Widget(parent)
1688+ : Widget(parent), cursor(renderer())
16291689 {
16301690 input = &history.emplace_back (U" " );
16311691
16321692 set_prompt_text (props ().get (property::PROMPT_TEXT).value_or (U" > " ));
16331693
1634- create_cursor_texture ();
1635-
16361694 connect_global<SDL_KeyboardEvent>(SDL_KEYDOWN, [this ](SDL_KeyboardEvent& e) {
16371695 on_SDL_KEYDOWN (e);
16381696 });
@@ -1647,22 +1705,6 @@ class Prompt : public Widget {
16471705 // sdl_tsd.DestroyTexture(cursor_texture);
16481706 }
16491707
1650- // NOTE: Only called by constructor.
1651- void create_cursor_texture ()
1652- {
1653- cursor_texture = sdl_tsd.CreateTexture (renderer (), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 1 , 1 );
1654- if (cursor_texture == nullptr )
1655- throw (std::runtime_error (sdl_console::SDL_GetError ()));
1656-
1657- // FFFFFF = rgb white, 7F = 50% transparant
1658- Uint32 pixel = 0xFFFFFF7F ;
1659- if (sdl_console::SDL_UpdateTexture (cursor_texture, NULL , &pixel, sizeof (Uint32)) != 0 ) {
1660- throw (std::runtime_error (sdl_console::SDL_GetError ()));
1661- }
1662- // For transparancy
1663- sdl_console::SDL_SetTextureBlendMode (cursor_texture, SDL_BLENDMODE_BLEND);
1664- }
1665-
16661708 /* OutputPane does this */
16671709 void render () override
16681710 {
@@ -1671,8 +1713,9 @@ class Prompt : public Widget {
16711713 void put_input_from_clipboard ()
16721714 {
16731715 auto * str = sdl_console::SDL_GetClipboardText ();
1674- if (*str != ' \0 ' )
1716+ if (*str != ' \0 ' ) {
16751717 put_input_at_cursor (text::from_utf8 (str));
1718+ }
16761719 // Always free, even when empty.
16771720 sdl_console::SDL_free (str);
16781721 }
@@ -1715,13 +1758,13 @@ class Prompt : public Widget {
17151758
17161759 case SDLK_b:
17171760 if (sdl_console::SDL_GetModState () & KMOD_CTRL) {
1718- cursor = text::find_prev_word (*input, cursor);
1761+ cursor = text::find_prev_word (*input, cursor. position () );
17191762 }
17201763 break ;
17211764
17221765 case SDLK_f:
17231766 if (sdl_console::SDL_GetModState () & KMOD_CTRL) {
1724- cursor = text::find_next_word (*input, cursor);
1767+ cursor = text::find_next_word (*input, cursor. position () );
17251768 }
17261769 break ;
17271770 case SDLK_c:
@@ -1742,19 +1785,20 @@ class Prompt : public Widget {
17421785 std::swap (history, saved_history);
17431786 input = &history.emplace_back (U" " );
17441787 history_index = history.size () - 1 ;
1745- reset_cursor ();
1788+ cursor. reset_position ();
17461789 }
17471790
17481791 void new_command_input ()
17491792 {
17501793 emit (InternalEventType::new_command_input, input);
17511794
17521795 // If empty, log an empty line? But don't add it to history.
1753- if (input->empty ()) return ;
1796+ if (input->empty ()) { return ; }
17541797
17551798 input = &history.emplace_back (U" " );
17561799 history_index = history.size () - 1 ;
1757- reset_cursor ();
1800+ // reset_cursor();
1801+ cursor.reset_position ();
17581802 wrap_text ();
17591803 }
17601804
@@ -1763,7 +1807,7 @@ class Prompt : public Widget {
17631807 saved_input = *input;
17641808 emit (InternalEventType::new_input, input);
17651809 input->clear ();
1766- reset_cursor ();
1810+ cursor. reset_position ();
17671811 wrap_text ();
17681812 }
17691813
@@ -1774,11 +1818,6 @@ class Prompt : public Widget {
17741818 wrap_text ();
17751819 }
17761820
1777- void reset_cursor ()
1778- {
1779- cursor = 0 ;
1780- }
1781-
17821821 void set_prompt_text (const std::u32string& value)
17831822 {
17841823 prompt_text = value;
@@ -1793,7 +1832,7 @@ class Prompt : public Widget {
17931832 */
17941833 void set_input_from_history (const ScrollAction sa)
17951834 {
1796- if (history.empty ()) return ;
1835+ if (history.empty ()) { return ; }
17971836
17981837 if (sa == ScrollAction::up && history_index > 0 ) {
17991838 history_index--;
@@ -1810,6 +1849,7 @@ class Prompt : public Widget {
18101849
18111850 void put_input_at_cursor (const std::u32string& str)
18121851 {
1852+ #if 0
18131853 /* if cursor is at end of line, it's a simple concatenation */
18141854 if (cursor == input->length()) {
18151855 *input += str;
@@ -1818,6 +1858,16 @@ class Prompt : public Widget {
18181858 input->insert(cursor, str);
18191859 }
18201860 cursor += str.length();
1861+ #endif
1862+ /* if cursor is at end of line, it's a simple concatenation */
1863+ if (cursor.position () == input->length ()) {
1864+ *input += str;
1865+ } else {
1866+ /* else insert text into line at cursor's index */
1867+ input->insert (cursor.position (), str);
1868+ }
1869+ cursor += str.length ();
1870+
18211871 wrap_text ();
18221872 }
18231873
@@ -1830,30 +1880,31 @@ class Prompt : public Widget {
18301880
18311881 void erase_input ()
18321882 {
1833- if (cursor == 0 || input->empty ())
1883+ if (cursor. position () == 0 || input->empty ()) {
18341884 return ;
1885+ }
18351886
1836- if (input->length () == cursor) {
1887+ if (input->length () == cursor. position () ) {
18371888 input->pop_back ();
18381889 } else {
18391890 /* else shift the text from cursor left by one character */
1840- input->erase (cursor-1 , 1 );
1891+ input->erase (cursor. position () -1 , 1 );
18411892 }
1842- cursor -= 1 ;
1893+ --cursor ;
18431894 wrap_text ();
18441895 }
18451896
18461897 void move_cursor_left ()
18471898 {
1848- if (cursor > 0 ) {
1849- cursor-- ;
1899+ if (cursor. position () > 0 ) {
1900+ --cursor ;
18501901 }
18511902 }
18521903
18531904 void move_cursor_right ()
18541905 {
1855- if (cursor < input->length ()) {
1856- cursor++ ;
1906+ if (cursor. position () < input->length ()) {
1907+ ++cursor ;
18571908 }
18581909 }
18591910
@@ -1867,51 +1918,61 @@ class Prompt : public Widget {
18671918 {
18681919 entry.text = prompt_text + *input;
18691920 entry.wrap_text (font->char_width , frame.w );
1921+ update_cursor_geometry_for_render ();
18701922 }
18711923
1872- // TODO: The cursor x,y position should be updated
1873- // elsewhere, such as when the cursor position changes.
1874- // instead of within its rendering function.
1875- void render_cursor (int scroll_offset)
1924+ void update_cursor_geometry_for_render ()
18761925 {
1877- if (entry.fragments ().empty ())
1926+ if (entry.fragments ().empty ()) {
18781927 return ;
1928+ }
18791929
18801930 // cursor's starting position
1881- auto cursor_pos = cursor + prompt_text.length ();
1931+ auto cursor_pos = cursor. position () + prompt_text.length ();
18821932 TextEntry::Fragment *line;
18831933
18841934 // cursor is at the end
18851935 if (cursor_pos == entry.text .length ()) {
1936+ #if 0
18861937 if (scroll_offset > 0) {
18871938 // cursor is not visible.
18881939 return;
18891940 }
1941+ #endif
18901942 line = &entry.fragments ().back ();
1891- // else find the line containing the cursor
1943+ // else find the line containing the cursor
18921944 } else {
18931945 auto line_opt = entry.fragment_from_offset (cursor_pos);
1894- if (!line_opt.has_value ())
1946+ if (!line_opt.has_value ()) {
18951947 return ; // should not happen
1948+ }
18961949
18971950 line = &line_opt.value ().get ();
1951+
1952+ #if 0
18981953 // the very bottom of the prompt is the last entry
18991954 // entry_offset = entry.size-1 at bottom
19001955 int r = (entry.size - 1) - line->entry_offset;
1956+
19011957 // scroll_offset starts at 0.
19021958 if (scroll_offset > r) {
19031959 // cursor is not visible.
19041960 return;
19051961 }
1962+ #endif
19061963 }
19071964
19081965 auto lh = font->line_height_with_spacing ();
19091966 auto cw = font->char_width ;
19101967 auto cx = (cursor_pos - line->start_offset ) * cw;
19111968 auto cy = line->coord .y ;
19121969
1913- SDL_Rect rect{ (int )cx, cy, cw, lh };
1914- render_texture (renderer (), cursor_texture, rect);
1970+ cursor.set_rect ({ (int )cx, cy, cw, lh });
1971+ }
1972+
1973+ void render_cursor (int scroll_offset)
1974+ {
1975+ cursor.render ();
19151976 }
19161977
19171978 Prompt (const Prompt&) = delete ;
@@ -2785,7 +2846,6 @@ class OutputPane : public Widget {
27852846 }
27862847 }
27872848
2788- // FIXME: Position and rows to render calculations should be done elsewhere.
27892849 std::vector<VisibleRow> get_visible_entry_rows (TextEntry& entry, int & ypos, int & row_counter, int max_row)
27902850 {
27912851 std::vector<VisibleRow> vrows;
@@ -2819,7 +2879,7 @@ class OutputPane : public Widget {
28192879 }
28202880
28212881 for (const auto & row : visible_rows_cache.rows ) {
2822- font->set_color (row.color );
2882+ auto sc = font->set_color (row.color );
28232883 font->render (row.text , row.coord .x , row.coord .y );
28242884 }
28252885 }
0 commit comments