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)));
}