Ui DSL — get rid of setDefaultVisualPadding, get rid of static debug millis in test, do not paint debug grid (but still enable debug mode to compute debug bounds)

This commit is contained in:
Vladimir Krivosheev
2018-05-04 11:49:30 +02:00
parent ab0686e5db
commit 80c0afbfdf
3 changed files with 94 additions and 185 deletions
@@ -6,8 +6,6 @@ import com.intellij.ui.layout.*
import com.intellij.ui.layout.migLayout.patched.*
import com.intellij.util.containers.ContainerUtil
import net.miginfocom.layout.*
import net.miginfocom.layout.PlatformDefaults.VISUAL_PADDING_PROPERTY
import net.miginfocom.layout.PlatformDefaults.setDefaultVisualPadding
import java.awt.Component
import java.awt.Container
import javax.swing.ButtonGroup
@@ -15,47 +13,6 @@ import javax.swing.JDialog
import javax.swing.JLabel
internal class MigLayoutBuilder(val spacing: SpacingConfiguration) : LayoutBuilderImpl {
companion object {
init {
// unset incorrect for our LaF values (todo add ability to provide own provider to MigLayout)
setDefaultVisualPadding("Button.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.square.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.square.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.gradient.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.gradient.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.bevel.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.bevel.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.textured.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.textured.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.roundRect.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.roundRect.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.recessed.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.recessed.icon.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.help.$VISUAL_PADDING_PROPERTY", null)
setDefaultVisualPadding("Button.help.icon.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("ComboBox.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("ComboBox.isPopDown.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("ComboBox.isSquare.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("ComboBox.editable.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("ComboBox.editable.isSquare.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("TextField.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("TabbedPane.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("Spinner.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("RadioButton.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("RadioButton.small.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("RadioButton.mini.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("CheckBox.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("CheckBox.small.$VISUAL_PADDING_PROPERTY", null)
PlatformDefaults.setDefaultVisualPadding("CheckBox.mini.$VISUAL_PADDING_PROPERTY", null)
}
}
/**
* Map of component to constraints shared among rows (since components are unique)
*/
@@ -84,10 +41,6 @@ internal class MigLayoutBuilder(val spacing: SpacingConfiguration) : LayoutBuild
}
override fun build(container: Container, layoutConstraints: Array<out LCFlags>) {
// if (spacing.isCompensateVisualPaddings) {
// (container as JComponent).putClientProperty(JBUI.COMPENSATE_VISUAL_PADDING_KEY, false)
// }
val lc = createLayoutConstraints()
lc.gridGapY = gapToBoundSize(spacing.verticalGap, false)
if (layoutConstraints.isEmpty()) {
@@ -37,9 +37,7 @@ import gnu.trove.THashMap
import net.miginfocom.layout.*
import java.awt.*
import java.awt.event.ActionListener
import java.util.*
import javax.swing.*
import javax.swing.Timer
/** A very flexible layout manager.
* Read the documentation that came with this layout manager for information on usage.
@@ -49,7 +47,7 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
private var cacheParentW: ContainerWrapper? = null
@Transient
private val ccMap = THashMap<ComponentWrapper, CC>()
private val componentWrapperToConstraints = THashMap<ComponentWrapper, CC>()
@Transient
private var debugTimer: Timer? = null
@@ -62,18 +60,14 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
@Transient
private var lastInvalidSize: Dimension? = null
@Transient
private var lastWasInvalid = false // Added in 3.7.1. May have regressions
private var lastWasInvalid = false
@Transient
private var lastParentSize: Dimension? = null
@Transient
private var dirty = true
/** Returns the current debugging state.
* @return The current debugging state.
*/
private val isDebugEnabled: Boolean
get() = debugTimer != null
var isDebugEnabled = false
private val debugMillis: Int
get() {
@@ -83,61 +77,42 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
private var lastSize: Long = 0
private fun setComponentConstraintsImpl(comp: Component, constraints: Any) {
val parent = comp.parent
synchronized(if (parent != null) parent.treeLock else Any()) {
// 3.7.2. No sync if not added to a hierarchy. Defeats a NPE.
val cw = SwingComponentWrapper(comp as JComponent)
if (constraints is CC) {
ccMap[cw] = constraints
}
else {
throw IllegalArgumentException("Constraint must be ComponentConstraint: " + constraints.javaClass.toString())
}
dirty = true
private fun stopDebug() {
if (debugTimer != null) {
debugTimer!!.stop()
debugTimer = null
}
}
/** Sets the debugging state for this layout manager instance. If debug is turned on a timer will repaint the last laid out parent
* with debug information on top.
*
*
* Red fill and dashed red outline is used to indicate occupied cells in the grid. Blue dashed outline indicate
* component bounds set.
*
*
* Note that debug can also be set on the layout constraints. There it will be persisted. The value set here will not. See the class
* JavaDocs for information.
* @param parentW The parent to set debug for.
* @param b `true` means debug is turned on.
* @param parentWrapper The parent to set debug for.
*/
private fun setDebug(parentW: ComponentWrapper?, b: Boolean) {
if (!b || !(debugTimer == null || debugTimer!!.delay != debugMillis)) {
if (!b && debugTimer != null) {
debugTimer!!.stop()
debugTimer = null
}
private fun startDebug(parentWrapper: ComponentWrapper?) {
if (debugTimer?.delay == debugMillis) {
return
}
if (debugTimer != null) {
debugTimer!!.stop()
}
debugTimer?.stop()
debugTimer = Timer(debugMillis, ActionListener {
val grid = grid
if (grid != null && (grid.container.component as Component).isShowing) {
grid.paintDebug()
return@ActionListener
}
debugTimer!!.stop()
debugTimer = null
else {
debugTimer!!.stop()
debugTimer = null
}
})
val parent = parentW?.parent?.component as? Component
val parent = parentWrapper?.parent?.component as? Component
if (parent != null) {
SwingUtilities.invokeLater {
val p = parent.parent ?: return@invokeLater
@@ -158,85 +133,69 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
/** Check if something has changed and if so recreate it to the cached objects.
* @param parent The parent that is the target for this layout manager.
*/
private fun checkCache(parent: Container?) {
if (parent == null) {
return
}
private fun checkCache(parent: Container) {
if (dirty) {
grid = null
}
cleanConstraintMaps(parent)
componentWrapperToConstraints.retainEntries { wrapper, _ -> (wrapper as SwingComponentWrapper).component.parent === parent }
// Check if the grid is valid
// check if the grid is valid
val mc = PlatformDefaults.getModCount()
if (lastModCount != mc) {
grid = null
lastModCount = mc
}
if (!parent.isValid) {
if (!lastWasInvalid) {
lastWasInvalid = true
var hash = 0
var resetLastInvalidOnParent = false // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
for (wrapper in ccMap.keys) {
val component = wrapper.component
if (component is JTextArea || component is JEditorPane)
resetLastInvalidOnParent = true
hash = hash xor wrapper.layoutHashCode
hash += 285134905
}
if (resetLastInvalidOnParent) {
resetLastInvalidOnParent(parent)
}
if (hash != lastHash) {
grid = null
lastHash = hash
}
val ps = parent.size
if (lastInvalidSize == null || lastInvalidSize != ps) {
grid = null
lastInvalidSize = ps
}
}
}
else {
if (parent.isValid) {
lastWasInvalid = false
}
else if (!lastWasInvalid) {
lastWasInvalid = true
var hash = 0
var resetLastInvalidOnParent = false // Added in 3.7.3 to resolve a timing regression introduced in 3.7.1
for (wrapper in componentWrapperToConstraints.keys) {
val component = wrapper.component
if (component is JTextArea || component is JEditorPane) {
resetLastInvalidOnParent = true
}
hash = hash xor wrapper.layoutHashCode
hash += 285134905
}
if (resetLastInvalidOnParent) {
resetLastInvalidOnParent(parent)
}
if (hash != lastHash) {
grid = null
lastHash = hash
}
val ps = parent.size
if (lastInvalidSize == null || lastInvalidSize != ps) {
grid = null
lastInvalidSize = ps
}
}
val par = checkParent(parent)
setDebug(par, debugMillis > 0)
if (debugMillis > 0) {
startDebug(par)
}
else {
stopDebug()
}
if (grid == null) {
grid = Grid(par!!, layoutConstraints, rowConstraints, columnConstraints, ccMap, null)
grid = Grid(par!!, layoutConstraints, rowConstraints, columnConstraints, componentWrapperToConstraints, null)
}
dirty = false
}
/** Checks so all components in ccMap actually exist in the parent's collection. Removes
* any references that don't.
* @param parent The parent to compare ccMap against. Never null.
*/
private fun cleanConstraintMaps(parent: Container) {
val parentCompSet = HashSet(Arrays.asList(*parent.components))
val it = ccMap.entries.iterator()
while (it.hasNext()) {
val c = it.next().key.component as Component
if (!parentCompSet.contains(c)) {
it.remove()
}
}
}
/**
* @since 3.7.3
*/
@@ -268,13 +227,13 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
synchronized(parent.treeLock) {
checkCache(parent)
val i = parent.insets
val b = intArrayOf(i.left, i.top, parent.width - i.left - i.right, parent.height - i.top - i.bottom)
if (grid!!.layout(b, layoutConstraints.alignX, layoutConstraints.alignY, isDebugEnabled)) {
val insets = parent.insets
val bounds = intArrayOf(insets.left, insets.top, parent.width - insets.left - insets.right, parent.height - insets.top - insets.bottom)
val isDebugEnabled = isDebugEnabled || debugMillis > 0
if (grid!!.layout(bounds, layoutConstraints.alignX, layoutConstraints.alignY, isDebugEnabled)) {
grid = null
checkCache(parent)
grid!!.layout(b, layoutConstraints.alignX, layoutConstraints.alignY, isDebugEnabled)
grid!!.layout(bounds, layoutConstraints.alignX, layoutConstraints.alignY, isDebugEnabled)
}
val newSize = grid!!.height[1] + (grid!!.width[1].toLong() shl 32)
@@ -301,23 +260,20 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
private fun adjustWindowSize(parent: ContainerWrapper?) {
val wBounds = layoutConstraints.packWidth
val hBounds = layoutConstraints.packHeight
if (wBounds === BoundSize.NULL_SIZE && hBounds === BoundSize.NULL_SIZE) {
return
}
val packable = getPackable(parent!!.component as Component)
if (packable == null) {
return
}
val packable = getPackable(parent!!.component as Component) ?: return
val pc = parent.component as Component
var c: Container? = pc as? Container ?: pc.parent
var c = pc as? Container ?: pc.parent
while (c != null) {
val layout = c.layout
if (layout is BoxLayout || layout is OverlayLayout)
if (layout is BoxLayout || layout is OverlayLayout) {
(layout as LayoutManager2).invalidateLayout(c)
}
c = c.parent
}
@@ -339,24 +295,27 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
SwingUtilities.convertPointFromScreen(popPoint, invoker)
packable.show(invoker, popPoint.x, popPoint.y)
packable.preferredSize = null // Reset preferred size so we don't read it again.
// reset preferred size so we don't read it again.
packable.preferredSize = null
}
else {
packable.setBounds(x, y, targetW, targetH)
}
}
/** Returns a high level window or popup to pack, if any.
* @return May be null.
/**
* Returns a high level window or popup to pack, if any.
*/
private fun getPackable(comp: Component): Container? {
val popup = findType(JPopupMenu::class.java, comp)
if (popup != null) { // Lightweight/HeavyWeight popup must be handled separately
if (popup != null) {
// Lightweight/HeavyWeight popup must be handled separately
var popupComp: Container? = popup
while (popupComp != null) {
if (popupComp.javaClass.name.contains("HeavyWeightWindow"))
return popupComp // Return the heavy weight window for normal processing
if (popupComp.javaClass.name.contains("HeavyWeightWindow")) {
// return the heavy weight window for normal processing
return popupComp
}
popupComp = popupComp.parent
}
return popup // Return the JPopup.
@@ -382,8 +341,8 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
fun getComponentConstraints(): Map<Component, CC> {
val result = THashMap<Component, CC>()
for (entry in ccMap) {
result.put(entry.key.component as Component, entry.value)
for (entry in componentWrapperToConstraints) {
result.put((entry.key as SwingComponentWrapper).component, entry.value)
}
return result
}
@@ -397,7 +356,7 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
override fun preferredLayoutSize(parent: Container): Dimension {
synchronized(parent.treeLock) {
if (lastParentSize == null || parent.size != lastParentSize) {
for (wrapper in ccMap.keys) {
for (wrapper in componentWrapperToConstraints.keys) {
if (wrapper.contentBias != -1) {
layoutContainer(parent)
break
@@ -420,33 +379,37 @@ open class MigLayout @JvmOverloads constructor(val layoutConstraints: LC = LC(),
val grid = grid
val w = LayoutUtil.getSizeSafe(grid?.width, sizeType) + i.left + i.right
val h = LayoutUtil.getSizeSafe(grid?.height, sizeType) + i.top + i.bottom
return Dimension(w, h)
}
override fun getLayoutAlignmentX(parent: Container): Float {
val lc = layoutConstraints
return (if (lc.alignX != null) lc.alignX.getPixels(1f, checkParent(parent), null) else 0).toFloat()
val alignX = layoutConstraints.alignX ?: return 0f
return alignX.getPixels(1f, checkParent(parent), null).toFloat()
}
override fun getLayoutAlignmentY(parent: Container): Float {
val lc = layoutConstraints
return (if (lc.alignY != null) lc.alignY.getPixels(1f, checkParent(parent), null) else 0).toFloat()
val alignY = layoutConstraints.alignY ?: return 0f
return alignY.getPixels(1f, checkParent(parent), null).toFloat()
}
override fun addLayoutComponent(s: String, comp: Component) {
addLayoutComponent(comp, s)
}
override fun addLayoutComponent(comp: Component, constraints: Any) {
override fun addLayoutComponent(comp: Component, constraints: Any?) {
synchronized(comp.parent.treeLock) {
setComponentConstraintsImpl(comp, constraints)
val componentWrapper = SwingComponentWrapper(comp as JComponent)
if (constraints != null) {
componentWrapperToConstraints.put(componentWrapper, constraints as CC)
}
dirty = true
}
}
override fun removeLayoutComponent(comp: Component) {
synchronized(comp.parent.treeLock) {
ccMap.remove(SwingComponentWrapper(comp as JComponent))
componentWrapperToConstraints.remove(SwingComponentWrapper(comp as JComponent))
// to clear references
grid = null
}
@@ -8,7 +8,7 @@ import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.UsefulTestCase
import com.intellij.ui.UiTestRule
import com.intellij.ui.changeLafIfNeed
import net.miginfocom.layout.LayoutUtil
import com.intellij.ui.layout.migLayout.patched.*
import org.junit.*
import org.junit.Assume.assumeTrue
import org.junit.rules.TestName
@@ -86,17 +86,10 @@ class UiDslTest {
private fun doTest(panelCreator: () -> JPanel) {
invokeAndWaitIfNeed {
// otherwise rectangles are not set
LayoutUtil.setGlobalDebugMillis(1000)
val panel = panelCreator()
try {
uiRule.validate(panel, testName, lafName)
}
finally {
LayoutUtil.setGlobalDebugMillis(0)
// as result, MigLayout will stop debug timer
panel.doLayout()
}
// otherwise rectangles are not set
(panel.layout as MigLayout).isDebugEnabled = true
uiRule.validate(panel, testName, lafName)
}
}
}