1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module w4d.util.textline; 8 import w4d.event; 9 import std.algorithm, 10 std.conv; 11 12 /// A handler of chainging text. 13 alias TextChangeHandler = EventHandler!( void, dstring ); 14 /// A handler of cursor moving. 15 alias CursorMoveHandler = EventHandler!( void, long ); 16 17 /// An object of editable text. 18 class TextLine 19 { 20 /// 21 TextChangeHandler onTextChange; 22 /// 23 CursorMoveHandler onCursorMove; 24 25 protected dstring _text; 26 /// Current text. 27 const @property text () { return _text; } 28 29 protected bool _locked; 30 /// Checks if editing is locked. 31 const @property isLocked () { return _locked; } 32 33 protected long _cursorIndex; 34 /// Index of cursor. 35 const @property cursorIndex () { return _cursorIndex; } 36 37 protected long _selectionIndex; 38 /// Index that selection is beginning. 39 const @property selectionIndex () { return _selectionIndex; } 40 41 /// Checks if text is selected. 42 const @property isSelecting () 43 { 44 return _selectionIndex >= 0 && _selectionIndex != _cursorIndex; 45 } 46 47 /// 48 this () 49 { 50 _text = ""d; 51 _locked = false; 52 _cursorIndex = 0; 53 _selectionIndex = 0; 54 } 55 56 /// Moves cursor to absolute index. 57 void moveCursorTo ( long i, bool selecting = false ) 58 { 59 if ( selecting && !isSelecting ) { 60 _selectionIndex = _cursorIndex; 61 } 62 const temp = _cursorIndex; 63 _cursorIndex = i.clamp( 0, _text.length ); 64 65 if ( !selecting ) { 66 _selectionIndex = -1; 67 } 68 if ( temp != _cursorIndex ) { 69 onCursorMove.call( _cursorIndex ); 70 } 71 } 72 /// Moves cursor to relative index. 73 void moveCursor ( long i, bool selecting = false ) 74 { 75 moveCursorTo( _cursorIndex+i, selecting ); 76 } 77 /// Moves cursor to left. 78 void left ( bool selecting ) 79 { 80 moveCursor( -1, selecting ); 81 } 82 /// Moves cursor to right. 83 void right ( bool selecting ) 84 { 85 moveCursor( 1, selecting ); 86 } 87 /// Moves cursor to home. 88 void home ( bool selecting ) 89 { 90 moveCursorTo( 0, selecting ); 91 } 92 /// Moves cursor to end. 93 void end ( bool selecting ) 94 { 95 moveCursorTo( _text.length, selecting ); 96 } 97 98 protected @property leftText () 99 { 100 auto index = _cursorIndex; 101 if ( isSelecting ) { 102 index = min( _cursorIndex, _selectionIndex ); 103 } 104 return _text[0 .. index.to!size_t]; 105 } 106 protected @property rightText () 107 { 108 auto index = _cursorIndex; 109 if ( isSelecting ) { 110 index = max( _cursorIndex, _selectionIndex ); 111 } 112 return _text[index.to!size_t .. $]; 113 } 114 115 /// Inserts text at cursor. 116 void insert ( dstring v ) 117 { 118 if ( isLocked ) return; 119 120 setText( leftText ~v~ rightText ); 121 moveCursor( v.length ); 122 } 123 /// Removes a left char of cursor. 124 void backspace () 125 { 126 if ( isLocked ) return; 127 128 if ( isSelecting ) { 129 removeSelecting(); 130 return; 131 } 132 auto left = leftText; 133 auto right = rightText; 134 135 if ( left.length ) { 136 left = left[0..$-1]; 137 moveCursor( -1 ); 138 setText( left~right ); 139 } 140 } 141 /// Removes a right char of cursor. 142 void del () 143 { 144 if ( isLocked ) return; 145 146 if ( isSelecting ) { 147 removeSelecting(); 148 return; 149 } 150 auto left = leftText; 151 auto right = rightText; 152 153 if ( right.length ) { 154 right = right[1..$]; 155 setText( left~right ); 156 } 157 } 158 159 /// Removes the selected text. 160 void removeSelecting () 161 { 162 if ( !isSelecting || isLocked ) return; 163 164 long cursorMove = 0; 165 if ( _cursorIndex > _selectionIndex ) { 166 cursorMove = -(_cursorIndex-_selectionIndex); 167 } 168 setText( leftText ~ rightText ); 169 170 moveCursor( cursorMove ); 171 } 172 /// Sets all text selected. 173 void selectAll () 174 { 175 moveCursorTo( 0 ); 176 moveCursorTo( _text.length, true ); 177 } 178 /// 179 void deselect () 180 { 181 moveCursor( 0 ); 182 // This method doesn't call onCursorMove. 183 // So requstRedraw won't be called. 184 } 185 186 /// Changes text. 187 void setText ( dstring v ) 188 { 189 if ( _text != v ) { 190 deselect(); 191 _text = v; 192 moveCursor(0); 193 onTextChange.call( v ); 194 } 195 } 196 197 /// Locks editing. 198 void lock () 199 { 200 _locked = true; 201 } 202 /// Unlocks editing. 203 void unlock () 204 { 205 _locked = false; 206 } 207 }