Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8313424: JavaFX controls in the title bar #1605

Open
wants to merge 66 commits into
base: master
Choose a base branch
from

Conversation

mstr2
Copy link
Collaborator

@mstr2 mstr2 commented Oct 20, 2024

Implementation of EXTENDED and EXTENDED_UTILITY stage style.


Progress

  • Change requires a CSR request matching fixVersion jfx25 to be approved (needs to be created)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 2 Reviewers)

Issue

  • JDK-8313424: JavaFX controls in the title bar (Enhancement - P4)

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1605/head:pull/1605
$ git checkout pull/1605

Update a local copy of the PR:
$ git checkout pull/1605
$ git pull https://git.openjdk.org/jfx.git pull/1605/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1605

View PR using the GUI difftool:
$ git pr show -t 1605

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1605.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Oct 20, 2024

👋 Welcome back mstrauss! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Oct 20, 2024

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk bot added the rfr Ready for review label Oct 20, 2024
@mlbridge
Copy link

mlbridge bot commented Oct 20, 2024

Webrevs

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 20, 2024

/reviewers 2 reviewers
/csr

@openjdk
Copy link

openjdk bot commented Oct 20, 2024

@mstr2
The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 2 Reviewers).

@openjdk openjdk bot added the csr Need approved CSR to integrate pull request label Oct 20, 2024
@openjdk
Copy link

openjdk bot commented Oct 20, 2024

@mstr2 has indicated that a compatibility and specification (CSR) request is needed for this pull request.

@mstr2 please create a CSR request for issue JDK-8313424 with the correct fix version. This pull request cannot be integrated until the CSR request is approved.

@Glavo
Copy link
Contributor

Glavo commented Oct 20, 2024

Hey, I'm glad to see this PR, but do you have any ideas for continuing to provide UNDECORATED_INTERACTIVE in the future? I think UNDECORATED_INTERACTIVE is more useful than EXTENDED for users who want to provide a consistent UI on different platforms.

@tsayao
Copy link
Collaborator

tsayao commented Oct 20, 2024

Very nice. I'll test on Linux and report back.

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 20, 2024

Hey, I'm glad to see this PR, but do you have any ideas for continuing to provide UNDECORATED_INTERACTIVE in the future? I think UNDECORATED_INTERACTIVE is more useful than EXTENDED for users who want to provide a consistent UI on different platforms.

The only difference between the two would be whether the default window buttons are provided. I don't see how a window without default window buttons would be more useful. Even heavily stylized apps like Spotify use window buttons that feel at home on the OS, that doesn't take away from a consistent look and feel.

@tsayao
Copy link
Collaborator

tsayao commented Oct 21, 2024

A few points observed on Linux:

  1. It's possible to resize it to 1px using the provided functionality with gtk_window_begin_resize_drag. An then it's not possible to resize back. The Headerbar should block resizing to the size of window controls.
  2. It's not possible to move the window if the cursor is over a control. Maybe you should just gtk_window_begin_move_drag when a drag is detected, not on click. That would be on WindowContextBase::process_mouse_motion;
  3. The application is closing if the click happens on the top right corner (that's because it's triggering the close button instead of resizing (I think 2 should solve it as well). It closes with:
    (java:16179): Gtk-CRITICAL **: 07:34:26.721: gtk_window_begin_resize_drag: assertion 'gtk_widget_get_visible (widget)' failed
  4. Alt + F8 is working (it's a desktop shortcut for resizing the window) - just pointing out to include in manual testing;
  5. I think rounded edges should be supported on the HeaderBar, since it's the default on gnome. For that to work, EXTENDED should be also transparent on Linux. It would loose the window drop shadow which can be added on the JavaFX side. gdk_window_set_shadow_width should be called with the drop shadow size, so the desktop will know the correct window bounds.

Added later:
6) It should have a "focused" state/pseudo class because on gnome (maybe others) the focused window has a different background on the HeaderBar which is darker.
7) Suggestion: Maybe make window states stylable on the HeaderBar with pseudo-classes like :maximized, :fullscreen, :focused, :solid (when it does not have rounded corners). Then it would be possible to CSS style it.
8) Maybe integrate with platform preferences and provide a way to CSS style it when it's Light or Dark?

Nice cleanup on Window.java . UndecoratedMoveResizeHelper was not going to work on Linux anyways.

@andy-goryachev-oracle
Copy link
Contributor

I suggest we convert this PR to Draft and first discuss this in the mailing list.

There are so many issues with the proposal that need to be ironed first: CSS support, height limitation (what happens when the app or CSS places too large of the component?), user-defined color/accents/transparency on Windows, to name just a few.

This change also may add a significant maintenance burden to the platform, for what I feel is a very small payout.

What do you think?

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 21, 2024

I suggest we convert this PR to Draft and first discuss this in the mailing list.

This has already been discussed at various points in time, and was always received positively. The previous implementation is one of the most-upvoted feature proposals since OpenJFX moved to GitHub.

There are so many issues with the proposal that need to be ironed first: CSS support, height limitation (what happens when the app or CSS places too large of the component?), user-defined color/accents/transparency on Windows, to name just a few.

What about these things? I don't understand the question, but let me try to give some answers nontheless:

  1. CSS support: HeaderBar is a normal part of the scene graph, so it fully supports CSS styling.
  2. Height limitation: the height of HeaderBar is user-configurable, just like any layout container. It can be as large as you want.
  3. User-defined color/accents/transparency: Again, since HeaderBar is a part of the scene graph, all rules are the same.

This change also may add a significant maintenance burden to the platform, for what I feel is a very small payout.

Popular demand says otherwise.

@andy-goryachev-oracle
Copy link
Contributor

Popular demand is good, but I would like to hear from the tech leads and the maintainers.

@andy-goryachev-oracle
Copy link
Contributor

To clarify about height: do all the platforms support arbitrary height? What if size set by the app/CSS exceeds the height supported by the platform?

About platform preferences: will it support all the attributes of the window decorations provided by the platform?

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 21, 2024

To clarify about height: do all the platforms support arbitrary height? What if size set by the app/CSS exceeds the height supported by the platform?

Yes, all platforms support header bars of arbitrary height. You can make the header bar the size of the entire window, in which case everything is draggable.

About platform preferences: will it support all the attributes of the window decorations provided by the platform?

If by "attributes" you mean the default behavior of platform decorations, then the anwer is mostly. These are the behaviors that are available on every platform:

  1. A resize border.
  2. Minimize, maximize, and close buttons (this includes all states these buttons can be in: available/deactivated/disabled).
  3. Click-and-drag on the header bar.
  4. Double-click to maximize on the header bar.

On Windows, native window decorations also have the "system menu", which is this thing that appears when you click on the program icon:
Ml7Pf

EXTENDED windows have no system menu, because they have no program icon.
Edit: they now have a system menu that opens with a right click on the header bar.

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 21, 2024

@tsayao Thanks for the comments. I'll have a look at the bugs that you found.

  1. It's not possible to move the window if the cursor is over a control. Maybe you should just gtk_window_begin_move_drag when a drag is detected, not on click. That would be on WindowContextBase::process_mouse_motion;

I know that dragging on interactive controls is a thing on Linux, but I don't think that we should be doing that. JavaFX applications are multi-platform apps, which means that their behavior should be consistent across platforms. Stealing mouse interactions on interactive controls is not a thing on Windows and macOS, and this has the potential to cause problems for application developers.

If you want, you can declare any node in the header bar to be draggable on all platforms with HeaderBar.setDraggable(Node, boolean).

  1. I think rounded edges should be supported on the HeaderBar, since it's the default on gnome. For that to work, EXTENDED should be also transparent on Linux. It would loose the window drop shadow which can be added on the JavaFX side. gdk_window_set_shadow_width should be called with the drop shadow size, so the desktop will know the correct window bounds.

I'll have to look into that, but in general an EXTENDED window should work out of the box for all platforms, without platform-specific changes on the JavaFX side.

Added later: 6) It should have a "focused" state/pseudo class because on gnome (maybe others) the focused window has a different background on the HeaderBar which is darker. 7) Suggestion: Maybe make window states stylable on the HeaderBar with pseudo-classes like :maximized, :fullscreen, :focused, :solid (when it does not have rounded corners). Then it would be possible to CSS style it. 8) Maybe integrate with platform preferences and provide a way to CSS style it when it's Light or Dark?

While that sounds useful at first, I don't think it carries its own weight. Many platforms use different styling for windows that are focused vs. windows that are not. This not only includes the header bar, but many other parts of the user interface as well. I don't think that we should be adding what would essentially be ad-hoc pseudo-classes only to HeaderBar.

In addition to that, it is extremely easy for an application to do this by adding a listener to Stage.focused and then toggling pseudo-classes on all relevant controls.

We should definitely not do pseudo-classes for light vs. dark mode. The correct way to solve this problem is with media queries (prefers-color-scheme).

@mlbridge
Copy link

mlbridge bot commented Oct 22, 2024

Mailing list message from quizynox at gmail.com on openjfx-dev:

Hello,

Thank you so much for your effort! I'm really glad this hasn't been
forgotten. I wouldn't say it's just popular demand; it's an absolute must.
Here are a few thoughts, if you don't mind.

Every modern platform supports this feature: Electron, Tauri, Wails, Qt,
and even Swing via FlatLaf. If you use IntelliJ or VSCode, you can see it
for yourself. It's a popular design trend, which is why there's so much
demand.

Unfortunately, the current UNDECORATED stage implementation lacks two
important things: shadows and smooth resizing. Implementing shadows is
tricky but possible. However, achieving smooth resizing with Java code
alone is not feasible. There are several implementations on StackOverflow,
but they tend to be jerky and not very performant.

That's why the implementation should be handled on the native side, which
isn't something an app developer can do. We can only patiently wait for
this feature to be integrated into the core JavaFX platform.

It's indeed a complex feature. For that reason, I believe the
implementation shouldn't provide platform-dependent window controls. It
should be left to app developers to dodge theming issues. In Linux, for
example, it's common to install 3rd-party themes or decorations. You never
know what decorations the end user will use, and OS developers can change
themes over time too. It's just simpler to support this feature as a
separate library, which I'm sure will be developed.

??, 22 ???. 2024??. ? 03:24, Michael Strau? <mstrauss at openjdk.org>:

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20241022/367f812e/attachment-0001.htm>

@tsayao
Copy link
Collaborator

tsayao commented Oct 22, 2024

I think we should look at use cases and design a simple solution that can be extended.

Doing this with JavaFX as it is now will be hacky, since it would need to touch internals that are not exposed by default.

Intellij Idea
image

Nautilus
image

Chrome
image

Amberol (music player).
image

App Center (Ubuntu Software Store) - This one uses flutter
image

Some of them has no title at all. Some fuses the HeaderBar with the body, like nautilus. Even chrome, the tabs on the HeaderBar are not "isolated" from the body.

On modern Gnome, everything is client side decorated. Server side decoration is legacy. It's better for rendering since the window manager does not need to calculate, draw and sync with the window. It's probably less flickery.

Since JavaFx accounts the title as part of the window size, the current glass implementation is very hacky because it needs to request the decoration sizes from the window manager and then recalculate it. Having it on the client side is better, because no hacky solution is required.

@nlisker
Copy link
Collaborator

nlisker commented Oct 22, 2024

Adding some more examples.

Vivaldi browser actually allows you to switch between two modes:
image

which can be switched with
image

to
image

So in the first mode, the menu is compressed vertically into the Vivaldi button.

Here is Discord:
image

Very compact.

As for the payout of this feature, while personally I don't have a need for it, I will probably use it if it's not too much trouble. Regardless of my own opinion, this has been one of the most requested features, along with tray icon support, and it's available in the main "competition" frameworks. I would say that a very strong case will need to be made for this to not be added. The question that remains, as it often does, is if this is the right implementation. Hopefully the review process will figure that out.

@andy-goryachev-oracle
Copy link
Contributor

andy-goryachev-oracle commented Oct 22, 2024

Continuing on window attributes. I would like to know what is and is not supported by platform, by feature.

For example:

Windows

  • translucency
  • color gradient
  • system menu on right click, on the toolbar and on the app components
  • color accent on hover over window buttons
  • window borders
  • round corners
  • double click to maximize
  • drag to reposition the window
  • dark/light theme
  • accessibility: keyboard focus, accessible focus, narrator

Did I miss anything?

@andy-goryachev-oracle
Copy link
Contributor

For the HeaderBar, I would like to see the explanation of different layout options, including the cases when all the information does not fit the window width. Which parts are contracted? How is overflow handled?

@andy-goryachev-oracle
Copy link
Contributor

andy-goryachev-oracle commented Oct 22, 2024

May be I am late to the party, but I would suggest to discuss the JEP first. I would like to see the summary by platform by feature, I would like to see details of the layout, and the description if and how the proposed design responds to user preferences changes dynamically.

I would also like to see alternatives. Perhaps the app developers has all the tools already (for example, creating an overlay transparent scene on top of the platform title bar?), or maybe this functionality should be rather implemented in a library.

Lastly, there is a concern of adding a huge maintenance burden on the platform. Next time the major OS vendor changes the L&F or adds something to the window decorations, we are on the hook for supporting that.

I am not even qualified to access the impact of this feature in the Linux world. There are so many frameworks and opinions - how do you propose to handle that? Is it going to be supported on Wayland?

@tsayao
Copy link
Collaborator

tsayao commented Oct 22, 2024

Gtk does work on Mac and Windows, maybe we can see how it handles it's HeaderBar, for reference.

@andy-goryachev-oracle
Copy link
Contributor

Another aspect is whether this should be a conditional feature.
If not, how will it be supported on Android/iOS? RaspPI?

@mstr2
Copy link
Collaborator Author

mstr2 commented Oct 22, 2024

Continuing on window attributes. I would like to know what is and is not supported by platform, by feature.

For example:

Windows

  • translucency
  • color gradient
  • system menu on right click, on the toolbar and on the app components
  • color accent on hover over window buttons
  • window borders
  • round corners
  • double click to maximize
  • drag to reposition the window
  • dark/light theme
  • accessibility: keyboard focus, accessible focus, narrator

Did I miss anything?

I'll eagerly invite you to dissect this PR for its merits, its pros and cons. However, your questions lead me to conclude that you haven't really looked at what I'm proposing. Half of your questions are answered just by looking at the documentation for StageStyle.EXTENDED and HeaderBar. These are the two primary APIs that I've mentioned over and over again in this PR.

Let me try to explain StageStyle.EXTENDED in different terms:

  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 you can place scene graph nodes 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.

Since there is no non-client title bar, all questions regarding the appearance or accessibility of the HeaderBar (color gradient, dark/light theme, etc.) are left to the purview of application developers. HeaderBar 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. This has nothing to do with this feature proposal, and should be discussed on its own merits.

@haidar47x
Copy link

haidar47x commented Jan 30, 2025

I've been stalking this PR since October. Can't wait to use this feature in my project.

import java.util.Objects;
import java.util.Optional;

public final class HeaderButtonBehavior implements EventHandler<MouseEvent> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be better to call this class other than "behavior", "handler" perhaps? mouse handler?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit more than just a generic handler, it basically encapsulates the entire behavior of a window button (including setting its visibility and disabled states).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally use "Behavior" in the context of controls. Although not wrong, I also think another name might be better. Maybe "Controller"? Or "Manager"? Or ...

} else {
stage.setMaximized(!stage.isMaximized());
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to add a default case here, in case this enum evolves.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? This isn't some external enum that might evolve out from under this code, but is part of the same feature. If the enum evolves then the resulting compiler error is a good thing as it alerts whoever added the new enum that they didn't finish implementing it.

A default makes sense when the enum might evolve separately from the use of the enum.

@@ -154,7 +154,8 @@ private MenuBar createMenu() {
FX.menu(m, "_Window");
FX.item(m, orientation);
FX.separator(m);
FX.item(m, "Open Modal Window", this::openModalWindow);
FX.item(m, "Stage Tester", this::openStageTesterWindow);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this menu item be moved under "Tools"?

@kevinrushforth
Copy link
Member

It looks like this is shaping up nicely. I'll put this on my review queue.

One thing I wanted to raise is that there are a few new concepts here as well as a reasonably large API surface. This feature seems like a good candidate to release as a Preview Feature (*) to get more feedback on the API before finalizing it. That would mean reviving your Preview Feature JEP / Draft PR, which you had also mentioned as something to consider in your initial Description of this PR. Do you have any thoughts on this?

(*) - It can't be an incubator module (at least not without doing something unnatural like creating a subclass of Stage, and that might not even work), since the new API elements and implementation are necessarily in the javafx.graphics module.

@andy-goryachev-oracle
Copy link
Contributor

+1 for the preview idea

@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 4, 2025

I also think that this should be a preview feature.

@andy-goryachev-oracle
Copy link
Contributor

andy-goryachev-oracle commented Feb 4, 2025

I like the buttons in the content area of the test stage in the monkey tester!

Do you mind if I eventually move the changes to the "Stage" page?

Screenshot 2025-02-04 at 11 06 43

@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 4, 2025

Do you mind if I eventually move the changes to the "Stage" page?

Sure, do that. It might be prudent to wait for the API in this PR to stabilize, though.

@andy-goryachev-oracle
Copy link
Contributor

andy-goryachev-oracle commented Feb 4, 2025

I wonder if the header bar needs to get clipped somehow:

Screenshot 2025-02-04 at 12 50 23

(also weird padding around buttons in the left content area)

@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 13, 2025

I've simplified and fine-tuned the API a bit:

  1. HeaderBarBase is removed, and its functionality is rolled into HeaderBar. The rationale for the base class was that it allows developers to create new header bar implementations, but I now think that it is easier to just use the fully-featured HeaderBar class as a starting point, and drop any custom implementation into its center slot.
  2. HeaderBar.setPrefHeaderButtonHeight(Stage, double) is added, which allows applications to explicitly express a preference how tall the header buttons should be. This makes it possible to control the appearance of header buttons (for example, have the header buttons match the height of the title bar).
  3. Stage.initDefaultHeaderButtons() is removed. Instead, set HeaderBar.setPrefHeaderButtonHeight to zero in order to remove the system-provided header buttons.

@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 13, 2025

I wonder if the header bar needs to get clipped somehow:

I don't think that's possible, layout containers never visually clip their contents (i.e. prevent pixels from being drawn).

(also weird padding around buttons in the left content area)

Yes, it's a bit strange. I think these are bugs in SplitPane.

@tsayao
Copy link
Collaborator

tsayao commented Feb 14, 2025

Does the name EXTENDED accurately reflect its functionality? I have a feeling there's a better name for it.

@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 14, 2025

Does the name EXTENDED accurately reflect its functionality? I have a feeling there's a better name for it.

Here are some alternatives:

  • FULL_SIZE, like in AppKit's NSWindowStyleMaskFullSizeContentView
  • COMBINED, meaning that non-client and client areas are combined
  • UNIFIED already exists, but is non-functional; we could repurpose it

@andy-goryachev-oracle
Copy link
Contributor

layout containers never visually clip their contents

see PaginationSkin.install()

Although in Pane:94:

 * Pane does not clip its content by default, so it is possible that children's
 * bounds may extend outside its own bounds, either if children are positioned
 * at negative coordinates or the pane is resized smaller than its preferred size.</p>

BorderPane:120, FlowPane:146, ...

Can the application set the clip rectangle easily if needed?

@@ -711,6 +711,12 @@ public final boolean supportsUnifiedWindows() {
return _supportsUnifiedWindows();
}

protected abstract boolean _supportsExtendedWindows();
public final boolean supportsExtendedWindows() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline after L714?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, doesn't look nice, but I wanted to match the style of the surrounding methods.

* @param rightInset the size of the right inset
* @param minHeight the minimum height of the window buttons
*/
public record HeaderButtonMetrics(Dimension2D leftInset, Dimension2D rightInset, double minHeight) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make more sense to use Insets instead of Dimension2D? What if the app calls for asymmetrical padding?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is not API, so it doesn't matter here. However, it matters in HeaderBar.leftSystemInset/rightSystemInset, which are both of type Dimension2D. The system insets are not arbitrary rectangles, they are always aligned with the top-left and top-right edges of the window. As such, the only necessary information is their spatial extent, which is best represented as a Dimension2D.
I'm not sure what you mean by asymmetrical padding. Can you elaborate?

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word "inset" confused me. Could you use some other word here, if it is referring to a dimension? Like maybe explain where exactly these are used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we talking about the specification of the system-reserved insets in HeaderBar? Or do you suggest to link from HeaderButtonMetrics to HeaderBar?

    /**
     * Describes the size of the left system-reserved inset, which is an area reserved for the iconify, maximize,
     * and close window buttons. If there are no window buttons on the left side of the window, the returned area
     * is an empty {@code Dimension2D}.
     * <p>
     * Note that the left system inset refers to the left side of the window, independent of layout orientation.
     */
    private final ReadOnlyObjectWrapper<Dimension2D> leftSystemInset =

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a link would be nice, yes (for javafx developers) :-)

it could be just me - the word "inset" confused me ("insets" -> Insets, "inset" -> Dimension2D), but I guess it does describe the concept correctly.

public final class HeaderButtonOverlay extends Region {

private static final CssMetaData<HeaderButtonOverlay, Number> BUTTON_DEFAULT_HEIGHT_METADATA =
new CssMetaData<>("-fx-button-default-height", StyleConverter.getSizeConverter()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new styleables need to be documented in cssref.html as a part of this PR, right?
(The javadoc for this class will not be visible in the API specification).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HeaderButtonOverlay is not API, it's an internal implementation detail. The javadoc is just informational for future JavaFX developers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me clarify:
I think the css properties must be listed in cssref.html as the component can be styled via CSS. I don't see the cssref.html file modified in this PR.

Copy link
Collaborator Author

@mstr2 mstr2 Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cssref.html is the normative reference for the JavaFX CSS API. HeaderButtonOverlay is not API, it can't be styled by developers, it's completely inaccessible, and therefore must not be specified anywhere.

It's even unspecified whether HeaderButtonOverlay is used at all. For example, on macOS it's never used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cssref.html IS a part of normative reference for JavaFX API.

And my comment is not limited to a single class, it applies to every new CSS property added here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we're going in circles here. You want me to specify something which I've repeatedly explained is not API. I can't specify something which is not API.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying not a single styleable property can be set with an application CSS, only internally?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit like using deep reflection to access internals. It's technically possible, but explicitly unsupported. Much in the same way, any attempt to style header buttons can stop working at any time (because we're free to change the implementation in any way without prior notice). If we were to even mention the internal structure of HeaderButtonOverlay, we'd sign up for all of the compatibility guarantees that users expect from public API.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, thanks!
maybe add a not pointing to the css where the styles are being defined?

};

private static final List<CssMetaData<?, ?>> METADATA =
Stream.concat(getClassCssMetaData().stream(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RichUtils.combine() L419 avoids creating intermediary objects.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to wait until we have something like this in an accessible place...

* This property corresponds to the {@code -fx-allow-rtl} CSS property.
*/
private final StyleableBooleanProperty allowRtl =
new SimpleStyleableBooleanProperty(ALLOW_RTL_METADATA, this, "allowRtl", true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the rationale for this property?
Instead, I think, it should look at the Node::getEffectiveNodeOrientation() which either inherits the value from the parent or allows the app to override for a specific component (in other words, we already have this functionality for free).

Or am I missing something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HeaderButtonOverlay inherits its node orientation from the scene, which is under the control of the application developer. On the other hand, allowRtl is under the control of JavaFX developers (that's us), specifying whether the selected header button implementation supports RTL layouts at all. Currently, all header button implementations do.

The property is there to make all aspects of HeaderButtonOverlay styleable, so that future JavaFX developers can add other header button styles that may have fixed locations (i.e. don't move from one side to the other in RTL mode).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it have a different name then? fixedPosition or something like that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed allowRtl completely. Let's entertain this problem when (and if) we decide to have system-provided header buttons that don't adjust for RTL mode.

@@ -285,6 +296,54 @@ protected Window(Window owner, Screen screen, int styleMask) {
}
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the majority of windows is expected to be DECORATED, would it make more sense to move these properties into a separate container akin to Node.getMiscProperties() ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but the downside is that everything gets more bloated for little benefit. Right now, these properties are accessed in derived classes without any checks, since they are final and non-null. In this case, I think ease of use is more important.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ease of use for whom?

bloated code: a few bytes in extra methods
bloated data: thousand of bytes added per instance

Since we are adding properties to Window, it's probably ok because the number of instances is relatively low (a real app won't have hundreds of windows).
Still, I would like to recommend investigating the alternative: the memory is not an unlimited resource, and in my experience, it's worth thinking about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ease of use for JavaFX developers. Easily maintainable code is one of the most important aspects, miles ahead of chasing bytes and chasing nanoseconds. There's no tangible value in spending any considerable amount of time on thinking how to shave 150 bytes off of a window.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a) we are constantly adding runtime bytes, we have to be aware of the cost
b) I think we should rather focus on the application developers, and they do not see the complexity behind the API. And moving a rarely used stuff into a rarely initialized object should not be a problem for any JavaFX developer, or am I mistaken?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the problem: we're also constantly adding code that needs to be written, reviewed, tested, and maintained. We're talking about a small amount of code here in this particular example, but then again, what particular straw broke the camel's back?

I think we have to be aware of runtime overhead where it matters most: in hot loops, or where something is instantiated tens of thousands of times. In all other cases, focusing on bytes and nanoseconds can actually be detrimental in the long run. It makes code inherently harder to reason about, and when many such "optimizations" are taken together, we can end up with code that you can't change any more for fear of breaking the myriads of code paths.

}

.close-button > .glyph {
-fx-shape: "m 8.1464844,8.1464844 a 0.5,0.5 0 0 0 0,0.7070312 L 11.292969,12 8.1464844,15.146484 a 0.5,0.5 0 0 0 0,0.707032 0.5,0.5 0 0 0 0.7070312,0 L 12,12.707031 l 3.146484,3.146485 a 0.5,0.5 0 0 0 0.707032,0 0.5,0.5 0 0 0 0,-0.707032 L 12.707031,12 15.853516,8.8535156 a 0.5,0.5 0 0 0 0,-0.7070312 0.5,0.5 0 0 0 -0.707032,0 L 12,11.292969 8.8535156,8.1464844 a 0.5,0.5 0 0 0 -0.7070312,0 z";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any response on this?

}

.close-button > .glyph {
-fx-shape: "m 8.1464844,8.1464844 a 0.5,0.5 0 0 0 0,0.7070312 L 11.292969,12 8.1464844,15.146484 a 0.5,0.5 0 0 0 0,0.707032 0.5,0.5 0 0 0 0.7070312,0 L 12,12.707031 l 3.146484,3.146485 a 0.5,0.5 0 0 0 0.707032,0 0.5,0.5 0 0 0 0,-0.707032 L 12.707031,12 15.853516,8.8535156 a 0.5,0.5 0 0 0 0,-0.7070312 0.5,0.5 0 0 0 -0.707032,0 L 12,11.292969 8.8535156,8.1464844 a 0.5,0.5 0 0 0 -0.7070312,0 z";
-fx-shape: "m 8.1465,8.1465 a 0.5,0.5 0 0 0 0,0.707 L 11.293,12 8.1465,15.1465 a 0.5,0.5 0 0 0 0,0.707 0.5,0.5 0 0 0 0.707,0 L 12,12.707 l 3.1465,3.1465 a 0.5,0.5 0 0 0 0.707,0 0.5,0.5 0 0 0 0,-0.707 L 12.707,12 15.8535,8.8535 a 0.5,0.5 0 0 0 0,-0.707 0.5,0.5 0 0 0 -0.707,0 L 12,11.293 8.8535,8.1465 a 0.5,0.5 0 0 0 -0.707,0 z";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I like the idea of using chatGPT.
The changes look inconsistent - some are rounded to 3 places after the decimal, some to 4 (3 should be sufficient, I think even 2 will suffice)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no original work here that wasn't there before. I've checked the rounding, it's correct and consistent. There are three decimal places only when the fourth would be a zero. In any case, tweaking this further is a side-quest with no value.

* @param center the center node
* @param trailing the trailing node
*/
public HeaderBar(Node leading, Node center, Node trailing) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you refer to "leading" and "trailing", but the system insets are "left" and "right", why is that?

When the platform goes RTL, don't the buttons flip as well? Should the system insets be referred to as leading/trailing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On most platforms, the header buttons will switch sides when using RTL orientation, but it doesn't necessarily have to do that. For example, if you switch the layout orientation after the window has been shown, the header buttons will stay where they are.

I'm using "leading" and "trailing" when I'm referring to JavaFX layout, which can change at any time. On the other hand, I'm using "left" and "right" to refer to the physical placement of window buttons, which may be different from JavaFX layout.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
csr Need approved CSR to integrate pull request rfr Ready for review
Development

Successfully merging this pull request may close these issues.

9 participants