UI DSL and LaF tests — run on Retina monitor

This commit is contained in:
Vladimir Krivosheev
2018-04-06 11:57:25 +02:00
parent a84e4fd74b
commit 3e5b51fd24
20 changed files with 148 additions and 81 deletions
@@ -0,0 +1,5 @@
<root>
<item name='javax.swing.JComponent void paint(java.awt.Graphics) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -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<ScaleContext> {
}
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);
}
@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 357" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
@@ -109,12 +110,12 @@
<rect x="2" width="458" height="24" y="2" clip-path="url(#clipPath3)" stroke="none"/>
</g>
<g fill="rgb(187,187,187)" font-size="13px" font-family="'.SF NS Text'" transform="translate(8,52)" stroke="rgb(187,187,187)">
<text x="9" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="20" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="31" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="42" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="53" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="64" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text x="9" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text x="15" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text x="21" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text x="27" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text x="33" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text x="39" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
</g>
<g fill="rgb(100,100,100)" text-rendering="optimizeLegibility" font-size="13px" font-family="'.SF NS Text'" transform="translate(9,53)" stroke="rgb(100,100,100)">
<path d="M2 2 L460 2 L460 26 L2 26 L2 2 ZM3 3 L459 3 L459 25 L3 25 L3 3 Z" fill-rule="evenodd" clip-path="url(#clipPath3)" stroke="none"/>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 343" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
@@ -86,12 +87,12 @@
<g fill="rgb(236,236,236)" font-size="13px" font-family="'.SF NS Text'" transform="translate(8,46)" stroke="rgb(236,236,236)">
<rect x="0" width="464" height="30" y="0" clip-path="url(#clipPath6)" stroke="none"/>
<rect x="3" y="3" clip-path="url(#clipPath6)" fill="white" width="458" height="24" stroke="none"/>
<text fill="rgb(29,29,29)" x="8" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="19" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="30" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="41" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="52" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="63" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">●</text>
<text fill="rgb(29,29,29)" x="8" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text fill="rgb(29,29,29)" x="14" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text fill="rgb(29,29,29)" x="20" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text fill="rgb(29,29,29)" x="26" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text fill="rgb(29,29,29)" x="32" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
<text fill="rgb(29,29,29)" x="38" xml:space="preserve" y="20" clip-path="url(#clipPath7)" stroke="none">•</text>
</g>
<g fill="rgb(188,188,188)" text-rendering="optimizeLegibility" font-size="13px" font-family="'.SF NS Text'" transform="translate(9,47)" stroke="rgb(188,188,188)">
<path d="M3 3 L459 3 L459 25 L3 25 L3 3 ZM4 4 L458 4 L458 24 L4 24 L4 4 Z" fill-rule="evenodd" clip-path="url(#clipPath8)" stroke="none"/>

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -1,6 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square"
stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320"
font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
<g>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 646 371" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

@@ -1,6 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square"
stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320"
font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>
<g>

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 480 320" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -1,3 +1,4 @@
<!--suppress XmlUnusedNamespaceDeclaration -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill-opacity="1" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="0 0 649 341" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto">
<!--Generated by the Batik Graphics2D SVG Generator-->
<defs id="genericDefs"/>

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@@ -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 = "<!--suppress XmlUnusedNamespaceDeclaration -->\n" + writer
.toString()
// &#27;Remember
// no idea why transformer/batik doesn't escape it correctly
.replace(">&#27;", ">&amp;")
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)
}
@@ -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")
@@ -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")
@@ -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)
}
@@ -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
@@ -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)));
}