| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, |
| Version 2.0 (the "License"); you may not use this file except in |
| compliance with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <document id="menu-bars"> |
| <properties> |
| <title>Menu Bars</title> |
| </properties> |
| |
| <body> |
| <p> |
| Menu bars are generally used to provide convenient access to major application |
| features. They act as a repository for top-level hierarchical menus, keeping the menus |
| out of sight until they are needed. |
| </p> |
| |
| <p> |
| Like all other components, menu bars can actually be placed anywhere in an |
| application's user interface. However, they are most often located at the top of an |
| application's main window. Pivot provides framework-level support for simplifying the |
| task of defining a menu bar positioned in this way. The <tt>Frame</tt> class defines |
| a "menuBar" property that is handled specially by the application using the |
| <tt>configureMenuBar()</tt> and <tt>cleanupMenuBar()</tt> methods of the |
| <tt>MenuHandler</tt> interface. These methods are called by the framework as the focus |
| changes within a window, to allow the application to customize the contents of the menu |
| bar based on the currently focused component. |
| </p> |
| |
| <p> |
| Note that for a keystroke to be processed (for example assigned to a Menu Item), |
| a component must have the focus to receive the key event and propagate it up the component hierarchy. |
| So, make sure that the window's content contains a focusable component, like a TextInput or PushButton. |
| </p> |
| |
| <p> |
| The example application below shows a menu bar containing two common top-level menu |
| items: "File" and "Edit" (note that the applet is signed since it makes use of the |
| <tt>FileBrowserSheet</tt> component, which requires access to the local file system): |
| </p> |
| |
| <application class="org.apache.pivot.wtk.ScriptApplication" |
| width="640" height="480"> |
| <libraries signed="true"> |
| <library>core</library> |
| <library>wtk</library> |
| <library>wtk-terra</library> |
| <library>tutorials</library> |
| </libraries> |
| <startup-properties> |
| <src>/org/apache/pivot/tutorials/menus/menu_bars.bxml</src> |
| </startup-properties> |
| </application> |
| |
| <p> |
| Each sub-menu item is associated with an <tt>Action</tt> that is executed when the |
| item is selected. For example, the action attached to the "File > Open" menu item |
| simulates opening a document by showing a file browser sheet and adding a new tab to |
| the application's tab pane. Each component in the "document" has a menu handler |
| attached to it that configures the menu contents as appropriate for the current |
| selection. When a text input component has the focus, the "Paste" menu item is enabled. |
| If text is selected in the text input, the "Cut" and "Copy" menu items are also |
| enabled. |
| </p> |
| |
| <p> |
| The BXML source for this example is shown below. It creates the initial menu structure |
| as well as the tab pane that will host the simulated documents. It also defines a set |
| of "action mappings" in the root frame's "actionMappings" sequence. Action mappings |
| associate keystrokes with actions; when a keystroke matching an action in the sequence |
| is processed by the window, the action is invoked. Action mappings are often called |
| "keyboard shortcuts". |
| </p> |
| |
| <p> |
| Note that the actions in this example are associated with the "CMD" key. This is a |
| Pivot-specific, platform-independent modifier. It maps to the Control key (CTRL) |
| on Windows and Linux and the Command key (META) on Mac OS X: |
| </p> |
| |
| <source type="xml" location="org/apache/pivot/tutorials/menus/menu_bars.bxml"> |
| <![CDATA[ |
| <menus:MenuBars title="Menu Bars" maximized="true" |
| styles="{padding:{top:0, left:4, bottom:4, right:4}, showWindowControls:false}" |
| xmlns:bxml="http://pivot.apache.org/bxml" |
| xmlns:content="org.apache.pivot.wtk.content" |
| xmlns:menus="org.apache.pivot.tutorials.menus" |
| xmlns="org.apache.pivot.wtk"> |
| <bxml:define> |
| <FileBrowserSheet bxml:id="fileBrowserSheet"/> |
| </bxml:define> |
| |
| <actionMappings> |
| <Window.ActionMapping action="fileNew" keyStroke="CMD-N"/> |
| <Window.ActionMapping action="fileOpen" keyStroke="CMD-O"/> |
| </actionMappings> |
| |
| <menuBar> |
| <MenuBar> |
| <MenuBar.Item buttonData="File"> |
| <Menu> |
| <Menu.Section> |
| <Menu.Item action="fileNew"> |
| <buttonData> |
| <content:MenuItemData text="New" keyboardShortcut="CMD-N"/> |
| </buttonData> |
| </Menu.Item> |
| |
| <Menu.Item action="fileOpen"> |
| <buttonData> |
| <content:MenuItemData text="Open" keyboardShortcut="CMD-O"/> |
| </buttonData> |
| </Menu.Item> |
| </Menu.Section> |
| </Menu> |
| </MenuBar.Item> |
| |
| <MenuBar.Item buttonData="Edit"> |
| <Menu> |
| <Menu.Section> |
| <Menu.Item action="cut"> |
| <buttonData> |
| <content:MenuItemData text="Cut" keyboardShortcut="CMD-X"/> |
| </buttonData> |
| </Menu.Item> |
| <Menu.Item action="copy"> |
| <buttonData> |
| <content:MenuItemData text="Copy" keyboardShortcut="CMD-C"/> |
| </buttonData> |
| </Menu.Item> |
| <Menu.Item action="paste"> |
| <buttonData> |
| <content:MenuItemData text="Paste" keyboardShortcut="CMD-V"/> |
| </buttonData> |
| </Menu.Item> |
| </Menu.Section> |
| </Menu> |
| </MenuBar.Item> |
| </MenuBar> |
| </menuBar> |
| |
| <Border styles="{backgroundColor:null, padding:2}"> |
| <TabPane bxml:id="tabPane"/> |
| </Border> |
| </menus:MenuBars> |
| ]]> |
| </source> |
| |
| <p> |
| The Java source for the example is shown below. In the constructor, the application's |
| actions are created and added to the global action dictionary. Note that, since the |
| BXML file refers to the actions by ID, it is essential that the actions be available |
| before the BXML is read. |
| </p> |
| |
| <source type="java" location="org/apache/pivot/tutorials/menus/MenuBars.java"> |
| <![CDATA[ |
| package org.apache.pivot.tutorials.menus; |
| |
| import java.io.IOException; |
| import java.net.URL; |
| |
| import org.apache.pivot.beans.BXML; |
| import org.apache.pivot.beans.BXMLSerializer; |
| import org.apache.pivot.beans.Bindable; |
| import org.apache.pivot.collections.Map; |
| import org.apache.pivot.serialization.SerializationException; |
| import org.apache.pivot.util.Resources; |
| import org.apache.pivot.wtk.Action; |
| import org.apache.pivot.wtk.Border; |
| import org.apache.pivot.wtk.Component; |
| import org.apache.pivot.wtk.FileBrowserSheet; |
| import org.apache.pivot.wtk.Frame; |
| import org.apache.pivot.wtk.MenuBar; |
| import org.apache.pivot.wtk.MenuHandler; |
| import org.apache.pivot.wtk.TabPane; |
| import org.apache.pivot.wtk.TextInput; |
| import org.apache.pivot.wtk.TextInputSelectionListener; |
| import org.apache.pivot.wtk.TextInputContentListener; |
| |
| public class MenuBars extends Frame implements Bindable { |
| @BXML private FileBrowserSheet fileBrowserSheet; |
| @BXML private TabPane tabPane = null; |
| |
| private MenuHandler menuHandler = new MenuHandler.Adapter() { |
| TextInputContentListener textInputTextListener = new TextInputContentListener.Adapter() { |
| @Override |
| public void textChanged(TextInput textInput) { |
| updateActionState(textInput); |
| } |
| }; |
| |
| TextInputSelectionListener textInputSelectionListener = new TextInputSelectionListener() { |
| @Override |
| public void selectionChanged(TextInput textInput, int previousSelectionStart, |
| int previousSelectionLength) { |
| updateActionState(textInput); |
| } |
| }; |
| |
| @Override |
| public void configureMenuBar(Component component, MenuBar menuBar) { |
| if (component instanceof TextInput) { |
| TextInput textInput = (TextInput)component; |
| |
| updateActionState(textInput); |
| Action.getNamedActions().get("paste").setEnabled(true); |
| |
| textInput.getTextInputContentListeners().add(textInputTextListener); |
| textInput.getTextInputSelectionListeners().add(textInputSelectionListener); |
| } else { |
| Action.getNamedActions().get("cut").setEnabled(false); |
| Action.getNamedActions().get("copy").setEnabled(false); |
| Action.getNamedActions().get("paste").setEnabled(false); |
| } |
| } |
| |
| @Override |
| public void cleanupMenuBar(Component component, MenuBar menuBar) { |
| if (component instanceof TextInput) { |
| TextInput textInput = (TextInput)component; |
| textInput.getTextInputContentListeners().remove(textInputTextListener); |
| textInput.getTextInputSelectionListeners().remove(textInputSelectionListener); |
| } |
| } |
| |
| private void updateActionState(TextInput textInput) { |
| Action.getNamedActions().get("cut").setEnabled(textInput.getSelectionLength() > 0); |
| Action.getNamedActions().get("copy").setEnabled(textInput.getSelectionLength() > 0); |
| } |
| }; |
| |
| public MenuBars() { |
| Action.getNamedActions().put("fileNew", new Action() { |
| @Override |
| public void perform(Component source) { |
| BXMLSerializer bxmlSerializer = new BXMLSerializer(); |
| bxmlSerializer.getNamespace().put("menuHandler", menuHandler); |
| |
| Component tab; |
| try { |
| tab = new Border((Component)bxmlSerializer.readObject(MenuBars.class, "document.bxml")); |
| } catch (IOException exception) { |
| throw new RuntimeException(exception); |
| } catch (SerializationException exception) { |
| throw new RuntimeException(exception); |
| } |
| |
| tabPane.getTabs().add(tab); |
| TabPane.setTabData(tab, "Document " + tabPane.getTabs().getLength()); |
| tabPane.setSelectedIndex(tabPane.getTabs().getLength() - 1); |
| } |
| }); |
| |
| Action.getNamedActions().put("fileOpen", new Action() { |
| @Override |
| public void perform(Component source) { |
| fileBrowserSheet.open(MenuBars.this); |
| } |
| }); |
| |
| Action.getNamedActions().put("cut", new Action(false) { |
| @Override |
| public void perform(Component source) { |
| TextInput textInput = (TextInput)MenuBars.this.getFocusDescendant(); |
| textInput.cut(); |
| } |
| }); |
| |
| Action.getNamedActions().put("copy", new Action(false) { |
| @Override |
| public void perform(Component source) { |
| TextInput textInput = (TextInput)MenuBars.this.getFocusDescendant(); |
| textInput.copy(); |
| } |
| }); |
| |
| Action.getNamedActions().put("paste", new Action(false) { |
| @Override |
| public void perform(Component source) { |
| TextInput textInput = (TextInput)MenuBars.this.getFocusDescendant(); |
| textInput.paste(); |
| } |
| }); |
| } |
| |
| @Override |
| public void initialize(Map<String, Object> namespace, URL location, Resources resources) { |
| } |
| } |
| ]]> |
| </source> |
| |
| <p> |
| The class also defines an anonymous inner implementation of the <tt>MenuHandler</tt> |
| interface that is used to configure the menu bar based on the focused component. In |
| <tt>configureMenuBar()</tt>, the actions associated with the "cut", "copy", and "paste" |
| operations are enabled and disabled as appropriate. Listeners are also added to the |
| focused component (if it is a <tt>TextInput</tt>) to ensure that the action's state |
| accurately reflects the current selection. The listeners are removed in |
| <tt>cleanupMenuBar()</tt>, if necessary. |
| </p> |
| |
| <p> |
| Note that menu bar configuration via <tt>MenuHandler</tt> isn't limited to enabling or |
| disabling actions - new menu items can be dynamically created, menu item selection |
| state can be changed, etc. However, unlike context menus, the framework does not |
| automatically clean up any changes made to the menu bar. It is up to the application |
| to ensure that the menu bar remains in a consistent state using the |
| <tt>configureMenuBar()</tt> and <tt>cleanupMenuBar()</tt> methods. |
| </p> |
| </body> |
| </document> |