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 com.sun.javafx.scene.control.behavior;
27
28 import javafx.event.Event;
29 import javafx.geometry.NodeOrientation;
30 import javafx.scene.Node;
31 import javafx.scene.Parent;
32 import javafx.scene.control.SelectionModel;
33 import javafx.scene.control.SingleSelectionModel;
34 import javafx.scene.control.Tab;
35 import javafx.scene.control.TabPane;
36 import javafx.scene.input.KeyCode;
37 import javafx.scene.input.MouseEvent;
38 import java.util.ArrayList;
39 import java.util.List;
40
41 public class TabPaneBehavior extends BehaviorBase<TabPane> {
42
43 /**************************************************************************
44 * Setup KeyBindings *
45 *************************************************************************/
46 private static final String HOME = "Home";
47 private static final String END = "End";
48 private static final String CTRL_PAGE_UP = "Ctrl_Page_Up";
49 private static final String CTRL_PAGE_DOWN = "Ctrl_Page_Down";
50 private static final String CTRL_TAB = "Ctrl_Tab";
51 private static final String CTRL_SHIFT_TAB = "Ctrl_Shift_Tab";
52
53 protected static final List<KeyBinding> TAB_PANE_BINDINGS = new ArrayList<>();
54 static {
55 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.UP, "TraverseUp"));
56 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.DOWN, "TraverseDown"));
57 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.LEFT, "TraverseLeft"));
58 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.RIGHT, "TraverseRight"));
59 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.HOME, HOME));
60 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.END, END));
61 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.PAGE_UP, CTRL_PAGE_UP).ctrl());
62 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.PAGE_DOWN, CTRL_PAGE_DOWN).ctrl());
63 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.TAB, CTRL_TAB).ctrl());
64 TAB_PANE_BINDINGS.add(new KeyBinding(KeyCode.TAB, CTRL_SHIFT_TAB).shift().ctrl());
65 }
66
67 @Override protected void callAction(String name) {
68 boolean rtl = (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT);
69
70 if (("TraverseLeft".equals(name) && !rtl) ||
71 ("TraverseRight".equals(name) && rtl) ||
72 "TraverseUp".equals(name)) {
73 if (getControl().isFocused()) {
74 selectPreviousTab();
75 }
76 } else if (("TraverseRight".equals(name) && !rtl) ||
77 ("TraverseLeft".equals(name) && rtl) ||
78 "TraverseDown".equals(name)) {
79 if (getControl().isFocused()) {
80 selectNextTab();
81 }
82 } else if (CTRL_TAB.equals(name) || CTRL_PAGE_DOWN.equals(name)) {
83 selectNextTab();
84 } else if (CTRL_SHIFT_TAB.equals(name) || CTRL_PAGE_UP.equals(name)) {
85 selectPreviousTab();
86 } else if (HOME.equals(name)) {
87 if (getControl().isFocused()) {
88 moveSelection(0, 1);
89 }
90 } else if (END.equals(name)) {
91 if (getControl().isFocused()) {
92 moveSelection(getControl().getTabs().size() - 1, -1);
93 }
94 } else {
95 super.callAction(name);
96 }
97 }
98
99
100
101 /***************************************************************************
102 * *
103 * Mouse event handling *
104 * *
105 **************************************************************************/
106
107 @Override public void mousePressed(MouseEvent e) {
108 super.mousePressed(e);
109 TabPane tp = getControl();
110 tp.requestFocus();
111 }
112
113 /**************************************************************************
114 * State and Functions *
115 *************************************************************************/
116
117 public TabPaneBehavior(TabPane tabPane) {
118 super(tabPane, TAB_PANE_BINDINGS);
119 }
120
121 public void selectTab(Tab tab) {
122 getControl().getSelectionModel().select(tab);
123 }
124
125 public boolean canCloseTab(Tab tab) {
126 Event event = new Event(tab,tab,Tab.TAB_CLOSE_REQUEST_EVENT);
127 Event.fireEvent(tab, event);
128 return ! event.isConsumed();
129 }
130
131 public void closeTab(Tab tab) {
132 TabPane tabPane = getControl();
133 // only switch to another tab if the selected tab is the one we're closing
134 int index = tabPane.getTabs().indexOf(tab);
135 if (index != -1) {
136 tabPane.getTabs().remove(index);
137 }
138 if (tab.getOnClosed() != null) {
139 Event.fireEvent(tab, new Event(Tab.CLOSED_EVENT));
140 }
141 }
142
143 // Find a tab after the currently selected that is not disabled. Loop around
144 // if no tabs are found after currently selected tab.
145 public void selectNextTab() {
146 moveSelection(1);
147 }
148
149 // Find a tab before the currently selected that is not disabled.
150 public void selectPreviousTab() {
151 moveSelection(-1);
152 }
153
154 private void moveSelection(int delta) {
155 moveSelection(getControl().getSelectionModel().getSelectedIndex(), delta);
156 }
157
158 private void moveSelection(int startIndex, int delta) {
159 final TabPane tabPane = getControl();
160 int tabIndex = findValidTab(startIndex, delta);
161 if (tabIndex > -1) {
162 final SelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
163 selectionModel.select(tabIndex);
164 }
165 tabPane.requestFocus();
166 }
167
168 private int findValidTab(int startIndex, int delta) {
169 final TabPane tabPane = getControl();
170 final List<Tab> tabs = tabPane.getTabs();
171 final int max = tabs.size();
172
173 int index = startIndex;
174 do {
175 index = nextIndex(index + delta, max);
176 Tab tab = tabs.get(index);
177 if (tab != null && !tab.isDisable()) {
178 return index;
179 }
180 } while (index != startIndex);
181
182 return -1;
183 }
184
185 private int nextIndex(int value, int max) {
186 final int min = 0;
187 int r = value % max;
188 if (r > min && max < min) {
189 r = r + max - min;
|
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 com.sun.javafx.scene.control.behavior;
27
28 import com.sun.javafx.scene.control.inputmap.InputMap;
29 import javafx.event.Event;
30 import javafx.scene.control.SelectionModel;
31 import javafx.scene.control.Tab;
32 import javafx.scene.control.TabPane;
33 import javafx.scene.input.*;
34 import com.sun.javafx.scene.control.inputmap.KeyBinding;
35
36 import java.util.List;
37
38 import static javafx.scene.input.KeyCode.*;
39 import static com.sun.javafx.scene.control.inputmap.InputMap.KeyMapping;
40 import static com.sun.javafx.scene.control.inputmap.InputMap.MouseMapping;
41
42 public class TabPaneBehavior extends BehaviorBase<TabPane> {
43
44 private final InputMap<TabPane> tabPaneInputMap;
45
46 public TabPaneBehavior(TabPane tabPane) {
47 super(tabPane);
48
49 // create a map for TabPane-specific mappings (this reuses the default
50 // InputMap installed on the control, if it is non-null, allowing us to pick up any user-specified mappings)
51 tabPaneInputMap = createInputMap();
52
53 // TabPane-specific mappings for key and mouse input
54 addDefaultMapping(tabPaneInputMap,
55 new KeyMapping(UP, e -> selectPreviousTab()),
56 new KeyMapping(DOWN, e -> selectNextTab()),
57 new KeyMapping(LEFT, e -> rtl(tabPane, this::selectNextTab, this::selectPreviousTab)),
58 new KeyMapping(RIGHT, e -> rtl(tabPane, this::selectPreviousTab, this::selectNextTab)),
59 new KeyMapping(HOME, e -> {
60 if (getNode().isFocused()) {
61 moveSelection(0, 1);
62 }
63 }),
64 new KeyMapping(END, e -> {
65 if (getNode().isFocused()) {
66 moveSelection(getNode().getTabs().size() - 1, -1);
67 }
68 }),
69 new KeyMapping(new KeyBinding(PAGE_UP).ctrl(), e -> selectPreviousTab()),
70 new KeyMapping(new KeyBinding(PAGE_DOWN).ctrl(), e -> selectNextTab()),
71 new KeyMapping(new KeyBinding(TAB).ctrl(), e -> selectNextTab()),
72 new KeyMapping(new KeyBinding(TAB).ctrl().shift(), e -> selectPreviousTab()),
73 new MouseMapping(MouseEvent.MOUSE_PRESSED, e -> getNode().requestFocus())
74 );
75 }
76
77 @Override public InputMap<TabPane> getInputMap() {
78 return tabPaneInputMap;
79 }
80
81 public void selectTab(Tab tab) {
82 getNode().getSelectionModel().select(tab);
83 }
84
85 public boolean canCloseTab(Tab tab) {
86 Event event = new Event(tab,tab,Tab.TAB_CLOSE_REQUEST_EVENT);
87 Event.fireEvent(tab, event);
88 return ! event.isConsumed();
89 }
90
91 public void closeTab(Tab tab) {
92 TabPane tabPane = getNode();
93 // only switch to another tab if the selected tab is the one we're closing
94 int index = tabPane.getTabs().indexOf(tab);
95 if (index != -1) {
96 tabPane.getTabs().remove(index);
97 }
98 if (tab.getOnClosed() != null) {
99 Event.fireEvent(tab, new Event(Tab.CLOSED_EVENT));
100 }
101 }
102
103 // Find a tab after the currently selected that is not disabled. Loop around
104 // if no tabs are found after currently selected tab.
105 public void selectNextTab() {
106 moveSelection(1);
107 }
108
109 // Find a tab before the currently selected that is not disabled.
110 public void selectPreviousTab() {
111 moveSelection(-1);
112 }
113
114 private void moveSelection(int delta) {
115 moveSelection(getNode().getSelectionModel().getSelectedIndex(), delta);
116 }
117
118 private void moveSelection(int startIndex, int delta) {
119 final TabPane tabPane = getNode();
120 int tabIndex = findValidTab(startIndex, delta);
121 if (tabIndex > -1) {
122 final SelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
123 selectionModel.select(tabIndex);
124 }
125 tabPane.requestFocus();
126 }
127
128 private int findValidTab(int startIndex, int delta) {
129 final TabPane tabPane = getNode();
130 final List<Tab> tabs = tabPane.getTabs();
131 final int max = tabs.size();
132
133 int index = startIndex;
134 do {
135 index = nextIndex(index + delta, max);
136 Tab tab = tabs.get(index);
137 if (tab != null && !tab.isDisable()) {
138 return index;
139 }
140 } while (index != startIndex);
141
142 return -1;
143 }
144
145 private int nextIndex(int value, int max) {
146 final int min = 0;
147 int r = value % max;
148 if (r > min && max < min) {
149 r = r + max - min;
|