From 2af35dce02863e3ed4aec79c3eac2c502e6d017a Mon Sep 17 00:00:00 2001 From: Fabrizio Scarponi <36624359+fscarponi@users.noreply.github.com> Date: Thu, 28 Dec 2023 11:03:58 +0100 Subject: [PATCH] Extend TabStrip API to support custom behaviours (#276) * Update TabData for flexible content * Update ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt Co-authored-by: Sebastiano Poggi * Update ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt Co-authored-by: Sebastiano Poggi * review * review * Add tab content spacing to the user interface The user interface has been updated to include tab content spacing. This is established by adding a new parameter, `tabContentSpacing`, in TabMetrics and adjusting and testing its implementation across various files like `IntUiTabStyling.kt`, `IntUiBridge.kt`, and `Tabs.kt`. This modification will ensure more consistency and control over the user interface design. The user interface api file has also been updated to reflect this change. * lint * api fix * Update int-ui-standalone APIs --- .../jewel/bridge/theme/IntUiBridge.kt | 22 ++++--- .../api/int-ui-standalone.api | 4 +- .../standalone/styling/IntUiTabStyling.kt | 43 ++++++------- .../samples/standalone/view/component/Tabs.kt | 47 +++++++++++++- ui/api/ui.api | 45 ++++++++----- .../jetbrains/jewel/ui/component/TabStrip.kt | 28 ++++----- .../org/jetbrains/jewel/ui/component/Tabs.kt | 63 ++++++++++++++----- .../jewel/ui/component/styling/TabStyling.kt | 25 ++++---- 8 files changed, 180 insertions(+), 97 deletions(-) diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/IntUiBridge.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/IntUiBridge.kt index ce27c2f81..758f52a75 100644 --- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/IntUiBridge.kt +++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/IntUiBridge.kt @@ -807,6 +807,7 @@ private fun readDefaultTabStyle(): TabStyle { .takeOrElse { 2.dp }, tabPadding = retrieveInsetsAsPaddingValues("TabbedPane.tabInsets"), closeContentGap = 4.dp, + tabContentSpacing = 4.dp, tabHeight = retrieveIntAsDpOrUnspecified("TabbedPane.tabHeight").takeOrElse { 24.dp }, ), icons = TabIcons(close = bridgePainterProvider("expui/general/closeSmall.svg")), @@ -816,11 +817,11 @@ private fun readDefaultTabStyle(): TabStyle { iconPressed = 1f, iconHovered = 1f, iconSelected = 1f, - labelNormal = 1f, - labelDisabled = 1f, - labelPressed = 1f, - labelHovered = 1f, - labelSelected = 1f, + contentNormal = 1f, + contentDisabled = 1f, + contentPressed = 1f, + contentHovered = 1f, + contentSelected = 1f, ), ) } @@ -856,6 +857,7 @@ private fun readEditorTabStyle(): TabStyle { .takeOrElse { 2.dp }, tabPadding = retrieveInsetsAsPaddingValues("TabbedPane.tabInsets"), closeContentGap = 4.dp, + tabContentSpacing = 4.dp, tabHeight = retrieveIntAsDpOrUnspecified("TabbedPane.tabHeight") .takeOrElse { 24.dp }, ), @@ -866,11 +868,11 @@ private fun readEditorTabStyle(): TabStyle { iconPressed = 1f, iconHovered = 1f, iconSelected = 1f, - labelNormal = .7f, - labelDisabled = .7f, - labelPressed = 1f, - labelHovered = 1f, - labelSelected = 1f, + contentNormal = .7f, + contentDisabled = .7f, + contentPressed = 1f, + contentHovered = 1f, + contentSelected = 1f, ), ) } diff --git a/int-ui/int-ui-standalone/api/int-ui-standalone.api b/int-ui/int-ui-standalone/api/int-ui-standalone.api index 0bbf459a1..66183c5b5 100644 --- a/int-ui/int-ui-standalone/api/int-ui-standalone.api +++ b/int-ui/int-ui-standalone/api/int-ui-standalone.api @@ -267,8 +267,8 @@ public final class org/jetbrains/jewel/intui/standalone/styling/IntUiTabStylingK public static synthetic fun default$default (Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion;FFFFFFFFFFILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha; public static final fun defaults (Lorg/jetbrains/jewel/ui/component/styling/TabIcons$Companion;Lorg/jetbrains/jewel/ui/painter/PainterProvider;)Lorg/jetbrains/jewel/ui/component/styling/TabIcons; public static synthetic fun defaults$default (Lorg/jetbrains/jewel/ui/component/styling/TabIcons$Companion;Lorg/jetbrains/jewel/ui/painter/PainterProvider;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabIcons; - public static final fun defaults-u_T4Ytw (Lorg/jetbrains/jewel/ui/component/styling/TabMetrics$Companion;FLandroidx/compose/foundation/layout/PaddingValues;FF)Lorg/jetbrains/jewel/ui/component/styling/TabMetrics; - public static synthetic fun defaults-u_T4Ytw$default (Lorg/jetbrains/jewel/ui/component/styling/TabMetrics$Companion;FLandroidx/compose/foundation/layout/PaddingValues;FFILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabMetrics; + public static final fun defaults-t6ZgxxQ (Lorg/jetbrains/jewel/ui/component/styling/TabMetrics$Companion;FLandroidx/compose/foundation/layout/PaddingValues;FFF)Lorg/jetbrains/jewel/ui/component/styling/TabMetrics; + public static synthetic fun defaults-t6ZgxxQ$default (Lorg/jetbrains/jewel/ui/component/styling/TabMetrics$Companion;FLandroidx/compose/foundation/layout/PaddingValues;FFFILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabMetrics; public static final fun editor (Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion;FFFFFFFFFF)Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha; public static synthetic fun editor$default (Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion;FFFFFFFFFFILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha; public static final fun getDefault (Lorg/jetbrains/jewel/ui/component/styling/TabColors$Companion;)Lorg/jetbrains/jewel/intui/standalone/styling/IntUiDefaultTabColorsFactory; diff --git a/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStyling.kt b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStyling.kt index c7e2b4cad..89e05bcab 100644 --- a/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStyling.kt +++ b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStyling.kt @@ -219,9 +219,10 @@ public fun TabMetrics.Companion.defaults( underlineThickness: Dp = 3.dp, tabPadding: PaddingValues = PaddingValues(horizontal = 8.dp), closeContentGap: Dp = 8.dp, + tabContentSpacing: Dp = 4.dp, tabHeight: Dp = 40.dp, ): TabMetrics = - TabMetrics(underlineThickness, tabPadding, tabHeight, closeContentGap) + TabMetrics(underlineThickness, tabPadding, tabHeight, tabContentSpacing, closeContentGap) public fun TabContentAlpha.Companion.default( iconNormal: Float = 1f, @@ -229,11 +230,11 @@ public fun TabContentAlpha.Companion.default( iconPressed: Float = iconNormal, iconHovered: Float = iconNormal, iconSelected: Float = iconNormal, - labelNormal: Float = iconNormal, - labelDisabled: Float = iconNormal, - labelPressed: Float = iconNormal, - labelHovered: Float = iconNormal, - labelSelected: Float = iconNormal, + contentNormal: Float = iconNormal, + contentDisabled: Float = iconNormal, + contentPressed: Float = iconNormal, + contentHovered: Float = iconNormal, + contentSelected: Float = iconNormal, ): TabContentAlpha = TabContentAlpha( iconNormal = iconNormal, @@ -241,11 +242,11 @@ public fun TabContentAlpha.Companion.default( iconPressed = iconPressed, iconHovered = iconHovered, iconSelected = iconSelected, - labelNormal = labelNormal, - labelDisabled = labelDisabled, - labelPressed = labelPressed, - labelHovered = labelHovered, - labelSelected = labelSelected, + contentNormal = contentNormal, + contentDisabled = contentDisabled, + contentPressed = contentPressed, + contentHovered = contentHovered, + contentSelected = contentSelected, ) public fun TabContentAlpha.Companion.editor( @@ -254,11 +255,11 @@ public fun TabContentAlpha.Companion.editor( iconPressed: Float = 1f, iconHovered: Float = iconPressed, iconSelected: Float = iconPressed, - labelNormal: Float = .9f, - labelDisabled: Float = labelNormal, - labelPressed: Float = 1f, - labelHovered: Float = labelPressed, - labelSelected: Float = labelPressed, + contentNormal: Float = .9f, + contentDisabled: Float = contentNormal, + contentPressed: Float = 1f, + contentHovered: Float = contentPressed, + contentSelected: Float = contentPressed, ): TabContentAlpha = TabContentAlpha( iconNormal = iconNormal, @@ -266,11 +267,11 @@ public fun TabContentAlpha.Companion.editor( iconPressed = iconPressed, iconHovered = iconHovered, iconSelected = iconSelected, - labelNormal = labelNormal, - labelDisabled = labelDisabled, - labelPressed = labelPressed, - labelHovered = labelHovered, - labelSelected = labelSelected, + contentNormal = contentNormal, + contentDisabled = contentDisabled, + contentPressed = contentPressed, + contentHovered = contentHovered, + contentSelected = contentSelected, ) public fun TabIcons.Companion.defaults( diff --git a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Tabs.kt b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Tabs.kt index 8fd2105ed..72df2dc21 100644 --- a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Tabs.kt +++ b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Tabs.kt @@ -2,6 +2,7 @@ package org.jetbrains.jewel.samples.standalone.view.component +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -14,16 +15,21 @@ 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.drawWithCache +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.jetbrains.jewel.foundation.theme.JewelTheme import org.jetbrains.jewel.samples.standalone.StandaloneSampleIcons import org.jetbrains.jewel.samples.standalone.viewmodel.View import org.jetbrains.jewel.ui.component.Icon import org.jetbrains.jewel.ui.component.IconButton +import org.jetbrains.jewel.ui.component.SimpleTabContent import org.jetbrains.jewel.ui.component.TabData import org.jetbrains.jewel.ui.component.TabStrip import org.jetbrains.jewel.ui.component.Text +import org.jetbrains.jewel.ui.painter.rememberResourcePainterProvider import org.jetbrains.jewel.ui.theme.defaultTabStyle +import org.jetbrains.jewel.ui.util.thenIf import kotlin.math.max @Composable @@ -48,7 +54,16 @@ private fun DefaultTabShowcase() { tabIds.mapIndexed { index, id -> TabData.Default( selected = index == selectedTabIndex, - label = "Default tab $id", + content = { + val iconProvider = + rememberResourcePainterProvider("icons/search.svg", StandaloneSampleIcons::class.java) + val icon by iconProvider.getPainter() + SimpleTabContent( + state = it, + title = "Default Tab $id", + icon = icon, + ) + }, onClose = { tabIds = tabIds.toMutableList().apply { removeAt(index) } if (selectedTabIndex >= index) { @@ -83,7 +98,35 @@ private fun EditorTabShowcase() { tabIds.mapIndexed { index, id -> TabData.Editor( selected = index == selectedTabIndex, - label = "Editor tab $id", + content = { tabState -> + Row { + SimpleTabContent( + modifier = Modifier, + state = tabState, + label = { Text("Editor tab $id") }, + icon = { + Icon( + resource = "icons/search.svg", + contentDescription = "SearchIcon", + iconClass = StandaloneSampleIcons::class.java, + modifier = Modifier.size(16.dp).tabContentAlpha(state = tabState), + tint = Color.Magenta, + ) + }, + ) + } + Box( + modifier = Modifier + .size(12.dp) + .thenIf(tabState.isHovered) { + drawWithCache { + onDrawBehind { + drawCircle(color = Color.Magenta.copy(alpha = .4f), radius = 6.dp.toPx()) + } + } + }, + ) + }, onClose = { tabIds = tabIds.toMutableList().apply { removeAt(index) } if (selectedTabIndex >= index) { diff --git a/ui/api/ui.api b/ui/api/ui.api index d26fbfe9d..ba31708f4 100644 --- a/ui/api/ui.api +++ b/ui/api/ui.api @@ -594,11 +594,18 @@ public final class org/jetbrains/jewel/ui/component/SplitLayoutKt { public static final fun VerticalSplitLayout-BssWTFQ (Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Landroidx/compose/ui/Modifier;JFFFFFFLandroidx/compose/runtime/Composer;II)V } +public abstract interface class org/jetbrains/jewel/ui/component/TabContentScope { + public abstract fun tabContentAlpha-A_ZS63w (Landroidx/compose/ui/Modifier;JLandroidx/compose/runtime/Composer;I)Landroidx/compose/ui/Modifier; +} + +public final class org/jetbrains/jewel/ui/component/TabContentScope$DefaultImpls { + public static fun tabContentAlpha-A_ZS63w (Lorg/jetbrains/jewel/ui/component/TabContentScope;Landroidx/compose/ui/Modifier;JLandroidx/compose/runtime/Composer;I)Landroidx/compose/ui/Modifier; +} + public abstract class org/jetbrains/jewel/ui/component/TabData { public static final field $stable I public abstract fun getClosable ()Z - public abstract fun getIcon ()Landroidx/compose/ui/graphics/painter/Painter; - public abstract fun getLabel ()Ljava/lang/String; + public abstract fun getContent ()Lkotlin/jvm/functions/Function4; public abstract fun getOnClick ()Lkotlin/jvm/functions/Function0; public abstract fun getOnClose ()Lkotlin/jvm/functions/Function0; public abstract fun getSelected ()Z @@ -606,12 +613,11 @@ public abstract class org/jetbrains/jewel/ui/component/TabData { public final class org/jetbrains/jewel/ui/component/TabData$Default : org/jetbrains/jewel/ui/component/TabData { public static final field $stable I - public fun (ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (ZLkotlin/jvm/functions/Function4;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (ZLkotlin/jvm/functions/Function4;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public fun getClosable ()Z - public fun getIcon ()Landroidx/compose/ui/graphics/painter/Painter; - public fun getLabel ()Ljava/lang/String; + public fun getContent ()Lkotlin/jvm/functions/Function4; public fun getOnClick ()Lkotlin/jvm/functions/Function0; public fun getOnClose ()Lkotlin/jvm/functions/Function0; public fun getSelected ()Z @@ -621,12 +627,11 @@ public final class org/jetbrains/jewel/ui/component/TabData$Default : org/jetbra public final class org/jetbrains/jewel/ui/component/TabData$Editor : org/jetbrains/jewel/ui/component/TabData { public static final field $stable I - public fun (ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (ZLjava/lang/String;Landroidx/compose/ui/graphics/painter/Painter;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (ZLkotlin/jvm/functions/Function4;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (ZLkotlin/jvm/functions/Function4;ZLkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public fun getClosable ()Z - public fun getIcon ()Landroidx/compose/ui/graphics/painter/Painter; - public fun getLabel ()Ljava/lang/String; + public fun getContent ()Lkotlin/jvm/functions/Function4; public fun getOnClick ()Lkotlin/jvm/functions/Function0; public fun getOnClose ()Lkotlin/jvm/functions/Function0; public fun getSelected ()Z @@ -704,6 +709,11 @@ public final class org/jetbrains/jewel/ui/component/TabStripState$Companion { public static synthetic fun of-zFr0jqg$default (Lorg/jetbrains/jewel/ui/component/TabStripState$Companion;ZZZZZILjava/lang/Object;)J } +public final class org/jetbrains/jewel/ui/component/TabsKt { + public static final fun SimpleTabContent-A5h6_LM (Lorg/jetbrains/jewel/ui/component/TabContentScope;Landroidx/compose/ui/Modifier;JLkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun SimpleTabContent-A5h6_LM (Lorg/jetbrains/jewel/ui/component/TabContentScope;Ljava/lang/String;JLandroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V +} + public final class org/jetbrains/jewel/ui/component/TextAreaKt { public static final fun TextArea (Landroidx/compose/ui/text/input/TextFieldValue;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZZLkotlin/jvm/functions/Function2;ZLorg/jetbrains/jewel/ui/Outline;Landroidx/compose/ui/text/input/VisualTransformation;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/KeyboardActions;ILkotlin/jvm/functions/Function1;Lorg/jetbrains/jewel/ui/component/styling/TextAreaStyle;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;III)V public static final fun TextArea (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZZLorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/ui/text/input/VisualTransformation;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/KeyboardActions;ILkotlin/jvm/functions/Function1;Lorg/jetbrains/jewel/ui/component/styling/TextAreaStyle;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;III)V @@ -1796,20 +1806,20 @@ public final class org/jetbrains/jewel/ui/component/styling/TabContentAlpha { public static final field $stable I public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion; public fun (FFFFFFFFFF)V + public final fun contentFor-UXw_FYk (JLandroidx/compose/runtime/Composer;I)Landroidx/compose/runtime/State; public fun equals (Ljava/lang/Object;)Z + public final fun getContentDisabled ()F + public final fun getContentHovered ()F + public final fun getContentNormal ()F + public final fun getContentPressed ()F + public final fun getContentSelected ()F public final fun getIconDisabled ()F public final fun getIconHovered ()F public final fun getIconNormal ()F public final fun getIconPressed ()F public final fun getIconSelected ()F - public final fun getLabelDisabled ()F - public final fun getLabelHovered ()F - public final fun getLabelNormal ()F - public final fun getLabelPressed ()F - public final fun getLabelSelected ()F public fun hashCode ()I public final fun iconFor-UXw_FYk (JLandroidx/compose/runtime/Composer;I)Landroidx/compose/runtime/State; - public final fun labelFor-UXw_FYk (JLandroidx/compose/runtime/Composer;I)Landroidx/compose/runtime/State; public fun toString ()Ljava/lang/String; } @@ -1832,9 +1842,10 @@ public final class org/jetbrains/jewel/ui/component/styling/TabIcons$Companion { public final class org/jetbrains/jewel/ui/component/styling/TabMetrics { public static final field $stable I public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/TabMetrics$Companion; - public synthetic fun (FLandroidx/compose/foundation/layout/PaddingValues;FFLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FLandroidx/compose/foundation/layout/PaddingValues;FFFLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun getCloseContentGap-D9Ej5fM ()F + public final fun getTabContentSpacing-D9Ej5fM ()F public final fun getTabHeight-D9Ej5fM ()F public final fun getTabPadding ()Landroidx/compose/foundation/layout/PaddingValues; public final fun getUnderlineThickness-D9Ej5fM ()F diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt index 5b3735313..3d8224e61 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TabStrip.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalLayoutDirection import org.jetbrains.jewel.foundation.GenerateDataFunctions import org.jetbrains.jewel.foundation.modifier.onHover @@ -50,8 +49,7 @@ public fun TabStrip( modifier = Modifier.horizontalScroll(scrollState) .scrollable( orientation = Orientation.Vertical, - reverseDirection = - ScrollableDefaults.reverseDirection( + reverseDirection = ScrollableDefaults.reverseDirection( LocalLayoutDirection.current, Orientation.Vertical, false, @@ -81,8 +79,7 @@ public fun TabStrip( public sealed class TabData { public abstract val selected: Boolean - public abstract val label: String - public abstract val icon: Painter? + public abstract val content: @Composable TabContentScope.(tabState: TabState) -> Unit public abstract val closable: Boolean public abstract val onClose: () -> Unit public abstract val onClick: () -> Unit @@ -91,8 +88,7 @@ public sealed class TabData { @GenerateDataFunctions public class Default( override val selected: Boolean, - override val label: String, - override val icon: Painter? = null, + override val content: @Composable TabContentScope.(tabState: TabState) -> Unit, override val closable: Boolean = true, override val onClose: () -> Unit = {}, override val onClick: () -> Unit = {}, @@ -102,8 +98,7 @@ public sealed class TabData { @GenerateDataFunctions public class Editor( override val selected: Boolean, - override val label: String, - override val icon: Painter? = null, + override val content: @Composable TabContentScope.(tabState: TabState) -> Unit, override val closable: Boolean = true, override val onClose: () -> Unit = {}, override val onClick: () -> Unit = {}, @@ -135,14 +130,13 @@ public value class TabStripState(public val state: ULong) : FocusableComponentSt pressed: Boolean = isPressed, hovered: Boolean = isHovered, active: Boolean = isActive, - ): TabStripState = - of( - enabled = enabled, - focused = focused, - pressed = pressed, - hovered = hovered, - active = active, - ) + ): TabStripState = of( + enabled = enabled, + focused = focused, + pressed = pressed, + hovered = hovered, + active = active, + ) override fun toString(): String = "${javaClass.simpleName}(isEnabled=$isEnabled, isFocused=$isFocused, isHovered=$isHovered, " + diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Tabs.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Tabs.kt index 41df851fe..fa8653a97 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Tabs.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Tabs.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.isTertiary @@ -48,6 +49,49 @@ import org.jetbrains.jewel.ui.painter.hints.Stateful import org.jetbrains.jewel.ui.theme.defaultTabStyle import org.jetbrains.jewel.ui.theme.editorTabStyle +public interface TabContentScope { + + @Composable + public fun Modifier.tabContentAlpha(state: TabState): Modifier = + this.alpha(JewelTheme.editorTabStyle.contentAlpha.contentFor(state).value) +} + +internal class TabContentScopeContainer : TabContentScope + +@Composable +public fun TabContentScope.SimpleTabContent( + title: String, + state: TabState, + icon: Painter?, + modifier: Modifier = Modifier, +) { + SimpleTabContent( + modifier = modifier, + label = { Text(title) }, + icon = icon?.let { { Icon(painter = icon, contentDescription = null) } }, + state = state, + ) +} + +@Composable +public fun TabContentScope.SimpleTabContent( + modifier: Modifier = Modifier, + state: TabState, + icon: (@Composable () -> Unit)? = null, + label: @Composable () -> Unit, +) { + Row( + modifier.tabContentAlpha(state), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = JewelTheme.defaultTabStyle.metrics.tabContentSpacing), + ) { + if (icon != null) { + icon() + } + label() + } +} + @Composable internal fun TabImpl( modifier: Modifier = Modifier, @@ -72,10 +116,7 @@ internal fun TabImpl( interactionSource.interactions.collect { interaction -> when (interaction) { is PressInteraction.Press -> tabState = tabState.copy(pressed = true) - is PressInteraction.Cancel, - is PressInteraction.Release, - -> tabState = tabState.copy(pressed = false) - + is PressInteraction.Cancel, is PressInteraction.Release -> tabState = tabState.copy(pressed = false) is HoverInteraction.Enter -> tabState = tabState.copy(hovered = true) is HoverInteraction.Exit -> tabState = tabState.copy(hovered = false) } @@ -90,9 +131,6 @@ internal fun TabImpl( .value.takeOrElse { LocalContentColor.current } CompositionLocalProvider(LocalContentColor provides resolvedContentColor) { - val labelAlpha by tabStyle.contentAlpha.labelFor(tabState) - val iconAlpha by tabStyle.contentAlpha.iconFor(tabState) - Row( modifier.height(tabStyle.metrics.tabHeight) .background(backgroundColor) @@ -123,15 +161,8 @@ internal fun TabImpl( horizontalArrangement = Arrangement.spacedBy(tabStyle.metrics.closeContentGap), verticalAlignment = Alignment.CenterVertically, ) { - tabData.icon?.let { icon -> - Image(modifier = Modifier.alpha(iconAlpha), painter = icon, contentDescription = null) - } + tabData.content(TabContentScopeContainer(), tabState) - Text( - modifier = Modifier.alpha(labelAlpha), - text = tabData.label, - color = tabStyle.colors.contentFor(tabState).value, - ) val showCloseIcon = when (tabData) { is TabData.Default -> tabData.closable @@ -166,7 +197,7 @@ internal fun TabImpl( ) .size(16.dp), painter = closePainter, - contentDescription = "Close tab ${tabData.label}", + contentDescription = "Close tab", ) } else if (tabData.closable) { Spacer(Modifier.size(16.dp)) diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/TabStyling.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/TabStyling.kt index 67a5a8d9b..35cf8d9b6 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/TabStyling.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/TabStyling.kt @@ -32,6 +32,7 @@ public class TabMetrics( public val underlineThickness: Dp, public val tabPadding: PaddingValues, public val tabHeight: Dp, + public val tabContentSpacing: Dp, public val closeContentGap: Dp, ) { @@ -121,11 +122,11 @@ public class TabContentAlpha( public val iconPressed: Float, public val iconHovered: Float, public val iconSelected: Float, - public val labelNormal: Float, - public val labelDisabled: Float, - public val labelPressed: Float, - public val labelHovered: Float, - public val labelSelected: Float, + public val contentNormal: Float, + public val contentDisabled: Float, + public val contentPressed: Float, + public val contentHovered: Float, + public val contentSelected: Float, ) { @Composable @@ -145,17 +146,17 @@ public class TabContentAlpha( ) @Composable - public fun labelFor(state: TabState): State = + public fun contentFor(state: TabState): State = rememberUpdatedState( when { - state.isSelected -> labelSelected + state.isSelected -> contentSelected else -> state.chooseValueIgnoreCompat( - normal = labelNormal, - disabled = labelDisabled, - pressed = labelPressed, - hovered = labelHovered, - active = labelNormal, + normal = contentNormal, + disabled = contentDisabled, + pressed = contentPressed, + hovered = contentHovered, + active = contentNormal, ) }, )