Home > CSS, Delphi, JavaScript, Object Pascal, OP4JS, Smart Mobile Studio > Selecting text, Smart Mobile Studio

Selecting text, Smart Mobile Studio

A really good question came my way regarding text selection and why ordinary browser behavior is disabled in Smart Mobile Studio applications. It was Jarto Tarpio that posted it on the SMS Facebook page, and I was a little surpriced at first to say the least.

Now the VJL was designed to look and feel more or less identical to any ordinary, native UI framework. This means that ordinary HTML text selection (marking text with your mouse) is something you dont want to have.

But what Jarto pointed out was that there is something fishy about how it’s implemented in Smart at the moment, because even when he removed the CSS rules that disables this, selecting text is still not possible.

That one line ..

Two RTL updates ago I remember dealing with this exact topic. I was happy to find that CSS had a couple of universal rules you could use, which meant that i could remove the code I was using to avoid selection ever taking place.

Sadly, I must have forgotten all about it (or it got accidentally filtered out during a manual unit merge) because it was till there (!) In other words not only did the CSS make sure you couldnt enable text selection, the startup code for all TW3TagObj derived classes kidnaps the OnSelectStart event — effectively killing initializing a selection at all.

Patching the RTL yourself

The change you need is basically a “one liner”, and it wont affect your programs at all. If you open up SmartCL.Components (right click the unit in your units clause), hit ALT + F which brings up the search dialog and then enter “TW3CustomControl.HookEvents” that’s where the problem lies:

procedure TW3CustomControl.HookEvents;
begin
  inherited;
  Handle['onclick'] := @CBClick;
  //w3_bind2(Handle, 'onselectstart', CBNoBehavior); //Remove this line !!
  w3_bind2(Handle, 'onfocus', CBFocused);
  w3_bind2(Handle, 'onblur', CBLostFocus);
end;

So simply delete the first “w3_bind2” call, the one that assigns OnSelectStart to the “no operation” event handler CBNoBehavior — now go to your main form unit, type something and then click Save.

Note: The editor doesnt monitor RTL files for edit-changes since these units are not meant to be edited. So you have to alter something in your project and force the IDE to allow your change to be saved.

Making selection an option

With that nasty (and no longer needed) line of code out of the way – I have added two new methods to the VJL that gives you 100% control over content selection. This will be in the next update but for those that cant wait, the following methods have been added to TW3MovableControl:

function GetContentSelectionMode: TW3ContentSelectionMode;virtual;
procedure SetContentSelectionMode(const NewMode: TW3ContentSelectionMode);virtual;

Here is the code if you want to get these features straight away:


type
  TW3ContentSelectionMode = (
    tsmNone,
    tsmAuto,
    tsmText,
    tsmAll,
    tsmElement
    );

function TW3MovableControl.GetContentSelectionMode: TW3ContentSelectionMode;
begin
  var CurrentMode := Handle.style[BrowserAPI.PrefixDef("user-select")];
  if (CurrentMode) then
  begin
    case TVariant.AsString(CurrentMode).ToLower() of
    'auto': result := tsmAuto;
    'text': result := tsmText;
    'all': result := tsmAll;
    'element': result := tsmElement;
    else
      result := tsmNone;
    end;
  end else
  begin
    Handle.style[w3_CSSPrefixDef("user-select")] := 'none';
    result := tsmNone;
  end;
end;

procedure TW3MovableControl.SetContentSelectionMode
  (const NewMode: TW3ContentSelectionMode);
begin
  case NewMode of
  tsmAuto:  Handle.style[w3_CSSPrefixDef("user-select")] := 'auto';
  tsmAll:   Handle.style[w3_CSSPrefixDef("user-select")] := 'all';
  tsmText:  Handle.style[w3_CSSPrefixDef("user-select")] := 'text';
  tsmNone:  Handle.style[w3_CSSPrefixDef("user-select")] := 'none';
  tsmElement: Handle.style[w3_CSSPrefixDef("user-select")] := 'element';
  end;
end;

Writing your custom controls with selection turned on

Ok, first let’s write a class that allows text-selection. We are going to use a DIB, which is the default element of TW3CustomControl, so this will be a short example:

type

TSelectTestControl = class(TW3CustomControl)
protected
  procedure ObjectReady;override;
public
  property Text:string read (GetInnerText) write (SetInnerText(Value));
end;

procedure TSelectTestControl.ObjectReady;
begin
  inherited;
  SetContentSelectionMode(tsmText);
end;

Since user-select is set to “none” in all our CSS style themes, enabling editing means telling the DOM (document object model) that this control does allow text selection. In the above example I do that in the ObjectReady method, just to make sure the DIV TSelectTestControl represent is in the clear and has been created successfully and injected into the DOM.

So lets go back to our main form and create an instance of our new control. You should also drop a TW3DIVHtmlElement on the form first, so we have something to compare with.

Ok, here is the form unit completed:

unit Form1;

interface

uses
  System.Colors, SmartCL.Controls.Elements, SmartCL.System,
  SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,
  SmartCL.Fonts, SmartCL.Borders, SmartCL.Application;

type

  (* our spanking new control. Not a TW3DIVHtmlElement in sight! *)
  TSelectTestControl = class(TW3CustomControl)
  protected
    procedure ObjectReady;override;
  public
    property Text:string read (GetInnerText) write (SetInnerText(Value));
  end;

  TForm1 = class(TW3Form)
  private
    {$I 'Form1:intf'}
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure Resize; override;
  end;

implementation

{ TForm1 }

uses system.types, system.time;

procedure TSelectTestControl.ObjectReady;
begin
  inherited;
  SetContentSelectionMode(tsmText);
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
  var LTemp := TSelectTestControl.Create(self);
  LTemp.background.fromColor(clRed);
  LTemp.Text:="this is some text";
  LTemp.setBounds(10,10,200,200);
  W3DivHTMLElement1.innerhtml := "<i>This is some text";
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;

procedure TForm1.Resize;
begin
  inherited;
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

And the results are exactly what we wanted:

select text

The red box is our control, as you can see you can now mark the text

Final words

I know this has been a topic many people have asked about in the past. But this time we have made sure this is possible (and possible per individual control).

Well what are you waiting for! Go patch that RTL right now and get cracking 🙂

 

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

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

%d bloggers like this: