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