weisJ / darklaf

Darklaf - A themeable swing Look and Feel based on Darcula-Laf
MIT License
437 stars 40 forks source link

JOptionPane sometimes opens fully squashed (height of title bar) on Mac #230

Closed steveyegge closed 3 years ago

steveyegge commented 3 years ago

Describe the bug In my game app, JOptionPane dialogs sometimes appear without the message area, buttons, or icon -- they pop up as a truncated title bar with only the window controls and the first few characters of the title. They are not resizable in this condition. Screen shot included.

Unfortunately the behavior is not deterministic. I first found the issue when I was popping up JOptionPane dialogs on the wrong thread, and was able to fix most of the occurrences by ensuring I was invoking JOptionPane.show*Dialog using SwingUtilities.invokeLater. Changing it to happen on the Swing main UI thread cut down dramatically on the problem.

However, I now am seeing it happen in a particular place in my application, in which an open dialog box invokes JOptionPane.showConfirmDialog() on the Swing main UI thread. Other JOptionPane instances in my app are not affected when this one is misbehaving - they continue to work fine.

To Reproduce I do not have a prepackaged repro case for this issue. The invoking code is straightforward:

private fun maybePurchaseCrowns() {
        val index = crownList?.selectedIndex ?: return gameWindow.message("Unable to purchase Crowns")
        val item = DATA[index]
        val title = "Purchase Wyvern Crowns"
        val price = "$%.2f".format(item.price)
        val message = "Buy ${item.numCrows} Crowns for $price?"
        val result = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_OPTION)
        closeDialog()
        if (result == JOptionPane.YES_OPTION) {
            purchaseCrowns(item)
        } else {
            gameWindow.message("Crown purchase cancelled.")
        }
    }

My app exhibits the problem whether I pass the owning frame (gameWindow) or the dialog (this) as the first parameter to JOptionPane.showConfirmDialog.

Given that it was happening more frequently when I was calling it on the wrong thread, maybe that's a path towards creating a repro case. Let me know if you'd like me to try putting one together. Alternately I can try debugging it in my own app, if you give me some pointers on where to look.

Screenshots image

Additional Information:

Additional context

Once the bug starts occurring -- rarely on the first try -- then it usually (though not always) continues happening on subsequent tries. It might be easier for me to debug it by catching it in the act than to create a standalone repro case, assuming it's not a race condition.

If we can't figure this one out, I can just implement my own replacement prompt dialog.

weisJ commented 3 years ago

This looks really strange to me. First of you already made the important observation that all ui related things should happen on the swing event dispatch thread, so we can (almost) rule out this as the cause. A possible cause could be that the rootpane reports a preferred size of (0,0) when it shouldn't (possibly due to an invalid layout state) causing JDialog#pack to set an incorrect window size.

To get a baseline of the situation could you please do the following:

  1. Check that you are actually on the edt when opening the dialog using SwingUtilities.isEventDispatchThread() .
  2. See whether the issue is present with the default and/or system laf.
  3. Try out the newest version of darklaf (currently 2.5.5)
steveyegge commented 3 years ago

Thanks for taking a look.

I have confirmed that:

I have some good news, at least for me. :) I've found a reliable workaround: passing null as the first parameter to the call to JOptionPane.showConfirmDialog. If I pass either a reference to the parent dialog which is invoking JOptionPane, or to the dialog's parent frame (the main game window), then the bug happens. Typically it takes 2-4 attempts to make it start happening.

However, if I pass null, then a different behavior occurs. When the bug is not triggered, the confirm dialog looks like this:

image

and when the bug would normally be triggered, resulting in a squashed dialog, but I've passed null, the confirm dialog instead looks like this:

image

It is a bit taller, and it is positioned with the upper-leftmost coordinate of the confirm dialog approximately (though not exactly) in the center of my laptop screen.

In any case, with the workaround the dialog is visible and usable, so it's good enough for me to launch.

weisJ commented 3 years ago

Thank you for the additional input. The fact that using a 'null' parent produces a slightly higher components makes me more confident in my theory that somethings is going wrong when calculating the preferred size of the frame.

The additional space looks suspiciously close to what amounts to the height of the title bar: Could you please try what happens when you specify UIManager.put("macos.coloredTitleBar", false) after installing the laf?

steveyegge commented 3 years ago

I have verified that putting UIManager.put("macos.coloredTitleBar", false) in after initializing the laf fixes the problem.

Just to make sure I won't be wasting your time:

So while I can't be 100% certain that turning off the colored title bar fixes it, as I may have just been very unlucky, statistically it seems very likely that it does fix it.

weisJ commented 3 years ago

Using the latest snapshot version

repositories {
    maven {
        url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
    }
}
dependencies {
    implementation("com.github.weisj:darklaf-core:latest.integration")
}

and without UIManager.put("macos.coloredTitleBar", false) could you please provide the output when using LafManager.setLogLevel(Level.FINER)

steveyegge commented 3 years ago

This is the entire logging output:

[2021-03-01 17:24:23] [FINER] Loading icon 'jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/navigation/verticalGlue.svg'. [at com.github.weisj.darklaf.icons.DarkSVGIcon]
[2021-03-01 17:24:23] [FINER] Patching colors of icon jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/navigation/verticalGlue.svg [at com.github.weisj.darklaf.icons.IconColorMapper]
[2021-03-01 17:24:23] [FINER] Loading icon 'jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/navigation/horizontalGlue.svg'. [at com.github.weisj.darklaf.icons.DarkSVGIcon]
[2021-03-01 17:24:23] [FINER] Patching colors of icon jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/navigation/horizontalGlue.svg [at com.github.weisj.darklaf.icons.IconColorMapper]
[2021-03-01 17:27:14] [FINER] Loading icon 'jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/dialog/questionDialog.svg'. [at com.github.weisj.darklaf.icons.DarkSVGIcon]
[2021-03-01 17:27:14] [FINER] Patching colors of icon jar:file:/Users/stevey/.gradle/caches/modules-2/files-2.1/com.github.weisj/darklaf-core/2.5.5/8a609c3473a1e191e06de38f9eaf06caefca6682/darklaf-core-2.5.5.jar!/com/github/weisj/darklaf/icons/dialog/questionDialog.svg [at com.github.weisj.darklaf.icons.IconColorMapper]

The first four lines are emitted on app startup. The second two lines are emitted when I first open the JOptionPane (non-squished). After that, no more log entries are emitted, no matter how many times I show the JOptionPane dialog.

Unfortunately, it usually takes 4-5 tries to get it to squish, but no further logging output was emitted after first opening the JOptionPane dialog. I tried several times to see if it would squish on the first try, so we could see the log entries for the failure case, but I was unsuccessful. Feel free to put in more logging and I can run it again.

weisJ commented 3 years ago

Hmm... I have added some additional logging. Regardless I am missing some lines in the log which should definitely come up. Could you please add

configurations.all {
    resolutionStrategy.cacheChangingModulesFor(0, "seconds")
}

to your build file and run with LafManager.setLogLevel(Level.FINEST). (Make sure to not call LafManager#enableLogging as it will overwrite the log level)

steveyegge commented 3 years ago

Here is the log output after 3 runs at opening the JOptionPane. For the first 2 runs, it opened normally. For the 3rd run, it opened extra tall (like my screenshot above). With logging turned off, it opens squished when it fails, and when logging is turned on, it opens tall. I've verified this by trying it with logging on and then off several times.

[2021-03-03 14:30:35] [FINE] Installing decorations for window 140612435864032 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:35] [FINER] Title bar is hidden. [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:35] [FINER] Preferred height of title pane: 0 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:37] [FINE] Installing decorations for window 140614854061664 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:37] [FINER] [PrefSize] JRootPane content size: java.awt.Dimension[width=250,height=100] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:37] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:37] [FINER] [PrefSize] JRootPane titlePane size: java.awt.Dimension[width=0,height=28] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:37] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:39] [FINE] Installing decorations for window 140612435888272 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:39] [FINER] Title bar is hidden. [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:39] [FINER] Preferred height of title pane: 0 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:41] [FINE] Installing decorations for window 140611902261072 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:41] [FINER] [PrefSize] JRootPane content size: java.awt.Dimension[width=250,height=100] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:41] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:41] [FINER] [PrefSize] JRootPane titlePane size: java.awt.Dimension[width=0,height=28] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:41] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:45] [FINE] Installing decorations for window 140611914358192 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:45] [FINER] Title bar is hidden. [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:45] [FINER] Preferred height of title pane: 0 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:46] [FINE] Installing decorations for window 140612435942848 [at com.github.weisj.darklaf.platform.macos.ui.MacOSDecorationsUtil]
[2021-03-03 14:30:46] [FINER] [PrefSize] JRootPane content size: java.awt.Dimension[width=250,height=100] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:46] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:46] [FINER] [PrefSize] JRootPane titlePane size: java.awt.Dimension[width=0,height=28] [at com.github.weisj.darklaf.ui.rootpane.DarkSubstanceRootLayout]
[2021-03-03 14:30:46] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]
[2021-03-03 14:30:46] [FINER] Preferred height of title pane: 28 [at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane]

Note that the parent dialog, which is invoking the JOptionPane, has no frame decorations (isUndecorated = true). Not sure if that's relevant, but it might be.

weisJ commented 3 years ago

What parts of the log correspond to which opening runs? The fact that it disappeared with logging enabled is very weird as the logging doesn’t interface with the component state at all. For me this looks like some nasty race conditions which can’t be observed because of IO operations. Just to be sure would it be able for you to output the size and preferred size of the JDialog in https://github.com/openjdk/jdk/blob/9730266d02bc2952c3621ebc2b7d6dee2a97c0e0/src/java.desktop/share/classes/javax/swing/JOptionPane.java#L997 before and after pack() has been called. Additionally inside pack() output the preferred size calculated there.