Skip to content

Commit

Permalink
Support text overflow in command buttons
Browse files Browse the repository at this point in the history
* Add `textOverflow` in command button presentation model
* Also wire it in the command panel presentation model
* Respect the available horizontal space in `Medium` and `Tile` command button states

Closes #12
  • Loading branch information
kirill-grouchnikov committed Jan 18, 2022
1 parent ff52cc4 commit 5516a21
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 10 deletions.
Expand Up @@ -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
Expand Down Expand Up @@ -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
)
}

Expand Down Expand Up @@ -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
Expand All @@ -1265,15 +1268,15 @@ 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)
}
}

@OptIn(AuroraInternalApi::class)
@Composable
private fun CommandButtonExtraTextContent(
text: String, modelStateInfo: ModelStateInfo, currState: ComponentState,
style: TextStyle
style: TextStyle, overflow: TextOverflow
) {
val decorationAreaType = AuroraSkin.decorationAreaType
val skinColors = AuroraSkin.colors
Expand Down Expand Up @@ -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
)
}
}

Expand Down
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -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(
Expand Down Expand Up @@ -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 -> {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 -> {
Expand Down
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 -> {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 -> {
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 5516a21

Please sign in to comment.