diff --git a/apps/toys/Hello/src/main/java/hello/HelloPopupMenu.java b/apps/toys/Hello/src/main/java/hello/HelloPopupMenu.java --- a/apps/toys/Hello/src/main/java/hello/HelloPopupMenu.java +++ b/apps/toys/Hello/src/main/java/hello/HelloPopupMenu.java @@ -25,11 +25,14 @@ package hello; +import com.sun.webkit.ContextMenuItem; + import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; +import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; import javafx.scene.paint.Color; import javafx.stage.Stage; @@ -64,10 +67,27 @@ MenuItem item3 = new MenuItem("Templates"); item3.setOnAction(e -> System.out.println("Templates")); + + MenuItem item4 = new MenuItem("About"); + item1.setOnAction(e -> System.out.println("About")); - popupMenu.getItems().add(item1); - popupMenu.getItems().add(item2); - popupMenu.getItems().add(item3); + MenuItem item5 = new MenuItem("Preferences"); + item2.setOnAction(e -> System.out.println("Preferences")); + + MenuItem item6 = new MenuItem("Templates"); + item3.setOnAction(e -> System.out.println("Templates")); + + Menu menu = new Menu(); + menu.getItems().add(item1); + menu.getItems().add(item2); + menu.getItems().add(item3); + menu.setText("Another Menu"); + + + popupMenu.getItems().add(item4); + popupMenu.getItems().add(item5); + popupMenu.getItems().add(item6); + popupMenu.getItems().add(menu); final Button button = new Button("Click me"); button.setContextMenu(popupMenu); @@ -77,6 +97,11 @@ root.getChildren().add(button); return scene; } + + class TestMenuItem extends MenuItem { + + + } /** * @param args the command line arguments diff --git a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuContent.java b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuContent.java --- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuContent.java +++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuContent.java @@ -53,6 +53,8 @@ import javafx.geometry.*; import javafx.scene.Node; import javafx.scene.Parent; +import javafx.scene.accessibility.Attribute; +import javafx.scene.accessibility.Role; import javafx.scene.control.*; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; @@ -1429,7 +1431,23 @@ return null; } - + + @Override + public Object accGetAttribute(Attribute attribute, Object... parameters) { + switch(attribute) { + case TITLE: return item.getText(); + case ROLE: return Role.MENUITEM; + } + return super.accGetAttribute(attribute, parameters); + } + } + + @Override + public Object accGetAttribute(Attribute attribute, Object... parameters) { + switch(attribute) { + case ROLE: return Role.CONTEXTMENU; + } + return super.accGetAttribute(attribute, parameters); } diff --git a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuSkin.java b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuSkin.java --- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuSkin.java +++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/ContextMenuSkin.java @@ -30,6 +30,7 @@ import javafx.event.Event; import javafx.event.EventHandler; import javafx.scene.Node; +import javafx.scene.accessibility.Attribute; import javafx.scene.control.ContextMenu; import javafx.scene.control.Menu; import javafx.scene.control.Skin; @@ -125,4 +126,22 @@ root.styleProperty().unbind(); if (tlFocus != null) tlFocus.dispose(); } + + /** + * Accessibility hook to allow controls to use the skin to redefine attributes. + */ + public Object accGetAttribute(Attribute attribute, Object... parameters) { + + /* + * Because the skin is does not inherit from SkinBase, we have make sure we got to the right Class + * */ + if (BehaviorSkinBase.IS_TOUCH_SUPPORTED && + popupMenu.getStyleClass().contains("text-input-context-menu")) { + EmbeddedTextContextMenuContent menuContent = (EmbeddedTextContextMenuContent)root; + return menuContent.accGetAttribute(attribute, parameters); + } else { + ContextMenuContent menuContent = (ContextMenuContent)root; + return menuContent.accGetAttribute(attribute, parameters); + } + } } diff --git a/modules/controls/src/main/java/javafx/scene/control/ChoiceBox.java b/modules/controls/src/main/java/javafx/scene/control/ChoiceBox.java --- a/modules/controls/src/main/java/javafx/scene/control/ChoiceBox.java +++ b/modules/controls/src/main/java/javafx/scene/control/ChoiceBox.java @@ -36,10 +36,14 @@ import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.event.ActionEvent; +import javafx.scene.accessibility.Action; +import javafx.scene.accessibility.Attribute; +import javafx.scene.accessibility.Role; import javafx.util.StringConverter; +import javafx.css.PseudoClass; -import javafx.css.PseudoClass; import com.sun.javafx.scene.control.skin.ChoiceBoxSkin; + import javafx.beans.DefaultProperty; /** @@ -164,6 +168,7 @@ private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper() { @Override protected void invalidated() { pseudoClassStateChanged(SHOWING_PSEUDOCLASS_STATE, get()); + accSendNotification(Attribute.EXPANDED); } @Override @@ -282,6 +287,7 @@ if (sm != null) { sm.select(super.getValue()); } + accSendNotification(Attribute.TITLE); } }; public final void setValue(T value) { valueProperty().set(value); } @@ -413,4 +419,35 @@ } } } + + /** @treatAsPrivate */ + @Override + public Object accGetAttribute(Attribute attribute, Object... parameters) { + switch(attribute) { + case ROLE: return Role.CHOICEBOX; + case TITLE: + //let the skin first. + Object title = super.accGetAttribute(attribute, parameters); + if (title != null) return title; + StringConverter converter = getConverter(); + if (converter == null) { + return getValue() != null ? getValue().toString() : ""; + } + return converter.toString(getValue()); + case EXPANDED: + System.err.println("_____-------EXPANDING"); + return isShowing(); + default: return super.accGetAttribute(attribute, parameters); + } + } + + @Override + public void accExecuteAction(Action action, Object... parameters) { + switch (action) { + case COLLAPSE: hide(); break; + case EXPAND: show(); break; + default: super.accExecuteAction(action); break; + } + } + } diff --git a/modules/controls/src/main/java/javafx/scene/control/ContextMenu.java b/modules/controls/src/main/java/javafx/scene/control/ContextMenu.java --- a/modules/controls/src/main/java/javafx/scene/control/ContextMenu.java +++ b/modules/controls/src/main/java/javafx/scene/control/ContextMenu.java @@ -38,10 +38,12 @@ import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.accessibility.Attribute; import javafx.stage.Window; import com.sun.javafx.Utils; import com.sun.javafx.collections.TrackableObservableList; +import com.sun.javafx.scene.control.skin.ContextMenuContent; import com.sun.javafx.scene.control.skin.ContextMenuSkin; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -273,8 +275,23 @@ doShow(anchor, screenX, screenY); } + boolean isRootMenu; + Node owner; //HACK: save for reference when hiding; + private void doShow(Node anchor, double screenX, double screenY) { Event.fireEvent(this, new Event(Menu.ON_SHOWING)); + //HACK - not sure if this is best time/place for these calls. + if (anchor instanceof ContextMenuContent.MenuItemContainer) { + //sub menus + System.err.println("MenuOpen"); + anchor.accSendNotification(Attribute.MENUOPEN); + } else { + //Since ContextMenus are recursive, the root menu calls start + System.err.println("MenuStart"); + anchor.accSendNotification(Attribute.MENUSTART); + isRootMenu = true; + } + owner = anchor; if(isImpl_showRelativeToWindow()) { final Scene scene = (anchor == null) ? null : anchor.getScene(); final Window win = (scene == null) ? null : scene.getWindow(); @@ -297,6 +314,13 @@ Event.fireEvent(this, new Event(Menu.ON_HIDING)); super.hide(); Event.fireEvent(this, new Event(Menu.ON_HIDDEN)); + if (isRootMenu) { + System.err.println("MenuEnd"); + owner.accSendNotification(Attribute.MENUEND); + } else { + System.err.println("MenuClose"); + owner.accSendNotification(Attribute.MENUCLOSE); + } } /** {@inheritDoc} */ diff --git a/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java b/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java --- a/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java +++ b/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java @@ -122,6 +122,9 @@ private static final int UIA_HyperlinkControlTypeId = 50005; private static final int UIA_ImageControlTypeId = 50006; private static final int UIA_ListControlTypeId = 50008; + private static final int UIA_MenuControlTypeId = 50009; + private static final int UIA_MenuBarControlTypeId = 50010; + private static final int UIA_MenuItemControlTypeId = 50011; private static final int UIA_ProgressBarControlTypeId = 50012; private static final int UIA_RadioButtonControlTypeId = 50013; private static final int UIA_SliderControlTypeId = 50015; @@ -152,8 +155,12 @@ private static final int RowOrColumnMajor_Indeterminate = 2; /* Event ID constants */ + private static final int UIA_MenuOpenedEventId = 20003; private static final int UIA_AutomationPropertyChangedEventId= 20004; private static final int UIA_AutomationFocusChangedEventId = 20005; + private static final int UIA_MenuClosedEventId = 20007; + private static final int UIA_MenuModeEndEventId = 20019; + private static final int UIA_MenuModeStartEventId = 20018; private static final int UIA_SelectionItem_ElementRemovedFromSelectionEventId = 20011; private static final int UIA_SelectionItem_ElementSelectedEventId = 20012; @@ -229,6 +236,22 @@ @Override public void sendNotification(Attribute notification) { switch (notification) { + case MENUSTART: + System.err.println("++Event : " + notification + " from: " + this); + UiaRaiseAutomationEvent(peer, UIA_MenuModeStartEventId); + break; + case MENUOPEN: + System.err.println("++Event : " + notification + " from: " + this); + UiaRaiseAutomationEvent(peer, UIA_MenuOpenedEventId); + break; + case MENUCLOSE: + System.err.println("++Event : " + notification + " from: " + this); + UiaRaiseAutomationEvent(peer, UIA_MenuClosedEventId); + break; + case MENUEND: + System.err.println("++Event : " + notification + " from: " + this); + UiaRaiseAutomationEvent(peer, UIA_MenuModeEndEventId); + break; case FOCUS_NODE: if (getView() != null) { long focus = GetFocus(); @@ -393,6 +416,8 @@ Role role = (Role)getAttribute(ROLE); if (role == null) return UIA_GroupControlTypeId; switch (role) { + case CONTEXTMENU: return UIA_MenuControlTypeId; + case MENUITEM: return UIA_MenuItemControlTypeId; case BUTTON: return UIA_ButtonControlTypeId; case TOGGLE_BUTTON: return UIA_ButtonControlTypeId; case PAGINATION: @@ -414,6 +439,7 @@ case IMAGE: return UIA_ImageControlTypeId; case RADIO_BUTTON: return UIA_RadioButtonControlTypeId; case CHECKBOX: return UIA_CheckBoxControlTypeId; + case CHOICEBOX: case COMBOBOX: return UIA_ComboBoxControlTypeId; case HYPERLINK: return UIA_HyperlinkControlTypeId; case TREE_VIEW: return UIA_TreeControlTypeId; @@ -437,6 +463,7 @@ Role role = (Role)getAttribute(ROLE); boolean impl = false; switch (role) { + case MENUITEM: case HYPERLINK: case BUTTON: impl = patternId == UIA_InvokePatternId; @@ -514,6 +541,10 @@ case TOGGLE_BUTTON: impl = patternId == UIA_TogglePatternId; break; + case CHOICEBOX: + impl = patternId == UIA_ValuePatternId || + patternId == UIA_ExpandCollapsePatternId; + break; case TITLED_PANE: case TOOLBAR: impl = patternId == UIA_ExpandCollapsePatternId; diff --git a/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java b/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java --- a/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java +++ b/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java @@ -363,6 +363,10 @@ * Type: Boolean */ VISITED("Visited", Boolean.class), + MENUOPEN("OpenMenu", Node.class), + MENUCLOSE("CloseMenu", Node.class), + MENUSTART("StartMenu", Node.class), + MENUEND("EndMenu", Node.class), ; private String name; diff --git a/modules/graphics/src/main/java/javafx/scene/accessibility/Role.java b/modules/graphics/src/main/java/javafx/scene/accessibility/Role.java --- a/modules/graphics/src/main/java/javafx/scene/accessibility/Role.java +++ b/modules/graphics/src/main/java/javafx/scene/accessibility/Role.java @@ -31,6 +31,8 @@ * @treatAsPrivate */ public enum Role { + CONTEXTMENU, + MENUITEM, /** * Accordion @@ -51,6 +53,13 @@ * Actions: FIRE */ CHECKBOX, + + /** + * ChoiceBox. + * Attributes: Title, TOGGLE_STATE + * Actions: Fire + */ + CHOICEBOX, /** * ComboBox.