Change Cursor For All Windows

I just noticed that when I set app.mouseCursor = system.cursors.wait, I only see the watch cursor when the mouse is over the frontmost window; when it’s over a different window or no window, it always reverts to the regular pointer. This is totally contrary to how the documentation says it’s supposed to work (particularly on a Mac)!

After encountering this in the app that I’m working on, I created a minimal test app to make sure there wasn’t any other code somewhere that could be changing the cursor. Same behavior.

Has anyone else noticed this? Is it a bug? Is there a way around it?

(I’m using Xojo 2014 Release 3.2 and running on OS X 10.10.5.)

MouseCursor is a property of the window. So it is normal that it affects only the window you call it in.

You could cycle through all open windows and set the cursor for each.

See http://documentation.xojo.com/index.php/Window_Method

I know that there is a mouseCursor property for each window, but I’m not setting those; I’m setting app.mouseCursor. As in, a property of the application class. According to the documentation, this is supposed to work universally throughout the app (at least on the Mac). (On Windows it is supposed to apply whenever the cursor is over an application window, but not when it isn’t.)

Was researching the documentation to find the specific wording to try to help determine what behavior we should expect.

Found this line: “On Macintosh, it is also the cursor that is displayed when the application is frontmost and the pointer moves outside of any of the application’s windows.” on Application.MouseCursor. So the behavior you expect is exactly what the docs say should be happening. If you can reproduce it misbehaving in a brand new project, please file a bug report and attach it!

I don’t think this is true anymore. As far as I can remember, it only affects the mouse while it’s inside of a window that has a Nil MouseCursor.

Ah, that’s too bad. Someone should update the docs to reflect this.
(on this note I can volunteer to maintain the old docs while you migrate to the new developer center)

Dave if you’d like to see this behavior, I guess you should file a feature request instead of a bug report.

I don’t think it’s possible to implement.

An open mind can do wonder. This goes in App.

Sub Open() app.MouseCursor=System.Cursors.FingerPointer For i As Integer = 0 To WindowCount - 1 Dim w As Window = Window(i) If w <> Nil Then w.MouseCursor = nil End If Next End Sub

As long as a window mousecursor is nil, App.MouseCursor is applied.

Incidentally, setting each window mousecursor to nil seems unnecessary if no cursor has been set for them before. I see the finger pointer on Mac without the loop.

Thanks, everyone. None of my windows have non-nil mouseCursor properties. I’m seeing the cursor that I set when the mouse is over whichever window is frontmost, but it always reverts to the default arrow cursor when it’s over any other window (or no window).

My test program has two document windows (window1 & window2) with no code in them at all, and the following two lines in the application’s Open event:

  mouseCursor = system.cursors.wait
  window2.show

That’s the only code anywhere in the porject (window1 is the app’s default window, so it is instantiated automatically).

No matter which window is frontmost at any given time, the cursor is the watch when in front of that window, but the arrow when it’s anywhere else. If I click on the other window to bring it forward, then it switches to the watch while in front of that window, but will then be the arrow if moved over the other window.

That is how it is supposed to work. You cannot set the mouse cursor system wide, out of your app’s windows.

Michel–even though it’s contrary to what the documentation says, I’m actually fine with not being able to set it when it’s outside of my app’s windows. But surely I should be able to set it when it’s over a window that does belong to my app, but just happens not to currently be the frontmost window?

Not sure what you mean by “contrary to what the documentation says”. I do not see anything in the LR implying that the cursor could be changed systemwide. It is indeed not saying the scope of the change, but one could infer it is limited to the app.

I suspect the manner in which the system manages windows is such that only the active window can change the cursor, for the same reasons. To Mac OS X, all inactive windows are the same, from your app or not, and do not have the right to change the cursor.

A while ago I explored changing the cursor systemwide with declares, unsuccessfully.

Sorry not to be able to give you the magic trick.

I have thought of a possible workaround using a global floating window with a transparent background that shows the picture of the cursor. When exiting the front window switch to that global floating window (show) that has cursor none, and use a timer monitoring system.MouseX and MouseY to keep that false cursor under the mouse.

When back over the front window, show it again, hide the false cursor.

I have not tested that, though.

I do appreciate your help/input, Michel. But I’m not sure how we’re interpreting the language in the LR so differently. For the mouseCursor property of the application class, it says “The cursor that is displayed while the application is running and the pointer is within one of the application’s windows.” Then, just below that under “Notes,” it further says “On Macintosh, it is also the cursor that is displayed when the application is frontmost and the pointer moves outside of any of the application’s windows.”

I’m having a hard time reading either of these statements as being at all consistent with the actual behavior, which is that app.mouseCursor seems to determine how the cursor is displayed only when the mouse is over one particular window (i.e., whichever one is frontmost at any given time). For my money, this makes app.mouseCursor effectively useless.

Can it really be possible that there is no way (short of rather involved tricks/workarounds) to control the cursor when it is anywhere other than in the currently frontmost window?? And that this is the intended situation?

All I want is to be able to set the “watch cursor” to show that the app is busy for a few seconds, and not have it suddenly switching back to the default arrow just because the user happens to move it over a different window. I cannot be the only one who has ever wanted to do this…

That is what I found out a while ago. It seems to be a Mac OS X thing and I did not find any documented way to do otherwise.

That said, the Human Interface Guidelines do recommend the use of a ProgressWheel on the window, which indicates to the user a process is ongoing.

Huh. Baffling.

If I did want to try a workaround along the lines you suggested…forgive my ignorance, but how do I give a window a “transparent background”?

A progress wheel would be fine for some kinds of situations, but the app that I’m working on is a game, and the “ongoing process” is the animation of a piece moving across the board. I don’t want non-game-related elements that clash with the overall aesthetics of my visual design cluttering up the interface. But I do want to provide feedback, in the form of the cursor, that lets the user/player know that (s)he can’t click on other stuff while the animation is happening and expect results. And sometimes, there are other windows (floaters, mainly) showing in addition to the main “game board” window while the animation happens–hence my “control the cursor for multiple windows” problem…

The simplest way to avoid that cursor issue seems to me to use your game in full screen mode. That is what most games do.

But here is how you turn the background transparent. Add this to a module and do self.transparent.

[code]Sub transparency(Extends pWindow As Window, pValue As Double = 0.0)
#if TargetCocoa
// get the reference to the NSColor class so we can call one of the class methods
soft declare function NSClassFromString lib “Cocoa” (aClassName as CFStringRef) as Ptr
dim NSColorClassRef as Ptr = NSClassFromString(“NSColor”)

// now ask the NSColor class to create a new NSColor from the values we have
soft declare function colorWithDeviceRed lib "Cocoa" selector "colorWithDeviceRed:green:blue:alpha:" (classRef as Ptr, red as Single, green as single, blue as single, alpha as single) as Ptr
dim NSColorInstance as Ptr = colorWithDeviceRed(NSColorClassRef, 0.0, 0.0, 0.0, pValue)

// set the features on the window
soft declare sub setAlphaValue lib "Cocoa" selector "setAlphaValue:" (WindowRef As Integer, AlphaValue As Single)
soft declare sub setOpaque lib "Cocoa" selector "setOpaque:" (WindowRef As Integer, IsOpaque As Boolean)
soft declare sub setBackgroundColor lib "Cocoa" selector "setBackgroundColor:" (WindowRef As Integer, inNSColorInstance As Ptr)

setAlphaValue pWindow.Handle, 1.0
setOpaque pWindow.Handle, FALSE
setBackgroundColor pWindow.Handle, NSColorInstance

// force the window to update so we get the proper shadowing
pWindow.Width = pWindow.Width + 1
pWindow.Width = pWindow.Width - 1

#endif

End Sub
[/code]

I believe you can do this with declares but it’s all about cursor rects and tracking areas which I’m not very clear on, plus I’m not sure how Xojo manages these or does anything extra. Still here’s some code that effects a global cursor albeit maybe there’s a better way.

Set App.MouseCursor to something and call this for all your windows

declare sub disableCursRects lib "AppKit" selector "disableCursorRects" (id As integer) disableCursRects(self.Handle)

Now the cursor will persist when leaving the Window but there’s 2 situations (I know of) that’ll revert to a pointer.

  1. If your app isn’t active and made active the cursor doesn’t change back. Workaround is to apply App.MouseCursor again in the Apps Activate event.

  2. TextFields and TextAreas (and possibly other controls that change the cursor like a Listboxes editing cell) can change the cursor, but in reverse. Entering the control the cursor stays but exiting the cursor changes to the pointer. A workaround is to set the cursor to IBeam in the controls MouseEnter and to your cursor in MouseExit.

Again this may not be the best way and I’ve only tested on Yosemite.

I believe I’ve solved my problem using a variation on Michel Bujardet’s idea about a “transparent” window. During my animation, I cover the entire screen with the transparent window and set that window’s cursor to the watch. Seems to be working…

Why does the user have to wait for your animation?