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 }