hedgewars/uChat.pas
changeset 10834 2e83f33dfe5b
parent 10737 408803ca951a
child 10835 8ac09cd322b7
equal deleted inserted replaced
10833:655bab155a58 10834:2e83f33dfe5b
    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;
   137 
   168 
   138 if isInput then
   169 if isInput then
   139     begin
   170     begin
   140     cl.s:= str;
   171     cl.s:= str;
   141     color:= colors[#6];
   172     color:= colors[#6];
   142     str:= UserNick + '> ' + str + '_'
   173     str:= InputLinePrefix + str + ' ';
   143     end
   174     end
   144 else
   175 else
   145     begin
   176     begin
   146     color:= colors[str[1]];
   177     color:= colors[str[1]];
   147     delete(str, 1, 1);
   178     delete(str, 1, 1);
   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
   559     ChatReady:= false;
   718     ChatReady:= false;
   560     missedCount:= 0;
   719     missedCount:= 0;
   561     liveLua:= false;
   720     liveLua:= false;
   562     ChatHidden:= false;
   721     ChatHidden:= false;
   563 
   722 
       
   723     InputLinePrefix:= UserNick + '> ';
       
   724     inputStr.s:= '';
   564     inputStr.Tex := nil;
   725     inputStr.Tex := nil;
   565     for i:= 0 to MaxStrIndex do
   726     for i:= 0 to MaxStrIndex do
   566         Strs[i].Tex := nil;
   727         Strs[i].Tex := nil;
       
   728 
       
   729     FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   730 
       
   731     LastKeyPressTick:= 0;
       
   732     ResetCursor();
   567 end;
   733 end;
   568 
   734 
   569 procedure freeModule;
   735 procedure freeModule;
   570 var i: ShortInt;
   736 var i: ShortInt;
   571 begin
   737 begin