Mercurial > projects > mde
comparison mde/content/AStringContent.d @ 105:08651e8a8c51
Quit button, big changes to content system.
Moved mde.gui.content to mde.content to reflect it's not only used by the gui.
Split Content module into Content and AStringContent.
New AContent and EventContent class.
Callbacks are now generic and implemented in AContent.
Renamed TextContent to StringContent and ValueContent to AStringContent.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 29 Nov 2008 12:36:39 +0000 |
parents | |
children | fe061009029d |
comparison
equal
deleted
inserted
replaced
104:ee209602770d | 105:08651e8a8c51 |
---|---|
1 /* LICENSE BLOCK | |
2 Part of mde: a Modular D game-oriented Engine | |
3 Copyright © 2007-2008 Diggory Hardy | |
4 | |
5 This program is free software: you can redistribute it and/or modify it under the terms | |
6 of the GNU General Public License as published by the Free Software Foundation, either | |
7 version 2 of the License, or (at your option) any later version. | |
8 | |
9 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; | |
10 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
11 See the GNU General Public License for more details. | |
12 | |
13 You should have received a copy of the GNU General Public License | |
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
15 | |
16 /** The content system − string-based editable content. | |
17 */ | |
18 module mde.content.AStringContent; | |
19 public import mde.content.Content; | |
20 | |
21 //FIXME: efficient conversions? Need to dup result when formatting a string anyway? | |
22 import Int = tango.text.convert.Integer; | |
23 import Float = tango.text.convert.Float; | |
24 import derelict.sdl.keysym; | |
25 | |
26 debug { | |
27 import tango.util.log.Log : Log, Logger; | |
28 private Logger logger; | |
29 static this () { | |
30 logger = Log.getLogger ("mde.content.AStringContent"); | |
31 } | |
32 } | |
33 | |
34 // Used by Options | |
35 template ContentN(T) { | |
36 static if (is(T == bool)) { | |
37 const char[] ContentN = "BoolContent"; | |
38 } else static if (is(T == int)) { | |
39 const char[] ContentN = "IntContent"; | |
40 } else static if (is(T == double)) { | |
41 const char[] ContentN = "DoubleContent"; | |
42 } else static if (is(T == char[])) { | |
43 const char[] ContentN = "StringContent"; | |
44 } else | |
45 static assert (false, "No Content of type "~T.stringof); | |
46 } | |
47 | |
48 /** Base class for content containing a simple value editable as text. All content types used by | |
49 * Options extend this class. | |
50 * | |
51 * All derived classes should have the following functions: | |
52 * --- | |
53 * void endEdit (); // Should convert sv and assign to self, then call endEvent | |
54 * // Used by Options: | |
55 * BoolContent changeCb (void delegate (char[] symbol,T value) cb); // The callback used by Options | |
56 * void assignNoCb (T val); // assign val, but without calling callbacks | |
57 * --- | |
58 * On any assignation (by this, assignNoCb, opAssign) the value should be converted to a string and | |
59 * assigned to sv, and pos should be clamped to [0,sv.length] (i.e. enforce pos <= sv.length). */ | |
60 abstract class AStringContent : AContent | |
61 { | |
62 protected this (char[] symbol) { | |
63 super (symbol); | |
64 } | |
65 | |
66 /// Get the text. | |
67 char[] toString (uint i) { | |
68 return i == 0 ? sv | |
69 : i == 1 ? name_ | |
70 : i == 2 ? desc_ | |
71 : null; | |
72 } | |
73 | |
74 /** Acts on a keystroke and returns the new value. | |
75 * | |
76 * Supports one-line editing: left/right, home/end, backspace/delete. */ | |
77 char[] keyStroke (ushort sym, char[] i) { | |
78 debug assert (i.length, "StringContent.keyStroke: no value (??)"); // impossible? | |
79 char k = *i; | |
80 if (k > 0x20) { | |
81 if (k == 0x7f) { // delete | |
82 size_t p = pos; | |
83 if (p < sv.length) ++p; | |
84 while (p < sv.length && (sv[p] & 0x80) && !(sv[p] & 0x40)) | |
85 ++p; | |
86 sv = sv[0..pos] ~ sv[p..$]; | |
87 } else { // insert character | |
88 char[] tail = sv[pos..$]; | |
89 sv.length = sv.length + i.length; | |
90 size_t npos = pos+i.length; | |
91 if (tail) sv[npos..$] = tail.dup; // cannot assign with overlapping ranges | |
92 sv[pos..npos] = i; | |
93 pos = npos; | |
94 } | |
95 } else { // use sym; many keys output 0 | |
96 if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8 | |
97 char[] tail = sv[pos..$]; | |
98 if (pos) --pos; | |
99 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) | |
100 --pos; | |
101 sv = sv[0..pos] ~ tail; | |
102 } else if (sym == SDLK_LEFT) { | |
103 if (pos) --pos; | |
104 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) | |
105 --pos; | |
106 } else if (sym == SDLK_RIGHT) { | |
107 if (pos < sv.length) ++pos; | |
108 while (pos < sv.length && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) | |
109 ++pos; | |
110 } else if (sym == SDLK_HOME || sym == SDLK_UP) { | |
111 pos = 0; | |
112 } else if (sym == SDLK_END || sym == SDLK_DOWN) { | |
113 pos = sv.length; | |
114 } else | |
115 debug logger.trace ("Unhandled symbol: {}", sym); | |
116 } | |
117 return sv; | |
118 } | |
119 | |
120 /// Get the character the edit cursor is in front of | |
121 size_t editIndex () { | |
122 size_t i = 0; | |
123 for (size_t p = 0; p < pos; ++p) | |
124 if (!(sv[p] & 0x80) || sv[p] & 0x40) | |
125 ++i; | |
126 return i; | |
127 } | |
128 | |
129 /// Call after editing a string | |
130 void endEdit (); | |
131 | |
132 protected: | |
133 char[] sv; // string of value; updated on assignment for displaying and editing | |
134 size_t pos; // editing position; used by keyStroke | |
135 } | |
136 | |
137 class BoolContent : AStringContent | |
138 { | |
139 /** Create a content with _symbol name symbol. */ | |
140 this (char[] symbol, bool val = false) { | |
141 assignNoCb (val); | |
142 super (symbol); | |
143 } | |
144 | |
145 void assignNoCb (bool val) { | |
146 v = val; | |
147 sv = v ? "true" : "false"; | |
148 if (pos > sv.length) pos = sv.length; | |
149 } | |
150 void opAssign (bool val) { | |
151 assignNoCb (val); | |
152 endEvent; | |
153 } | |
154 bool opCall () { | |
155 return v; | |
156 } | |
157 alias opCall opCast; | |
158 | |
159 override void endEdit () { | |
160 v = sv && (sv[0] == 't' || sv[0] == 'T' || sv[0] == '1'); | |
161 endEvent; | |
162 } | |
163 | |
164 protected: | |
165 bool v; | |
166 } | |
167 | |
168 /** Text content. */ | |
169 class StringContent : AStringContent | |
170 { | |
171 this (char[] symbol, char[] val = null) { | |
172 v = val; | |
173 super (symbol); | |
174 } | |
175 | |
176 void assignNoCb (char[] val) { | |
177 v = val; | |
178 if (pos > sv.length) pos = sv.length; | |
179 } | |
180 void opAssign (char[] val) { | |
181 assignNoCb (val); | |
182 endEvent; | |
183 } | |
184 char[] opCall () { | |
185 return v; | |
186 } | |
187 alias opCall opCast; | |
188 | |
189 override void endEdit () { | |
190 endEvent; | |
191 } | |
192 | |
193 protected: | |
194 alias sv v; // don't need separate v and sv in this case | |
195 } | |
196 | |
197 /** Integer content. */ | |
198 class IntContent : AStringContent | |
199 { | |
200 /** Create a content with _symbol name symbol. */ | |
201 this (char[] symbol, int val = 0) { | |
202 assignNoCb (val); | |
203 super (symbol); | |
204 } | |
205 | |
206 void assignNoCb (int val) { | |
207 v = val; | |
208 sv = Int.toString (v); | |
209 if (pos > sv.length) pos = sv.length; | |
210 } | |
211 void opAssign (int val) { | |
212 assignNoCb (val); | |
213 endEvent; | |
214 } | |
215 int opCall () { | |
216 return v; | |
217 } | |
218 alias opCall opCast; | |
219 | |
220 override void endEdit () { | |
221 v = Int.toInt (sv); | |
222 endEvent; | |
223 } | |
224 | |
225 protected: | |
226 int v; | |
227 } | |
228 | |
229 /** Double content. */ | |
230 class DoubleContent : AStringContent | |
231 { | |
232 /** Create a content with _symbol name symbol. */ | |
233 this (char[] symbol, double val = 0) { | |
234 assignNoCb (val); | |
235 super (symbol); | |
236 } | |
237 | |
238 void assignNoCb (double val) { | |
239 v = val; | |
240 sv = Float.toString (v); | |
241 if (pos > sv.length) pos = sv.length; | |
242 } | |
243 void opAssign (double val) { | |
244 assignNoCb (val); | |
245 endEvent; | |
246 } | |
247 double opCall () { | |
248 return v; | |
249 } | |
250 alias opCall opCast; | |
251 | |
252 override void endEdit () { | |
253 v = Float.toFloat (sv); | |
254 endEvent; | |
255 } | |
256 | |
257 protected: | |
258 double v; | |
259 } |