I want button size (width and height) to be as small as possible, but I want it to fit the text. Any code example? Delphi XE4 FireMonkey Mobile Application.
Asked
Active
Viewed 5,549 times
2 Answers
10
FireMonkey renders text via methods using TTextLayout class.
We can access this methods via a class helper and then change the buttons size based on the information provided by the layout.
uses FMX.TextLayout;
type
TextHelper = class helper for TText
function getLayout : TTextLayout;
end;
function TextHelper.getLayout;
begin
result := Self.fLayout;
end;
procedure ButtonAutoSize(Button : TButton);
var
bCaption : TText;
m : TBounds;
begin
bCaption := TText(Button.FindStyleResource('text',false));
bCaption.HorzTextAlign := TTextAlign.taLeading;
bCaption.VertTextAlign := TTextAlign.taLeading;
m := bCaption.Margins;
Button.Width := bCaption.getLayout.Width + m.Left + m.Right;
Button.Height := bCaption.getLayout.Height + m.Top + m.Bottom;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ButtonAutoSize(Sender as TButton);
end;
Update
Here is a more future proof solution that doesn't require exposing private class fields.
uses FMX.Objects;
procedure ButtonAutoSizeEx(Button: TButton);
var
Bitmap: TBitmap;
Margins: TBounds;
Width, Height: Single;
begin
Bitmap := TBitmap.Create;
Bitmap.Canvas.Font.Assign(Button.TextSettings.Font);
Width := Bitmap.Canvas.TextWidth(Button.Text);
Height := Bitmap.Canvas.TextHeight(Button.Text);
Margins := (Button.FindStyleResource('text', false) as TText).Margins;
Button.TextSettings.HorzAlign := TTextAlign.Leading;
Button.Width := Width + Margins.Left + Margins.Right;
Button.Height := Height + Margins.Top + Margins.Bottom;
end;
This example omits any word wrapping or character trimming.

Peter
- 2,977
- 1
- 17
- 29
-
It behave strangely on XE6. If I do successive calls on the same button with different text lenghts the width grows if needed but never get reduced. – Regis St-Gelais Sep 09 '14 at 14:51
-
@RegisSt-Gelais, please keep in mind that this was written for XE4. Firemonkey has went through a lot of changes since then so results may vary. You should post a new SO question for XE6 if this is important to you. – Peter Sep 09 '14 at 15:29
-
Thank you for your comment and also thank you for the original solution that you gave.The question is still valid. I don't want to create a duplicate. Maybe by reviving it and saying that it does not work on XE6, someone will post a new solution. Regards. – Regis St-Gelais Sep 09 '14 at 19:22
-
@RegisSt-Gelais, you're right, I've updated my answer with an alternative solution to the problem, hopefully something more future proof. Let me know if this fixes your issues. – Peter Sep 10 '14 at 09:48
-
1It works great with one addition. Some time FindStyleResource returns nil making the app crash. I added something like Text := Button.FindStyleResource('text', false) as TText; if Text is TText then begin .... – Regis St-Gelais Sep 10 '14 at 16:09
-
and I added it before the Bitmap := TBitmap.Create; so that the bitmap is not created if not needed. – Regis St-Gelais Sep 10 '14 at 17:11
-
@RegisSt-Gelais, If you are executing this routine many times then I suggest that you cache the Style object, also probably best to create the Bitmap in the outter scope of this method. Also, you don't need to use the `is` operator, `as` already performs strict type checking automatically. – Peter Sep 10 '14 at 17:25
-
Why don't you access Button.Canvas instead? – yonojoy Sep 05 '16 at 08:27
-
I'll post my code as an other answer. That code does not need to create a bitmap and does not access any private fields and seems to work fine. Feel free to update your answer with this code, then I'll remove my answer. – yonojoy Sep 05 '16 at 08:34
1
Based on the answer of @Peter, but no need to create a bitmap:
//...
type
TButtonHelper = class helper for TButton
procedure FitToText(AOnlyWidth: Boolean = False);
end;
implementation
//...
// Adapt button size to text.
// This code does not account for word wrapping or character trimming.
procedure TButtonHelper.FitToText(AOnlyWidth: Boolean = False);
var
Margins: TBounds;
TextWidth, TextHeight: Single;
Obj: TFmxObject;
const
CLONE_NO = False;
begin
Obj := FindStyleResource('text', CLONE_NO);
if Obj is TText then //from Stackoverflow comments: Some time FindStyleResource returns nil making the app crash
begin
Margins := (Obj as TText).Margins;
TextWidth := Canvas.TextWidth(Text);
if not AOnlyWidth then
TextHeight := Canvas.TextHeight(Text);
TextSettings.HorzAlign := TTextAlign.taLeading; //works in XE4
//later FMX-Versions ?: TextSettings.HorzAlign := TTextAlign.Leading;
Width := TextWidth + Margins.Left + Margins.Right;
if not AOnlyWidth then
Height := TextHeight + Margins.Top + Margins.Bottom;
end;
end;

yonojoy
- 5,486
- 1
- 31
- 60
-
@Peter Feel free to update your answer with this code. Then I'll remove my answer, because you owe the original idea. – yonojoy Sep 05 '16 at 08:40
-
Works great on Android, but on Windows it comes out half the size it should be...? (D10 Seattle) – Jerry Dodge Sep 11 '17 at 21:22
-
@JerryDodge I tested this with XE4 and XE8 on Windows without any problems. Does the solution of Peter (ButtonAutoSizeEx) work for you? – yonojoy Sep 18 '17 at 08:01