Skip to content

8091629: [Mac] Support the automatic hiding of the "java" menu bar item when using the system menu bar #1737

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

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,49 @@ public final boolean isUseSystemMenuBar() {
return useSystemMenuBar == null ? false : useSystemMenuBar.getValue();
}

/**
* Remove any default menus that are normally added to the system
* menu bar. Only effective when the system menu bar is in use.
*
* @return the use default menus property
* @since JavaFX 24
*/
public final BooleanProperty useDefaultMenusProperty() {
if (useDefaultMenus == null) {
useDefaultMenus = new StyleableBooleanProperty(true) {

@Override
public CssMetaData<MenuBar,Boolean> getCssMetaData() {
return StyleableProperties.USE_DEFAULT_MENUS;
}

@Override
public Object getBean() {
return MenuBar.this;
}

@Override
public String getName() {
return "useDefaultMenus";
}

@Override
public void bind(final ObservableValue<? extends Boolean> rawObservable) {
throw new RuntimeException(BIND_MSG);
}

};
}
return useDefaultMenus;
}

private BooleanProperty useDefaultMenus;
public final void setUseDefaultMenus(boolean value) {
useDefaultMenusProperty().setValue(value);
}
public final boolean isUseDefaultMenus() {
return useDefaultMenus == null ? true : useDefaultMenus.getValue();
}

/* *************************************************************************
* *
Expand Down Expand Up @@ -223,6 +266,19 @@ private static class StyleableProperties {
}
};

private static final CssMetaData<MenuBar, Boolean> USE_DEFAULT_MENUS =
new CssMetaData<>("-fx-use-default-menus",
BooleanConverter.getInstance(),
true) {
@Override public boolean isSettable(MenuBar n) {
return n.useDefaultMenus == null || !n.useDefaultMenus.isBound();
}

@Override public StyleableProperty<Boolean> getStyleableProperty(MenuBar n) {
return (StyleableProperty<Boolean>)n.useDefaultMenusProperty();
}
};

private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
static {
final List<CssMetaData<? extends Styleable, ?>> styleables =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ public MenuBarSkin(final MenuBar control) {
lh.addInvalidationListener(control.useSystemMenuBarProperty(), (v) -> {
rebuildUI();
});
lh.addInvalidationListener(control.useDefaultMenusProperty(), (v) -> {
rebuildUI();
});
}

// When the mouse leaves the menu, the last hovered item should lose
Expand Down Expand Up @@ -522,6 +525,9 @@ private static void initSystemMenuBar() {
stages.addListener((ListChangeListener<Window>) c -> {
while (c.next()) {
for (Window stage : c.getRemoved()) {
if (stage == currentMenuBarStage) {
setSystemMenu(null);
}
stage.focusedProperty().removeListener(focusedStageListener);
}
for (Window stage : c.getAddedSubList()) {
Expand Down Expand Up @@ -820,6 +826,9 @@ private void rebuildUI() {
cleanUpListeners();

if (Toolkit.getToolkit().getSystemMenu().isSupported()) {
boolean useDefaultMenus = !getSkinnable().isUseSystemMenuBar() || getSkinnable().isUseDefaultMenus();
Toolkit.getToolkit().getSystemMenu().setUseDefaultMenus(useDefaultMenus);

final Scene scene = getSkinnable().getScene();
if (scene != null) {
// JDK-8094110 - make sure system menu is updated when this MenuBar's scene changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ public void installDefaultMenus(MenuBar menubar) {
// To override in subclasses
}

public void removeDefaultMenus(MenuBar menubar) {
checkEventThread();
// To override in subclasses
}

public EventHandler getEventHandler() {
//checkEventThread(); // Glass (Mac)
// When an app is closing, Mac calls notify- Will/DidHide, Will/DidResignActive
Expand Down Expand Up @@ -813,6 +818,21 @@ public Map<String, Class<?>> getPlatformKeys() {
*/
public void checkPlatformPreferencesSupport() {}

/**
* Hides the current application. Only implemented on Mac.
*/
public void hideApplication() {}

/**
* Hides all other applications. Only implemented on Mac.
*/
public void hideOtherApplications() {}

/**
* Undoes the effects of hideOtherApplications. Only implemented on Mac.
*/
public void showAllApplications() {}

private static native void _overrideNativeWindowHandle(Class lwFrameWrapperClass,
Object frame,
long handle, Runnable closeWindow);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ private void setEventThread() {
native private void _hideOtherApplications();
native private void _unhideAllApplications();

@Override public void hideApplication() {
_hide();
}

@Override public void hideOtherApplications() {
_hideOtherApplications();
}

@Override public void showAllApplications() {
_unhideAllApplications();
}

public void installAppleMenu(MenuBar menubar) {
this.appleMenu = createMenu("Apple");

Expand Down Expand Up @@ -263,7 +275,7 @@ public void installAppleMenu(MenuBar menubar) {
}, 'q', KeyEvent.MODIFIER_COMMAND);
this.appleMenu.add(quitMenu);

menubar.add(this.appleMenu);
menubar.insert(this.appleMenu, 0);
}

public Menu getAppleMenu() {
Expand All @@ -274,6 +286,12 @@ public Menu getAppleMenu() {
installAppleMenu(menubar);
}

@Override public void removeDefaultMenus(MenuBar menubar) {
if (appleMenu != null) {
menubar.remove(appleMenu);
appleMenu = null;
}
}

// FACTORY METHODS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,4 +977,24 @@ private static void checkHighContrastThemeChanged(Map<String, Object> preference
}
}

public static void hideApplication() {
var application = com.sun.glass.ui.Application.GetApplication();
if (application != null) {
application.hideApplication();
}
}

public static void hideOtherApplications() {
var application = com.sun.glass.ui.Application.GetApplication();
if (application != null) {
application.hideOtherApplications();
}
}

public static void showAllApplications() {
var application = com.sun.glass.ui.Application.GetApplication();
if (application != null) {
application.showAllApplications();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public interface TKSystemMenu {
*/
public boolean isSupported();

public void setUseDefaultMenus(boolean use);
public void setMenus(List<MenuBase> menus);

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class GlassSystemMenu implements TKSystemMenu {

private List<MenuBase> systemMenus = null;
private MenuBar glassSystemMenuBar = null;
private boolean useDefaultMenus = true;
private final Map<Menu, ListChangeListener<MenuItemBase>> menuListeners = new HashMap<>();
private final Map<ListChangeListener<MenuItemBase>, ObservableList<MenuItemBase>> listenerItems = new HashMap<>();
private BooleanProperty active;
Expand All @@ -75,8 +76,9 @@ protected void createMenuBar() {
if (glassSystemMenuBar == null) {
Application app = Application.GetApplication();
glassSystemMenuBar = app.createMenuBar();
app.installDefaultMenus(glassSystemMenuBar);

if (useDefaultMenus) {
app.installDefaultMenus(glassSystemMenuBar);
}
if (systemMenus != null) {
setMenus(systemMenus);
}
Expand All @@ -91,6 +93,19 @@ protected MenuBar getMenuBar() {
return Application.GetApplication().supportsSystemMenu();
}

@Override public void setUseDefaultMenus(boolean use) {
if (use != useDefaultMenus) {
if (glassSystemMenuBar != null) {
if (useDefaultMenus) {
Application.GetApplication().removeDefaultMenus(glassSystemMenuBar);
} else {
Application.GetApplication().installDefaultMenus(glassSystemMenuBar);
}
}
useDefaultMenus = use;
}
}

@Override public void setMenus(List<MenuBase> menus) {
if (active != null) {
active.set(false);
Expand All @@ -99,16 +114,25 @@ protected MenuBar getMenuBar() {
systemMenus = menus;
if (glassSystemMenuBar != null) {

/*
* If we added default menus to ensure the menu bar did
* not become empty remove them now.
*/
if (!useDefaultMenus) {
Application.GetApplication().removeDefaultMenus(glassSystemMenuBar);
}

/*
* Remove existing menus
*/
List<Menu> existingMenus = glassSystemMenuBar.getMenus();
int existingSize = existingMenus.size();

/*
* Leave the Apple menu in place
* Leave the Apple menu in place, if using
*/
for (int index = existingSize - 1; index >= 1; index--) {
int limit = (useDefaultMenus ? 1 : 0);
for (int index = existingSize - 1; index >= limit; index--) {
Menu menu = existingMenus.get(index);
clearMenu(menu);
glassSystemMenuBar.remove(index);
Expand All @@ -117,6 +141,13 @@ protected MenuBar getMenuBar() {
for (MenuBase menu : menus) {
addMenu(null, menu);
}

/*
* Do not let the menu bar become empty.
*/
if (existingMenus.size() == 0) {
Application.GetApplication().installDefaultMenus(glassSystemMenuBar);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
Copy link

@OguzOzkeroglu OguzOzkeroglu Mar 26, 2025

Choose a reason for hiding this comment

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

Suggested change
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.

* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package javafx.application;

import com.sun.javafx.application.PlatformImpl;

/**
* This class provides services for an Application. This includes
* methods to show and hide other applications.
*
* @since JavaFX 24
*/
public final class ApplicationServices {

// To prevent instantiation
private ApplicationServices() {
}

/**
* Hide the application.
*/
public static void hideApplication() {
PlatformImpl.hideApplication();
}

/**
* Hide applications other than the current one.
*/
public static void hideOtherApplications() {
PlatformImpl.hideOtherApplications();
}

/**
* Show all applications.
*/
public static void showAllApplications() {
PlatformImpl.showAllApplications();
}

}
13 changes: 0 additions & 13 deletions modules/javafx.graphics/src/main/native-glass/mac/GlassMenu.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,6 @@
static jfieldID jPixelsScaleXField = 0;
static jfieldID jPixelsScaleYField = 0;

@interface NSMenuItem (SPI)

// Apple's SPI
- setAppleMenu:(NSMenuItem*)item;

@end

@implementation GlassMenubar

- (id)init
Expand Down Expand Up @@ -342,12 +335,6 @@ - (void)_setPixels:(jobject)pixels

[glassmenu->menu setAutoenablesItems:YES];

if ([[glassmenu->item title] compare:@"Apple"] == NSOrderedSame)
{
LOG("calling setAppleMenu");
[NSApp performSelector:@selector(setAppleMenu:) withObject:glassmenu->item];
}

[[NSApp mainMenu] update];
}
GLASS_POOL_EXIT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,10 @@ public boolean isSupported() {
// return (os != null && os.startsWith("Mac"));
}

@Override
public void setUseDefaultMenus(boolean use) {
}

@Override
public void setMenus(List<MenuBase> menus) {
this.menus = menus;
Expand Down
Loading