1 /*
  2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package test.javafx.scene.control.skin;
 27 
 28 import static org.junit.Assert.assertEquals;
 29 import static org.junit.Assert.assertTrue;
 30 
 31 import com.sun.javafx.menu.MenuBase;
 32 import com.sun.javafx.stage.WindowHelper;
 33 import test.com.sun.javafx.pgstub.StubToolkit;
 34 import com.sun.javafx.tk.Toolkit;
 35 import javafx.beans.value.ObservableValue;
 36 import javafx.geometry.Insets;
 37 import javafx.scene.Group;
 38 import javafx.scene.Scene;
 39 import javafx.scene.control.Menu;
 40 import javafx.scene.control.MenuBar;
 41 import javafx.scene.control.MenuItem;
 42 import javafx.stage.Stage;
 43 
 44 import java.util.List;
 45 import javafx.scene.control.skin.MenuBarSkin;
 46 
 47 import org.junit.Before;
 48 import org.junit.BeforeClass;
 49 import org.junit.Test;
 50 
 51 /**
 52  * This fails with IllegalStateException because of the toolkit's check for the FX application thread
 53  */
 54 public class MenuBarSkinTest {
 55     private MenuBar menubar;
 56     private MenuBarSkinMock skin;
 57     private static Toolkit tk;
 58     private Scene scene;
 59     private Stage stage;
 60 
 61 
 62     @BeforeClass public static void initToolKit() {
 63         tk = Toolkit.getToolkit();
 64     }
 65 
 66     @Before public void setup() {
 67         menubar = new MenuBar();
 68         menubar.setUseSystemMenuBar(false);
 69         menubar.getMenus().addAll(new Menu("File"), new Menu("Edit"));
 70 
 71         // Pending RT-37118, MenuBar needs to be in a scene in order to set the skin.
 72         scene = new Scene(new Group(menubar));
 73         skin = new MenuBarSkinMock(menubar);
 74         menubar.setSkin(skin);
 75 
 76         // Set some padding so that any places where padding was being
 77         // computed but wasn't expected will be caught.
 78         menubar.setPadding(new Insets(10, 10, 10, 10));
 79 
 80         stage = new Stage();
 81 
 82         // MenuBar needs to have a stage in order for system menus to work
 83         stage.setScene(scene);
 84 
 85         // Stage has to be focused in order for system menus to work
 86         WindowHelper.setFocused(stage, true);
 87     }
 88 
 89     @Test public void maxHeightTracksPreferred() {
 90         menubar.setPrefHeight(100);
 91         assertEquals(100, menubar.maxHeight(-1), 0);
 92     }
 93 
 94     @Test public void testDispose() {
 95 
 96         if (tk.getSystemMenu().isSupported()) {
 97             // setting system menu bar true should create a sceneProperty listener for RT-36554
 98             menubar.setUseSystemMenuBar(true);
 99             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
100         }
101 
102         // This will cause the dispose method to be called.
103         menubar.setSkin(null);
104 
105         if (tk.getSystemMenu().isSupported()) {
106 
107             // dispose should clean up the system menu.
108             assertEquals(0, getSystemMenus().size());
109 
110         }
111 
112     }
113 
114     @Test public void testSetUseSystemMenuBar() {
115         if (tk.getSystemMenu().isSupported()) {
116             menubar.setUseSystemMenuBar(true);
117             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
118 
119             menubar.setUseSystemMenuBar(false);
120             assertEquals(0, getSystemMenus().size());
121 
122             menubar.setUseSystemMenuBar(true);
123             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
124         }
125     }
126 
127     @Test public void testSystemMenuBarUpdatesWhenMenusChange() {
128 
129         if (tk.getSystemMenu().isSupported()) {
130             menubar.setUseSystemMenuBar(true);
131             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
132 
133             menubar.getMenus().add(new Menu("testSystemMenuBarUpdatesWhenMenusChange"));
134             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
135         }
136     }
137 
138     @Test public void testRT_36554() {
139 
140         if (tk.getSystemMenu().isSupported()) {
141 
142             menubar.setUseSystemMenuBar(true);
143             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
144 
145             // removing the menubar from the scene should remove the system menus.
146             ((Group)scene.getRoot()).getChildren().remove(menubar);
147             assertEquals(0, getSystemMenus().size());
148 
149             // adding the menubar from the scene should add back the system menus.
150             ((Group)scene.getRoot()).getChildren().add(menubar);
151             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
152 
153             // remove, then set useSystemMenuBar to false. Upon re-adding,
154             // there should still be no system menu.
155             ((Group)scene.getRoot()).getChildren().remove(menubar);
156             assertEquals(0, getSystemMenus().size());
157 
158             menubar.setUseSystemMenuBar(false);
159             ((Group)scene.getRoot()).getChildren().add(menubar);
160             assertEquals(0, getSystemMenus().size());
161 
162             // setting useSystemMenuBar to true again, should add back the system menus.
163             menubar.setUseSystemMenuBar(true);
164             assertEquals(menubar.getMenus().size(), getSystemMenus().size());
165         }
166     }
167 
168     @Test public void testModifyingNonSystemMenuBar() {
169         if (tk.getSystemMenu().isSupported()) {
170             // Set system menubar to true
171             menubar.setUseSystemMenuBar(true);
172 
173             // Create a secondary menubar that is not
174             // a system menubar
175             MenuBar secondaryMenuBar = new MenuBar(
176                     new Menu("Menu 1", null, new MenuItem("Item 1")),
177                     new Menu("Menu 2", null, new MenuItem("Item 2")));
178             secondaryMenuBar.setSkin(new MenuBarSkin(secondaryMenuBar));
179 
180             // Add the secondary menubar to the scene
181             ((Group)scene.getRoot()).getChildren().add(secondaryMenuBar);
182 
183             // Verify that the menubar is the system menubar
184             assertTrue(menubar.isUseSystemMenuBar());
185 
186             // Remove a menu from the secondary menubar
187             // to trigger a rebuild of its UI and a call
188             // to the sceneProperty listener
189             secondaryMenuBar.getMenus().remove(1);
190 
191             // Verify that this has not affected whether the
192             // original menubar is the system menubar
193             assertTrue(menubar.isUseSystemMenuBar());
194         }
195     }
196 
197     public static final class MenuBarSkinMock extends MenuBarSkin {
198         boolean propertyChanged = false;
199         int propertyChangeCount = 0;
200         public MenuBarSkinMock(MenuBar menubar) {
201             super(menubar);
202         }
203 
204         public void addWatchedProperty(ObservableValue<?> p) {
205             p.addListener(o -> {
206                 propertyChanged = true;
207                 propertyChangeCount++;
208             });
209         }
210     }
211 
212     private List<MenuBase> getSystemMenus() {
213         return ((StubToolkit.StubSystemMenu)tk.getSystemMenu()).getMenus();
214     }
215 
216 }