FocusLost fires twice (Desktop/Mac)

I thought this was a problem of my original app somewhere. To be sure, I created a small and very simple test project simulating the scenario, which, to my surprise, acted the same unexpected way:

Add a Window to an empty Desktop Project (Mac); a Container Control with 4 textfield controls, 4 label controls on the CC. Drag-Drop the CC onto the Window (in the IDE). Add “FocusLost” Event to every textfield control. Add a Method (with an Integer Parameter) to the CC that is called by every Event (of each textfield control) and does some more or less useful things. Do not insert a messagebox code line in that method (that would change the behavior). Run.

When I tab out of the first textfield control (to land the cursor in the second), the FocusLost Event fires (correctly). When I “resume” from the debugger, my expectation is to land in the next textfield control. But instead, I immediately land in the (fired) Event of the next textfield control. When I remove the FocusLost Event Handler from the second textfield control, the fields and Events “behave” as expected: Event is fired on the first textfield, on “resume” I land on the second textfield (before any other “FocusLost” code is being executed). However, I would need the FocusLost Event on that second field, of course. This happens “between” all the textfields controls.

So, a “FocusLost” (on the second textfield control) Event is fired before even having “entered” the control. I would expect that for a “FocusReceived” event - but for a FocusLost?

What am I missing here? Is this intended behavior? Or am I missing a “trick”?

It seems like just a weird reproducible behavior caused by the window losing focus and a race condition with the debugger communication.

I played with a demo project for a while and found the behavior is different if you click on the desktop to lose focus instead of changing text fields.

My guess is that the IDE coming forward is causing the Window / TextField to lose focus before the execution can actually pause. When you resume the event is able to actually reach the debugger causing the second pause.

Is this behavior causing adverse effects for your release build? I don’t think it should be a problem for your built application. You should always avoid using MessageBox for debugging purposes, this has long been a recommendation by experts.

Demo project: Download Focus-Fun.xojo_xml_project

Thanks for that quick reply. Your demo project looks pretty much identical. Good to learn that “I am not alone”.

I admit that I haven’t double checked this behavior with the build yet. I was so perplexed checking the behavior of my app (degbugger level, because that “doing something more or less useful” is a bit more complex in the original app) that I have not proceeded to build level with this. When this “only” happens in debug: not a big thing (yet). In production? not nice: some of the fields depend on each other and some others (in that sequence) get filled from other methods (from other classes/objects). I’ll have to check.

Again, thanks for your quick “re-assurance” of direction, and for the hot tip on not using messagebox for debugging (links, please?).

https://documentation.xojo.com/api/user_interface/messagebox.html#notes

3 Likes

This is kinda reminding me of the “Schroedingers cat” phenomenon. You only know if its dead when you disturb/destroy the sytem… Thanks for the link!

It seems I cannot see this weird behavior in “build”, only in debug. Thanks a lot for the help! Could this be approached for a “better debug behavior” (probably not an easy/quick task, though)?

FocusLost and I will probably not become best friends. In other places of the app with custom controls (textfield + button on a container control to either autocomplete textfield entry after a successful search or choosing an entry from a list in another (pop-up) window after clicking the button, and in both cases fill in fields on the originating form) I get that double firing as well.

The bad thing in this case is that it can lead to hard crashes of the app. I changed the Event from FocusLost to KeyDown (for tabbing out) and that’s how I got rid of the problem (not only in the IDE/debugger) immediately. Not quite the same as “FocusLost”, but nevertheless very helpful for now.

The focus seemed to “jump back” from the button to the textfield (all within the same custom control), or from the Textfield to itself again (after “tabbing out”), firing the FocusLost Event (again), which happens to execute the same code (under certain conditions) like clicking the button. The code opens another Window (with Tabstoppable controls on it). This seems to influence the focus (for the controls on the originating window) and therefore does not do what I would expect. Merely clicking into the textfield on the original window executed the FocusLost event again immediately (shouldn’t that be handled by FocusReceived?), resulting in a multitude of windows (one after another click-sequence between windows) being opened. Even calling FocusNext (nearly everywhere) didn’t change that.

Seems like all “focus” is tied to the app and not to a window (object).

Is there a tutorial anywhere going through a rather complex example regarding “Focus” stuff between windows and container controls (with many textfield and even custom controls)?

Focus events are not the place for logic that affects anything but the control itself, such as formatting the value of a textfield.

1 Like

Hey Tim, thank you very much for this “robust” clarification! I thought by merely using this Event as a trigger for “more workflow control” would be a good idea, which it is clearly not.

The new “xojo documentation” seems to not even give a hint into the direction you are pointing out, which would be pretty important from my pov. Am I missing something in the docs or is this so obvious (or such an old xojo wisdom), that I have just earned the noobie price of the month?

I am still seeing code triggered from a “FocusLost” Event, that looks (and from a users perspevtive, feels) like being triggered from a “FocusReceived” Event (which doesn’t even exist), but when I am on a disrecommended path for using something, I just need to reconfig my approach, no? It just feels like circumventing a bug, but I might not be using xojo long enough to be dealing with these situations adequately. Obviously, after more than 10 years, I am still on my way to learning how to love my xojo :wink:

Sorry for being terse, but I’ve been bitten by this so many times in the last 20 years that I have just gotten used to coding around it. One trick the might work would be to fire a short period timer or use Timer.CallLater, just to get you out of the bowels of the focus event. That might smooth things out a bit. Focus events are just finicky.