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 CMIconChanged(var Message: TLMessage); message CM_ICONCHANGED;
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;
const Handler: TMethod; AsLast: Boolean);
procedure RemoveHandler(HandlerType: TFormHandlerType;
@ -941,6 +943,7 @@ type
function GetWidth : Integer;
procedure AddForm(AForm: TCustomForm);
procedure RemoveForm(AForm: TCustomForm);
function SetFocusedForm(AForm: TCustomForm): Boolean;
procedure SetCursor(const AValue: TCursor);
procedure SetCursors(AIndex: Integer; const AValue: HCURSOR);
procedure SetHintFont(const AValue: TFont);

View File

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

View File

@ -763,6 +763,34 @@ begin
Application.UpdateVisible;
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);
------------------------------------------------------------------------------}