Issue: FMX.Objects.TText, what I need


Issue: FMX.Objects.TText, what I need

public property TextWidth: single read FLayout.TextWidth;
public property TextHeight: single read FLayout.TextHeight;

or

TText = class(TControl, ITextSettings)
private
  FLayout: TTextLayout; //exists
public
  property Layout: TTextLayout read FLayout; // missing
end;

Why?

In my code I have TableText: TText; TableText.Font.Family is set to a monospaced font; TableText.Text is coming from StringList.Text;
The text data is equivalent to a retangular matrix of char; Yes, the text is seen by the user as rows and columns; The user is able to navigate "cells" like in TStringGrid; TableText.Autosize is true; My code wants to read the actual width and heigh of the text, in order to manage the position and size of the current cell. (The application displays a TRectangle with Opacity := 0.5 on top of the current cell.)

Here is the problem:

Using Width and Height properties is not good enough, because the actual text is a little smaller, several pixels. In order to compute the position of the InplaceRectangle I need the actual value of the size of the text.

//not good enough
w := TableText.Width;
h := TableText.Height;

//works perfect (tested in XE4)
w := TableText.Layout.TextWidth;
h := TableText.Layout.TextHeight;

(The TText-based "Grid" uses an InplaceRectangle and an OutplaceEdit. You can see the Rectangle in the picture. It updates when I click with the mouse or use arrow keys.)

Comments

  1. You didn't say which version of Delphi but in XE7, the field is protected, so couldn't use a wrapper class (some call it a hack class) to gain access:

    type
      TTextWrapper = class(TText);

    w := TTextWrapper(TableText).Layout.TextWidth;
    h := TTextWrapper(TableText).Layout.TextHeight;

    ?

    ReplyDelete
  2. The field is private in XE4, which is what I am using. I see - if the field is protected now -  this is better. (Failed to find info in the XE8 docwiki, where I looked for public properties.)

    ReplyDelete
  3. http://docwiki.embarcadero.com/Libraries/XE8/en/FMX.Objects.TText_Properties - Make sure that "Proctected" is ticked at the top.

    Maybe a class helper will help you out here?

    Unfortunately this is as far I can go, as I don't use FMX (and rarely use class helpers)

    ReplyDelete
  4. Indeed - it is showing as protected there. :) In my case, Margin and Padding are 0. Have not investigated yet why there is a difference between Width and Layout.TextWidth.

    ReplyDelete
  5. Test in XE4, Win32, with property Layout made public.

    Expected: Width - Layout.TextWidth = 0;
    Actual: Width - Layout.TextWidth = (3..5);

    T: TText;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      T.AutoSize := True;
      T.WordWrap := False;
      T.Font.Family := 'Courier New';
      T.Font.Size := 16;
      //Margin is 0 by default
      T.Margins.Left := 0;
      T.Margins.Top := 0;
      T.Margins.Right := 0;
      T.Margins.Bottom := 0;
      //Padding is 0 by default
      T.Padding.Left := 0;
      T.Padding.Top := 0;
      T.Padding.Right := 0;
      T.Padding.Bottom := 0;
    end;

    procedure TForm1.Button1Click;
    begin
      T.Text := 'foo'; //tested with different data
    end;

    procedure TForm1.TestBtnClick;
    var
      w, tw: single;
      dx: single;
    begin
      w := T.Width;
      tw := T.Layout.TextWidth;
      dx := w - tw;
      Caption := Format('%.2f', [dx]);
    end;

    Typical difference between Width and Layout.TextWidth is between 3 and 5. Need to allow for the control to update after assigning to T.Text. In other words, don't run the test from the same event handler. Special case is when T.Text is empty.

    ReplyDelete
  6. Update: not a big issue any more. Focus has shifted. :)

    Found out that the Width of TText is designed to be 4 Pixels wider than Layout.TextWidth, see procedure TText.AdjustSize;
    SetBounds(Position.X, Position.Y, Round(R.Width) + 4, Round(R.Height) + 4);

    After learning that the real diff is approximately 4 Pixels I can work with Width and Height property.

    When I assign the content of a TStringList, it adds a trailing empty line, because of the trailing LineBreak in the text. (It would be nice if TStrings had a TextWithoutTrailingLineBreak readonly property.)
    Still thinking whether my TextGridControl should keep the empty last line by design - to be compatible with TStrings - or not.

    SL: TStrings;
    TableText: TText;
    TableText.Text := SL.Text;´

    //used in the test
    procedure TForm1.RemoveEmptyLastLineFromText;
    var
      s: string;
      t: string;
    begin
      t := SL.LineBreak;
      s := TableText.Text;
      if s.EndsWith(t) then
        SetLength(s, s.Length - t.Length);
      TableText.Text := s;
      HasChanged := True; //draw rectangle around text
    end;

    Have a test project now, which works with and without the empty last line.

    I expected the text to look the same on screen, and it almost does, but line 2 out of 9 visible lines in the test data dances a little up and down when I toggle the inclusion of the empty last line (line 10 at index 9). But I don't want to complain about that. This is probably the graphics driver.

    ReplyDelete

Post a Comment