mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-07 05:18:00 +02:00
lcl: fix OnEnter/OnExit handling:
- don't assume DoEnter, DoExit are the same as WM_SETFOCUS, WM_KILLFOCUS - Enter must happen for all controls from the current focused control to the new focused control on the form, Exit must happen from the focused control to the first parent of the new focused control. If another focusing event happens during this look we need to stop the loop and return False state for SetFocusedControl method (this is VCL compatible behavior) - handle enter, exit code in SetFocusedControl because this is the only place where form handles focus for child controls - send CM_ENTER, CM_EXIT messages instead of direct call of DoEnter, DoExit methods - reimplement SaveFocusState, RestoreFocusState - save restore the last global focused control (fixes issue #0014041) git-svn-id: trunk@25255 -
This commit is contained in:
parent
9528a09d43
commit
0254875222
@ -1725,7 +1725,9 @@ type
|
||||
procedure CMEnabledChanged(var Message: TLMessage); message CM_ENABLEDCHANGED;
|
||||
procedure CMShowingChanged(var Message: TLMessage); message CM_SHOWINGCHANGED; // called by TWinControl.UpdateShowing
|
||||
procedure CMShowHintChanged(var Message: TLMessage); message CM_SHOWHINTCHANGED;
|
||||
procedure CMVisibleChanged(var TheMessage: TLMessage); message CM_VISIBLECHANGED;
|
||||
procedure CMVisibleChanged(var Message: TLMessage); message CM_VISIBLECHANGED;
|
||||
procedure CMEnter(var Message: TLMessage); message CM_ENTER;
|
||||
procedure CMExit(var Message: TLMessage); message CM_EXIT;
|
||||
procedure WMEraseBkgnd(var Message: TLMEraseBkgnd); message LM_ERASEBKGND;
|
||||
procedure WMNotify(var Message: TLMNotify); message LM_NOTIFY;
|
||||
procedure WMSetFocus(var Message: TLMSetFocus); message LM_SETFOCUS;
|
||||
|
23
lcl/forms.pp
23
lcl/forms.pp
@ -422,6 +422,7 @@ type
|
||||
FKeyPreview: Boolean;
|
||||
FMenu: TMainMenu;
|
||||
FModalResult: TModalResult;
|
||||
FLastFocusedControl: TWinControl;
|
||||
FOldBorderStyle: TFormBorderStyle;
|
||||
FOnActivate: TNotifyEvent;
|
||||
FOnClose: TCloseEvent;
|
||||
@ -1635,9 +1636,9 @@ uses
|
||||
WSForms; // Widgetset uses circle is allowed
|
||||
|
||||
var
|
||||
FocusCount: Integer=0;
|
||||
HandlingException: boolean=False;
|
||||
HaltingProgram: boolean=False;
|
||||
HandlingException: Boolean = False;
|
||||
HaltingProgram: Boolean = False;
|
||||
LastFocusedControl: TWinControl = nil;
|
||||
|
||||
procedure Register;
|
||||
begin
|
||||
@ -1684,28 +1685,16 @@ begin
|
||||
Application.DoBeforeFinalization;
|
||||
end;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// The focus state is just the focus count for now. To save having to allocate
|
||||
// anything, I just map the Integer to the TFocusState.
|
||||
function SaveFocusState: TFocusState;
|
||||
begin
|
||||
Result := TFocusState(PtrInt(FocusCount));
|
||||
Result := LastFocusedControl;
|
||||
end;
|
||||
|
||||
procedure RestoreFocusState(FocusState: TFocusState);
|
||||
begin
|
||||
FocusCount := integer(PtrUInt(FocusState));
|
||||
LastFocusedControl := TWinControl(FocusState);
|
||||
end;
|
||||
|
||||
{function SendFocusMessage(Window: HWnd; Msg: Word): Boolean;
|
||||
var
|
||||
Count: Integer;
|
||||
begin
|
||||
Count := FocusCount;
|
||||
SendMessage(Window, Msg, 0, 0);
|
||||
Result := (FocusCount = Count);
|
||||
end;}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
function KeysToShiftState(Keys: PtrUInt): TShiftState;
|
||||
begin
|
||||
|
@ -1846,6 +1846,7 @@ begin
|
||||
Include(FFormState,fsFirstShow);
|
||||
//DebugLn('[TCustomForm.CreateNew] Class=',Classname);
|
||||
BeginFormUpdate;
|
||||
FLastFocusedControl := Self;
|
||||
FBorderIcons := [biSystemMenu, biMinimize, biMaximize];
|
||||
FDefaultMonitor := dmActiveForm;
|
||||
FPopupMode := pmNone;
|
||||
@ -2130,10 +2131,20 @@ end;
|
||||
Switch focus.
|
||||
------------------------------------------------------------------------------}
|
||||
function TCustomForm.SetFocusedControl(Control: TWinControl): Boolean;
|
||||
|
||||
function NextChildControl(CurParent, Target: TWinControl): TWinControl; inline;
|
||||
begin
|
||||
while Target.Parent <> CurParent do
|
||||
Target := Target.Parent;
|
||||
Result := Target;
|
||||
end;
|
||||
|
||||
var
|
||||
ParentForm: TCustomForm;
|
||||
CurControl: TWinControl;
|
||||
LastState: TFocusState;
|
||||
begin
|
||||
LastFocusedControl := Control;
|
||||
Result := False;
|
||||
if (Control <> nil) and (csDestroying in Control.ComponentState) then Exit;
|
||||
if (csDestroying in ComponentState) or (csDestroyingHandle in ControlState) then
|
||||
@ -2180,17 +2191,42 @@ begin
|
||||
if (Control <> nil) and (not (csFocusing in Control.ControlState)) then
|
||||
begin
|
||||
Control.ControlState := Control.ControlState + [csFocusing];
|
||||
Screen.FFocusedForm := Self;
|
||||
// prevent looping
|
||||
// update ActiveControls of all parent forms
|
||||
CurControl := Control.Parent;
|
||||
while CurControl <> nil do
|
||||
begin
|
||||
if CurControl is TCustomForm then
|
||||
TCustomForm(CurControl).FActiveControl := Control;
|
||||
CurControl := CurControl.Parent;
|
||||
try
|
||||
Screen.FFocusedForm := Self;
|
||||
// update ActiveControls of all parent forms
|
||||
CurControl := Control.Parent;
|
||||
while CurControl <> nil do
|
||||
begin
|
||||
if CurControl is TCustomForm then
|
||||
TCustomForm(CurControl).FActiveControl := Control;
|
||||
CurControl := CurControl.Parent;
|
||||
end;
|
||||
|
||||
// send cm_exit, cm_enter messages
|
||||
// cm_exit must be sent to all controls from lastfocusedcontrol to the first parent which contains control
|
||||
// cm_enter must be sent from the control we stoped up to control
|
||||
// if during this loop something happens with focus (another control or form has aquired it) we need to stop it
|
||||
|
||||
while not FLastFocusedControl.ContainsControl(Control) do
|
||||
begin
|
||||
LastState := SaveFocusState;
|
||||
SendMessage(FLastFocusedControl.Handle, CM_EXIT, 0, 0);
|
||||
if SaveFocusState <> LastState then
|
||||
Exit;
|
||||
FLastFocusedControl := FLastFocusedControl.Parent;
|
||||
end;
|
||||
|
||||
while FLastFocusedControl <> Control do
|
||||
begin
|
||||
FLastFocusedControl := NextChildControl(FLastFocusedControl, Control);
|
||||
LastState := SaveFocusState;
|
||||
SendMessage(FLastFocusedControl.Handle, CM_ENTER, 0, 0);
|
||||
if SaveFocusState <> LastState then
|
||||
Exit;
|
||||
end;
|
||||
finally
|
||||
Control.ControlState := Control.ControlState - [csFocusing];
|
||||
end;
|
||||
Control.ControlState := Control.ControlState - [csFocusing];
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
@ -6330,10 +6330,6 @@ end;
|
||||
procedure TWinControl.WMSetFocus(var Message: TLMSetFocus);
|
||||
begin
|
||||
//DebugLn('TWinControl.WMSetFocus A ',Name,':',ClassName);
|
||||
Assert(False, Format('Trace: %s', [ClassName]));
|
||||
if [csLoading,csDestroying,csDesigning]*ComponentState=[] then begin
|
||||
DoEnter;
|
||||
end;
|
||||
end;
|
||||
|
||||
{------------------------------------------------------------------------------
|
||||
@ -6351,23 +6347,9 @@ begin
|
||||
Assert(False, Format('Trace: %s', [ClassName]));
|
||||
if [csLoading,csDestroying,csDesigning]*ComponentState=[] then
|
||||
begin
|
||||
if Self is TCustomForm then
|
||||
begin
|
||||
if TCustomForm(Self).Active then
|
||||
begin
|
||||
EditingDone;
|
||||
DoExit;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
ParentForm := GetParentForm(Self);
|
||||
if Assigned(ParentForm) and
|
||||
ParentForm.Active then
|
||||
begin
|
||||
EditingDone;
|
||||
DoExit;
|
||||
end;
|
||||
end;
|
||||
ParentForm := GetParentForm(Self);
|
||||
if Assigned(ParentForm) and ParentForm.Active then
|
||||
EditingDone;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -7815,17 +7797,25 @@ end;
|
||||
|
||||
Performs actions when visibility has changed
|
||||
------------------------------------------------------------------------------}
|
||||
procedure TWinControl.CMVisibleChanged(var TheMessage : TLMessage);
|
||||
procedure TWinControl.CMVisibleChanged(var Message : TLMessage);
|
||||
begin
|
||||
if not FVisible and (Parent <> nil)
|
||||
then RemoveFocus(False);
|
||||
if not FVisible and (Parent <> nil) then
|
||||
RemoveFocus(False);
|
||||
|
||||
if not (csDesigning in ComponentState)
|
||||
or (csNoDesignVisible in ControlStyle)
|
||||
then
|
||||
if not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle) then
|
||||
UpdateControlState;
|
||||
end;
|
||||
|
||||
procedure TWinControl.CMEnter(var Message: TLMessage);
|
||||
begin
|
||||
DoEnter;
|
||||
end;
|
||||
|
||||
procedure TWinControl.CMExit(var Message: TLMessage);
|
||||
begin
|
||||
DoExit;
|
||||
end;
|
||||
|
||||
procedure TWinControl.DoSendShowHideToInterface;
|
||||
var
|
||||
NewVisible: Boolean;
|
||||
|
Loading…
Reference in New Issue
Block a user