FMX tip: ‘labels’ with selectable text

Say you are designing a form in FireMonkey, and wish to use a read-only edit box. No problem: stick a TEdit on the form and set its ReadOnly property to False. However, doing this doesn’t give any visual indication the edit box’s contents can’t be changed. Moreover, what if you’re using a TEdit rather than a TLabel simply to allow users to copy the text, if they really want to, in which case the edit box should completely blend into the form background and not even have a border? A common example of this is the version number text shown in an application’s ‘about’ box, for example the Delphi IDE’s or Firefox’s.

In the VCL removing a TEdit’s background is no problem – set its ParentColor property to True, and its BorderStyle property to bsNone. Neither the Color, ParentColor not BorderStyle properties exist on the FMX TEdit however, since all these attributes are controlled by its style.

So, one’s first thought might be to create a custom style for the edit box. To do that, right click on the TEdit in the form designer, then choose Edit Custom Style… from the popup menu. Once the style editor is showing, head to the structure view, open up the control hierarchy, select the item labelled ‘background’, then click the little ‘delete’ button (top left). Choose ‘Apply and Close’, and both the edit box’s white background and its thin border should be gone. Great!

Custom TEdit style with default main style active

Alas, but there’s a problem – if you change the global style, the edit box’s custom style won’t be updated accordingly. E.g., here’s what happens when the ‘dark’ style is loaded:

Custom TEdit style with different main style active

The problem here is that custom styles set up at design time involve merely ‘copy and paste “inheritance”‘ from whatever style is used by the IDE at the time. Change the main style for an application later, and any custom style won’t have its underpinings updated accordingly.

While this inherent limitation of the FMX styling system is bad enough in the present scenario, it is even worse for things like customising a list box item’s style, since the default platform styles differ (as you might expect) on details like highlight colours – just try running XE3’s CustomListBox demo on OS X to see this in action. Another pain point is the XE3 platform styles relying on their own bitmaps, one per platform – since no care was taken for each control to refer to the same part of the main bitmap when running on Windows and OS X, customising a control’s style at design time can lead to half of it disappearing when the program is run on a Mac!

What we need to do, then, is not use a custom style, but correct the default style for a read-only edit box at runtime, specifically by manipulating things in a handler for the control’s OnApplyStyleLookup event. Learning what to modify exactly means delving into the source and the stock style files, preferably more than one in the case of the latter. To take the TEdit case, for example, while all the standard styles define its background by a control with a style element name of ‘background’, the control type depends upon the style. In the case of the ‘platform’ styles it is a TSubImage, which references part of one big platform-specific bitmap. In the case of the additional, vector-only styles however (like ‘dark’), a TRectangle is used. Given that, removing the background must be done in a way that is neutral between both primitive control types (i.e., TSubImage and TRectange). In fact, it’s a good idea to make the code as generic as possible, since it’s perfectly possible Embarcadero will change the internals once more in XE4 or whatever.

Anyhow, to cut to the chase, this is what I’ve come up with –

procedure TForm2.LabelEditApplyStyleLookup(Sender: TObject);
var
  Obj: TFmxObject;
begin
  Obj := (Sender as TCustomEdit).FindStyleResource('background');
  if Obj is TControl then TControl(Obj).Opacity := 0;
end;

Why make the background control transparent rather than just delete it? Ultimately, because I’m not keen on just randomly deleting internal objects. Further, talk of automatic reference counting (ARC) being introduced (see here) makes me nervous simple Free calls on FMX objects (*) will even be possible in the future given the inherent conflict between reference counting and the TComponent ownership pattern that FMX currently employs.

(*) And no, I’m not predicting ARC will be forced (or even introduced at all) for Win32/Win64 projects. I’m purely talking about FMX, on mobile and possibly desktop too given much of the framework will presumably cross compile between mobile and desktop platforms.
Advertisements

2 thoughts on “FMX tip: ‘labels’ with selectable text

  1. Good writing and high level of expertise, but why the hell it only ends up in a crude workaround? What if you want to do that for hundreds of outputs in a big real app? Isn’t there a way to disable the control of TEdit color property in the style, and setting it as usual in the Property Editor, like in Delphi VCL?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s