Search code examples
delphivcldelphi-10.1-berlin

TEdit select text on click like the URL bars in Chrome and Firefox do


I want to have a TEdit that reacts on a click like the Url bars in Chrome and Firefox. On first click they select all text and following clicks remove the selection as shown here:

enter image description here

My approach:

// This method is bound to the OnClick event
procedure TForm.edt_SearchClick(Sender: TObject);
begin 
  if edt_Search.SelLength > 0 then
    edt_Search.SelLength := 0
  else
    edt_Search.SelectAll;
end;

This code doesn't work as expected as edt_Search.SelLength is always 0. The selection will always be cleared before the OnClick event is about to be triggered. I've already tried to put this code into the OnMouseUp and OnMouseDown events but the problem stays the same.

How can I solve it? Is there a way to do this without adding an additional boolean variable which saves the current state?


Solution

  • To select all text in a TEdit control when the control gains focus simply handle the OnEnter event and :

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
      PostMessage(Edit1.Handle, EM_SETSEL, 0, -1);
    end;
    

    You cannot use Edit1.SelectAll since default behaviour (which happens after OnEnter) clears any selections in the Edit control. Posting the message ensures that it gets handled after the remaining default behaviour completes.

    To fully emulate the address bar in those browsers, the field also deselects when exiting the control, so in OnExit :

    procedure TForm.Edit1Exit(Sender: TObject);
    begin
      PostMessage(Edit1.Handle, EM_SETSEL, 0, 0);
    end;
    

    The browser field also allows you to select text when first entering, so in this case you need to be a bit more careful. As a hack you can do it with an interposer, but ideally you'd make a custom control :

    type
      TEdit = class(Vcl.StdCtrls.TEdit)
        private
          FDoEnterSelect : boolean;
      end;
    

    and then

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
      Edit1.FDoEnterSelect := true;
    end;
    
    procedure TForm1.Edit1Exit(Sender: TObject);
    begin
      PostMessage(Edit1.Handle, EM_SETSEL, 0, 0);
    end;
    
    procedure TForm1.Edit1MouseUp(Sender: TObject; Button: TMouseButton;
                                  Shift: TShiftState; X, Y: Integer);
    begin
      if Edit1.FDoEnterSelect and
         (Edit1.SelLength = 0) then
           PostMessage(Edit1.Handle, EM_SETSEL, 0, -1);
      Edit1.FDoEnterSelect := false;
    end;
    
    procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    begin
      Edit1.FDoEnterSelect := false;
    end;
    

    The KeyUp handler deals with the case of tabbing to the control. The only remaining odd case is if the edit control has TabOrder of zero and, therefore, is focused when the form is created (and therefore selected). This would affect the first click into the control only.