Paint problem in AC 15.14

Viewing 20 posts - 1 through 20 (of 22 total)
  • Author
    Posts
  • #69327
    HeDiBo
    Participant

    Here are two gifs, showing a horrible painting problem in AC 15.14.
    The problem manifests itself when on a TsDBNavigator the “Last Record” button is clicked.
    The first GIF shows what happens when the button is clicked and the mouse is not moved.
    The second GIF shows the same thing, but now the mouse is moved away from the “Last Record” button.
    Watch and weep.

    Attachments:
    You must be logged in to view attached files.
    #69343
    Support
    Keymaster

    What happens after a button clicking? Some controls are resized?

    #69349
    HeDiBo
    Participant

    A whole panel with buttons appears at the bottom causing all other controls to move up or resize.

    There are more ways to make this panel appear. In the navigator I could press the Next Record button. If that would bring me to the last record also, the panel with buttons would appear also.

    The strange thing is, that only pressing the Last Record button produces the effect shown. I’m going to make a trace, to see what the difference between these two is.

    #69350
    HeDiBo
    Participant

    If I set the Active property of the SkinManager to False, the problem disappears. So, it’s definitely an AC problem.

    #69353
    HeDiBo
    Participant

    This is a very nasty problem to debug. As soon as I type Alt-Tab to Pause the running program in the IDE it has already disappeared.
    It happens in all windows configurations (Win 32 / 64 with or without debugging).

    #69354
    HeDiBo
    Participant

    Is there any way to start a log in code at a certain moment, to show the Windows messages being sent? It should not involve any user action: that would cancel the problem immediately.

    #69355
    HeDiBo
    Participant

    I managed to capture a list of Windows Messages starting right after the bottom panel was made visible.
    Remarkable are the countless WM_USER+41216 (0xA100) messages. There are two sets of them marked in the file.

    Attachments:
    You must be logged in to view attached files.
    #69357
    HeDiBo
    Participant

    Thanks to Spy++ from the MS Visual Studio package, I was able to generate another trace, that shows quite clearly where the problem is.
    There is an endless repetition of

    <002618> 001F0650 S WM_SETCURSOR hwnd:007C0BA4 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002619> 001F0650 R WM_SETCURSOR fHaltProcessing:False

    which, I think, is an event that is not shown as completed. Therefore it repeats itself.

    • This reply was modified 4 years, 2 months ago by HeDiBo.
    Attachments:
    You must be logged in to view attached files.
    #69362
    HeDiBo
    Participant

    Because clicking the Next Record button to reach the last record did not produce this bug, I added a TDBNavigator to the frame. It shares the OnClick event with the TsDBNavigator. Clicking the Last Record button on that navigator does not produce the error 😲

    I already had the suspicion that it was the Last Record click itself that caused it. The Spy++ trace also goes in that direction.

    Then I added a TsDBNavigator in stead. And now the problem does not occur. Which means it must be related to the specific place of the original TsDBNavigator. Notice that the original TsDBNavigator moves up to make room for the panel made visible. The difference between the Next Record and Last Record buttons is: the specific Hint shown!! And indeed, when I’m quick in clicking the Last Record, before the Hint would be shown, the problem does not occur.

    What then is the significant difference between Next and Last record (apart from the slightly different text of their hints)?

    If the navigator moves up to make room for the panel made visible, the buttons in the panel are shown each with their own hints. And sure enough, if I disable the hint for the button below the Last Record button, the problem goes away. But why does it go OK on the Next Record button? The hint that appears on the button below the Next Record button is a short one. The one below the Last Record button is a longer two line hint.

    Bringing us to the cause of the problem: timing!! If the hint which appears immediately below the navigator button is long enough to cause a bit of time to paint, the click handling of the navigator button goes haywire.

    It’s ironic to conclude that the bug of showing the hint without any pause to speak of, causes this bug (it’s still a bug) to appear.

    • This reply was modified 4 years, 2 months ago by HeDiBo.
    #69364
    HeDiBo
    Participant

    If HandleDisabledCtrls is False, the problem disappears. That’s remarkable, because the button that comes into view is a TsSpeedButton, which will show the hint also if disabled.
    That narrows the problem to AC’s specific code for hints on disabled controls.

    #69379
    Support
    Keymaster

    Are you sure that you can’t give me a demo with such behaviour?
    I think, you know better how to repeat it..

    #69384
    HeDiBo
    Participant

    Since it is clearly a timing problem, even if I could reproduce this very complicated setup as a demo project, it would probably not repeat itself in your environment. It may even be unrepeatable in my own environment.

    The endless repetition of WM_SETCURSOR in the SPY++ trace (they come during the idle time, when waiting for the controls to complete painting) means that a mouse message is not handled. This is the explanation of Microsoft about the WM_SETCURSOR message:

    The DefWindowProc function passes the WM_SETCURSOR message to a parent window before processing. If the parent window returns TRUE, further processing is halted. Passing the message to a window’s parent window gives the parent window control over the cursor’s setting in a child window. The DefWindowProc function also uses this message to set the cursor to an arrow if it is not in the client area, or to the registered class cursor if it is in the client area. If the low-order word of the lParam parameter is HTERROR and the high-order word of lParam specifies that one of the mouse buttons is pressed, DefWindowProc calls the MessageBeep function.

    As you can see: if not returning TRUE, the message keeps repeating.

    #69385
    HeDiBo
    Participant

    I found a way to prevent the bug. The bug stays I think, but it is not visible anymore.

    In acAlphaHints is a procedure TacCustomHintWindow.ActivateHint. On roughly 1/3 of its code, I found:

        Manager.KillTimer;
    //    Application.ProcessMessages; // Patch for refresh of columns in cxGrid
        if Manager.Active then
          Text := AHint
        else
          Caption := AHint;

    If I remove the comment (execute Application.ProcessMessages) the problem goes away. However it introduces a possible Access Violation on the Text := AHint statement. I could not repeat the error, because Application.ProcessMessages is very timing dependent. Also it’s an extremely dangerous statement, making it possible to send messages totally out of context.

    My problem indeed has a cxGrid, that is repopulated and resized during the panel visibility change. This find may allow you to narrow down the cause (I could imagine it is in acLFPainter).

    #69386
    HeDiBo
    Participant

    Changing the code to:

        Manager.KillTimer;
        if Manager.Active then
          Text := AHint
        else
          Caption := AHint;
    (****** DB ********)
        Application.ProcessMessages; // Patch for refresh of columns in cxGrid
    (******************)

    seems to circumvent the AV.
    But as I said, Application.ProcessMessages may cause all kind of problems.

    #69402
    Support
    Keymaster

    Do you know which control doesn’t handle the WM_SETCURSOR message?

    Really, using of the Application.ProcessMessages is dongerous in some situations, it’s a reason why this line has been commented. I need to repeat the issue somehow..

    #69416
    HeDiBo
    Participant

    I wasn’t saying that Application.ProcessMessages should be done. I only enabled the statement at the right place (the commented one was in the wrong place) to find out what it was for. And to my surprise the problem disappeared.
    So, your comment (Patch for refresh of columns in cxGrid) was the one that made me think that the presence of a cxGrid on this Frame caused the problem. Can you recollect why you made this comment?
    The WM_SETCURSOR message is normally left unhandled. There must be another reason why it repeats.

    The SPY++ trace has this at the end of the endless WM_SETCURSOR messages:

    <002801> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002802> 001F0650 S WM_SETCURSOR hwnd:002309EC nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002803> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002804> 001F0650 S WM_SETCURSOR hwnd:002309EC nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002801> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002802> 001F0650 S WM_SETCURSOR hwnd:002309EC nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002803> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002804> 001F0650 S WM_SETCURSOR hwnd:002309EC nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002805> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002806> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002807> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002808> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002809> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002810> 001F0650 S WM_SETCURSOR hwnd:000E0848 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002811> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002812> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002813> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002814> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002815> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002816> 001F0650 S WM_SETCURSOR hwnd:000E0848 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002817> 001F0650 R WM_SETCURSOR fHaltProcessing:False
    <002818> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002819> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002820> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002821> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002822> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002823> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002824> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002825> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002826> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002827> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002828> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002829> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002830> 001F0650 S message:0xA100 [User-defined:WM_APP+8448] wParam:000A0000 lParam:00000000
    <002831> 001F0650 R message:0xA100 [User-defined:WM_APP+8448] lResult:00000000
    <002832> 001F0650 S WM_SETCURSOR hwnd:000E0848 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
    <002833> 001F0650 R WM_SETCURSOR fHaltProcessing:False

    It must be a non-windows message. Do you recognize the WM_APP+8448 message ID?

    The TcxContainer class contains this:

    procedure TcxContainer.WMSetCursor(var Message: TWMSetCursor);
    begin
      with Message do
        if HasInnerControl and (CursorWnd = Handle) and
          (Smallint(HitTest) = HTCLIENT) and not PtInRect(ViewInfo.ClientRect, GetMouseCursorClientPos) then
        begin
          Windows.SetCursor(Screen.Cursors[crArrow]);
          Result := 1;
          Exit;
        end;
      inherited;
    end;

    That looks as a potential candidate. TcxContainer is the base class of TcxCustomEdit and every cell in a grid is essentially an edit field. The scroll may cause the grid cells to shift, making the test not PtInRect(ViewInfo.ClientRect permanently True. But it may also be my wild imagination.

    #69417
    HeDiBo
    Participant

    Maybe you noticed also, that the clicking of the Last Record button inserts a new column at the left hand side of the grid. I’m going to test if that’s what makes the bug surface.
    But don’t forget: if the Next Record button lands on the Last Record, the same operations take place. But the bug does not.
    And indeed, if I keep the first column visible, the bug does not surface.

    #69434
    HeDiBo
    Participant

    No change in AC 15.15 😒
    Why did you put the comment “Patch for refresh of columns in cxGrid” in the source? That’s exactly what happens here.

    • This reply was modified 4 years, 2 months ago by HeDiBo.
    #69501
    Support
    Keymaster

    I will uncomment this line soon.

    #69516
    HeDiBo
    Participant

    That is inadvisable. Application.ProcessMessages may have problematic side effects.
    There may be a safer place a bit more down in the code.
    Do you remember why you put it there? What problem with columns in CxGrid was it supposed to cure?

Viewing 20 posts - 1 through 20 (of 22 total)
  • You must be logged in to reply to this topic.