Skip to content

Commit 62c137b

Browse files
authored
The balloon renders in a PopupWindow for the compatibilities (#967)
1 parent 849f791 commit 62c137b

3 files changed

Lines changed: 15 additions & 21 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ google-services.json
6161
freeline.py
6262
freeline/
6363
freeline_project_description.json
64+
.hotswan/

balloon-compose/src/main/kotlin/com/skydoves/balloon/compose/Balloon.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,21 +153,23 @@ public fun Balloon(
153153
// Calculate width constraints for balloon content (issue #779)
154154
val horizontalPadding = builder.paddingLeft + builder.paddingRight +
155155
builder.marginLeft + builder.marginRight
156+
// The balloon renders in a PopupWindow (floating overlay), not in the parent layout.
157+
// Use screen dimensions instead of parent constraints for measurement.
156158
val maxContentWidth = when {
157159
builder.widthRatio > 0f ->
158160
(screenWidth * builder.widthRatio - horizontalPadding).toInt()
159161
builder.maxWidthRatio > 0f ->
160162
(screenWidth * builder.maxWidthRatio - horizontalPadding).toInt()
161-
else -> constraints.maxWidth - horizontalPadding
163+
else -> (screenWidth - horizontalPadding)
162164
}.coerceAtLeast(0)
163165

164166
// Measure balloon content if needed (only when content exists and size not yet calculated)
165167
if (isComposableContent && balloonComposeView.balloonLayoutInfo.value == null) {
166168
val balloonContentConstraints = Constraints(
167169
minWidth = 0,
168-
maxWidth = maxContentWidth.coerceAtMost(constraints.maxWidth),
170+
maxWidth = maxContentWidth.coerceAtMost(screenWidth),
169171
minHeight = 0,
170-
maxHeight = constraints.maxHeight,
172+
maxHeight = (screenWidth * 2).coerceAtLeast(0),
171173
)
172174
val balloonPlaceables = subcompose("balloon_measurement") {
173175
// balloonContent is guaranteed non-null here since isComposableContent is true

balloon-compose/src/main/kotlin/com/skydoves/balloon/compose/BalloonModifier.kt

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -229,24 +229,17 @@ public fun Modifier.balloon(
229229
Layout(
230230
content = { balloonContent() },
231231
measurePolicy = { measurables, constraints ->
232-
val isUnboundedWidth = constraints.maxWidth == Constraints.Infinity
233-
val isUnboundedHeight = constraints.maxHeight == Constraints.Infinity ||
234-
constraints.maxHeight == 0
235-
236-
val effectiveMaxWidth = if (fixedWidthMode) {
237-
// Fixed mode: always use screenWidth so ratio/explicit widths aren't clamped by anchor.
238-
screenWidth
239-
} else if (isUnboundedWidth) {
240-
// Unbounded (e.g., KMM, ScrollView): fall back to screenWidth.
241-
screenWidth
242-
} else {
243-
// Bounded non-fixed mode: respect the parent constraints (e.g., XML+ComposeView).
244-
constraints.maxWidth.coerceAtMost(screenWidth)
245-
}
232+
// The balloon renders in a PopupWindow (floating overlay), not in the parent layout.
233+
// Therefore, parent constraints must NOT limit the balloon content measurement.
234+
// The Layout here is purely for measurement (layout(0, 0) {}), so we always use
235+
// screen dimensions as the upper bound. This fixes:
236+
// - #943: width incorrectly clamped to parent constraints in XML+ComposeView/KMM
237+
// - #952: height incorrectly clamped to parent's fixed height
238+
// - #963: negative constraints.maxHeight from ConstraintLayout on Chromebooks
239+
val effectiveMaxWidth = screenWidth
246240
val effectiveMaxHeight = when {
247241
builder.height != BalloonSizeSpec.WRAP -> builder.height
248-
isUnboundedHeight -> screenWidth * 2
249-
else -> constraints.maxHeight
242+
else -> screenWidth * 2
250243
}.coerceAtLeast(0)
251244

252245
val maxContentWidth = when {
@@ -264,10 +257,8 @@ public fun Modifier.balloon(
264257
}.coerceAtLeast(0)
265258

266259
val targetWidth = if (fixedWidthMode) {
267-
// IMPORTANT: do NOT clamp to constraints.maxWidth (anchor width)
268260
maxContentWidth
269261
} else {
270-
// Non-fixed mode: respect parent constraints.
271262
maxContentWidth.coerceAtMost((effectiveMaxWidth - horizontalPadding).coerceAtLeast(0))
272263
}.coerceAtLeast(0)
273264

0 commit comments

Comments
 (0)