-
Notifications
You must be signed in to change notification settings - Fork 496
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
base: master
Are you sure you want to change the base?
Conversation
👋 Welcome back mstrauss! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
/reviewers 2 reviewers |
@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. |
Hey, I'm glad to see this PR, but do you have any ideas for continuing to provide |
Very nice. I'll test on Linux and report back. |
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. |
A few points observed on Linux:
Added later: Nice cleanup on |
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? |
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.
What about these things? I don't understand the question, but let me try to give some answers nontheless:
Popular demand says otherwise. |
Popular demand is good, but I would like to hear from the tech leads and the maintainers. |
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? |
@tsayao Thanks for the comments. I'll have a look at the bugs that you found.
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
I'll have to look into that, but in general an
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 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). |
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 Every modern platform supports this feature: Electron, Tauri, Wails, Qt, Unfortunately, the current UNDECORATED stage implementation lacks two That's why the implementation should be handled on the native side, which It's indeed a complex feature. For that reason, I believe the ??, 22 ???. 2024??. ? 03:24, Michael Strau? <mstrauss at openjdk.org>: -------------- next part -------------- |
Continuing on window attributes. I would like to know what is and is not supported by platform, by feature. For example: Windows
Did I miss anything? |
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? |
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? |
Gtk does work on Mac and Windows, maybe we can see how it handles it's HeaderBar, for reference. |
Another aspect is whether this should be a conditional feature. |
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 Let me try to explain
Since there is no non-client title bar, all questions regarding the appearance or accessibility of the |
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> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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()); | ||
} | ||
}); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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"?
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 |
+1 for the preview idea |
I also think that this should be a preview feature. |
Sure, do that. It might be prudent to wait for the API in this PR to stabilize, though. |
modules/javafx.graphics/src/main/native-glass/gtk/glass_window.h
Outdated
Show resolved
Hide resolved
tests/manual/monkey/src/com/oracle/tools/fx/monkey/tools/StageTesterWindow.java
Outdated
Show resolved
Hide resolved
I've simplified and fine-tuned the API a bit:
|
I don't think that's possible, layout containers never visually clip their contents (i.e. prevent pixels from being drawn).
Yes, it's a bit strange. I think these are bugs in |
Does the name |
Here are some alternatives:
|
see Although in Pane:94:
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() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing newline after L714?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 =
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { | |||
} | |||
} | |||
|
|||
/** |
There was a problem hiding this comment.
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()
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
modules/javafx.graphics/src/main/java/javafx/application/ConditionalFeature.java
Show resolved
Hide resolved
modules/javafx.graphics/src/main/java/javafx/scene/layout/HeaderBar.java
Show resolved
Hide resolved
} | ||
|
||
.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"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any response on this?
modules/javafx.graphics/src/main/resources/com/sun/glass/ui/win/WindowDecoration.css
Outdated
Show resolved
Hide resolved
} | ||
|
||
.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"; |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Implementation of
EXTENDED
andEXTENDED_UTILITY
stage style.Progress
Issue
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