diff --git a/ide-laf-bridge/api/ide-laf-bridge.api b/ide-laf-bridge/api/ide-laf-bridge.api index b2bb465e5..f86286c20 100644 --- a/ide-laf-bridge/api/ide-laf-bridge.api +++ b/ide-laf-bridge/api/ide-laf-bridge.api @@ -25,6 +25,7 @@ public final class org/jetbrains/jewel/bridge/BridgeUtilsKt { public static final fun retrieveArcAsCornerSize (Ljava/lang/String;)Landroidx/compose/foundation/shape/CornerSize; public static final fun retrieveArcAsCornerSizeOrDefault (Ljava/lang/String;Landroidx/compose/foundation/shape/CornerSize;)Landroidx/compose/foundation/shape/CornerSize; public static final fun retrieveArcAsCornerSizeWithFallbacks ([Ljava/lang/String;)Landroidx/compose/foundation/shape/CornerSize; + public static final fun retrieveColor-0YGnOg8 (Ljava/lang/String;ZJJ)J public static final fun retrieveColor-4WTKRHQ (Ljava/lang/String;J)J public static final fun retrieveColorOrNull (Ljava/lang/String;)Landroidx/compose/ui/graphics/Color; public static final fun retrieveColorOrUnspecified (Ljava/lang/String;)J @@ -139,8 +140,16 @@ public final class org/jetbrains/jewel/bridge/theme/IntUiBridgeKt { } public final class org/jetbrains/jewel/bridge/theme/ScrollbarBridgeKt { - public static final fun defaults-6ksGUsA (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;JJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; - public static synthetic fun defaults-6ksGUsA$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;JJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static final fun default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static final fun macOs-TZvXluI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static synthetic fun macOs-TZvXluI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static final fun macOs-fYp4AQw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static synthetic fun macOs-fYp4AQw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun windowsAndLinux-TZvXluI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static synthetic fun windowsAndLinux-TZvXluI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static final fun windowsAndLinux-tYhzLtE (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static synthetic fun windowsAndLinux-tYhzLtE$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; } public final class org/jetbrains/jewel/bridge/theme/SwingBridgeThemeKt { diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeUtils.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeUtils.kt index 6f2655a06..5682ffdb0 100644 --- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeUtils.kt +++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeUtils.kt @@ -48,12 +48,19 @@ public fun retrieveColor( default: Color, ): Color = retrieveColorOrNull(key) ?: default +public fun retrieveColor( + key: String, + isDark: Boolean, + default: Color, + defaultDark: Color, +): Color = retrieveColorOrNull(key) ?: if (isDark) defaultDark else default + public fun retrieveColorOrNull(key: String): Color? = JBColor.namedColorOrNull(key)?.toComposeColor() public fun retrieveColorOrUnspecified(key: String): Color { val color = retrieveColorOrNull(key) if (color == null) { - logger.warn("Color with key \"$key\" not found, fallback to 'Color.Unspecified'") + logger.debug("Color with key \"$key\" not found, fallback to 'Color.Unspecified'") } return color ?: Color.Unspecified } @@ -98,9 +105,8 @@ public fun retrieveInsetsAsPaddingValues( ): PaddingValues = UIManager.getInsets(key)?.toPaddingValues() ?: default ?: keyNotFound(key, "Insets") /** - * Converts a [Insets] to [PaddingValues]. If the receiver is a [JBInsets] - * instance, this function delegates to the specific [toPaddingValues] for - * it, which is scaling-aware. + * Converts a [Insets] to [PaddingValues]. If the receiver is a [JBInsets] instance, this function delegates to the + * specific [toPaddingValues] for it, which is scaling-aware. */ public fun Insets.toPaddingValues(): PaddingValues = if (this is JBInsets) { @@ -110,26 +116,22 @@ public fun Insets.toPaddingValues(): PaddingValues = } /** - * Converts a [JBInsets] to [PaddingValues], in a scaling-aware way. This - * means that the resulting [PaddingValues] will be constructed from the - * [JBInsets.getUnscaled] values, treated as [Dp]. This avoids double - * scaling. + * Converts a [JBInsets] to [PaddingValues], in a scaling-aware way. This means that the resulting [PaddingValues] will + * be constructed from the [JBInsets.getUnscaled] values, treated as [Dp]. This avoids double scaling. */ @Suppress("ktlint:standard:function-signature") // False positive public fun JBInsets.toPaddingValues(): PaddingValues = PaddingValues(unscaled.left.dp, unscaled.top.dp, unscaled.right.dp, unscaled.bottom.dp) /** - * Converts a [Dimension] to [DpSize]. If the receiver is a [JBDimension] - * instance, this function delegates to the specific [toDpSize] for it, - * which is scaling-aware. + * Converts a [Dimension] to [DpSize]. If the receiver is a [JBDimension] instance, this function delegates to the + * specific [toDpSize] for it, which is scaling-aware. */ public fun Dimension.toDpSize(): DpSize = if (this is JBDimension) toDpSize() else DpSize(width.dp, height.dp) /** - * Converts a [JBDimension] to [DpSize], in a scaling-aware way. This means - * that the resulting [DpSize] will be constructed by first obtaining the - * unscaled values. This avoids double scaling. + * Converts a [JBDimension] to [DpSize], in a scaling-aware way. This means that the resulting [DpSize] will be + * constructed by first obtaining the unscaled values. This avoids double scaling. */ public fun JBDimension.toDpSize(): DpSize { val scaleFactor = scale(1f) @@ -182,7 +184,8 @@ public fun retrieveTextStyle( val jbFont = JBFont.create(lafFont, false) val derivedFont = - jbFont.let { if (bold) it.asBold() else it.asPlain() } + jbFont + .let { if (bold) it.asBold() else it.asPlain() } .let { if (fontStyle == FontStyle.Italic) it.asItalic() else it } return TextStyle( diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/MacScrollbarHelper.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/MacScrollbarHelper.kt deleted file mode 100644 index f3d3296f0..000000000 --- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/MacScrollbarHelper.kt +++ /dev/null @@ -1,163 +0,0 @@ -package org.jetbrains.jewel.bridge - -import com.intellij.openapi.util.SystemInfoRt -import com.intellij.ui.mac.foundation.Foundation -import com.intellij.ui.mac.foundation.Foundation.NSAutoreleasePool -import com.intellij.ui.mac.foundation.ID -import com.sun.jna.Callback -import com.sun.jna.Pointer -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import org.jetbrains.jewel.bridge.theme.defaults -import org.jetbrains.jewel.foundation.util.myLogger -import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility -import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior - -internal object MacScrollbarHelper { - private val _scrollbarVisibilityStyleFlow = MutableStateFlow(scrollbarVisibility) - val scrollbarVisibilityStyleFlow: StateFlow = _scrollbarVisibilityStyleFlow - - private val _trackClickBehaviorFlow = MutableStateFlow(trackClickBehavior) - val trackClickBehaviorFlow: StateFlow = _trackClickBehaviorFlow - - init { - if (SystemInfoRt.isMac) { - initNotificationObserver() - } - } - - val trackClickBehavior: TrackClickBehavior - get() { - if (!SystemInfoRt.isMac) { - return TrackClickBehavior.JumpToSpot - } - - val pool = NSAutoreleasePool() - try { - return readMacScrollbarBehavior() - } finally { - pool.drain() - } - } - - val scrollbarVisibility: ScrollbarVisibility - get() { - if (!SystemInfoRt.isMac) { - return ScrollbarVisibility.AlwaysVisible - } - - val pool = NSAutoreleasePool() - try { - return readMacScrollbarStyle() - } catch (ignore: Throwable) { - } finally { - pool.drain() - } - return ScrollbarVisibility.AlwaysVisible - } - - private fun initNotificationObserver() { - val pool = NSAutoreleasePool() - - val delegateClass = - Foundation.allocateObjcClassPair(Foundation.getObjcClass("NSObject"), "NSScrollerChangesObserver") - if (ID.NIL != delegateClass) { - if (!addScrollbarVisibilityChangeListener(delegateClass)) { - myLogger().error("Cannot add observer method") - } - - if (!addTrackClickBehaviorChangeListener(delegateClass)) { - myLogger().error("Cannot add observer method") - } - Foundation.registerObjcClassPair(delegateClass) - } - val delegate = Foundation.invoke("NSScrollerChangesObserver", "new") - - try { - var center = Foundation.invoke("NSNotificationCenter", "defaultCenter") - Foundation.invoke( - center, - "addObserver:selector:name:object:", - delegate, - Foundation.createSelector("handleScrollerStyleChanged:"), - Foundation.nsString("NSPreferredScrollerStyleDidChangeNotification"), - ID.NIL, - ) - - center = Foundation.invoke("NSDistributedNotificationCenter", "defaultCenter") - Foundation.invoke( - center, - "addObserver:selector:name:object:", - delegate, - Foundation.createSelector("handleBehaviorChanged:"), - Foundation.nsString("AppleNoRedisplayAppearancePreferenceChanged"), - ID.NIL, - 2, // NSNotificationSuspensionBehaviorCoalesce - ) - } finally { - pool.drain() - } - } - - private val APPEARANCE_CALLBACK: Callback = - object : Callback { - @Suppress("UNUSED_PARAMETER", "unused") - @SuppressWarnings("UnusedDeclaration") - fun callback( - self: ID?, - selector: Pointer?, - event: ID?, - ) { - _scrollbarVisibilityStyleFlow.tryEmit(scrollbarVisibility) - } - } - - private val BEHAVIOR_CALLBACK: Callback = - object : Callback { - @Suppress("UNUSED_PARAMETER", "unused") - @SuppressWarnings("UnusedDeclaration") - fun callback( - self: ID?, - selector: Pointer?, - event: ID?, - ) { - _trackClickBehaviorFlow.tryEmit(trackClickBehavior) - } - } - - private fun readMacScrollbarBehavior(): TrackClickBehavior { - val defaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults") - Foundation.invoke(defaults, "synchronize") - return Foundation - .invoke(defaults, "boolForKey:", Foundation.nsString("AppleScrollerPagingBehavior")) - .run { if (toInt() == 1) TrackClickBehavior.JumpToSpot else TrackClickBehavior.NextPage } - } - - private fun readMacScrollbarStyle(): ScrollbarVisibility { - val nsScroller = Foundation.invoke(Foundation.getObjcClass("NSScroller"), "preferredScrollerStyle") - - val visibility: ScrollbarVisibility = - if (1 == nsScroller.toInt()) { - ScrollbarVisibility.WhenScrolling.Companion.defaults() - } else { - ScrollbarVisibility.AlwaysVisible - } - return visibility - } - - private fun addScrollbarVisibilityChangeListener(delegateClass: ID?) = - Foundation.addMethod( - delegateClass, - Foundation.createSelector("handleScrollerStyleChanged:"), - APPEARANCE_CALLBACK, - "v@", - ) - - private fun addTrackClickBehaviorChangeListener(delegateClass: ID?) = - Foundation.addMethod( - delegateClass, - Foundation.createSelector("handleBehaviorChanged:"), - BEHAVIOR_CALLBACK, - "v@", - ) -} diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/ScrollbarHelper.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/ScrollbarHelper.kt new file mode 100644 index 000000000..cd588c8d4 --- /dev/null +++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/ScrollbarHelper.kt @@ -0,0 +1,191 @@ +package org.jetbrains.jewel.bridge + +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.util.SystemInfoRt +import com.intellij.ui.mac.foundation.Foundation +import com.intellij.ui.mac.foundation.ID +import com.sun.jna.Callback +import com.sun.jna.Pointer +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import org.jetbrains.jewel.bridge.theme.default +import org.jetbrains.jewel.bridge.theme.macOs +import org.jetbrains.jewel.foundation.util.myLogger +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior +import org.jetbrains.skiko.OS +import org.jetbrains.skiko.hostOs + +internal interface ScrollbarHelper { + val scrollbarVisibilityStyleFlow: StateFlow + val trackClickBehaviorFlow: StateFlow + + companion object { + @JvmStatic + fun getInstance(): ScrollbarHelper = if (hostOs == OS.MacOS) service() else DummyScrollbarHelper + } +} + +@Service(Service.Level.APP) +private class MacScrollbarHelperImpl : Callback, ScrollbarHelper { + private val logger = myLogger() + + private val _scrollbarVisibilityStyleFlow = + MutableStateFlow(ScrollbarVisibility.AlwaysVisible.default()) + override val scrollbarVisibilityStyleFlow: StateFlow = _scrollbarVisibilityStyleFlow + + private val _trackClickBehaviorFlow = MutableStateFlow(TrackClickBehavior.JumpToSpot) + override val trackClickBehaviorFlow: StateFlow = _trackClickBehaviorFlow + + init { + if (hostOs != OS.MacOS) { + logger.error("${javaClass.simpleName} should only be initialized on macOS.") + } else { + callback(null, null, null) + + listenToTrackClickBehaviorChange() + listenToScrollbarVisibilityChange() + } + } + + private fun listenToTrackClickBehaviorChange() { + callMac { + // Copied from MacScrollBarUI + Foundation.invoke( + Foundation.invoke("NSDistributedNotificationCenter", "defaultCenter"), + "addObserver:selector:name:object:", + createDelegate( + "JewelScrollbarTrackClickBehaviorObserver", + Foundation.createSelector("handleBehaviorChanged:"), + this, + ), + Foundation.createSelector("handleBehaviorChanged:"), + Foundation.nsString("AppleNoRedisplayAppearancePreferenceChanged"), + ID.NIL, + 2, // NSNotificationSuspensionBehaviorCoalesce + ) + } + } + + private fun listenToScrollbarVisibilityChange() { + callMac { + // Copied from MacScrollBarUI + Foundation.invoke( + Foundation.invoke("NSNotificationCenter", "defaultCenter"), + "addObserver:selector:name:object:", + createDelegate( + "JewelScrollbarVisibilityObserver", + Foundation.createSelector("handleScrollerStyleChanged:"), + this, + ), + Foundation.createSelector("handleScrollerStyleChanged:"), + Foundation.nsString("NSPreferredScrollerStyleDidChangeNotification"), + ID.NIL, + ) + } + } + + @Suppress("unused", "UNUSED_PARAMETER") + fun callback( + self: ID?, + selector: Pointer?, + event: ID?, + ) { + readTrackClickBehavior() + readScrollbarVisibility() + } + + private fun readTrackClickBehavior() { + callMac { + // Inspired from MacScrollBarUI + val userDefaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults") + Foundation.invoke(userDefaults, "synchronize") + val isJumpToPage = + Foundation.invoke( + // id = + userDefaults, + // selector = + "boolForKey:", + // ...args = + Foundation.nsString("AppleScrollerPagingBehavior"), + ) + .booleanValue() + + val behavior = + if (isJumpToPage) { + TrackClickBehavior.JumpToSpot + } else { + TrackClickBehavior.NextPage + } + + logger.debug("Scrollbar track click behavior: $behavior") + _trackClickBehaviorFlow.value = behavior + } + } + + private fun readScrollbarVisibility() { + callMac { + // Inspired from MacScrollBarUI + val isOverlayStyle = + Foundation.invoke( + // id= + Foundation.getObjcClass("NSScroller"), + // selector= + "preferredScrollerStyle", + ) + .booleanValue() + + val visibility = + if (isOverlayStyle) { + ScrollbarVisibility.WhenScrolling.macOs() + } else { + ScrollbarVisibility.AlwaysVisible.macOs() + } + + logger.debug("Scrollbar visibility style: $visibility") + _scrollbarVisibilityStyleFlow.value = visibility + } + } + + // Copied from MacScrollBarUI + @Suppress("detekt:TooGenericExceptionCaught") // Copied from IJP + private fun callMac(producer: () -> T?): T? { + if (!SystemInfoRt.isMac) { + return null + } + + val pool = Foundation.NSAutoreleasePool() + try { + return producer() + } catch (e: Throwable) { + logger.warn(e) + } finally { + pool.drain() + } + return null + } + + // Copied from MacScrollBarUI + private fun createDelegate( + name: String, + pointer: Pointer, + callback: Callback, + ): ID { + val delegateClass = Foundation.allocateObjcClassPair(Foundation.getObjcClass("NSObject"), name) + if (ID.NIL != delegateClass) { + if (!Foundation.addMethod(delegateClass, pointer, callback, "v@")) { + @Suppress("detekt:TooGenericExceptionThrown") // Copied from IJP + throw RuntimeException("Cannot add observer method") + } + Foundation.registerObjcClassPair(delegateClass) + } + return Foundation.invoke(name, "new") + } +} + +private object DummyScrollbarHelper : ScrollbarHelper { + override val scrollbarVisibilityStyleFlow: StateFlow = + MutableStateFlow(ScrollbarVisibility.AlwaysVisible.default()) + override val trackClickBehaviorFlow: StateFlow = MutableStateFlow(TrackClickBehavior.JumpToSpot) +} diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/SwingBridgeService.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/SwingBridgeService.kt index b27c3455c..53e99aacc 100644 --- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/SwingBridgeService.kt +++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/SwingBridgeService.kt @@ -19,14 +19,17 @@ import kotlin.time.Duration.Companion.milliseconds @Service(Level.APP) internal class SwingBridgeService(scope: CoroutineScope) { + private val scrollbarHelper = ScrollbarHelper.getInstance() + internal val currentBridgeThemeData: StateFlow = combine( IntelliJApplication.lookAndFeelChangedFlow(scope), - MacScrollbarHelper.scrollbarVisibilityStyleFlow, - MacScrollbarHelper.trackClickBehaviorFlow, + scrollbarHelper.scrollbarVisibilityStyleFlow, + scrollbarHelper.trackClickBehaviorFlow, ) { _, _, _ -> tryGettingThemeData() - }.stateIn(scope, SharingStarted.Eagerly, BridgeThemeData.DEFAULT) + } + .stateIn(scope, SharingStarted.Eagerly, BridgeThemeData.DEFAULT) private suspend fun tryGettingThemeData(): BridgeThemeData { var counter = 0 @@ -43,16 +46,10 @@ internal class SwingBridgeService(scope: CoroutineScope) { private fun readThemeData(): BridgeThemeData { val themeDefinition = createBridgeThemeDefinition() - return BridgeThemeData( - themeDefinition = createBridgeThemeDefinition(), - componentStyling = createBridgeComponentStyling(themeDefinition), - ) + return BridgeThemeData(themeDefinition = createBridgeThemeDefinition(), componentStyling = createBridgeComponentStyling(themeDefinition)) } - internal data class BridgeThemeData( - val themeDefinition: ThemeDefinition, - val componentStyling: ComponentStyling, - ) { + internal data class BridgeThemeData(val themeDefinition: ThemeDefinition, val componentStyling: ComponentStyling) { companion object { val DEFAULT = run { @@ -65,10 +62,7 @@ internal class SwingBridgeService(scope: CoroutineScope) { consoleTextStyle = monospaceTextStyle, ) - BridgeThemeData( - themeDefinition = themeDefinition, - componentStyling = createBridgeComponentStyling(themeDefinition), - ) + BridgeThemeData(themeDefinition = themeDefinition, componentStyling = createBridgeComponentStyling(themeDefinition)) } } } 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 da3841968..e60e31764 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 @@ -968,7 +968,7 @@ private fun readTextFieldStyle(): TextFieldStyle { TextFieldMetrics( cornerSize = CornerSize(DarculaUIUtil.COMPONENT_ARC.dp / 2), contentPadding = PaddingValues(horizontal = 9.dp, vertical = 2.dp), - minSize = DpSize(minimumSize.width, minimumSize.height), + minSize = DpSize(144.dp, minimumSize.height), borderWidth = DarculaUIUtil.LW.dp, ), ) diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/ScrollbarBridge.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/ScrollbarBridge.kt index 1121348cf..870b3eac8 100644 --- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/ScrollbarBridge.kt +++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/theme/ScrollbarBridge.kt @@ -3,10 +3,10 @@ package org.jetbrains.jewel.bridge.theme import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.shape.CornerSize import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.takeOrElse +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import org.jetbrains.jewel.bridge.MacScrollbarHelper -import org.jetbrains.jewel.bridge.retrieveColorOrUnspecified +import org.jetbrains.jewel.bridge.ScrollbarHelper +import org.jetbrains.jewel.bridge.retrieveColor import org.jetbrains.jewel.ui.component.styling.ScrollbarColors import org.jetbrains.jewel.ui.component.styling.ScrollbarMetrics import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle @@ -24,185 +24,295 @@ internal fun readScrollbarStyle(isDark: Boolean): ScrollbarStyle = scrollbarVisibility = readScrollbarVisibility(), ) -private fun readScrollbarVisibility() = - if (hostOs.isMacOS) { - MacScrollbarHelper.scrollbarVisibility - } else { - ScrollbarVisibility.AlwaysVisible - } - private fun readScrollbarColors(isDark: Boolean) = if (hostOs.isMacOS) { readScrollbarMacColors(isDark) } else { - readScrollbarWinColors(isDark) - } - -private fun readTrackClickBehavior() = - if (hostOs.isMacOS) { - MacScrollbarHelper.trackClickBehavior - } else { - TrackClickBehavior.JumpToSpot + readScrollbarWindowsAndLinuxColors(isDark) } -private fun readScrollbarWinColors(isDark: Boolean): ScrollbarColors = +private fun readScrollbarMacColors(isDark: Boolean): ScrollbarColors = ScrollbarColors( thumbBackground = - readScrollBarColorForKey( - isDark, - "ScrollBar.thumbColor", - 0x33737373, - 0x47A6A6A6, - ), - thumbBackgroundHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.hoverThumbColor", - 0x47737373, - 0x59A6A6A6, - ), - thumbBackgroundPressed = - readScrollBarColorForKey( - isDark, - "ScrollBar.hoverThumbColor", - 0x47737373, - 0x59A6A6A6, + retrieveColor( + key = "ScrollBar.Mac.Transparent.thumbColor", + isDark = isDark, + default = Color(0x00000000), + defaultDark = Color(0x00808080), + ), + thumbBackgroundActive = + retrieveColor( + key = "ScrollBar.Mac.Transparent.hoverThumbColor", + isDark = isDark, + default = Color(0x80000000), + defaultDark = Color(0x8C808080), + ), + thumbOpaqueBackground = + retrieveColor( + key = "ScrollBar.Mac.thumbColor", + isDark = isDark, + default = Color(0x33000000), + defaultDark = Color(0x59808080), + ), + thumbOpaqueBackgroundHovered = + retrieveColor( + key = "ScrollBar.Mac.hoverThumbColor", + isDark = isDark, + default = Color(0x80000000), + defaultDark = Color(0x8C808080), ), thumbBorder = - readScrollBarColorForKey( - isDark, - "ScrollBar.thumbBorderColor", - 0x33595959, - 0x47383838, - ), - thumbBorderHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.hoverThumbBorderColor", - 0x47595959, - 0x59383838, - ), - thumbBorderPressed = - readScrollBarColorForKey( - isDark, - "ScrollBar.hoverThumbBorderColor", - 0x47595959, - 0x59383838, + retrieveColor( + key = "ScrollBar.Mac.Transparent.thumbBorderColor", + isDark = isDark, + default = Color(0x00000000), + defaultDark = Color(0x00262626), + ), + thumbBorderActive = + retrieveColor( + key = "ScrollBar.Mac.Transparent.hoverThumbBorderColor", + isDark = isDark, + default = Color(0x80000000), + defaultDark = Color(0x8C262626), + ), + thumbOpaqueBorder = + retrieveColor( + key = "ScrollBar.Mac.thumbBorderColor", + isDark = isDark, + default = Color(0x33000000), + defaultDark = Color(0x59262626), + ), + thumbOpaqueBorderHovered = + retrieveColor( + key = "ScrollBar.Mac.hoverThumbBorderColor", + isDark = isDark, + default = Color(0x80000000), + defaultDark = Color(0x8C262626), ), trackBackground = - readScrollBarColorForKey( - isDark, - "ScrollBar.Transparent.trackColor", - 0x00808080, - 0x00808080, - ), - trackBackgroundHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.Transparent.hoverTrackColor", - 0x1A808080, - 0x1A808080, + retrieveColor( + key = "ScrollBar.Mac.Transparent.trackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), + ), + trackBackgroundExpanded = + retrieveColor( + key = "ScrollBar.Mac.Transparent.hoverTrackColor", + isDark = isDark, + default = Color(0x1A808080), + defaultDark = Color(0x1A808080), + ), + trackOpaqueBackground = + retrieveColor( + key = "ScrollBar.Mac.trackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), + ), + trackOpaqueBackgroundHovered = + retrieveColor( + key = "ScrollBar.Mac.hoverTrackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), ), ) -private fun readScrollbarMacColors(isDark: Boolean): ScrollbarColors = +private fun readScrollbarWindowsAndLinuxColors(isDark: Boolean): ScrollbarColors = ScrollbarColors( thumbBackground = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.thumbColor", - 0x33000000, - 0x59808080, - ), - thumbBackgroundHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.hoverThumbColor", - 0x80000000, - 0x8C808080, - ), - thumbBackgroundPressed = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.hoverThumbColor", - 0x80000000, - 0x8C808080, + retrieveColor( + key = "ScrollBar.Transparent.thumbColor", + isDark = isDark, + default = Color(0x33737373), + defaultDark = Color(0x47A6A6A6), + ), + thumbBackgroundActive = + retrieveColor( + key = "ScrollBar.Transparent.hoverThumbColor", + isDark = isDark, + default = Color(0x47737373), + defaultDark = Color(0x59A6A6A6), + ), + thumbOpaqueBackground = + retrieveColor( + key = "ScrollBar.thumbColor", + isDark = isDark, + default = Color(0x33737373), + defaultDark = Color(0x47A6A6A6), + ), + thumbOpaqueBackgroundHovered = + retrieveColor( + key = "ScrollBar.hoverThumbColor", + isDark = isDark, + default = Color(0x47737373), + defaultDark = Color(0x59A6A6A6), ), thumbBorder = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.thumbBorderColor", - 0x33000000, - 0x59262626, - ), - thumbBorderHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.hoverThumbBorderColor", - 0x80000000, - 0x8C262626, - ), - thumbBorderPressed = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.hoverThumbBorderColor", - 0x80000000, - 0x8C262626, + retrieveColor( + key = "ScrollBar.Transparent.thumbBorderColor", + isDark = isDark, + default = Color(0x33595959), + defaultDark = Color(0x47383838), + ), + thumbBorderActive = + retrieveColor( + key = "ScrollBar.Transparent.hoverThumbBorderColor", + isDark = isDark, + default = Color(0x47595959), + defaultDark = Color(0x59383838), + ), + thumbOpaqueBorder = + retrieveColor( + key = "ScrollBar.thumbBorderColor", + isDark = isDark, + default = Color(0x33595959), + defaultDark = Color(0x47383838), + ), + thumbOpaqueBorderHovered = + retrieveColor( + key = "ScrollBar.hoverThumbBorderColor", + isDark = isDark, + default = Color(0x47595959), + defaultDark = Color(0x59383838), ), trackBackground = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.trackColor", - 0x00808080, - 0x00808080, - ), - trackBackgroundHovered = - readScrollBarColorForKey( - isDark, - "ScrollBar.Mac.hoverTrackColor", - 0x00808080, - 0x00808080, + retrieveColor( + key = "ScrollBar.Transparent.trackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), + ), + trackBackgroundExpanded = + retrieveColor( + key = "ScrollBar.Transparent.hoverTrackColor", + isDark = isDark, + default = Color(0x1A808080), + defaultDark = Color(0x1A808080), + ), + trackOpaqueBackground = + retrieveColor( + key = "ScrollBar.trackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), + ), + trackOpaqueBackgroundHovered = + retrieveColor( + key = "ScrollBar.hoverTrackColor", + isDark = isDark, + default = Color(0x00808080), + defaultDark = Color(0x00808080), ), ) -private fun readScrollBarColorForKey( - isDark: Boolean, - colorKey: String, - fallbackLight: Long, - fallbackDark: Long, -) = retrieveColorOrUnspecified(colorKey) - .takeOrElse { if (isDark) Color(fallbackDark) else Color(fallbackLight) } - private fun readScrollbarMetrics(): ScrollbarMetrics = if (hostOs.isMacOS) { - ScrollbarMetrics( - thumbCornerSize = CornerSize(percent = 100), - thumbThickness = 8.dp, - thumbThicknessExpanded = 14.dp, - minThumbLength = 20.dp, - trackPadding = PaddingValues(2.dp), - trackPaddingExpanded = PaddingValues(2.dp), - ) + ScrollbarMetrics(thumbCornerSize = CornerSize(percent = 100), minThumbLength = 20.dp) + } else { + ScrollbarMetrics(thumbCornerSize = CornerSize(0), minThumbLength = 16.dp) + } + +private fun readTrackClickBehavior() = + if (hostOs.isMacOS) { + ScrollbarHelper.getInstance().trackClickBehaviorFlow.value + } else { + TrackClickBehavior.JumpToSpot + } + +private fun readScrollbarVisibility() = + if (hostOs.isMacOS) { + ScrollbarHelper.getInstance().scrollbarVisibilityStyleFlow.value + } else { + ScrollbarVisibility.AlwaysVisible.windowsAndLinux() + } + +public fun ScrollbarVisibility.WhenScrolling.Companion.default(): ScrollbarVisibility.WhenScrolling = + if (hostOs.isMacOS) { + ScrollbarVisibility.WhenScrolling.macOs() } else { - ScrollbarMetrics( - thumbCornerSize = CornerSize(0), - thumbThickness = 8.dp, - thumbThicknessExpanded = 8.dp, - minThumbLength = 16.dp, - trackPadding = PaddingValues(), - trackPaddingExpanded = PaddingValues(), - ) + ScrollbarVisibility.WhenScrolling.windowsAndLinux() } -public fun ScrollbarVisibility.WhenScrolling.Companion.defaults( - appearAnimationDuration: Duration = 125.milliseconds, - disappearAnimationDuration: Duration = 125.milliseconds, - expandAnimationDuration: Duration = 125.milliseconds, +public fun ScrollbarVisibility.WhenScrolling.Companion.macOs( + trackThickness: Dp = 11.dp, + trackThicknessExpanded: Dp = 14.dp, + trackPadding: PaddingValues = PaddingValues(2.dp), + trackPaddingWithBorder: PaddingValues = PaddingValues(1.dp), + trackColorAnimationDuration: Duration = 125.milliseconds, + expandAnimationDuration: Duration = trackColorAnimationDuration, + thumbColorAnimationDuration: Duration = trackColorAnimationDuration, lingerDuration: Duration = 700.milliseconds, ): ScrollbarVisibility.WhenScrolling = ScrollbarVisibility.WhenScrolling( - appearAnimationDuration = appearAnimationDuration, - disappearAnimationDuration = disappearAnimationDuration, + trackThickness = trackThickness, + trackThicknessExpanded = trackThicknessExpanded, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPaddingWithBorder, + trackColorAnimationDuration = trackColorAnimationDuration, expandAnimationDuration = expandAnimationDuration, + thumbColorAnimationDuration = thumbColorAnimationDuration, lingerDuration = lingerDuration, ) + +public fun ScrollbarVisibility.WhenScrolling.Companion.windowsAndLinux( + trackThickness: Dp = 11.dp, + trackThicknessExpanded: Dp = 14.dp, + trackPadding: PaddingValues = PaddingValues(), + trackPaddingWithBorder: PaddingValues = trackPadding, + trackColorAnimationDuration: Duration = 125.milliseconds, + expandAnimationDuration: Duration = trackColorAnimationDuration, + thumbColorAnimationDuration: Duration = trackColorAnimationDuration, + lingerDuration: Duration = 700.milliseconds, +): ScrollbarVisibility.WhenScrolling = + ScrollbarVisibility.WhenScrolling( + trackThickness = trackThickness, + trackThicknessExpanded = trackThicknessExpanded, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPaddingWithBorder, + trackColorAnimationDuration = trackColorAnimationDuration, + expandAnimationDuration = expandAnimationDuration, + thumbColorAnimationDuration = thumbColorAnimationDuration, + lingerDuration = lingerDuration, + ) + +public fun ScrollbarVisibility.AlwaysVisible.Companion.default(): ScrollbarVisibility.AlwaysVisible = + if (hostOs.isMacOS) { + ScrollbarVisibility.AlwaysVisible.macOs() + } else { + ScrollbarVisibility.AlwaysVisible.windowsAndLinux() + } + +public fun ScrollbarVisibility.AlwaysVisible.Companion.macOs( + trackThickness: Dp = 14.dp, + trackPadding: PaddingValues = PaddingValues(2.dp), + thumbColorAnimationDuration: Duration = 330.milliseconds, + scrollbarBackgroundColorLight: Color = retrieveColor("ScrollBar.background", Color(0xFFF5F5F5)), + scrollbarBackgroundColorDark: Color = retrieveColor("ScrollBar.background", Color(0xFF3F4244)), +): ScrollbarVisibility.AlwaysVisible = + ScrollbarVisibility.AlwaysVisible( + trackThickness = trackThickness, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPadding, + thumbColorAnimationDuration = thumbColorAnimationDuration, + trackColorAnimationDuration = 0.milliseconds, + scrollbarBackgroundColorLight = scrollbarBackgroundColorLight, + scrollbarBackgroundColorDark = scrollbarBackgroundColorDark, + ) + +public fun ScrollbarVisibility.AlwaysVisible.Companion.windowsAndLinux( + trackThickness: Dp = 10.dp, + trackPadding: PaddingValues = PaddingValues(0.5.dp), + thumbColorAnimationDuration: Duration = 330.milliseconds, + trackColorAnimationDuration: Duration = thumbColorAnimationDuration, +): ScrollbarVisibility.AlwaysVisible = + ScrollbarVisibility.AlwaysVisible( + trackThickness, + trackPadding, + trackPadding, + thumbColorAnimationDuration, + trackColorAnimationDuration, + Color.Unspecified, + Color.Unspecified, + ) 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 96ada1ff4..4f246279d 100644 --- a/int-ui/int-ui-standalone/api/int-ui-standalone.api +++ b/int-ui/int-ui-standalone/api/int-ui-standalone.api @@ -254,47 +254,34 @@ public final class org/jetbrains/jewel/intui/standalone/styling/IntUiRadioButton public final class org/jetbrains/jewel/intui/standalone/styling/IntUiScrollbarStylingKt { public static final fun dark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun defaults-6ksGUsA (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;JJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; - public static synthetic fun defaults-6ksGUsA$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;JJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; - public static final fun defaults-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun defaults-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static final fun default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun default-TZvXluI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; + public static synthetic fun default-TZvXluI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion;FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling; public static final fun light (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun linux-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun linux-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static final fun macOs-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun macOs-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static final fun macOs-fYp4AQw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static synthetic fun macOs-fYp4AQw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun macOs-wH6b6FI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;F)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static synthetic fun macOs-wH6b6FI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; public static final fun macOsDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; public static synthetic fun macOsDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun macOsDark-iLRpYWo (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static synthetic fun macOsDark-iLRpYWo$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static final fun macOsDark-zwkVjRg (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static synthetic fun macOsDark-zwkVjRg$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; public static final fun macOsLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; public static synthetic fun macOsLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun macOsLight-iLRpYWo (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static synthetic fun macOsLight-iLRpYWo$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static final fun tabStripDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun tabStripLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun tabStripMacOs-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun tabStripMacOs-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static final fun tabStripMacOsDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun tabStripMacOsDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun tabStripMacOsLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun tabStripMacOsLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun tabStripWindowsAndLinux-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun tabStripWindowsAndLinux-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static final fun tabStripWindowsAndLinuxDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun tabStripWindowsAndLinuxDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun tabStripWindowsAndLinuxLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun tabStripWindowsAndLinuxLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun windows-VkLD3kw (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static synthetic fun windows-VkLD3kw$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FFLandroidx/compose/foundation/layout/PaddingValues;FLandroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; - public static final fun windowsAndLinuxDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun windowsAndLinuxDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun windowsAndLinuxDark-iLRpYWo (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static synthetic fun windowsAndLinuxDark-iLRpYWo$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static final fun windowsAndLinuxLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static synthetic fun windowsAndLinuxLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; - public static final fun windowsAndLinuxLight-iLRpYWo (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; - public static synthetic fun windowsAndLinuxLight-iLRpYWo$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static final fun macOsLight-zwkVjRg (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static synthetic fun macOsLight-zwkVjRg$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static final fun windowsAndLinux-tYhzLtE (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static synthetic fun windowsAndLinux-tYhzLtE$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;JJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun windowsAndLinux-wH6b6FI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;F)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static synthetic fun windowsAndLinux-wH6b6FI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static final fun windowsAndLinuxDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun windowsAndLinuxDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun windowsAndLinuxDark-zwkVjRg (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static synthetic fun windowsAndLinuxDark-zwkVjRg$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static final fun windowsAndLinuxLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun windowsAndLinuxLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun windowsAndLinuxLight-zwkVjRg (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJ)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; + public static synthetic fun windowsAndLinuxLight-zwkVjRg$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion;JJJJJJJJJJJJILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors; } public final class org/jetbrains/jewel/intui/standalone/styling/IntUiSegmentedControlButtonStylingKt { @@ -323,6 +310,25 @@ public final class org/jetbrains/jewel/intui/standalone/styling/IntUiSliderStyli public static final fun light-8v1krLo (Lorg/jetbrains/jewel/ui/component/styling/SliderColors$Companion;JJJJJJJJJJJJJJJLandroidx/compose/runtime/Composer;III)Lorg/jetbrains/jewel/ui/component/styling/SliderColors; } +public final class org/jetbrains/jewel/intui/standalone/styling/IntUiTabStripScrollbarStylingKt { + public static final fun tabStrip-ziNgDLE (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static synthetic fun tabStrip-ziNgDLE$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion;FLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final fun tabStripDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun tabStripLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun tabStripMacOs-wH6b6FI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;F)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static synthetic fun tabStripMacOs-wH6b6FI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static final fun tabStripMacOsDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun tabStripMacOsDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun tabStripMacOsLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun tabStripMacOsLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun tabStripWindowsAndLinux-wH6b6FI (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;F)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static synthetic fun tabStripWindowsAndLinux-wH6b6FI$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion;Landroidx/compose/foundation/shape/CornerSize;FILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics; + public static final fun tabStripWindowsAndLinuxDark (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun tabStripWindowsAndLinuxDark$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static final fun tabStripWindowsAndLinuxLight (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; + public static synthetic fun tabStripWindowsAndLinuxLight$default (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle$Companion;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics;Lorg/jetbrains/jewel/ui/component/styling/TrackClickBehavior;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility;ILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle; +} + public final class org/jetbrains/jewel/intui/standalone/styling/IntUiTabStylingKt { public static final fun default (Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion;FFFFFFFFFF)Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha; public static synthetic fun default$default (Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha$Companion;FFFFFFFFFFILjava/lang/Object;)Lorg/jetbrains/jewel/ui/component/styling/TabContentAlpha; diff --git a/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiScrollbarStyling.kt b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiScrollbarStyling.kt index 078dcbaae..eb59a9733 100644 --- a/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiScrollbarStyling.kt +++ b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiScrollbarStyling.kt @@ -9,30 +9,32 @@ import org.jetbrains.jewel.ui.component.styling.ScrollbarColors import org.jetbrains.jewel.ui.component.styling.ScrollbarMetrics import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.AlwaysVisible +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.WhenScrolling import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior import org.jetbrains.skiko.hostOs import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -public fun ScrollbarStyle.Companion.dark(): ScrollbarStyle = +public fun ScrollbarStyle.Companion.light(): ScrollbarStyle = if (hostOs.isMacOS) { - ScrollbarStyle.macOsDark() + ScrollbarStyle.macOsLight() } else { - ScrollbarStyle.windowsAndLinuxDark() + ScrollbarStyle.windowsAndLinuxLight() } -public fun ScrollbarStyle.Companion.light(): ScrollbarStyle = +public fun ScrollbarStyle.Companion.dark(): ScrollbarStyle = if (hostOs.isMacOS) { - ScrollbarStyle.macOsLight() + ScrollbarStyle.macOsDark() } else { - ScrollbarStyle.windowsAndLinuxLight() + ScrollbarStyle.windowsAndLinuxDark() } public fun ScrollbarStyle.Companion.macOsLight( colors: ScrollbarColors = ScrollbarColors.macOsLight(), metrics: ScrollbarMetrics = ScrollbarMetrics.macOs(), trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.defaults(), + scrollbarVisibility: ScrollbarVisibility = WhenScrolling.default(), ): ScrollbarStyle = ScrollbarStyle( colors = colors, @@ -45,7 +47,7 @@ public fun ScrollbarStyle.Companion.macOsDark( colors: ScrollbarColors = ScrollbarColors.macOsDark(), metrics: ScrollbarMetrics = ScrollbarMetrics.macOs(), trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.defaults(), + scrollbarVisibility: ScrollbarVisibility = WhenScrolling.default(), ): ScrollbarStyle = ScrollbarStyle( colors = colors, @@ -54,11 +56,11 @@ public fun ScrollbarStyle.Companion.macOsDark( scrollbarVisibility = scrollbarVisibility, ) -public fun ScrollbarStyle.Companion.windowsAndLinuxDark( - colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxDark(), - metrics: ScrollbarMetrics = ScrollbarMetrics.windows(), +public fun ScrollbarStyle.Companion.windowsAndLinuxLight( + colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxLight(), + metrics: ScrollbarMetrics = ScrollbarMetrics.windowsAndLinux(), trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible, + scrollbarVisibility: AlwaysVisible = AlwaysVisible.windowsAndLinux(), ): ScrollbarStyle = ScrollbarStyle( colors = colors, @@ -67,11 +69,11 @@ public fun ScrollbarStyle.Companion.windowsAndLinuxDark( scrollbarVisibility = scrollbarVisibility, ) -public fun ScrollbarStyle.Companion.windowsAndLinuxLight( - colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxLight(), - metrics: ScrollbarMetrics = ScrollbarMetrics.windows(), +public fun ScrollbarStyle.Companion.windowsAndLinuxDark( + colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxDark(), + metrics: ScrollbarMetrics = ScrollbarMetrics.windowsAndLinux(), trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible, + scrollbarVisibility: AlwaysVisible = AlwaysVisible.windowsAndLinux(), ): ScrollbarStyle = ScrollbarStyle( colors = colors, @@ -80,267 +82,189 @@ public fun ScrollbarStyle.Companion.windowsAndLinuxLight( scrollbarVisibility = scrollbarVisibility, ) -public fun ScrollbarVisibility.WhenScrolling.Companion.defaults( - appearAnimationDuration: Duration = 125.milliseconds, - disappearAnimationDuration: Duration = 125.milliseconds, - expandAnimationDuration: Duration = 125.milliseconds, - lingerDuration: Duration = 700.milliseconds, -): ScrollbarVisibility.WhenScrolling = - ScrollbarVisibility.WhenScrolling( - appearAnimationDuration = appearAnimationDuration, - disappearAnimationDuration = disappearAnimationDuration, - expandAnimationDuration = expandAnimationDuration, - lingerDuration = lingerDuration, - ) - public fun ScrollbarColors.Companion.macOsLight( - thumbBackground: Color = Color(0xFFBBBBBA), - thumbBackgroundHovered: Color = Color(0xFF7D7D7C), - thumbBackgroundPressed: Color = Color(0xFF7D7D7C), - thumbBorder: Color = Color(0x33000000), - thumbBorderHovered: Color = Color(0x80000000), - thumbBorderPressed: Color = thumbBorderHovered, + thumbBackground: Color = Color(0x00000000), + thumbBackgroundActive: Color = Color(0x80000000), + thumbOpaqueBackground: Color = Color(0x33000000), + thumbOpaqueBackgroundHovered: Color = thumbBackgroundActive, + thumbBorder: Color = thumbBackground, + thumbBorderActive: Color = thumbBackgroundActive, + thumbOpaqueBorder: Color = thumbOpaqueBackground, + thumbOpaqueBorderHovered: Color = thumbBackgroundActive, trackBackground: Color = Color(0x00808080), - trackBackgroundHovered: Color = Color(0x00808080), + trackBackgroundHovered: Color = Color(0x1A808080), + trackOpaqueBackground: Color = trackBackground, + trackOpaqueBackgroundHovered: Color = trackBackground, ): ScrollbarColors = ScrollbarColors( - thumbBackground, - thumbBackgroundHovered, - thumbBackgroundPressed, - thumbBorder, - thumbBorderHovered, - thumbBorderPressed, - trackBackground, - trackBackgroundHovered, + thumbBackground = thumbBackground, + thumbBackgroundActive = thumbBackgroundActive, + thumbOpaqueBackground = thumbOpaqueBackground, + thumbOpaqueBackgroundHovered = thumbOpaqueBackgroundHovered, + thumbBorder = thumbBorder, + thumbBorderActive = thumbBorderActive, + thumbOpaqueBorder = thumbOpaqueBorder, + thumbOpaqueBorderHovered = thumbOpaqueBorderHovered, + trackBackground = trackBackground, + trackBackgroundExpanded = trackBackgroundHovered, + trackOpaqueBackground = trackOpaqueBackground, + trackOpaqueBackgroundHovered = trackOpaqueBackgroundHovered, ) -public fun ScrollbarColors.Companion.windowsAndLinuxLight( - thumbBackground: Color = Color(0x33737373), - thumbBackgroundHovered: Color = Color(0x47737373), - thumbBackgroundPressed: Color = thumbBackgroundHovered, - thumbBorder: Color = Color(0x33595959), - thumbBorderHovered: Color = Color(0x47595959), - thumbBorderPressed: Color = thumbBorderHovered, +public fun ScrollbarColors.Companion.macOsDark( + thumbBackground: Color = Color(0x00808080), + thumbBackgroundActive: Color = Color(0x8C808080), + thumbOpaqueBackground: Color = Color(0x59808080), + thumbOpaqueBackgroundHovered: Color = thumbBackgroundActive, + thumbBorder: Color = Color(0x00262626), + thumbBorderActive: Color = Color(0x8C262626), + thumbOpaqueBorder: Color = Color(0x59262626), + thumbOpaqueBorderHovered: Color = thumbBorderActive, trackBackground: Color = Color(0x00808080), trackBackgroundHovered: Color = Color(0x1A808080), + trackOpaqueBackground: Color = trackBackground, + trackOpaqueBackgroundHovered: Color = trackBackground, ): ScrollbarColors = ScrollbarColors( - thumbBackground, - thumbBackgroundHovered, - thumbBackgroundPressed, - thumbBorder, - thumbBorderHovered, - thumbBorderPressed, - trackBackground, - trackBackgroundHovered, + thumbBackground = thumbBackground, + thumbBackgroundActive = thumbBackgroundActive, + thumbOpaqueBackground = thumbOpaqueBackground, + thumbOpaqueBackgroundHovered = thumbOpaqueBackgroundHovered, + thumbBorder = thumbBorder, + thumbBorderActive = thumbBorderActive, + thumbOpaqueBorder = thumbOpaqueBorder, + thumbOpaqueBorderHovered = thumbOpaqueBorderHovered, + trackBackground = trackBackground, + trackBackgroundExpanded = trackBackgroundHovered, + trackOpaqueBackground = trackOpaqueBackground, + trackOpaqueBackgroundHovered = trackOpaqueBackgroundHovered, ) -public fun ScrollbarColors.Companion.macOsDark( - thumbBackground: Color = Color(0x59808080), - thumbBackgroundHovered: Color = Color(0x8C808080), - thumbBackgroundPressed: Color = Color(0x8C808080), - thumbBorder: Color = Color(0x59262626), - thumbBorderHovered: Color = Color(0x8C262626), - thumbBorderPressed: Color = Color(0x8C262626), +public fun ScrollbarColors.Companion.windowsAndLinuxLight( + thumbBackground: Color = Color(0x33737373), + thumbBackgroundActive: Color = Color(0x47737373), + thumbOpaqueBackground: Color = thumbBackground, + thumbOpaqueBackgroundHovered: Color = thumbBackgroundActive, + thumbBorder: Color = Color(0x33595959), + thumbBorderActive: Color = Color(0x47595959), + thumbOpaqueBorder: Color = thumbBorder, + thumbOpaqueBorderHovered: Color = thumbBorderActive, trackBackground: Color = Color(0x00808080), trackBackgroundHovered: Color = Color(0x1A808080), + trackOpaqueBackground: Color = trackBackground, + trackOpaqueBackgroundHovered: Color = trackBackgroundHovered, ): ScrollbarColors = ScrollbarColors( - thumbBackground, - thumbBackgroundHovered, - thumbBackgroundPressed, - thumbBorder, - thumbBorderHovered, - thumbBorderPressed, - trackBackground, - trackBackgroundHovered, + thumbBackground = thumbBackground, + thumbBackgroundActive = thumbBackgroundActive, + thumbOpaqueBackground = thumbOpaqueBackground, + thumbOpaqueBackgroundHovered = thumbOpaqueBackgroundHovered, + thumbBorder = thumbBorder, + thumbBorderActive = thumbBorderActive, + thumbOpaqueBorder = thumbOpaqueBorder, + thumbOpaqueBorderHovered = thumbOpaqueBorderHovered, + trackBackground = trackBackground, + trackBackgroundExpanded = trackBackgroundHovered, + trackOpaqueBackground = trackOpaqueBackground, + trackOpaqueBackgroundHovered = trackOpaqueBackgroundHovered, ) public fun ScrollbarColors.Companion.windowsAndLinuxDark( thumbBackground: Color = Color(0x47A6A6A6), - thumbBackgroundHovered: Color = Color(0x59A6A6A6), - thumbBackgroundPressed: Color = Color(0x59A6A6A6), + thumbBackgroundActive: Color = Color(0x59A6A6A6), + thumbOpaqueBackground: Color = thumbBackground, + thumbOpaqueBackgroundHovered: Color = thumbBackgroundActive, thumbBorder: Color = Color(0x47383838), - thumbBorderHovered: Color = Color(0x59A6A6A6), - thumbBorderPressed: Color = Color(0x59A6A6A6), + thumbBorderActive: Color = Color(0x59383838), + thumbOpaqueBorder: Color = thumbBorder, + thumbOpaqueBorderHovered: Color = thumbBorderActive, trackBackground: Color = Color(0x00808080), trackBackgroundHovered: Color = Color(0x1A808080), + trackOpaqueBackground: Color = trackBackground, + trackOpaqueBackgroundHovered: Color = trackBackgroundHovered, ): ScrollbarColors = ScrollbarColors( - thumbBackground, - thumbBackgroundHovered, - thumbBackgroundPressed, - thumbBorder, - thumbBorderHovered, - thumbBorderPressed, - trackBackground, - trackBackgroundHovered, - ) - -public fun ScrollbarMetrics.Companion.defaults( - thumbCornerSize: CornerSize = CornerSize(100), - thumbThickness: Dp = 8.dp, - minThumbLength: Dp = 20.dp, - trackPadding: PaddingValues = PaddingValues(2.dp), - thumbThicknessExpanded: Dp = 14.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(2.dp), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, + thumbBackground = thumbBackground, + thumbBackgroundActive = thumbBackgroundActive, + thumbOpaqueBackground = thumbOpaqueBackground, + thumbOpaqueBackgroundHovered = thumbOpaqueBackgroundHovered, + thumbBorder = thumbBorder, + thumbBorderActive = thumbBorderActive, + thumbOpaqueBorder = thumbOpaqueBorder, + thumbOpaqueBorderHovered = thumbOpaqueBorderHovered, + trackBackground = trackBackground, + trackBackgroundExpanded = trackBackgroundHovered, + trackOpaqueBackground = trackOpaqueBackground, + trackOpaqueBackgroundHovered = trackOpaqueBackgroundHovered, ) public fun ScrollbarMetrics.Companion.macOs( thumbCornerSize: CornerSize = CornerSize(100), - thumbThickness: Dp = 8.dp, minThumbLength: Dp = 20.dp, - trackPadding: PaddingValues = PaddingValues(2.dp), - thumbThicknessExpanded: Dp = 14.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(2.dp), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, - ) - -public fun ScrollbarMetrics.Companion.windows( - thumbCornerSize: CornerSize = CornerSize(0), - thumbThickness: Dp = 8.dp, - minThumbLength: Dp = 16.dp, - trackPadding: PaddingValues = PaddingValues(horizontal = 0.dp), - thumbThicknessExpanded: Dp = 8.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(horizontal = 0.dp), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, - ) +): ScrollbarMetrics = ScrollbarMetrics(thumbCornerSize, minThumbLength) -public fun ScrollbarMetrics.Companion.linux( +public fun ScrollbarMetrics.Companion.windowsAndLinux( thumbCornerSize: CornerSize = CornerSize(0), - thumbThickness: Dp = 8.dp, - minThumbLength: Dp = 16.dp, - trackPadding: PaddingValues = PaddingValues(horizontal = 0.dp), - thumbThicknessExpanded: Dp = 8.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(horizontal = 0.dp), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, - ) - -public fun ScrollbarStyle.Companion.tabStripMacOsDark( - colors: ScrollbarColors = ScrollbarColors.macOsDark(), - metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripMacOs(), - trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.defaults(), -): ScrollbarStyle = - ScrollbarStyle( - colors = colors, - metrics = metrics, - trackClickBehavior = trackClickBehavior, - scrollbarVisibility = scrollbarVisibility, - ) - -public fun ScrollbarStyle.Companion.tabStripMacOsLight( - colors: ScrollbarColors = ScrollbarColors.macOsLight(), - metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripMacOs(), - trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.defaults(), -): ScrollbarStyle = - ScrollbarStyle( - colors = colors, - metrics = metrics, - trackClickBehavior = trackClickBehavior, - scrollbarVisibility = scrollbarVisibility, - ) - -public fun ScrollbarStyle.Companion.tabStripWindowsAndLinuxDark( - colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxDark(), - metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripWindowsAndLinux(), - trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible, -): ScrollbarStyle = - ScrollbarStyle( - colors = colors, - metrics = metrics, - trackClickBehavior = trackClickBehavior, - scrollbarVisibility = scrollbarVisibility, - ) - -public fun ScrollbarStyle.Companion.tabStripWindowsAndLinuxLight( - colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxLight(), - metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripWindowsAndLinux(), - trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, - scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible, -): ScrollbarStyle = - ScrollbarStyle( - colors = colors, - metrics = metrics, - trackClickBehavior = trackClickBehavior, - scrollbarVisibility = scrollbarVisibility, - ) + minThumbLength: Dp = 20.dp, +): ScrollbarMetrics = ScrollbarMetrics(thumbCornerSize, minThumbLength) -public fun ScrollbarStyle.Companion.tabStripDark(): ScrollbarStyle = +public fun AlwaysVisible.Companion.default(): AlwaysVisible = if (hostOs.isMacOS) { - ScrollbarStyle.tabStripMacOsDark() + AlwaysVisible.macOs() } else { - ScrollbarStyle.tabStripWindowsAndLinuxDark() + AlwaysVisible.windowsAndLinux() } -public fun ScrollbarStyle.Companion.tabStripLight(): ScrollbarStyle = - if (hostOs.isMacOS) { - ScrollbarStyle.tabStripMacOsLight() - } else { - ScrollbarStyle.tabStripWindowsAndLinuxLight() - } +public fun AlwaysVisible.Companion.macOs( + trackThickness: Dp = 14.dp, + trackPadding: PaddingValues = PaddingValues(2.dp), + thumbColorAnimationDuration: Duration = 330.milliseconds, + scrollbarBackgroundColorLight: Color = Color(0xFFF5F5F5), + scrollbarBackgroundColorDark: Color = Color(0xFF3F4244), +): AlwaysVisible = + AlwaysVisible( + trackThickness = trackThickness, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPadding, + thumbColorAnimationDuration = thumbColorAnimationDuration, + trackColorAnimationDuration = 0.milliseconds, + scrollbarBackgroundColorLight = scrollbarBackgroundColorLight, + scrollbarBackgroundColorDark = scrollbarBackgroundColorDark, + ) -public fun ScrollbarMetrics.Companion.tabStripMacOs( - thumbCornerSize: CornerSize = CornerSize(100), - thumbThickness: Dp = 3.dp, - minThumbLength: Dp = 20.dp, - trackPadding: PaddingValues = PaddingValues(), - thumbThicknessExpanded: Dp = 3.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, +public fun AlwaysVisible.Companion.windowsAndLinux( + trackThickness: Dp = 10.dp, + trackPadding: PaddingValues = PaddingValues(0.5.dp), + thumbColorAnimationDuration: Duration = 330.milliseconds, + trackColorAnimationDuration: Duration = thumbColorAnimationDuration, +): AlwaysVisible = + AlwaysVisible( + trackThickness = trackThickness, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPadding, + thumbColorAnimationDuration = thumbColorAnimationDuration, + trackColorAnimationDuration = trackColorAnimationDuration, + scrollbarBackgroundColorLight = Color.Unspecified, + scrollbarBackgroundColorDark = Color.Unspecified, ) -public fun ScrollbarMetrics.Companion.tabStripWindowsAndLinux( - thumbCornerSize: CornerSize = CornerSize(0), - thumbThickness: Dp = 3.dp, - minThumbLength: Dp = 16.dp, - trackPadding: PaddingValues = PaddingValues(), - thumbThicknessExpanded: Dp = 3.dp, - trackPaddingExpanded: PaddingValues = PaddingValues(), -): ScrollbarMetrics = - ScrollbarMetrics( - thumbCornerSize, - thumbThickness, - thumbThicknessExpanded, - minThumbLength, - trackPadding, - trackPaddingExpanded, +public fun WhenScrolling.Companion.default( + trackThickness: Dp = 11.dp, + trackThicknessExpanded: Dp = 14.dp, + trackPadding: PaddingValues = PaddingValues(2.dp), + trackPaddingWithBorder: PaddingValues = PaddingValues(1.dp), + trackColorAnimationDuration: Duration = 125.milliseconds, + expandAnimationDuration: Duration = trackColorAnimationDuration, + thumbColorAnimationDuration: Duration = trackColorAnimationDuration, + lingerDuration: Duration = 700.milliseconds, +): WhenScrolling = + WhenScrolling( + trackThickness = trackThickness, + trackThicknessExpanded = trackThicknessExpanded, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPaddingWithBorder, + trackColorAnimationDuration = trackColorAnimationDuration, + expandAnimationDuration = expandAnimationDuration, + thumbColorAnimationDuration = thumbColorAnimationDuration, + lingerDuration = lingerDuration, ) diff --git a/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStripScrollbarStyling.kt b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStripScrollbarStyling.kt new file mode 100644 index 000000000..79561c630 --- /dev/null +++ b/int-ui/int-ui-standalone/src/main/kotlin/org/jetbrains/jewel/intui/standalone/styling/IntUiTabStripScrollbarStyling.kt @@ -0,0 +1,105 @@ +package org.jetbrains.jewel.intui.standalone.styling + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import org.jetbrains.jewel.ui.component.styling.ScrollbarColors +import org.jetbrains.jewel.ui.component.styling.ScrollbarMetrics +import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior +import org.jetbrains.skiko.hostOs +import kotlin.time.Duration.Companion.milliseconds + +public fun ScrollbarStyle.Companion.tabStripLight(): ScrollbarStyle = + if (hostOs.isMacOS) { + ScrollbarStyle.tabStripMacOsLight() + } else { + ScrollbarStyle.tabStripWindowsAndLinuxLight() + } + +public fun ScrollbarStyle.Companion.tabStripDark(): ScrollbarStyle = + if (hostOs.isMacOS) { + ScrollbarStyle.tabStripMacOsDark() + } else { + ScrollbarStyle.tabStripWindowsAndLinuxDark() + } + +public fun ScrollbarStyle.Companion.tabStripMacOsLight( + colors: ScrollbarColors = ScrollbarColors.macOsLight(), + metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripMacOs(), + trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, + scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.default(), +): ScrollbarStyle = + ScrollbarStyle( + colors = colors, + metrics = metrics, + trackClickBehavior = trackClickBehavior, + scrollbarVisibility = scrollbarVisibility, + ) + +public fun ScrollbarStyle.Companion.tabStripMacOsDark( + colors: ScrollbarColors = ScrollbarColors.macOsDark(), + metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripMacOs(), + trackClickBehavior: TrackClickBehavior = TrackClickBehavior.NextPage, + scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.WhenScrolling.default(), +): ScrollbarStyle = + ScrollbarStyle( + colors = colors, + metrics = metrics, + trackClickBehavior = trackClickBehavior, + scrollbarVisibility = scrollbarVisibility, + ) + +public fun ScrollbarStyle.Companion.tabStripWindowsAndLinuxLight( + colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxLight(), + metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripWindowsAndLinux(), + trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, + scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible.tabStrip(), +): ScrollbarStyle = + ScrollbarStyle( + colors = colors, + metrics = metrics, + trackClickBehavior = trackClickBehavior, + scrollbarVisibility = scrollbarVisibility, + ) + +public fun ScrollbarStyle.Companion.tabStripWindowsAndLinuxDark( + colors: ScrollbarColors = ScrollbarColors.windowsAndLinuxDark(), + metrics: ScrollbarMetrics = ScrollbarMetrics.tabStripWindowsAndLinux(), + trackClickBehavior: TrackClickBehavior = TrackClickBehavior.JumpToSpot, + scrollbarVisibility: ScrollbarVisibility = ScrollbarVisibility.AlwaysVisible.tabStrip(), +): ScrollbarStyle = + ScrollbarStyle( + colors = colors, + metrics = metrics, + trackClickBehavior = trackClickBehavior, + scrollbarVisibility = scrollbarVisibility, + ) + +public fun ScrollbarMetrics.Companion.tabStripMacOs( + thumbCornerSize: CornerSize = CornerSize(100), + minThumbLength: Dp = 20.dp, +): ScrollbarMetrics = ScrollbarMetrics(thumbCornerSize, minThumbLength) + +public fun ScrollbarMetrics.Companion.tabStripWindowsAndLinux( + thumbCornerSize: CornerSize = CornerSize(0), + minThumbLength: Dp = 20.dp, +): ScrollbarMetrics = ScrollbarMetrics(thumbCornerSize, minThumbLength) + +public fun ScrollbarVisibility.AlwaysVisible.Companion.tabStrip( + trackThickness: Dp = 4.dp, + trackPadding: PaddingValues = PaddingValues(), + trackPaddingWithBorder: PaddingValues = trackPadding, +): ScrollbarVisibility.AlwaysVisible = + ScrollbarVisibility.AlwaysVisible( + trackThickness = trackThickness, + trackPadding = trackPadding, + trackPaddingWithBorder = trackPaddingWithBorder, + thumbColorAnimationDuration = 0.milliseconds, + trackColorAnimationDuration = 0.milliseconds, + scrollbarBackgroundColorLight = Color.Unspecified, + scrollbarBackgroundColorDark = Color.Unspecified, + ) diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt index 530d0952a..ebce59c67 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt @@ -1,13 +1,8 @@ package org.jetbrains.jewel.markdown.rendering -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.HorizontalScrollbar import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -20,19 +15,12 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.rememberScrollbarAdapter import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.derivedStateOf -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.alpha import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.geometry.Offset @@ -41,15 +29,12 @@ import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.pointerHoverIcon -import androidx.compose.ui.layout.Layout -import androidx.compose.ui.layout.layoutId import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection.Ltr import androidx.compose.ui.unit.dp import org.jetbrains.jewel.foundation.ExperimentalJewelApi -import org.jetbrains.jewel.foundation.modifier.onHover import org.jetbrains.jewel.foundation.theme.LocalContentColor import org.jetbrains.jewel.markdown.MarkdownBlock import org.jetbrains.jewel.markdown.MarkdownBlock.BlockQuote @@ -69,6 +54,7 @@ import org.jetbrains.jewel.markdown.WithInlineMarkdown import org.jetbrains.jewel.markdown.extensions.MarkdownRendererExtension import org.jetbrains.jewel.ui.Orientation.Horizontal import org.jetbrains.jewel.ui.component.Divider +import org.jetbrains.jewel.ui.component.HorizontallyScrollableContainer import org.jetbrains.jewel.ui.component.Text @ExperimentalJewelApi @@ -126,13 +112,7 @@ public open class DefaultMarkdownBlockRenderer( onUrlClick: (String) -> Unit, onTextClick: () -> Unit, ) { - val renderedContent = - rememberRenderedContent( - block, - styling.inlinesStyling, - enabled, - onUrlClick, - ) + val renderedContent = rememberRenderedContent(block, styling.inlinesStyling, enabled, onUrlClick) val textColor = styling.inlinesStyling.textStyle.color .takeOrElse { LocalContentColor.current } @@ -142,13 +122,8 @@ public open class DefaultMarkdownBlockRenderer( Text( modifier = - Modifier - .focusProperties { canFocus = false } - .clickable( - interactionSource = interactionSource, - indication = null, - onClick = onTextClick, - ), + Modifier.focusProperties { canFocus = false } + .clickable(interactionSource = interactionSource, indication = null, onClick = onTextClick), text = renderedContent, style = mergedStyle, ) @@ -181,13 +156,7 @@ public open class DefaultMarkdownBlockRenderer( onUrlClick: (String) -> Unit, onTextClick: () -> Unit, ) { - val renderedContent = - rememberRenderedContent( - block, - styling.inlinesStyling, - enabled, - onUrlClick, - ) + val renderedContent = rememberRenderedContent(block, styling.inlinesStyling, enabled, onUrlClick) Heading( renderedContent, styling.inlinesStyling.textStyle, @@ -208,15 +177,9 @@ public open class DefaultMarkdownBlockRenderer( underlineGap: Dp, ) { Column(modifier = Modifier.padding(paddingValues)) { - val textColor = - textStyle.color - .takeOrElse { LocalContentColor.current.takeOrElse { textStyle.color } } + val textColor = textStyle.color.takeOrElse { LocalContentColor.current.takeOrElse { textStyle.color } } val mergedStyle = textStyle.merge(TextStyle(color = textColor)) - Text( - text = renderedContent, - style = mergedStyle, - modifier = Modifier.focusProperties { canFocus = false }, - ) + Text(text = renderedContent, style = mergedStyle, modifier = Modifier.focusProperties { canFocus = false }) if (underlineWidth > 0.dp && underlineColor.isSpecified) { Spacer(Modifier.height(underlineGap)) @@ -234,21 +197,21 @@ public open class DefaultMarkdownBlockRenderer( onTextClick: () -> Unit, ) { Column( - Modifier - .drawBehind { - val isLtr = layoutDirection == Ltr - val lineWidthPx = styling.lineWidth.toPx() - val x = if (isLtr) lineWidthPx / 2 else size.width - lineWidthPx / 2 - - drawLine( - styling.lineColor, - Offset(x, 0f), - Offset(x, size.height), - lineWidthPx, - styling.strokeCap, - styling.pathEffect, - ) - }.padding(styling.padding), + Modifier.drawBehind { + val isLtr = layoutDirection == Ltr + val lineWidthPx = styling.lineWidth.toPx() + val x = if (isLtr) lineWidthPx / 2 else size.width - lineWidthPx / 2 + + drawLine( + styling.lineColor, + Offset(x, 0f), + Offset(x, size.height), + lineWidthPx, + styling.strokeCap, + styling.pathEffect, + ) + } + .padding(styling.padding), verticalArrangement = Arrangement.spacedBy(rootStyling.blockVerticalSpacing), ) { CompositionLocalProvider(LocalContentColor provides styling.textColor) { @@ -286,10 +249,7 @@ public open class DefaultMarkdownBlockRenderer( styling.itemVerticalSpacing } - Column( - modifier = Modifier.padding(styling.padding), - verticalArrangement = Arrangement.spacedBy(itemSpacing), - ) { + Column(modifier = Modifier.padding(styling.padding), verticalArrangement = Arrangement.spacedBy(itemSpacing)) { for ((index, item) in block.children.withIndex()) { Row { val number = block.startFrom + index @@ -298,8 +258,7 @@ public open class DefaultMarkdownBlockRenderer( style = styling.numberStyle, color = styling.numberStyle.color.takeOrElse { LocalContentColor.current }, modifier = - Modifier - .focusProperties { canFocus = false } + Modifier.focusProperties { canFocus = false } .widthIn(min = styling.numberMinWidth) .pointerHoverIcon(PointerIcon.Default, overrideDescendants = true), textAlign = styling.numberTextAlign, @@ -328,10 +287,7 @@ public open class DefaultMarkdownBlockRenderer( styling.itemVerticalSpacing } - Column( - modifier = Modifier.padding(styling.padding), - verticalArrangement = Arrangement.spacedBy(itemSpacing), - ) { + Column(modifier = Modifier.padding(styling.padding), verticalArrangement = Arrangement.spacedBy(itemSpacing)) { for (item in block.children) { Row { Text( @@ -379,10 +335,9 @@ public open class DefaultMarkdownBlockRenderer( block: IndentedCodeBlock, styling: MarkdownStyling.Code.Indented, ) { - HorizontallyScrollingContainer( + MaybeScrollingContainer( isScrollable = styling.scrollsHorizontally, - Modifier - .background(styling.background, styling.shape) + Modifier.background(styling.background, styling.shape) .border(styling.borderWidth, styling.borderColor, styling.shape) .then(if (styling.fillWidth) Modifier.fillMaxWidth() else Modifier), ) { @@ -403,10 +358,9 @@ public open class DefaultMarkdownBlockRenderer( block: FencedCodeBlock, styling: MarkdownStyling.Code.Fenced, ) { - HorizontallyScrollingContainer( + MaybeScrollingContainer( isScrollable = styling.scrollsHorizontally, - Modifier - .background(styling.background, styling.shape) + Modifier.background(styling.background, styling.shape) .border(styling.borderWidth, styling.borderColor, styling.shape) .then(if (styling.fillWidth) Modifier.fillMaxWidth() else Modifier), ) { @@ -486,60 +440,15 @@ public open class DefaultMarkdownBlockRenderer( } @Composable - private fun HorizontallyScrollingContainer( + private fun MaybeScrollingContainer( isScrollable: Boolean, modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { - var isHovered by remember { mutableStateOf(false) } - - Layout( - content = { - val scrollState = rememberScrollState() - Box( - Modifier - .layoutId("mainContent") - .then(if (isScrollable) Modifier.horizontalScroll(scrollState) else Modifier), - ) { - content() - } - - val canScroll by derivedStateOf { - scrollState.canScrollBackward || scrollState.canScrollForward - } - - if (isScrollable && canScroll) { - val alpha by animateFloatAsState( - if (isHovered) 1f else 0f, - tween(durationMillis = 150, easing = LinearEasing), - ) - - HorizontalScrollbar( - rememberScrollbarAdapter(scrollState), - Modifier - .layoutId("containerHScrollbar") - .padding(start = 2.dp, end = 2.dp, bottom = 2.dp) - .alpha(alpha), - ) - } - }, - modifier.onHover { isHovered = it }, - { measurables, incomingConstraints -> - val contentMeasurable = - measurables.singleOrNull { it.layoutId == "mainContent" } - ?: error("There must be one and only one child with ID 'mainContent'") - - val contentPlaceable = contentMeasurable.measure(incomingConstraints) - - val scrollbarMeasurable = measurables.find { it.layoutId == "containerHScrollbar" } - val scrollbarPlaceable = scrollbarMeasurable?.measure(incomingConstraints) - val scrollbarHeight = scrollbarPlaceable?.measuredHeight ?: 0 - - layout(contentPlaceable.width, contentPlaceable.height + scrollbarHeight) { - contentPlaceable.place(0, 0) - scrollbarPlaceable?.place(0, contentPlaceable.height) - } - }, - ) + if (isScrollable) { + HorizontallyScrollableContainer(modifier) { content() } + } else { + content() + } } } diff --git a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt index a330cdcae..c23cc9bfc 100644 --- a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt +++ b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt @@ -13,10 +13,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -64,6 +62,7 @@ import org.jetbrains.jewel.ui.component.Text import org.jetbrains.jewel.ui.component.TextField import org.jetbrains.jewel.ui.component.Tooltip import org.jetbrains.jewel.ui.component.Typography +import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer import org.jetbrains.jewel.ui.component.separator import org.jetbrains.jewel.ui.icons.AllIconsKeys import org.jetbrains.jewel.ui.painter.badge.DotBadgeShape @@ -76,19 +75,19 @@ import org.jetbrains.jewel.ui.theme.colorPalette internal fun ComponentShowcaseTab() { val bgColor by remember(JBColor.PanelBackground.rgb) { mutableStateOf(JBColor.PanelBackground.toComposeColor()) } - val scrollState = rememberScrollState() - Row( - modifier = - Modifier - .trackComponentActivation(LocalComponent.current) - .fillMaxSize() - .background(bgColor) - .verticalScroll(scrollState) - .padding(16.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp), - ) { - ColumnOne() - ColumnTwo() + VerticallyScrollableContainer { + Row( + modifier = + Modifier + .trackComponentActivation(LocalComponent.current) + .fillMaxSize() + .background(bgColor) + .padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + ColumnOne() + ColumnTwo() + } } } diff --git a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ScrollbarsShowcaseTab.kt b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ScrollbarsShowcaseTab.kt index ed12972c4..06c9dd123 100644 --- a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ScrollbarsShowcaseTab.kt +++ b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ScrollbarsShowcaseTab.kt @@ -1,11 +1,12 @@ package org.jetbrains.jewel.samples.ideplugin -import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -13,20 +14,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.intellij.ui.JBColor import org.jetbrains.jewel.bridge.LocalComponent -import org.jetbrains.jewel.bridge.toComposeColor import org.jetbrains.jewel.foundation.modifier.trackActivation import org.jetbrains.jewel.foundation.modifier.trackComponentActivation import org.jetbrains.jewel.foundation.theme.JewelTheme @@ -34,56 +29,46 @@ import org.jetbrains.jewel.ui.Orientation import org.jetbrains.jewel.ui.component.Divider import org.jetbrains.jewel.ui.component.Text import org.jetbrains.jewel.ui.component.TextArea -import org.jetbrains.jewel.ui.component.VerticalScrollbar -import org.jetbrains.jewel.ui.theme.scrollbarStyle +import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer +import org.jetbrains.jewel.ui.component.scrollbarContentSafePadding import java.util.Locale @Composable internal fun ScrollbarsShowcaseTab() { - val bgColor by remember(JBColor.PanelBackground.rgb) { mutableStateOf(JBColor.PanelBackground.toComposeColor()) } - Column( - Modifier - .trackComponentActivation(LocalComponent.current) - .fillMaxSize() - .background(bgColor) - .padding(16.dp) - .trackActivation(), + Modifier.trackComponentActivation(LocalComponent.current).fillMaxSize().padding(16.dp).trackActivation(), verticalArrangement = Arrangement.spacedBy(16.dp), ) { Row(modifier = Modifier.fillMaxWidth().height(200.dp)) { val textFieldState = rememberTextFieldState(ANDROID_IPSUM) - TextArea( - state = textFieldState, - modifier = Modifier.size(300.dp), - ) + TextArea(state = textFieldState, modifier = Modifier.size(300.dp)) - Divider(Orientation.Vertical, modifier = Modifier.width(10.dp)) + Spacer(Modifier.width(10.dp)) - Box(Modifier.border(1.dp, JewelTheme.globalColors.borders.normal)) { - val scrollState = rememberLazyListState() - LazyColumn( - Modifier - .width(200.dp) - .padding(end = JewelTheme.scrollbarStyle.metrics.thumbThicknessExpanded) - .align(Alignment.CenterStart), - verticalArrangement = Arrangement.spacedBy(4.dp), - state = scrollState, - ) { - items(LIST_ITEMS) { item -> - Column(modifier = Modifier.height(48.dp)) { + val scrollState = rememberLazyListState() + VerticallyScrollableContainer( + scrollState, + Modifier.width(200.dp).border(1.dp, JewelTheme.globalColors.borders.normal), + ) { + LazyColumn(state = scrollState, contentPadding = PaddingValues(vertical = 8.dp)) { + itemsIndexed(LIST_ITEMS) { index, item -> + Column { Text( - modifier = Modifier.padding(horizontal = 8.dp), text = item, + modifier = Modifier.padding(start = 8.dp, end = 8.dp + scrollbarContentSafePadding()), ) - Divider(orientation = Orientation.Horizontal, color = Color.Gray) + + if (index < LIST_ITEMS.lastIndex) { + Box(Modifier.height(8.dp)) { + Divider( + orientation = Orientation.Horizontal, + modifier = Modifier.fillMaxWidth().align(Alignment.CenterStart), + ) + } + } } } } - VerticalScrollbar( - scrollState = scrollState, - modifier = Modifier.align(Alignment.CenterEnd), - ) } } } @@ -106,12 +91,6 @@ private const val ANDROID_IPSUM = " sunt in culpa qui officia material design deserunt mollit anim id est laborum." private val LIST_ITEMS = - ANDROID_IPSUM - .split(",") - .map { lorem -> - lorem - .trim() - .replaceFirstChar { - if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() - } - } + ANDROID_IPSUM.split(",").map { lorem -> + lorem.trim().replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + } diff --git a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/releasessample/ReleasesSampleCompose.kt b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/releasessample/ReleasesSampleCompose.kt index f47087c57..27f9c6ef4 100644 --- a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/releasessample/ReleasesSampleCompose.kt +++ b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/releasessample/ReleasesSampleCompose.kt @@ -8,7 +8,6 @@ import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.Image -import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -30,11 +29,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.width import androidx.compose.foundation.onClick -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.rememberScrollbarAdapter import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.input.rememberTextFieldState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -59,7 +55,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.intellij.icons.AllIcons import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.util.ui.JBUI @@ -84,8 +79,9 @@ import org.jetbrains.jewel.ui.component.PopupMenu import org.jetbrains.jewel.ui.component.Text import org.jetbrains.jewel.ui.component.TextField import org.jetbrains.jewel.ui.component.Typography -import org.jetbrains.jewel.ui.component.VerticalScrollbar +import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer import org.jetbrains.jewel.ui.component.items +import org.jetbrains.jewel.ui.component.scrollbarContentSafePadding import org.jetbrains.jewel.ui.icons.AllIconsKeys import org.jetbrains.jewel.ui.painter.rememberResourcePainterProvider import org.jetbrains.jewel.ui.theme.iconButtonStyle @@ -149,7 +145,7 @@ private fun LeftColumn( } val listState = rememberSelectableLazyListState() - Box(modifier) { + VerticallyScrollableContainer(listState.lazyListState, modifier) { SelectableLazyColumn( modifier = Modifier.fillMaxSize(), state = listState, @@ -180,11 +176,6 @@ private fun LeftColumn( } } } - - VerticalScrollbar( - adapter = rememberScrollbarAdapter(listState.lazyListState), - modifier = Modifier.fillMaxHeight().align(Alignment.CenterEnd), - ) } } } @@ -206,7 +197,8 @@ private fun ContentItemRow( modifier = Modifier.height(JewelTheme.globalMetrics.rowHeight) .background(color) - .padding(start = 4.dp, end = 12.dp), + .padding(horizontal = 4.dp) + .padding(end = scrollbarContentSafePadding()), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { @@ -364,11 +356,7 @@ private fun OverflowMenu( }, onClick = { menuVisible = !menuVisible }, ) { - Icon( - resource = "actions/more.svg", - iconClass = AllIcons::class.java, - contentDescription = "Select data source", - ) + Icon(key = AllIconsKeys.Ide.Notification.Gear, contentDescription = "Select data source") } val contentSources = @@ -417,24 +405,22 @@ private fun RightColumn( selectedItem: ContentItem?, modifier: Modifier, ) { - Box(modifier, contentAlignment = Alignment.Center) { - if (selectedItem == null) { + if (selectedItem == null) { + Box(modifier, contentAlignment = Alignment.Center) { Text("Nothing to see here", color = JBUI.CurrentTheme.Label.disabledForeground().toComposeColor()) - } else { - val scrollState = rememberScrollState() - VerticalScrollbarContainer(scrollState, modifier = modifier) { - Column( - modifier = Modifier.verticalScroll(scrollState), - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.Start, - ) { - val imagePath = selectedItem.imagePath - if (imagePath != null) { - ReleaseImage(imagePath) - } - - ItemDetailsText(selectedItem) + } + } else { + VerticallyScrollableContainer(modifier = modifier) { + Column( + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + val imagePath = selectedItem.imagePath + if (imagePath != null) { + ReleaseImage(imagePath) } + + ItemDetailsText(selectedItem) } } } @@ -479,22 +465,6 @@ private fun ReleaseImage(imagePath: String) { ) } -@Composable -private fun VerticalScrollbarContainer( - scrollState: ScrollState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { - Box(modifier) { - content() - - VerticalScrollbar( - adapter = rememberScrollbarAdapter(scrollState), - modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(), - ) - } -} - @Composable private fun ItemDetailsText(selectedItem: ContentItem) { Column( diff --git a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Scrollbars.kt b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Scrollbars.kt index be6663483..dd56a9a9f 100644 --- a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Scrollbars.kt +++ b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Scrollbars.kt @@ -1,166 +1,233 @@ package org.jetbrains.jewel.samples.standalone.view.component -import androidx.compose.foundation.border +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.verticalScroll 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.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import org.jetbrains.jewel.foundation.Stroke +import org.jetbrains.jewel.foundation.modifier.border import org.jetbrains.jewel.foundation.theme.JewelTheme -import org.jetbrains.jewel.intui.standalone.styling.defaults -import org.jetbrains.jewel.intui.standalone.styling.macOsDark -import org.jetbrains.jewel.intui.standalone.styling.macOsLight -import org.jetbrains.jewel.intui.standalone.styling.windowsAndLinuxDark -import org.jetbrains.jewel.intui.standalone.styling.windowsAndLinuxLight +import org.jetbrains.jewel.intui.standalone.styling.dark +import org.jetbrains.jewel.intui.standalone.styling.default +import org.jetbrains.jewel.intui.standalone.styling.light import org.jetbrains.jewel.ui.Orientation import org.jetbrains.jewel.ui.component.CheckboxRow import org.jetbrains.jewel.ui.component.Divider +import org.jetbrains.jewel.ui.component.HorizontallyScrollableContainer +import org.jetbrains.jewel.ui.component.RadioButtonRow import org.jetbrains.jewel.ui.component.Text +import org.jetbrains.jewel.ui.component.Typography import org.jetbrains.jewel.ui.component.VerticalScrollbar +import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer +import org.jetbrains.jewel.ui.component.scrollbarContentSafePadding import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility -import org.jetbrains.jewel.ui.theme.scrollbarStyle +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior +import org.jetbrains.jewel.ui.theme.textAreaStyle +import org.jetbrains.skiko.OS import org.jetbrains.skiko.hostOs import java.util.Locale @Composable fun Scrollbars() { - Column { + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { val isDark = JewelTheme.isDark - var alwaysVisible by remember { mutableStateOf(false) } - val initialStyle by remember { mutableStateOf(readStyle(hostOs.isMacOS, isDark)) } - var style by remember { mutableStateOf(initialStyle) } - - LaunchedEffect(alwaysVisible) { - style = - if (alwaysVisible) { - ScrollbarStyle( - colors = style.colors, - metrics = style.metrics, - trackClickBehavior = style.trackClickBehavior, - scrollbarVisibility = ScrollbarVisibility.AlwaysVisible, - ) - } else { - ScrollbarStyle( - colors = style.colors, - metrics = style.metrics, - trackClickBehavior = style.trackClickBehavior, - scrollbarVisibility = ScrollbarVisibility.WhenScrolling.defaults(), - ) - } - } + val baseStyle = remember(isDark) { if (isDark) ScrollbarStyle.dark() else ScrollbarStyle.light() } - CheckboxRow( - checked = alwaysVisible, - onCheckedChange = { alwaysVisible = it }, - text = "Always visible", - ) + var alwaysVisible by remember { mutableStateOf(hostOs != OS.MacOS) } + var clickBehavior by remember { mutableStateOf(baseStyle.trackClickBehavior) } + SettingsRow(alwaysVisible, clickBehavior, { alwaysVisible = it }, { clickBehavior = it }) - Spacer(modifier = Modifier.height(16.dp)) + val style by + remember(alwaysVisible, clickBehavior, baseStyle) { + mutableStateOf( + if (alwaysVisible) { + ScrollbarStyle( + colors = baseStyle.colors, + metrics = baseStyle.metrics, + trackClickBehavior = clickBehavior, + scrollbarVisibility = ScrollbarVisibility.AlwaysVisible.default(), + ) + } else { + ScrollbarStyle( + colors = baseStyle.colors, + metrics = baseStyle.metrics, + trackClickBehavior = clickBehavior, + scrollbarVisibility = ScrollbarVisibility.WhenScrolling.default(), + ) + }, + ) + } Row( - Modifier.padding(horizontal = 16.dp).height(200.dp), + Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, ) { - Column { - Text("LazyColumn", fontSize = 18.sp) - Spacer(Modifier.height(8.dp)) - - Box(Modifier.border(1.dp, JewelTheme.globalColors.borders.normal)) { - val scrollState = rememberLazyListState() - LazyColumn( - Modifier - .width(200.dp) - .padding(end = JewelTheme.scrollbarStyle.metrics.thumbThicknessExpanded) - .align(Alignment.CenterStart), - verticalArrangement = Arrangement.spacedBy(4.dp), - state = scrollState, - ) { - items(LIST_ITEMS) { item -> - Column { - Text( - modifier = Modifier.padding(horizontal = 8.dp), - text = item, - ) - Divider(orientation = Orientation.Horizontal, color = Color.Gray) - } + LazyColumnWithScrollbar(style, Modifier.height(200.dp).weight(1f)) + ColumnWithScrollbar(style, Modifier.height(200.dp).weight(1f)) + } + + HorizontalScrollbarContent(style, Modifier.fillMaxWidth()) + } +} + +@Composable +private fun SettingsRow( + alwaysVisible: Boolean, + clickBehavior: TrackClickBehavior, + onAlwaysVisibleChange: (Boolean) -> Unit, + onClickBehaviorChange: (TrackClickBehavior) -> Unit, +) { + Row(verticalAlignment = Alignment.CenterVertically) { + CheckboxRow(checked = alwaysVisible, onCheckedChange = onAlwaysVisibleChange, text = "Always visible") + + Spacer(Modifier.weight(1f)) + + Text("Track click behavior:") + + Spacer(Modifier.width(8.dp)) + + Row(Modifier.selectableGroup(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { + RadioButtonRow( + text = "Jump to spot", + selected = clickBehavior == TrackClickBehavior.JumpToSpot, + onClick = { onClickBehaviorChange(TrackClickBehavior.JumpToSpot) }, + ) + RadioButtonRow( + text = "Move by one page", + selected = clickBehavior == TrackClickBehavior.NextPage, + onClick = { onClickBehaviorChange(TrackClickBehavior.NextPage) }, + ) + } + } +} + +@Composable +private fun LazyColumnWithScrollbar( + style: ScrollbarStyle, + modifier: Modifier, +) { + Column(modifier) { + Text("LazyColumn", style = Typography.h2TextStyle()) + + Spacer(Modifier.height(8.dp)) + + val scrollState = rememberLazyListState() + VerticallyScrollableContainer( + scrollState, + modifier = + Modifier.weight(1f) + .fillMaxWidth() + .border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal), + style = style, + ) { + LazyColumn( + state = scrollState, + modifier = Modifier.fillMaxSize().background(JewelTheme.textAreaStyle.colors.background), + ) { + itemsIndexed(LIST_ITEMS) { index, item -> + Column { + Text( + modifier = + Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + .padding(end = scrollbarContentSafePadding(style)), + text = item, + ) + + if (index != LIST_ITEMS.lastIndex) { + Divider( + orientation = Orientation.Horizontal, + color = JewelTheme.globalColors.borders.normal, + ) } } - VerticalScrollbar( - scrollState = scrollState, - modifier = Modifier.align(Alignment.CenterEnd), - style = style, - ) } } - Column { - Text("Column", fontSize = 18.sp) - Spacer(Modifier.height(8.dp)) + } + } +} - Box(Modifier.border(1.dp, JewelTheme.globalColors.borders.normal)) { - val scrollState = rememberScrollState() - Column( +@Composable +private fun ColumnWithScrollbar( + style: ScrollbarStyle, + modifier: Modifier, +) { + Column(modifier) { + Text("Column", fontSize = 18.sp) + Spacer(Modifier.height(8.dp)) + + Box(Modifier.border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal)) { + val scrollState = rememberScrollState() + Column( + modifier = + Modifier.background(JewelTheme.textAreaStyle.colors.background) + .verticalScroll(scrollState) + .align(Alignment.CenterStart), + ) { + LIST_ITEMS.forEachIndexed { index, line -> + Text( modifier = - Modifier - .verticalScroll(scrollState) - .padding(end = JewelTheme.scrollbarStyle.metrics.thumbThicknessExpanded) - .align(Alignment.CenterStart), - ) { - LIST_ITEMS.forEach { - Text( - modifier = Modifier.padding(horizontal = 8.dp), - text = it, - ) + Modifier.padding(horizontal = 8.dp).padding(end = scrollbarContentSafePadding(style)), + text = line, + ) + if (index < LIST_ITEMS.lastIndex) { + Box(Modifier.height(8.dp), contentAlignment = Alignment.CenterStart) { + Divider(Orientation.Horizontal, Modifier.fillMaxWidth()) } } - VerticalScrollbar( - scrollState = scrollState, - modifier = Modifier.align(Alignment.CenterEnd), - style = style, - ) } } + VerticalScrollbar(scrollState = scrollState, modifier = Modifier.align(Alignment.CenterEnd), style = style) } } } -fun readStyle( - isMac: Boolean, - isDark: Boolean, -): ScrollbarStyle = - if (isDark) { - if (isMac) { - ScrollbarStyle.macOsDark() - } else { - ScrollbarStyle.windowsAndLinuxDark() - } - } else { - if (isMac) { - ScrollbarStyle.macOsLight() - } else { - ScrollbarStyle.windowsAndLinuxLight() +@Composable +private fun HorizontalScrollbarContent( + scrollbarStyle: ScrollbarStyle, + modifier: Modifier, +) { + HorizontallyScrollableContainer( + modifier = modifier.border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal), + style = scrollbarStyle, + ) { + Column( + modifier = + Modifier.fillMaxHeight() + .background(JewelTheme.textAreaStyle.colors.background) + .padding(bottom = scrollbarContentSafePadding(scrollbarStyle)) + .padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + val oneLineIpsum = LOREM_IPSUM.replace('\n', ' ') + repeat(4) { Text(oneLineIpsum) } } } +} @Suppress("SpellCheckingInspection") private const val LOREM_IPSUM = @@ -177,12 +244,8 @@ private const val LOREM_IPSUM = "Sed nec sapien nec dui rhoncus bibendum. Sed blandit bibendum libero." private val LIST_ITEMS = - LOREM_IPSUM - .split(",") + LOREM_IPSUM.split(",") .map { lorem -> - lorem - .trim() - .replaceFirstChar { - if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() - } + lorem.trim().replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } } + .let { it + it + it + it + it + it } diff --git a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownEditor.kt b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownEditor.kt index 51b83790d..51daef3c7 100644 --- a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownEditor.kt +++ b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownEditor.kt @@ -124,12 +124,13 @@ private fun Editor( state: TextFieldState, modifier: Modifier = Modifier, ) { - Box(modifier.padding(16.dp)) { + Box(modifier) { TextArea( state = state, modifier = Modifier.align(Alignment.TopStart).fillMaxWidth(), undecorated = true, textStyle = JewelTheme.editorTextStyle, + decorationBoxModifier = Modifier.padding(horizontal = 8.dp), ) } } diff --git a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt index 4194a58d8..9eaaacb0c 100644 --- a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt +++ b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt @@ -1,19 +1,14 @@ package org.jetbrains.jewel.samples.standalone.view.markdown import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.rememberScrollbarAdapter 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.graphics.Color import androidx.compose.ui.unit.dp @@ -36,7 +31,8 @@ import org.jetbrains.jewel.markdown.extensions.github.alerts.GitHubAlertRenderer import org.jetbrains.jewel.markdown.processing.MarkdownProcessor import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer import org.jetbrains.jewel.markdown.rendering.MarkdownStyling -import org.jetbrains.jewel.ui.component.VerticalScrollbar +import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer +import org.jetbrains.jewel.ui.component.scrollbarContentSafePadding import java.awt.Desktop import java.net.URI @@ -87,21 +83,17 @@ internal fun MarkdownPreview( val background = remember(isDark) { if (isDark) Color(0xff0d1117) else Color.White } ProvideMarkdownStyling(markdownStyling, blockRenderer) { - Box(modifier.background(background)) { - val lazyListState = rememberLazyListState() + val lazyListState = rememberLazyListState() + VerticallyScrollableContainer(lazyListState, modifier.background(background)) { LazyMarkdown( markdownBlocks = markdownBlocks, modifier = Modifier.background(background), - contentPadding = PaddingValues(16.dp), + contentPadding = + PaddingValues(start = 8.dp, top = 8.dp, end = 8.dp + scrollbarContentSafePadding(), bottom = 8.dp), state = lazyListState, selectable = true, onUrlClick = { url -> Desktop.getDesktop().browse(URI.create(url)) }, ) - - VerticalScrollbar( - rememberScrollbarAdapter(lazyListState), - Modifier.align(Alignment.TopEnd).fillMaxHeight().padding(2.dp), - ) } } } diff --git a/ui/api/ui.api b/ui/api/ui.api index 6b1c1116c..8b063f572 100644 --- a/ui/api/ui.api +++ b/ui/api/ui.api @@ -577,12 +577,22 @@ public final class org/jetbrains/jewel/ui/component/RadioButtonState$Companion { public static synthetic fun of-fp8g3n8$default (Lorg/jetbrains/jewel/ui/component/RadioButtonState$Companion;ZZZZZZILjava/lang/Object;)J } -public final class org/jetbrains/jewel/ui/component/ScrollbarsKt { - public static final fun HorizontalScrollbar (Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;II)V - public static final fun HorizontalScrollbar (Landroidx/compose/foundation/v2/ScrollbarAdapter;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;II)V - public static final fun TabStripHorizontalScrollbar (Landroidx/compose/foundation/v2/ScrollbarAdapter;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;II)V - public static final fun VerticalScrollbar (Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;II)V - public static final fun VerticalScrollbar (Landroidx/compose/foundation/v2/ScrollbarAdapter;Landroidx/compose/ui/Modifier;ZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;II)V +public final class org/jetbrains/jewel/ui/component/ScrollableContainerKt { + public static final fun HorizontallyScrollableContainer (Landroidx/compose/foundation/lazy/LazyListState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun HorizontallyScrollableContainer (Landroidx/compose/foundation/lazy/grid/LazyGridState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun HorizontallyScrollableContainer (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/ScrollState;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun ScrollableContainer (Landroidx/compose/foundation/lazy/LazyListState;Landroidx/compose/foundation/lazy/LazyListState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun ScrollableContainer (Landroidx/compose/foundation/lazy/grid/LazyGridState;Landroidx/compose/foundation/lazy/grid/LazyGridState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun ScrollableContainer (Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/ScrollState;Landroidx/compose/foundation/ScrollState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun VerticallyScrollableContainer (Landroidx/compose/foundation/lazy/LazyListState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun VerticallyScrollableContainer (Landroidx/compose/foundation/lazy/grid/LazyGridState;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun VerticallyScrollableContainer (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/ScrollState;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun scrollbarContentSafePadding (Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;II)F +} + +public final class org/jetbrains/jewel/ui/component/ScrollbarKt { + public static final fun HorizontalScrollbar (Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/ui/Modifier;ZZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;ZLandroidx/compose/runtime/Composer;II)V + public static final fun VerticalScrollbar (Landroidx/compose/foundation/gestures/ScrollableState;Landroidx/compose/ui/Modifier;ZZLandroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;ZLandroidx/compose/runtime/Composer;II)V } public final class org/jetbrains/jewel/ui/component/SegmentedControlButtonData { @@ -870,13 +880,13 @@ public final class org/jetbrains/jewel/ui/component/TabsKt { } public final class org/jetbrains/jewel/ui/component/TextAreaKt { - public static final fun TextArea (Landroidx/compose/foundation/text/input/TextFieldState;Landroidx/compose/ui/Modifier;ZZLorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/foundation/text/KeyboardOptions;ILorg/jetbrains/jewel/ui/component/styling/TextAreaStyle;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;ZLandroidx/compose/runtime/Composer;III)V - 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 + public static final fun TextArea (Landroidx/compose/foundation/text/input/TextFieldState;Landroidx/compose/ui/Modifier;ZZLandroidx/compose/foundation/text/input/InputTransformation;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/input/KeyboardActionHandler;Landroidx/compose/foundation/text/input/TextFieldLineLimits;Lkotlin/jvm/functions/Function2;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/TextAreaStyle;Lorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/text/input/OutputTransformation;ZLandroidx/compose/foundation/ScrollState;Lorg/jetbrains/jewel/ui/component/styling/ScrollbarStyle;Landroidx/compose/runtime/Composer;III)V + 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/ui/Modifier;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/ui/Modifier;Landroidx/compose/runtime/Composer;III)V } public final class org/jetbrains/jewel/ui/component/TextFieldKt { - public static final fun TextField (Landroidx/compose/foundation/text/input/TextFieldState;Landroidx/compose/ui/Modifier;ZZLorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/foundation/text/KeyboardOptions;Lorg/jetbrains/jewel/ui/component/styling/TextFieldStyle;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;III)V + public static final fun TextField (Landroidx/compose/foundation/text/input/TextFieldState;Landroidx/compose/ui/Modifier;ZZLandroidx/compose/foundation/text/input/InputTransformation;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/input/KeyboardActionHandler;Lkotlin/jvm/functions/Function2;Landroidx/compose/foundation/interaction/MutableInteractionSource;Lorg/jetbrains/jewel/ui/component/styling/TextFieldStyle;Lorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Landroidx/compose/foundation/text/input/OutputTransformation;ZLandroidx/compose/runtime/Composer;III)V public static final fun TextField (Landroidx/compose/ui/text/input/TextFieldValue;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZZLorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/ui/text/input/VisualTransformation;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/KeyboardActions;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/jewel/ui/component/styling/TextFieldStyle;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;III)V public static final fun TextField (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZZLorg/jetbrains/jewel/ui/Outline;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ZLandroidx/compose/ui/text/input/VisualTransformation;Landroidx/compose/foundation/text/KeyboardOptions;Landroidx/compose/foundation/text/KeyboardActions;Lkotlin/jvm/functions/Function1;Lorg/jetbrains/jewel/ui/component/styling/TextFieldStyle;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/runtime/Composer;III)V } @@ -1862,16 +1872,20 @@ public final class org/jetbrains/jewel/ui/component/styling/RadioButtonStylingKt public final class org/jetbrains/jewel/ui/component/styling/ScrollbarColors { public static final field $stable I public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/ScrollbarColors$Companion; - public synthetic fun (JJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun getThumbBackground-0d7_KjU ()J - public final fun getThumbBackgroundHovered-0d7_KjU ()J - public final fun getThumbBackgroundPressed-0d7_KjU ()J + public final fun getThumbBackgroundActive-0d7_KjU ()J public final fun getThumbBorder-0d7_KjU ()J - public final fun getThumbBorderHovered-0d7_KjU ()J - public final fun getThumbBorderPressed-0d7_KjU ()J + public final fun getThumbBorderActive-0d7_KjU ()J + public final fun getThumbOpaqueBackground-0d7_KjU ()J + public final fun getThumbOpaqueBackgroundHovered-0d7_KjU ()J + public final fun getThumbOpaqueBorder-0d7_KjU ()J + public final fun getThumbOpaqueBorderHovered-0d7_KjU ()J public final fun getTrackBackground-0d7_KjU ()J - public final fun getTrackBackgroundHovered-0d7_KjU ()J + public final fun getTrackBackgroundExpanded-0d7_KjU ()J + public final fun getTrackOpaqueBackground-0d7_KjU ()J + public final fun getTrackOpaqueBackgroundHovered-0d7_KjU ()J public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1882,14 +1896,10 @@ public final class org/jetbrains/jewel/ui/component/styling/ScrollbarColors$Comp public final class org/jetbrains/jewel/ui/component/styling/ScrollbarMetrics { public static final field $stable I public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/ScrollbarMetrics$Companion; - public synthetic fun (Landroidx/compose/foundation/shape/CornerSize;FFFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Landroidx/compose/foundation/shape/CornerSize;FLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun getMinThumbLength-D9Ej5fM ()F public final fun getThumbCornerSize ()Landroidx/compose/foundation/shape/CornerSize; - public final fun getThumbThickness-D9Ej5fM ()F - public final fun getThumbThicknessExpanded-D9Ej5fM ()F - public final fun getTrackPadding ()Landroidx/compose/foundation/layout/PaddingValues; - public final fun getTrackPaddingExpanded ()Landroidx/compose/foundation/layout/PaddingValues; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1918,25 +1928,51 @@ public final class org/jetbrains/jewel/ui/component/styling/ScrollbarStylingKt { } public abstract interface class org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility { + public abstract fun getExpandAnimationDuration-UwyO8pc ()J + public abstract fun getLingerDuration-UwyO8pc ()J + public abstract fun getThumbColorAnimationDuration-UwyO8pc ()J + public abstract fun getTrackColorAnimationDuration-UwyO8pc ()J + public abstract fun getTrackPadding ()Landroidx/compose/foundation/layout/PaddingValues; + public abstract fun getTrackPaddingWithBorder ()Landroidx/compose/foundation/layout/PaddingValues; + public abstract fun getTrackThickness-D9Ej5fM ()F + public abstract fun getTrackThicknessExpanded-D9Ej5fM ()F } public final class org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible : org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility { public static final field $stable I - public static final field INSTANCE Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible; + public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion; + public synthetic fun (FLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z + public fun getExpandAnimationDuration-UwyO8pc ()J + public fun getLingerDuration-UwyO8pc ()J + public final fun getScrollbarBackgroundColorDark-0d7_KjU ()J + public final fun getScrollbarBackgroundColorLight-0d7_KjU ()J + public fun getThumbColorAnimationDuration-UwyO8pc ()J + public fun getTrackColorAnimationDuration-UwyO8pc ()J + public fun getTrackPadding ()Landroidx/compose/foundation/layout/PaddingValues; + public fun getTrackPaddingWithBorder ()Landroidx/compose/foundation/layout/PaddingValues; + public fun getTrackThickness-D9Ej5fM ()F + public fun getTrackThicknessExpanded-D9Ej5fM ()F public fun hashCode ()I public fun toString ()Ljava/lang/String; } +public final class org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$AlwaysVisible$Companion { +} + public final class org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling : org/jetbrains/jewel/ui/component/styling/ScrollbarVisibility { public static final field $stable I public static final field Companion Lorg/jetbrains/jewel/ui/component/styling/ScrollbarVisibility$WhenScrolling$Companion; - public synthetic fun (JJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (FFLandroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/layout/PaddingValues;JJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z - public final fun getAppearAnimationDuration-UwyO8pc ()J - public final fun getDisappearAnimationDuration-UwyO8pc ()J - public final fun getExpandAnimationDuration-UwyO8pc ()J - public final fun getLingerDuration-UwyO8pc ()J + public fun getExpandAnimationDuration-UwyO8pc ()J + public fun getLingerDuration-UwyO8pc ()J + public fun getThumbColorAnimationDuration-UwyO8pc ()J + public fun getTrackColorAnimationDuration-UwyO8pc ()J + public fun getTrackPadding ()Landroidx/compose/foundation/layout/PaddingValues; + public fun getTrackPaddingWithBorder ()Landroidx/compose/foundation/layout/PaddingValues; + public fun getTrackThickness-D9Ej5fM ()F + public fun getTrackThicknessExpanded-D9Ej5fM ()F public fun hashCode ()I public fun toString ()Ljava/lang/String; } diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/InputField.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/InputField.kt index afb7a873c..21f0ad04a 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/InputField.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/InputField.kt @@ -1,28 +1,26 @@ package org.jetbrains.jewel.ui.component +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.background import androidx.compose.foundation.interaction.FocusInteraction import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine -import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine +import androidx.compose.foundation.text.input.InputTransformation +import androidx.compose.foundation.text.input.KeyboardActionHandler +import androidx.compose.foundation.text.input.OutputTransformation +import androidx.compose.foundation.text.input.TextFieldDecorator +import androidx.compose.foundation.text.input.TextFieldLineLimits import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf 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.graphics.SolidColor import androidx.compose.ui.graphics.isSpecified @@ -30,7 +28,8 @@ import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.Density +import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval import org.jetbrains.jewel.foundation.Stroke import org.jetbrains.jewel.foundation.modifier.border import org.jetbrains.jewel.foundation.state.CommonStateBitMask.Active @@ -48,44 +47,43 @@ import org.jetbrains.jewel.ui.util.thenIf @Composable internal fun InputField( state: TextFieldState, + modifier: Modifier, enabled: Boolean, readOnly: Boolean, - outline: Outline, - undecorated: Boolean, + inputTransformation: InputTransformation?, + textStyle: TextStyle, keyboardOptions: KeyboardOptions, - singleLine: Boolean, - maxLines: Int, + onKeyboardAction: KeyboardActionHandler?, + lineLimits: TextFieldLineLimits, + onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)?, interactionSource: MutableInteractionSource, style: InputFieldStyle, - textStyle: TextStyle, - showScrollbar: Boolean, - modifier: Modifier, - decorationBox: @Composable (innerTextField: @Composable () -> Unit, state: InputFieldState) -> Unit, + outline: Outline, + outputTransformation: OutputTransformation?, + decorator: TextFieldDecorator?, + undecorated: Boolean = decorator == null, + scrollState: ScrollState, ) { - var inputState by remember(interactionSource) { - mutableStateOf(InputFieldState.of(enabled = enabled)) - } - remember(enabled) { inputState = inputState.copy(enabled = enabled) } + var inputFieldState by remember(interactionSource) { mutableStateOf(InputFieldState.of(enabled = enabled)) } + remember(enabled) { inputFieldState = inputFieldState.copy(enabled = enabled) } LaunchedEffect(interactionSource) { interactionSource.interactions.collect { interaction -> when (interaction) { - is FocusInteraction.Focus -> inputState = inputState.copy(focused = true) - is FocusInteraction.Unfocus -> inputState = inputState.copy(focused = false) + is FocusInteraction.Focus -> inputFieldState = inputFieldState.copy(focused = true) + is FocusInteraction.Unfocus -> inputFieldState = inputFieldState.copy(focused = false) } } } val colors = style.colors - val backgroundColor by colors.backgroundFor(inputState) + val backgroundColor by colors.backgroundFor(inputFieldState) val shape = RoundedCornerShape(style.metrics.cornerSize) val backgroundModifier = - Modifier.thenIf(!undecorated && backgroundColor.isSpecified) { - background(backgroundColor, shape) - } + Modifier.thenIf(!undecorated && backgroundColor.isSpecified) { background(backgroundColor, shape) } - val borderColor by style.colors.borderFor(inputState) + val borderColor by style.colors.borderFor(inputFieldState) val hasNoOutline = outline == Outline.None val borderModifier = Modifier.thenIf(!undecorated && borderColor.isSpecified && hasNoOutline) { @@ -97,65 +95,44 @@ internal fun InputField( ) } - val contentColor by colors.contentFor(inputState) + val contentColor by colors.contentFor(inputFieldState) val mergedTextStyle = textStyle.copy(color = contentColor) - val caretColor by colors.caretFor(inputState) - - val lineLimits = - when { - singleLine -> SingleLine - else -> MultiLine(maxLines) - } - - val scrollState = rememberScrollState() - val canScroll by remember { - derivedStateOf { - scrollState.canScrollBackward || scrollState.canScrollForward - } - } - - Box( - modifier = modifier - .then(backgroundModifier) - .then(borderModifier) - .thenIf(!undecorated && hasNoOutline) { focusOutline(inputState, shape) } - .outline(inputState, outline, shape, Stroke.Alignment.Center), - ) { - BasicTextField( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.CenterStart) - .thenIf(canScroll && showScrollbar) { padding(end = 12.dp) }, - state = state, - enabled = enabled, - readOnly = readOnly, - textStyle = mergedTextStyle, - cursorBrush = SolidColor(caretColor), - keyboardOptions = keyboardOptions, - lineLimits = lineLimits, - interactionSource = interactionSource, - decorator = { innerTextField: @Composable () -> Unit -> decorationBox(innerTextField, inputState) }, - scrollState = scrollState, - ) + val caretColor by colors.caretFor(inputFieldState) - if (showScrollbar) { - VerticalScrollbar( - scrollState = scrollState, - modifier = Modifier.align(Alignment.CenterEnd), - interactionSource = interactionSource, - ) - } - } + BasicTextField( + state = state, + modifier = + modifier + .then(backgroundModifier) + .then(borderModifier) + .thenIf(!undecorated && hasNoOutline) { focusOutline(inputFieldState, shape) } + .outline(inputFieldState, outline, shape, Stroke.Alignment.Center), + enabled = enabled, + readOnly = readOnly, + inputTransformation = inputTransformation, + textStyle = mergedTextStyle, + keyboardOptions = keyboardOptions, + onKeyboardAction = onKeyboardAction, + lineLimits = lineLimits, + onTextLayout = onTextLayout, + interactionSource = interactionSource, + cursorBrush = SolidColor(caretColor), + outputTransformation = outputTransformation, + decorator = decorator, + scrollState = scrollState, + ) } -@Deprecated("Please use InputField(state) instead. If you want to observe text changes, use snapshotFlow { state.text }") +@Deprecated("NO") +@ScheduledForRemoval @Composable internal fun InputField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, + modifier: Modifier, enabled: Boolean, - outline: Outline, readOnly: Boolean, + outline: Outline, undecorated: Boolean, visualTransformation: VisualTransformation, keyboardOptions: KeyboardOptions, @@ -166,7 +143,6 @@ internal fun InputField( interactionSource: MutableInteractionSource, style: InputFieldStyle, textStyle: TextStyle, - modifier: Modifier, decorationBox: @Composable (innerTextField: @Composable () -> Unit, state: InputFieldState) -> Unit, ) { var inputState by remember(interactionSource) { mutableStateOf(InputFieldState.of(enabled = enabled)) } @@ -186,9 +162,7 @@ internal fun InputField( val shape = RoundedCornerShape(style.metrics.cornerSize) val backgroundModifier = - Modifier.thenIf(!undecorated && backgroundColor.isSpecified) { - background(backgroundColor, shape) - } + Modifier.thenIf(!undecorated && backgroundColor.isSpecified) { background(backgroundColor, shape) } val borderColor by style.colors.borderFor(inputState) val hasNoOutline = outline == Outline.None @@ -226,9 +200,8 @@ internal fun InputField( interactionSource = interactionSource, singleLine = singleLine, maxLines = maxLines, - decorationBox = @Composable { innerTextField: @Composable () -> Unit -> - decorationBox(innerTextField, inputState) - }, + decorationBox = + @Composable { innerTextField: @Composable () -> Unit -> decorationBox(innerTextField, inputState) }, ) } @@ -256,14 +229,7 @@ public value class InputFieldState(public val state: ULong) : FocusableComponent pressed: Boolean = isPressed, hovered: Boolean = isHovered, active: Boolean = isActive, - ): InputFieldState = - of( - enabled = enabled, - focused = focused, - pressed = pressed, - hovered = hovered, - active = active, - ) + ): InputFieldState = of(enabled = enabled, focused = focused, pressed = pressed, hovered = hovered, active = active) override fun toString(): String = "${javaClass.simpleName}(isEnabled=$isEnabled, isFocused=$isFocused, " + @@ -283,7 +249,7 @@ public value class InputFieldState(public val state: ULong) : FocusableComponent (if (focused) Focused else 0UL) or (if (hovered) Hovered else 0UL) or (if (pressed) Pressed else 0UL) or - (if (active) Active else 0UL), + (if (active) Active else 0UL) ) } } diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/ScrollableContainer.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/ScrollableContainer.kt new file mode 100644 index 000000000..656ce1675 --- /dev/null +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/ScrollableContainer.kt @@ -0,0 +1,484 @@ +@file:Suppress("DuplicatedCode") // Lots of identical-looking but not deduplicable code + +package org.jetbrains.jewel.ui.component + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.PointerIcon +import androidx.compose.ui.input.pointer.pointerHoverIcon +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.jetbrains.jewel.foundation.ExperimentalJewelApi +import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.AlwaysVisible +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.WhenScrolling +import org.jetbrains.jewel.ui.theme.scrollbarStyle +import org.jetbrains.skiko.OS +import org.jetbrains.skiko.hostOs +import kotlin.time.Duration + +private const val ID_CONTENT = "VerticallyScrollableContainer_content" +private const val ID_VERTICAL_SCROLLBAR = "VerticallyScrollableContainer_verticalScrollbar" +private const val ID_HORIZONTAL_SCROLLBAR = "VerticallyScrollableContainer_horizontalScrollbar" + +@Composable +public fun VerticallyScrollableContainer( + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + scrollState: ScrollState = rememberScrollState(), + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = null, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT).verticalScroll(scrollState)) { content() } + } +} + +@Composable +internal fun TextAreaScrollableContainer( + scrollState: ScrollState, + style: ScrollbarStyle, + contentModifier: Modifier, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar( + scrollState, + style = style, + modifier = Modifier.pointerHoverIcon(PointerIcon.Default), + keepVisible = keepVisible, + ) + }, + horizontalScrollbar = null, + modifier = Modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(contentModifier.layoutId(ID_CONTENT)) { content() } + } +} + +@Composable +public fun VerticallyScrollableContainer( + scrollState: LazyListState, + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = null, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +@Composable +public fun VerticallyScrollableContainer( + scrollState: LazyGridState, + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = null, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +@Composable +public fun HorizontallyScrollableContainer( + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + scrollState: ScrollState = rememberScrollState(), + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = null, + horizontalScrollbar = { + HorizontalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT).horizontalScroll(scrollState)) { content() } + } +} + +@Composable +public fun HorizontallyScrollableContainer( + scrollState: LazyListState, + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = null, + horizontalScrollbar = { + HorizontalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +@Composable +public fun HorizontallyScrollableContainer( + scrollState: LazyGridState, + modifier: Modifier = Modifier, + scrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = null, + horizontalScrollbar = { + HorizontalScrollbar(scrollState, scrollbarModifier, style = style, keepVisible = keepVisible) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +@ExperimentalJewelApi +@Composable +public fun ScrollableContainer( + modifier: Modifier = Modifier, + verticalScrollState: ScrollState = rememberScrollState(), + horizontalScrollState: ScrollState = rememberScrollState(), + verticalScrollbarModifier: Modifier = Modifier, + horizontalScrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(verticalScrollState, verticalScrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = { + HorizontalScrollbar( + horizontalScrollState, + horizontalScrollbarModifier, + style = style, + keepVisible = keepVisible, + ) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT).verticalScroll(verticalScrollState).horizontalScroll(horizontalScrollState)) { + content() + } + } +} + +@ExperimentalJewelApi +@Composable +public fun ScrollableContainer( + verticalScrollState: LazyListState, + horizontalScrollState: LazyListState, + modifier: Modifier = Modifier, + verticalScrollbarModifier: Modifier = Modifier, + horizontalScrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(verticalScrollState, verticalScrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = { + HorizontalScrollbar( + horizontalScrollState, + horizontalScrollbarModifier, + style = style, + keepVisible = keepVisible, + ) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +@ExperimentalJewelApi +@Composable +public fun ScrollableContainer( + verticalScrollState: LazyGridState, + horizontalScrollState: LazyGridState, + modifier: Modifier = Modifier, + verticalScrollbarModifier: Modifier = Modifier, + horizontalScrollbarModifier: Modifier = Modifier, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + content: @Composable () -> Unit, +) { + var keepVisible by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + ScrollableContainerImpl( + verticalScrollbar = { + VerticalScrollbar(verticalScrollState, verticalScrollbarModifier, style = style, keepVisible = keepVisible) + }, + horizontalScrollbar = { + HorizontalScrollbar( + horizontalScrollState, + horizontalScrollbarModifier, + style = style, + keepVisible = keepVisible, + ) + }, + modifier = modifier.withKeepVisible(style.scrollbarVisibility.lingerDuration, scope) { keepVisible = it }, + scrollbarStyle = style, + ) { + Box(Modifier.layoutId(ID_CONTENT)) { content() } + } +} + +private fun Modifier.withKeepVisible( + lingerDuration: Duration, + scope: CoroutineScope, + onKeepVisibleChange: (Boolean) -> Unit, +) = + pointerInput(scope) { + var delayJob: Job? = null + awaitEachGesture { + val event = awaitPointerEvent() + if (event.type == PointerEventType.Move) { + delayJob?.cancel() + onKeepVisibleChange(true) + delayJob = + scope.launch { + delay(lingerDuration) + onKeepVisibleChange(false) + } + } + } + } + +@Composable +private fun ScrollableContainerImpl( + verticalScrollbar: (@Composable () -> Unit)?, + horizontalScrollbar: (@Composable () -> Unit)?, + modifier: Modifier, + scrollbarStyle: ScrollbarStyle, + content: @Composable () -> Unit, +) { + Layout( + content = { + content() + + if (verticalScrollbar != null) { + Box(Modifier.layoutId(ID_VERTICAL_SCROLLBAR)) { verticalScrollbar() } + } + + if (horizontalScrollbar != null) { + Box(Modifier.layoutId(ID_HORIZONTAL_SCROLLBAR)) { horizontalScrollbar() } + } + }, + modifier, + ) { measurables, incomingConstraints -> + val verticalScrollbarMeasurable = measurables.find { it.layoutId == ID_VERTICAL_SCROLLBAR } + val horizontalScrollbarMeasurable = measurables.find { it.layoutId == ID_HORIZONTAL_SCROLLBAR } + + // Leaving the bottom-end corner empty when both scrollbars visible at the same time + val sizeOffsetWhenBothVisible = + if (verticalScrollbarMeasurable != null && horizontalScrollbarMeasurable != null) { + scrollbarStyle.scrollbarVisibility.trackThicknessExpanded.roundToPx() + } else 0 + + val verticalScrollbarPlaceable = + if (verticalScrollbarMeasurable != null) { + val verticalScrollbarConstraints = + Constraints.fixedHeight(incomingConstraints.maxHeight - sizeOffsetWhenBothVisible) + verticalScrollbarMeasurable.measure(verticalScrollbarConstraints) + } else null + + val horizontalScrollbarPlaceable = + if (horizontalScrollbarMeasurable != null) { + val horizontalScrollbarConstraints = + Constraints.fixedWidth(incomingConstraints.maxWidth - sizeOffsetWhenBothVisible) + horizontalScrollbarMeasurable.measure(horizontalScrollbarConstraints) + } else null + + val isMacOs = hostOs == OS.MacOS + val contentMeasurable = measurables.find { it.layoutId == ID_CONTENT } ?: error("Content not provided") + val contentConstraints = + computeContentConstraints( + scrollbarStyle, + isMacOs, + incomingConstraints, + verticalScrollbarPlaceable, + horizontalScrollbarPlaceable, + ) + val contentPlaceable = contentMeasurable.measure(contentConstraints) + + val isAlwaysVisible = scrollbarStyle.scrollbarVisibility is AlwaysVisible + val vScrollbarWidth = + when { + !isMacOs -> 0 + isAlwaysVisible -> verticalScrollbarPlaceable?.width ?: 0 + else -> 0 + } + val width = contentPlaceable.width + vScrollbarWidth + + val hScrollbarHeight = + when { + !isMacOs -> 0 + isAlwaysVisible -> horizontalScrollbarPlaceable?.height ?: 0 + else -> 0 + } + val height = contentPlaceable.height + hScrollbarHeight + + layout(width, height) { + contentPlaceable.placeRelative(x = 0, y = 0, zIndex = 0f) + verticalScrollbarPlaceable?.placeRelative(x = width - verticalScrollbarPlaceable.width, y = 0, zIndex = 1f) + horizontalScrollbarPlaceable?.placeRelative( + x = 0, + y = height - horizontalScrollbarPlaceable.height, + zIndex = 1f, + ) + } + } +} + +private fun computeContentConstraints( + scrollbarStyle: ScrollbarStyle, + isMacOs: Boolean, + incomingConstraints: Constraints, + verticalScrollbarPlaceable: Placeable?, + horizontalScrollbarPlaceable: Placeable?, +): Constraints { + val visibility = scrollbarStyle.scrollbarVisibility + + fun width() = + if (incomingConstraints.hasBoundedWidth) { + val maxWidth = incomingConstraints.maxWidth + when { + !isMacOs -> maxWidth // Scrollbars on Win/Linux are always overlaid + visibility is AlwaysVisible -> maxWidth - (verticalScrollbarPlaceable?.width ?: 0) + visibility is WhenScrolling -> maxWidth + else -> error("Unsupported visibility style: $visibility") + } + } else { + error("Incoming constraints have infinite width, should not use fixed width") + } + + fun height() = + if (incomingConstraints.hasBoundedHeight) { + val maxHeight = incomingConstraints.maxHeight + when { + !isMacOs -> maxHeight // Scrollbars on Win/Linux are always overlaid + visibility is AlwaysVisible -> maxHeight - (horizontalScrollbarPlaceable?.height ?: 0) + visibility is WhenScrolling -> maxHeight + else -> error("Unsupported visibility style: $visibility") + } + } else { + error("Incoming constraints have infinite height, should not use fixed height") + } + + return when { + incomingConstraints.hasBoundedWidth && incomingConstraints.hasBoundedHeight -> { + Constraints.fixed(width(), height()) + } + !incomingConstraints.hasBoundedWidth && incomingConstraints.hasBoundedHeight -> { + Constraints.fixedHeight(height()) + } + incomingConstraints.hasBoundedWidth && !incomingConstraints.hasBoundedHeight -> { + Constraints.fixedWidth(width()) + } + else -> Constraints() + } +} + +/** + * A content padding to apply when you want to ensure the content is not overlapped by scrollbars. This value can be + * used for both vertical and horizontal scrollbars. + * + * When the [style] is [AlwaysVisible], this value is zero, since the various `ScrollableContainer`s will prevent + * overlapping anyway. If it is [WhenScrolling], this value will be the maximum thickness of the scrollbar. + */ +@Composable +public fun scrollbarContentSafePadding(style: ScrollbarStyle = JewelTheme.scrollbarStyle): Dp = + when { + hostOs != OS.MacOS -> style.scrollbarVisibility.trackThicknessExpanded + style.scrollbarVisibility is AlwaysVisible -> 0.dp + style.scrollbarVisibility is WhenScrolling -> style.scrollbarVisibility.trackThicknessExpanded + else -> error("Unsupported visibility: ${style.scrollbarVisibility}") + } diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbar.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbar.kt new file mode 100644 index 000000000..ab763b0d2 --- /dev/null +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbar.kt @@ -0,0 +1,721 @@ +package org.jetbrains.jewel.ui.component + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.awaitHorizontalDragOrCancellation +import androidx.compose.foundation.gestures.awaitVerticalDragOrCancellation +import androidx.compose.foundation.gestures.drag +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.hoverable +import androidx.compose.foundation.interaction.DragInteraction +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsHoveredAsState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.foundation.rememberScrollbarAdapter +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.text.TextFieldScrollState +import androidx.compose.foundation.v2.ScrollbarAdapter +import androidx.compose.foundation.v2.maxScrollOffset +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.input.pointer.PointerInputScope +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.positionChange +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.constrainHeight +import androidx.compose.ui.unit.constrainWidth +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility +import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.AlwaysVisible +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior.JumpToSpot +import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior.NextPage +import org.jetbrains.jewel.ui.theme.scrollbarStyle +import org.jetbrains.jewel.ui.util.thenIf +import kotlin.math.roundToInt + +@Composable +public fun VerticalScrollbar( + scrollState: ScrollableState, + modifier: Modifier = Modifier, + reverseLayout: Boolean = false, + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + keepVisible: Boolean = false, +) { + BaseScrollbar( + scrollState = scrollState, + modifier = modifier, + reverseLayout = reverseLayout, + enabled = enabled, + interactionSource = interactionSource, + isVertical = true, + style = style, + keepVisible = keepVisible, + ) +} + +@Composable +public fun HorizontalScrollbar( + scrollState: ScrollableState, + modifier: Modifier = Modifier, + reverseLayout: Boolean = false, + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + style: ScrollbarStyle = JewelTheme.scrollbarStyle, + keepVisible: Boolean = false, +) { + BaseScrollbar( + scrollState = scrollState, + modifier = modifier, + reverseLayout = reverseLayout, + enabled = enabled, + interactionSource = interactionSource, + isVertical = false, + style = style, + keepVisible = keepVisible, + ) +} + +@Composable +private fun BaseScrollbar( + scrollState: ScrollableState, + modifier: Modifier, + reverseLayout: Boolean, + enabled: Boolean, + interactionSource: MutableInteractionSource, + isVertical: Boolean, + style: ScrollbarStyle, + keepVisible: Boolean, +) { + val dragInteraction = remember { mutableStateOf(null) } + DisposableEffect(interactionSource) { + onDispose { + dragInteraction.value?.let { interaction -> + interactionSource.tryEmit(DragInteraction.Cancel(interaction)) + dragInteraction.value = null + } + } + } + + val visibilityStyle = style.scrollbarVisibility + val isOpaque = visibilityStyle is AlwaysVisible + var isExpanded by remember { mutableStateOf(false) } + val isHovered by interactionSource.collectIsHoveredAsState() + var showScrollbar by remember { mutableStateOf(false) } + + val isDragging = dragInteraction.value != null + val isScrolling = scrollState.isScrollInProgress || isDragging + val isActive = isOpaque || isScrolling || (keepVisible && showScrollbar) + + if (isHovered && showScrollbar) isExpanded = true + + LaunchedEffect(isActive, isHovered, showScrollbar) { + val isVisibleAndHovered = showScrollbar && isHovered + if (isActive || isVisibleAndHovered) { + showScrollbar = true + } else { + launch { + delay(visibilityStyle.lingerDuration) + showScrollbar = false + isExpanded = false + } + } + } + + val animatedThickness by + animateDpAsState( + if (isExpanded) visibilityStyle.trackThicknessExpanded else visibilityStyle.trackThickness, + tween(visibilityStyle.expandAnimationDuration.inWholeMilliseconds.toInt(), easing = LinearEasing), + "scrollbar_thickness", + ) + + val adapter = + when (scrollState) { + is LazyListState -> rememberScrollbarAdapter(scrollState) + is LazyGridState -> rememberScrollbarAdapter(scrollState) + is ScrollState -> rememberScrollbarAdapter(scrollState) + is TextFieldScrollState -> rememberScrollbarAdapter(scrollState) + else -> error("Unsupported scroll state type: ${scrollState::class.qualifiedName}") + } + + with(LocalDensity.current) { + var containerSize by remember { mutableIntStateOf(0) } + val thumbMinHeight = style.metrics.minThumbLength.toPx() + + val coroutineScope = rememberCoroutineScope() + val sliderAdapter = + remember(adapter, containerSize, thumbMinHeight, reverseLayout, isVertical, coroutineScope) { + SliderAdapter(adapter, containerSize, thumbMinHeight, reverseLayout, isVertical, coroutineScope) + } + + val thumbBackgroundColor = getThumbBackgroundColor(isOpaque, isHovered, isScrolling, style, showScrollbar) + val thumbBorderColor = getThumbBorderColor(isOpaque, isHovered, isScrolling, style, showScrollbar) + val hasVisibleBorder = !areTheSameColor(thumbBackgroundColor, thumbBorderColor) + val trackPadding = + if (hasVisibleBorder) visibilityStyle.trackPaddingWithBorder else visibilityStyle.trackPadding + + val thumbThicknessPx = + if (isVertical) { + val layoutDirection = LocalLayoutDirection.current + animatedThickness - + trackPadding.calculateLeftPadding(layoutDirection) - + trackPadding.calculateRightPadding(layoutDirection) + } else { + animatedThickness - trackPadding.calculateTopPadding() - trackPadding.calculateBottomPadding() + } + .roundToPx() + + val measurePolicy = + if (isVertical) { + remember(sliderAdapter, thumbThicknessPx) { + verticalMeasurePolicy(sliderAdapter, { containerSize = it }, thumbThicknessPx) + } + } else { + remember(sliderAdapter, thumbThicknessPx) { + horizontalMeasurePolicy(sliderAdapter, { containerSize = it }, thumbThicknessPx) + } + } + + val canScroll = sliderAdapter.thumbSize < containerSize + + val trackBackground by + animateColorAsState( + targetValue = getTrackColor(isOpaque, isDragging, isHovered, style, isExpanded), + animationSpec = trackColorTween(visibilityStyle), + label = "scrollbar_trackBackground", + ) + + Layout( + content = { + Thumb( + showScrollbar, + visibilityStyle, + canScroll, + enabled, + interactionSource, + dragInteraction, + sliderAdapter, + thumbBackgroundColor, + thumbBorderColor, + hasVisibleBorder, + style.metrics.thumbCornerSize, + ) + }, + modifier = + modifier + .thenIf(showScrollbar && canScroll && isExpanded) { background(trackBackground) } + .scrollable( + state = scrollState, + orientation = if (isVertical) Orientation.Vertical else Orientation.Horizontal, + enabled = enabled, + reverseDirection = true, // Not sure why it's needed, but it is — TODO revisit this + ) + .padding(trackPadding) + .hoverable(interactionSource = interactionSource) + .thenIf(enabled && showScrollbar) { + scrollOnPressTrack(style.trackClickBehavior, isVertical, reverseLayout, sliderAdapter) + }, + measurePolicy = measurePolicy, + ) + } +} + +private fun getTrackColor( + isOpaque: Boolean, + isDragging: Boolean, + isHovered: Boolean, + style: ScrollbarStyle, + isExpanded: Boolean, +) = + if (isOpaque) { + if (isHovered || isDragging) { + style.colors.trackOpaqueBackgroundHovered + } else { + style.colors.trackOpaqueBackground + } + } else { + if (isExpanded) { + style.colors.trackBackgroundExpanded + } else { + style.colors.trackBackground + } + } + +private fun getThumbBackgroundColor( + isOpaque: Boolean, + isHovered: Boolean, + isScrolling: Boolean, + style: ScrollbarStyle, + showScrollbar: Boolean, +) = + if (isOpaque) { + if (isHovered || isScrolling) { + style.colors.thumbOpaqueBackgroundHovered + } else { + style.colors.thumbOpaqueBackground + } + } else { + if (showScrollbar) { + style.colors.thumbBackgroundActive + } else { + style.colors.thumbBackground + } + } + +private fun getThumbBorderColor( + isOpaque: Boolean, + isHovered: Boolean, + isScrolling: Boolean, + style: ScrollbarStyle, + showScrollbar: Boolean, +) = + if (isOpaque) { + if (isHovered || isScrolling) { + style.colors.thumbOpaqueBorderHovered + } else { + style.colors.thumbOpaqueBorder + } + } else { + if (showScrollbar) { + style.colors.thumbBorderActive + } else { + style.colors.thumbBorder + } + } + +private fun areTheSameColor(first: Color, second: Color) = first.toArgb() == second.toArgb() + +@Composable +private fun Thumb( + showScrollbar: Boolean, + visibilityStyle: ScrollbarVisibility, + canScroll: Boolean, + enabled: Boolean, + interactionSource: MutableInteractionSource, + dragInteraction: MutableState, + sliderAdapter: SliderAdapter, + thumbBackgroundColor: Color, + thumbBorderColor: Color, + hasVisibleBorder: Boolean, + cornerSize: CornerSize, +) { + val background by + animateColorAsState( + targetValue = thumbBackgroundColor, + animationSpec = thumbColorTween(showScrollbar, visibilityStyle), + label = "scrollbar_thumbBackground", + ) + + val border by + animateColorAsState( + targetValue = thumbBorderColor, + animationSpec = thumbColorTween(showScrollbar, visibilityStyle), + label = "scrollbar_thumbBorder", + ) + + val borderWidth = 1.dp + val density = LocalDensity.current + Box( + Modifier.layoutId("thumb") + .thenIf(canScroll) { drawThumb(background, borderWidth, border, hasVisibleBorder, cornerSize, density) } + .thenIf(enabled) { scrollbarDrag(interactionSource, dragInteraction, sliderAdapter) } + ) +} + +private fun Modifier.drawThumb( + backgroundColor: Color, + borderWidth: Dp, + borderColor: Color, + hasVisibleBorder: Boolean, + cornerSize: CornerSize, + density: Density, +) = drawBehind { + val borderWidthPx = if (hasVisibleBorder) borderWidth.toPx() else 0f + + // First, draw the background, leaving room for the border around it + val bgCornerRadius = CornerRadius((cornerSize.toPx(size, density) - borderWidthPx * 2).coerceAtLeast(0f)) + drawRoundRect( + color = backgroundColor, + topLeft = Offset(borderWidthPx, borderWidthPx), + size = Size(size.width - borderWidthPx * 2, size.height - borderWidthPx * 2f), + cornerRadius = bgCornerRadius, + ) + + // Then, draw the border itself + if (hasVisibleBorder) { + val strokeCornerRadius = CornerRadius(cornerSize.toPx(size, density)) + drawRoundRect( + color = borderColor, + topLeft = Offset(borderWidthPx / 2, borderWidthPx / 2), + size = Size(size.width - borderWidthPx, size.height - borderWidthPx), + cornerRadius = strokeCornerRadius, + style = Stroke(borderWidthPx), + ) + } +} + +private fun trackColorTween(visibility: ScrollbarVisibility) = + tween(visibility.trackColorAnimationDuration.inWholeMilliseconds.toInt(), easing = LinearEasing) + +private fun thumbColorTween(showScrollbar: Boolean, visibility: ScrollbarVisibility) = + tween( + durationMillis = + if (visibility is AlwaysVisible || !showScrollbar) { + visibility.thumbColorAnimationDuration.inWholeMilliseconds.toInt() + } else 0, + delayMillis = + when { + visibility is AlwaysVisible && !showScrollbar -> visibility.lingerDuration.inWholeMilliseconds.toInt() + else -> 0 + }, + easing = LinearEasing, + ) + +// =========================================================================== +// Note: most of the code below is copied and adapted from the stock scrollbar +// =========================================================================== + +private val SliderAdapter.thumbPixelRange: IntRange + get() { + val start = position.roundToInt() + val endExclusive = start + thumbSize.roundToInt() + + return (start until endExclusive) + } + +private val IntRange.size + get() = last + 1 - first + +private fun verticalMeasurePolicy(sliderAdapter: SliderAdapter, setContainerSize: (Int) -> Unit, thumbThickness: Int) = + MeasurePolicy { measurables, constraints -> + setContainerSize(constraints.maxHeight) + val pixelRange = sliderAdapter.thumbPixelRange + val placeable = + measurables.first().measure(Constraints.fixed(constraints.constrainWidth(thumbThickness), pixelRange.size)) + layout(placeable.width, constraints.maxHeight) { placeable.place(0, pixelRange.first) } + } + +private fun horizontalMeasurePolicy( + sliderAdapter: SliderAdapter, + setContainerSize: (Int) -> Unit, + thumbThickness: Int, +) = MeasurePolicy { measurables, constraints -> + setContainerSize(constraints.maxWidth) + val pixelRange = sliderAdapter.thumbPixelRange + val placeable = + measurables.first().measure(Constraints.fixed(pixelRange.size, constraints.constrainHeight(thumbThickness))) + layout(constraints.maxWidth, placeable.height) { placeable.place(pixelRange.first, 0) } +} + +private fun Modifier.scrollbarDrag( + interactionSource: MutableInteractionSource, + draggedInteraction: MutableState, + sliderAdapter: SliderAdapter, +): Modifier = composed { + val currentInteractionSource by rememberUpdatedState(interactionSource) + val currentDraggedInteraction by rememberUpdatedState(draggedInteraction) + val currentSliderAdapter by rememberUpdatedState(sliderAdapter) + + pointerInput(Unit) { + awaitEachGesture { + val down = awaitFirstDown(requireUnconsumed = false) + val interaction = DragInteraction.Start() + currentInteractionSource.tryEmit(interaction) + currentDraggedInteraction.value = interaction + currentSliderAdapter.onDragStarted() + val isSuccess = + drag(down.id) { change -> + currentSliderAdapter.onDragDelta(change.positionChange()) + change.consume() + } + val finishInteraction = + if (isSuccess) { + DragInteraction.Stop(interaction) + } else { + DragInteraction.Cancel(interaction) + } + currentInteractionSource.tryEmit(finishInteraction) + currentDraggedInteraction.value = null + } + } +} + +private fun Modifier.scrollOnPressTrack( + clickBehavior: TrackClickBehavior, + isVertical: Boolean, + reverseLayout: Boolean, + sliderAdapter: SliderAdapter, +) = composed { + val coroutineScope = rememberCoroutineScope() + val scroller = + remember(sliderAdapter, coroutineScope, reverseLayout, clickBehavior) { + TrackPressScroller(coroutineScope, sliderAdapter, reverseLayout, clickBehavior) + } + + Modifier.pointerInput(scroller) { detectScrollViaTrackGestures(isVertical = isVertical, scroller = scroller) } +} + +/** Responsible for scrolling when the scrollbar track is pressed (outside the thumb). */ +private class TrackPressScroller( + private val coroutineScope: CoroutineScope, + private val sliderAdapter: SliderAdapter, + private val reverseLayout: Boolean, + private val clickBehavior: TrackClickBehavior, +) { + /** The current direction of scroll (1: down/right, -1: up/left, 0: not scrolling) */ + private var direction = 0 + + /** The currently pressed location (in pixels) on the scrollable axis. */ + private var offset: Float? = null + + /** The job that keeps scrolling while the track is pressed. */ + private var job: Job? = null + + /** Calculates the direction of scrolling towards the given offset (in pixels). */ + private fun directionOfScrollTowards(offset: Float): Int { + val pixelRange = sliderAdapter.thumbPixelRange + return when { + offset < pixelRange.first -> if (reverseLayout) 1 else -1 + offset > pixelRange.last -> if (reverseLayout) -1 else 1 + else -> 0 + } + } + + /** Scrolls once towards the current offset, if it matches the direction of the current gesture. */ + private suspend fun scrollTowardsCurrentOffset() { + offset?.let { + val currentDirection = directionOfScrollTowards(it) + if (currentDirection != direction) { + return + } + with(sliderAdapter.adapter) { scrollTo(scrollOffset + currentDirection * viewportSize) } + } + } + + /** Starts the job that scrolls continuously towards the current offset. */ + private fun startScrollingByPage() { + job?.cancel() + job = + coroutineScope.launch { + scrollTowardsCurrentOffset() + delay(DELAY_BEFORE_SECOND_SCROLL_ON_TRACK_PRESS) + while (true) { + scrollTowardsCurrentOffset() + delay(DELAY_BETWEEN_SCROLLS_ON_TRACK_PRESS) + } + } + } + + /** Invoked on the first press for a gesture. */ + fun onPress(offset: Float) { + this.offset = offset + this.direction = directionOfScrollTowards(offset) + + if (direction == 0) return + + if (clickBehavior == NextPage) startScrollingByPage() + else if (clickBehavior == JumpToSpot) scrollToOffset(offset) + } + + /** Invoked when the pointer moves while pressed during the gesture. */ + fun onMovePressed(offset: Float) { + this.offset = offset + if (clickBehavior == JumpToSpot) scrollToOffset(offset) + } + + /** Cleans up when the gesture finishes. */ + private fun cleanupAfterGesture() { + job?.cancel() + direction = 0 + offset = null + } + + /** Invoked when the button is released. */ + fun onRelease() { + cleanupAfterGesture() + } + + private fun scrollToOffset(offset: Float) { + job?.cancel() + job = + coroutineScope.launch { + val contentSize = sliderAdapter.adapter.contentSize + val scrollOffset = offset / sliderAdapter.adapter.viewportSize * contentSize + sliderAdapter.adapter.scrollTo(scrollOffset) + } + } + + /** Invoked when the gesture is cancelled. */ + fun onGestureCancelled() { + cleanupAfterGesture() + // Maybe revert to the initial position? + } +} + +/** + * Detects the pointer events relevant for the "scroll by pressing on the track outside the thumb" gesture and calls the + * corresponding methods in the [scroller]. + */ +private suspend fun PointerInputScope.detectScrollViaTrackGestures(isVertical: Boolean, scroller: TrackPressScroller) { + fun Offset.onScrollAxis() = if (isVertical) y else x + + awaitEachGesture { + val down = awaitFirstDown() + scroller.onPress(down.position.onScrollAxis()) + + while (true) { + val drag = + if (isVertical) { + awaitVerticalDragOrCancellation(down.id) + } else { + awaitHorizontalDragOrCancellation(down.id) + } + + if (drag == null) { + scroller.onGestureCancelled() + break + } else if (!drag.pressed) { + scroller.onRelease() + break + } else { + scroller.onMovePressed(drag.position.onScrollAxis()) + } + } + } +} + +/** The delay between the 1st and 2nd scroll while the scrollbar track is pressed outside the thumb. */ +internal const val DELAY_BEFORE_SECOND_SCROLL_ON_TRACK_PRESS: Long = 300L + +/** The delay between each subsequent (after the 2nd) scroll while the scrollbar track is pressed outside the thumb. */ +internal const val DELAY_BETWEEN_SCROLLS_ON_TRACK_PRESS: Long = 100L + +internal class SliderAdapter( + val adapter: ScrollbarAdapter, + private val trackSize: Int, + private val minHeight: Float, + private val reverseLayout: Boolean, + private val isVertical: Boolean, + private val coroutineScope: CoroutineScope, +) { + private val contentSize + get() = adapter.contentSize + + private val visiblePart: Double + get() { + val contentSize = contentSize + return if (contentSize == 0.0) { + 1.0 + } else { + (adapter.viewportSize / contentSize).coerceAtMost(1.0) + } + } + + val thumbSize + get() = (trackSize * visiblePart).coerceAtLeast(minHeight.toDouble()) + + private val scrollScale: Double + get() { + val extraScrollbarSpace = trackSize - thumbSize + val extraContentSpace = adapter.maxScrollOffset // == contentSize - viewportSize + return if (extraContentSpace == 0.0) 1.0 else extraScrollbarSpace / extraContentSpace + } + + private val rawPosition: Double + get() = scrollScale * adapter.scrollOffset + + val position: Double + get() = if (reverseLayout) trackSize - thumbSize - rawPosition else rawPosition + + val bounds + get() = position..position + thumbSize + + // How much of the current drag was ignored because we've reached the end of the scrollbar area + private var unscrolledDragDistance = 0.0 + + /** Called when the thumb dragging starts */ + fun onDragStarted() { + unscrolledDragDistance = 0.0 + } + + private suspend fun setPosition(value: Double) { + val rawPosition = + if (reverseLayout) { + trackSize - thumbSize - value + } else { + value + } + adapter.scrollTo(rawPosition / scrollScale) + } + + private val dragMutex = Mutex() + + /** Called on every movement while dragging the thumb */ + fun onDragDelta(offset: Offset) { + coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { + // Mutex is used to ensure that all earlier drag deltas were applied + // before calculating a new raw position + dragMutex.withLock { + val dragDelta = if (isVertical) offset.y else offset.x + val maxScrollPosition = adapter.maxScrollOffset * scrollScale + val currentPosition = position + val targetPosition = + (currentPosition + dragDelta + unscrolledDragDistance).coerceIn(0.0, maxScrollPosition) + val sliderDelta = targetPosition - currentPosition + + // Have to add to position for smooth content scroll if the items are of different + // size + val newPos = position + sliderDelta + setPosition(newPos) + unscrolledDragDistance += dragDelta - sliderDelta + } + } + } +} diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbars.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbars.kt deleted file mode 100644 index 0bab1a1d8..000000000 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/Scrollbars.kt +++ /dev/null @@ -1,679 +0,0 @@ -package org.jetbrains.jewel.ui.component - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.animateContentSize -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.ScrollState -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.ScrollableState -import androidx.compose.foundation.gestures.awaitEachGesture -import androidx.compose.foundation.gestures.awaitFirstDown -import androidx.compose.foundation.gestures.awaitHorizontalDragOrCancellation -import androidx.compose.foundation.gestures.awaitVerticalDragOrCancellation -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.drag -import androidx.compose.foundation.gestures.scrollable -import androidx.compose.foundation.hoverable -import androidx.compose.foundation.interaction.DragInteraction -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsHoveredAsState -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.foundation.rememberScrollbarAdapter -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.TextFieldScrollState -import androidx.compose.foundation.v2.ScrollbarAdapter -import androidx.compose.foundation.v2.maxScrollOffset -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.PointerInputScope -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.input.pointer.positionChange -import androidx.compose.ui.layout.Layout -import androidx.compose.ui.layout.MeasurePolicy -import androidx.compose.ui.layout.layoutId -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Constraints -import androidx.compose.ui.unit.constrainHeight -import androidx.compose.ui.unit.constrainWidth -import androidx.compose.ui.unit.dp -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import org.jetbrains.jewel.foundation.theme.JewelTheme -import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle -import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.AlwaysVisible -import org.jetbrains.jewel.ui.component.styling.ScrollbarVisibility.WhenScrolling -import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior.JumpToSpot -import org.jetbrains.jewel.ui.component.styling.TrackClickBehavior.NextPage -import org.jetbrains.jewel.ui.theme.scrollbarStyle -import org.jetbrains.jewel.ui.util.thenIf -import kotlin.math.roundToInt - -@Composable -public fun VerticalScrollbar( - scrollState: ScrollableState, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - style: ScrollbarStyle = JewelTheme.scrollbarStyle, -) { - MyScrollbar( - scrollState = scrollState, - modifier = modifier, - reverseLayout = reverseLayout, - interactionSource = interactionSource, - isVertical = true, - style = style, - ) -} - -@Composable -public fun HorizontalScrollbar( - scrollState: ScrollableState, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - style: ScrollbarStyle = JewelTheme.scrollbarStyle, -) { - MyScrollbar( - scrollState = scrollState, - modifier = modifier, - reverseLayout = reverseLayout, - interactionSource = interactionSource, - isVertical = false, - style = style, - ) -} - -@Composable -private fun MyScrollbar( - scrollState: ScrollableState, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - isVertical: Boolean, - style: ScrollbarStyle, -) { - // Click to scroll - var clickPosition by remember { mutableIntStateOf(0) } - val scrollbarWidth = remember { mutableIntStateOf(0) } - val scrollbarHeight = remember { mutableIntStateOf(0) } - LaunchedEffect(clickPosition) { - if (scrollState is ScrollState) { - if (scrollbarHeight.value == 0) return@LaunchedEffect - - val jumpTo = when (style.trackClickBehavior) { - NextPage -> scrollbarHeight.value + scrollState.viewportSize - JumpToSpot -> (scrollState.maxValue * clickPosition) / scrollbarHeight.value - } - - scrollState.scrollTo(jumpTo) - } - } - - // Visibility, hover and fade out - var visible by remember { mutableStateOf(scrollState.canScrollBackward) } - val hovered = interactionSource.collectIsHoveredAsState().value - - val animatedAlpha by animateFloatAsState( - targetValue = if (visible) 1.0f else 0f, - label = "alpha", - ) - - LaunchedEffect(scrollState.isScrollInProgress, hovered, style.scrollbarVisibility) { - if(style.scrollbarVisibility is AlwaysVisible || scrollState.isScrollInProgress || hovered) { - visible = true - } - - if (style.scrollbarVisibility is WhenScrolling && !hovered) { - delay(style.scrollbarVisibility.lingerDuration) - visible = false - } - } - - val adapter = - when (scrollState) { - is LazyListState -> rememberScrollbarAdapter(scrollState) - is LazyGridState -> rememberScrollbarAdapter(scrollState) - is ScrollState -> rememberScrollbarAdapter(scrollState) - is TextFieldScrollState -> rememberScrollbarAdapter(scrollState) - else -> error("Unsupported scroll state type: ${scrollState::class}") - } - - val thumbWidth = if (visible) style.metrics.thumbThicknessExpanded else style.metrics.thumbThickness - val trackBackground = if (visible) style.colors.trackBackground else Color.Transparent - val trackPadding = if (visible) style.metrics.trackPaddingExpanded else style.metrics.trackPadding - ScrollbarImpl( - adapter = adapter, - modifier = - modifier - .alpha(animatedAlpha) - .animateContentSize() - .width(thumbWidth) - .background(trackBackground) - .padding(trackPadding) - .scrollable( - scrollState, - orientation = Orientation.Vertical, - reverseDirection = true, - ).pointerInput(Unit) { - detectTapGestures { offset -> - clickPosition = offset.y.toInt() - } - }.onSizeChanged { - scrollbarWidth.value = it.width - scrollbarHeight.value = it.height - }, - reverseLayout = reverseLayout, - style = style, - interactionSource = interactionSource, - isVertical = isVertical, - ) -} - -@Composable -public fun VerticalScrollbar( - adapter: ScrollbarAdapter, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - style: ScrollbarStyle = JewelTheme.scrollbarStyle, -) { - ScrollbarImpl( - adapter = adapter, - modifier = modifier, - reverseLayout = reverseLayout, - style = style, - interactionSource = interactionSource, - isVertical = true, - ) -} - -@Composable -public fun HorizontalScrollbar( - adapter: ScrollbarAdapter, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - style: ScrollbarStyle = JewelTheme.scrollbarStyle, -) { - ScrollbarImpl( - adapter = adapter, - modifier = modifier, - reverseLayout = reverseLayout, - style = style, - interactionSource = interactionSource, - isVertical = false, - ) -} - -@Deprecated("Use HorizontalScrollbar with an appropriate style.") -@Composable -public fun TabStripHorizontalScrollbar( - adapter: ScrollbarAdapter, - style: ScrollbarStyle, - modifier: Modifier = Modifier, - reverseLayout: Boolean = false, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, -) { - HorizontalScrollbar( - adapter = adapter, - modifier = modifier.padding(1.dp), - reverseLayout = reverseLayout, - style = style, - interactionSource = interactionSource, - ) -} - -// =========================================================================== -// Note: most of the code below is copied and adapted from the stock scrollbar -// =========================================================================== - -@Composable -private fun ScrollbarImpl( - adapter: ScrollbarAdapter, - reverseLayout: Boolean, - style: ScrollbarStyle, - interactionSource: MutableInteractionSource, - isVertical: Boolean, - modifier: Modifier = Modifier, -) { - with(LocalDensity.current) { - val dragInteraction = remember { mutableStateOf(null) } - DisposableEffect(interactionSource) { - onDispose { - dragInteraction.value?.let { interaction -> - interactionSource.tryEmit(DragInteraction.Cancel(interaction)) - dragInteraction.value = null - } - } - } - - var containerSize by remember { mutableIntStateOf(0) } - val isHovered by interactionSource.collectIsHoveredAsState() - - val isHighlighted by remember { - derivedStateOf { isHovered || dragInteraction.value is DragInteraction.Start } - } - - val thumbMinHeight = style.metrics.minThumbLength.toPx() - - val coroutineScope = rememberCoroutineScope() - val sliderAdapter = - remember( - adapter, - containerSize, - thumbMinHeight, - reverseLayout, - isVertical, - coroutineScope, - ) { - SliderAdapter(adapter, containerSize, thumbMinHeight, reverseLayout, isVertical, coroutineScope) - } - - val thumbThickness = style.metrics.thumbThickness.roundToPx() - val measurePolicy = - if (isVertical) { - remember(sliderAdapter, thumbThickness) { - verticalMeasurePolicy(sliderAdapter, { containerSize = it }, thumbThickness) - } - } else { - remember(sliderAdapter, thumbThickness) { - horizontalMeasurePolicy(sliderAdapter, { containerSize = it }, thumbThickness) - } - } - - val targetColor = if (isHighlighted) { - style.colors.thumbBackgroundHovered - } else { - style.colors.thumbBackground - } - val thumbColor = if (style.scrollbarVisibility is WhenScrolling) { - val durationMillis = style.scrollbarVisibility.expandAnimationDuration.inWholeMilliseconds.toInt() - animateColorAsState( - targetValue = targetColor, - animationSpec = tween(durationMillis), - ).value - } else { - targetColor - } - val isVisible = sliderAdapter.thumbSize < containerSize - - Layout( - { - Box( - Modifier - .layoutId("thumb") - .thenIf(isVisible) { - background( - color = thumbColor, - shape = RoundedCornerShape(style.metrics.thumbCornerSize), - ) - }.scrollbarDrag( - interactionSource = interactionSource, - draggedInteraction = dragInteraction, - sliderAdapter = sliderAdapter, - ), - ) - }, - modifier - .hoverable(interactionSource = interactionSource) - .scrollOnPressTrack(isVertical, reverseLayout, sliderAdapter), - measurePolicy, - ) - } -} - -private val SliderAdapter.thumbPixelRange: IntRange - get() { - val start = position.roundToInt() - val endExclusive = start + thumbSize.roundToInt() - - return (start until endExclusive) - } - -private val IntRange.size get() = last + 1 - first - -private fun verticalMeasurePolicy( - sliderAdapter: SliderAdapter, - setContainerSize: (Int) -> Unit, - scrollThickness: Int, -) = MeasurePolicy { measurables, constraints -> - setContainerSize(constraints.maxHeight) - val pixelRange = sliderAdapter.thumbPixelRange - val placeable = - measurables.first().measure( - Constraints.fixed( - constraints.constrainWidth(scrollThickness), - pixelRange.size, - ), - ) - layout(placeable.width, constraints.maxHeight) { - placeable.place(0, pixelRange.first) - } -} - -private fun horizontalMeasurePolicy( - sliderAdapter: SliderAdapter, - setContainerSize: (Int) -> Unit, - scrollThickness: Int, -) = MeasurePolicy { measurables, constraints -> - setContainerSize(constraints.maxWidth) - val pixelRange = sliderAdapter.thumbPixelRange - val placeable = - measurables.first().measure( - Constraints.fixed( - pixelRange.size, - constraints.constrainHeight(scrollThickness), - ), - ) - layout(constraints.maxWidth, placeable.height) { - placeable.place(pixelRange.first, 0) - } -} - -private fun Modifier.scrollbarDrag( - interactionSource: MutableInteractionSource, - draggedInteraction: MutableState, - sliderAdapter: SliderAdapter, -): Modifier = - composed { - val currentInteractionSource by rememberUpdatedState(interactionSource) - val currentDraggedInteraction by rememberUpdatedState(draggedInteraction) - val currentSliderAdapter by rememberUpdatedState(sliderAdapter) - - pointerInput(Unit) { - awaitEachGesture { - val down = awaitFirstDown(requireUnconsumed = false) - val interaction = DragInteraction.Start() - currentInteractionSource.tryEmit(interaction) - currentDraggedInteraction.value = interaction - currentSliderAdapter.onDragStarted() - val isSuccess = - drag(down.id) { change -> - currentSliderAdapter.onDragDelta(change.positionChange()) - change.consume() - } - val finishInteraction = - if (isSuccess) { - DragInteraction.Stop(interaction) - } else { - DragInteraction.Cancel(interaction) - } - currentInteractionSource.tryEmit(finishInteraction) - currentDraggedInteraction.value = null - } - } - } - -private fun Modifier.scrollOnPressTrack( - isVertical: Boolean, - reverseLayout: Boolean, - sliderAdapter: SliderAdapter, -) = composed { - val coroutineScope = rememberCoroutineScope() - val scroller = - remember(sliderAdapter, coroutineScope, reverseLayout) { - TrackPressScroller(coroutineScope, sliderAdapter, reverseLayout) - } - Modifier.pointerInput(scroller) { - detectScrollViaTrackGestures( - isVertical = isVertical, - scroller = scroller, - ) - } -} - -/** - * Responsible for scrolling when the scrollbar track is pressed (outside - * the thumb). - */ -private class TrackPressScroller( - private val coroutineScope: CoroutineScope, - private val sliderAdapter: SliderAdapter, - private val reverseLayout: Boolean, -) { - /** - * The current direction of scroll (1: down/right, -1: up/left, 0: not - * scrolling) - */ - private var direction = 0 - - /** The currently pressed location (in pixels) on the scrollable axis. */ - private var offset: Float? = null - - /** The job that keeps scrolling while the track is pressed. */ - private var job: Job? = null - - /** - * Calculates the direction of scrolling towards the given offset (in - * pixels). - */ - private fun directionOfScrollTowards(offset: Float): Int { - val pixelRange = sliderAdapter.thumbPixelRange - return when { - offset < pixelRange.first -> if (reverseLayout) 1 else -1 - offset > pixelRange.last -> if (reverseLayout) -1 else 1 - else -> 0 - } - } - - /** - * Scrolls once towards the current offset, if it matches the direction of - * the current gesture. - */ - private suspend fun scrollTowardsCurrentOffset() { - offset?.let { - val currentDirection = directionOfScrollTowards(it) - if (currentDirection != direction) { - return - } - with(sliderAdapter.adapter) { - scrollTo(scrollOffset + currentDirection * viewportSize) - } - } - } - - /** Starts the job that scrolls continuously towards the current offset. */ - private fun startScrolling() { - job?.cancel() - job = - coroutineScope.launch { - scrollTowardsCurrentOffset() - delay(DELAY_BEFORE_SECOND_SCROLL_ON_TRACK_PRESS) - while (true) { - scrollTowardsCurrentOffset() - delay(DELAY_BETWEEN_SCROLLS_ON_TRACK_PRESS) - } - } - } - - /** Invoked on the first press for a gesture. */ - fun onPress(offset: Float) { - this.offset = offset - this.direction = directionOfScrollTowards(offset) - - if (direction != 0) { - startScrolling() - } - } - - /** Invoked when the pointer moves while pressed during the gesture. */ - fun onMovePressed(offset: Float) { - this.offset = offset - } - - /** Cleans up when the gesture finishes. */ - private fun cleanupAfterGesture() { - job?.cancel() - direction = 0 - offset = null - } - - /** Invoked when the button is released. */ - fun onRelease() { - cleanupAfterGesture() - } - - /** Invoked when the gesture is cancelled. */ - fun onGestureCancelled() { - cleanupAfterGesture() - // Maybe revert to the initial position? - } -} - -/** - * Detects the pointer events relevant for the "scroll by pressing on the - * track outside the thumb" gesture and calls the corresponding methods in - * the [scroller]. - */ -private suspend fun PointerInputScope.detectScrollViaTrackGestures( - isVertical: Boolean, - scroller: TrackPressScroller, -) { - fun Offset.onScrollAxis() = if (isVertical) y else x - - awaitEachGesture { - val down = awaitFirstDown() - scroller.onPress(down.position.onScrollAxis()) - - while (true) { - val drag = - if (isVertical) { - awaitVerticalDragOrCancellation(down.id) - } else { - awaitHorizontalDragOrCancellation(down.id) - } - - if (drag == null) { - scroller.onGestureCancelled() - break - } else if (!drag.pressed) { - scroller.onRelease() - break - } else { - scroller.onMovePressed(drag.position.onScrollAxis()) - } - } - } -} - -/** - * The delay between the 1st and 2nd scroll while the scrollbar track is - * pressed outside the thumb. - */ -internal const val DELAY_BEFORE_SECOND_SCROLL_ON_TRACK_PRESS: Long = 300L - -/** - * The delay between each subsequent (after the 2nd) scroll while the - * scrollbar track is pressed outside the thumb. - */ -internal const val DELAY_BETWEEN_SCROLLS_ON_TRACK_PRESS: Long = 100L - -internal class SliderAdapter( - val adapter: ScrollbarAdapter, - private val trackSize: Int, - private val minHeight: Float, - private val reverseLayout: Boolean, - private val isVertical: Boolean, - private val coroutineScope: CoroutineScope, -) { - private val contentSize get() = adapter.contentSize - private val visiblePart: Double - get() { - val contentSize = contentSize - return if (contentSize == 0.0) { - 1.0 - } else { - (adapter.viewportSize / contentSize).coerceAtMost(1.0) - } - } - - val thumbSize - get() = (trackSize * visiblePart).coerceAtLeast(minHeight.toDouble()) - - private val scrollScale: Double - get() { - val extraScrollbarSpace = trackSize - thumbSize - val extraContentSpace = adapter.maxScrollOffset // == contentSize - viewportSize - return if (extraContentSpace == 0.0) 1.0 else extraScrollbarSpace / extraContentSpace - } - - private val rawPosition: Double - get() = scrollScale * adapter.scrollOffset - - val position: Double - get() = if (reverseLayout) trackSize - thumbSize - rawPosition else rawPosition - - val bounds get() = position..position + thumbSize - - // How much of the current drag was ignored because we've reached the end of the scrollbar area - private var unscrolledDragDistance = 0.0 - - /** Called when the thumb dragging starts */ - fun onDragStarted() { - unscrolledDragDistance = 0.0 - } - - private suspend fun setPosition(value: Double) { - val rawPosition = - if (reverseLayout) { - trackSize - thumbSize - value - } else { - value - } - adapter.scrollTo(rawPosition / scrollScale) - } - - private val dragMutex = Mutex() - - /** Called on every movement while dragging the thumb */ - fun onDragDelta(offset: Offset) { - coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { - // Mutex is used to ensure that all earlier drag deltas were applied - // before calculating a new raw position - dragMutex.withLock { - val dragDelta = if (isVertical) offset.y else offset.x - val maxScrollPosition = adapter.maxScrollOffset * scrollScale - val currentPosition = position - val targetPosition = - (currentPosition + dragDelta + unscrolledDragDistance).coerceIn( - 0.0, - maxScrollPosition, - ) - val sliderDelta = targetPosition - currentPosition - - // Have to add to position for smooth content scroll if the items are of different size - val newPos = position + sliderDelta - setPosition(newPos) - unscrolledDragDistance += dragDelta - sliderDelta - } - } - } -} 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 ec4038ae7..cd83d43a2 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 @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.rememberScrollbarAdapter import androidx.compose.foundation.selection.selectableGroup import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable @@ -73,7 +72,7 @@ public fun TabStrip( exit = fadeOut(tween(durationMillis = 125, delayMillis = 700, easing = LinearEasing)), ) { HorizontalScrollbar( - adapter = rememberScrollbarAdapter(scrollState), + scrollState, style = style.scrollbarStyle, modifier = Modifier.fillMaxWidth(), ) diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextArea.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextArea.kt index cbdcb4cab..5d8bda2a6 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextArea.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextArea.kt @@ -1,12 +1,21 @@ package org.jetbrains.jewel.ui.component +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.input.InputTransformation +import androidx.compose.foundation.text.input.KeyboardActionHandler +import androidx.compose.foundation.text.input.OutputTransformation +import androidx.compose.foundation.text.input.TextFieldDecorator +import androidx.compose.foundation.text.input.TextFieldLineLimits import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -20,70 +29,168 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Placeable import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.offset +import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval import org.jetbrains.jewel.foundation.theme.JewelTheme import org.jetbrains.jewel.foundation.theme.LocalContentColor import org.jetbrains.jewel.foundation.theme.LocalTextStyle import org.jetbrains.jewel.ui.Outline +import org.jetbrains.jewel.ui.component.styling.ScrollbarStyle import org.jetbrains.jewel.ui.component.styling.TextAreaStyle +import org.jetbrains.jewel.ui.theme.scrollbarStyle import org.jetbrains.jewel.ui.theme.textAreaStyle -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ @Composable public fun TextArea( state: TextFieldState, modifier: Modifier = Modifier, enabled: Boolean = true, readOnly: Boolean = false, - outline: Outline = Outline.None, - placeholder: @Composable() (() -> Unit)? = null, - undecorated: Boolean = false, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - maxLines: Int = Int.MAX_VALUE, - style: TextAreaStyle = JewelTheme.textAreaStyle, + inputTransformation: InputTransformation? = null, textStyle: TextStyle = JewelTheme.defaultTextStyle, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + onKeyboardAction: KeyboardActionHandler? = null, + lineLimits: TextFieldLineLimits = TextFieldLineLimits.MultiLine(), + onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - showScrollbar: Boolean = true, + style: TextAreaStyle = JewelTheme.textAreaStyle, + outline: Outline = Outline.None, + placeholder: @Composable (() -> Unit)? = null, + decorationBoxModifier: Modifier = Modifier, + outputTransformation: OutputTransformation? = null, + undecorated: Boolean = false, + scrollState: ScrollState = rememberScrollState(), + scrollbarStyle: ScrollbarStyle? = JewelTheme.scrollbarStyle, ) { val minSize = style.metrics.minSize InputField( state = state, + modifier = modifier, enabled = enabled, readOnly = readOnly, - outline = outline, - undecorated = undecorated, + inputTransformation = inputTransformation, + textStyle = textStyle, keyboardOptions = keyboardOptions, - singleLine = false, - maxLines = maxLines, + onKeyboardAction = onKeyboardAction, + lineLimits = lineLimits, + onTextLayout = onTextLayout, interactionSource = interactionSource, style = style, - textStyle = textStyle, - showScrollbar = showScrollbar, - modifier = modifier.defaultMinSize(minWidth = minSize.width, minHeight = minSize.height), - decorationBox = { innerTextField, _ -> - TextAreaDecorationBox( - innerTextField = innerTextField, - contentPadding = style.metrics.contentPadding, - placeholderTextColor = style.colors.placeholder, - placeholder = if (state.text.isEmpty()) placeholder else null, - textStyle = textStyle, + outline = outline, + outputTransformation = outputTransformation, + decorator = + if (undecorated) { + NoTextAreaDecorator(style, scrollbarStyle, scrollState) + } else { + TextAreaDecorator( + style, + state, + placeholder, + textStyle, + decorationBoxModifier, + minSize, + scrollbarStyle, + scrollState, + ) + }, + undecorated = undecorated, + scrollState = scrollState, + ) +} + +@Composable +private fun NoTextAreaDecorator(style: TextAreaStyle, scrollbarStyle: ScrollbarStyle?, scrollState: ScrollState) = + TextFieldDecorator { innerTextField -> + val (contentPadding, innerEndPadding) = + calculatePaddings(scrollbarStyle, style, scrollState, LocalLayoutDirection.current) + + if (scrollbarStyle != null) { + TextAreaScrollableContainer( + scrollState = scrollState, + style = scrollbarStyle, + contentModifier = Modifier.padding(end = innerEndPadding), + content = { Box(Modifier.padding(contentPadding)) { innerTextField() } }, ) + } else { + Box(Modifier.padding(contentPadding)) { innerTextField() } + } + } + +@Composable +private fun TextAreaDecorator( + style: TextAreaStyle, + state: TextFieldState, + placeholder: @Composable (() -> Unit)?, + textStyle: TextStyle, + decorationBoxModifier: Modifier, + minSize: DpSize, + scrollbarStyle: ScrollbarStyle?, + scrollState: ScrollState, +) = TextFieldDecorator { innerTextField -> + val (contentPadding, innerEndPadding) = + calculatePaddings(scrollbarStyle, style, scrollState, LocalLayoutDirection.current) + + TextAreaDecorationBox( + innerTextField = { + if (scrollbarStyle != null) { + TextAreaScrollableContainer( + scrollState = scrollState, + style = scrollbarStyle, + contentModifier = Modifier.padding(end = innerEndPadding), + content = innerTextField, + ) + } else { + innerTextField() + } }, + contentPadding = contentPadding, + placeholderTextColor = style.colors.placeholder, + placeholder = if (state.text.isEmpty()) placeholder else null, + textStyle = textStyle, + modifier = decorationBoxModifier.defaultMinSize(minWidth = minSize.width, minHeight = minSize.height), ) } -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ +@Composable +private fun calculatePaddings( + scrollbarStyle: ScrollbarStyle?, + style: TextAreaStyle, + scrollState: ScrollState, + layoutDirection: LayoutDirection, +): Pair = + if (scrollbarStyle != null) { + with(style.metrics.contentPadding) { + val paddingValues = + PaddingValues( + start = calculateStartPadding(layoutDirection), + top = calculateTopPadding(), + end = 0.dp, + bottom = calculateBottomPadding(), + ) + + val scrollbarExtraPadding = + if (scrollState.canScrollForward || scrollState.canScrollBackward) { + scrollbarContentSafePadding(scrollbarStyle) + } else 0.dp + + paddingValues to calculateEndPadding(layoutDirection) + scrollbarExtraPadding + } + } else { + style.metrics.contentPadding to 0.dp + } + +@ScheduledForRemoval(inVersion = "Before 1.0") @Deprecated("Please use TextArea(state) instead. If you want to observe text changes, use snapshotFlow { state.text }") @Composable public fun TextArea( @@ -103,11 +210,13 @@ public fun TextArea( style: TextAreaStyle = JewelTheme.textAreaStyle, textStyle: TextStyle = JewelTheme.defaultTextStyle, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + decorationBoxModifier: Modifier = Modifier, ) { var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = value)) } val textFieldValue = textFieldValueState.copy(text = value) var lastTextValue by remember(value) { mutableStateOf(value) } + @Suppress("DEPRECATION") TextArea( value = textFieldValue, onValueChange = { newTextFieldValueState -> @@ -134,13 +243,11 @@ public fun TextArea( style = style, textStyle = textStyle, interactionSource = interactionSource, + decorationBoxModifier = decorationBoxModifier, ) } -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ +@ScheduledForRemoval(inVersion = "Before 1.0") @Deprecated("Please use TextArea(state) instead. If you want to observe text changes, use snapshotFlow { state.text }") @Composable public fun TextArea( @@ -160,8 +267,10 @@ public fun TextArea( style: TextAreaStyle = JewelTheme.textAreaStyle, textStyle: TextStyle = JewelTheme.defaultTextStyle, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + decorationBoxModifier: Modifier = Modifier, ) { val minSize = style.metrics.minSize + @Suppress("DEPRECATION") InputField( value = value, onValueChange = onValueChange, @@ -186,6 +295,7 @@ public fun TextArea( placeholderTextColor = style.colors.placeholder, placeholder = if (value.text.isEmpty()) placeholder else null, textStyle = textStyle, + modifier = decorationBoxModifier, ) } } @@ -195,16 +305,14 @@ private fun TextAreaDecorationBox( innerTextField: @Composable () -> Unit, contentPadding: PaddingValues, textStyle: TextStyle, + modifier: Modifier, placeholderTextColor: Color, placeholder: @Composable (() -> Unit)?, ) { Layout( content = { if (placeholder != null) { - Box( - modifier = Modifier.layoutId(PLACEHOLDER_ID), - contentAlignment = Alignment.TopStart, - ) { + Box(modifier = Modifier.layoutId(PLACEHOLDER_ID), contentAlignment = Alignment.TopStart) { CompositionLocalProvider( LocalTextStyle provides textStyle.copy(color = placeholderTextColor), LocalContentColor provides placeholderTextColor, @@ -221,30 +329,22 @@ private fun TextAreaDecorationBox( innerTextField() } }, + modifier, ) { measurables, incomingConstraints -> val leftPadding = contentPadding.calculateLeftPadding(layoutDirection) val rightPadding = contentPadding.calculateRightPadding(layoutDirection) val horizontalPadding = (leftPadding + rightPadding).roundToPx() val verticalPadding = - (contentPadding.calculateTopPadding() + contentPadding.calculateBottomPadding()) - .roundToPx() + (contentPadding.calculateTopPadding() + contentPadding.calculateBottomPadding()).roundToPx() val textAreaConstraints = - incomingConstraints - .offset(horizontal = -horizontalPadding, vertical = -verticalPadding) - .copy(minHeight = 0) + incomingConstraints.offset(horizontal = -horizontalPadding, vertical = -verticalPadding).copy(minHeight = 0) - val textAreaPlaceable = - measurables - .single { it.layoutId == TEXT_AREA_ID } - .measure(textAreaConstraints) + val textAreaPlaceable = measurables.single { it.layoutId == TEXT_AREA_ID }.measure(textAreaConstraints) // Measure placeholder val placeholderConstraints = textAreaConstraints.copy(minWidth = 0, minHeight = 0) - val placeholderPlaceable = - measurables - .find { it.layoutId == PLACEHOLDER_ID } - ?.measure(placeholderConstraints) + val placeholderPlaceable = measurables.find { it.layoutId == PLACEHOLDER_ID }?.measure(placeholderConstraints) val width = calculateWidth(textAreaPlaceable, placeholderPlaceable, incomingConstraints) val height = calculateHeight(textAreaPlaceable, placeholderPlaceable, verticalPadding, incomingConstraints) @@ -266,9 +366,7 @@ private fun calculateWidth( textFieldPlaceable: Placeable, placeholderPlaceable: Placeable?, incomingConstraints: Constraints, -): Int = - maxOf(textFieldPlaceable.width, placeholderPlaceable?.width ?: 0) - .coerceAtLeast(incomingConstraints.minWidth) +): Int = maxOf(textFieldPlaceable.width, placeholderPlaceable?.width ?: 0).coerceAtLeast(incomingConstraints.minWidth) private fun calculateHeight( textFieldPlaceable: Placeable, diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextField.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextField.kt index abd4722a2..d7b417eb9 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextField.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/TextField.kt @@ -4,8 +4,14 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.input.InputTransformation +import androidx.compose.foundation.text.input.KeyboardActionHandler +import androidx.compose.foundation.text.input.OutputTransformation +import androidx.compose.foundation.text.input.TextFieldDecorator +import androidx.compose.foundation.text.input.TextFieldLineLimits import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -24,71 +30,78 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.offset +import kotlin.math.max +import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval import org.jetbrains.jewel.foundation.theme.JewelTheme import org.jetbrains.jewel.foundation.theme.LocalContentColor import org.jetbrains.jewel.foundation.theme.LocalTextStyle import org.jetbrains.jewel.ui.Outline import org.jetbrains.jewel.ui.component.styling.TextFieldStyle import org.jetbrains.jewel.ui.theme.textFieldStyle -import kotlin.math.max -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ +@Suppress("DuplicatedCode") // The dupe is scheduled for removal @Composable public fun TextField( state: TextFieldState, modifier: Modifier = Modifier, enabled: Boolean = true, readOnly: Boolean = false, - outline: Outline = Outline.None, - placeholder: @Composable() (() -> Unit)? = null, - leadingIcon: @Composable() (() -> Unit)? = null, - trailingIcon: @Composable() (() -> Unit)? = null, - undecorated: Boolean = false, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - style: TextFieldStyle = JewelTheme.textFieldStyle, + inputTransformation: InputTransformation? = null, textStyle: TextStyle = JewelTheme.defaultTextStyle, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + onKeyboardAction: KeyboardActionHandler? = null, + onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + style: TextFieldStyle = JewelTheme.textFieldStyle, + outline: Outline = Outline.None, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + outputTransformation: OutputTransformation? = null, + undecorated: Boolean = false, ) { InputField( state = state, + modifier = modifier, enabled = enabled, readOnly = readOnly, - outline = outline, - undecorated = undecorated, + inputTransformation = inputTransformation, + textStyle = textStyle, keyboardOptions = keyboardOptions, - singleLine = true, - maxLines = 1, + onKeyboardAction = onKeyboardAction, + lineLimits = TextFieldLineLimits.SingleLine, + onTextLayout = onTextLayout, interactionSource = interactionSource, style = style, - textStyle = textStyle, - showScrollbar = false, - modifier = modifier, - decorationBox = { innerTextField, _ -> - val minSize = style.metrics.minSize + outline = outline, + outputTransformation = outputTransformation, + decorator = + if (!undecorated) { + TextFieldDecorator { innerTextField -> + val minSize = style.metrics.minSize - TextFieldDecorationBox( - modifier = Modifier - .defaultMinSize(minWidth = minSize.width, minHeight = minSize.height) - .padding(style.metrics.contentPadding), - innerTextField = innerTextField, - textStyle = textStyle, - placeholderTextColor = style.colors.placeholder, - placeholder = if (state.text.isEmpty()) placeholder else null, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - ) - }, + TextFieldDecorationBox( + modifier = + Modifier.defaultMinSize(minWidth = minSize.width, minHeight = minSize.height) + .padding(style.metrics.contentPadding), + innerTextField = innerTextField, + textStyle = textStyle, + placeholderTextColor = style.colors.placeholder, + placeholder = if (state.text.isEmpty()) placeholder else null, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + ) + } + } else { + null + }, + scrollState = rememberScrollState(), ) } -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ +@ScheduledForRemoval(inVersion = "Before 1.0") @Deprecated("Please use TextField(state) instead. If you want to observe text changes, use snapshotFlow { state.text }") @Composable public fun TextField( @@ -113,6 +126,7 @@ public fun TextField( val textFieldValue = textFieldValueState.copy(text = value) var lastTextValue by remember(value) { mutableStateOf(value) } + @Suppress("DEPRECATION") TextField( value = textFieldValue, onValueChange = { newTextFieldValueState -> @@ -142,10 +156,8 @@ public fun TextField( ) } -/** - * @param placeholder the optional placeholder to be displayed over the - * component when the [value] is empty. - */ +@Suppress("DuplicatedCode") // This is scheduled for removal +@ScheduledForRemoval(inVersion = "Before 1.0") @Deprecated("Please use TextField(state) instead. If you want to observe text changes, use snapshotFlow { state.text }") @Composable public fun TextField( @@ -167,6 +179,7 @@ public fun TextField( textStyle: TextStyle = JewelTheme.defaultTextStyle, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { + @Suppress("DEPRECATION") InputField( value = value, onValueChange = onValueChange, @@ -215,14 +228,10 @@ private fun TextFieldDecorationBox( modifier = modifier, content = { if (leadingIcon != null) { - Box(modifier = Modifier.layoutId(LEADING_ID), contentAlignment = Alignment.Center) { - leadingIcon() - } + Box(modifier = Modifier.layoutId(LEADING_ID), contentAlignment = Alignment.Center) { leadingIcon() } } if (trailingIcon != null) { - Box(modifier = Modifier.layoutId(TRAILING_ID), contentAlignment = Alignment.Center) { - trailingIcon() - } + Box(modifier = Modifier.layoutId(TRAILING_ID), contentAlignment = Alignment.Center) { trailingIcon() } } if (placeholder != null) { Box(modifier = Modifier.layoutId(PLACEHOLDER_ID), contentAlignment = Alignment.Center) { @@ -234,9 +243,7 @@ private fun TextFieldDecorationBox( } } - Box(modifier = Modifier.layoutId(TEXT_FIELD_ID), propagateMinConstraints = true) { - innerTextField() - } + Box(modifier = Modifier.layoutId(TEXT_FIELD_ID), propagateMinConstraints = true) { innerTextField() } }, ) { measurables, incomingConstraints -> // used to calculate the constraints for measuring elements that will be placed in a row @@ -244,40 +251,22 @@ private fun TextFieldDecorationBox( val iconConstraints = incomingConstraints.copy(minWidth = 0, minHeight = 0) // measure trailing icon - val trailingPlaceable = - measurables.find { it.layoutId == TRAILING_ID }?.measure(iconConstraints) + val trailingPlaceable = measurables.find { it.layoutId == TRAILING_ID }?.measure(iconConstraints) val leadingPlaceable = measurables.find { it.layoutId == LEADING_ID }?.measure(iconConstraints) occupiedSpaceHorizontally += trailingPlaceable?.width ?: 0 occupiedSpaceHorizontally += leadingPlaceable?.width ?: 0 val textFieldConstraints = - incomingConstraints.offset(horizontal = -occupiedSpaceHorizontally) - .copy(minHeight = 0) - val textFieldPlaceable = - measurables.single { it.layoutId == TEXT_FIELD_ID } - .measure(textFieldConstraints) + incomingConstraints.offset(horizontal = -occupiedSpaceHorizontally).copy(minHeight = 0) + val textFieldPlaceable = measurables.single { it.layoutId == TEXT_FIELD_ID }.measure(textFieldConstraints) // measure placeholder val placeholderConstraints = textFieldConstraints.copy(minWidth = 0) - val placeholderPlaceable = - measurables.find { it.layoutId == PLACEHOLDER_ID } - ?.measure(placeholderConstraints) + val placeholderPlaceable = measurables.find { it.layoutId == PLACEHOLDER_ID }?.measure(placeholderConstraints) - val width = - calculateWidth( - leadingPlaceable, - trailingPlaceable, - textFieldPlaceable, - incomingConstraints, - ) - val height = - calculateHeight( - leadingPlaceable, - trailingPlaceable, - textFieldPlaceable, - incomingConstraints, - ) + val width = calculateWidth(leadingPlaceable, trailingPlaceable, textFieldPlaceable, incomingConstraints) + val height = calculateHeight(leadingPlaceable, trailingPlaceable, textFieldPlaceable, incomingConstraints) layout(width, height) { place( @@ -299,8 +288,7 @@ private fun calculateWidth( constraints: Constraints, ): Int { val middleSection = textFieldPlaceable.width - val wrappedWidth = - middleSection + (trailingPlaceable?.width ?: 0) + (leadingPlaceable?.width ?: 0) + val wrappedWidth = middleSection + (trailingPlaceable?.width ?: 0) + (leadingPlaceable?.width ?: 0) return max(wrappedWidth, constraints.minWidth) } @@ -326,10 +314,7 @@ private fun Placeable.PlacementScope.place( placeholderPlaceable: Placeable?, ) { // placed center vertically and to the end edge horizontally - leadingPlaceable?.placeRelative( - 0, - Alignment.CenterVertically.align(leadingPlaceable.height, height), - ) + leadingPlaceable?.placeRelative(0, Alignment.CenterVertically.align(leadingPlaceable.height, height)) trailingPlaceable?.placeRelative( width - trailingPlaceable.width, Alignment.CenterVertically.align(trailingPlaceable.height, height), diff --git a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/ScrollbarStyling.kt b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/ScrollbarStyling.kt index d447f95f9..75f66b643 100644 --- a/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/ScrollbarStyling.kt +++ b/ui/src/main/kotlin/org/jetbrains/jewel/ui/component/styling/ScrollbarStyling.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import org.jetbrains.jewel.foundation.GenerateDataFunctions import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds @Stable @GenerateDataFunctions @@ -26,13 +27,17 @@ public class ScrollbarStyle( @GenerateDataFunctions public class ScrollbarColors( public val thumbBackground: Color, - public val thumbBackgroundHovered: Color, - public val thumbBackgroundPressed: Color, + public val thumbBackgroundActive: Color, + public val thumbOpaqueBackground: Color, + public val thumbOpaqueBackgroundHovered: Color, public val thumbBorder: Color, - public val thumbBorderHovered: Color, - public val thumbBorderPressed: Color, + public val thumbBorderActive: Color, + public val thumbOpaqueBorder: Color, + public val thumbOpaqueBorderHovered: Color, public val trackBackground: Color, - public val trackBackgroundHovered: Color, + public val trackBackgroundExpanded: Color, + public val trackOpaqueBackground: Color, + public val trackOpaqueBackgroundHovered: Color, ) { public companion object } @@ -41,25 +46,50 @@ public class ScrollbarColors( @GenerateDataFunctions public class ScrollbarMetrics( public val thumbCornerSize: CornerSize, - public val thumbThickness: Dp, - public val thumbThicknessExpanded: Dp, public val minThumbLength: Dp, - public val trackPadding: PaddingValues, - public val trackPaddingExpanded: PaddingValues, ) { public companion object } public sealed interface ScrollbarVisibility { - public data object AlwaysVisible : ScrollbarVisibility + public val trackThickness: Dp + public val trackThicknessExpanded: Dp + public val trackPadding: PaddingValues + public val trackPaddingWithBorder: PaddingValues + public val trackColorAnimationDuration: Duration + public val expandAnimationDuration: Duration + public val thumbColorAnimationDuration: Duration + public val lingerDuration: Duration + + @GenerateDataFunctions + public class AlwaysVisible( + public override val trackThickness: Dp, + public override val trackPadding: PaddingValues, + public override val trackPaddingWithBorder: PaddingValues, + public override val thumbColorAnimationDuration: Duration, + public override val trackColorAnimationDuration: Duration, + public val scrollbarBackgroundColorLight: Color, + public val scrollbarBackgroundColorDark: Color, + ) : ScrollbarVisibility { + public override val trackThicknessExpanded: Dp = trackThickness + public override val expandAnimationDuration: Duration = 0.milliseconds + public override val lingerDuration: Duration = 0.milliseconds + + public companion object + } @GenerateDataFunctions public class WhenScrolling( - public val appearAnimationDuration: Duration, - public val disappearAnimationDuration: Duration, - public val expandAnimationDuration: Duration, - public val lingerDuration: Duration, + public override val trackThickness: Dp, + public override val trackThicknessExpanded: Dp, + public override val trackPadding: PaddingValues, + public override val trackPaddingWithBorder: PaddingValues, + public override val trackColorAnimationDuration: Duration, + public override val expandAnimationDuration: Duration, + public override val thumbColorAnimationDuration: Duration, + public override val lingerDuration: Duration, ) : ScrollbarVisibility { + public companion object } }