diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButton.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButton.kt index ce64b2fa..bc21bbba 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButton.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButton.kt @@ -49,6 +49,7 @@ import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.resolveDefaults +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -1074,12 +1075,14 @@ internal fun AuroraCommandButton( for (text in preLayoutInfo.texts) { CommandButtonTextContent( - text, modelStateInfoForText, currStateForText, resolvedTextStyle + text, modelStateInfoForText, currStateForText, resolvedTextStyle, + presentationModel.textOverflow ) } for (extraText in preLayoutInfo.extraTexts) { CommandButtonExtraTextContent( - extraText, modelStateInfoForText, currStateForText, resolvedTextStyle + extraText, modelStateInfoForText, currStateForText, resolvedTextStyle, + presentationModel.textOverflow ) } @@ -1242,7 +1245,7 @@ internal fun AuroraCommandButton( @Composable private fun CommandButtonTextContent( text: String, modelStateInfo: ModelStateInfo, currState: ComponentState, - style: TextStyle + style: TextStyle, overflow: TextOverflow ) { val decorationAreaType = AuroraSkin.decorationAreaType val skinColors = AuroraSkin.colors @@ -1265,7 +1268,7 @@ private fun CommandButtonTextContent( ) { // Since we're passing the resolved style that has the default color, // also explicitly pass our text color to override the one set in the style - AuroraText(text = text, color = textColor, style = style) + AuroraText(text = text, color = textColor, style = style, maxLines = 1, overflow = overflow) } } @@ -1273,7 +1276,7 @@ private fun CommandButtonTextContent( @Composable private fun CommandButtonExtraTextContent( text: String, modelStateInfo: ModelStateInfo, currState: ComponentState, - style: TextStyle + style: TextStyle, overflow: TextOverflow ) { val decorationAreaType = AuroraSkin.decorationAreaType val skinColors = AuroraSkin.colors @@ -1315,7 +1318,13 @@ private fun CommandButtonExtraTextContent( ) { // Since we're passing the resolved style that has the default color, // also explicitly pass our text color to override the one set in the style - AuroraText(text = text, color = disabledFgColor, style = style) + AuroraText( + text = text, + color = disabledFgColor, + style = style, + maxLines = 1, + overflow = overflow + ) } } diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButtonPanel.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButtonPanel.kt index fcfac649..ff1d0c23 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButtonPanel.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/AuroraCommandButtonPanel.kt @@ -146,6 +146,7 @@ internal fun AuroraCommandButtonPanel( isMenu = presentationModel.isMenu, backgroundAppearanceStrategy = presentationModel.backgroundAppearanceStrategy, textStyle = presentationModel.commandTextStyle, + textOverflow = presentationModel.commandTextOverflow, horizontalAlignment = presentationModel.commandHorizontalAlignment, horizontalGapScaleFactor = presentationModel.commandHorizontalGapScaleFactor, verticalGapScaleFactor = presentationModel.commandVerticalGapScaleFactor, @@ -579,6 +580,7 @@ internal fun getPreferredCommandButtonPanelSize( isMenu = presentationModel.isMenu, backgroundAppearanceStrategy = presentationModel.backgroundAppearanceStrategy, textStyle = presentationModel.commandTextStyle, + textOverflow = presentationModel.commandTextOverflow, horizontalAlignment = presentationModel.commandHorizontalAlignment, horizontalGapScaleFactor = presentationModel.commandHorizontalGapScaleFactor, verticalGapScaleFactor = presentationModel.commandVerticalGapScaleFactor, diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerMedium.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerMedium.kt index 8acf2714..1080c93d 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerMedium.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerMedium.kt @@ -251,7 +251,8 @@ internal open class CommandButtonLayoutManagerMedium( ) textHeight = paragraph.height - val textTop = paddingTop + (finalHeight - textHeight - paddingTop - paddingBottom) / 2.0f + val textTop = + paddingTop + (finalHeight - textHeight - paddingTop - paddingBottom) / 2.0f val lineLayoutInfo = CommandButtonLayoutManager.TextLayoutInfo( text = command.text, textRect = Rect( @@ -283,6 +284,41 @@ internal open class CommandButtonLayoutManagerMedium( bottom = (finalHeight - popupIconHeight) / 2.0f + popupIconHeight + 1.0f ) } + + // Account for content overflowing the available horizontal space (constrained width + // scenario). + if (hasText || hasPopup) { + val paddingEnd = presentationModel.horizontalGapScaleFactor * + paddingValues.calculateEndPadding(layoutDirection).toPx() + if (hasPopup) { + if (popupActionRect.right > (finalWidth - paddingEnd)) { + shiftX = popupActionRect.right - (finalWidth - paddingEnd) + // Shift the popup action rectangle to the left + popupActionRect = + popupActionRect.translate(translateX = -shiftX, translateY = 0.0f) + if (hasText) { + // And shift the right coordinate of the text rectangle + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left, + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right - shiftX, + bottom = textLayoutInfoList[0].textRect.bottom + ) + } + } + } else { + // We have no popup, but guaranteed to have text in here + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left, + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right.coerceAtMost( + finalWidth - paddingEnd + ), + bottom = textLayoutInfoList[0].textRect.bottom + ) + } + } + var xBorderBetweenActionAndPopup = 0.0f when (preLayoutInfo.commandButtonKind) { CommandButtonKind.ActionOnly -> { @@ -419,7 +455,8 @@ internal open class CommandButtonLayoutManagerMedium( density = _density, maxLines = 1, resourceLoader = resourceLoader ) textHeight = paragraph.height - val textTop = paddingTop + (finalHeight - textHeight - paddingTop - paddingBottom) / 2.0f + val textTop = + paddingTop + (finalHeight - textHeight - paddingTop - paddingBottom) / 2.0f val lineLayoutInfo = CommandButtonLayoutManager.TextLayoutInfo( text = command.text, textRect = Rect( @@ -451,6 +488,39 @@ internal open class CommandButtonLayoutManagerMedium( bottom = (finalHeight - popupIconHeight) / 2.0f + popupIconHeight + 1.0f ) } + + // Account for content overflowing the available horizontal space (constrained width + // scenario). + if (hasText || hasPopup) { + val paddingEnd = presentationModel.horizontalGapScaleFactor * + paddingValues.calculateEndPadding(layoutDirection).toPx() + if (hasPopup) { + if (popupActionRect.left < paddingEnd) { + shiftX = paddingEnd - popupActionRect.left + // Shift the popup action rectangle to the right + popupActionRect = + popupActionRect.translate(translateX = shiftX, translateY = 0.0f) + if (hasText) { + // And shift the left coordinate of the text rectangle + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left + shiftX, + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right, + bottom = textLayoutInfoList[0].textRect.bottom + ) + } + } + } else { + // We have no popup, but guaranteed to have text in here + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left.coerceAtLeast(popupActionRect.left), + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right, + bottom = textLayoutInfoList[0].textRect.bottom + ) + } + } + var xBorderBetweenActionAndPopup = 0.0f when (preLayoutInfo.commandButtonKind) { CommandButtonKind.ActionOnly -> { diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerTile.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerTile.kt index 5b6c0522..1356b440 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerTile.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/layout/CommandButtonLayoutManagerTile.kt @@ -218,7 +218,8 @@ internal open class CommandButtonLayoutManagerTile( if (ltr) { var x = presentationModel.horizontalGapScaleFactor * - paddingValues.calculateStartPadding(layoutDirection).toPx() + shiftX - layoutHGap + paddingValues.calculateStartPadding(layoutDirection) + .toPx() + shiftX - layoutHGap // icon if (hasIcon) { @@ -300,6 +301,65 @@ internal open class CommandButtonLayoutManagerTile( bottom = (finalHeight - popupIconHeight) / 2.0f + popupIconHeight + 1.0f ) } + + // Account for content overflowing the available horizontal space (constrained width + // scenario). + if (hasText || hasPopup) { + val paddingEnd = presentationModel.horizontalGapScaleFactor * + paddingValues.calculateEndPadding(layoutDirection).toPx() + if (hasPopup) { + if (popupActionRect.right > (finalWidth - paddingEnd)) { + shiftX = popupActionRect.right - (finalWidth - paddingEnd) + // Shift the popup action rectangle to the left + popupActionRect = + popupActionRect.translate(translateX = -shiftX, translateY = 0.0f) + if (hasText) { + // And shift the right coordinate of the text rectangle if needed + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left, + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right.coerceAtMost( + popupActionRect.left - 2 * layoutHGap + ), + bottom = textLayoutInfoList[0].textRect.bottom + ) + } + if (command.extraText != null) { + // And shift the right coordinate of the extra text rectangle if needed + extraTextLayoutInfoList[0].textRect = Rect( + left = extraTextLayoutInfoList[0].textRect.left, + top = extraTextLayoutInfoList[0].textRect.top, + right = extraTextLayoutInfoList[0].textRect.right.coerceAtMost( + popupActionRect.left - 2 * layoutHGap + ), + bottom = extraTextLayoutInfoList[0].textRect.bottom + ) + } + } + } else { + // We have no popup, but guaranteed to have text in here + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left, + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right.coerceAtMost( + finalWidth - paddingEnd + ), + bottom = textLayoutInfoList[0].textRect.bottom + ) + if (command.extraText != null) { + // And shift the right coordinate of the extra text rectangle if needed + extraTextLayoutInfoList[0].textRect = Rect( + left = extraTextLayoutInfoList[0].textRect.left, + top = extraTextLayoutInfoList[0].textRect.top, + right = extraTextLayoutInfoList[0].textRect.right.coerceAtMost( + finalWidth - paddingEnd + ), + bottom = extraTextLayoutInfoList[0].textRect.bottom + ) + } + } + } + var xBorderBetweenActionAndPopup = 0.0f when (preLayoutInfo.commandButtonKind) { CommandButtonKind.ActionOnly -> { @@ -414,7 +474,8 @@ internal open class CommandButtonLayoutManagerTile( } } else { var x = finalWidth - presentationModel.horizontalGapScaleFactor * - paddingValues.calculateStartPadding(layoutDirection).toPx() - shiftX + layoutHGap + paddingValues.calculateStartPadding(layoutDirection) + .toPx() - shiftX + layoutHGap // icon if (hasIcon) { @@ -497,6 +558,60 @@ internal open class CommandButtonLayoutManagerTile( ) } + // Account for content overflowing the available horizontal space (constrained width + // scenario). + if (hasText || hasPopup) { + val paddingEnd = presentationModel.horizontalGapScaleFactor * + paddingValues.calculateEndPadding(layoutDirection).toPx() + if (hasPopup) { + if (popupActionRect.left < paddingEnd) { + shiftX = paddingEnd - popupActionRect.left + // Shift the popup action rectangle to the right + popupActionRect = + popupActionRect.translate(translateX = shiftX, translateY = 0.0f) + if (hasText) { + // And shift the left coordinate of the text rectangle if needed + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left.coerceAtLeast( + popupActionRect.right + 2 * layoutHGap + ), + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right, + bottom = textLayoutInfoList[0].textRect.bottom + ) + if (command.extraText != null) { + // And shift the right coordinate of the extra text rectangle if needed + extraTextLayoutInfoList[0].textRect = Rect( + left = extraTextLayoutInfoList[0].textRect.left.coerceAtLeast( + popupActionRect.right + 2 * layoutHGap + ), + top = extraTextLayoutInfoList[0].textRect.top, + right = extraTextLayoutInfoList[0].textRect.right, + bottom = extraTextLayoutInfoList[0].textRect.bottom + ) + } + } + } + } else { + // We have no popup, but guaranteed to have text in here + textLayoutInfoList[0].textRect = Rect( + left = textLayoutInfoList[0].textRect.left.coerceAtLeast(paddingEnd), + top = textLayoutInfoList[0].textRect.top, + right = textLayoutInfoList[0].textRect.right, + bottom = textLayoutInfoList[0].textRect.bottom + ) + if (command.extraText != null) { + // And shift the right coordinate of the extra text rectangle if needed + extraTextLayoutInfoList[0].textRect = Rect( + left = extraTextLayoutInfoList[0].textRect.left.coerceAtLeast(paddingEnd), + top = extraTextLayoutInfoList[0].textRect.top, + right = extraTextLayoutInfoList[0].textRect.right, + bottom = extraTextLayoutInfoList[0].textRect.bottom + ) + } + } + } + var xBorderBetweenActionAndPopup = 0.0f when (preLayoutInfo.commandButtonKind) { CommandButtonKind.ActionOnly -> { diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandButtonPresentationModel.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandButtonPresentationModel.kt index f36dc493..e74f248f 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandButtonPresentationModel.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandButtonPresentationModel.kt @@ -17,6 +17,7 @@ package org.pushingpixels.aurora.component.model import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.pushingpixels.aurora.theming.BackgroundAppearanceStrategy @@ -55,6 +56,7 @@ data class CommandButtonPresentationModel( val iconActiveFilterStrategy: IconFilterStrategy = IconFilterStrategy.Original, val forceAllocateSpaceForIcon: Boolean = false, val textStyle: TextStyle? = null, + val textOverflow: TextOverflow = TextOverflow.Clip, val popupPlacementStrategy: PopupPlacementStrategy = PopupPlacementStrategy.Downward, val toDismissPopupsOnActivation: Boolean = true, val autoRepeatAction: Boolean = false, @@ -82,6 +84,7 @@ data class CommandButtonPresentationModel( val iconActiveFilterStrategy: IconFilterStrategy? = null, val forceAllocateSpaceForIcon: Boolean? = null, val textStyle: TextStyle? = null, + val textOverflow: TextOverflow? = null, val popupPlacementStrategy: PopupPlacementStrategy? = null, val toDismissPopupsOnActivation: Boolean? = null, val autoRepeatAction: Boolean? = null, @@ -109,6 +112,7 @@ data class CommandButtonPresentationModel( iconActiveFilterStrategy = overlay.iconActiveFilterStrategy ?: this.iconActiveFilterStrategy, forceAllocateSpaceForIcon = overlay.forceAllocateSpaceForIcon ?: this.forceAllocateSpaceForIcon, textStyle = overlay.textStyle ?: this.textStyle, + textOverflow = overlay.textOverflow ?: this.textOverflow, popupPlacementStrategy = overlay.popupPlacementStrategy ?: this.popupPlacementStrategy, toDismissPopupsOnActivation = overlay.toDismissPopupsOnActivation ?: this.toDismissPopupsOnActivation, autoRepeatAction = overlay.autoRepeatAction ?: this.autoRepeatAction, diff --git a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandPanelModels.kt b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandPanelModels.kt index 3c311f58..539443d2 100644 --- a/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandPanelModels.kt +++ b/component/src/desktopMain/kotlin/org/pushingpixels/aurora/component/model/CommandPanelModels.kt @@ -17,6 +17,7 @@ package org.pushingpixels.aurora.component.model import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.pushingpixels.aurora.theming.BackgroundAppearanceStrategy @@ -52,6 +53,7 @@ data class CommandPanelPresentationModel( val commandIconDimension: Dp = 0.dp, val commandContentPadding: PaddingValues = CommandButtonSizingConstants.CompactButtonContentPadding, val commandTextStyle: TextStyle? = null, + val commandTextOverflow: TextOverflow = TextOverflow.Clip, val commandHorizontalAlignment: HorizontalAlignment = HorizontalAlignment.Center, val commandHorizontalGapScaleFactor: Float = 1.0f, val commandVerticalGapScaleFactor: Float = 1.0f, diff --git a/demo/src/desktopMain/kotlin/org/pushingpixels/aurora/demo/AuroraBreadcrumbBarDemo.kt b/demo/src/desktopMain/kotlin/org/pushingpixels/aurora/demo/AuroraBreadcrumbBarDemo.kt index a72b9137..c156cf12 100644 --- a/demo/src/desktopMain/kotlin/org/pushingpixels/aurora/demo/AuroraBreadcrumbBarDemo.kt +++ b/demo/src/desktopMain/kotlin/org/pushingpixels/aurora/demo/AuroraBreadcrumbBarDemo.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.window.WindowPlacement @@ -185,6 +186,7 @@ fun AuroraWindowScope.BreadcrumbContent(auroraSkinDefinition: MutableState