@@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier
3131import androidx.compose.ui.draw.alpha
3232import androidx.compose.ui.draw.clip
3333import androidx.compose.ui.graphics.Color
34+ import androidx.compose.ui.graphics.Shape
3435import androidx.compose.ui.layout.onGloballyPositioned
3536import androidx.compose.ui.layout.positionInParent
3637import androidx.compose.ui.platform.LocalDensity
@@ -43,18 +44,62 @@ import androidx.compose.ui.unit.IntOffset
4344import androidx.compose.ui.unit.dp
4445import androidx.compose.ui.unit.sp
4546import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.ControlSize
46- import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SpringPreset
47- import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.MacosTheme
48- import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.LocalControlSize
4947import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.LocalContentColor
48+ import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.LocalControlSize
5049import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.LocalTextStyle
5150import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.LocalWindowActive
52- import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SegmentedControlStyle
51+ import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.MacosTheme
52+ import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SegmentedControlColors
5353import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SegmentedControlVariant
54+ import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.SpringPreset
5455import io.github.kdroidfilter.nucleus.ui.apple.macos.theme.macosSpring
5556import kotlinx.coroutines.launch
5657import kotlin.math.roundToInt
5758
59+ // =============================================================================
60+ // SegmentedControlDefaults
61+ // =============================================================================
62+
63+ object SegmentedControlDefaults {
64+
65+ @Composable
66+ fun colors (
67+ track : Color = Color .Unspecified ,
68+ selectedSegment : Color = Color .Unspecified ,
69+ selectedContent : Color = Color .Unspecified ,
70+ unselectedContent : Color = Color .Unspecified ,
71+ pressedOverlay : Color = Color .Unspecified ,
72+ disabledContent : Color = Color .Unspecified ,
73+ separatorColor : Color = Color .Unspecified ,
74+ inactiveSelectedSegment : Color = Color .Unspecified ,
75+ inactiveSelectedContent : Color = Color .Unspecified ,
76+ variant : SegmentedControlVariant = SegmentedControlVariant .ContentArea ,
77+ ): SegmentedControlColors {
78+ val style = MacosTheme .componentStyling.segmentedControl.colorsFor(variant)
79+ return SegmentedControlColors (
80+ track = track.takeOrElse(style.track),
81+ selectedSegment = selectedSegment.takeOrElse(style.selectedSegment),
82+ selectedContent = selectedContent.takeOrElse(style.selectedContent),
83+ unselectedContent = unselectedContent.takeOrElse(style.unselectedContent),
84+ pressedOverlay = pressedOverlay.takeOrElse(style.pressedOverlay),
85+ disabledContent = disabledContent.takeOrElse(style.disabledContent),
86+ separatorColor = separatorColor.takeOrElse(style.separatorColor),
87+ inactiveSelectedSegment = inactiveSelectedSegment.takeOrElse(style.inactiveSelectedSegment),
88+ inactiveSelectedContent = inactiveSelectedContent.takeOrElse(style.inactiveSelectedContent),
89+ )
90+ }
91+
92+ @Composable
93+ fun shape (): Shape {
94+ val controlSize = LocalControlSize .current
95+ val metrics = MacosTheme .componentStyling.segmentedControl.metrics
96+ return RoundedCornerShape (metrics.cornerRadiusFor(controlSize))
97+ }
98+ }
99+
100+ private fun Color.takeOrElse (other : Color ): Color =
101+ if (this != Color .Unspecified ) this else other
102+
58103// =============================================================================
59104// SegmentedControl — slot-based API
60105// =============================================================================
@@ -66,22 +111,18 @@ fun SegmentedControl(
66111 onSelectedIndexChange : (Int ) -> Unit ,
67112 modifier : Modifier = Modifier ,
68113 enabled : Boolean = true,
69- variant : SegmentedControlVariant = SegmentedControlVariant .ContentArea ,
114+ colors : SegmentedControlColors = SegmentedControlDefaults .colors(),
115+ shape : Shape = SegmentedControlDefaults .shape(),
70116 segment : @Composable (index: Int ) -> Unit ,
71117) {
72118 require(segmentCount > 0 ) { " segmentCount must be > 0" }
73119
74120 val controlSize = LocalControlSize .current
75121 val isWindowActive = LocalWindowActive .current
76- val style = MacosTheme .componentStyling.segmentedControl
77- val metrics = style.metrics
78- val colors = style.colorsFor(variant)
122+ val metrics = MacosTheme .componentStyling.segmentedControl.metrics
79123 val density = LocalDensity .current
80124
81125 val trackHeight = metrics.containerHeightFor(controlSize)
82- val cornerRadius = metrics.cornerRadiusFor(controlSize)
83- val trackShape = RoundedCornerShape (cornerRadius)
84- val pillShape = RoundedCornerShape (cornerRadius)
85126
86127 // Resolve pill color based on active/inactive window state
87128 val pillColor by animateColorAsState(
@@ -123,8 +164,8 @@ fun SegmentedControl(
123164 Box (
124165 modifier = modifier
125166 .height(trackHeight)
126- .clip(trackShape )
127- .background(colors.track, trackShape ),
167+ .clip(shape )
168+ .background(colors.track, shape ),
128169 contentAlignment = Alignment .CenterStart ,
129170 ) {
130171 // Sliding pill indicator
@@ -141,7 +182,7 @@ fun SegmentedControl(
141182 width = with (density) { indicatorWidthAnim.value.toDp() },
142183 height = trackHeight,
143184 )
144- .background(pillColor, pillShape ),
185+ .background(pillColor, shape ),
145186 )
146187 }
147188
@@ -164,7 +205,7 @@ fun SegmentedControl(
164205 isSelected = index == selectedIndex,
165206 enabled = enabled,
166207 controlSize = controlSize,
167- cornerRadius = cornerRadius ,
208+ shape = shape ,
168209 selectedTextColor = selectedContentColor,
169210 unselectedTextColor = if (enabled) colors.unselectedContent else colors.disabledContent,
170211 pressedOverlay = colors.pressedOverlay,
@@ -184,7 +225,7 @@ fun SegmentedControl(
184225}
185226
186227private fun resolveSelectedSegmentColor (
187- colors : SegmentedControlStyle . Colors ,
228+ colors : SegmentedControlColors ,
188229 enabled : Boolean ,
189230 isWindowActive : Boolean ,
190231): Color = when {
@@ -204,15 +245,17 @@ fun SegmentedControl(
204245 onSelectedIndexChange : (Int ) -> Unit ,
205246 modifier : Modifier = Modifier ,
206247 enabled : Boolean = true,
207- variant : SegmentedControlVariant = SegmentedControlVariant .ContentArea ,
248+ colors : SegmentedControlColors = SegmentedControlDefaults .colors(),
249+ shape : Shape = SegmentedControlDefaults .shape(),
208250) {
209251 SegmentedControl (
210252 segmentCount = options.size,
211253 selectedIndex = selectedIndex,
212254 onSelectedIndexChange = onSelectedIndexChange,
213255 modifier = modifier,
214256 enabled = enabled,
215- variant = variant,
257+ colors = colors,
258+ shape = shape,
216259 ) { index ->
217260 Text (options[index])
218261 }
@@ -236,7 +279,7 @@ private fun Segment(
236279 isSelected : Boolean ,
237280 enabled : Boolean ,
238281 controlSize : ControlSize ,
239- cornerRadius : Dp ,
282+ shape : Shape ,
240283 selectedTextColor : Color ,
241284 unselectedTextColor : Color ,
242285 pressedOverlay : Color ,
@@ -270,8 +313,6 @@ private fun Segment(
270313 fontSize = fontSizeFor(controlSize),
271314 )
272315
273- val segmentShape = RoundedCornerShape (cornerRadius)
274-
275316 val metrics = MacosTheme .componentStyling.segmentedControl.metrics
276317 val minWidth = metrics.segmentMinWidthFor(controlSize)
277318 val horizontalPadding = metrics.segmentHorizontalPaddingFor(controlSize)
@@ -287,7 +328,7 @@ private fun Segment(
287328 coordinates.size.width.toFloat(),
288329 )
289330 }
290- .clip(segmentShape )
331+ .clip(shape )
291332 .then(
292333 if (pressAlpha > 0f ) {
293334 Modifier .background(pressedOverlay.copy(alpha = pressedOverlay.alpha * pressAlpha))
0 commit comments