From 00d38fd701350d2a9d03f9903293fb419dd8b8c5 Mon Sep 17 00:00:00 2001 From: zeljko Date: Sat, 11 Sep 2010 16:11:48 +0000 Subject: [PATCH] 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 - --- lcl/interfaces/qt/qtint.pp | 10 ++++ lcl/interfaces/qt/qtobject.inc | 39 ++++++++++++++++ lcl/interfaces/qt/qtwidgets.pas | 26 ++++++++++- lcl/interfaces/qt/qtwinapi.inc | 82 +++++++++++++++++++++------------ lcl/interfaces/qt/qtwinapih.inc | 2 +- 5 files changed, 127 insertions(+), 32 deletions(-) diff --git a/lcl/interfaces/qt/qtint.pp b/lcl/interfaces/qt/qtint.pp index c718e5e222..f3c8bd4194 100644 --- a/lcl/interfaces/qt/qtint.pp +++ b/lcl/interfaces/qt/qtint.pp @@ -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; diff --git a/lcl/interfaces/qt/qtobject.inc b/lcl/interfaces/qt/qtobject.inc index 8b15b85afb..5b368747cd 100644 --- a/lcl/interfaces/qt/qtobject.inc +++ b/lcl/interfaces/qt/qtobject.inc @@ -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 diff --git a/lcl/interfaces/qt/qtwidgets.pas b/lcl/interfaces/qt/qtwidgets.pas index ee5da3b21d..8a6a9c6775 100644 --- a/lcl/interfaces/qt/qtwidgets.pas +++ b/lcl/interfaces/qt/qtwidgets.pas @@ -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; @@ -3094,7 +3102,10 @@ begin {$ifdef VerboseQt} 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; diff --git a/lcl/interfaces/qt/qtwinapi.inc b/lcl/interfaces/qt/qtwinapi.inc index 90ae6bd86e..444edbf8c9 100644 --- a/lcl/interfaces/qt/qtwinapi.inc +++ b/lcl/interfaces/qt/qtwinapi.inc @@ -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 - exit; + 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)); @@ -5700,44 +5732,34 @@ begin begin if QWidget_parentWidget(Widget) <> nil then begin - Widget := QWidget_parentWidget(Widget); - Result := HWND(QtObjectFromWidgetH(Widget)); + 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 diff --git a/lcl/interfaces/qt/qtwinapih.inc b/lcl/interfaces/qt/qtwinapih.inc index dcf047b16f..0959e1a680 100644 --- a/lcl/interfaces/qt/qtwinapih.inc +++ b/lcl/interfaces/qt/qtwinapih.inc @@ -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