Git Product home page Git Product logo

Comments (47)

weisJ avatar weisJ commented on June 11, 2024 2

Could you elaborate what you mean by "jumpy".

Sorry I should have dropped a god example and a video.

Screen.Recording.2022-04-06.at.21.54.40.mov
The sync checkbox action is basically calling this method, and in this experience I tried to install custom decorations when os reporting is off and vice versa.

        void toggleSync(boolean sync) {
            try {
                manager.enableReporting(sync);
+               if (sync) {
+                   ExternalLafDecorator.instance().uninstall();
+               } else {
+                   ExternalLafDecorator.instance().install();
+               }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

This is expected behaviour when installing/uninstalling the decorations. Due to the nature that it changes the underlying window the "jumps" are somewhat inevitable.

Ok after having the time to dig it a bit I noticed an issue in my preference detector, it seems that in some specific circumstances my darkMode boolean got inverted, I've changed that and this fixes the three issues mentioned above (the darker border, the inactive buttons, and the window title.
...
So for me everything looks good now, thank you very much !!!

Thats great to hear :)

Maybe one thing that might be worth documenting is that installing the platform decorations make these properties ignored, it is expected since it is no the same UI component.

I think I can reasonably replicate the behaviour of those two properties.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024 2

You are right. I overlooked that fact :D I‘ll release the next version later today

from darklaf.

weisJ avatar weisJ commented on June 11, 2024 1

I have added a more convenient way to customize the titlebar colors:

ExternalLafDecorator.instance().setColorProvider(new DecorationsColorProvider() {

    @Override
    public Color backgroundColor() {
        return Color.CYAN;
    }

    @Override
    public Color activeForegroundColor() {
        return Color.ORANGE;
    }

    @Override
    public Color inactiveForegroundColor() {
        return Color.RED;
    }
});

There are more colors you can customize, but these three should suffice for the most use cases.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024 1

Does the current state of the implementation work for you suffiently? If yes I would cut the next release.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024 1

With the very latest snapshots (3.0.0-20220406.014333-77), I tried to install/uninstall the ExternalLafDecorator yet the title bar changes are is jumpy.

Could you elaborate what you mean by "jumpy".

Sorry I should have dropped a god example and a video.

Screen.Recording.2022-04-06.at.21.54.40.mov

The sync checkbox action is basically calling this method, and in this experience I tried to install custom decorations when os reporting is off and vice versa.

        void toggleSync(boolean sync) {
            try {
                manager.enableReporting(sync);
+               if (sync) {
+                   ExternalLafDecorator.instance().uninstall();
+               } else {
+                   ExternalLafDecorator.instance().install();
+               }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

Note this code change is only for this experiment. All other remarks are based on code like this

https://github.com/bric3/fireplace/pull/17/files#diff-926a8863ccf9519490252d2b47c70a1cdcaf401e59b66c977ce13cbe74a26db4R237-R265


Ok after having the time to dig it a bit I noticed an issue in my preference detector, it seems that in some specific circumstances my darkMode boolean got inverted, I've changed that and this fixes the three issues mentioned above (the darker border, the inactive buttons, and the window title.

Thanks for your "insistance" on this, I was able to nail the thing.

So for me everything looks good now, thank you very much !!!

from darklaf.

bric3 avatar bric3 commented on June 11, 2024 1

Excellent ! it works really well !

I just noted a simple issue upon first launch, the orange border does not seem to be correctly rendered / visible at the beginning of the app, however when toggling stuff it comes back.
That's not a big issue though.

Screen.Recording.2022-04-08.at.16.36.03.mov

Really awesome work !

from darklaf.

weisJ avatar weisJ commented on June 11, 2024 1

I realised that the correct behaviour should be that no border is painted at all in this case (specifying that the titlebar ist transparent should make it non-visible). Latest snapshot resolve the issue.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

I'm totally experimenting with it. So I noticed the CustomTitlePane in darklaf-platform-base, although I'm not quite sure it works as expected.

var frame = new JFrame("FirePlace");
final JRootPane rootPane = frame.getRootPane();
if (SystemInfo.isMacOS) {
    UIManager.put("MacOS.TitlePane.background", Color.PINK);
    UIManager.put("MacOS.TitlePane.foreground", Color.GRAY);
    UIManager.put("MacOS.TitlePane.inactiveBackground", Color.PINK.darker());
    UIManager.put("MacOS.TitlePane.inactiveForeground", Color.GRAY.darker());
    UIManager.put("MacOS.TitlePane.borderColor", Color.PINK);
    CustomTitlePane titlePane = DecorationsHandler.getSharedInstance().createTitlePane(rootPane, JRootPane.FRAME, frame);
    var layeredPane = rootPane.getLayeredPane();
    layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
    titlePane.setVisible(true);
}

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Adding doesn't seem to change a thing (I thought that the custom title bar might have been hidden by the system one.)

// allows to place swing components on the whole window
rootPane.putClientProperty("apple.awt.fullWindowContent", true);
// makes the title bar transparent
rootPane.putClientProperty("apple.awt.transparentTitleBar", true);

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

In comparison to the system theme detection mechanism it isn't quite as straight forward to get the custom decorations to work without actually using the laf itself. More specifically I don't think that it is possible at all in the current state. This is because DarkRootPaneUI has to be the ui of the frames JRootPane. I'll have to think about whether it is feasible to abstract it aways to the point where only setting the frames rootpane ui is enough.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

I have somewhat extracted the necessary code for native decorations to work. Using the following version:

repositories {
    maven {
        url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
    }
}

dependencies {
    // The double decorations isn't a typo. It has this name because the artifact is named "platform-decorations" and
    // the branch "decorations" ;)
    implementation("com.github.weisj:darklaf-platform-decorations-decorations:latest.integration")
}

You should be able to do the following then:

import com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator;
...
// Everything here should happen on the swing UI thread:
ExternalLafDecorator.instance().install();
// Install you LaF.

I can't be sure that it will work as expected with every LaF e.g. any LaF that needs to have it's own implementation of RootPaneUI present won't work (though most lafs should be fine). I haven't had the chance to test this on macOS yet, so it might not work fully for you.

Now if you wan't to change color of the titlebar you can do the following:
After installing the LaF set the appropriate properties in the UIManager. They property names can be found here:

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Cool il check it out. thanks a lot.
Can these properties be adjusted on Leaf change?

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

As long as you change the properties before you call updatesUI on the window hierarchy you should be fine.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Hi sorry for the delay, I got caught in various things.

So I have tried the ExternalLafDecorator, but I'm unsure how to use it.

var frame = new JFrame("FirePlace");
frame.getRootPane().getUI(); // =>BasicNativeDecorationsRootPaneUI

When changing the colors myself the title bar don't get updated, after an updateUI, I'm using FlatLaf from formdev.

  switch (appearanceModeButton.getClientProperty(TO_APPEARANCE).toString()) {
      case TO_DARK_LAF:
          FlatDarculaLaf.setup();
          Colors.setDarkMode(true);
          appearanceModeButton.putClientProperty(TO_APPEARANCE, TO_LIGHT_LAF);
          appearanceModeButton.setIcon(toLightMode);
          break;
      case TO_LIGHT_LAF:
          FlatIntelliJLaf.setup();
          Colors.setDarkMode(false);
          appearanceModeButton.putClientProperty(TO_APPEARANCE, TO_DARK_LAF);
          appearanceModeButton.setIcon(toDarkMode);
          break;
  }
+ UIManager.put("MacOS.TitlePane.borderColor", UIManager.get("TitlePane.borderColor"));
+ UIManager.put("MacOS.TitlePane.background", UIManager.get("TitlePane.background"));
+ UIManager.put("MacOS.TitlePane.foreground", UIManager.get("TitlePane.foreground"));
+ UIManager.put("MacOS.TitlePane.inactiveBackground", UIManager.get("TitlePane.inactiveBackground"));
+ UIManager.put("MacOS.TitlePane.inactiveForeground", UIManager.get(" TitlePane.inactiveForeground"));
  FlatLaf.updateUI();
  FlatAnimatedLafChange.hideSnapshotWithAnimation();

Also as a convenience I wonder if there's a default color that matches the light/dark system.


The following happens right in ExternalLafDecorator.instance().install();

Exception in thread "main" java.lang.IncompatibleClassChangeError: Class com.github.weisj.darklaf.platform.macos.MacOSDecorationsProvider does not implement the requested interface com.github.weisj.darklaf.platform.DecorationsProvider
	at com.github.weisj.darklaf.platform.decorations.NativeDecorationsManager.initialize(NativeDecorationsManager.java:94)
	at com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator.install(ExternalLafDecorator.java:61)
	at io.github.bric3.fireplace.FirePlaceMain$AppearanceControl.install(FirePlaceMain.java:257)
	at io.github.bric3.fireplace.FirePlaceMain.setupLaF(FirePlaceMain.java:224)
	at io.github.bric3.fireplace.FirePlaceMain.initUI(FirePlaceMain.java:73)
	at io.github.bric3.fireplace.FirePlaceMain.main(FirePlaceMain.java:66)

I just had to do that.

    implementation("com.github.weisj:darklaf-platform-preferences:latest.integration") {
        exclude("com.github.weisj", "darklaf-platform-decorations")
    }
    implementation("com.github.weisj:darklaf-platform-decorations-decorations:latest.integration")

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

The current draft PR is here : bric3/fireplace#17

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

The following works perfectly fine for me:

public class Test {

    static boolean systemLaf = true;

    private static void setLaf(String name, boolean updateUI) {
        try {
            UIManager.setLookAndFeel(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (systemLaf) {
            UIManager.put("Windows.TitlePane.borderColor", Color.YELLOW);
            UIManager.put("Windows.TitlePane.background", Color.CYAN);
            UIManager.put("Windows.TitlePane.foreground", Color.RED);
            UIManager.put("Windows.TitlePane.inactiveBackground", Color.BLUE);
            UIManager.put("Windows.TitlePane.inactiveForeground", Color.ORANGE);
        } else {
            UIManager.put("Windows.TitlePane.borderColor", Color.YELLOW);
            UIManager.put("Windows.TitlePane.background", Color.RED);
            UIManager.put("Windows.TitlePane.foreground", Color.CYAN);
            UIManager.put("Windows.TitlePane.inactiveBackground", Color.ORANGE);
            UIManager.put("Windows.TitlePane.inactiveForeground", Color.BLUE);
        }
        if (updateUI) updateLaf();
    }

    static void updateLaf() {
        for (final Window w : Window.getWindows()) {
            updateLafRecursively(w);
        }
    }

    private static void updateLafRecursively(final Window window) {
        for (final Window childWindow : window.getOwnedWindows()) {
            updateLafRecursively(childWindow);
        }
        SwingUtilities.updateComponentTreeUI(window);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ExternalLafDecorator.instance().install();
            setLaf(UIManager.getSystemLookAndFeelClassName(), false);
            systemLaf = false;

            JFrame frame = new JFrame("Test Frame");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().setLayout(new GridBagLayout());
            frame.getContentPane().setPreferredSize(new Dimension(400, 400));
            JButton button = (JButton) frame.getContentPane().add(new JButton("I am a button!"));

            button.addActionListener(e -> {
                setLaf(systemLaf
                    ? UIManager.getSystemLookAndFeelClassName()
                    : UIManager.getCrossPlatformLookAndFeelClassName(), true);
                systemLaf = !systemLaf;
                button.setText(systemLaf ? "Change to System Laf" : "Change to cross platform Laf");
                button.getParent().doLayout();
            });

            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

Let me quickly check whether it also works on macOS (with adjusted prooerty names of course).
It also works on macOS.

Also as a convenience I wonder if there's a default color that matches the light/dark system.

Currently it tries to "match" the colors of installed LaF (at least as good as possible without making assumptions about the LaF). Based on the colors one could try to guess whether the colors are supposed to be light or dark, but that isn't really reliable and the resulting colors will have to be adjusted to be compatible with the LaF in question to not look out of place.
What did you have in mind specifically here?

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

What did you have in mind specifically here?

Oh your description is actually better that what I had in mind (I was suggesting tha default "color set" matching either the light / dark modes of the OS.

I don't have any windows installation, nor Linux but I'll also try to adapt your example on macOs.
Thanks for sharing this!

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Ah sorry I'm between two "fires" and forgot to report back.

So I'm not sure if I'm doing this right but I got a NPE, because there's no value for the key borderSecondary.

Apr 05, 2022 11:33:37 AM com.formdev.flatlaf.util.LoggingFacadeImpl logSevere
SEVERE: FlatLaf: Failed to setup look and feel 'com.formdev.flatlaf.FlatIntelliJLaf'.
java.lang.NullPointerException
	at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
	at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
	at java.base/java.util.Properties.put(Properties.java:1301)
	at com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator.putOrCopy(ExternalLafDecorator.java:89)
	at com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator.installExtraProperties(ExternalLafDecorator.java:99)
	at com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator.lambda$new$0(ExternalLafDecorator.java:47)
	at java.desktop/java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:343)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:335)
	at java.desktop/javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(SwingPropertyChangeSupport.java:93)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:268)
	at java.desktop/javax.swing.UIManager.setLookAndFeel(UIManager.java:602)
	at com.formdev.flatlaf.FlatLaf.setup(FlatLaf.java:117)
	at com.formdev.flatlaf.FlatIntelliJLaf.setup(FlatIntelliJLaf.java:39)
	at io.github.bric3.fireplace.FirePlaceMain$AppearanceControl.lambda$new$0(FirePlaceMain.java:225)
	at io.github.bric3.fireplace.FirePlaceMain$AppearanceControl.lambda$new$1(FirePlaceMain.java:240)
	at com.github.weisj.darklaf.platform.preferences.SystemPreferencesManager.lambda$onPreferenceChange$0(SystemPreferencesManager.java:55)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.github.weisj.darklaf.platform.preferences.SystemPreferencesManager.onPreferenceChange(SystemPreferencesManager.java:54)
	at com.github.weisj.darklaf.platform.preferences.SystemPreferencesManager.enableReporting(SystemPreferencesManager.java:100)
	at io.github.bric3.fireplace.FirePlaceMain$AppearanceControl.install(FirePlaceMain.java:262)
	at io.github.bric3.fireplace.FirePlaceMain.setupLaF(FirePlaceMain.java:203)
	at io.github.bric3.fireplace.FirePlaceMain.initUI(FirePlaceMain.java:74)
	at io.github.bric3.fireplace.FirePlaceMain.main(FirePlaceMain.java:67)
ExternalLafDecorator.instance().install();
ExternalLafDecorator.instance().setColorProvider(new DecorationsColorProvider() {
    @Override
    public Color backgroundColor() {
        return UIManager.getColor("TitlePane.background");
    }
    @Override
    public Color activeForegroundColor() {
        return UIManager.getColor("TitlePane.foreground");
    }
    @Override
    public Color inactiveForegroundColor() {
        return UIManager.getColor(" TitlePane.inactiveForeground");
    }
});

So I think the code should protect itself against null values, returned by the color provider.

Thank you very much for your patience !

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Also I'm not sure about how to change the window title color. (It is set like this new JFrame("FirePlace"))

image image

Shouldn't be orange with this code ?

    @Override
    public Color activeForegroundColor() {
        return Color.ORANGE;
    }

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

So I think the code should protect itself against null values, returned by the color provider.

I can fallback to the default colors if null is returned. However it probably will look somewhat out of place.

Also I'm not sure about how to change the window title color. (It is set like this new JFrame("FirePlace"))

On macOS there actually isn't any way to change the color of the window title as it is painted by the OS. I have added to option to switch between light/dark title text by implementing DecorationsColorProvider::isDark.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

This looks way better, thanks. I noted two things

  1. I have my own color theme setting, however the setting has to be inverted, I think the isDark name could be renamed to something more speaking when reading the code isSystemWindowTitleDark

    @Override
    public boolean isDark() {
        return !Colors.isDarkMode();
    }
  2. The Window title is not the right color when I first start the application, once the LaF changes it works as expected though. I'll investigate.

    video showcasing the issue with system wide color change
    Screen.Recording.2022-04-05.at.15.07.11.mov
    video showcasing the issue with interface toggle
    Screen.Recording.2022-04-05.at.15.11.26.mov
  3. It's a nitpick, but why not propose a default implementation for this method.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

On point 1 and 2

After looking at the code, I noticed this line

Actually affects this one.

if (titleVisible) {
boolean isDarkTheme = UIManager.getBoolean("Theme.dark");
JNIDecorationsMacOS.setDarkTheme(windowHandle, isDarkTheme);
}

So when my implementation

@Override
public boolean isDark() {
    return !Colors.isDarkMode();
}

returns true from isDark, I actually get a dark text, and vice versa when returning false.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Also I noticed these logs (running JDK 17), not sure it is related though.

2022-04-05 15:04:04.733 java[69308:1421889] Bad JNI lookup accessibilityHitTest
2022-04-05 15:04:04.734 java[69308:1421889] (
	0   libawt_lwawt.dylib                  0x0000000102ed720d -[CommonComponentAccessibility accessibilityHitTest:] + 173
	1   libawt_lwawt.dylib                  0x0000000102e920a0 -[AWTView accessibilityHitTest:] + 176
	2   AppKit                              0x00007ff81e780eca -[NSWindow(NSWindowAccessibility) accessibilityHitTest:] + 302
	3   AppKit                              0x00007ff81e31531b -[NSApplication(NSApplicationAccessibility) accessibilityHitTest:] + 285
	4   AppKit                              0x00007ff81e2e4cec CopyElementAtPosition + 136
	5   HIServices                          0x00007ff820f3979e _AXXMIGCopyElementAtPosition + 393
	6   HIServices                          0x00007ff820f5b084 _XCopyElementAtPosition + 355
	7   HIServices                          0x00007ff820f189b9 mshMIGPerform + 182
	8   CoreFoundation                      0x00007ff81b4c0294 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
	9   CoreFoundation                      0x00007ff81b4c0174 __CFRunLoopDoSource1 + 619
	10  CoreFoundation                      0x00007ff81b4be7db __CFRunLoopRun + 2415
	11  CoreFoundation                      0x00007ff81b4bd7ac CFRunLoopRunSpecific + 562
	12  HIToolbox                           0x00007ff824144ce6 RunCurrentEventLoopInMode + 292
	13  HIToolbox                           0x00007ff824144913 ReceiveNextEventCommon + 283
	14  HIToolbox                           0x00007ff8241447e5 _BlockUntilNextEventMatchingListInModeWithFilter + 70
	15  AppKit                              0x00007ff81dee453d _DPSNextEvent + 927
	16  AppKit                              0x00007ff81dee2bfa -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1394
	17  libosxapp.dylib                     0x00000001023234aa -[NSApplicationAWT nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
	18  AppKit                              0x00007ff81ded52a9 -[NSApplication run] + 586
	19  libosxapp.dylib                     0x0000000102323275 +[NSApplicationAWT runAWTLoopWithApp:] + 165
	20  libawt_lwawt.dylib                  0x0000000102ef7140 +[AWTStarter starter:headless:] + 496
	21  libosxapp.dylib                     0x0000000102324f5f +[ThreadUtilities invokeBlockCopy:] + 15
	22  Foundation                          0x00007ff81c333857 __NSThreadPerformPerform + 179
	23  CoreFoundation                      0x00007ff81b4bfaeb __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	24  CoreFoundation                      0x00007ff81b4bfa53 __CFRunLoopDoSource0 + 180
	25  CoreFoundation                      0x00007ff81b4bf7cd __CFRunLoopDoSources0 + 242
	26  CoreFoundation                      0x00007ff81b4be1e8 __CFRunLoopRun + 892
	27  CoreFoundation                      0x00007ff81b4bd7ac CFRunLoopRunSpecific + 562
	28  libjli.dylib                        0x0000000101ef0132 CreateExecutionEnvironment + 386
	29  libjli.dylib                        0x0000000101eebc96 JLI_Launch + 1366
	30  java                                0x0000000101dc3c0a main + 394
	31  dyld                                0x000000011196351e start + 462
)
Exception in thread "AppKit Thread" java.lang.NoSuchMethodError: accessibilityHitTest

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

Also I noticed these logs (running JDK 17), not sure it is related though.
...

I don't think this is related. At least I don't see anything which might cause it as the native portion of the implementation doesn't interface with accessibilityHitTest.

  1. I have my own color theme setting, however the setting has to be inverted, I think the isDark name could be renamed to something more speaking when reading the code isSystemWindowTitleDark

I have changed the api to make it clearer on how to change the title color. You can now specify windowTitleColor as either LIGHT, DARK or CUSTOM, wheras CUSTOM will force the color returned by ::activeForegroundColor to be used and LIGHT/DARK repsectively use the native colors (or use sane defaults based on ::activeForegroundColor on Windows). Let me know if this API is easier to reason about.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

As for point 2: The beginning of the first video is directly after application startup right? In the second video it breaks after checking "Sync appearance". I suspect there might be some bad interplay between system theme detection and the decorations. Is bric3/fireplace#17 an up-to date version reproducing this behaviour?

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

On each video I restarted the app. But I agree there's something fishy, when the sync is re-enabled.

Is bric3/fireplace#17 an up-to date version reproducing this behaviour?

Yes, however it is based on the snapshot earlier today : https://oss.sonatype.org/content/repositories/snapshots/com/github/weisj/darklaf-platform-decorations/3.0.0-SNAPSHOT/darklaf-platform-decorations-3.0.0-20220405.121934-75.jar

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

The API is nicer with the latest change, however the title color is still weird. Wether it syncs with the OS or from settings.

I'll push the latest commit.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Hi there,

With the very latest snapshots (3.0.0-20220406.014333-77), I tried to install/uninstall the ExternalLafDecorator yet the title bar changes are is jumpy.

That made be realize there's a thin black border when the ExternalLafDecorator is active, in some occasions, and toggling the to dark theme, then light theme again makes this border disappear.

image image

Also I'm not sure about the buttons when inactive, they are not visible, or not even there. This seems to happen when os synchronization is active new SystemPreferencesManager().enableReporting(true)

And even for the out of color title, this seems related to when system reporting is active.

image

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Also I noticed these logs (running JDK 17), not sure it is related though.
...

I don't think this is related. At least I don't see anything which might cause it as the native portion of the implementation doesn't interface with accessibilityHitTest.

This seem to be related to these issues (running corretto 17.0.2.8.1)

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

Hi there,

With the very latest snapshots (3.0.0-20220406.014333-77), I tried to install/uninstall the ExternalLafDecorator yet the title bar changes are is jumpy.

Could you elaborate what you mean by "jumpy".

That made be realize there's a thin black border when the ExternalLafDecorator is active, in some occasions, and toggling the to dark theme, then light theme again makes this border disappear.

I suppose the dark border is coupled to the color of the window title. If it is light (which is achieved by switching the window to dark mode) the border also becomes dark. If this is bothering you, you can use TitleColor.CUSTOM to force the window to be in light theme (i.e. it has a light border).

Also I'm not sure about the buttons when inactive, they are not visible, or not even there. This seems to happen when os synchronization is active new SystemPreferencesManager().enableReporting(true)

What do you mean by inactive? Inactive as in ExternalLafDecorator is uninstalled or inactive as in the window isn't the active window?

And even for the out of color title, this seems related to when system reporting is active.

image

Does it behave correctly without system reporting enabled (except maybe the first time)?

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Maybe one thing that might be worth documenting is that installing the platform decorations make these properties ignored, it is expected since it is no the same UI component.

final JRootPane rootPane = frame.getRootPane();
if (SystemInfo.isMacOS) {
    // allows to place swing components on the whole window
    rootPane.putClientProperty("apple.awt.fullWindowContent", true);
    // makes the title bar transparent
    rootPane.putClientProperty("apple.awt.transparentTitleBar", true);
}

In a previous experiment I used these to have some UI components in the title bar.

Screen.Recording.2022-04-06.at.23.08.15.mov

But here as you see the color of window title is not updated

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Maybe one thing that might be worth documenting is that installing the platform decorations make these properties ignored, it is expected since it is no the same UI component.

I think I can reasonably replicate the behaviour of those two properties.

That would be nice to have a proper API for that. So it's possible to benefit from both worlds.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

With the latest snapshot using the properties should be possible (I hope).

Edit: Due to failing CI I suppose there is a regression and it won’t work as expected at all.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

That's curious the Linux and Windows failures seem unrelated.

  • macos:
    CustomTitleBarTest > checkTitleBarColored() FAILED
        org.opentest4j.AssertionFailedError: Expected java.awt.Color[r=255,g=0,b=0], but got java.awt.Color[r=242,g=242,b=242]. Allowed tolerance is 60. Title color not equal.
    
  • Linux:
    MemoryTest > frameGetsGarbageCollectedComplexContent() FAILED
        org.opentest4j.AssertionFailedError: expected: <true> but was: <false>
    
  • Windows:
    DemoTest > runningDemosDoesNotThrow() FAILED
    FontTest > initializationError FAILED
    MemoryTest > initializationError FAILED
    TooltipTest > initializationError FAILED
    
        Caused by:
        java.lang.IllegalArgumentException: Width (170) and height (0) cannot be <= 0
            at java.desktop/java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1016)
    

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

Should be fixed now

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

Does it appear if you force the whole window to be repainted? This might be a situation where the titlebar doesn't yet know that it is visible when painting (i.e. try to call SwingUtilities.invokeLater(() -> frame.repaint()) after installing the laf).

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Actually I'm installing the laf and decorations before creating the JFrame, and before frame.setVisible(true).

I'll investigate.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

So I just discovered the DecorationsColorProvider isn't called if UIManager::setLookAndFeel() or if the look and feel is com.apple.laf.AquaLookAndFeel. I was expecting this wasn't called if updateUI isn't called.

So once I got this using nimbus this gave this, it seems the border is painted over.

image

As for my app I still don't quite get what going on. It seems that on bootstrap the border is painted first, then the other component are painted over.
And once I click on the buttons, the border is painted on the top.

Screen.Recording.2022-04-09.at.00.52.59.mov

I'm not sure this a bug per se but a behavior I'm not familiar with.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

I've cooked have simple reproducer. Basically when the components are added to the frame they cover the border, but when refreshing the LaF, the border stays on top.

Single class reproducer code
import com.github.weisj.darklaf.platform.decorations.DecorationsColorProvider;
import com.github.weisj.darklaf.platform.decorations.ExternalLafDecorator;
import com.github.weisj.darklaf.platform.preferences.SystemPreferencesManager;
import com.github.weisj.darklaf.theme.spec.ColorToneRule;

import javax.swing.*;
import java.awt.*;

public class BorderMain {
    private static final SystemPreferencesManager manager = new SystemPreferencesManager();
    public static final Runnable SYNC_THEME_CHANGER = () -> {
        dark = manager.getPreferredThemeStyle().getColorToneRule() == ColorToneRule.DARK;
        setLaF();
        updateUI();
    };

    private static void setLaF() {
        try {
            UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel());
        } catch (UnsupportedLookAndFeelException e) {
            throw new RuntimeException(e);
        }
    }

    static {
        manager.addListener(style -> SYNC_THEME_CHANGER.run());
    }

    private static boolean dark = false;


    public static void main(String[] args) {
        System.setProperty("apple.awt.application.name", "Border");
        System.setProperty("apple.awt.application.appearance", "system");

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Border");

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(new Dimension(1000, 600));
            frame.getContentPane().add(getComp());

            var rootPane = frame.getRootPane();
            rootPane.putClientProperty("apple.awt.fullWindowContent", true);
            rootPane.putClientProperty("apple.awt.transparentTitleBar", true);

            ExternalLafDecorator.instance().install();
            ExternalLafDecorator.instance().setColorProvider(new DecorationsColorProvider() {
                @Override
                public Color backgroundColor() {
                    return dark ? Color.BLACK : Color.WHITE;
                }

                @Override
                public Color activeForegroundColor() {
                    return UIManager.getColor("TitlePane.foreground");
                }

                @Override
                public Color inactiveForegroundColor() {
                    return Color.RED;
                }

                @Override
                public TitleColor windowTitleColor() {
                    return dark ? TitleColor.LIGHT : TitleColor.DARK;
                }
            });
            SYNC_THEME_CHANGER.run();
            manager.enableReporting(true);
            frame.setVisible(true);
        });
    }

    private static JComponent getComp() {
        var topPanel = new JPanel(new BorderLayout());

        var textField = new JTextField("");
        textField.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLUE, 1),
                BorderFactory.createEmptyBorder(30, 5, 5, 5)
        ));
        textField.setEditable(false);
        textField.setBackground(Color.PINK);
        topPanel.add(textField, BorderLayout.CENTER);
        topPanel.setBackground(Color.DARK_GRAY);

        var control = getAppearanceControls();
        control.setBorder(BorderFactory.createLineBorder(Color.PINK, 1));
        control.setBackground(Color.GRAY);
        topPanel.add(control, BorderLayout.EAST);


        var content = new JPanel(new BorderLayout());
        content.setBackground(Color.GRAY);

        var panel = new JPanel(new BorderLayout());
        panel.add(topPanel, BorderLayout.NORTH);
        panel.add(content, BorderLayout.CENTER);

        return panel;
    }


    static JComponent getAppearanceControls() {
        var appearanceModeButton = new JCheckBox("L/D");
        {
            appearanceModeButton.addActionListener(e -> {
                dark = !dark;
                setLaF();
                updateUI();
            });
            appearanceModeButton.setEnabled(false);
            appearanceModeButton.setVisible(true);
        }

        var syncAppearanceButton = new JCheckBox("Sync appearance");
        {
            syncAppearanceButton.addActionListener(e -> {
                manager.enableReporting(syncAppearanceButton.isSelected());
                appearanceModeButton.setEnabled(!syncAppearanceButton.isSelected());
                SYNC_THEME_CHANGER.run();
            });
            syncAppearanceButton.setSelected(true);
            syncAppearanceButton.setVisible(true);
        }

        var appearanceControlsPanel = new JPanel(new FlowLayout());
        appearanceControlsPanel.add(appearanceModeButton);
        appearanceControlsPanel.add(syncAppearanceButton);

        return appearanceControlsPanel;
    }

    public static void updateUI() {
        for (var window : Window.getWindows()) {
            SwingUtilities.updateComponentTreeUI(window);
        }
    }
}

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Works for me, on my side there's nothing against publishing v3 :)

Quick question, if this is possible to get the height of the title bar or the button location. If I want to place a menu on the left to the titlebar buttons (close, minimize, expand), or if I want to put something under. At this time I'm settings my borders with magic numbers that I found by guess.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

I think I can expose the bounds of the buttons somehow. I’ll probably also add an easy way to hide the title as well (without setting the title to "", which would cripple accessibility), as otherwise one has to respect that one too which might be difficult.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Nice, although don't block the V3 for that it can wait. I mean if you're keen on releasing V3 sooner rather than later.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024

I scheduled the release of V3 for this weekend so I had some time on my hands. You can check the bound of the title buttons using ExternalLafDecorator.instance().decorationsManager().titlePaneLayout(frame).windowButtonRect(). Also you can hide the title of the window by setting rootPane.putClientProperty(DecorationsConstants.KEY_HIDE_TITLE, true)

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Cool!

I just gave these a try

  • Hiding window title, it does work ✅
    But the property is inverted DecorationsConstants.KEY_HIDE_TITLE to false will hide the title.

  • Getting the button rectangle
    image

    I was wondering if I could get them before the window is shown, in order to do layout before. But it doesn't seem possible.

    Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    	at com.github.weisj.darklaf.platform.macos.ui.MacOSTitlePane.windowHandle(MacOSTitlePane.java:203)
    	at com.github.weisj.darklaf.platform.macos.MacOSDecorationsProvider.titlePaneLayoutInfo(MacOSDecorationsProvider.java:69)
    	at com.github.weisj.darklaf.platform.decorations.NativeDecorationsManager.titlePaneLayoutInfo(NativeDecorationsManager.java:123)
    	at com.github.weisj.darklaf.platform.decorations.NativeDecorationsManager.titlePaneLayoutInfo(NativeDecorationsManager.java:127)
    	at io.github.bric3.fireplace.AppearanceControl.getWindowButtonsRect(AppearanceControl.java:88)
    	at io.github.bric3.fireplace.FirePlaceMain.lambda$initUI$10(FirePlaceMain.java:163)
    	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
    	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
    	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    ...
    

    After setting the frame visible there's no issue. But this happesn really fast so that seem ok. Maybe throwing a IllegalStateEx could suggest a dev to use that API after the frame is visible.

from darklaf.

weisJ avatar weisJ commented on June 11, 2024
  • Hiding window title, it does work
    But the property is inverted DecorationsConstants.KEY_HIDE_TITLE to false will hide the title.

Fixed

After setting the frame visible there's no issue. But this happesn really fast so that seem ok. Maybe throwing a IllegalStateEx could suggest a dev to use that API after the frame is visible.

An exception is now thrown with a more descriptive error message. You can actually force it to succeed by calling frame.addNotify() before calling it if you really need it before the window is visible. But I would go for something like this

class TitleBarSpace extends JComponent {
    public Dimension getPreferredSize() {
        ExternalLafDecorator.instance().decorationsManager().titlePaneLayout(frame).windowButtonRect().getSize();
    }

    public Dimension getMinimumSize() {
        return getPreferredSize();
    }
    
    public Dimension getPreferredSize() {
        return getPreferredSize();
    }
}

and using it as a spacer in e.g. a ``BorderLayoutorBoxLayout`.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

Interesting. I'll give it a try.

from darklaf.

bric3 avatar bric3 commented on June 11, 2024

That works, however the location (x in particular for my case) of the rectangle must taken into account. I suppose on Linux or Windows the button rectangle is on the other side, which require a different handling.

from darklaf.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.