b

    Ben Abramovitch

    1 year ago
    Hey, if anyone has a moment to take a look at this issue I’m having that would be wonderful. https://stackoverflow.com/questions/68187240/custom-layout-draws-position-0-off-screen-when-content-is-larger-than-screen Essentially when I try to place a placeable off screen, instead of position 0 drawing at position 0, it’s drawing as if the total content was centered, so the further i am off screen, the further x=0 gets drawn, such as -300 I feel like there is maybe a way to handle this with AlignmentLines but i can’t figure it out if so. Also, dividing the difference by two and tacking that on seemed to work for width, but it’s not working for height even if i try to account for the statusbar/toolbars etc, and it. seems pretty hacky anyway
    Tash

    Tash

    1 year ago
    isn’t that just the coordinate system, i.e. “offscreen to the left” for X has to be a negative value?
    b

    Ben Abramovitch

    1 year ago
    If i go a b c d e f and e/f are off screen it draws b c d e
    i can use the drag gesture to go left/right and see a and f, but it isn’t drawing it uncentered
    Tash

    Tash

    1 year ago
    i see, so you want a to be at 0,0
    b

    Ben Abramovitch

    1 year ago
    correct
    Tash

    Tash

    1 year ago
    i see, so you’re saying when you do
    width = totalWidth
    in your layout, (0,0) is actually offscreen, and what you need is almost a translation of the “canvas” to bring 0,0 to the top-left of the visible window
    you said you’d tried
    AlignmentLine
    , how’d that go?
    b

    Ben Abramovitch

    1 year ago
    Well, if totalWidth = Screenwidth then 0 is 0 its when totalWidth > ScreenWidth it starts inching off screen I don’t understand what it wants me to do with the alignment lines. I’ve been doing translations with width / 2 as the offset but it doesn’t work accurately on the height
    and i feel like having manually translate it is the wrong way
    Where i set the layout height and width It can take in this map of alignment lines, but I can’t find a good explanation of what I’m supposed to do with them….
    package androidx.compose.ui.layout
    
     fun layout(
            width: Int,
            height: Int,
            alignmentLines: Map<AlignmentLine, Int> = emptyMap(),
            placementBlock: Placeable.PlacementScope.() -> Unit
        ) = object : MeasureResult {
            override val width = width
            override val height = height
            override val alignmentLines = alignmentLines
            override fun placeChildren() {
                Placeable.PlacementScope.executeWithRtlMirroringValues(
                    width,
                    layoutDirection,
                    placementBlock
                )
            }
        }
    }
    also i’m not sure if thats even what can solve it… but setting normal alignment on on the columns didn’t help
    Adam Powell

    Adam Powell

    1 year ago
    if a custom layout composable or layout modifier attempts to declare a size outside of the bounds of the constraints it was given, it will center its desired sized content in a space that satisfies the constraints
    b

    Ben Abramovitch

    1 year ago
    I declared that the height and width when creating the layout, how else would i do it otherwise?
    Adam Powell

    Adam Powell

    1 year ago
    this allows the parent of the misbehaving element to work with a placeable that meets the constraints it was measured with so that it doesn't have to defensively account for misbehaving child measurables
    looks like you're ignoring
    constraints.maxWidth
    in that code
    b

    Ben Abramovitch

    1 year ago
    @Adam Powell Where? The stack example has
    constraints.copy(
                        minWidth = headerWidth,
                        maxWidth = headerWidth,
                        minHeight = height,
                        maxHeight = height,
                    )
    Adding a fillMaxWidth modifier here doesn’t help either
    Layout(
        modifier = Modifier.fillMaxWidth(),
        content = {
            columnList.forEach { header ->
                Box {
                    headerContent(header)
                }
            }
        },
    )
    or here on the text box
    headerContent: @Composable (title: String) -> Unit = { title ->
        Text(title, modifier = Modifier.fillMaxWidth())
    },
    Adam Powell

    Adam Powell

    1 year ago
    the code in the SO question has:
    layout(width = totalWidth, height = height) {
    and those are calculated as
    val height = 45.dp.roundToPx()
    val totalWidth = headerWidth * numberOfHeaders
    both of which completely ignore the incoming
    constraints
    the contract of layout is that the width/height passed to
    layout
    must be in range of the
    constraints
    , if you violate that, your measurable gets clamped to the constraints and your placement is centered in the clamped space where your parent placed you
    b

    Ben Abramovitch

    1 year ago
    I’m sorry but I don’t understand. Which incoming constraints?
    Adam Powell

    Adam Powell

    1 year ago
    this: `
    ) { measureables, constraints ->
    b

    Ben Abramovitch

    1 year ago
    Those are vals and I can’t edit them?
    Adam Powell

    Adam Powell

    1 year ago
    that is correct
    b

    Ben Abramovitch

    1 year ago
    And then i set up each placeable with the widths i want?
    Adam Powell

    Adam Powell

    1 year ago
    you can do what you wish in your own space, yes, but your layout size must satisfy those constraints
    b

    Ben Abramovitch

    1 year ago
    So i have to set the width/height on the layout ?
    Layout(
        modifier = Modifier.fillMaxWidth(headerWidth * numberOfHeaders.toFloat()).fillMaxHeight(100f),
        content = {
            columnList.forEach { header ->
                Box {
                    headerContent(header)
                }
            }
        }
    )
    This doesn’t work either (i changed height to also be 100 further below)
    Doesn’t work when i try width/height either
    Same problem with this
    @Composable
    fun HeaderTest(
        columnList: List<String>,
        headerWidth: Dp,
        headerContent: @Composable (title: String) -> Unit = { title ->
            Text(title, modifier = Modifier.fillMaxWidth())
        },
    ) {
        val numberOfHeaders = columnList.count()
    
        Layout(
            modifier = Modifier
                .width(headerWidth * numberOfHeaders)
                .height(100.dp)
                .fillMaxHeight()
                .fillMaxWidth(),
            content = {
                columnList.forEach { header ->
                    Box {
                        headerContent(header)
                    }
                }
            }
        ) { measureables, constraints ->
    
            val height = 100.dp
            val totalWidth = headerWidth * numberOfHeaders
    
            val placeWithHeader = measureables.map { measurable ->
                val constraint = constraints.copy(
                    minWidth = headerWidth.roundToPx(),
                    maxWidth = headerWidth.roundToPx(),
                    minHeight = height.roundToPx(),
                    maxHeight = height.roundToPx()
                )
    
                measurable.measure(constraint)
            }
    
            layout(width = totalWidth.roundToPx(), height = height.roundToPx()) {
                placeWithHeader.forEachIndexed { index, placeable ->
                    val offset = index * headerWidth.roundToPx()
                    placeable.place(offset, 0)
                }
            }
        }
    }
    Adam Powell

    Adam Powell

    1 year ago
    you are still ignoring the constraints
    something that would satisfy the constraints could be:
    val height = 100.dp.roundToPx().coerceIn(constraints.minHeight, constraints.maxHeight)
    val totalWidth = (headerWidth * numberOfHeaders).coerceIn(constraints.minWidth, constraints.maxWidth)
    b

    Ben Abramovitch

    1 year ago
    Okay that does work on that, but I’m still not really sure I understand what was going…. It seemed like I was doing the right thing since I was constraining the placable and I was within the bounds I was setting. I’ll have to do more reading, but I haven’t really seen that in custom layout examples. E.g this one says don’t constrain them
    Layout(
            modifier = modifier,
            content = content
        ) { measurables, constraints ->
            // Don't constrain child views further, measure them with given constraints
            // List of measured children
            val placeables = measurables.map { measurable ->
                // Measure each children
                measurable.measure(constraints)
            }
    But it also does the layout different….
    // Set the size of the layout as big as it can
            layout(constraints.maxWidth, constraints.maxHeight) {
    Lots more reading to do, but this is why i’m starting sooner!
    maybe if i’d done one of the code labs….
    Oh and thank you! I spent a whole day trying to narrow down what was going on and then trying things out =(
    Adam Powell

    Adam Powell

    1 year ago
    no problem 🙂
    what you're seeing here is the bridge between composition and layout. The modifiers you pass to the
    Layout
    composable influence but don't completely control the constraints your layout receives
    when you call
    measurable.measure(constraints)
    , it's calling the same kind of measuring code you're writing right now, but on the child element
    when you call the
    layout(width, height) {
    function in that block, you're telling it the desired size of the
    Placeable
    that the measure call returns
    and then you can place your own children however you like
    things like the horizontal and vertical scroll modifiers measure their child with infinite max constraints, and if the child measures larger than the max the scroll modifier was given, the scroll modifier occupies that max space and the difference becomes the amount you can scroll by
    we don't have as much documentation around this as I'd like 🙂
    b

    Ben Abramovitch

    1 year ago
    Oh that makes more sense with the layout now. I thought we were coercing it for the childen, but the same values go into the layout
    Ya, well this is pretty new, not even stable yet (soon) AND i’m trying to do something custom hehe
    Adam Powell

    Adam Powell

    1 year ago
    eh, this part has been stable for a while 🙂 after years of writing Views and ViewGroups with custom measure logic we wanted to make sure doing it was a lot more straightforward here
    b

    Ben Abramovitch

    1 year ago
    I meant compose in general
    it’s still beta (although ya, components can be more stable. than others)
    Also as in, not enough time to fully document everything as you want because things are still being worked on elsewhere