unit console_painters; interface uses types, painters, buffers, terminals, encodings, textfitters; // TODO better use a "dummy" terminal instead of TDummyContinuousConsoleCharacterPainter? type // don't create these yourself. TDummyContinuousConsoleCharacterPainter = class(TDummyContinuousCharacterPainter, IContinuousCharacterPainter, IInterface) protected procedure EmitLineFeed(); override; published constructor Create; protected function MeasureCharacter(const aCharacter : TUnicodeCharacter) : TSize; override; // DO NOT CACHE. end; TContinuousConsoleCharacterPainter = class(TDummyContinuousConsoleCharacterPainter, IContinuousCharacterPainter, IInterface) private fDummyPainter : IContinuousCharacterPainter; fTerminal : ITerminal; published constructor Create(aTerminal : ITerminal); public destructor Destroy; override; protected property Terminal : ITerminal read fTerminal; procedure EmitLineFeed(); override; procedure RenderCharacter(const aCharacter : TUnicodeCharacter; aCount : Cardinal; aBeginning : Cardinal = 0); override; // no checks. procedure BeginRound; override; procedure Flush; override; procedure FinishRound; override; procedure SetTotalSizeChanged(aValue : TContinuousCharacterPainterTotalSizeChanged); override; procedure Resize(Sender : TObject; newSize : TSize); procedure SetRectangle(aValue : TTextLayoutRectangle); override; published function StoreCaretPosition : Cardinal; override; procedure SetCaretPosition(aValue : Cardinal); override; // Restore, actually. protected procedure BeginSelectingAt(aX, aY : Cardinal); override; procedure EndSelectingAt(aX, aY : Cardinal); override; published function GetDummyPainter() : IContinuousCharacterPainter; override; end; implementation uses sysutils; { TDummyContinuousConsoleCharacterPainter } constructor TDummyContinuousConsoleCharacterPainter.Create; begin inherited Create; end; function TDummyContinuousConsoleCharacterPainter.MeasureCharacter(const aCharacter : TUnicodeCharacter) : TSize; begin if (Length(aCharacter) < 1) or (aCharacter[0] = Ord(buffers.cLineBreak)) or (Ord(aCharacter[0]) < 32) then begin Result.cx := 0; Result.cy := 0; end else begin { TODO support other sizes. } Result.cx := 1; Result.cy := 1; end; end; procedure TDummyContinuousConsoleCharacterPainter.EmitLineFeed(); begin if Self.RowHeight < 1 then Self.RowHeight := 1; inherited EmitLineFeed(); end; { TContinuousCharacterPainter. } // TODO: return aEnd. procedure TContinuousConsoleCharacterPainter.RenderCharacter(const aCharacter : TUnicodeCharacter; aCount : Cardinal; aBeginning : Cardinal = 0); // no checks. var fCodepointIndex : Integer; begin { overly paranoid. } if aCount = 0 then Exit; // fTerminal.GotoXY(Self.PositionX, Self.PositionY); //if Self.PositionY < fTerminal.SizeY - 1 then // evil workaround to avoid scrolling. //Result := aBeginning + 1; if aCharacter[aBeginning] = Ord(buffers.cLineBreak) then begin end else if Ord(aCharacter[aBeginning]) < 32 then begin // ??? end else begin Terminal.WriteUnicode(aCharacter[aBeginning]); for fCodepointIndex := aBeginning + 1 to aBeginning + aCount - 1 do begin if IsBeginningOfNewCharacter(aCharacter[fCodepointIndex]) then Break; Terminal.WriteUnicode(aCharacter[fCodepointIndex]); end; end; {if aCharacter = Ord(buffers.cLineBreak) then fTerminal.EnableLineWrapping;} end; procedure TContinuousConsoleCharacterPainter.EmitLineFeed(); begin inherited EmitLineFeed(); Terminal.ClrEOL; //if Self.PositionY > Terminal.SizeY - 1 then // Exit; Terminal.GotoXY(Self.PositionX, Self.PositionY); if Self.PositionY >= Terminal.SizeY - 1 then // on the last line, don't wrap so it doesn't scroll off screen Terminal.DisableLineWrapping else Terminal.EnableLineWrapping; end; procedure TContinuousConsoleCharacterPainter.BeginRound; begin inherited; Terminal.GotoHome; Terminal.EnableLineWrapping; end; procedure TContinuousConsoleCharacterPainter.Flush; begin Terminal.Flush; end; procedure TContinuousConsoleCharacterPainter.FinishRound; begin inherited FinishRound; if PositionY < Rectangle.Height then Terminal.ClrEOS; end; constructor TContinuousConsoleCharacterPainter.Create(aTerminal : ITerminal); begin inherited Create; fTerminal := aTerminal; Terminal.SizeChanged := Resize; end; destructor TContinuousConsoleCharacterPainter.Destroy; begin inherited; end; function TContinuousConsoleCharacterPainter.StoreCaretPosition : Cardinal; begin Terminal.StoreCurrentPosition; Result := 0; end; procedure TContinuousConsoleCharacterPainter.SetCaretPosition(aValue : Cardinal); begin Terminal.RestoreCurrentPosition; Terminal.Flush; end; procedure TContinuousConsoleCharacterPainter.BeginSelectingAt(aX, aY : Cardinal); begin // FIXME? Terminal.SetMouseHighlightTrackingRange(Terminal.WhereX + 1, Terminal.WhereY + 1, 1, Rectangle.Height + 1); // Terminal.SizeY); inherited BeginSelectingAt(aX, aY); // TODO end; procedure TContinuousConsoleCharacterPainter.EndSelectingAt(aX, aY : Cardinal); begin inherited EndSelectingAt(aX, aY); // TODO end; function TContinuousConsoleCharacterPainter.GetDummyPainter() : IContinuousCharacterPainter; begin if not Assigned(fDummyPainter) then begin fDummyPainter := TDummyContinuousConsoleCharacterPainter.Create(); fDummyPainter.RowSpacing := Self.RowSpacing; end; Result := fDummyPainter; end; procedure TContinuousConsoleCharacterPainter.SetTotalSizeChanged(aValue : TContinuousCharacterPainterTotalSizeChanged); begin inherited SetTotalSizeChanged(aValue); Terminal.SizeChanged := nil; Terminal.SizeChanged := Resize; end; procedure TContinuousConsoleCharacterPainter.Resize(Sender : TObject; newSize : TSize); begin Self.EmitTotalSizeChanged(newSize); if Assigned(fDummyPainter) and Assigned(fDummyPainter.TotalSizeChanged) then fDummyPainter.TotalSizeChanged(Self, newSize); end; procedure TContinuousConsoleCharacterPainter.SetRectangle(aValue : TTextLayoutRectangle); begin inherited SetRectangle(aValue); if Assigned(fDummyPainter) then fDummyPainter.SetRectangle(aValue); end; end.