]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/menu/item/inputbox.qc
522801f818d92a37747560dc1da201fdc79b2574
[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                 ATTRIB(InputBox, applyButton, entity, NULL)
35
36                 ATTRIB(InputBox, enableClearButton, float, 1)
37                 ATTRIB(InputBox, clearButton, entity, NULL)
38                 ATTRIB(InputBox, cb_width, float, 0)
39                 ATTRIB(InputBox, cb_pressed, float, 0)
40                 ATTRIB(InputBox, cb_focused, float, 0)
41                 ATTRIB(InputBox, cb_color, vector, '1 1 1')
42                 ATTRIB(InputBox, cb_colorF, vector, '1 1 1')
43                 ATTRIB(InputBox, cb_colorC, vector, '1 1 1')
44         ENDCLASS(InputBox)
45 #endif
46
47 #ifdef IMPLEMENTATION
48         void InputBox_configureInputBox(entity me, string theText, float theCursorPos, float theFontSize, string gfx)
49         {
50                 SUPER(InputBox).configureLabel(me, theText, theFontSize, 0.0);
51                 me.src = gfx;
52                 me.cursorPos = theCursorPos;
53         }
54         void InputBox_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
55         {
56                 SUPER(InputBox).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
57                 if (me.enableClearButton)
58                 {
59                         me.cb_width = absSize.y / absSize.x;
60                         me.cb_offset = bound(-1, me.cb_offset, 0) * me.cb_width;  // bound to range -1, 0
61                         me.keepspaceRight = me.keepspaceRight - me.cb_offset + me.cb_width;
62                 }
63         }
64
65         void InputBox_setText(entity me, string txt)
66         {
67                 if (me.text) 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) return 1;
77                 return 0;
78         }
79
80         float InputBox_mouseMove(entity me, vector pos)
81         {
82                 if (me.enableClearButton)
83                 {
84                         if (over_ClearButton(me, pos))
85                         {
86                                 me.cb_focused = 1;
87                                 return 1;
88                         }
89                         me.cb_focused = 0;
90                 }
91                 return 1;
92         }
93
94         float InputBox_mouseDrag(entity me, vector pos)
95         {
96                 float p;
97                 if (me.pressed)
98                 {
99                         me.dragScrollPos = pos;
100                         p = me.scrollPos + pos.x - me.keepspaceLeft;
101                         me.cursorPos = draw_TextLengthUpToWidth(me.text, p, 0, me.realFontSize);
102                         me.lastChangeTime = time;
103                 }
104                 else if (me.enableClearButton)
105                 {
106                         if (over_ClearButton(me, pos))
107                         {
108                                 me.cb_pressed = 1;
109                                 return 1;
110                         }
111                 }
112                 me.cb_pressed = 0;
113                 return 1;
114         }
115
116         float InputBox_mousePress(entity me, vector pos)
117         {
118                 if (me.enableClearButton)
119                         if (over_ClearButton(me, pos))
120                         {
121                                 me.cb_pressed = 1;
122                                 return 1;
123                         }
124                 me.dragScrollTimer = time;
125                 me.pressed = 1;
126                 return InputBox_mouseDrag(me, pos);
127         }
128
129         float InputBox_mouseRelease(entity me, vector pos)
130         {
131                 if (me.cb_pressed)
132                         if (over_ClearButton(me, pos))
133                         {
134                                 m_play_click_sound(MENU_SOUND_CLEAR);
135                                 me.setText(me, "");
136                                 if(me.applyButton)
137                                         me.applyButton.disabled = false;
138                                 me.cb_pressed = 0;
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) return;
154                 if (me.maxLength > 0)
155                 {
156                         if (strlen(ch) + strlen(me.text) > me.maxLength) return;
157                 }
158                 else if (me.maxLength < 0)
159                 {
160                         if (u8_strsize(ch) + u8_strsize(me.text) > -me.maxLength) return;
161                 }
162                 me.setText(me, strcat(substring(me.text, 0, me.cursorPos), ch, substring(me.text, me.cursorPos, strlen(me.text) - me.cursorPos)));
163                 me.cursorPos += strlen(ch);
164         }
165
166         float InputBox_keyDown(entity me, float key, float ascii, float shift)
167         {
168                 me.lastChangeTime = time;
169                 me.dragScrollTimer = time;
170                 if (ascii >= 32 && ascii != 127)
171                 {
172                         me.enterText(me, chr(ascii));
173                         if(me.applyButton)
174                                 me.applyButton.disabled = false;
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                                         if(me.applyButton)
201                                                 me.applyButton.disabled = false;
202                                 }
203                                 return 1;
204                         case K_KP_DEL:
205                         case K_DEL:
206                                 if (shift & S_CTRL)
207                                 {
208                                         m_play_click_sound(MENU_SOUND_CLEAR);
209                                         me.setText(me, "");
210                                 }
211                                 else
212                                         me.setText(me, strcat(substring(me.text, 0, me.cursorPos), substring(me.text, me.cursorPos + 1, strlen(me.text) - me.cursorPos - 1)));
213                                 if(me.applyButton)
214                                         me.applyButton.disabled = false;
215                                 return 1;
216                 }
217                 return 0;
218         }
219
220         void InputBox_draw(entity me)
221         {
222                 string CURSOR = "_";
223                 float cursorPosInWidths, totalSizeInWidths;
224
225                 if (me.pressed) me.mouseDrag(me, me.dragScrollPos);  // simulate mouseDrag event
226
227                 if (me.recalcPos) me.recalcPositionWithText(me, me.text);
228
229                 me.focusable = !me.disabled;
230                 if (me.disabled) draw_alpha *= me.disabledAlpha;
231
232                 if (me.src)
233                 {
234                         if (me.focused && !me.disabled) draw_ButtonPicture('0 0 0', strcat(me.src, "_f"), '1 1 0', me.colorF, 1);
235                         else draw_ButtonPicture('0 0 0', strcat(me.src, "_n"), '1 1 0', me.color, 1);
236                 }
237
238                 me.cursorPos = bound(0, me.cursorPos, strlen(me.text));
239                 cursorPosInWidths = draw_TextWidth(substring(me.text, 0, me.cursorPos), 0, me.realFontSize);
240                 totalSizeInWidths = draw_TextWidth(strcat(me.text, CURSOR), 0, me.realFontSize);
241
242                 if (me.dragScrollTimer < time)
243                 {
244                         float save;
245                         save = me.scrollPos;
246                         me.scrollPos = bound(cursorPosInWidths - (0.875 - me.keepspaceLeft - me.keepspaceRight), me.scrollPos, cursorPosInWidths - 0.125);
247                         if (me.scrollPos != save) me.dragScrollTimer = time + 0.2;
248                 }
249                 me.scrollPos = min(me.scrollPos, totalSizeInWidths - (1 - me.keepspaceRight - me.keepspaceLeft));
250                 me.scrollPos = max(0, me.scrollPos);
251
252                 draw_SetClipRect(eX * me.keepspaceLeft, eX * (1 - me.keepspaceLeft - me.keepspaceRight) + eY);
253                 if (me.editColorCodes)
254                 {
255                         string ch, ch2;
256                         float i, n;
257                         vector theColor;
258                         float theAlpha;  // float theVariableAlpha;
259                         vector p;
260                         vector theTempColor;
261                         float component;
262
263                         p = me.realOrigin - eX * me.scrollPos;
264                         theColor = '1 1 1';
265                         theAlpha = 1;  // theVariableAlpha = 1; // changes when ^ax found
266
267                         n = strlen(me.text);
268                         for (i = 0; i < n; ++i)
269                         {
270                                 ch = substring(me.text, i, 1);
271                                 if (ch == "^")
272                                 {
273                                         float w;
274                                         ch2 = substring(me.text, i + 1, 1);
275                                         w = draw_TextWidth(strcat(ch, ch2), 0, me.realFontSize);
276                                         if (ch2 == "^")
277                                         {
278                                                 draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
279                                                 draw_Text(p + eX * 0.25 * w, "^", me.realFontSize, theColor, theAlpha, 0);
280                                         }
281                                         else if (ch2 == "0" || stof(ch2))  // digit?
282                                         {
283                                                 switch (stof(ch2))
284                                                 {
285                                                         case 0: theColor = '0 0 0';
286                                                                 theAlpha = 1;
287                                                                 break;
288                                                         case 1: theColor = '1 0 0';
289                                                                 theAlpha = 1;
290                                                                 break;
291                                                         case 2: theColor = '0 1 0';
292                                                                 theAlpha = 1;
293                                                                 break;
294                                                         case 3: theColor = '1 1 0';
295                                                                 theAlpha = 1;
296                                                                 break;
297                                                         case 4: theColor = '0 0 1';
298                                                                 theAlpha = 1;
299                                                                 break;
300                                                         case 5: theColor = '0 1 1';
301                                                                 theAlpha = 1;
302                                                                 break;
303                                                         case 6: theColor = '1 0 1';
304                                                                 theAlpha = 1;
305                                                                 break;
306                                                         case 7: theColor = '1 1 1';
307                                                                 theAlpha = 1;
308                                                                 break;
309                                                         case 8: theColor = '1 1 1';
310                                                                 theAlpha = 0.5;
311                                                                 break;
312                                                         case 9: theColor = '0.5 0.5 0.5';
313                                                                 theAlpha = 1;
314                                                                 break;
315                                                 }
316                                                 draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
317                                                 draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
318                                         }
319                                         else if (ch2 == "x")  // ^x found
320                                         {
321                                                 theColor = '1 1 1';
322
323                                                 component = HEXDIGIT_TO_DEC(substring(me.text, i + 2, 1));
324                                                 if (component >= 0)  // ^xr found
325                                                 {
326                                                         theTempColor.x = component / 15;
327
328                                                         component = HEXDIGIT_TO_DEC(substring(me.text, i + 3, 1));
329                                                         if (component >= 0)  // ^xrg found
330                                                         {
331                                                                 theTempColor.y = component / 15;
332
333                                                                 component = HEXDIGIT_TO_DEC(substring(me.text, i + 4, 1));
334                                                                 if (component >= 0)  // ^xrgb found
335                                                                 {
336                                                                         theTempColor.z = component / 15;
337                                                                         theColor = theTempColor;
338                                                                         w = draw_TextWidth(substring(me.text, i, 5), 0, me.realFontSize);
339
340                                                                         draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
341                                                                         draw_Text(p, substring(me.text, i, 5), me.realFontSize, theColor, 1, 0);  // theVariableAlpha instead of 1 using alpha tags ^ax
342                                                                         i += 3;
343                                                                 }
344                                                                 else
345                                                                 {
346                                                                         // blue missing
347                                                                         w = draw_TextWidth(substring(me.text, i, 4), 0, me.realFontSize);
348                                                                         draw_Fill(p, eX * w + eY * me.realFontSize.y, eZ, 0.5);
349                                                                         draw_Text(p, substring(me.text, i, 4), me.realFontSize, '1 1 1', theAlpha, 0);
350                                                                         i += 2;
351                                                                 }
352                                                         }
353                                                         else
354                                                         {
355                                                                 // green missing
356                                                                 w = draw_TextWidth(substring(me.text, i, 3), 0, me.realFontSize);
357                                                                 draw_Fill(p, eX * w + eY * me.realFontSize.y, eY, 0.5);
358                                                                 draw_Text(p, substring(me.text, i, 3), me.realFontSize, '1 1 1', theAlpha, 0);
359                                                                 i += 1;
360                                                         }
361                                                 }
362                                                 else
363                                                 {
364                                                         // red missing
365                                                         // w = draw_TextWidth(substring(me.text, i, 2), 0) * me.realFontSize_x;
366                                                         draw_Fill(p, eX * w + eY * me.realFontSize.y, eX, 0.5);
367                                                         draw_Text(p, substring(me.text, i, 2), me.realFontSize, '1 1 1', theAlpha, 0);
368                                                 }
369                                         }
370                                         else
371                                         {
372                                                 draw_Fill(p, eX * w + eY * me.realFontSize.y, '1 1 1', 0.5);
373                                                 draw_Text(p, strcat(ch, ch2), me.realFontSize, theColor, theAlpha, 0);
374                                         }
375                                         p += w * eX;
376                                         ++i;
377                                         continue;
378                                 }
379                                 draw_Text(p, ch, me.realFontSize, theColor, theAlpha, 0);  // TODO theVariableAlpha
380                                 p += eX * draw_TextWidth(ch, 0, me.realFontSize);
381                         }
382                 }
383                 else
384                 {
385                         draw_Text(me.realOrigin - eX * me.scrollPos, me.text, me.realFontSize, '1 1 1', 1, 0);
386                 }
387
388                 if (!me.focused || (time - me.lastChangeTime) < floor(time - me.lastChangeTime) + 0.5) draw_Text(me.realOrigin + eX * (cursorPosInWidths - me.scrollPos), CURSOR, me.realFontSize, '1 1 1', 1, 0);
389
390                 draw_ClearClip();
391
392                 if (me.enableClearButton)
393                         if (me.text != "")
394                         {
395                                 if (me.focused && me.cb_pressed) draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_c"), eX * me.cb_width + eY, me.cb_colorC, 1);
396                                 else if (me.focused && me.cb_focused) draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_f"), eX * me.cb_width + eY, me.cb_colorF, 1);
397                                 else draw_Picture(eX * (1 + me.cb_offset - me.cb_width), strcat(me.cb_src, "_n"), eX * me.cb_width + eY, me.cb_color, 1);
398                         }
399
400                 // skipping SUPER(InputBox).draw(me);
401                 Item_draw(me);
402         }
403
404         void InputBox_showNotify(entity me)
405         {
406                 me.focusable = !me.disabled;
407         }
408 #endif