Qt: added caching of TQtWidgetSet.WindowFromPoint() to avoid unnecesarry calls of very slow QApplication_widgetAt() function. Now TQtWidgetSet.WindowFromPoint() returns same as Win32 WindowFromPoint(), especially in case if mouse isn't moved but widget under it has been disabled or hidden, or new widget is shown above it.

git-svn-id: trunk@27309 -
This commit is contained in:
zeljko 2010-09-11 16:11:48 +00:00
parent 1fc4c3a359
commit 00d38fd701
5 changed files with 127 additions and 32 deletions

View File

@ -57,6 +57,11 @@ type
TQtWidgetSet = Class(TWidgetSet)
private
App: QApplicationH;
// cache for WindowFromPoint
FLastWFPMousePos: TPoint;
FLastWFPResult: HWND;
FEatNextDeactivate: Boolean;
FOverrideCursor: TObject;
SavedDCList: TFPList;
@ -135,6 +140,11 @@ type
procedure RemoveHandle(AHandle: TObject);
function IsValidHandle(AHandle: HWND): Boolean;
// cache for WindowFromPoint to reduce very expensive calls
// of QApplication_widgetAt() inside WindowFromPoint().
function IsWidgetAtCache(AHandle: HWND): Boolean;
procedure InvalidateWidgetAtCache;
// drag image list
function DragImageList_BeginDrag(AImage: QImageH; AHotSpot: TPoint): Boolean;
procedure DragImageList_EndDrag;

View File

@ -52,6 +52,9 @@ end;
------------------------------------------------------------------------------}
constructor TQtWidgetSet.Create;
begin
FLastWFPMousePos := Point(MaxInt, MaxInt);
FLastWFPResult := 0;
inherited Create;
App := QApplication_Create(@argc, argv);
@ -455,6 +458,9 @@ function TQtWidgetSet.EventFilter(Sender: QObjectH; Event: QEventH): Boolean; cd
var
AObject: TQtObject;
W: TQtMainWindow;
R: TRect;
P: TPoint;
Pt: TQtPoint;
begin
Result := False;
case QEvent_type(Event) of
@ -491,6 +497,21 @@ begin
FreeSysColorBrushes(True);
end;
end;
QEventShow,
QEventHide:
begin
// invalidate widgetAt cache if needed
if QObject_isWidgetType(Sender) and IsValidHandle(FLastWFPResult) then
begin
QWidget_geometry(QWidgetH(Sender), @R);
Pt.x := 0;
Pt.y := 0;
QWidget_mapToGlobal(QWidgetH(Sender), @Pt, @Pt);
R := Rect(Pt.X, Pt.Y, Pt.X + (R.Right - R.Left), Pt.Y + (R.Bottom - R.Top));
if PtInRect(R, FLastWFPMousePos) then
InvalidateWidgetAtCache;
end;
end;
LCLQt_Destroy:
begin
AObject := TQtObject(Pointer(QLCLMessageEvent_getWParam(QLCLMessageEventH(Event))));
@ -701,6 +722,24 @@ begin
System.LeaveCriticalsection(CriticalSection);
end;
{Params: HWND
This function is needed by cache used in TQtWidgetSet.WindowFromPoint().
Returns: True if we are cached (FLastWFPResult).
}
function TQtWidgetSet.IsWidgetAtCache(AHandle: HWND): Boolean;
begin
Result := AHandle = FLastWFPResult;
end;
{Params: none
Invalidates TQtWidgetSet.WindowFromPoint() cache (FLastWFPResult).
Returns: nothing
}
procedure TQtWidgetSet.InvalidateWidgetAtCache;
begin
FLastWFPResult := 0;
end;
function TQtWidgetSet.DragImageList_BeginDrag(AImage: QImageH;
AHotSpot: TPoint): Boolean;
var

View File

@ -209,6 +209,7 @@ type
function getLayoutDirection: QtLayoutDirection;
function getVisible: Boolean; virtual;
function getVisibleTo(AWidget: QWidgetH): Boolean; virtual;
function getOwner: TQtWidget;
function getParent: QWidgetH;
function getPos: TQtPoint;
function getFrameSize: TSize;
@ -2046,6 +2047,11 @@ begin
if LCLObject <> nil then
begin
case QEvent_type(Event) of
QEventEnabledChange:
begin
if QtWidgetSet.IsWidgetAtCache(HWND(Self)) then
QtWidgetSet.InvalidateWidgetAtCache;
end;
QEventShow: SlotShow(True);
QEventHide: SlotShow(False);
QEventClose:
@ -2950,6 +2956,8 @@ begin
WriteLn('TQtWidget.SlotMove');
{$endif}
if QtWidgetSet.IsWidgetAtCache(HWND(Self)) then
QtWidgetSet.InvalidateWidgetAtCache;
// do not loop with LCL
if InUpdate then
exit;
@ -3095,6 +3103,9 @@ begin
WriteLn('TQtWidget.SlotResize');
{$endif}
if QtWidgetSet.IsWidgetAtCache(HWND(Self)) then
QtWidgetSet.InvalidateWidgetAtCache;
// return size w/o frame
NewSize := QResizeEvent_size(QResizeEventH(Event))^;
{
@ -3379,6 +3390,8 @@ end;
function TQtWidget.getEnabled: Boolean;
begin
if Widget = nil then
exit(False);
Result := QWidget_isEnabled(Widget);
end;
@ -3409,14 +3422,23 @@ end;
function TQtWidget.getVisible: boolean;
begin
if Widget = nil then
exit(False);
Result := QWidget_isVisible(Widget);
end;
function TQtWidget.getVisibleTo(AWidget: QWidgetH): Boolean;
begin
if Widget = nil then
exit(False);
Result := QWidget_isVisibleTo(Widget, AWidget);
end;
function TQtWidget.getOwner: TQtWidget;
begin
Result := FOwner;
end;
function TQtWidget.getParent: QWidgetH;
begin
Result := QWidget_parentWidget(Widget);
@ -10539,6 +10561,8 @@ end;
function TQtMenu.getVisible: Boolean;
begin
if ActionHandle = nil then
exit(False);
Result := QAction_isVisible(ActionHandle);
end;

View File

@ -5677,21 +5677,53 @@ end;
If the point is over a static text control,
the return value is a handle to the window under the static text control.
------------------------------------------------------------------------------}
function TQtWidgetSet.WindowFromPoint(Point: TPoint): HWND;
function TQtWidgetSet.WindowFromPoint(APoint: TPoint): HWND;
var
Widget: QWidgetH;
AForm: TCustomForm;
begin
{$note QApplication_widgetAt() is very expensive operation,
we must do some kind of caching here like gtk2 does.}
// we use cachedresults instead of calling very expensive widgetAt
if (FLastWFPResult <> 0) then
begin
if not IsValidHandle(FLastWFPResult) then
FLastWFPResult := 0
else
if (APoint.X = FLastWFPMousePos.X) and (APoint.Y = FLastWFPMousePos.Y) and
TQtWidget(FLastWFPResult).getVisible and
TQtWidget(FLastWFPResult).getEnabled then
begin
// return from cache
exit(FLastWFPResult);
end;
end;
Result := 0;
Widget := QApplication_widgetAt(Point.x, Point.y);
Widget := QApplication_widgetAt(APoint.x, APoint.y);
if (Widget = nil) then
begin
if (APoint.X = FLastWFPMousePos.X) and (APoint.Y = FLastWFPMousePos.Y) then
begin
FLastWFPMousePos := Point(MaxInt, MaxInt);
FLastWFPResult := 0;
end;
exit;
end;
// according to MSDN disabled widget shouldn't be in result
// but win32 returns first enabled and visible parent !
if not QWidget_isEnabled(Widget) or not QWidget_isVisible(Widget) then
begin
while Widget <> nil do
begin
Widget := QWidget_parentWidget(Widget);
if (Widget <> nil) and QWidget_IsVisible(Widget) and
QWidget_isEnabled(Widget) then
break;
end;
if Widget = nil then
exit;
end;
Result := HWND(QtObjectFromWidgetH(Widget));
@ -5699,45 +5731,35 @@ begin
if (Result = 0) then
begin
if QWidget_parentWidget(Widget) <> nil then
begin
while (Widget <> nil) do
begin
Widget := QWidget_parentWidget(Widget);
if Widget <> nil then
Result := HWND(QtObjectFromWidgetH(Widget));
if Result <> 0 then
break;
end;
end;
end;
if (Result <> 0) and
not (TQtWidget(Result) is TQtMainWindow) then
begin
// just in case we are an orphan
if TQtWidget(Result).LCLObject = nil then
begin
while Widget <> nil do
begin
Widget := QWidget_parentWidget(Widget);
if Widget <> nil then
begin
Result := HWND(QtObjectFromWidgetH(Widget));
if (Result <> 0) and (TQtWidget(Result).LCLObject <> nil) then
break;
end;
end;
end;
//if we are TQtViewPort -> return correct handle -> TQtCustomControl !
if (Result <> 0) and (TQtWidget(Result) is TQtViewPort) then
begin
Widget := QWidget_parentWidget(TQtWidget(Result).Widget);
Result := HWND(QtObjectFromWidgetH(Widget));
end;
if TQtWidget(Result).getOwner <> nil then
Result := HWND(TQtWidget(Result).getOwner);
end else
begin
Widget := QApplication_topLevelAt(Point.x, Point.y);
Widget := QApplication_topLevelAt(APoint.x, APoint.y);
if (Widget <> nil) and QWidget_isEnabled(Widget) then
Result := HWND(QtObjectFromWidgetH(Widget))
else
Result := 0;
end;
// add to cache
FLastWFPResult := Result;
FLastWFPMousePos := APoint;
end;
//##apiwiz##eps## // Do not remove, no wizard declaration after this line

View File

@ -215,7 +215,7 @@ function SystemParametersInfo(uiAction: DWord; uiParam: DWord; pvParam: Pointer;
function TextOut(DC: HDC; X,Y : Integer; Str : Pchar; Count: Integer) : Boolean; override;
function UpdateWindow(Handle: HWND): Boolean; override;
function WindowFromPoint(Point: TPoint): HWND; override;
function WindowFromPoint(APoint: TPoint): HWND; override;
//##apiwiz##eps## // Do not remove, no wizard declaration after this line