From 1bb524646e11521e31a1b7b99cb805501279c6ad Mon Sep 17 00:00:00 2001 From: Stephen Byrne Date: Tue, 15 Jul 2025 02:04:27 +0000 Subject: [PATCH] mig and other goodies --- build.gradle.kts | 13 ++-- .../kotlin/net/eksb/kswingutil/SwingUtil.kt | 13 ++++ .../kotlin/net/eksb/kswingutil/ext/Bind.kt | 23 +++++- .../kotlin/net/eksb/kswingutil/ext/Ext.kt | 30 ++++++++ .../kotlin/net/eksb/kswingutil/mig/Mig.kt | 74 +++++++++++++++++++ .../eksb/kswingutil/property/PropertyExt.kt | 24 +++++- 6 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/net/eksb/kswingutil/SwingUtil.kt create mode 100644 src/main/kotlin/net/eksb/kswingutil/mig/Mig.kt diff --git a/build.gradle.kts b/build.gradle.kts index 80b132f..b60d737 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,24 +1,27 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { - kotlin("jvm") version "2.1.10" + kotlin("jvm") version "2.2.0" `maven-publish` // for publishToMavenLocal id("org.jetbrains.dokka-javadoc") version "2.0.0" } group = "net.eksb" -dependencies {} +dependencies { + api("com.miglayout:miglayout-swing:11.4.2") +} java { - targetCompatibility = JavaVersion.VERSION_17 - sourceCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } withSourcesJar() } kotlin { compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) + jvmTarget.set(JvmTarget.JVM_21) } } diff --git a/src/main/kotlin/net/eksb/kswingutil/SwingUtil.kt b/src/main/kotlin/net/eksb/kswingutil/SwingUtil.kt new file mode 100644 index 0000000..0d2166d --- /dev/null +++ b/src/main/kotlin/net/eksb/kswingutil/SwingUtil.kt @@ -0,0 +1,13 @@ +package net.eksb.kswingutil + +import javax.swing.UIManager + +fun systemLookAndFeel() { + try { + UIManager.setLookAndFeel( + UIManager.getSystemLookAndFeelClassName() + ) + } catch ( e:java.lang.Exception) { + System.err.println( "Error setting laf: ${e.message}" ) + } +} diff --git a/src/main/kotlin/net/eksb/kswingutil/ext/Bind.kt b/src/main/kotlin/net/eksb/kswingutil/ext/Bind.kt index a61fb0f..6147210 100644 --- a/src/main/kotlin/net/eksb/kswingutil/ext/Bind.kt +++ b/src/main/kotlin/net/eksb/kswingutil/ext/Bind.kt @@ -2,7 +2,10 @@ package net.eksb.kswingutil.ext import net.eksb.kswingutil.property.MutableProperty import net.eksb.kswingutil.property.Property +import java.awt.Color import java.awt.Component +import javax.swing.JComponent +import javax.swing.border.Border /** * One-way bind a [java.awt.Component] to a [Property]. @@ -82,4 +85,22 @@ fun Component.bind( // initialize component with the property's value propertyToComponent() -} \ No newline at end of file +} + +fun JComponent.bindTooltipText(property:Property) { + bind(property) { + toolTipText = it + } +} + +fun JComponent.bindBackground(property:Property) { + bind(property) { + background = it + } +} + +fun JComponent.bindBorder(property:Property) { + bind(property) { + border = it + } +} diff --git a/src/main/kotlin/net/eksb/kswingutil/ext/Ext.kt b/src/main/kotlin/net/eksb/kswingutil/ext/Ext.kt index 61ff344..4f1b551 100644 --- a/src/main/kotlin/net/eksb/kswingutil/ext/Ext.kt +++ b/src/main/kotlin/net/eksb/kswingutil/ext/Ext.kt @@ -5,6 +5,9 @@ import net.eksb.kswingutil.property.Property import net.eksb.kswingutil.ext.bind import java.awt.Component import java.awt.Container +import java.awt.Font +import java.awt.event.MouseAdapter +import java.awt.event.MouseEvent import javax.swing.BorderFactory import javax.swing.Box import javax.swing.DefaultComboBoxModel @@ -87,3 +90,30 @@ fun JComboBox.bind(property:MutableProperty) { fun Container.button(constraints:Any?=null, text:String?=null, block:JButton.()->Unit = {}):JButton = add(constraints, JButton(text),block) + +fun JComponent.onHover( + enter:(MouseEvent)->Unit, + exit:(MouseEvent)->Unit +) { + addMouseListener(object: MouseAdapter() { + override fun mouseEntered(e: MouseEvent) { + enter.invoke(e) + } + override fun mouseExited(e: MouseEvent) { + exit.invoke(e) + } + }) + +} +fun JComponent.hoverProperty(): Property = + MutableProperty(false).apply { + onHover( + enter = { e -> value = true }, + exit = { e -> value = false } + ) + } + +fun JComponent.bold() { + font = font.deriveFont(Font.BOLD) + repaint() +} diff --git a/src/main/kotlin/net/eksb/kswingutil/mig/Mig.kt b/src/main/kotlin/net/eksb/kswingutil/mig/Mig.kt new file mode 100644 index 0000000..76445e0 --- /dev/null +++ b/src/main/kotlin/net/eksb/kswingutil/mig/Mig.kt @@ -0,0 +1,74 @@ +package net.eksb.kswingutil.mig + +// Utility and extension functions for using MigLayout. See http://miglayout.com/ + +import java.awt.Container +import java.util.UUID + +import net.miginfocom.swing.* + +// Let users import net.eksb.kswingutil.mig.* and not have to also import net.miginfocom.layout.* +typealias LC = net.miginfocom.layout.LC +typealias AC = net.miginfocom.layout.AC +typealias CC = net.miginfocom.layout.CC + +fun Container.migLayout( + layoutConstraints:LC?=null, + colConstraints:AC?=null, + rowConstraints:AC?=null +) { + layout = MigLayout( + ( layoutConstraints ?: LC() ).debugIfEnv(), + colConstraints ?: AC(), + rowConstraints ?: AC() ) +} + +fun CC.heightMin():CC = height("min") + +/** + * Set a cell to take up the full height of the row + * without impacting the height of the row. + * Or, in other words, to take the full height of the tallest cell + * besides this one. + */ +fun CC.heightGrowMin():CC = growY().heightMin() + +fun AC.grow(w:Int) = grow(w.toFloat()) +fun AC.grow(w:Double) = grow(w.toFloat()) + +/** + * Set all columns to be the same width. + * + * This works by setting [AC.sizeGroup] to the same thing + * for all columns. + * + * After this method returns, the end, [AC.curIx] is the last index. + */ +fun AC.allColsSameWidth():AC = apply { + val group = UUID.randomUUID().toString() + (0 until count).forEach { index -> + index(index).sizeGroup(group) + } +} + +fun AC.alignRight() = align("right") +fun CC.alignRight() = alignX("right") +fun AC.alignLeft() = align("left") +fun CC.alignLeft() = alignX("left") +fun AC.alignCenter() = align("center") +fun CC.alignCenter() = alignX("center") + +private val debug:Boolean = System.getenv("SWINGUTIL_MIG_DEBUG")?.toBoolean() == true +/** + * Show debug decorations if $SWINGUTIL_MIG_DEBUG is set to "true" + * @param repaintMillis The new debug repaint interval. + */ +fun LC.debugIfEnv(repaintMillis:Int=300):LC = apply { + if ( debug ) { + debug(repaintMillis) + } +} + +fun LC.noInsets() = insetsAll("0") + +fun LC.noGridGap() = gridGap("0","0") diff --git a/src/main/kotlin/net/eksb/kswingutil/property/PropertyExt.kt b/src/main/kotlin/net/eksb/kswingutil/property/PropertyExt.kt index 6b90e95..6d91914 100644 --- a/src/main/kotlin/net/eksb/kswingutil/property/PropertyExt.kt +++ b/src/main/kotlin/net/eksb/kswingutil/property/PropertyExt.kt @@ -14,4 +14,26 @@ fun Property.map(transform:(T)->R):Property = /** * Get a [Property] that is true when this property is not null. */ -fun Property.mapNotNull():Property = map { it != null } \ No newline at end of file +fun Property.mapNotNull():Property = map { it != null } + +/** + * Create a Property that is updated to the value generated by [op] + * when any of the supplied [props] change. + * + * @param props Update this property when any of these properties change. + * @param op Calculates the value of this property + */ +fun propertyFrom(vararg props:Property<*>, op:()->T): Property = + MutableProperty(op()) + .apply { + props.forEach { prop -> + prop.addListener { _, _ -> + value = op() + } + } + } + +/** + * Get a `Property` that is `!this.value`. + */ +operator fun Property.not():Property = map { ! it }