Paint events don't always refresh on Catalina

I did a quick search, but Feedback provided nothing.

Have any of you run into issue where Canvas Paint events execute, but the canvas contents do not update in the UI?

I have a container that acts as a custom tab bar for an app. I draw the tab border around the inside of the clicked canvas to indicate which tab is selected along with changing the label text color. On Windows, Linux, and other macOS versions, this works as expected. On Catalina, however, the text color of the label changes, but the DrawRoundRect call result does not update in the GUI. All of the code runs in the main loop.

I’ve tried adding a timer calling Canvas.Invaildate just to try to force a refresh with no difference.

Logic specifics:

  • TGToolBar is a Container
  • The container has up to 8 “tab” canvas controls
  • Each tab canvas control has a Label
  • The Canvases are part of a control array
  • The tabs are used to select a page on a PagePanel (the TGToolBar is NOT a child of the PagePanel)
  • The MouseDown event for the control array resets the background (which also doesn’t take affect) and assigns a variable “tbIndex”
  • The Paint event then uses the tbIndex value to either clear the background (thus removing the previous outline) and call DrawRoundedRect to draw the outline on the clicked tab.

As I mentioned, this has worked as expected for a very long time on Windows XP - 10 and maltose Server version, Linux, and all previous versions os OS X / macOS.

Any thoughts?

Here’s what it SHOULD look like:

Here’s the MouseDown event code:

[code]// check for the user clicking the button they are on
If index = WMain.ppMainPage.Value Or index = WMain.ppMainPage.Value + 6 Then
Return True
End If

// Reset the text to unselected
cvToolButton(0).Backdrop = Backup32x32
lBackup.TextColor = &c2F2F2F00
If Not LoadingArchives Then
cvToolButton(1).Backdrop = Restore32x32
lRestore.TextColor = &c2F2F2F00
cvToolButton(2).Backdrop = Verify32x32
lVerify.TextColor = &c2F2F2F00
End If
cvToolButton(3).Backdrop = DataManager32x32
lDataManager.TextColor = &c2F2F2F00
cvToolButton(4).Backdrop = Tools32x32
lTools.TextColor = &c2F2F2F00

// Set the text to the active color
// The low half is the icon canvas, the higher half is the main canvas for the tab
Select Case Index
Case 0, 6
lBackup.TextColor = &c3249B500
WMain.ppMainPage.Value = 0
tbIndex = 6
Case 1, 7
If Not LoadingArchives Then
lRestore.TextColor = &c3249B500
WMain.ppMainPage.Value = 1
tbIndex = 7
End If
Case 2, 8
If Not LoadingArchives Then
lVerify.TextColor = &c3249B500
WMain.ppMainPage.Value = 2
tbIndex = 8
End If
Case 3, 9
lDataManager.TextColor = &c3249B500
WMain.ppMainPage.Value = 3
tbIndex = 9
Case 4, 10
lTools.TextColor = &c3249B500
WMain.ppMainPage.Value = 4
tbIndex = 10
End Select
Return True[/code]
Here’s the Paint code:

// draw the faux frame around the tab in the parent cannvas If index = tbIndex Then #If TargetMacOS // Clear the previous outline g.ForeColor = &cE7E7E700 g.FillRoundRect(0, 0, g.Width, g.Height + 11 , 10, 10) #endif g.PenWidth = .5 g.PenHeight = .5 g.ForeColor = &c7B7B7E00 g.DrawRoundRect(0,0,g.Width, g.Height + 11, 10, 10) ElseIf index > 5 Then g.ForeColor = &cE7E7E700 g.FillRect(0, 0, g.Width, g.Height + 3) End If

Also, this applies to apps built with 19r1.1, 19r2.1, and 19r3,1,

I suppose you have verified with a system.debuglog() that the paint event actually fires ?

Yes - and by adding a breakpoint in the paint event.

Xojo 2018r3 has updated the macOS SDK (in order to support DarkMode).
All I remember is that this had some effects when it comes to drawing. And somehow a different behavior of ClearRect has come to mind.

In our code I’ve found this in a “similar situation”:

#if (XojoVersion >= 2018.03) and TargetMacOS then 'it seems with this newer Xojo Version we need to clear first g.ClearRect(0, 0, g.Width, g.Height) #endif

Again just a situation I think to remember… that in some places we had to switch from .Invalidate (which we’ve used a lot in the windows-flickering time) back to .Refresh, because otherwise the ReDrawing would not occur (or much later than expected).

Oh, only do that when RemoteDebugging.
Otherwise your app gets (De)Activated when swiching to Xojo/Debugger. And that will cause different Paint-Events obviously for the entire app, compared to when it’s staying “frontmost” the whole time.
Use DebugLog in such a situation, or some other means (filling a property, writing to a file, whatever).

I don’t know if this pieces I think to remember will help, or if they do apply to your app, too…
In your situation I’d try to see what happens if you do a .ClearRect in the appropriate Paint-Events before drawing.

Try to use the paint event and not a backdrop.

Tried that, and then the icon didn’t get drawn, either …

As usual: you must be doing something wrong. You got an example? Why are you using multiple canvases and not one? Today I uploaded an updated version of some old code that does something similar to what you do. Can you try to run the code? http://www.mothsoftware.com/downloads/tabbar.zip

I don’t see where you are .Invalidate-ing or .Refresh-ing in your “MouseDown event code”.
But I’m sure you’ve added that when trying to avoid .BackDrop. :wink:

And just another thing that has come to mind when reading "MouseDown event code" containing WMain.ppMainPage.Value = x
…I don’t expect and don’t want a “(Toolbar)Button” to execute an “Action” in MouseDown!
MouseDown should indicate it’s “in a pushed state” (while the mouse is “inside”), the Action only gets executed in MouseUp (if it’s still “inside”). So one can click on a Button, keep the Mouse Button pushed down, decide otherwise by moving out of the Button, then release the Mouse Button - with no action being executed.

So if you’re going to refactor this anyway, then please keep that in mind, too…

[quote=474881:@Jürg Otter]And just another thing that has come to mind when reading “MouseDown event code” containing WMain.ppMainPage.Value = x…
…I don’t expect and don’t want a “(Toolbar)Button” to execute an “Action” in MouseDown!
[/quote]
That was a shift to try to reduce the time between the click and the paint events. In my real code, I have a method that does what I show above in the MouseDown that is actually called in the MouseUp Named “UpdateTabGraphics”.

I have tried invalidate, refresh, and I even went so far as to place a DoEvents call in as a last resort. Again, the code works properly on all other platforms (I’ve even now tested on a Raspberry Pi).

Hi Trixi, that does work and I’m comparing your workflow with mine. I will report back.

There are several things I can think of.

  1. You have a declare or plugin that it modifying how the window paints, the most common way is some UI API enables Layer-Backed views, which you’ll need to ask the CALayer to update. It was a huge problem for me back in the days of 10.10, but I don’t see it so much now.
  2. You may be getting a NSException somewhere in your code, recent macOS versions, simply stop the current thread and swallow the execution. One of the worst changes that Apple have made in recent years, sure the application doesn’t crash, but no-one knows that there’s a problem except some things simply don’t work right.

I would recommend everyone who uses declares or plugins to follow the advice on this page. https://forum.xojo.com/58302-crash-an-application-on-nsexception/p1#p473604

Then when an NSException is raised, you a) know about it, b) Know what it is and c) know where it is.

Thank you Sam! This is exactly what I’m witnessing on 3 other apps under Catalina. However, I wrote a very quick Exception Handler Window with that setting applied and now I’m getting the exception backtrace that was not being seen by the apps previous. I ran a quick test and the Catalina run of this specific app is not caught up in that idiocy.

Glad I could help.

If you’re on twitter; make sure you hit up @steipete and let him know it helped. I wouldn’t have found this tip without him. I’d started trying to get into the depths of handling NSExceptions myself, simply so that I was aware when, where and what was happening.

Oh well, it was worth a shot, was hoping it might give some indicators.

I’m refactoring taking into account Trixi’s class and another version of this same code that doesn’t exhibit the issue on Catalina.

It’s just very odd that the problem doesn’t manifest on any platform BUT Catalina.

Interesting that you’re actually doing the same thing that I’m doing, just you’ve created a class that is reusable and mine is fixed and dependent on the number of tabs I physically code.

As I mentioned to Sam above, I’ve also tried drawing my icons in the primary canvas, but in that case, I then lose the icon, as well.

However, in extending what I was writing to System.Log in the Paint event and my MouseUp event, I see something very odd - the index in the Paint event when called AFTER the click is always 0 instead of the Canvas clicked:

1:09:16 PM : ccMainTools.cvToolButton.MouseUp: Index is 1, tbIndex is 1
             ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 1
1:09:18 PM : ccMainTools.cvToolButton.MouseUp: Index is 2, tbIndex is 2
             ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 2
1:09:19 PM : ccMainTools.cvToolButton.MouseUp: Index is 3, tbIndex is 3
             ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 3
1:09:20 PM : ccMainTools.cvToolButton.MouseUp: Index is 4, tbIndex is 4
             ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 4
1:09:21 PM : ccMainTools.cvToolButton.MouseUp: Index is 0, tbIndex is 0
             ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 0

Here’s a simple one-window project that I created in 19r3.1 to test this. Do any of you see what I’m missing?

TabTestCatalina

As before, it works as expected on all platforms except Catalina.

I think I see what the issue is but I’m not on catalina right now

C’mon, give! Is it a Tim-oops, or something else? I’m slowly going bald here :smiley:

I don’t know much about Control Sets, but I do see the visual difference on Catalina you describe Tim, verses what I see on Mojave.

One observation I noticed, is the log on Catalina only outputs the following when I click on the “Data Manager” tab (for each OS):

ccMainTools.cvToolButton.MouseUp: Index is 3, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 3
Whereas on Mojave I see:

ccMainTools.cvToolButton.MouseUp: Index is 3, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 0, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 4, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 3, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 2, tbIndex is 3 ccMainTools.cvToolButton.Paint: Index is 1, tbIndex is 3
Does that help?
Edit to add: So more occurrences of the Paint event on Mojave with each click?

Yes - and it explains exactly what I see. All of the controls in the array are not repainted under Catalina. So, that looks like a Xojo thing. I feel much better now.

It appears that I’ll need to force a refresh of each member of the control array on MouseUp to get it sorted on Catalina.

Thanks, Scott!