I'm working on a tagging feature in my FMX application similar to Microsoft Teams.
Here's how it looks in Teams:
Now in my version I also want to add an edit field and other stuff to the popup.
I started off trying to use a TPopup
, but the issue with this is that if you click on a TEdit
inside the popup the popup will close. Due to this code in WndProc
in FMX.Platform.Win
:
WM_ACTIVATE:
begin
if not ((TFmxFormState.Recreating in LForm.FormState) or (LForm.FormStyle = TFormStyle.Popup) or
WindowHandleToPlatform(LForm.Handle).FDisableDeactivate) then
begin
if LoWord(wParam) <> 0 then
begin
if HiWord(wParam) = 0 then
LForm.Activate;
// If the window is minimized, then do nothing.
end
else
begin
PrepareClosePopups;
LForm.Deactivate;
ClosePopupList; // This is called when clicking on edit on popup
end;
end;
Result := 0;
end;
ClosePopupList
is called when clicking on the TEdit
inside the popup and this function closes the popup.
So I switched to using a TForm
and that fixes that issue, but after showing the form with Show
my memo doesn't have focus anymore.
I would like to keep my focus on the memo except when we click on the edit inside the popup, in that case the edit should get focus and the popup should remain open. If I click outside the popup then the popup should close.
Is there a way to fix this?
This should work.
MainFormUnit
var MainForm:TMainForm;
implementation
{$R *.fmx}
uses PopupFormUnit;
procedure TMainForm.FormMouseDown(Sender:TObject; Button:TMouseButton; Shift:TShiftState; X,Y:Single);
begin
//If you click anywhere on the main form that doesn't take focus, this will hide the popup.
PopupForm.Hide;
end;
procedure TMainForm.MemoClick(Sender:TObject);
//My trigger for testing purposes. You do what you need to to trigger the popup
begin
OpenPopup;
end;
procedure TMainForm.MemoExit(Sender:TObject);
begin
//If you click on any other focusable control on the main form, you want the popup to hide.
//See the note about the timer.
PopupTimer.Enabled:=True;
end;
procedure TMainForm.OpenPopup;
begin
PopupForm.Show; //Show the popup
Active:=True; //Return the focus to the main form.
end;
procedure TMainForm.PopupTimerTimer(Sender:TObject);
begin
PopupForm.Hide; //Hide the popup
PopupTimer.Enabled:=False; //And turn the timer off.
end;
end.
PopupFormUnit
NOTE: Set PopupForm.FormStyle
to StayOnTop
.
var
PopupForm: TPopupForm;
implementation
{$R *.fmx}
uses MainFormUnit;
procedure TPopupForm.FormActivate(Sender:TObject);
begin
//We want to keep the popup from showing, so cancel the timer.
MainForm.PopupTimer.Enabled:=False;
end;
end.
Timer
When you click on the popup, it becomes activated, but the main form's memo loses focus first. So if TMainForm.MemoExit
directly hid the popup, the popup would be hidden. So instead, we start a timer which will hide the popup when it's finished. To avoid hiding the popup when we've just clicked on it, we have TPopupForm.FormActivate
cancel the timer, so that in this case only, the popup remains showing. In other cases, the popup closes.
The popup activation happens immediately after the memo loses focus, so you don't need the timer's interval to be very long. I set it to 10 (milliseconds) and that worked okay for me.
I put the timer on the main form, but it should be okay to put it on the popup form, which would avoid the need for the popup form unit to reference the main form unit.