@@ -4,13 +4,11 @@ import io.github.tritium_launcher.launcher.connect
44import io.github.tritium_launcher.launcher.currentDpr
55import io.github.tritium_launcher.launcher.ui.theme.TColors
66import io.github.tritium_launcher.launcher.ui.theme.ThemeMngr
7+ import io.github.tritium_launcher.launcher.ui.widgets.pixel.PixelSkin
78import io.github.tritium_launcher.launcher.ui.widgets.pixel.pixelSkin
89import io.qt.Nullable
910import io.qt.core.QEvent
10- import io.qt.gui.QMoveEvent
11- import io.qt.gui.QPaintEvent
12- import io.qt.gui.QPainter
13- import io.qt.gui.QShowEvent
11+ import io.qt.gui.*
1412import io.qt.widgets.QPushButton
1513import io.qt.widgets.QStyle
1614import io.qt.widgets.QStyleOptionButton
@@ -23,11 +21,31 @@ import kotlin.math.abs
2321
2422/* *
2523 * Minecraft-styled push button.
24+ *
25+ * @param parent Parent widget.
26+ * @param tint Optional hex color string to tint the button (e.g. [TColors.Green]).
27+ * Preserves the original button's lightness gradients while applying the tint's
28+ * hue and saturation.
2629 */
2730class TPushButton (
28- parent : QWidget ? = null
31+ parent : QWidget ? = null ,
32+ tint : String? = null
2933) : QPushButton(parent) {
3034 private var lastDpr: Double = - 1.0
35+
36+ /* *
37+ * Optional hex color string to tint the button.
38+ * Preserves lightness from the theme button colors while applying this color's
39+ * hue and saturation.
40+ */
41+ var tint: String? = tint
42+ set(value) {
43+ if (field != value) {
44+ field = value
45+ update()
46+ }
47+ }
48+
3149 /* *
3250 * Additional Y offset applied to the button label.
3351 *
@@ -66,7 +84,8 @@ class TPushButton(
6684 val dpr = currentDpr(this )
6785 handleDprChange(dpr)
6886
69- val bg = skin.render(state.key, w, h, dpr)
87+ val s = if (tint != null ) getTintedSkin(tint!! ) else skin
88+ val bg = s.render(state.key, w, h, dpr)
7089
7190 if (! bg.isNull) {
7291 painter.drawPixmap(0 , 0 , bg)
@@ -102,6 +121,8 @@ class TPushButton(
102121 QEvent .Type .DevicePixelRatioChange ,
103122 QEvent .Type .ScreenChangeInternal -> {
104123 skin.clearCache(disposePixmaps = true )
124+ tintedSkins.values.forEach { it.clearCache(disposePixmaps = true ) }
125+ tintedSkins.clear()
105126 lastDpr = - 1.0
106127 update()
107128 }
@@ -132,6 +153,7 @@ class TPushButton(
132153 private fun handleDprChange (dpr : Double ) {
133154 if (lastDpr < 0.0 || abs(lastDpr - dpr) > 0.001 ) {
134155 skin.clearCache(disposePixmaps = true )
156+ tintedSkins.values.forEach { it.clearCache(disposePixmaps = true ) }
135157 lastDpr = dpr
136158 update()
137159 }
@@ -145,17 +167,98 @@ class TPushButton(
145167 companion object {
146168 private val scope = CoroutineScope (Dispatchers .Main + SupervisorJob ())
147169 private var skin = buildSkin()
170+ private val tintedSkins = mutableMapOf<String , PixelSkin >()
148171
149172 init {
150173 scope.launch {
151174 ThemeMngr .currentThemeId.collect {
152175 val prev = skin
153176 skin = buildSkin()
154177 prev.clearCache(disposePixmaps = true )
178+ tintedSkins.values.forEach { it.clearCache(disposePixmaps = true ) }
179+ tintedSkins.clear()
155180 }
156181 }
157182 }
158183
184+ /* *
185+ * Return the tinted skin for [tintHex], building it on first use.
186+ */
187+ private fun getTintedSkin (tintHex : String ): PixelSkin =
188+ tintedSkins.getOrPut(tintHex) { buildTintedSkin(tintHex) }
189+
190+ /* *
191+ * Apply [tintHex] hue and saturation to each button color while preserving
192+ * the original lightness.
193+ */
194+ private fun buildTintedSkin (tintHex : String ) = pixelSkin {
195+ pixelSize = 2
196+ palette {
197+ color(" border" , tintColor(TColors .Button0 , tintHex))
198+ color(" shadow" , tintColor(TColors .Button1 , tintHex))
199+ color(" primary" , tintColor(TColors .Button2 , tintHex))
200+ color(" bright" , tintColor(TColors .Button3 , tintHex))
201+ color(" disabled" , TColors .ButtonDisabled0 )
202+ color(" disabledBorder" , TColors .ButtonDisabled1 )
203+ }
204+
205+ state(" normal" ) {
206+ draw {
207+ val p = px
208+ val w = width
209+ val h = height
210+ fillRect(0 , 0 , w, h, " border" )
211+ fillRect(p, p, w - p * 2 , h - p * 2 , " primary" )
212+ fillRect(p, p, w - p * 2 , p, " bright" )
213+ fillRect(p, p, p, h - p * 5 , " bright" )
214+ fillRect(w - p * 2 , p, p, h - p * 5 , " bright" )
215+ fillRect(p, h - p * 4 , w - p * 2 , p, " bright" )
216+ fillRect(p, h - p * 3 , w - p * 2 , p * 2 , " shadow" )
217+ }
218+ }
219+
220+ state(" pressed" ) {
221+ draw {
222+ val p = px
223+ val w = width
224+ val h = height
225+ fillRect(0 , p, w, h - p, " border" )
226+ fillRect(p, p + 2 , w - p * 2 , h - p - 4 , " primary" )
227+ fillRect(p, p + 2 , w - p * 2 , p, " bright" )
228+ fillRect(p, p + 2 , p, h - p * 4 , " bright" )
229+ fillRect(w - p * 2 , p + 2 , p, h - p * 4 , " bright" )
230+ fillRect(p, h - p * 2 , w - p * 2 , p, " bright" )
231+ }
232+ }
233+
234+ state(" disabled" ) {
235+ draw {
236+ val p = px
237+ val w = width
238+ val h = height
239+ fillRect(0 , 0 , w, h, " disabledBorder" )
240+ fillRect(p, p, w - p * 2 , h - p * 2 , " disabled" )
241+ fillRect(p, p, w - p * 2 , p, " disabled" )
242+ fillRect(p, p, p, h - p * 3 , " disabled" )
243+ fillRect(w - p * 2 , p, p, h - p * 3 , " disabled" )
244+ fillRect(p, h - p * 2 , w - p * 2 , p, " disabled" )
245+ }
246+ }
247+ }
248+
249+ /* *
250+ * Replace the lightness of [originalHex] with the hue+saturation of [tintHex].
251+ */
252+ private fun tintColor (originalHex : String , tintHex : String ): String {
253+ val src = QColor (originalHex)
254+ val t = QColor (tintHex)
255+ val l = src.lightness()
256+ val h = t.hslHue()
257+ val s = t.hslSaturation()
258+ val effectiveHue = if (h < 0 ) src.hslHue() else h
259+ return QColor .fromHsl(effectiveHue, s, l).name()
260+ }
261+
159262 /* *
160263 * Build Sprite
161264 */
@@ -214,7 +317,7 @@ class TPushButton(
214317 }
215318 }
216319
217- operator fun invoke (parent : QWidget ? = null, block : TPushButton .() -> Unit = {}): TPushButton =
218- TPushButton (parent).apply (block)
320+ operator fun invoke (parent : QWidget ? = null, tint : String? = null, block : TPushButton .() -> Unit = {}): TPushButton =
321+ TPushButton (parent, tint ).apply (block)
219322 }
220323}
0 commit comments