From 3e5b51fd248b844d57dbe1b4ae812500009ea4ea Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 6 Apr 2018 10:52:00 +0200 Subject: [PATCH] =?UTF-8?q?UI=20DSL=20and=20LaF=20tests=20=E2=80=94=20run?= =?UTF-8?q?=20on=20Retina=20monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jdk/javax/swing/annotations.xml | 5 ++ .../openapi/ui/impl/ShadowPainter.java | 18 +---- .../ui/laf/Darcula/mac/components.svg | 13 +-- .../ui/laf/IntelliJ/mac/components.svg | 13 +-- .../mac/align fields in the nested grid.svg | 1 + .../ui/layout/Darcula/mac/align fields.svg | 1 + .../testData/ui/layout/Darcula/mac/cell.svg | 5 +- .../Darcula/mac/note row in the dialog.svg | 1 + .../ui/layout/Darcula/mac/visual paddings.svg | 1 + .../mac/align fields in the nested grid.svg | 1 + .../ui/layout/IntelliJ/mac/align fields.svg | 1 + .../testData/ui/layout/IntelliJ/mac/cell.svg | 5 +- .../IntelliJ/mac/note row in the dialog.svg | 1 + .../layout/IntelliJ/mac/visual paddings.svg | 1 + .../testSrc/com/intellij/ui/SvgRenderer.kt | 40 +++++++--- .../testSrc/com/intellij/ui/laf/LafTest.kt | 10 ++- .../com/intellij/ui/layout/UiDslTest.kt | 10 ++- .../testSrc/com/intellij/ui/uiTestUtil.kt | 79 +++++++++++++------ .../com/intellij/openapi/util/IconLoader.java | 5 +- .../util/src/com/intellij/util/ui/JBUI.java | 18 ++++- 20 files changed, 148 insertions(+), 81 deletions(-) create mode 100644 lib/annotations/jdk/javax/swing/annotations.xml diff --git a/lib/annotations/jdk/javax/swing/annotations.xml b/lib/annotations/jdk/javax/swing/annotations.xml new file mode 100644 index 000000000000..46d4390ef7f5 --- /dev/null +++ b/lib/annotations/jdk/javax/swing/annotations.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/ShadowPainter.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/ShadowPainter.java index a9ca8a234fac..3eec6ae134a3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/ShadowPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/ShadowPainter.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.ui.impl; import com.intellij.util.IconUtil; @@ -101,7 +87,7 @@ public class ShadowPainter extends ScaleContextSupport { } public void paintShadow(Component c, Graphics2D g, int x, int y, int width, int height) { - ScaleContext ctx = ScaleContext.create(c); + ScaleContext ctx = ScaleContext.create(c, g); if (updateScaleContext(ctx)) { updateIcons(ctx); } diff --git a/platform/platform-tests/testData/ui/laf/Darcula/mac/components.svg b/platform/platform-tests/testData/ui/laf/Darcula/mac/components.svg index 464e9689cf8f..f81ad7484e30 100644 --- a/platform/platform-tests/testData/ui/laf/Darcula/mac/components.svg +++ b/platform/platform-tests/testData/ui/laf/Darcula/mac/components.svg @@ -1,3 +1,4 @@ + @@ -109,12 +110,12 @@ - ● - ● - ● - ● - ● - ● + • + • + • + • + • + • diff --git a/platform/platform-tests/testData/ui/laf/IntelliJ/mac/components.svg b/platform/platform-tests/testData/ui/laf/IntelliJ/mac/components.svg index 8c6cc9ce154d..313c414b3a5a 100644 --- a/platform/platform-tests/testData/ui/laf/IntelliJ/mac/components.svg +++ b/platform/platform-tests/testData/ui/laf/IntelliJ/mac/components.svg @@ -1,3 +1,4 @@ + @@ -86,12 +87,12 @@ - ● - ● - ● - ● - ● - ● + • + • + • + • + • + • diff --git a/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields in the nested grid.svg b/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields in the nested grid.svg index eb6f8b74815d..4a522224301d 100644 --- a/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields in the nested grid.svg +++ b/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields in the nested grid.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields.svg b/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields.svg index 5bbc3ca7f281..fdde8fdc5a65 100644 --- a/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields.svg +++ b/platform/platform-tests/testData/ui/layout/Darcula/mac/align fields.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/Darcula/mac/cell.svg b/platform/platform-tests/testData/ui/layout/Darcula/mac/cell.svg index 138880fa096f..28f12ec6385a 100644 --- a/platform/platform-tests/testData/ui/layout/Darcula/mac/cell.svg +++ b/platform/platform-tests/testData/ui/layout/Darcula/mac/cell.svg @@ -1,6 +1,5 @@ - + + diff --git a/platform/platform-tests/testData/ui/layout/Darcula/mac/note row in the dialog.svg b/platform/platform-tests/testData/ui/layout/Darcula/mac/note row in the dialog.svg index 921067db895c..c89fb6d62ac3 100644 --- a/platform/platform-tests/testData/ui/layout/Darcula/mac/note row in the dialog.svg +++ b/platform/platform-tests/testData/ui/layout/Darcula/mac/note row in the dialog.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/Darcula/mac/visual paddings.svg b/platform/platform-tests/testData/ui/layout/Darcula/mac/visual paddings.svg index 7e4cc04b7fb7..f17112b0345e 100644 --- a/platform/platform-tests/testData/ui/layout/Darcula/mac/visual paddings.svg +++ b/platform/platform-tests/testData/ui/layout/Darcula/mac/visual paddings.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields in the nested grid.svg b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields in the nested grid.svg index 17a591f94057..b9436a69f294 100644 --- a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields in the nested grid.svg +++ b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields in the nested grid.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields.svg b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields.svg index 13ab9ef9c194..97e283192e54 100644 --- a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields.svg +++ b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/align fields.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/cell.svg b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/cell.svg index 64e9dcf6d286..fc3c5887d20b 100644 --- a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/cell.svg +++ b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/cell.svg @@ -1,6 +1,5 @@ - + + diff --git a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/note row in the dialog.svg b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/note row in the dialog.svg index 718d908b3a16..0942e765d53d 100644 --- a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/note row in the dialog.svg +++ b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/note row in the dialog.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/visual paddings.svg b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/visual paddings.svg index 4cf8f8447885..92a9ffb24306 100644 --- a/platform/platform-tests/testData/ui/layout/IntelliJ/mac/visual paddings.svg +++ b/platform/platform-tests/testData/ui/layout/IntelliJ/mac/visual paddings.svg @@ -1,3 +1,4 @@ + diff --git a/platform/platform-tests/testSrc/com/intellij/ui/SvgRenderer.kt b/platform/platform-tests/testSrc/com/intellij/ui/SvgRenderer.kt index c761e0769447..ea76256892b2 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/SvgRenderer.kt +++ b/platform/platform-tests/testSrc/com/intellij/ui/SvgRenderer.kt @@ -3,8 +3,10 @@ package com.intellij.ui import com.intellij.openapi.application.ex.PathManagerEx import com.intellij.openapi.util.IconLoader +import com.intellij.openapi.util.SystemInfoRt import com.intellij.openapi.util.io.FileUtilRt import com.intellij.util.ui.IconCache +import org.apache.batik.anim.dom.SVGDOMImplementation import org.apache.batik.dom.GenericDOMImplementation import org.apache.batik.svggen.ImageHandlerBase64Encoder import org.apache.batik.svggen.SVGGeneratorContext @@ -13,7 +15,9 @@ import org.apache.batik.svggen.SVGSyntax import org.apache.xmlgraphics.java2d.GraphicsConfigurationWithTransparency import org.w3c.dom.Element import java.awt.Component +import java.awt.GraphicsConfiguration import java.awt.Image +import java.awt.Rectangle import java.io.StringWriter import java.nio.file.Path import java.nio.file.Paths @@ -23,13 +27,15 @@ import javax.xml.transform.dom.DOMSource import javax.xml.transform.stream.StreamResult // jFreeSvg produces not so compact and readable SVG as batik -internal class SvgRenderer(private val svgFileDir: Path) { +internal class SvgRenderer(val svgFileDir: Path) { private val xmlTransformer = TransformerFactory.newInstance().newTransformer() // todo check on Retina - does it works or not (is Retina disabled or not) - private val deviceConfiguration = GraphicsConfigurationWithTransparency() + val deviceConfiguration = object : GraphicsConfigurationWithTransparency() { + override fun getBounds() = Rectangle(0, 0, 1000, 1000) + } - private val xmlFactory = GenericDOMImplementation.getDOMImplementation().createDocument("http://www.w3.org/2000/svg", "svg", null) + private val xmlFactory = GenericDOMImplementation.getDOMImplementation().createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null) private val context = SVGGeneratorContext.createDefault(xmlFactory) init { @@ -91,27 +97,41 @@ internal class SvgRenderer(private val svgFileDir: Path) { val writer = StringWriter() writer.use { val root = svgGenerator.root - root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", SVGSyntax.SVG_NAMESPACE_URI) root.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink") - val bounds = component.bounds - root.setAttributeNS(null, "viewBox", "${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}") + root.setAttributeNS(null, "viewBox", "0 0 ${component.width} ${component.height}") xmlTransformer.transform(DOMSource(root), StreamResult(writer)) } - return writer + // xlink is not used in some files and optimize imports on commit can modify file, so, as simple solution, disable inspection + val result = "\n" + writer .toString() // Remember // no idea why transformer/batik doesn't escape it correctly .replace(">", ">&") + return if (SystemInfoRt.isWindows) FileUtilRt.toSystemIndependentName(result) else result } fun render(component: Component): String { - val svgGenerator = object : SVGGraphics2D(context, false) { - override fun getDeviceConfiguration() = this@SvgRenderer.deviceConfiguration - } + val svgGenerator = SvgGraphics2dWithDeviceConfiguration(context, deviceConfiguration) component.paint(svgGenerator) return svgGraphicsToString(svgGenerator, component) } +} + +private class SvgGraphics2dWithDeviceConfiguration : SVGGraphics2D { + private val _deviceConfiguration: GraphicsConfiguration + + constructor(context: SVGGeneratorContext, _deviceConfiguration: GraphicsConfiguration) : super(context, false) { + this._deviceConfiguration = _deviceConfiguration + } + + private constructor(g: SvgGraphics2dWithDeviceConfiguration): super(g) { + this._deviceConfiguration = g._deviceConfiguration + } + + override fun getDeviceConfiguration() = _deviceConfiguration + + override fun create() = SvgGraphics2dWithDeviceConfiguration(this) } \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/ui/laf/LafTest.kt b/platform/platform-tests/testSrc/com/intellij/ui/laf/LafTest.kt index ff27d671e303..396cc4e0fcb9 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/laf/LafTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ui/laf/LafTest.kt @@ -6,15 +6,13 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.ui.VerticalFlowLayout import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.UsefulTestCase -import com.intellij.ui.JBIntSpinner -import com.intellij.ui.changeLafIfNeed +import com.intellij.ui.* import com.intellij.ui.components.CheckBox import com.intellij.ui.components.textFieldWithHistoryWithBrowseButton import com.intellij.ui.layout.* -import com.intellij.ui.snapshotFileName -import com.intellij.ui.validatePanel import org.junit.Assume.assumeTrue import org.junit.Before +import org.junit.ClassRule import org.junit.Rule import org.junit.Test import org.junit.rules.TestName @@ -33,6 +31,10 @@ class LafTest { @JvmStatic @Parameterized.Parameters(name = "{0}") fun lafNames() = listOf("Darcula", "IntelliJ") + + @JvmField + @ClassRule + val requireHeadlessMode = RequireHeadlessMode() } @Suppress("MemberVisibilityCanBePrivate") diff --git a/platform/platform-tests/testSrc/com/intellij/ui/layout/UiDslTest.kt b/platform/platform-tests/testSrc/com/intellij/ui/layout/UiDslTest.kt index 458fd1144a28..2a2665667879 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/layout/UiDslTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ui/layout/UiDslTest.kt @@ -5,15 +5,13 @@ import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.util.SystemInfoRt import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.UsefulTestCase +import com.intellij.ui.RequireHeadlessMode import com.intellij.ui.changeLafIfNeed import com.intellij.ui.snapshotFileName import com.intellij.ui.validatePanel import net.miginfocom.layout.LayoutUtil -import org.junit.After +import org.junit.* import org.junit.Assume.assumeTrue -import org.junit.Before -import org.junit.Rule -import org.junit.Test import org.junit.rules.TestName import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -29,6 +27,10 @@ class UiDslTest { @JvmStatic @Parameterized.Parameters(name = "{0}") fun lafNames() = listOf("Darcula", "IntelliJ") + + @JvmField + @ClassRule + val requireHeadlessMode = RequireHeadlessMode() } @Suppress("MemberVisibilityCanBePrivate") diff --git a/platform/platform-tests/testSrc/com/intellij/ui/uiTestUtil.kt b/platform/platform-tests/testSrc/com/intellij/ui/uiTestUtil.kt index 1364f693783b..7a391a591fe8 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/uiTestUtil.kt +++ b/platform/platform-tests/testSrc/com/intellij/ui/uiTestUtil.kt @@ -6,20 +6,25 @@ import com.intellij.ide.ui.laf.darcula.DarculaLaf import com.intellij.openapi.application.invokeAndWaitIfNeed import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.text.StringUtil +import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.ui.layout.* import com.intellij.util.io.exists import com.intellij.util.io.sanitizeFileName import com.intellij.util.io.write import com.intellij.util.ui.JBUI +import com.intellij.util.ui.TestScaleHelper import com.intellij.util.ui.UIUtil import io.netty.util.internal.SystemPropertyUtil import net.miginfocom.swing.MigLayout +import org.junit.Assume +import org.junit.rules.ExternalResource import org.junit.rules.TestName import org.yaml.snakeyaml.DumperOptions import org.yaml.snakeyaml.Yaml import java.awt.Component import java.awt.Container +import java.awt.GraphicsEnvironment import java.io.File import java.nio.file.Path import javax.swing.AbstractButton @@ -30,21 +35,32 @@ import javax.swing.plaf.metal.MetalLookAndFeel private val isUpdateSnapshotsGlobal by lazy { SystemPropertyUtil.getBoolean("test.update.snapshots", false) } -//class RestoreLafRule : ExternalResource() { -// var oldLafName: String? = null -// -// override fun before() { -// oldLafName = UIManager.getLookAndFeel().name -// } -// -// override fun after() { -// val oldLafName = oldLafName -// if (oldLafName != null && oldLafName != UIManager.getLookAndFeel().name) { -// assertThat(oldLafName).isEqualTo("IntelliJ") -// UIManager.setLookAndFeel(IntelliJLaf()) -// } -// } -//} +class NoScaleRule : ExternalResource() { + private var scaleHelper = TestScaleHelper() + + override fun before() { + scaleHelper.setState() + } + + override fun after() { + scaleHelper.restoreState() + } +} + +class RequireHeadlessMode : ExternalResource() { + override fun before() { + // there is some difference if run as not headless (on retina monitor, at least), not yet clear why, so, just require to run in headless mode + if (UsefulTestCase.IS_UNDER_TEAMCITY) { + Assume.assumeTrue(GraphicsEnvironment.isHeadless()) + } + else { + System.setProperty("java.awt.headless", "true") + if (!GraphicsEnvironment.isHeadless()) { + throw RuntimeException("must be headless") + } + } + } +} fun changeLafIfNeed(lafName: String) { if (UIManager.getLookAndFeel().name == lafName) { @@ -119,8 +135,8 @@ private fun compareSnapshot(snapshotFile: Path, newData: String, isUpdateSnapsho } } -fun validateUsingImage(component: Component, snapshotDir: Path, snapshotName: String, isUpdateSnapshots: Boolean = isUpdateSnapshotsGlobal) { - compareSnapshot(snapshotDir.resolve("$snapshotName.svg"), SvgRenderer(snapshotDir).render(component), isUpdateSnapshots) +internal fun validateUsingImage(component: Component, svgRenderer: SvgRenderer, snapshotName: String, isUpdateSnapshots: Boolean = isUpdateSnapshotsGlobal) { + compareSnapshot(svgRenderer.svgFileDir.resolve("$snapshotName.svg"), svgRenderer.render(component), isUpdateSnapshots) } val TestName.snapshotFileName: String @@ -152,11 +168,28 @@ internal fun getComponentKey(c: Component, index: Int): String { } } -fun validatePanel(panel: JPanel, testDataRoot: Path, snapshotName: String, lafName: String) { - val preferredSize = panel.preferredSize - panel.setBounds(0, 0, Math.max(preferredSize.width, JBUI.scale(480)), Math.max(preferredSize.height, 320)) - panel.doLayout() +fun validatePanel(userPanel: JPanel, testDataRoot: Path, snapshotName: String, lafName: String) { + val svgRenderer = SvgRenderer(testDataRoot.resolve(getSnapshotRelativePath(lafName, isForImage = true))) - validateUsingImage(panel, testDataRoot.resolve(getSnapshotRelativePath(lafName, isForImage = true)), snapshotName) - validateBounds(panel, testDataRoot.resolve(getSnapshotRelativePath(lafName, isForImage = false)), snapshotName) + // to run tests on retina monitor (@2x images must be not used and so on) + // Graphics2D.getDeviceConfiguration is not enough because our IconLoader.paintIcon uses component.getGraphicsConfiguration() instead of g.getDeviceConfiguration() +// val panel = object : JComponent() { +// override fun getGraphicsConfiguration() = svgRenderer.deviceConfiguration +// +// override fun paint(g: Graphics) { +// // paint userPanel directly to ensure that SVG doesn't contain this wrapper +// userPanel.paint(g) +// } +// } + +// panel.add(userPanel) + +// panel.addNotify() + val preferredSize = userPanel.preferredSize +// panel.setBounds(0, 0, Math.max(preferredSize.width, JBUI.scale(480)), Math.max(preferredSize.height, 320)) + userPanel.setBounds(0, 0, Math.max(preferredSize.width, JBUI.scale(480)), Math.max(preferredSize.height, 320)) + userPanel.doLayout() + + validateUsingImage(userPanel, svgRenderer, snapshotName) + validateBounds(userPanel, testDataRoot.resolve(getSnapshotRelativePath(lafName, isForImage = false)), snapshotName) } \ No newline at end of file diff --git a/platform/util/src/com/intellij/openapi/util/IconLoader.java b/platform/util/src/com/intellij/openapi/util/IconLoader.java index 49fa3972c03e..234d45fee77c 100644 --- a/platform/util/src/com/intellij/openapi/util/IconLoader.java +++ b/platform/util/src/com/intellij/openapi/util/IconLoader.java @@ -515,10 +515,7 @@ public final class IconLoader { @Override public void paintIcon(Component c, Graphics g, int x, int y) { - // Component is preferable to Graphics as a scale provider, as it lets the context stick - // to the comp's actual scale via the update method. - ScaleContext ctx = c != null ? ScaleContext.create(c) : ScaleContext.create((Graphics2D)g); - getRealIcon(ctx).paintIcon(c, g, x, y); + getRealIcon(ScaleContext.create(c, g)).paintIcon(c, g, x, y); } @Override diff --git a/platform/util/src/com/intellij/util/ui/JBUI.java b/platform/util/src/com/intellij/util/ui/JBUI.java index a38a8eb39d0c..791f701daee8 100644 --- a/platform/util/src/com/intellij/util/ui/JBUI.java +++ b/platform/util/src/com/intellij/util/ui/JBUI.java @@ -628,7 +628,7 @@ public class JBUI { return SystemInfo.isMac ? smallFont() : label(); } } - + private static final JBEmptyBorder SHARED_EMPTY_INSTANCE = new JBEmptyBorder(0); @SuppressWarnings("UseDPIAwareBorders") @@ -964,10 +964,24 @@ public class JBUI { return ctx; } + /** + * Creates a context based on the component's (or graphics's) scale and sticks to it via the {@link #update()} method. + */ + public static ScaleContext create(@Nullable Component component, @Nullable Graphics graphics) { + // Component is preferable to Graphics as a scale provider, as it lets the context stick + // to the comp's actual scale via the update method. + if (component == null || component.getGraphicsConfiguration() == null) { + return create((Graphics2D)graphics); + } + else { + return create(component); + } + } + /** * Creates a context based on the gc's system scale */ - public static ScaleContext create(GraphicsConfiguration gc) { + public static ScaleContext create(@Nullable GraphicsConfiguration gc) { return new ScaleContext(SYS_SCALE.of(sysScale(gc))); }