[Lazarus] Cursor handling on Windows
Vladimir Zhirov
vvzh.lists at gmail.com
Fri Aug 14 11:54:23 CEST 2009
Hi,
Martin and I started a discussion in the bugtracker
and decided to move it here so more people can participate.
The bug report (http://bugs.freepascal.org/view.php?id=14336)
is related to cursor handling on Windows.
To sum up, the bug is resolved, but we are still looking for
more reliable means to set cursors in general and default cursor
in particular.
Martin Friebe wrote:
> There are 2 issues:
> 1) For SetCursor it is already documented on msdn that
> it does nothing, if the value is already set.
> Who is to say that future Window versions will check
> the position first, and ignore if it hasn't changed?
> It should work using "CallWindowProc "
CallWindowProc would require us to pass WParam an LParam values
for WM_SETCURSOR message, and documentation about them seems
a bit vague to me. I would not rely on default WindowProc behaviour
unless I know what exactly must be passed in WParam and LParam.
If I don't know it I would rather use SetCursorPos because at least
it does not have documented restrictions like SetCursor has.
SetCursorPos behavior can change later but for now it is predictable
and clearly documented in contrast to WM_SETCURSOR.
That's my thoughts on "CallWindowProc vs SetCursorPos".
The problem is, as you stated, both CallWindowProc and SetCursorPos
will cause WindowProc execution, that is more expensive than SetCursor
preceded by some calculations.
So, let me summarize your proposed changes for the future
as I understand them and add my comments.
1) Remove cursor handling from MouseMove (at least in the cross widget
part of the LCL)
I agree with this suggestion. AFAIU the first place to look
at is TControl.WMMouseMove.
2) Add new LCL message talking controls to update their cursor
(like WM_SETCURSOR on Windows) and ensure all WidgetSets use
this message.
I'm not sure I understand you right here, but missing LM_SETMOUSE
message led me to conclusion that you suggest what I stated above.
If I get it right, it is hard to comment for me because I'm almost
completely clueless about GTK and QT APIs so I cannot say if it worth
the effort to make such change for all widgetsets.
3) Decide (or find out) if Control.SetCursor should trigger this message
to update a cursor.
This is a controversial question, because triggering this message
would involve costly message processing, but allow to handle cursors
from one place. So it seems to evaluate to "execution speed vs code clarity"
and the problem is to estimate differences.
I have not done any measurements but both
"CalculateBounds[+GetClassCursor]+SetCursor" (acting independently) and
"GetCursorPos+SetCursorPos" (causing costly message processing)
seems to work fast enough for me not to notice the difference.
So maybe it is a matter of taste. It would be interesting to know
what other think.
4) Optimize the current handling in WindowProc
See (3).
5) Decide if passing on handling to parent WinControl is wanted if
"control.Cursor := crDefault" is done
To conitue "Intended behaviour of crDefault cursor..." thread,
here is what I think.
a. We usually want to set a specific cursor for single control
like Button, not for container like Panel
b. It means containers usually have Cursor = crDefault,
so it makes no difference whether cursor handling
is passed to Parent or not.
c. If we set custom cursor for container like Panel what
do we most likely mean by it? Users coming from Delphi
would expect Delphi behaviour that is setting it for
Panel only. If there is a Button on the Panel and
Button.Cursor = crDefault then it is system default
cursor for buttons.
<IMHO>
So it seems crDefault in Delphi means
"System default cursor for this kind of control".
Screen.Cursor is an exception and crDefault here means
"Do not override cursor settings"
</IMHO>
d. Sometimes it is useful to make child controls inherit
cursor settings from parent. This helps to set e.g.
crHourGlass for disabled panel with all child
controls and then restore original cursor.
In order to keep Delphi compatibility we can introduce
crParent cursor and pass cursor handling to parent
control if this cursor is set for child control.
In this case crParent could still fallback to crDefault
if the control has no parent.
More information about the Lazarus
mailing list