52 lastStr: LongWord; |
52 lastStr: LongWord; |
53 localLastStr: LongInt; |
53 localLastStr: LongInt; |
54 history: LongInt; |
54 history: LongInt; |
55 visibleCount: LongWord; |
55 visibleCount: LongWord; |
56 InputStr: TChatLine; |
56 InputStr: TChatLine; |
57 InputStrL: array[0..260] of char; // for full str + 4-byte utf-8 char |
57 InputStrL: array[0..260] of byte; // for full str + 4-byte utf-8 char |
58 ChatReady: boolean; |
58 ChatReady: boolean; |
59 showAll: boolean; |
59 showAll: boolean; |
60 liveLua: boolean; |
60 liveLua: boolean; |
61 ChatHidden: boolean; |
61 ChatHidden: boolean; |
|
62 InputLinePrefix: shortstring; |
|
63 // cursor |
|
64 cursorPos, cursorX: LongInt; |
|
65 LastKeyPressTick: LongWord; |
62 |
66 |
63 const |
67 const |
|
68 InputStrLNoPred: byte = 255; |
|
69 |
64 colors: array[#0..#6] of TSDL_Color = ( |
70 colors: array[#0..#6] of TSDL_Color = ( |
65 (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything |
71 (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything |
66 (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White] |
72 (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White] |
67 (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple] |
73 (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple] |
68 (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime] |
74 (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime] |
83 |
89 |
84 |
90 |
85 const Padding = 2; |
91 const Padding = 2; |
86 ClHeight = 2 * Padding + 16; // font height |
92 ClHeight = 2 * Padding + 16; // font height |
87 |
93 |
|
94 procedure UpdateCursorCoords(); |
|
95 var font: THWFont; |
|
96 str : shortstring; |
|
97 coff: LongInt; |
|
98 begin |
|
99 // calculate cursor offset |
|
100 |
|
101 str:= InputLinePrefix + InputStr.s; |
|
102 font:= CheckCJKFont(ansistring(str), fnt16); |
|
103 |
|
104 // get only substring before cursor to determine length |
|
105 SetLength(str, Length(InputLinePrefix) + cursorPos); |
|
106 // get render size of text |
|
107 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil); |
|
108 |
|
109 |
|
110 cursorX:= 7 - cScreenWidth div 2 + coff; |
|
111 end; |
|
112 |
|
113 procedure ResetCursor(); |
|
114 begin |
|
115 cursorPos:= 0; |
|
116 UpdateCursorCoords(); |
|
117 end; |
|
118 |
88 procedure RenderChatLineTex(var cl: TChatLine; var str: shortstring); |
119 procedure RenderChatLineTex(var cl: TChatLine; var str: shortstring); |
89 var strSurface, |
120 var strSurface, |
90 resSurface: PSDL_Surface; |
121 resSurface: PSDL_Surface; |
91 dstrect : TSDL_Rect; // destination rectangle for blitting |
122 dstrect : TSDL_Rect; // destination rectangle for blitting |
92 font : THWFont; |
123 font : THWFont; |
202 left:= 4 - cScreenWidth div 2; |
233 left:= 4 - cScreenWidth div 2; |
203 top := 10 + visibleCount * ClHeight; // we start with input line (if any) |
234 top := 10 + visibleCount * ClHeight; // we start with input line (if any) |
204 |
235 |
205 // draw chat input line first and under all other lines |
236 // draw chat input line first and under all other lines |
206 if (GameState = gsChat) and (InputStr.Tex <> nil) then |
237 if (GameState = gsChat) and (InputStr.Tex <> nil) then |
|
238 begin |
207 DrawTexture(left, top, InputStr.Tex); |
239 DrawTexture(left, top, InputStr.Tex); |
208 |
240 // draw cursor |
|
241 if ((RealTicks - LastKeyPressTick) and 512) < 256 then |
|
242 DrawLineOnScreen(cursorX, top + 2, cursorX, top + ClHeight - 2, 2.0, $00, $FF, $FF, $FF); |
|
243 end; |
|
244 |
|
245 |
|
246 // draw chat lines |
209 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then |
247 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then |
210 begin |
248 begin |
211 if MissedCount <> 0 then // there are chat strings we missed, so print them now |
249 if MissedCount <> 0 then // there are chat strings we missed, so print them now |
212 begin |
250 begin |
213 for i:= 0 to MissedCount - 1 do |
251 for i:= 0 to MissedCount - 1 do |
408 {$ENDIF} |
446 {$ENDIF} |
409 GameState:= gsGame; |
447 GameState:= gsGame; |
410 ResetKbd; |
448 ResetKbd; |
411 end; |
449 end; |
412 |
450 |
|
451 procedure DelBytesFromInputStr(endIdx: integer; count: byte); |
|
452 var i, startIdx: integer; |
|
453 begin |
|
454 // nothing to do if count is 0 |
|
455 if count = 0 then |
|
456 exit; |
|
457 |
|
458 // first byte to delete |
|
459 startIdx:= endIdx - (count - 1); |
|
460 |
|
461 // delete bytes from string |
|
462 Delete(InputStr.s, startIdx, count); |
|
463 |
|
464 // wipe utf8 info for deleted char |
|
465 InputStrL[endIdx]:= InputStrLNoPred; |
|
466 |
|
467 // shift utf8 char info to reflect new string |
|
468 for i:= endIdx + 1 to Length(InputStr.s) + count do |
|
469 begin |
|
470 if InputStrL[i] <> InputStrLNoPred then |
|
471 begin |
|
472 InputStrL[i-count]:= InputStrL[i] - count; |
|
473 InputStrL[i]:= InputStrLNoPred; |
|
474 end; |
|
475 end; |
|
476 |
|
477 SetLine(InputStr, InputStr.s, true); |
|
478 end; |
|
479 |
|
480 // returns count of removed bytes |
|
481 function DelCharFromInputStr(idx: integer): integer; |
|
482 var btw: byte; |
|
483 begin |
|
484 // note: idx is always at last byte of utf8 chars. cuz relevant for InputStrL |
|
485 |
|
486 if (Length(InputStr.s) < 1) or (idx < 1) or (idx > Length(InputStr.s)) then |
|
487 exit(0); |
|
488 |
|
489 btw:= byte(idx) - InputStrL[idx]; |
|
490 |
|
491 DelCharFromInputStr:= btw; |
|
492 |
|
493 DelBytesFromInputStr(idx, btw); |
|
494 end; |
|
495 |
|
496 procedure DoCursorStepForward(); |
|
497 begin |
|
498 if cursorPos < Length(InputStr.s) then |
|
499 begin |
|
500 // go to end of next utf8-char |
|
501 repeat |
|
502 inc(cursorPos); |
|
503 until InputStrL[cursorPos] <> InputStrLNoPred; |
|
504 end; |
|
505 end; |
|
506 |
413 procedure KeyPressChat(Key, Sym: Longword); |
507 procedure KeyPressChat(Key, Sym: Longword); |
414 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0); |
508 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0); |
415 var i, btw, index: integer; |
509 var i, btw, index: integer; |
416 utf8: shortstring; |
510 utf8: shortstring; |
417 action: boolean; |
511 action: boolean; |
418 begin |
512 begin |
|
513 LastKeyPressTick:= RealTicks; |
419 action:= true; |
514 action:= true; |
420 case Sym of |
515 case Sym of |
421 SDLK_BACKSPACE: |
516 SDLK_BACKSPACE: |
422 begin |
517 begin |
|
518 // remove char before cursor (note: cursorPos is 0-based, char idx isn't) |
|
519 dec(cursorPos, DelCharFromInputStr(cursorPos)); |
|
520 UpdateCursorCoords(); |
|
521 end; |
|
522 SDLK_DELETE: |
|
523 begin |
|
524 // remove char after cursor |
|
525 if cursorPos < Length(InputStr.s) then |
|
526 begin |
|
527 DoCursorStepForward(); |
|
528 dec(cursorPos, DelCharFromInputStr(cursorPos)); |
|
529 UpdateCursorCoords(); |
|
530 end; |
|
531 end; |
|
532 SDLK_ESCAPE: |
|
533 begin |
423 if Length(InputStr.s) > 0 then |
534 if Length(InputStr.s) > 0 then |
424 begin |
535 begin |
425 InputStr.s[0]:= InputStrL[byte(InputStr.s[0])]; |
536 SetLine(InputStr, '', true); |
426 SetLine(InputStr, InputStr.s, true) |
537 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); |
|
538 ResetCursor(); |
427 end |
539 end |
428 end; |
540 else CleanupInput |
429 SDLK_ESCAPE: |
541 end; |
|
542 SDLK_RETURN, SDLK_KP_ENTER: |
430 begin |
543 begin |
431 if Length(InputStr.s) > 0 then |
544 if Length(InputStr.s) > 0 then |
432 SetLine(InputStr, '', true) |
|
433 else CleanupInput |
|
434 end; |
|
435 SDLK_RETURN, SDLK_KP_ENTER: |
|
436 begin |
|
437 if Length(InputStr.s) > 0 then |
|
438 begin |
545 begin |
439 AcceptChatString(InputStr.s); |
546 AcceptChatString(InputStr.s); |
440 SetLine(InputStr, '', false) |
547 SetLine(InputStr, '', false); |
|
548 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred); |
|
549 ResetCursor(); |
441 end; |
550 end; |
442 CleanupInput |
551 CleanupInput |
443 end; |
552 end; |
444 SDLK_UP, SDLK_DOWN: |
553 SDLK_UP, SDLK_DOWN: |
445 begin |
554 begin |
446 if (Sym = SDLK_UP) and (history < localLastStr) then inc(history); |
555 if (Sym = SDLK_UP) and (history < localLastStr) then inc(history); |
447 if (Sym = SDLK_DOWN) and (history > 0) then dec(history); |
556 if (Sym = SDLK_DOWN) and (history > 0) then dec(history); |
448 index:= localLastStr - history + 1; |
557 index:= localLastStr - history + 1; |
449 if (index > localLastStr) then |
558 if (index > localLastStr) then |
450 SetLine(InputStr, '', true) |
559 SetLine(InputStr, '', true) |
451 else SetLine(InputStr, LocalStrs[index], true) |
560 else SetLine(InputStr, LocalStrs[index], true); |
452 end; |
561 // TODO rebuild/restore InputStrL!! |
453 SDLK_RIGHT, SDLK_LEFT, SDLK_DELETE, |
562 cursorPos:= Length(InputStr.s); |
454 SDLK_HOME, SDLK_END, |
563 UpdateCursorCoords(); |
|
564 end; |
|
565 SDLK_HOME: |
|
566 begin |
|
567 if cursorPos > 0 then |
|
568 begin |
|
569 cursorPos:= 0; |
|
570 UpdateCursorCoords(); |
|
571 end; |
|
572 end; |
|
573 SDLK_END: |
|
574 begin |
|
575 i:= Length(InputStr.s); |
|
576 if cursorPos < i then |
|
577 begin |
|
578 // TODO uft-8 |
|
579 cursorPos:= i; |
|
580 UpdateCursorCoords(); |
|
581 end; |
|
582 end; |
|
583 SDLK_LEFT: |
|
584 begin |
|
585 if cursorPos > 0 then |
|
586 begin |
|
587 // go to end of previous utf8-char |
|
588 cursorPos:= InputStrL[cursorPos]; |
|
589 UpdateCursorCoords(); |
|
590 end; |
|
591 end; |
|
592 SDLK_RIGHT: |
|
593 begin |
|
594 DoCursorStepForward(); |
|
595 UpdateCursorCoords(); |
|
596 end; |
455 SDLK_PAGEUP, SDLK_PAGEDOWN: |
597 SDLK_PAGEUP, SDLK_PAGEDOWN: |
456 begin |
598 begin |
457 // ignore me!!! |
599 // ignore me!!! |
458 end; |
600 end; |
459 else |
601 else |
478 Key:= Key shr 6 |
620 Key:= Key shr 6 |
479 end; |
621 end; |
480 |
622 |
481 utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8; |
623 utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8; |
482 |
624 |
483 if byte(InputStr.s[0]) + btw > 240 then |
625 if Length(InputStr.s) + btw > 240 then |
484 exit; |
626 exit; |
485 |
627 |
486 InputStrL[byte(InputStr.s[0]) + btw]:= InputStr.s[0]; |
628 // if we insert rather than append, shift info in InputStrL accordingly |
487 SetLine(InputStr, InputStr.s + utf8, true) |
629 if cursorPos < Length(InputStr.s) then |
|
630 begin |
|
631 for i:= Length(InputStr.s) downto cursorPos + 1 do |
|
632 begin |
|
633 if InputStrL[i] <> InputStrLNoPred then |
|
634 begin |
|
635 InputStrL[i+btw]:= InputStrL[i] + btw; |
|
636 InputStrL[i]:= InputStrLNoPred; |
|
637 end; |
|
638 end; |
|
639 end; |
|
640 |
|
641 InputStrL[cursorPos + btw]:= cursorPos; |
|
642 Insert(utf8, InputStr.s, cursorPos + 1); |
|
643 SetLine(InputStr, InputStr.s, true); |
|
644 |
|
645 cursorPos:= cursorPos + btw; |
|
646 UpdateCursorCoords(); |
488 end |
647 end |
489 end; |
648 end; |
490 |
649 |
491 procedure chChatMessage(var s: shortstring); |
650 procedure chChatMessage(var s: shortstring); |
492 begin |
651 begin |