View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0029646 | Lazarus | LCL | public | 2016-02-13 16:01 | 2016-02-25 15:34 |
Reporter | Zoran Vučenović | Assigned To | Bart Broersma | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | closed | Resolution | fixed | ||
OS | Windows | ||||
Product Version | 1.7 (SVN) | ||||
Target Version | 1.8 | Fixed in Version | 1.8 | ||
Summary | 0029646: TCalendar.HitTest method incorectly returns cpDate | ||||
Description | In recent Windows versions (probably since Windows 7), the native windows MontCalendar widget has new look. When the user clicks on month name in title (for example February 2016), then the calendar changes to grid of months (see attached file Capture.PNG). Then, when user clicks on a month, the Calendar.HitTest (http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcustomcalendar.hittest.html) returns cpDate. I think that it should return cpTitleMonth. | ||||
Additional Information | This affects the incorrect behaviour of TDateTimePicker, TDateEdit and TCalendarDialog. All those controls ask Calendar.HitTest(aPoint) in [cpDate, cpNoWhere] and, when they get cpDate, they incorrectly close the calendar when the user hasn't chosen the date. This is reported as TDateTimePicker bug (see issue 29617), but it should be solved in TCalendar for Win32/64 widgetset, so I'm creating this bug report. | ||||
Tags | No tags attached. | ||||
Fixed in Revision | r51694, r51695, r51696 | ||||
LazTarget | 1.8 | ||||
Widgetset | Win32/Win64 | ||||
Attached Files |
|
related to | 0029617 | resolved | Zoran Vučenović | Lazarus | TDateTimePicker – can’t pick date when year is changed |
related to | 0029696 | resolved | Zoran Vučenović | Lazarus CCR | TDatetimePicker and TDBDatetimePicker prematurely leaves calendar after selecting a year |
|
|
|
Testing on Win7-64 with 32-bit fpc. Testing TWin32WSCustomCalendar.HitTest gives rather confusing results: I click on the title (february 2016), this opens the months. I click a month. HitPart can be any of these results: $02020001, $00020000, $01020001, $00020001 (!), $00020003 or $00020002 I cannot see any logic in what it returns. Note that when you click on a date in the calendar HitPart will be $00020001. So, at least in certain cases it is impossible to distinguish between clicking a month and clicking a date. |
|
Win7: MCHT_NOWHERE seems to be $00100000 (commctrl says: $00000000) MCHT_CALENDARBK seems to be $00000000 (commctrl says: $00020000) |
|
Yes, Bart, I see that TWin32WSCustomCalendar.HitTest relies on message MCM_HITTEST, so I tried to search for Microsoft documentation for this and, looking at this page https://msdn.microsoft.com/en-us/library/windows/desktop/bb760991%28v=vs.85%29.aspx , it doesn't seem to have the result which can be used for this special case. |
|
To me some of the descriptions on that page are a bit cryptic. E.g the difference between MCHT_TITLE and MCHT_TITLEMONTH is beyond me. |
|
Another potential ptoblem is that HitTest is called in Click or DoubleClik. This may lead to inconsistent results, since the calendar itself reacts before the Click event is firend, but after MouseDown. E.g. When the calendar is in the state as attached screenshot (Capture.PNG), then when I click in the "right" spot you get these results: CalendarMouseDown *** uHit = 00020001 *** HitPart = 00020001 *** HitPart = MCHT_CALENDARDATE //MouseDown on "Jan" TCustomCalendar.HitTest: Result = cpDate CalendarClick *** uHit = 00020003 *** HitPart = 00020003 *** HitPart = MCHT_CALENDARWEEKNUM //Calendar has changed and mouse is now over weeknumber TCustomCalendar.HitTest: Result = cpWeekNumber The problem here is that user can move the mouse after MouseDown and (in this example) choose to select "Dec" instead of "Jan". So, we cannot rely on HitTest informtion in MouseDown, but neither can we do so in Click. |
|
Bart, if user moves the mouse, that is his responsibility... Let him open the calendar once more and make sure that he doesn't move the mouse this time. :) And, just to mention, in DateTimePicker I actually use MouseUp (as you noticed here: http://bugs.freepascal.org/view.php?id=29617#c89854 ). |
|
> Bart, if user moves the mouse, that is his responsibility... You miss the point (no pun intended). Even without moving the mouse the result of HitTest can differ between MouseDown/Click/MouseUp, simply because the calendar changes in between, and hence the part that is under the mouse can change. See the example in note 0090008. No mouse was moved (used the "click-part" on the mousepad to ensure this). |
|
> simply because the calendar changes in between, Ah, I see now what you mean. |
|
It seems that this cannot be fixed (at least not on our part)? |
|
>It seems that this cannot be fixed (at least not on our part)? Seems so. However, in forum, GetMem says that in Delphi, the calendar does close (see http://forum.lazarus.freepascal.org/index.php/topic,31626.msg202839.html#msg202839). So, if it is possible in Delphi, it might be possible to fix. But I still don't know how. |
|
current_view.diff (4,199 bytes)
Index: lcl/calendar.pp =================================================================== --- lcl/calendar.pp (revision 51675) +++ lcl/calendar.pp (working copy) @@ -55,6 +55,16 @@ cpTitleYear // year value in the title ); + { In Windows since Vista native calendar control has four possible views. + In other widgetsets, as well as in older windows, calendar can only have + standard "month view" - grid with days representing a month. } + TCalendarView = ( + cvMonth, // grid with days in one month + cvYear, // grid with months in one year + cvDecade, // grid with years from one decade + cvCentury // grid with decades of one century + ); + EInvalidDate = class(Exception); { TCustomCalendar } @@ -90,6 +100,7 @@ public constructor Create(AOwner: TComponent); override; function HitTest(APoint: TPoint): TCalendarPart; + function GetCalendarView: TCalendarView; property Date: String read GetDate write SetDate stored False; property DateTime: TDateTime read GetDateTime write SetDateTime; property DisplaySettings: TDisplaySettings read GetDisplaySettings @@ -173,6 +184,14 @@ Result := cpNoWhere; end; +function TCustomCalendar.GetCalendarView: TCalendarView; +begin + if HandleAllocated then + Result := TWSCustomCalendarClass(WidgetSetClass).GetCurrentView(Self) + else + Result := cvMonth; +end; + procedure TCustomCalendar.Loaded; begin inherited Loaded; Index: lcl/interfaces/win32/win32wscalendar.pp =================================================================== --- lcl/interfaces/win32/win32wscalendar.pp (revision 51675) +++ lcl/interfaces/win32/win32wscalendar.pp (working copy) @@ -46,6 +46,7 @@ const AConstraints: TObject): Boolean; override; class function GetDateTime(const ACalendar: TCustomCalendar): TDateTime; override; class function HitTest(const ACalendar: TCustomCalendar; const APoint: TPoint): TCalendarPart; override; + class function GetCurrentView(const ACalendar: TCustomCalendar): TCalendarView; override; class procedure SetDateTime(const ACalendar: TCustomCalendar; const ADateTime: TDateTime); override; class procedure SetDisplaySettings(const ACalendar: TCustomCalendar; const ASettings: TDisplaySettings); override; end; @@ -159,6 +160,26 @@ end; end; +class function TWin32WSCustomCalendar.GetCurrentView( + const ACalendar: TCustomCalendar): TCalendarView; +var + CurrentView: LRESULT; +begin + Result := inherited GetCurrentView(ACalendar); + if WindowsVersion >= wvVista then begin + if not WSCheckHandleAllocated(ACalendar, 'TWin32WSCustomCalendar.GetCurrentView') then + Exit; + + CurrentView := SendMessage(ACalendar.Handle, MCM_GETCURRENTVIEW, 0, 0); + case CurrentView of + MCMV_MONTH: Result := cvMonth; + MCMV_YEAR: Result := cvYear; + MCMV_DECADE: Result := cvDecade; + MCMV_CENTURY: Result := cvCentury; + end; + end; +end; + class procedure TWin32WSCustomCalendar.SetDateTime(const ACalendar: TCustomCalendar; const ADateTime: TDateTime); var ST: SystemTime; Index: lcl/widgetset/wscalendar.pp =================================================================== --- lcl/widgetset/wscalendar.pp (revision 51675) +++ lcl/widgetset/wscalendar.pp (working copy) @@ -49,6 +49,7 @@ published class function GetDateTime(const ACalendar: TCustomCalendar): TDateTime; virtual; class function HitTest(const ACalendar: TCustomCalendar; const APoint: TPoint): TCalendarPart; virtual; + class function GetCurrentView(const ACalendar: TCustomCalendar): TCalendarView; virtual; class procedure SetDateTime(const ACalendar: TCustomCalendar; const ADateTime: TDateTime); virtual; class procedure SetDisplaySettings(const ACalendar: TCustomCalendar; const ADisplaySettings: TDisplaySettings); virtual; @@ -74,6 +75,12 @@ Result := cpNoWhere; end; +class function TWSCustomCalendar.GetCurrentView(const ACalendar: TCustomCalendar + ): TCalendarView; +begin + Result := cvMonth; +end; + class procedure TWSCustomCalendar.SetDateTime(const ACalendar: TCustomCalendar; const ADateTime: TDateTime); begin end; |
|
Yes, there is hope! See: https://msdn.microsoft.com/en-us/library/windows/desktop/bb760955%28v=vs.85%29.aspx Now, based on this message, I added new GetCurrentView function to calendar. Now, we will be able to ask what current view is and, only when it is month view, we ask HitTest to close the calendar. I'm attacihng the patch which adds new enum type TCalendarView and new function TCalendar.GetCurrentView. This function is implemented in win32 interface that, if windows version is Vista or after, then it can return one of four values and in all other cases, as in other widgetsets, it always returns cvDate. I'm attaching the patch (current_view.diff), please test. |
|
@Zoran: applied your patch in r51694. Attempted to fix the issue for TCalendarDialog and TDateEdit in r51695. I did not see a proper solution to fix it in TCustomCalendar, without breaking backwards compatibility. Someone else needs to fix TDateTimePicker. I can't find the correct place to do that. (I end up in some abstract method.) |
|
Bart, I am the maintainer of TDateTimePicker, I will fix it. Then, I'll look into TDateEdit and TCalendarDialog and try to provide tha patch. |
|
I fixed TDateTimePicker (rev. 51696). Please test. @Bart: I couldn't just ask GetCurrentView when I use MouseUp, because of the problem you described in 0090008. As a workaround I ask in LM_MOUSEDOWN and save the value in temporary variable. Seems that, for that same reason, your correction of TDateEdit doesn't work well (when in year view, that is, when grid of months is shown, it still closes). > I can't find the correct place to do that. (I end up in some abstract method.) The correct place is lclcalwrapper.pas. I made this abstraction to provide user the possibility to use some calendar control other then LCL TCalendar - see http://wiki.freepascal.org/DateTimeCtrls_Package#Using_some_custom_calendar_control_for_drop-down |
|
Please close if OK. |
|
Okay. |
Date Modified | Username | Field | Change |
---|---|---|---|
2016-02-13 16:01 | Zoran Vučenović | New Issue | |
2016-02-13 16:01 | Zoran Vučenović | File Added: Capture.PNG | |
2016-02-13 16:02 | Zoran Vučenović | Relationship added | related to 0029617 |
2016-02-13 16:33 | Zoran Vučenović | Additional Information Updated | View Revisions |
2016-02-13 19:55 | Bart Broersma | Note Added: 0089994 | |
2016-02-13 19:56 | Bart Broersma | Note Edited: 0089994 | View Revisions |
2016-02-13 19:56 | Bart Broersma | Note Edited: 0089994 | View Revisions |
2016-02-13 20:03 | Bart Broersma | Note Added: 0089995 | |
2016-02-13 23:59 | Bart Broersma | Note Edited: 0089994 | View Revisions |
2016-02-14 00:01 | Bart Broersma | Note Edited: 0089994 | View Revisions |
2016-02-14 11:54 | Zoran Vučenović | Note Added: 0090005 | |
2016-02-14 12:00 | Zoran Vučenović | Note Edited: 0090005 | View Revisions |
2016-02-14 12:23 | Bart Broersma | Note Added: 0090007 | |
2016-02-14 13:19 | Bart Broersma | Note Added: 0090008 | |
2016-02-14 13:22 | Bart Broersma | Note Edited: 0090008 | View Revisions |
2016-02-14 13:23 | Bart Broersma | Note Edited: 0090008 | View Revisions |
2016-02-14 13:26 | Bart Broersma | Note Edited: 0090008 | View Revisions |
2016-02-14 13:27 | Bart Broersma | Note Edited: 0090008 | View Revisions |
2016-02-14 13:38 | Zoran Vučenović | Note Added: 0090009 | |
2016-02-14 16:25 | Bart Broersma | Note Added: 0090012 | |
2016-02-14 23:57 | Zoran Vučenović | Note Added: 0090027 | |
2016-02-20 16:31 | Bart Broersma | Note Added: 0090166 | |
2016-02-22 00:52 | Zoran Vučenović | Note Added: 0090182 | |
2016-02-22 00:55 | Zoran Vučenović | Relationship added | related to 0029696 |
2016-02-23 00:14 | Zoran Vučenović | File Added: current_view.diff | |
2016-02-23 00:15 | Zoran Vučenović | Note Added: 0090197 | |
2016-02-25 11:03 | Bart Broersma | Note Added: 0090287 | |
2016-02-25 11:06 | Bart Broersma | Note Edited: 0090287 | View Revisions |
2016-02-25 12:08 | Zoran Vučenović | Note Added: 0090292 | |
2016-02-25 14:01 | Zoran Vučenović | Note Added: 0090296 | |
2016-02-25 14:15 | Bart Broersma | Fixed in Revision | => r51694, r51695, r51696 |
2016-02-25 14:15 | Bart Broersma | LazTarget | - => 1.8 |
2016-02-25 14:15 | Bart Broersma | Note Added: 0090297 | |
2016-02-25 14:15 | Bart Broersma | Status | new => resolved |
2016-02-25 14:15 | Bart Broersma | Fixed in Version | => 1.8 |
2016-02-25 14:15 | Bart Broersma | Resolution | open => fixed |
2016-02-25 14:15 | Bart Broersma | Assigned To | => Bart Broersma |
2016-02-25 14:15 | Bart Broersma | Target Version | => 1.8 |
2016-02-25 15:34 | Zoran Vučenović | Note Added: 0090300 | |
2016-02-25 15:34 | Zoran Vučenović | Status | resolved => closed |