1 /** 2 Grimoire 3 Copyright (c) 2017 Enalye 4 5 This software is provided 'as-is', without any express or implied warranty. 6 In no event will the authors be held liable for any damages arising 7 from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute 11 it freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; 14 you must not claim that you wrote the original software. 15 If you use this software in a product, an acknowledgment 16 in the product documentation would be appreciated but 17 is not required. 18 19 2. Altered source versions must be plainly marked as such, 20 and must not be misrepresented as being the original software. 21 22 3. This notice may not be removed or altered from any source distribution. 23 */ 24 25 module ui.inputfield; 26 27 import std.utf; 28 import std.conv: to; 29 30 import core.all; 31 import render.all; 32 import common.all; 33 34 import ui.widget; 35 import ui.label; 36 37 class InputField: Widget { 38 private { 39 Label _label; 40 View _view; 41 Color _borderColor, _caretColor; 42 dstring _text; 43 Timer _time; 44 uint _caretIndex = 0U; 45 Vec2f _caretPosition = Vec2f.zero; 46 uint _limit = 80u; 47 } 48 49 @property { 50 string text() const { return to!string(_text); } 51 string text(string newText) { 52 _text = to!dstring(newText); 53 _caretIndex = to!uint(_text.length); 54 _label.text = newText; 55 return newText; 56 } 57 58 uint limit() const { return _limit; } 59 uint limit(uint newLimit) { 60 _limit = newLimit; 61 if(_text.length > _limit) { 62 _text.length = _limit; 63 _label.text = to!string(_text); 64 } 65 if(_caretIndex > _limit) 66 _caretIndex = _limit; 67 return _limit; 68 } 69 } 70 71 this(Vec2f newSize, string defaultText = "", bool startWithFocus = false) { 72 _size = newSize; 73 _view = new View(newSize); 74 _view.position = Vec2f.zero; 75 _view.setColorMod(Color.white, Blend.AlphaBlending); 76 _label = new Label; 77 _hasFocus = startWithFocus; 78 _text = to!dstring(defaultText); 79 _caretIndex = to!uint(_text.length); 80 _label.text = to!string(_text); 81 82 _borderColor = Color.white; 83 _caretColor = Color.white; 84 _time.start(1f, TimeMode.Bounce); 85 } 86 87 override void onEvent(Event event) { 88 pushView(_view, false); 89 if(_hasFocus) { 90 switch(event.type) with(EventType) { 91 case MouseDown: 92 hasFocus = true; 93 break; 94 case KeyInput: 95 if(_caretIndex >= _limit) 96 break; 97 if(_caretIndex == _text.length) 98 _text ~= to!dstring(event.str); 99 else if(_caretIndex == 0U) 100 _text = to!dstring(event.str) ~ _text; 101 else 102 _text = _text[0U.._caretIndex] ~ to!dstring(event.str) ~ _text[_caretIndex..$]; 103 _caretIndex ++; 104 _label.text = to!string(_text); 105 break; 106 case KeyDelete: 107 if(_text.length) { 108 if(event.ivalue > 0) { 109 if(_caretIndex == 0U) 110 _text = _text[1U..$]; 111 else if(_caretIndex != _text.length) { 112 _text = _text[0U.._caretIndex] ~ _text[_caretIndex + 1..$]; 113 } 114 } 115 else { 116 if(_caretIndex == _text.length) { 117 _text.length --; 118 _caretIndex --; 119 } 120 else if(_caretIndex != 0U) { 121 _text = _text[0U.._caretIndex - 1] ~ _text[_caretIndex..$]; 122 _caretIndex --; 123 } 124 } 125 _label.text = to!string(_text); 126 } 127 break; 128 case KeyDir: 129 if(event.position.x > 0f && _caretIndex < _text.length) 130 _caretIndex ++; 131 if(event.position.x < 0f && _caretIndex > 0U) 132 _caretIndex --; 133 break; 134 case KeyEnter: 135 triggerCallback(); 136 break; 137 default: 138 break; 139 } 140 } 141 popView(); 142 } 143 144 override void update(float deltaTime) { 145 _caretPosition = Vec2f(_label.position.x - _label.size.x / 2f + (_label.size.x / _text.length) * _caretIndex, _label.position.y - _label.size.y / 2f); 146 _label.position = Vec2f(10f + (_label.size.x - _view.size.x) / 2f, 0f); 147 148 if(_caretPosition.x > _view.position.x + _view.size.x / 2f - 10f) 149 _view.position.x = _caretPosition.x - _view.size.x / 2f + 10f; 150 else if(_caretPosition.x < _view.position.x - _view.size.x / 2f + 10f) 151 _view.position.x = _caretPosition.x + _view.size.x / 2f - 10f; 152 153 if(_caretIndex == 0U) 154 _view.position.x = 0f; 155 156 if(_hasFocus) 157 _borderColor = lerp(_borderColor, Color.white, deltaTime * .25f); 158 else 159 _borderColor = lerp(_borderColor, Color.white * .21f, deltaTime * .1f); 160 161 _time.update(deltaTime); 162 _caretColor = lerp(Color.white, Color.white * .21f, _time.time); 163 } 164 165 override void draw() { 166 pushView(_view, true); 167 _label.draw(); 168 if(_text.length && _hasFocus) 169 drawFilledRect(_caretPosition, Vec2f(2f, _label.size.y), _caretColor); 170 popView(); 171 _view.draw(_position); 172 drawRect(_position - _size / 2f, _size, _borderColor); 173 } 174 175 void clear() { 176 _text.length = 0L; 177 _caretIndex = 0u; 178 _label.text = ""; 179 } 180 }