mirror of https://github.com/weisJ/darklaf.git
Browse Source
Add tests for custom title bars. Fix issue where the JRootPane.hideTitlePane option had no effect on macOS.pull/215/head
weisj
4 years ago
committed by
Jannis Weis
12 changed files with 389 additions and 63 deletions
@ -0,0 +1,226 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2020 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and |
||||
* associated documentation files (the "Software"), to deal in the Software without restriction, |
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, |
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all copies or |
||||
* substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT |
||||
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
* |
||||
*/ |
||||
package test; |
||||
|
||||
import java.awt.*; |
||||
import java.awt.event.WindowAdapter; |
||||
import java.awt.event.WindowEvent; |
||||
import java.awt.image.BufferedImage; |
||||
import java.util.concurrent.atomic.AtomicReference; |
||||
import java.util.function.Consumer; |
||||
|
||||
import javax.swing.*; |
||||
|
||||
import org.junit.jupiter.api.*; |
||||
import org.junit.jupiter.api.condition.EnabledOnOs; |
||||
import org.junit.jupiter.api.condition.OS; |
||||
|
||||
import com.github.weisj.darklaf.LafManager; |
||||
import com.github.weisj.darklaf.theme.DarculaTheme; |
||||
import com.github.weisj.darklaf.theme.IntelliJTheme; |
||||
import com.github.weisj.darklaf.ui.rootpane.DarkRootPaneUI; |
||||
import com.github.weisj.darklaf.util.ColorUtil; |
||||
import com.github.weisj.darklaf.util.SystemInfo; |
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS) |
||||
class CustomTitleBarTest extends AbstractImageTest { |
||||
|
||||
private static final Color TITLE_BAR_COLOR = Color.RED; |
||||
private static final Color CONTENT_COLOR = Color.BLUE; |
||||
private static final int TITLE_BAR_Y = 10; |
||||
private static final int TOLERANCE = SystemInfo.isMac ? 55 : 0; |
||||
|
||||
public CustomTitleBarTest() { |
||||
super("titlebar"); |
||||
} |
||||
|
||||
@BeforeAll |
||||
static void setup() { |
||||
LafManager.registerInitTask((t, d) -> { |
||||
d.put("MacOS.TitlePane.background", TITLE_BAR_COLOR); |
||||
d.put("MacOS.TitlePane.inactiveBackground", TITLE_BAR_COLOR); |
||||
d.put("Windows.TitlePane.background", TITLE_BAR_COLOR); |
||||
d.put("Windows.TitlePane.inactiveBackground", TITLE_BAR_COLOR); |
||||
}); |
||||
} |
||||
|
||||
@BeforeEach |
||||
void beforeEach() { |
||||
LafManager.setDecorationsEnabled(true); |
||||
} |
||||
|
||||
private AtomicReference<JFrame> createFrame(final Consumer<JFrame> frameModifier) { |
||||
AtomicReference<JFrame> frame = new AtomicReference<>(); |
||||
final Object lock = new Object(); |
||||
TestUtils.runOnSwingThreadNotThrowing(() -> { |
||||
JFrame f = new JFrame(""); |
||||
frame.set(f); |
||||
JPanel content = new JPanel(); |
||||
content.setBackground(CONTENT_COLOR); |
||||
content.setPreferredSize(new Dimension(200, 200)); |
||||
f.setContentPane(content); |
||||
f.pack(); |
||||
f.setLocationRelativeTo(null); |
||||
f.addWindowListener(new WindowAdapter() { |
||||
@Override |
||||
public void windowOpened(final WindowEvent e) { |
||||
f.removeWindowListener(this); |
||||
SwingUtilities.invokeLater(() -> { |
||||
synchronized (lock) { |
||||
lock.notify(); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
f.setVisible(true); |
||||
frameModifier.accept(f); |
||||
}); |
||||
synchronized (lock) { |
||||
try { |
||||
lock.wait(100000); |
||||
// Wait some time because the window may still be transparent.
|
||||
Thread.sleep(500); |
||||
} catch (InterruptedException e) { |
||||
e.printStackTrace(); |
||||
Thread.currentThread().interrupt(); |
||||
} |
||||
} |
||||
return frame; |
||||
} |
||||
|
||||
private boolean checkScreenColor(final Color expected, final Color value) { |
||||
return Math.abs(expected.getRed() - value.getRed()) <= CustomTitleBarTest.TOLERANCE |
||||
&& Math.abs(expected.getGreen() - value.getGreen()) <= CustomTitleBarTest.TOLERANCE |
||||
&& Math.abs(expected.getBlue() - value.getBlue()) <= CustomTitleBarTest.TOLERANCE; |
||||
} |
||||
|
||||
private void assertScreenColorEquals(final Color expected, final Color value, |
||||
final String message) { |
||||
if (!checkScreenColor(expected, value)) { |
||||
String failureMessage = "Expected " + expected + ", but got " + value + ". Allowed tolerance is " |
||||
+ CustomTitleBarTest.TOLERANCE + ". " + message; |
||||
Assertions.fail(failureMessage); |
||||
} |
||||
} |
||||
|
||||
private void assertScreenColorNotEquals(final Color expected, final Color value, |
||||
final String message) { |
||||
if (checkScreenColor(expected, value)) { |
||||
String failureMessage = "Did not expect " + expected + ", but got " + value + ". Allowed tolerance is " |
||||
+ CustomTitleBarTest.TOLERANCE + ". " + message; |
||||
Assertions.fail(failureMessage); |
||||
} |
||||
} |
||||
|
||||
private void checkImage(final String fileName, final Consumer<BufferedImage> check) { |
||||
checkImage(fileName, f -> { |
||||
}, check); |
||||
} |
||||
|
||||
private void checkImage(final String fileName, final Consumer<JFrame> frameModifier, |
||||
final Consumer<BufferedImage> check) { |
||||
AtomicReference<JFrame> frame = createFrame(frameModifier); |
||||
TestUtils.runOnSwingThreadNotThrowing(() -> { |
||||
Rectangle rect = frame.get().getBounds(); |
||||
rect.setLocation(0, 0); |
||||
check.accept(saveWindowScreenShot(getPath(fileName), frame.get())); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs({OS.MAC, OS.WINDOWS}) |
||||
void checkTitleBarColored() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new IntelliJTheme())); |
||||
UIManager.put("macos.coloredTitleBar", true); |
||||
checkImage("colored_title_" + SystemInfo.getOsName(), |
||||
img -> assertScreenColorEquals(TITLE_BAR_COLOR, new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)), |
||||
"Title color not equal.")); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs(OS.MAC) |
||||
void checkTitleBarNotColored() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new IntelliJTheme())); |
||||
UIManager.put("macos.coloredTitleBar", false); |
||||
checkImage("non_colored_title_" + SystemInfo.getOsName(), img -> { |
||||
Color c = new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)); |
||||
assertScreenColorNotEquals(TITLE_BAR_COLOR, c, "Title is colored. Shouldn't be"); |
||||
assertScreenColorNotEquals(CONTENT_COLOR, c, "No native titlebar is visible"); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs(OS.MAC) |
||||
void checkForDarkNativeTitle() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new DarculaTheme())); |
||||
UIManager.put("macos.coloredTitleBar", false); |
||||
checkImage("native_title_dark_mac", img -> { |
||||
assertScreenColorNotEquals(CONTENT_COLOR, new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)), |
||||
"No native titlebar is visible"); |
||||
Color bg = new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)); |
||||
double brightness = ColorUtil.getPerceivedBrightness(bg); |
||||
Assertions.assertTrue(brightness < 80, |
||||
"Titlebar is not dark. brightness = " + brightness + ". For color " + bg); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs(OS.MAC) |
||||
void checkForLightNativeTitle() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new IntelliJTheme())); |
||||
UIManager.put("macos.coloredTitleBar", false); |
||||
checkImage("native_title_light_mac", img -> { |
||||
assertScreenColorNotEquals(CONTENT_COLOR, new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)), |
||||
"No native titlebar is visible"); |
||||
Color bg = new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)); |
||||
double brightness = ColorUtil.getPerceivedBrightness(bg); |
||||
Assertions.assertTrue(brightness > 200, |
||||
"Titlebar is not light. brightness = " + brightness + ". For color " + bg); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs({OS.MAC, OS.WINDOWS}) |
||||
void checkTitleBarHidden() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new IntelliJTheme())); |
||||
UIManager.put("macos.coloredTitleBar", true); |
||||
Assertions.assertTrue(LafManager.isDecorationsEnabled()); |
||||
checkImage("title_bar_hidden_" + SystemInfo.getOsName(), |
||||
frame -> { |
||||
JRootPane rootPane = frame.getRootPane(); |
||||
rootPane.putClientProperty(DarkRootPaneUI.HIDE_TITLEBAR, true); |
||||
}, |
||||
img -> assertScreenColorEquals(CONTENT_COLOR, new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)), |
||||
"Titlebar isn't hidden")); |
||||
} |
||||
|
||||
@Test |
||||
@EnabledOnOs(OS.WINDOWS) |
||||
void checkDisableCustomDecoration() { |
||||
SwingUtilities.invokeLater(() -> LafManager.install(new IntelliJTheme())); |
||||
checkImage("native_title_bar_window", |
||||
f -> LafManager.setDecorationsEnabled(false), |
||||
img -> assertScreenColorNotEquals(TITLE_BAR_COLOR, |
||||
new Color(img.getRGB(img.getWidth() / 2, TITLE_BAR_Y)), |
||||
"No native titlebar installed")); |
||||
} |
||||
} |
@ -0,0 +1,57 @@
|
||||
/* |
||||
* MIT License |
||||
* |
||||
* Copyright (c) 2020 Jannis Weis |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and |
||||
* associated documentation files (the "Software"), to deal in the Software without restriction, |
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, |
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be included in all copies or |
||||
* substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT |
||||
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
* |
||||
*/ |
||||
package test; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.util.concurrent.atomic.AtomicReference; |
||||
|
||||
import javax.swing.*; |
||||
|
||||
import org.junit.jupiter.api.Assertions; |
||||
|
||||
class TestUtils { |
||||
|
||||
private TestUtils() {} |
||||
|
||||
static void runOnSwingThreadNotThrowing(final Runnable action) { |
||||
AtomicReference<Exception> exceptionRef = new AtomicReference<>(); |
||||
try { |
||||
SwingUtilities.invokeAndWait(() -> { |
||||
try { |
||||
action.run(); |
||||
} catch (Exception e) { |
||||
exceptionRef.set(e); |
||||
} |
||||
}); |
||||
} catch (InterruptedException e) { |
||||
e.printStackTrace(); |
||||
Assertions.fail(e.getMessage(), e); |
||||
} catch (InvocationTargetException e) { |
||||
e.getTargetException().printStackTrace(); |
||||
Throwable target = e.getTargetException(); |
||||
Assertions.fail(target.getMessage(), target); |
||||
} |
||||
if (exceptionRef.get() != null) { |
||||
Assertions.fail(exceptionRef.get().getMessage(), exceptionRef.get()); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue