Skip to content

Instantly share code, notes, and snippets.

@mstr2
Last active October 27, 2025 11:12
Show Gist options
  • Select an option

  • Save mstr2/0befc541ee7297b6db2865cc5e4dbd09 to your computer and use it in GitHub Desktop.

Select an option

Save mstr2/0befc541ee7297b6db2865cc5e4dbd09 to your computer and use it in GitHub Desktop.

Goals

Add a new stage style that extends the client area across the entire window, allowing JavaFX applications to place scene graph nodes everywhere and create custom client-side header bars with the javafx.scene.layout.HeaderBar layout container. It has three areas that accept child nodes: leading, center, and trailing. HeaderBar automatically adjusts for the placement of the default window buttons (iconify, maximize, close) on the left or right side of the window.

extendedwindow

Non-Goals

It is not a goal to:

  • Only provide a better version of an UNDECORATED stage style without window buttons (iconify, maximize, close). Almost all applications will want to have window buttons that correspond to the OS, and only a tiny minority will want to fully customize even the window buttons.
  • Provide an API to control the basic appearance of platform windows. Applications will have no control over aspects like rounded vs. non-rounded corners, resize borders, and so on.
  • Provide built-in tools to respond to light/dark modes of the OS, or stylistic changes for focused/unfocused windows. Applications can already to this on their own with the Platform.Preferences API.

Motivation

Many modern applications use a customized title bar area to achieve a cohesive look and feel, without the classic separation between client area and non-client title bar. Here are some examples:

example1

example2

example3

example4

example5

Notice how these applications take control of the title bar area by using interactive controls with application-specific styling.

Description

This feature needs to be designed for JavaFX with various constraints in mind:

  1. Multi-platform
    Since JavaFX is a multi-platform framework, this feature must work out of the box on all supported platforms. It is a non-starter to require application developers to add platform-specific code to their applications if they want to use this feature.

  2. Ease of use
    It must be easy to use. In particular, this means that default window interactions must work reliably and feel at home on the OS. If possible, we will use platform-native window buttons (iconify, maximize, close) instead of crafting our own bespoke versions.

    Window buttons require quite a bit of effort to get it just right. It's way more effort than just throwing a bunch of buttons in the window corner and calling it a day. Window buttons need to interact with the OS in very specific ways to enable features like snap layouts, right-to-left locales, and other aspects.

  3. Customizability
    All of the application examples given earlier use window buttons that are not gratuitously different than the platform-native window buttons; they feel at home on the target OS. This is why we provide platform-specific default window buttons. Application developers who need to customize window buttons can opt out of the default window buttons, and create their own buttons in the JavaFX scene graph.

What is the EXTENDED stage style?

  1. EXTENDED is like UNDECORATED in the following ways: The application controls the entirety of the window. There is no non-client area (i.e. no title bar), and scene graph nodes can be placed everywhere. Since there is no non-client title bar, applications will have to provide their own HeaderBar and their own system menu (if they so desire).

  2. EXTENDED is like DECORATED in the following ways: The window has a resize border, shadows, and window animations. It has the platform window buttons (minimize, maximize, close) superimposed over the application window. Just as with a decorated window, applications have no control over whether corners are rounded, how borders look like, and so on.

The EXTENDED style is a conditional feature that is available on Windows, macOS, and Linux, and it automatically downgrades to DECORATED on all other platforms.

FeatureWindowsmacOSLinuxAndroid/iOS
Default window buttonsprovided by JavaFXnativeprovided by JavaFXn/a
Resize borderoutside, nativeoutside, nativeinside, implemented in glass-gtkn/a
Drop shadowsyesyesyesn/a
Rounded cornersyesyesneeds investigationn/a
Click-and-drag to moveyes, with HeaderBarn/a
Double-click on HeaderBarmaximizemaximize/minimize/nothing,
depending on system settings
maximizen/a
Right-click to invoke system menuyes, with HeaderBarnoyes, with HeaderBarn/a
Window titleneeds to be provided by applicationn/a

Default window buttons

The default window buttons are superimposed over the application window, and are not part of the JavaFX scene graph. On macOS, we can simply ask the operating system to do this for us. On Windows and Linux/GTK, there is no such option, which is why we need to do this within JavaFX.

The preferred height of the default window buttons can be specified with HeaderBar.setPrefButtonHeight(Stage, double). The platform implementation tries to accommodate the preferred height in various ways, such as by stretching the window buttons (fully or partially) to fill the preferred height, or centering the window buttons (fully or partially) within the preferred height.

The color scheme of the default window buttons automatically adapts to the background of the Scene (more precisely, the Scene.fill property) to remain easily recognizable. Application developers need to set the scene background to a value that corrsponds to the overall brightness of their application, independent of whether the scene background is visible or obscured by other controls.

Custom window buttons

Applications can opt out of default window buttons by setting the stage's preferred window button height to zero by calling HeaderBar.setPrefButtonHeight(Stage, double), and provide their own custom buttons in the JavaFX scene graph. In order to integrate custom buttons with the platform window manager, applications need to set HeaderBar.setButtonType(Node, HeaderButtonType) on each button, where HeaderButtonType is defined as follows:

public enum javafx.scene.layout.HeaderButtonType {
    ICONIFY, MAXIMIZE, CLOSE
}

New layout container: javafx.scene.layout.HeaderBar

With the absence of a system-provided header bar, the click-to-drag and double-click to maximize behaviors are lost with the removal of the system-provided header bar. Applications need to provide their own client-side header bar to get these window behaviors back.

The appearance of the client-side header bar (colors, dark/light theme, etc.) is left to the purview of application developers. The client-side header bar is a control just like any other JavaFX control, and application developers will decide how it looks like. Notably, there is no translucency, because JavaFX does not support window-level translucency yet.

HeaderBar allows applications to create an entirely custom header bar control. It can be any size, since it is just a regular resizable region in the JavaFX scene graph. It enables the click-to-drag and double-click to maximize behaviors that are usually provided by the system title bar.

Defining draggable areas

The background of a header bar is draggable by default, but its content is not. Applications can further customize the draggable content with the HeaderBar.setDragType(Node, HeaderDragType) static method. HeaderDragType is defined as follows:

public enum javafx.scene.layout.HeaderDragType {
    DRAGGABLE, // Marks a single node as draggable
    DRAGGABLE_SUBTREE, // Marks a node and all of its descendants as draggable
    NONE // Cancels an inherited DRAGGABLE_SUBTREE flag
}

System-reserved areas for window buttons

HeaderBar also defines two read-only properties: leftSystemInset and rightSystemInset of type Dimension2D, which correspond to the size of the system-reserved areas for window buttons. These areas refer to the physical top left and top right corners of the window, not adjusted for layout orientation (left-to-right/right-to-left). Applications should not place nodes in these areas, as the window buttons will be superimposed over the application window. If HeaderBar is used with a stage style other than EXTENDED (or if it was downgraded to DECORATED), the system-reserved areas will be empty (i.e. Dimension2D(0, 0)).

HeaderBar content

HeaderBar defines three areas in which scene graph nodes can be placed: leading, center, and trailing. For left-to-right locales, leading corresponds to the left side of the window and trailing corresponds to the right side; on right-to-left locales, the directionality is switched. The layout direction can be configured with Node.setNodeOrientation.

HeaderBar automatically adjusts for the presence of superimposed platform window buttons on the left or right side of the window, and ensures that its child nodes are appropriately shifted to the left or to the right.

All children of HeaderBar will be resized to their preferred widths and extend the height of the HeaderBar. It honors the minimum, preferred, and maximum sizes of its children. If a child's resizable range prevents it from be resized to fit within its available space, it will be vertically centered relative to the available space. The alignment can be further customized with layout constraints:

ConstraintTypeDescription
alignmentjavafx.geometry.Pos The alignment of the child within its area of the HeaderBar.
margin javafx.geometry.InsetsMargin space around the outside of the child.

Special alignment of horizontally-centered nodes in the center layout area

When a node is placed in the center area, and its alignment is set to Pos.CENTER, Pos.TOP_CENTER, or Pos.BOTTOM_CENTER, a special layout algorithm is applied: The node is not centered with respect to the center of its layout area, but with respect to the center of the window.

There is a good reason for it: Suppose an application wants to place a control in the center of the header bar (for example, a search text field). HeaderBar automatically adjusts for the presence of superimposed platform window buttons by shifting the header bar content either to the left or to the right (depending on the platform and on layout directionality). However, this would also move the text field that was supposed to be centered in the window slightly off-center. This layout algorithm corrects that and ensures that a centered node in the center area remains centered with respect to the window.

Compare the same application, running on Windows and macOS, and observe the difference between the purple leading, center, and trailing areas, as well as the layout of child nodes within those areas: centeradjustment

Note that HeaderBar respects the minimium, preferred, and maximum sizes of its child nodes, so the centering algorithm will eventually shift the centered node off-center if the layout constraints cannot be satisfied in the available space. Compare the next example, in which the text field is wider than before and the layout is overconstrained. The text field is shifted off-center: centeradjustment

Usage

This is a minimal example that uses a HeaderBar with a TextField in the center area. HeaderBar is usually placed in the top area of a BorderPane root container:

public class MyApp extends Application {
    @Override
    public void start(Stage stage) {
        var headerBar = new HeaderBar();
        headerBar.setCenter(new TextField());

        var root = new BorderPane();
        root.setTop(headerBar);

        stage.setScene(new Scene(root));
        stage.initStyle(StageStyle.EXTENDED);
        stage.show();
    }
}

Alternatives

The integrated look-and-feel of an extended stage with client-side title bar requires intricate interactions with the native platform toolkits, and can only be done in JavaFX.

An alternative implementation might be to provide a window that is very similar to the proposed EXTENDED style, but without the superimposed window buttons. However, window buttons are surprisingly hard to get right. This would necessitate additional APIs to integrate user-provided window buttons with OS features like snap layouts.

@Eng-Fouad

Eng-Fouad commented Sep 18, 2025

Copy link
Copy Markdown

It seems that when we use stage.initStyle(StageStyle.EXTENDED) with RTL stage in Windows, the default buttons on the title bar are not rendered properly:

https://www.youtube.com/watch?v=hoZRQ8Hl-6s

@mstr2

mstr2 commented Sep 18, 2025

Copy link
Copy Markdown
Author

What's your OS version, region, and language settings? Please provide a minimal reproducible example.

@Eng-Fouad

Copy link
Copy Markdown

@Eng-Fouad

Copy link
Copy Markdown

Tried the producer on WSL2 (which supports running Linux GUI apps on Windows), and I got the same behavior.

@Eng-Fouad

Copy link
Copy Markdown

On MacOS, the buttons are always on the left in both cases (LTR & RTL) and it works as expected.

@mstr2

mstr2 commented Sep 18, 2025

Copy link
Copy Markdown
Author

Thanks for the information, I've created a ticket to track this issue: https://bugs.openjdk.org/browse/JDK-8368021

@bgmf

bgmf commented Sep 19, 2025

Copy link
Copy Markdown

While I agree that this RTL-thing might be a problem for at least some people that are using the app I maintain, I found something else that kind of bugs me:

Linux -> Rounded Corners -> need investigation:
The biggest issue is, that Gnome, for example, has pretty round windows by default, making the app look even more alien (custom color scheme and controls). Since I can't adjust the Stage's background, I have no means to do corners by myself (which I already did, when I created a custom window decoration for the app).

Linux -> window buttons in general:
On Gnome it's actually pretty rare to have three buttons at all. maximised is done via double click on the header, leaving minimizing and closing. But I can't change the visibility (or order) of the buttons.
Additionally, native buttons can be configured in which order (and on which side) they are. I don't know if the current implementation takes that into account.

@mstr2

mstr2 commented Sep 19, 2025

Copy link
Copy Markdown
Author

Most people in the OpenJFX community will not read comments here. Please use the mailing list for bug reports and especially for discussions, where more people have a chance to read, comment, and contribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment