I'm trying to create swing program with custom titlebar (undecorated window). I create a JPanel with BoxLayout, add some components, including glue between title label and buttons (minimize and exit). TitleBarPanel class:
package ru.robert_grammy.gifshooter.component.ui
import ru.robert_grammy.gifshooter.ResourceLoader
import ru.robert_grammy.gifshooter.component.listener.FrameMouseDraggedListener
import ru.robert_grammy.gifshooter.engine.ProgramStrings
import ru.robert_grammy.gifshooter.component.engine.ProgramButton
import ru.robert_grammy.gifshooter.engine.UiSettings
import java.awt.Dimension
import javax.swing.*
import kotlin.system.exitProcess
class TitleBarPanel(private val frame: JFrame) : JPanel() {
companion object {
private const val TITLE_ICON_FILENAME = "icon.png"
private const val MINIMIZE_BUTTON_ICON_FILENAME = "minimize.png"
private const val CLOSE_BUTTON_ICON_FILENAME = "close.png"
}
val titleLabel = JLabel().let {
it.foreground = UiSettings.Colors.DEFAULT_TEXT_COLOR.get()
it.font = UiSettings.Fonts.DEFAULT_FONT.get()
it.text = ProgramStrings.PROGRAM_TITLE.get()
it.icon = ResourceLoader.getIcon(TITLE_ICON_FILENAME)
it.preferredSize = Dimension(UiSettings.Sizes.DEFAULT_LINE_LABEL.getWidth(), UiSettings.Sizes.DEFAULT_LINE_LABEL.getHeight())
add(it)
add(Box.createGlue())
}
val minimizeButton = ProgramButton().let {
it.backgroundColor = UiSettings.Colors.BUTTON_COLOR.get()
it.hoverColor = UiSettings.Colors.DEFAULT_HOVER_BUTTON_COLOR.get()
it.pressColor = UiSettings.Colors.DEFAULT_PRESS_BUTTON_COLOR.get()
it.isBorderPainted = false
it.isFocusable = false
it.preferredSize = Dimension(UiSettings.Sizes.TITLE_BUTTON.getWidth(), UiSettings.Sizes.TITLE_BUTTON.getHeight())
it.icon = ResourceLoader.getIcon(MINIMIZE_BUTTON_ICON_FILENAME)
it.addActionListener {
frame.extendedState = JFrame.ICONIFIED
}
add(it)
}
val closeButton = ProgramButton().let {
it.backgroundColor = UiSettings.Colors.BUTTON_COLOR.get()
it.hoverColor = UiSettings.Colors.EXIT_HOVER_BUTTON_COLOR.get()
it.pressColor = UiSettings.Colors.EXIT_PRESS_BUTTON_COLOR.get()
it.isBorderPainted = false
it.isFocusable = false
it.preferredSize = Dimension(UiSettings.Sizes.TITLE_BUTTON.getWidth(), UiSettings.Sizes.TITLE_BUTTON.getHeight())
it.icon = ResourceLoader.getIcon(CLOSE_BUTTON_ICON_FILENAME)
it.addActionListener {
exitProcess(0)
}
add(it)
}
private val frameMouseDraggedListener = FrameMouseDraggedListener(frame)
init {
layout = BoxLayout(this, BoxLayout.LINE_AXIS)
background = UiSettings.Colors.PRIMARY_COLOR.get()
foreground = UiSettings.Colors.DEFAULT_TEXT_COLOR.get()
addMouseMotionListener(frameMouseDraggedListener)
}
}
I added instance to frame's content pane after. And it works correctly. Titlebar
But if I add something large to the main panel, Filler stretches as expected, but a one pixel wide space appears at the end of the panel. Extra pixel space
And if I add more Fillers, the space gets bigger. More Fillers = larger empty space
I realize that the Filler should be alone, just demonstrating that the empty space appears because of it. That 1 pixel of emptiness really stresses me out, as the button was supposed to be tight against the end of the pane.
I tried using Filler with a given unchanging width. It helped to get rid of the empty space, but the window lost its flexibility.
I assume that a working alternative is BorderLayout/FlowLayout. Label on one side, a panel with buttons on the other and everything should work. I suppose that's the way I'll go, but before I do I'm still wondering if there's any way to fix the extra space when adding a Glue component.