4
votes

I found that this question has been asked before for VCL, but I haven't had any luck getting the answers for that question to work on a Firemonkey TMemo.

I've noticed that memo.Lines.Count always seems to match the line count based off how many I add, not as they're formatted (the memo does have wordwrap turned on). Without knowing that number I'm not sure how to start figuring this out.

Any ideas?

Edit: The width of the memo will depend on the orientation of the device, obviously if the width changes the number of lines showing could change. Also, I'd like to not alter the font of the memo.

4
How about using DrawTextEx with the DT_CALC_RECT flag?Jerry Dodge
Wait, Firemonkey, that wouldn't workJerry Dodge

4 Answers

6
votes
Procedure ResizeMemo(AMemo: TMemo);
const
  Offset = 4; //The diference between ContentBounds and ContentLayout
begin
  AMemo.Height := AMemo.ContentBounds.Height + Offset;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ResizeMemo(Memo1);
end;
1
votes

The following function should give you what you want. It does not change anything in the memo and takes into account the font and any line breaks and wraps. If setting the memo height to the calculated value, you will need to add a few pixels for borders to eliminate scrollbars.

(add fmx.text to uses statement for XE3, might be different for other versions as they keep changing things with each release)

function get_memo_height(amemo:tmemo):single;

var i:integer;
    astring:string;
    layout:ttextlayout;

begin
  Layout := TTextLayoutManager.DefaultTextLayout.Create;
  astring:='';
  for i:=0 to amemo.lines.count-1 do astring:=astring+amemo.lines[i]+chr(10);
  Layout.BeginUpdate;
  Layout.Text :=astring;
  Layout.WordWrap := amemo.wordwrap;
  Layout.HorizontalAlign := amemo.TextAlign;
  Layout.MaxSize := PointF(amemo.width-amemo.VScrollBar.width,maxint);
  Layout.VerticalAlign := tTextAlign.taLeading;
  Layout.Font := amemo.Font;
  Layout.TopLeft := pointf(0,0);
  Layout.EndUpdate;
  result:=layout.textrect.bottom;
  Layout.free;
end;
1
votes

Here's my new attempt:

FMX:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 305
  ClientWidth = 333
  FormFactor.Width = 1920
  FormFactor.Height = 1080
  FormFactor.Devices = [dkDesktop]
  DesignerMobile = False
  DesignerWidth = 0
  DesignerHeight = 0
  DesignerDeviceName = ''
  DesignerOrientation = 0
  DesignerOSVersion = ''
  object Memo1: TMemo
    Touch.InteractiveGestures = [igPan, igLongTap, igDoubleTap]
    Anchors = [akLeft, akTop, akRight]
    Height = 257.000000000000000000
    Position.X = 8.000000000000000000
    Position.Y = 8.000000000000000000
    TabOrder = 0
    Width = 312.000000000000000000
    Lines.Strings = (

        'Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 L' +
        'ine 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 '
      'Line 2 Line 2 Line 2 Line 2 Line 2 '
      ''
      'Line 4'

        'Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 L' +
        'ine 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Li' +
        'ne 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Line 5 Lin' +
        'e 5 Line 5 Line 5 Line 5 Line 5 ')
    Font.Family = 'Arial'
    WordWrap = True
  end
  object Button1: TButton
    Anchors = [akLeft, akTop, akRight]
    Height = 22.000000000000000000
    Position.X = 8.000000000000000000
    Position.Y = 272.000000000000000000
    TabOrder = 1
    Text = 'Show Line Count'
    Width = 312.000000000000000000
    OnClick = Button1Click
  end
  object Memo3: TMemo
    Touch.InteractiveGestures = [igPan, igLongTap, igDoubleTap]
    Height = 50.000000000000000000
    Position.X = 176.000000000000000000
    Position.Y = 184.000000000000000000
    TabOrder = 2
    Visible = False
    Width = 100.000000000000000000
    Lines.Strings = (
      '1')
  end
end

PAS:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Rtti, System.Classes,
  System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Layouts,
  FMX.Memo, FMX.Text, FMX.StdCtrls, FMX.TextLayout;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Memo3: TMemo;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
    layout: TTextLayout;
    cont, LineHeight : real;
begin
  cont := 0;
  layout:= TTextLayout(memo3.Lines.Objects[0]);
  LineHeight := layout.TextHeight;
  for i:= 0 to memo1.Lines.Count-1 do
  begin
    layout:= TTextLayout(memo1.Lines.Objects[i]);
    if Assigned(layout) then
    begin
      cont := cont + (layout.TextHeight / LineHeight);
    end;
  end;
  showmessage('Line count according to firemonkey: ' + inttostr(memo1.Lines.Count));
  showmessage('Real line count: ' + VarToStr(cont));
end;

end.

Hope it Helps.

0
votes

This is, of course, not elegant, but you can move the caret to the end of your TMemo, and then ask for the CaretPosition:

function getLastMemoLineNumber(const memo: TMemo): Integer;
var
    oldCaretPosition: TCaretPosition;
begin
    Assert( Assigned(memo) );
    oldCaretPosition := memo.CaretPosition;

    try
        memo.GoToTextEnd();
        Result := memo.CaretPosition.Line;
    finally
        memo.CaretPosition := oldCaretPosition;
    end;
end;