This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
Summary: | Editor cursor visible even though the focus not in editor | ||
---|---|---|---|
Product: | editor | Reporter: | Jaromir Uhrik <juhrik> |
Component: | -- Other -- | Assignee: | _ tboudreau <tboudreau> |
Status: | RESOLVED FIXED | ||
Severity: | blocker | CC: | dsimonek, mkleint, tboudreau |
Priority: | P3 | Keywords: | FOCUS |
Version: | 6.x | ||
Hardware: | Macintosh | ||
OS: | Mac OS X | ||
Issue Type: | DEFECT | Exception Reporter: | |
Attachments: |
Messages.log with debug messages...
Dirty hack to set null caret on focus loss using only focusGained events Patch which works Final patch |
Description
Jaromir Uhrik
2006-11-30 16:43:41 UTC
Could you please try this on JDK1.6.0? Please run the IDE with -J-Dnetbeans.debug.editor.caret.focus=true and attach the output. It should give some additional focus debugging. THanks. Created attachment 39815 [details]
Messages.log with debug messages...
Vito, sorry I don't have 1.6.0 on my Mac so that I am not able to try on JDK1.6.0 - I use the last released Java 1.5.0_06; Java HotSpot(TM) Client VM 1.5.0_06-64. Ok, the log shows that the focus was moved from the editor tab, but the component that should recieve it was null and then (perhaps because of that) the focus was moved back to the editor. Strange. What happens when you type or use arrows? Are the key strokes captured by Project View or editor? *** Issue 77348 has been marked as a duplicate of this issue. *** BTW is this still reproducible on the Mac? Still occurs with nightly build of Apple JDK 6. I suspect Apple's UI delegates are depending on some event which our "QuietEditorPane" is being too quiet about. I do not see the same problem with IntellIiJ, which is also using Swing; in NetBeans, the editor cursor is always blinking, no matter where focus is, and it is clear that something is wrong in hiding inplace editors and quick-search boxes based on focus events as well, as you can send focus back to the editor and they aren't hidden. Probably it is a Mac OS bug of some sort, but since it does not occur in other Swing apps on mac, and does occur on both JDK 5 and JDK 6 (in which the Aqua L&F was completely rewritten), probably there is something we are doing with suppressing events that both iterations of their L&F depend on. So the characteristic that triggers the problem is a null opposite component from the FocusEvent? If we can nail down exactly the source of the problem, we can file a bug for Apple's JDK team, though probably we will need to do some local workaround anyway. I do know Apple's toolkit screws around weirdly with events and their sources - I was once advised never to trust EventQueue.getCurrentEvent() because events from various get replanned wildly and that call cannot be relied on to tell the truth; if the opposite component of a FocusEvent is not set when the FocusEvent is created, that would cause similar unpredictability. This is probably related - issue #119617. The focus transitions are definitely broken, even though I'm not sure if Netbeans or JDK is at fault. The weird thing in #119617 is that there is a guy reporting the same problem on Ubuntu with Sun's JDK6. moving opened issues from TM <= 6.1 to TM=Dev *** Issue 138105 has been marked as a duplicate of this issue. *** *** Issue 143413 has been marked as a duplicate of this issue. *** Lets focus next release.. Although this is not nice the behavior is not destructive. The focus indication is only visible in 2 places at once. But the focus is properly at the place where the user expects it to be. *** Issue 142775 has been marked as a duplicate of this issue. *** *** Issue 165613 has been marked as a duplicate of this issue. *** I just observed what I believe is this bug on a colleague's machine (using 6.7 on a MacBook Pro running Java 6 under Mac OS X 10.5.7). He had split the editor window for a file so he could look at two sections simultaneously. Both editor windows had flashing cursors, though obviously only one of them had focus. Additionally, there was no obvious way to tell by looking which of these two windows had focus. Both had active scrollbars and flashing cursors, and the title tab for both editor windows seemed to look exactly alike. I set up a small test app on my mac with an AWTEventListener to log focus events. It also contains a TopComponent that contains a plain JEditorPane and a JTextField. The problem is reproducible with the JEditorPane - give it focus, then give the explorer tree focus, and the cursor keeps blinking. Send focus from the JEditorPane to the JTextField, and *sometimes* the caret in the JEditorPane disappears. But at any rate, the logging nails down the root of the problem: The receiving component receives a FocusGained event. But the JEditorPane never receives the FocusLost event. That's the root of the problem. I have tried this in a standalone Swing app, and focus behavior is correct. So either something in NetBeans is either consuming FocusLost events before they reach the JEditorPane, or the events are never sent to begin with. Probably the best next step is to patch FocusEvent.consume() to print a stack trace; that should tell where it's coming from. Would do so myself but at the moment my macbook is out of power and the power adapter is MIA :-( At worst, if it cannot be fixed, it could be worked around by an AWTEventListener which listens for focus gained events, and if the opposite component is a JEditorPane, do something like pane.putClientProperty ("cachedCaret", pane.getCaret()); pane.setCaret(null); and the inverse when focus is gained. I can confirm that focus lost events are received by JEditorPanes in NetBeans on Windows; the logging is definitely different on mac. One complete shot-in-the-dark: See if the problem goes away if you call Beans.setDesignTime(false). I remember some other workarounds we had to do for Macs where we had to change that temporarily, cache the value, then set it back, for things to work correctly. I would respectfully disagree with juhrik's conclusion that "the behavior is not destructive. ... the focus is properly at the place where the user expects it to be." I've found the behavior to be destructive, precisely because focus is not where I expect it to be (or where it appears to be). A specific example: switching back and forth between NetBeans and a browser window while debugging a Ruby app, on return to NetBeans the focus indication is blinking in both the editor window and the Ruby interactive (output) window. Seeing the cursor in the interactive window, I paste some debugging code. Unfortunately, the cursor is actually in the editor window, and I've now inserted unwanted code into my source. True, the recovery from this is to hit "undo", but this requires a mental context switch away from the debugging task I was focused on and into noticing and repairing the unwanted edits to my source code. I'd argue that forcing that kind of context switch on a developer is "destructive" behavior for an IDE. (And if I'm not careful with the undos, literally destructive to my source code.) I got the following from an Apple JDK engineer - don't know if it is of use or not; the problem seems to be the focusLost being consumed or not fired, not the caret not paying attention to events. Frame.active might be useful somehow to make behavior of text controls more mac-like, however. ---------- Well, from reading the source of the com.apple.laf.AquaCaret class, we do install a FocusListener on the component and also check if the component has a "Frame.active" client property present, which we key off of to determine if the components in the window should be painted "active" or "inactive", depending on whether the window is in the foreground or background. We only set the caret visible if there is the dot and the mark are the same (no selection), the component has focus, and the "Frame.active" property is Boolean.TRUE. Created attachment 85584 [details]
Dirty hack to set null caret on focus loss using only focusGained events
I've attached an (untested) dirty hack to add to the other dirty hacks in the applemenu module, which may work to solve the caret problem. It does pretty much what I described below. Since we only get focus gained events, never focus lost events, it uses the existing AWT Event listener that makes ctrl-click work for popup menus to - check if the component focus was lost from is a JTextComponent - if yes, store the result of getCaret() using putClientProperty() on the component - check if the component focus was gained by is a JTextComponent - if yes, find the stored Caret, and if non-null, call setCaret() on the focus recipient with it I'll test it on my mac in the next few days, but if someone wants to beat me to it, feel free. The patch could have side effects - for example, going into overstrike mode with the keyboard and then setting focus might result in the wrong caret being shown - or any case where the caret is changed programmatically. It also probably depends on listener order. However, we can see how severe the side effects are, if there are any at all. Still would like to diagnose where the FocusLost events are being consumed, or if they are simply not there at all. Using a patched version of FocusEvent, I can confirm that FocusLost events are indeed created for JTextComponents. I can also confirm they are not being consumed. However, they are not being delivered to the component, or even to an AWTEventListener listening globally for focus events. I'd suspect some sort of bug in Apple's implementation of JTextComponent or KeyboardFocusManager. Construction stack traces below for switching between editor and explorer, and explorer quick search and editor, and editor and external application. Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:604) at java.awt.Component.dispatchEventImpl(Component.java:3941) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Window.dispatchEventImpl(Window.java:1801) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:93) at java.awt.EventQueue.dispatchEvent(EventQueue.java:461) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.KeyboardFocusManager.retargetFocusLost(KeyboardFocusManager.java:2785) at java.awt.KeyboardFocusManager.retargetFocusEvent(KeyboardFocusManager.java:2882) at java.awt.Component.dispatchEventImpl(Component.java:3934) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SentEvent.dispatch(SentEvent.java:50) at java.awt.DefaultKeyboardFocusManager$DefaultKeyboardFocusManagerSentEvent.dispatch(DefaultKeyboardFocusManager.java:161) at java.awt.DefaultKeyboardFocusManager.sendMessage(DefaultKeyboardFocusManager.java:188) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:604) at java.awt.Component.dispatchEventImpl(Component.java:3941) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Window.dispatchEventImpl(Window.java:1801) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:93) at java.awt.EventQueue.dispatchEvent(EventQueue.java:461) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) WARNING: A nonexistent Java extension /Library/Java/Extensions/libsvnjavahl.jnilib on /Library/Java/Extensions Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.KeyboardFocusManager.shouldNativelyFocusHeavyweight(KeyboardFocusManager.java:2320) Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.KeyboardFocusManager.processCurrentLightweightRequests(KeyboardFocusManager.java:2603) at java.awt.KeyboardFocusManager.retargetFocusEvent(KeyboardFocusManager.java:2874) at java.awt.Component.dispatchEventImpl(Component.java:3934) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:604) at java.awt.Component.dispatchEventImpl(Component.java:3941) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Window.dispatchEventImpl(Window.java:1801) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:93) at java.awt.EventQueue.dispatchEvent(EventQueue.java:461) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) Created a focus lost event java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1176) at java.awt.event.FocusEvent.<init>(FocusEvent.java:155) at java.awt.KeyboardFocusManager.retargetFocusLost(KeyboardFocusManager.java:2785) at java.awt.KeyboardFocusManager.retargetFocusEvent(KeyboardFocusManager.java:2882) at java.awt.Component.dispatchEventImpl(Component.java:3934) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SentEvent.dispatch(SentEvent.java:50) at java.awt.DefaultKeyboardFocusManager$DefaultKeyboardFocusManagerSentEvent.dispatch(DefaultKeyboardFocusManager.java:161) at java.awt.DefaultKeyboardFocusManager.sendMessage(DefaultKeyboardFocusManager.java:188) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:604) at java.awt.Component.dispatchEventImpl(Component.java:3941) at java.awt.Container.dispatchEventImpl(Container.java:2068) at java.awt.Window.dispatchEventImpl(Window.java:1801) at java.awt.Component.dispatchEvent(Component.java:3903) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:93) at java.awt.EventQueue.dispatchEvent(EventQueue.java:461) at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:104) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:19 A few more notes. Tried a tweaked version of the attached patch; however, frequently the opposite component on the FocusEvent is null, rather than the JTextComponent. This is something I noticed when working on the window system years ago - that focus events are frequently component -> null -> component rather than component -> next component. I still don't know what is the root of this behavior. Also patched QuietEditorPane, the parent class for all standard NB text editors, and overrode processEvent(AWTEvent) and processFocusEvent(FocusEvent). These methods appear never to be called under any circumstances. The fact that this behavior does happen in NetBeans, but not in standard Swing applications suggests there is *something* we are doing, either in core or the window system that is causing this problem. Will continue digging. Created attachment 85734 [details]
Patch which works
The latest attached patch fixes the problem, though not in the prettiest of ways: The applemenu already installs an AWTEventListener to make ctrl-clicks properly invoke popup menus. I added FOCUS_EVENT_MASK to the set of events it handles. When focus is gained by a JTextComponent, that component is stored in a WeakReference. When focus is gained by another component, the Reference<JTextComponent> is derefrenced, and if non-null, it calls caret.setVisible(false) on it. Could be refined to check and store the current value of Caret.isVisible() as a client property of the component before changing it, to ensure we don't cause the caret to be shown on components that are programmatically set not to show one. At the moment I know of no such cases, so probably this can be committed and if it causes such an issue that can be dealt with then. Created attachment 85736 [details]
Final patch
Sorry, specification.diff attached to wrong issue. Correct patch is the second applemenu.diff. Taking this issue. Fixed in changeset 990b9cc4f596 *** Issue 173050 has been marked as a duplicate of this issue. *** |