lcl: don't call Activate, Deactivate for TForm on WM_ACTIVATE message - since it also means activate/deactivate on application activation/deactivation. Use Screen.FocusedForm to track form activation/deactivation. Send CM_ACTIVATE, CM_DEACTIVATE instead of direct event calling. (this is VCL compatible and fixes issue #0015054)

git-svn-id: trunk@25274 -
This commit is contained in:
paul 2010-05-09 13:28:15 +00:00
parent b07cd9b4f5
commit a12d403ae0
3 changed files with 82 additions and 35 deletions

View File

@ -488,6 +488,8 @@ type
procedure CMAppShowMenuGlyphChanged(var Message: TLMessage); message CM_APPSHOWMENUGLYPHCHANGED; procedure CMAppShowMenuGlyphChanged(var Message: TLMessage); message CM_APPSHOWMENUGLYPHCHANGED;
procedure CMIconChanged(var Message: TLMessage); message CM_ICONCHANGED; procedure CMIconChanged(var Message: TLMessage); message CM_ICONCHANGED;
procedure CMRelease(var Message: TLMessage); message CM_RELEASE; procedure CMRelease(var Message: TLMessage); message CM_RELEASE;
procedure CMActivate(var Message: TLMessage); message CM_ACTIVATE;
procedure CMDeactivate(var Message: TLMessage); message CM_DEACTIVATE;
procedure AddHandler(HandlerType: TFormHandlerType; procedure AddHandler(HandlerType: TFormHandlerType;
const Handler: TMethod; AsLast: Boolean); const Handler: TMethod; AsLast: Boolean);
procedure RemoveHandler(HandlerType: TFormHandlerType; procedure RemoveHandler(HandlerType: TFormHandlerType;
@ -941,6 +943,7 @@ type
function GetWidth : Integer; function GetWidth : Integer;
procedure AddForm(AForm: TCustomForm); procedure AddForm(AForm: TCustomForm);
procedure RemoveForm(AForm: TCustomForm); procedure RemoveForm(AForm: TCustomForm);
function SetFocusedForm(AForm: TCustomForm): Boolean;
procedure SetCursor(const AValue: TCursor); procedure SetCursor(const AValue: TCursor);
procedure SetCursors(AIndex: Integer; const AValue: HCURSOR); procedure SetCursors(AIndex: Integer; const AValue: HCURSOR);
procedure SetHintFont(const AValue: TFont); procedure SetHintFont(const AValue: TFont);

View File

@ -483,8 +483,8 @@ begin
if (FormStyle <> fsMDIForm) or (csDesigning in ComponentState) then if (FormStyle <> fsMDIForm) or (csDesigning in ComponentState) then
SetActive(Message.Active); SetActive(Message.Active);
Activate; if Application <> nil then
if Application <> nil then Application.Activate; Application.Activate;
// The button reappears in some situations (e.g. when the window gets the // The button reappears in some situations (e.g. when the window gets the
//"urgency" flag) so we hide it again here. //"urgency" flag) so we hide it again here.
// This is the most important place to invoke UpdateShowInTaskBar, since // This is the most important place to invoke UpdateShowInTaskBar, since
@ -507,12 +507,11 @@ procedure TCustomForm.WMDeactivate(var Message : TLMActivate);
begin begin
FActive:=false; FActive:=false;
{$IFDEF EnableAsyncDeactivate} {$IFDEF EnableAsyncDeactivate}
Deactivate;
if Application<>nil then if Application<>nil then
Application.QueueAsyncCall(@Application.Deactivate,0); Application.QueueAsyncCall(@Application.Deactivate,0);
{$ELSE} {$ELSE}
if Application<>nil then Application.Deactivate; if Application<>nil then
Deactivate; Application.Deactivate;
{$ENDIF} {$ENDIF}
end; end;
@ -741,6 +740,16 @@ begin
Free; Free;
end; end;
procedure TCustomForm.CMActivate(var Message: TLMessage);
begin
Activate;
end;
procedure TCustomForm.CMDeactivate(var Message: TLMessage);
begin
Deactivate;
end;
procedure TCustomForm.AddHandler(HandlerType: TFormHandlerType; procedure TCustomForm.AddHandler(HandlerType: TFormHandlerType;
const Handler: TMethod; AsLast: Boolean); const Handler: TMethod; AsLast: Boolean);
begin begin
@ -2132,17 +2141,46 @@ end;
------------------------------------------------------------------------------} ------------------------------------------------------------------------------}
function TCustomForm.SetFocusedControl(Control: TWinControl): Boolean; function TCustomForm.SetFocusedControl(Control: TWinControl): Boolean;
function NextChildControl(CurParent, Target: TWinControl): TWinControl; inline; function SendEnterExitLoop: Boolean;
function NextChildControl(CurParent, Target: TWinControl): TWinControl; inline;
begin
while Target.Parent <> CurParent do
Target := Target.Parent;
Result := Target;
end;
var
LastState: TFocusState;
begin begin
while Target.Parent <> CurParent do // send cm_exit, cm_enter messages
Target := Target.Parent; // cm_exit must be sent to all controls from lastfocusedcontrol to the first parent which contains control
Result := Target; // 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;
FLastFocusedControl.Perform(CM_EXIT, 0, 0);
if SaveFocusState <> LastState then
Exit(False);
FLastFocusedControl := FLastFocusedControl.Parent;
end;
while FLastFocusedControl <> Control do
begin
FLastFocusedControl := NextChildControl(FLastFocusedControl, Control);
LastState := SaveFocusState;
FLastFocusedControl.Perform(CM_ENTER, 0, 0);
if SaveFocusState <> LastState then
Exit(False);
end;
Result := True;
end; end;
var var
ParentForm: TCustomForm; ParentForm: TCustomForm;
CurControl: TWinControl; CurControl: TWinControl;
LastState: TFocusState;
begin begin
LastFocusedControl := Control; LastFocusedControl := Control;
Result := False; Result := False;
@ -2192,7 +2230,8 @@ begin
begin begin
Control.ControlState := Control.ControlState + [csFocusing]; Control.ControlState := Control.ControlState + [csFocusing];
try try
Screen.FFocusedForm := Self; if not Screen.SetFocusedForm(Self) then
Exit;
// update ActiveControls of all parent forms // update ActiveControls of all parent forms
CurControl := Control.Parent; CurControl := Control.Parent;
while CurControl <> nil do while CurControl <> nil do
@ -2201,33 +2240,10 @@ begin
TCustomForm(CurControl).FActiveControl := Control; TCustomForm(CurControl).FActiveControl := Control;
CurControl := CurControl.Parent; CurControl := CurControl.Parent;
end; end;
Result := SendEnterExitLoop;
// 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;
FLastFocusedControl.Perform(CM_EXIT, 0, 0);
if SaveFocusState <> LastState then
Exit;
FLastFocusedControl := FLastFocusedControl.Parent;
end;
while FLastFocusedControl <> Control do
begin
FLastFocusedControl := NextChildControl(FLastFocusedControl, Control);
LastState := SaveFocusState;
FLastFocusedControl.Perform(CM_ENTER, 0, 0);
if SaveFocusState <> LastState then
Exit;
end;
finally finally
Control.ControlState := Control.ControlState - [csFocusing]; Control.ControlState := Control.ControlState - [csFocusing];
end; end;
Result := True;
end; end;
end; end;

View File

@ -763,6 +763,34 @@ begin
Application.UpdateVisible; Application.UpdateVisible;
end; end;
function TScreen.SetFocusedForm(AForm: TCustomForm): Boolean;
var
LastState: TFocusState;
begin
// result determins if focused state has changed during Activate/Deactivate events
// if so we should return False (since activate/deactivate failed)
Result := True;
if FFocusedForm <> AForm then
begin
// send deactivate to the previosly focused form
LastState := SaveFocusState;
if FFocusedForm <> nil then
FFocusedForm.Perform(CM_DEACTIVATE, 0, 0);
if SaveFocusState <> LastState then
begin
FFocusedForm := nil;
Exit(False);
end;
// send activate to the newly focused form
FFocusedForm := AForm;
LastState := SaveFocusState;
if FFocusedForm <> nil then
FFocusedForm.Perform(CM_ACTIVATE, 0, 0);
if SaveFocusState <> LastState then
Exit(False);
end;
end;
{------------------------------------------------------------------------------ {------------------------------------------------------------------------------
procedure TScreen.SetCursor(const AValue: TCursor); procedure TScreen.SetCursor(const AValue: TCursor);
------------------------------------------------------------------------------} ------------------------------------------------------------------------------}