@@ -231,6 +231,70 @@ public fun OutlinedSplitButton(
231231 )
232232}
233233
234+ /* *
235+ * A split button combining a primary action **without** a dropdown menu, using an outlined visual style.
236+ *
237+ * Similar to [DefaultSplitButton] but with an outlined visual treatment. Provides two interactive areas: the main
238+ * button area for the primary action and a chevron section whose expanded state is fully controlled by the caller.
239+ *
240+ * **IMPORTANT:** This overload does NOT manage any popup/dropdown. You are responsible for handling the lifecycle,
241+ * positioning, and disposal of any UI based on [expanded].
242+ *
243+ * **Popup toggle behavior:** When using a Compose `Popup`, prefer `PopupProperties(focusable = true)`. Non-focusable
244+ * popups dismiss on pointer down, which fires before the chevron click handler and causes the popup to immediately
245+ * re-open when clicking the chevron to close it. Focusable popups dismiss via focus loss, which fires after the click
246+ * is processed, allowing the chevron to correctly toggle the state.
247+ *
248+ * **Guidelines:** [on IJP SDK webhelp](https://plugins.jetbrains.com/docs/intellij/split-button.html)
249+ *
250+ * **Usage example:**
251+ * [`Buttons.kt`](https://github.com/JetBrains/intellij-community/blob/master/platform/jewel/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Buttons.kt)
252+ *
253+ * **Swing equivalent:**
254+ * [`JBOptionButton`](https://github.com/JetBrains/intellij-community/tree/idea/243.22562.145/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.kt)
255+ *
256+ * @param onClick Will be called when the user clicks the main button area
257+ * @param expanded Controls the visual expanded (active) state of the chevron area. Typically reflects whether a popup
258+ * or secondary UI is currently visible
259+ * @param onExpandedChange Called when the chevron is clicked or the down arrow key is pressed. The caller should update
260+ * [expanded] in response
261+ * @param modifier Modifier to be applied to the button
262+ * @param enabled Controls the enabled state of the button. When false, the button will not be clickable
263+ * @param interactionSource An optional [MutableInteractionSource] for observing and emitting [Interaction]s for this
264+ * button
265+ * @param style The visual styling configuration for the split button including colors, metrics and layout parameters
266+ * @param textStyle The typography style to be applied to the button's text content
267+ * @param content The content to be displayed in the main button area
268+ * @see com.intellij.ui.components.JBOptionButton
269+ */
270+ @Composable
271+ public fun OutlinedSplitButton (
272+ onClick : () -> Unit ,
273+ expanded : Boolean ,
274+ onExpandedChange : (Boolean ) -> Unit ,
275+ modifier : Modifier = Modifier ,
276+ enabled : Boolean = true,
277+ interactionSource : MutableInteractionSource = remember { MutableInteractionSource () },
278+ style : SplitButtonStyle = JewelTheme .outlinedSplitButtonStyle,
279+ textStyle : TextStyle = JewelTheme .defaultTextStyle,
280+ content : @Composable () -> Unit ,
281+ ) {
282+ SplitButtonImpl (
283+ onClick = onClick,
284+ secondaryOnClick = {},
285+ enabled = enabled,
286+ interactionSource = interactionSource,
287+ style = style,
288+ textStyle = textStyle,
289+ menuStyle = null ,
290+ isDefault = false ,
291+ modifier = modifier,
292+ expanded = expanded,
293+ onExpandedChange = onExpandedChange,
294+ content = content,
295+ )
296+ }
297+
234298/* *
235299 * A split button combining a primary action with a dropdown menu, using an outlined visual style.
236300 *
@@ -479,6 +543,70 @@ public fun DefaultSplitButton(
479543 )
480544}
481545
546+ /* *
547+ * A split button combining a primary action **without** a dropdown menu, using the default visual style.
548+ *
549+ * Provides two interactive areas: the main button area for the primary action and a chevron section whose expanded
550+ * state is fully controlled by the caller.
551+ *
552+ * **IMPORTANT:** This overload does NOT manage any popup/dropdown. You are responsible for handling the lifecycle,
553+ * positioning, and disposal of any UI based on [expanded].
554+ *
555+ * **Popup toggle behavior:** When using a Compose `Popup`, prefer `PopupProperties(focusable = true)`. Non-focusable
556+ * popups dismiss on pointer down, which fires before the chevron click handler and causes the popup to immediately
557+ * re-open when clicking the chevron to close it. Focusable popups dismiss via focus loss, which fires after the click
558+ * is processed, allowing the chevron to correctly toggle the state.
559+ *
560+ * **Guidelines:** [on IJP SDK webhelp](https://plugins.jetbrains.com/docs/intellij/split-button.html)
561+ *
562+ * **Usage example:**
563+ * [`Buttons.kt`](https://github.com/JetBrains/intellij-community/blob/master/platform/jewel/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/component/Buttons.kt)
564+ *
565+ * **Swing equivalent:**
566+ * [`JBOptionButton`](https://github.com/JetBrains/intellij-community/tree/idea/243.22562.145/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.kt)
567+ *
568+ * @param onClick Will be called when the user clicks the main button area
569+ * @param expanded Controls the visual expanded (active) state of the chevron area. Typically reflects whether a popup
570+ * or secondary UI is currently visible
571+ * @param onExpandedChange Called when the chevron is clicked or the down arrow key is pressed. The caller should update
572+ * [expanded] in response
573+ * @param modifier Modifier to be applied to the button
574+ * @param enabled Controls the enabled state of the button. When false, the button will not be clickable
575+ * @param interactionSource An optional [MutableInteractionSource] for observing and emitting [Interaction]s for this
576+ * button
577+ * @param style The visual styling configuration for the split button including colors, metrics and layout parameters
578+ * @param textStyle The typography style to be applied to the button's text content
579+ * @param content The content to be displayed in the main button area
580+ * @see com.intellij.ui.components.JBOptionButton
581+ */
582+ @Composable
583+ public fun DefaultSplitButton (
584+ onClick : () -> Unit ,
585+ expanded : Boolean ,
586+ onExpandedChange : (Boolean ) -> Unit ,
587+ modifier : Modifier = Modifier ,
588+ enabled : Boolean = true,
589+ interactionSource : MutableInteractionSource = remember { MutableInteractionSource () },
590+ style : SplitButtonStyle = JewelTheme .defaultSplitButtonStyle,
591+ textStyle : TextStyle = JewelTheme .defaultTextStyle,
592+ content : @Composable () -> Unit ,
593+ ) {
594+ SplitButtonImpl (
595+ onClick = onClick,
596+ secondaryOnClick = {},
597+ enabled = enabled,
598+ interactionSource = interactionSource,
599+ style = style,
600+ textStyle = textStyle,
601+ menuStyle = null ,
602+ isDefault = true ,
603+ modifier = modifier,
604+ expanded = expanded,
605+ onExpandedChange = onExpandedChange,
606+ content = content,
607+ )
608+ }
609+
482610/* *
483611 * A split button combining a primary action with a dropdown menu, using the default visual style.
484612 *
@@ -672,14 +800,16 @@ private fun SplitButtonImpl(
672800 interactionSource : MutableInteractionSource ,
673801 style : SplitButtonStyle ,
674802 textStyle : TextStyle ,
675- menuStyle : MenuStyle ,
803+ menuStyle : MenuStyle ? ,
676804 isDefault : Boolean ,
677805 modifier : Modifier = Modifier ,
678806 popupModifier : Modifier = Modifier ,
679807 maxPopupHeight : Dp = Dp .Unspecified ,
680808 maxPopupWidth : Dp = Dp .Unspecified ,
681809 secondaryContent : @Composable (() -> Unit )? = null,
682810 secondaryContentMenu : (MenuScope .() -> Unit )? = null,
811+ expanded : Boolean = false, // Used only by split buttons that don't use Jewel's popup
812+ onExpandedChange : ((Boolean ) -> Unit )? = null, // Used only by split buttons that don't use Jewel's popup
683813 content : @Composable () -> Unit ,
684814) {
685815 val density = LocalDensity .current
@@ -699,14 +829,19 @@ private fun SplitButtonImpl(
699829 .onFocusChanged {
700830 if (! it.isFocused) {
701831 popupVisible = false
832+ onExpandedChange?.invoke(false )
702833 }
703834 }
704835 .thenIf(enabled) {
705836 onPreviewKeyEvent { keyEvent ->
706837 if (keyEvent.type != KeyEventType .KeyDown ) return @onPreviewKeyEvent false
707838 when {
708839 keyEvent.key == Key .DirectionDown -> {
709- popupVisible = true
840+ if (menuStyle != null ) {
841+ popupVisible = true
842+ } else {
843+ onExpandedChange?.invoke(true )
844+ }
710845 true
711846 }
712847
@@ -716,7 +851,7 @@ private fun SplitButtonImpl(
716851 }
717852 .focusRequester(focusRequester),
718853 enabled = enabled,
719- forceFocused = popupVisible,
854+ forceFocused = if (menuStyle != null ) popupVisible else expanded ,
720855 onStateChange = { state -> buttonState = state },
721856 interactionSource = interactionSource,
722857 style = style.button,
@@ -728,16 +863,21 @@ private fun SplitButtonImpl(
728863 enabled = enabled,
729864 isDefault = isDefault,
730865 onChevronClick = {
731- secondaryOnClick()
732- popupVisible = ! popupVisible
866+ println (" popupvisible: $popupVisible " )
867+ if (menuStyle != null ) {
868+ secondaryOnClick()
869+ popupVisible = ! popupVisible
870+ } else {
871+ onExpandedChange?.invoke(! expanded)
872+ }
733873 if (! buttonState.isFocused) focusRequester.requestFocus()
734874 },
735875 modifier = Modifier .testTag(" Jewel.SplitButton.SecondaryAction" ),
736876 )
737877 },
738878 )
739879
740- if (popupVisible && enabled) {
880+ if (popupVisible && enabled && menuStyle != null ) {
741881 val splitButtonPopupModifier =
742882 Modifier .heightIn(max = maxPopupHeight)
743883 .widthIn(min = buttonWidth, max = maxPopupWidth.coerceAtLeast(buttonWidth))
@@ -758,6 +898,7 @@ private fun SplitButtonImpl(
758898 content = secondaryContentMenu,
759899 )
760900 }
901+
761902 secondaryContent != null -> {
762903 PopupContainer (
763904 modifier = splitButtonPopupModifier,
0 commit comments