Recent

Author Topic: TEditAction  (Read 1621 times)

Espectr0

  • Full Member
  • ***
  • Posts: 221
TEditAction
« on: December 31, 2023, 12:40:25 pm »
Hola,
I want to implement a TEdit component, which filters the actions according to its text (from actionlist), and shows said list in a pop-up window (combobox style).
For this I rely on a TEdit, and the pop-up list on a TCustomForm with a TListBox.
But one of the problems I find is that when displaying the form, it gets the focus and I don't want that  :-\

Any help to improve the component?

Thanks!!

Code: Pascal  [Select][+][-]
  1. unit UWEditAction;
  2.  
  3. //------------------------------------------------------------------------------
  4.  
  5. {$mode ObjFPC}{$H+}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, Controls, StdCtrls, Forms, LCLType, SysUtils, Math, ActnList,
  11.   LazarusPackageIntf, LCLProc;
  12.  
  13. type
  14.  
  15.   { TUWEditAction }
  16.  
  17.   TOnSelectActionEvent = procedure(Sender: TObject; const AAction: TAction) of object;
  18.  
  19.   TUWEditAction = class;
  20.  
  21.   TUWEditPopupList = class(TCustomForm)
  22.   private
  23.     FOnSelectActionEvent: TOnSelectActionEvent;
  24.     FEditAction: TUWEditAction;
  25.     FListBox: TListBox;
  26.     procedure ListBoxClick(Sender: TObject);
  27.     procedure DoActionEvent;
  28.     procedure CloseAndReturnAction;
  29.     procedure UpdateText;
  30.   protected
  31.     procedure DoClose(var CloseAction: TCloseAction); override;
  32.   public
  33.     constructor Create(AOwner: TComponent); override;
  34.     destructor Destroy; override;
  35.     procedure MoveSelection(const APrevious: Boolean);
  36.   end;
  37.  
  38.   { TUWEditAction }
  39.  
  40.   TUWEditAction = class(TCustomEdit)
  41.   private
  42.     FActionList : TActionList;
  43.     procedure FillListWithFilter(const AText: String);
  44.   protected
  45.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  46.     procedure Change; override;
  47.   public
  48.     UpdatingFromCode : Boolean;
  49.     constructor Create(AOwner: TComponent); override;
  50.     destructor Destroy; override;
  51.   published
  52.     property ActionList : TActionList read FActionList write FActionList;
  53.  
  54.     property Align;
  55.     property Alignment;
  56.     property Anchors;
  57.     property AutoSelect;
  58.     property BorderSpacing;
  59.     property BorderStyle;
  60.     property CharCase;
  61.     property Color;
  62.     property Constraints;
  63.     property DragCursor;
  64.     property DragKind;
  65.     property DragMode;
  66.     property EchoMode;
  67.     property Enabled;
  68.     property Font;
  69.     property HideSelection;
  70.     property ParentBidiMode;
  71.     property OnChange;
  72.     property OnChangeBounds;
  73.     property OnClick;
  74.     property OnContextPopup;
  75.     property OnDblClick;
  76.     property OnDragDrop;
  77.     property OnDragOver;
  78.     property OnEditingDone;
  79.     property OnEndDrag;
  80.     property OnEnter;
  81.     property OnExit;
  82.     property OnKeyDown;
  83.     property OnKeyPress;
  84.     property OnKeyUp;
  85.     property OnMouseDown;
  86.     property OnMouseEnter;
  87.     property OnMouseLeave;
  88.     property OnMouseMove;
  89.     property OnMouseUp;
  90.     property OnMouseWheel;
  91.     property OnMouseWheelDown;
  92.     property OnMouseWheelUp;
  93.     property OnResize;
  94.     property OnStartDrag;
  95.     property ParentColor;
  96.     property ParentFont;
  97.     property ParentShowHint;
  98.     property ShowHint;
  99.     property TabStop;
  100.     property TabOrder;
  101.     property TextHint;
  102.     property Visible;
  103.   end;
  104.  
  105. //------------------------------------------------------------------------------
  106.  
  107. implementation
  108.  
  109. var
  110.   PopupList: TUWEditPopupList;
  111.  
  112. // -----------------------------------------------------------------------------
  113.  
  114. { TUWEditPopupList }
  115.  
  116. // -----------------------------------------------------------------------------
  117.  
  118. constructor TUWEditPopupList.Create(AOwner: TComponent);
  119. begin
  120.   inherited Create(AOwner);
  121.  
  122.   BorderStyle := bsNone;
  123.   FormStyle := fsStayOnTop;
  124.   ShowInTaskBar := stNever;
  125.   FOnSelectActionEvent := NIL;
  126.   FEditAction := NIL;
  127.   FListBox := TListBox.Create(Self);
  128.   with FListBox do
  129.   begin
  130.     Parent := Self;
  131.     Align := alClient;
  132.     OnClick := @ListBoxClick;
  133.   end;
  134. end;
  135.  
  136. // -----------------------------------------------------------------------------
  137.  
  138. destructor TUWEditPopupList.Destroy;
  139. begin
  140.   FOnSelectActionEvent := NIL;
  141.   FEditAction := NIL;
  142.   FListBox.Free;
  143.  
  144.   inherited Destroy;
  145. end;
  146.  
  147. // -----------------------------------------------------------------------------
  148.  
  149. procedure TUWEditPopupList.DoClose(var CloseAction: TCloseAction);
  150. begin
  151.   CloseAction := caFree;
  152.   PopupList := NIL;
  153.  
  154.   inherited DoClose(CloseAction);
  155. end;
  156.  
  157. // -----------------------------------------------------------------------------
  158.  
  159. procedure TUWEditPopupList.ListBoxClick(Sender: TObject);
  160. begin
  161.   if TListBox(Sender).Items.Count > 0 then
  162.   begin
  163.     UpdateText;
  164.     CloseAndReturnAction;
  165.   end;
  166. end;
  167.  
  168. // -----------------------------------------------------------------------------
  169.  
  170. procedure TUWEditPopupList.DoActionEvent;
  171. begin
  172.   with FEditAction do
  173.     if Assigned(FOnSelectActionEvent) and Assigned(FActionList) and (FListBox.ItemIndex >= 0) then
  174.       FOnSelectActionEvent(Self, TAction(FActionList.Actions[PtrUInt(FListBox.Items.Objects[FListBox.ItemIndex])]));
  175. end;
  176.  
  177. // -----------------------------------------------------------------------------
  178.  
  179. procedure TUWEditPopupList.CloseAndReturnAction;
  180. begin
  181.   DoActionEvent;
  182.   Close;
  183. end;
  184.  
  185. // -----------------------------------------------------------------------------
  186.  
  187. procedure TUWEditPopupList.UpdateText;
  188. begin
  189.   with FEditAction do
  190.     if Assigned(FActionList) then
  191.     begin
  192.       UpdatingFromCode := True;
  193.       Text := TAction(FActionList.Actions[PtrInt(FListBox.Items.Objects[FListBox.ItemIndex])]).Caption;
  194.       UpdatingFromCode := False;
  195.     end;
  196. end;
  197.  
  198. // -----------------------------------------------------------------------------
  199.  
  200. procedure TUWEditPopupList.MoveSelection(const APrevious: Boolean);
  201. var
  202.   i: Integer;
  203. begin
  204.   if FListBox.Items.Count > 0 then
  205.   begin
  206.     i := FListBox.ItemIndex;
  207.     if APrevious then
  208.       Dec(i)
  209.     else
  210.       Inc(i);
  211.  
  212.     i := EnsureRange(i, 0, FListBox.Items.Count-1);
  213.  
  214.     if i <> FListBox.ItemIndex then
  215.     begin
  216.       FListBox.ItemIndex := i;
  217.       UpdateText;
  218.     end;
  219.   end;
  220. end;
  221.  
  222. // -----------------------------------------------------------------------------
  223.  
  224. { TUWEditAction }
  225.  
  226. // -----------------------------------------------------------------------------
  227.  
  228. constructor TUWEditAction.Create(AOwner: TComponent);
  229. begin
  230.   inherited Create(AOwner);
  231.  
  232.   FActionList := NIL;
  233.   PopupList := NIL;
  234.   UpdatingFromCode := False;
  235. end;
  236.  
  237. // -----------------------------------------------------------------------------
  238.  
  239. destructor TUWEditAction.Destroy;
  240. begin
  241.   if Assigned(PopupList) then
  242.     PopupList.Close;
  243.  
  244.   FActionList := NIL;
  245.  
  246.   inherited Destroy;
  247. end;
  248.  
  249. // -----------------------------------------------------------------------------
  250.  
  251. procedure TUWEditAction.KeyDown(var Key: Word; Shift: TShiftState);
  252.  
  253.   procedure UpDown(const AUp: Boolean);
  254.   begin
  255.     if Assigned(PopupList) then PopupList.MoveSelection(AUp);
  256.   end;
  257.  
  258. begin
  259.   case Key of
  260.     VK_UP     : UpDown(True);
  261.     VK_DOWN   : UpDown(False);
  262.     VK_RETURN,
  263.     VK_ESCAPE : if Assigned(PopupList) then PopupList.Close;
  264.   end;
  265. end;
  266.  
  267. // -----------------------------------------------------------------------------
  268.  
  269. procedure TUWEditAction.Change;
  270. begin
  271.   if not UpdatingFromCode then
  272.     FillListWithFilter(Text);
  273. end;
  274.  
  275. // -----------------------------------------------------------------------------
  276.  
  277. procedure TUWEditAction.FillListWithFilter(const AText: String);
  278. var
  279.   i : Integer;
  280.   s : String;
  281.   xy : TPoint;
  282. begin
  283.   if not Assigned(PopupList) then
  284.   begin
  285.     xy := ControlToScreen(Point(0, ClientHeight));
  286.     PopupList := TUWEditPopupList.Create(Application);
  287.     PopupList.FEditAction := Self;
  288.     PopupList.SetBounds(xy.x, xy.y + BorderWidth, Width, Height * 5);
  289.   end;
  290.  
  291.   with PopupList do
  292.   begin
  293.     FListBox.Items.Clear;
  294.     if AText.IsEmpty then
  295.     begin
  296.       PopupList.Close;
  297.       Exit;
  298.     end;
  299.  
  300.     FListBox.Items.BeginUpdate;
  301.     try
  302.       if Assigned(FActionList) then
  303.         for i := 0 to FActionList.ActionCount-1 do
  304.         begin
  305.           s := TAction(FActionList.Actions[i]).Caption;
  306.           if s.ToLower.Contains(AText.ToLower) then
  307.             FListBox.Items.AddObject(s, TObject(PtrInt(i)));
  308.         end;
  309.     finally
  310.       FListBox.Items.EndUpdate;
  311.     end;
  312.  
  313.     if (FListBox.Items.Count = 0) then
  314.       PopupList.Close
  315.     else if not PopupList.Visible then
  316.       PopupList.Visible := True;
  317.   end;
  318. end;
  319.  
  320. // -----------------------------------------------------------------------------
  321.  

wp

  • Hero Member
  • *****
  • Posts: 12864
Re: TEditAction
« Reply #1 on: December 31, 2023, 01:24:33 pm »
A side-note not addressing your real question: as a user of your component I would be confused that your TUWEditAction descends from TCustomEdit, but not from TAction or its ancestors.

Espectr0

  • Full Member
  • ***
  • Posts: 221
Re: TEditAction
« Reply #2 on: December 31, 2023, 02:50:22 pm »
A side-note not addressing your real question: as a user of your component I would be confused that your TUWEditAction descends from TCustomEdit, but not from TAction or its ancestors.

For the name? I didn't know what to name it because it's experimental...
but do you get the idea?

Espectr0

  • Full Member
  • ***
  • Posts: 221
Re: TEditAction
« Reply #3 on: December 31, 2023, 03:26:51 pm »
If I modify the following function, adding that I remember the active form, I achieve the desired effect but there is a minimum of flickering... it can work :-X

Code: Pascal  [Select][+][-]
  1. procedure TUWEditAction.FillListWithFilter(const AText: String);
  2. var
  3.   i : Integer;
  4.   s : String;
  5.   xy : TPoint;
  6.   frm : TForm;
  7. begin
  8.   if not Assigned(PopupList) then
  9.   begin
  10.     xy := ControlToScreen(Point(0, ClientHeight));
  11.     PopupList := TUWEditPopupList.Create(Application);
  12.     PopupList.FEditAction := Self;
  13.     PopupList.SetBounds(xy.x, xy.y + BorderWidth, Width, Height * 5);
  14.     frm := Screen.ActiveForm;
  15.   end;
  16.  
  17.   with PopupList do
  18.   begin
  19.     FListBox.Items.Clear;
  20.     if AText.IsEmpty then
  21.     begin
  22.       PopupList.Close;
  23.       Exit;
  24.     end;
  25.  
  26.     FListBox.Items.BeginUpdate;
  27.     try
  28.       if Assigned(FActionList) then
  29.         for i := 0 to FActionList.ActionCount-1 do
  30.         begin
  31.           s := TAction(FActionList.Actions[i]).Caption;
  32.           if s.ToLower.Contains(AText.ToLower) then
  33.             FListBox.Items.AddObject(s, TObject(PtrInt(i)));
  34.         end;
  35.     finally
  36.       FListBox.Items.EndUpdate;
  37.     end;
  38.  
  39.     if (FListBox.Items.Count = 0) then
  40.       PopupList.Close
  41.     else if not PopupList.Visible then
  42.     begin
  43.       PopupList.Visible := True;
  44.       if frm <> NIL then
  45.         frm.SetFocus;
  46.     end;
  47.   end;
  48. end;
  49.  

jamie

  • Hero Member
  • *****
  • Posts: 6953
Re: TEditAction
« Reply #4 on: December 31, 2023, 04:39:56 pm »
maybe this will help you a little;

Set the form for ShowOnTop so it stays visiable when showing.

and do this when you want to show it from a hidden state.

Code: Pascal  [Select][+][-]
  1.   ShowWindow(Form2.handle,SW_SHOWNOACTIVATE);
  2.  

Form2 being your form in question.

You need to include the LCLIntf and LclType units.

The only true wisdom is knowing you know nothing

Espectr0

  • Full Member
  • ***
  • Posts: 221
Re: TEditAction
« Reply #5 on: December 31, 2023, 07:05:29 pm »
maybe this will help you a little;
Set the form for ShowOnTop so it stays visiable when showing.
and do this when you want to show it from a hidden state.

Code: Pascal  [Select][+][-]
  1.   ShowWindow(Form2.handle,SW_SHOWNOACTIVATE);
  2.  

If I use said procedure to show the form, it shows it but without the listbox, can it be possible?

jamie

  • Hero Member
  • *****
  • Posts: 6953
Re: TEditAction
« Reply #6 on: December 31, 2023, 08:19:07 pm »
maybe this will help you a little;
Set the form for ShowOnTop so it stays visiable when showing.
and do this when you want to show it from a hidden state.

Code: Pascal  [Select][+][-]
  1.   ShowWindow(Form2.handle,SW_SHOWNOACTIVATE);
  2.  

 I would think it should show the listbox.

I did a simple test here dropping a listbox on a form along with an edit etc, it works fine but I used the designer for that.

I noticed you didn't set location and size of the list box while in your constructor of the form.

Also ensure the visible property of the list box is true.

I did the test on Windows, and if you did it on something different and it's failing then may I suggest there is a bug!


If I use said procedure to show the form, it shows it but without the listbox, can it be possible?
The only true wisdom is knowing you know nothing

Espectr0

  • Full Member
  • ***
  • Posts: 221
Re: TEditAction
« Reply #7 on: December 31, 2023, 10:00:22 pm »

 I would think it should show the listbox.
I did a simple test here dropping a listbox on a form along with an edit etc, it works fine but I used the designer for that.
I noticed you didn't set location and size of the list box while in your constructor of the form.
Also ensure the visible property of the list box is true.
I did the test on Windows, and if you did it on something different and it's failing then may I suggest there is a bug!

I actually locate it with "Align := alClient".
The strange thing is that it appears correctly with "Visible := True" but not with "ShowWindow".

Tested with Windows 11 and Lazarus 3.0
« Last Edit: December 31, 2023, 10:57:21 pm by Espectr0 »

jamie

  • Hero Member
  • *****
  • Posts: 6953
Re: TEditAction
« Reply #8 on: January 01, 2024, 01:15:29 am »
Try this.

You will need to reset the focus if you click inside the listbox.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.  Form2.Show;
  4.  SetFocus;
  5.  Form2.Listbox1.ItemIndex := 1;
  6. end;                              
  7.  
  8.  
The only true wisdom is knowing you know nothing

Espectr0

  • Full Member
  • ***
  • Posts: 221
Re: TEditAction
« Reply #9 on: January 01, 2024, 12:38:01 pm »
Try this.

You will need to reset the focus if you click inside the listbox.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.  Form2.Show;
  4.  SetFocus;
  5.  Form2.Listbox1.ItemIndex := 1;
  6. end;                              
  7.  
  8.  

Now I have a doubt why the ListBox does not appear using ShowWindow, anyway for now the solution I have is the one mentioned, I save the active form before showing the other form and return the focus to it.

In case anyone is interested, the code is in: https://github.com/URUWorks/TeroSubtitler/blob/main/TeroSubtitler/controls/UWEditAction.pas

 

TinyPortal © 2005-2018