Skip to content

Commit

Permalink
Replace IconButtonMetrics with IconButtonStyle for effective theming
Browse files Browse the repository at this point in the history
The existing metric defining approach, IconButtonMetrics, was restricting the theming of the IconButton component effectively. Thus, the 'IconButtonMetrics' was replaced by 'IconButtonStyle' and the necessary changes for the field change were implemented. This would assist in providing the same theme across different platforms. The 'IconButton' has been made theme-aware by allowing access to theme and style via properties. Changes were made in implementations concerning IntelliJTheme, BaseIntUiTheme, and ComponentShowcaseTab. File names were also appropriately renamed.
  • Loading branch information
fscarponi committed Oct 10, 2023
1 parent 63dd6f6 commit afaa7cd
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 103 deletions.
56 changes: 0 additions & 56 deletions core/src/main/kotlin/org/jetbrains/jewel/Button.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.defaultMinSize
Expand All @@ -25,7 +24,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.TextStyle
Expand All @@ -37,7 +35,6 @@ import org.jetbrains.jewel.CommonStateBitMask.Pressed
import org.jetbrains.jewel.foundation.Stroke
import org.jetbrains.jewel.foundation.border
import org.jetbrains.jewel.styling.ButtonStyle
import org.jetbrains.jewel.styling.IconButtonMetrics

@Composable
fun DefaultButton(
Expand Down Expand Up @@ -81,59 +78,6 @@ fun OutlinedButton(
)
}

@Composable
fun IconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
metrics: IconButtonMetrics = IntelliJTheme.iconButtonMetrics,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable (BoxScope.(ButtonState) -> Unit),
) {
var buttonState by remember(interactionSource) {
mutableStateOf(ButtonState.of(enabled = enabled))
}

remember(enabled) {
buttonState = buttonState.copy(enabled = enabled)
}

LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is PressInteraction.Press -> buttonState = buttonState.copy(pressed = true)
is PressInteraction.Cancel, is PressInteraction.Release ->
buttonState =
buttonState.copy(pressed = false)

is HoverInteraction.Enter -> buttonState = buttonState.copy(hovered = true)
is HoverInteraction.Exit -> buttonState = buttonState.copy(hovered = false)
is FocusInteraction.Focus -> buttonState = buttonState.copy(focused = true)
is FocusInteraction.Unfocus -> buttonState = buttonState.copy(focused = false)
}
}
}
val shape = RoundedCornerShape(metrics.cornerSize)
Box(
modifier = modifier
.defaultMinSize(metrics.minSize.width, metrics.minSize.height)
.clickable(
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = NoIndication,
)
.clip(shape)
.padding(metrics.padding),
propagateMinConstraints = true,
contentAlignment = Alignment.Center,
content = {
content(buttonState)
},
)
}

@Composable
private fun ButtonImpl(
onClick: () -> Unit,
Expand Down
79 changes: 79 additions & 0 deletions core/src/main/kotlin/org/jetbrains/jewel/IconButton.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.jetbrains.jewel

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.FocusInteraction
import androidx.compose.foundation.interaction.HoverInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.semantics.Role
import org.jetbrains.jewel.styling.IconButtonStyle

@Composable
fun IconButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
style: IconButtonStyle = IntelliJTheme.iconButtonStyle,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable (BoxScope.(ButtonState) -> Unit),
) {
var buttonState by remember(interactionSource) {
mutableStateOf(ButtonState.of(enabled = enabled))
}

remember(enabled) {
buttonState = buttonState.copy(enabled = enabled)
}

LaunchedEffect(interactionSource) {
interactionSource.interactions.collect { interaction ->
when (interaction) {
is PressInteraction.Press -> buttonState = buttonState.copy(pressed = true)
is PressInteraction.Cancel, is PressInteraction.Release ->
buttonState =
buttonState.copy(pressed = false)

is HoverInteraction.Enter -> buttonState = buttonState.copy(hovered = true)
is HoverInteraction.Exit -> buttonState = buttonState.copy(hovered = false)
is FocusInteraction.Focus -> buttonState = buttonState.copy(focused = true)
is FocusInteraction.Unfocus -> buttonState = buttonState.copy(focused = false)
}
}
}
val shape = RoundedCornerShape(style.metrics.cornerSize)
val background by style.colors.backgroundFor(buttonState)
Box(
modifier = modifier
.defaultMinSize(style.metrics.minSize.width, style.metrics.minSize.height)
.clickable(
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = NoIndication,
)
.clip(shape)
.padding(style.metrics.padding)
.background(background),
propagateMinConstraints = true,
contentAlignment = Alignment.Center,
content = {
content(buttonState)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.jetbrains.jewel.styling.DividerStyle
import org.jetbrains.jewel.styling.DropdownStyle
import org.jetbrains.jewel.styling.GroupHeaderStyle
import org.jetbrains.jewel.styling.HorizontalProgressBarStyle
import org.jetbrains.jewel.styling.IconButtonMetrics
import org.jetbrains.jewel.styling.IconButtonStyle
import org.jetbrains.jewel.styling.LabelledTextFieldStyle
import org.jetbrains.jewel.styling.LazyTreeStyle
import org.jetbrains.jewel.styling.LinkStyle
Expand Down Expand Up @@ -43,7 +43,7 @@ class IntelliJComponentStyling(
val textFieldStyle: TextFieldStyle,
val circularProgressStyle: CircularProgressStyle,
val tooltipStyle: TooltipStyle,
val iconButtonMetrics: IconButtonMetrics,
val iconButtonStyle: IconButtonStyle,
) {

override fun equals(other: Any?): Boolean {
Expand Down Expand Up @@ -72,7 +72,7 @@ class IntelliJComponentStyling(
if (textFieldStyle != other.textFieldStyle) return false
if (circularProgressStyle != other.circularProgressStyle) return false
if (tooltipStyle != other.tooltipStyle) return false
if (iconButtonMetrics != other.iconButtonMetrics) return false
if (iconButtonStyle != other.iconButtonStyle) return false

return true
}
Expand All @@ -98,7 +98,7 @@ class IntelliJComponentStyling(
result = 31 * result + textFieldStyle.hashCode()
result = 31 * result + circularProgressStyle.hashCode()
result = 31 * result + tooltipStyle.hashCode()
result = 31 * result + iconButtonMetrics.hashCode()
result = 31 * result + iconButtonStyle.hashCode()
return result
}

Expand All @@ -110,5 +110,5 @@ class IntelliJComponentStyling(
"labelledTextFieldStyle=$labelledTextFieldStyle, lazyTreeStyle=$lazyTreeStyle, linkStyle=$linkStyle, " +
"menuStyle=$menuStyle, outlinedButtonStyle=$outlinedButtonStyle, radioButtonStyle=$radioButtonStyle, " +
"scrollbarStyle=$scrollbarStyle, textAreaStyle=$textAreaStyle, textFieldStyle=$textFieldStyle, " +
"circularProgressStyle=$circularProgressStyle, tooltipStyle=$tooltipStyle)"
"circularProgressStyle=$circularProgressStyle, tooltipStyle=$tooltipStyle, iconButtonStyle=$iconButtonStyle)"
}
8 changes: 4 additions & 4 deletions core/src/main/kotlin/org/jetbrains/jewel/IntelliJTheme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import org.jetbrains.jewel.styling.DividerStyle
import org.jetbrains.jewel.styling.DropdownStyle
import org.jetbrains.jewel.styling.GroupHeaderStyle
import org.jetbrains.jewel.styling.HorizontalProgressBarStyle
import org.jetbrains.jewel.styling.IconButtonMetrics
import org.jetbrains.jewel.styling.IconButtonStyle
import org.jetbrains.jewel.styling.LabelledTextFieldStyle
import org.jetbrains.jewel.styling.LazyTreeStyle
import org.jetbrains.jewel.styling.LinkStyle
Expand All @@ -28,7 +28,7 @@ import org.jetbrains.jewel.styling.LocalDropdownStyle
import org.jetbrains.jewel.styling.LocalEditorTabStyle
import org.jetbrains.jewel.styling.LocalGroupHeaderStyle
import org.jetbrains.jewel.styling.LocalHorizontalProgressBarStyle
import org.jetbrains.jewel.styling.LocalIconButtonMetrics
import org.jetbrains.jewel.styling.LocalIconButtonStyle
import org.jetbrains.jewel.styling.LocalLabelledTextFieldStyle
import org.jetbrains.jewel.styling.LocalLazyTreeStyle
import org.jetbrains.jewel.styling.LocalLinkStyle
Expand Down Expand Up @@ -198,10 +198,10 @@ interface IntelliJTheme {
@Composable
@ReadOnlyComposable
get() = LocalTooltipStyle.current
val iconButtonMetrics: IconButtonMetrics
val iconButtonStyle: IconButtonStyle
@Composable
@ReadOnlyComposable
get() = LocalIconButtonMetrics.current
get() = LocalIconButtonStyle.current
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,43 @@ package org.jetbrains.jewel.styling

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.DpSize
import org.jetbrains.jewel.ButtonState

@Stable
interface IconButtonStyle {

val colors: IconButtonColors
val metrics: IconButtonMetrics
}

@Immutable
interface IconButtonColors {

val background: Color
val backgroundDisabled: Color
val backgroundFocused: Color
val backgroundPressed: Color
val backgroundHovered: Color

@Composable
fun backgroundFor(state: ButtonState) = rememberUpdatedState(
state.chooseValue(
normal = background,
disabled = backgroundDisabled,
focused = backgroundFocused,
pressed = backgroundPressed,
hovered = backgroundHovered,
active = background,
)
)
}

interface IconButtonMetrics {

Expand All @@ -12,6 +47,6 @@ interface IconButtonMetrics {
val minSize: DpSize
}

val LocalIconButtonMetrics = staticCompositionLocalOf<IconButtonMetrics> {
error("No outlined ButtonStyle provided")
val LocalIconButtonStyle = staticCompositionLocalOf<IconButtonStyle> {
error("No IconButtonStyle provided")
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ import org.jetbrains.jewel.intui.standalone.styling.IntUiGroupHeaderStyle
import org.jetbrains.jewel.intui.standalone.styling.IntUiHorizontalProgressBarColors
import org.jetbrains.jewel.intui.standalone.styling.IntUiHorizontalProgressBarMetrics
import org.jetbrains.jewel.intui.standalone.styling.IntUiHorizontalProgressBarStyle
import org.jetbrains.jewel.intui.standalone.styling.IntUiIconButtonColors
import org.jetbrains.jewel.intui.standalone.styling.IntUiIconButtonMetrics
import org.jetbrains.jewel.intui.standalone.styling.IntUiIconButtonStyle
import org.jetbrains.jewel.intui.standalone.styling.IntUiLabelledTextFieldColors
import org.jetbrains.jewel.intui.standalone.styling.IntUiLabelledTextFieldMetrics
import org.jetbrains.jewel.intui.standalone.styling.IntUiLabelledTextFieldStyle
Expand Down Expand Up @@ -173,7 +175,7 @@ internal fun createSwingIntUiComponentStyling(
circularProgressStyle = readCircularProgressStyle(theme.isDark),
tooltipStyle = readTooltipStyle(),
textFieldStyle = textFieldStyle,
iconButtonMetrics = IntUiIconButtonMetrics(),
iconButtonStyle = readIconButtonStyle(),
)
}

Expand Down Expand Up @@ -910,8 +912,8 @@ private fun readCircularProgressStyle(
?: if (isDark) Color(0xFF6F737A) else Color(0xFFA8ADBD),
)

private fun readTooltipStyle(): IntUiTooltipStyle {
return IntUiTooltipStyle(
private fun readTooltipStyle(): IntUiTooltipStyle =
IntUiTooltipStyle(
metrics = IntUiTooltipMetrics(),
colors = IntUiTooltipColors(
content = retrieveColorOrUnspecified("ToolTip.foreground"),
Expand All @@ -920,4 +922,15 @@ private fun readTooltipStyle(): IntUiTooltipStyle {
shadow = Color.Black.copy(alpha = .6f),
),
)
}

private fun readIconButtonStyle(): IntUiIconButtonStyle =
IntUiIconButtonStyle(
metrics = IntUiIconButtonMetrics(),
colors = IntUiIconButtonColors(
Color.Unspecified,
Color.Unspecified,
Color.Unspecified,
retrieveColorOrUnspecified("ActionButton.pressedBackground"),
retrieveColorOrUnspecified("ActionButton.hoverBackground"),
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import org.jetbrains.jewel.styling.LocalDropdownStyle
import org.jetbrains.jewel.styling.LocalEditorTabStyle
import org.jetbrains.jewel.styling.LocalGroupHeaderStyle
import org.jetbrains.jewel.styling.LocalHorizontalProgressBarStyle
import org.jetbrains.jewel.styling.LocalIconButtonMetrics
import org.jetbrains.jewel.styling.LocalIconButtonStyle
import org.jetbrains.jewel.styling.LocalLabelledTextFieldStyle
import org.jetbrains.jewel.styling.LocalLazyTreeStyle
import org.jetbrains.jewel.styling.LocalLinkStyle
Expand Down Expand Up @@ -234,7 +234,7 @@ fun BaseIntUiTheme(
LocalIndication provides NoIndication,
LocalCircularProgressStyle provides componentStyling.circularProgressStyle,
LocalTooltipStyle provides componentStyling.tooltipStyle,
LocalIconButtonMetrics provides componentStyling.iconButtonMetrics,
LocalIconButtonStyle provides componentStyling.iconButtonStyle,
) {
IntelliJTheme(theme, swingCompatMode, content)
}
Expand Down
Loading

0 comments on commit afaa7cd

Please sign in to comment.