1

I have some content which is exactly 500x500 pixels which I'd like to show in a window.

If I set the window size via state = WindowState(...), it seems to create a window where the size is actually slightly smaller, so the content gets cut off.

Quick test app:

fun main() = application {
    Window(onCloseRequest = ::exitApplication, state = WindowState(width = 500.dp, height = 500.dp), resizable = false) {
        Surface(color = Color.Black, modifier = Modifier.fillMaxSize()) {
            Canvas(modifier = Modifier.fillMaxSize()) {
                withTransform(transformBlock = {
                    scale(500.0f, 500.0f, pivot = Offset.Zero)
                }) {
                    drawLine(color = Color.Blue, start = Offset(0.0f, 0.0f), end = Offset(1.0f, 1.0f))
                    drawLine(color = Color.Blue, start = Offset(1.0f, 0.0f), end = Offset(0.0f, 1.0f))
                }
            }
        }
    }
}

The result is this window of outer size 486 x 493:

screenshot

How do I do the equivalent of Swing's pack() to fit the window to the content?

Hakanai
  • 12,010
  • 10
  • 62
  • 132

2 Answers2

2

The drawn window header is part of the selected area of the window size, it is possible to turn it off and use the composition elements to implement your own title bar

undecorated = true

Simple

application {
    val state = rememberWindowState(
        position = WindowPosition(Alignment.Center), size = DpSize(1280.dp, 768.dp)
    )
    Window(
        // Hide default window title
        undecorated = true
    )
    {
        AppView()
    }
    
    // You custom title bar
    AppWindowTitleBar(
                { state.isMinimized = !state.isMinimized },
                { exitApplication() }
            )
}

@Composable
fun WindowScope.AppWindowTitleBar(onMinimize: () -> Unit, onClose: () -> Unit) {
    AppDraggableArea()
    Box(Modifier.fillMaxWidth()) {
        Row(Modifier.align(Alignment.TopEnd).padding(horizontal = 8.dp)) {
            IconButton(onClick = onMinimize) {
                // minimize button
            }
            IconButton(onClick = onClose) {
                // close button
            }
        }
    }
}

@Composable
private fun WindowScope.AppDraggableArea() = WindowDraggableArea {
    Box(
        Modifier.fillMaxWidth().height(18.dp)/*.offset(y = 2.dp)*/
            .padding(horizontal = 160.dp)
            .shadow(4.dp, RoundedCornerShape(4.dp, 4.dp, 12.dp, 12.dp), ambientColor = Color.White)
            .background(Colors.DarkBlue)
    )
}
QiXi
  • 59
  • 5
  • Now if only I could access the OS routines for drawing the bits of the window, LOL – Hakanai Jun 11 '23 at 16:33
  • Look, it's very easy to create your own window removal elements. https://github.com/JetBrains/compose-multiplatform/tree/396413168fbb4eb31eeb7fbc7da3ac66cabf21d4/tutorials/Window_API_new#draggable-window-area – QiXi Jun 12 '23 at 02:22
  • By using your own window title, you have complete control over the contents of the window. If you need the contents of a window 500 * 500, then you draw your title, for example, with a height of 24dp and under it an area of ​​500 * 500, then you create a window with a width of 500 and a height of 524 – QiXi Jun 12 '23 at 02:45
  • This is useful in any case and something I've taken into consideration because I think I will eventually want the window to be draggable. But it doesn't solve the core question of how to have the window sized to fit the contents. What I think I might have to do is measure the contents up-front somehow. – Hakanai Jul 25 '23 at 22:59
1

It is simple

val windowState = rememberWindowState(size = DpSize.Unspecified)
Window(onCloseRequest = ::exitApplication, state = windowState) {...}
Ahmed Hnewa
  • 1,185
  • 1
  • 4
  • 14
  • 1
    That makes the window take up the whole screen. At least with my example. If there's just a Button inside it, it seems to fit the window to the button just fine, so maybe it's no good if the content is a Surface? – Hakanai Sep 13 '22 at 12:13