]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/menu/item/inputbox.c
Use an offset to draw the Clear button so that it can be better placed based on the...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / menu / item / inputbox.c
1 #ifdef INTERFACE
2 CLASS(InputBox) EXTENDS(Label)
3         METHOD(InputBox, configureInputBox, void(entity, string, float, float, string))
4         METHOD(InputBox, draw, void(entity))
5         METHOD(InputBox, setText, void(entity, string))
6         METHOD(InputBox, enterText, void(entity, string))
7         METHOD(InputBox, keyDown, float(entity, float, float, float))
8         METHOD(InputBox, mouseMove, float(entity, vector))
9         METHOD(InputBox, mouseRelease, float(entity, vector))
10         METHOD(InputBox, mousePress, float(entity, vector))
11         METHOD(InputBox, mouseDrag, float(entity, vector))
12         METHOD(InputBox, showNotify, void(entity))
13         METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector))
14
15         ATTRIB(InputBox, src, string, string_null)
16
17         ATTRIB(InputBox, cursorPos, float, 0) // characters
18         ATTRIB(InputBox, scrollPos, float, 0) // widths
19
20         ATTRIB(InputBox, focusable, float, 1)
21         ATTRIB(InputBox, disabled, float, 0)
22         ATTRIB(InputBox, lastChangeTime, float, 0)
23         ATTRIB(InputBox, dragScrollTimer, float, 0)
24         ATTRIB(InputBox, dragScrollPos, vector, '0 0 0')
25         ATTRIB(InputBox, pressed, float, 0)
26         ATTRIB(InputBox, editColorCodes, float, 1)
27         ATTRIB(InputBox, forbiddenCharacters, string, "")
28         ATTRIB(InputBox, color, vector, '1 1 1')
29         ATTRIB(InputBox, colorF, vector, '1 1 1')
30         ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars
31
32         ATTRIB(InputBox, enableClearButton, float, 1)
33         ATTRIB(InputBox, clearButton, entity, NULL)
34         ATTRIB(InputBox, cb_size, vector, '0 0 0')
35         ATTRIB(InputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON) // bound to range -1, 0
36         ATTRIB(InputBox, cb_pressed, float, 0)
37         ATTRIB(InputBox, cb_focused, float, 0)
38         ATTRIB(InputBox, cb_src, string, SKINGFX_CLEARBUTTON)
39 ENDCLASS(InputBox)
40 void InputBox_Clear_Click(entity btn, entity me);
41 #endif
42
43 #ifdef IMPLEMENTATION
44 void InputBox_configureInputBox(entity me, string theText, float theCursorPos, float theFontSize, string gfx)
45 {
46         SUPER(InputBox).configureLabel(me, theText, theFontSize, 0.0);
47         me.src = gfx;
48         me.cursorPos = theCursorPos;
49 }
50 void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
51 {
52         SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
53         if (me.enableClearButton)
54         {
55                 me.cb_size = eX * (absSize_y / absSize_x) + eY;
56                 me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_size_x; // bound to range -1, 0
57                 me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_size_x;
58         }
59 }
60
61 void InputBox_setText(entity me, string txt)
62 {
63         if(me.text)
64                 strunzone(me.text);
65         SUPER(InputBox).setText(me, strzone(txt));
66 }
67
68 void InputBox_Clear_Click(entity btn, entity me)
69 {
70         me.setText(me, "");
71 }
72
73 float over_ClearButton(entity me, vector pos)
74 {
75         if (pos_x >= 1 + me.cb_offset - me.cb_size_x)
76         if (pos_x < 1 + me.cb_offset)
77         if (pos_y >= 0)
78         if (pos_y < me.cb_size_y)
79                 return 1;
80         return 0;
81 }
82
83 float InputBox_mouseMove(entity me, vector pos)
84 {
85         if (me.enableClearButton)
86         {
87                 if (over_ClearButton(me, pos))
88                 {
89                         me.cb_focused = 1;
90                         return 1;
91                 }
92                 me.cb_focused = 0;
93         }
94         return 1;
95 }
96
97 float InputBox_mouseDrag(entity me, vector pos)
98 {
99         float p;
100         if(me.pressed)
101         {
102                 me.dragScrollPos = pos;
103                 p = me.scrollPos + pos_x - me.keepspaceLeft;
104                 me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize);
105                 me.lastChangeTime = time;
106         }
107         else if (me.enableClearButton)
108         {
109                 if (over_ClearButton(me, pos))
110                 {
111                         me.cb_pressed = 1;
112                         return 1;
113                 }
114         }
115         me.cb_pressed = 0;
116         return 1;
117 }
118
119 float InputBox_mousePress(entity me, vector pos)
120 {
121         if (me.enableClearButton)
122         if (over_ClearButton(me, pos))
123         {
124                 me.cb_pressed = 1;
125                 return 1;
126         }
127         me.dragScrollTimer = time;
128         me.pressed = 1;
129         return InputBox_mouseDrag(me, pos);
130 }
131
132 float InputBox_mouseRelease(entity me, vector pos)
133 {
134         if(me.cb_pressed)
135         if (over_ClearButton(me, pos))
136         {
137                 me.cb_pressed = 0;
138                 InputBox_Clear_Click(world, me);
139                 return 1;
140         }
141         float r = InputBox_mouseDrag(me, pos);
142         //reset cb_pressed after mouseDrag, mouseDrag could set cb_pressed in this case:
143         //mouse press out of the clear button, drag and then mouse release over the clear button
144         me.cb_pressed = 0;
145         me.pressed = 0;
146         return r;
147 }
148
149 void InputBox_enterText(entity me, string ch)
150 {
151         float i;
152         for(i = 0; i < strlen(ch); ++i)
153                 if(strstrofs(me.forbiddenCharacters, substring(ch, i, 1), 0) > -1)
154                         return;
155         if(me.maxLength > 0)
156         {
157                 if(strlen(ch) + strlen(me.text) > me.maxLength)
158                         return;
159         }
160         else if(me.maxLength < 0)
161         {
162                 if(u8_strsize(ch) + u8_strsize(me.text) > -me.maxLength)
163                         return;
164         }
165         me.setText(me, strcat(substring(me.text, 0, me.cursorPos), ch, substring(me.text, me.cursorPos, strlen(me.text) - me.cursorPos)));
166         me.cursorPos += strlen(ch);
167 }
168
169 float InputBox_keyDown(entity me, float key, float ascii, float shift)
170 {
171         me.lastChangeTime = time;
172         me.dragScrollTimer = time;
173         if(ascii >= 32 && ascii != 127)
174         {
175                 me.enterText(me, chr(ascii));
176                 return 1;
177         }
178         switch(key)
179         {
180                 case K_KP_LEFTARROW:
181                 case K_LEFTARROW:
182                         me.cursorPos -= 1;
183                         return 1;
184                 case K_KP_RIGHTARROW:
185                 case K_RIGHTARROW:
186                         me.cursorPos += 1;
187                         return 1;
188                 case K_KP_HOME:
189                 case K_HOME:
190                         me.cursorPos = 0;
191                         return 1;
192                 case K_KP_END:
193                 case K_END:
194                         me.cursorPos = strlen(me.text);
195                         return 1;
196                 case K_BACKSPACE:
197                         if(me.cursorPos > 0)
198                         {
199                                 me.cursorPos -= 1;
200                                 me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1)));
201                         }
202                         return 1;
203                 case K_KP_DEL:
204                 case K_DEL:
205                         if(shift & S_CTRL)
206                                 me.setText(me, "");
207                         else
208                                 me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1)));
209                         return 1;
210         }
211         return 0;
212 }
213
214 void InputBox_draw(entity me)
215 {
216 #define CURSOR "_"
217         float cursorPosInWidths, totalSizeInWidths;
218
219         if(me.pressed)
220                 me.mouseDrag(me, me.dragScrollPos); // simulate mouseDrag event
221
222         me.focusable = !me.disabled;
223         if(me.disabled)
224                 draw_alpha *= me.disabledAlpha;
225
226         if(me.src)
227         {
228                 if(me.focused && !me.disabled)
229                         draw_ButtonPicture('0 0 0', strcat(me.src, "_f"), '1 1 0', me.colorF, 1);
230                 else
231                         draw_ButtonPicture('0 0 0', strcat(me.src, "_n"), '1 1 0', me.color, 1);
232         }
233
234         me.cursorPos = bound(0, me.cursorPos, strlen(me.text));
235         cursorPosInWidths = draw_TextWidth(substring(me.text, 0, me.cursorPos), 0, me.realFontSize);
236         totalSizeInWidths = draw_TextWidth(strcat(me.text, CURSOR), 0, me.realFontSize);
237
238         if(me.dragScrollTimer < time)
239         {
240                 float save;
241                 save = me.scrollPos;
242                 me.scrollPos = bound(cursorPosInWidths - (0.875 - me.keepspaceLeft - me.keepspaceRight), me.scrollPos, cursorPosInWidths - 0.125);
243                 if(me.scrollPos != save)
244                         me.dragScrollTimer = time + 0.2;
245         }
246         me.scrollPos = min(me.scrollPos, totalSizeInWidths - (1 - me.keepspaceRight - me.keepspaceLeft));
247         me.scrollPos = max(0, me.scrollPos);
248
249         draw_SetClipRect(eX * me.keepspaceLeft, eX * (1 - me.keepspaceLeft - me.keepspaceRight) + eY);
250         if(me.editColorCodes)
251         {
252                 string ch, ch2;
253                 float i, n;
254                 vector theColor;
255                 float theAlpha;    //float theVariableAlpha;
256                 vector p;
257                 vector theTempColor;
258                 float component;
259                 
260                 p = me.realOrigin - eX * me.scrollPos;
261                 theColor = '1 1 1';
262                 theAlpha = 1;    //theVariableAlpha = 1; // changes when ^ax found
263                 
264                 n = strlen(me.text);
265                 for(i = 0; i < n; ++i)
266                 {
267                         ch = substring(me.text, i, 1);
268                         if(ch == "^")
269                         {
270                                 float w;
271                                 ch2 = substring(me.text, i+1, 1);
272                                 w = draw_TextWidth(strcat(ch, ch2), 0, me.realFontSize);
273                                 if(ch2 == "^")
274                                 {
275                                         draw_Fill(p, eX * w + eY * me.realFontSize_y, '1 1 1', 0.5);
276                                         draw_Text(p + eX * 0.25 * w, "^", me.realFontSize, theColor, theAlpha, 0);
277                                 }
278                                 else if(ch2 == "0" || stof(ch2)) // digit?
279                                 {
280                                         switch(stof(ch2))
281                                         {
282                                                 case 0: theColor = '0 0 0'; theAlpha = 1; break;
283                                                 case 1: theColor = '1 0 0'; theAlpha = 1; break;
284                                                 case 2: theColor = '0 1 0'; theAlpha = 1; break;
285                                                 case 3: theColor = '1 1 0'; theAlpha = 1; break;
286                                                 case 4: theColor = '0 0 1'; theAlpha = 1; break;
287                                                 case 5: theColor = '0 1 1'; theAlpha = 1; break;
288                                                 case 6: theColor = '1 0 1'; theAlpha = 1; break;
289                                                 case 7: theColor = '1 1 1'; theAlpha = 1; break;
290                                                 case 8: theColor = '1 1 1'; theAlpha = 0.5; break;
291                                                 case 9: theColor = '0.5 0.5 0.5'; theAlpha = 1; break;
292                                         }
293                                         draw_Fill(p, eX * w + eY * me.realFontSize_y, '1 1 1', 0.5);
294                                         draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
295                                 }
296                                 else if(ch2 == "x") // ^x found
297                                 {
298                                         theColor = '1 1 1';
299                                         theTempColor = '0 0 0';
300                                         
301                                         component = HEXDIGIT_TO_DEC(substring(me.text, i+2, 1));
302                                         if (component >= 0) // ^xr found
303                                         {
304                                                 theTempColor_x = component/15;
305                                                 
306                                                 component = HEXDIGIT_TO_DEC(substring(me.text, i+3, 1));
307                                                 if (component >= 0) // ^xrg found
308                                                 {
309                                                         theTempColor_y = component/15;
310                                                         
311                                                         component = HEXDIGIT_TO_DEC(substring(me.text, i+4, 1));
312                                                         if (component >= 0) // ^xrgb found
313                                                         {
314                                                                 theTempColor_z = component/15;
315                                                                 theColor = theTempColor;
316                                                                 w = draw_TextWidth(substring(me.text, i, 5), 0, me.realFontSize);
317                                                                 
318                                                                 draw_Fill(p, eX * w + eY * me.realFontSize_y, '1 1 1', 0.5);
319                                                                 draw_Text(p, substring(me.text, i, 5), me.realFontSize, theColor, 1, 0);    // theVariableAlpha instead of 1 using alpha tags ^ax
320                                                                 i += 3;
321                                                         }
322                                                         else
323                                                         {
324                                                                 // blue missing
325                                                                 w = draw_TextWidth(substring(me.text, i, 4), 0, me.realFontSize);
326                                                                 draw_Fill(p, eX * w + eY * me.realFontSize_y, eZ, 0.5);
327                                                                 draw_Text(p, substring(me.text, i, 4), me.realFontSize, '1 1 1', theAlpha, 0);
328                                                                 i += 2;
329                                                         }
330                                                 }
331                                                 else
332                                                 {
333                                                         // green missing
334                                                         w = draw_TextWidth(substring(me.text, i, 3), 0, me.realFontSize);
335                                                         draw_Fill(p, eX * w + eY * me.realFontSize_y, eY, 0.5);
336                                                         draw_Text(p, substring(me.text, i, 3), me.realFontSize, '1 1 1', theAlpha, 0);
337                                                         i += 1;
338                                                 }
339                                         }
340                                         else
341                                         {
342                                                 // red missing
343                                                 //w = draw_TextWidth(substring(me.text, i, 2), 0) * me.realFontSize_x;
344                                                 draw_Fill(p, eX * w + eY * me.realFontSize_y, eX, 0.5);
345                                                 draw_Text(p, substring(me.text, i, 2), me.realFontSize, '1 1 1', theAlpha, 0);
346                                         }
347                                 }
348                                 else
349                                 {
350                                         draw_Fill(p, eX * w + eY * me.realFontSize_y, '1 1 1', 0.5);
351                                         draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
352                                 }
353                                 p += w * eX;
354                                 ++i;
355                                 continue;
356                         }
357                         draw_Text(p, ch, me.realFontSize, theColor, theAlpha, 0); // TODO theVariableAlpha
358                         p += eX * draw_TextWidth(ch, 0, me.realFontSize);
359                 }
360         }
361         else
362                 draw_Text(me.realOrigin - eX * me.scrollPos, me.text, me.realFontSize, '1 1 1', 1, 0);
363                 // skipping SUPER(InputBox).draw(me);
364         if(!me.focused || (time - me.lastChangeTime) < floor(time - me.lastChangeTime) + 0.5)
365                 draw_Text(me.realOrigin + eX * (cursorPosInWidths - me.scrollPos), CURSOR, me.realFontSize, '1 1 1', 1, 0);
366
367         draw_ClearClip();
368
369         if (me.enableClearButton)
370         if (me.text != "")
371         {
372                 if(me.focused && me.cb_pressed)
373                         draw_Picture('1 1 0' + eX * me.cb_offset - me.cb_size, strcat(me.cb_src, "_c"), me.cb_size, '1 1 1', 1);
374                 else if(me.focused && me.cb_focused)
375                         draw_Picture('1 1 0' + eX * me.cb_offset - me.cb_size, strcat(me.cb_src, "_f"), me.cb_size, '1 1 1', 1);
376                 else
377                         draw_Picture('1 1 0' + eX * me.cb_offset - me.cb_size, strcat(me.cb_src, "_n"), me.cb_size, '1 1 1', 1);
378         }
379 }
380
381 void InputBox_showNotify(entity me)
382 {
383         me.focusable = !me.disabled;
384 }
385 #endif