Skip to content

Commit 6749ab6

Browse files
Michael Straußkevinrushforth
authored andcommitted
8274854: Mnemonics for menu containing numeric text not working
Reviewed-by: aghaisas, kcr
1 parent 4d8e12d commit 6749ab6

File tree

3 files changed

+172
-32
lines changed

3 files changed

+172
-32
lines changed

modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TextBinding.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,25 +183,33 @@ private void parseAndSplit(String s) {
183183
}
184184

185185
StringBuilder builder = new StringBuilder(s.length());
186+
int i = 0;
186187

187-
for (int i = 0, length = s.length(); i < length; ++i) {
188+
// Parse the input string and stop after the first mnemonic.
189+
for (int length = s.length(); i < length; ++i) {
188190
if (isEscapedMnemonicSymbol(s, i)) {
189191
builder.append(s.charAt(i++));
192+
} else if (isExtendedMnemonic(s, i)) {
193+
mnemonic = String.valueOf(s.charAt(i + 2));
194+
mnemonicIndex = i;
195+
extendedMnemonicText = s.substring(i + 1, i + 4);
196+
i += 4;
197+
break;
190198
} else if (isSimpleMnemonic(s, i)) {
191199
char c = s.charAt(i + 1);
192-
builder.append(c);
193200
mnemonic = String.valueOf(c);
194201
mnemonicIndex = i;
195202
i += 1;
196-
} else if (isExtendedMnemonic(s, i)) {
197-
mnemonic = String.valueOf(s.charAt(i + 2));
198-
extendedMnemonicText = s.substring(i + 1, i + 4);
199-
i += 3;
203+
break;
200204
} else {
201205
builder.append(s.charAt(i));
202206
}
203207
}
204208

209+
if (s.length() > i) {
210+
builder.append(s.substring(i));
211+
}
212+
205213
text = builder.toString();
206214
}
207215

@@ -216,21 +224,26 @@ private boolean isEscapedMnemonicSymbol(String s, int position) {
216224

217225
/**
218226
* Determines whether the string contains a simple mnemonic at the specified position.
227+
* A simple mnemonic is any two-character string similar to "_x", where x is not an
228+
* underscore or a whitespace character.
219229
*/
220230
private boolean isSimpleMnemonic(String s, int position) {
221231
return s.length() > position + 1
222232
&& s.charAt(position) == MNEMONIC_SYMBOL
223-
&& Character.isAlphabetic(s.charAt(position + 1));
233+
&& s.charAt(position + 1) != MNEMONIC_SYMBOL
234+
&& !Character.isWhitespace(s.charAt(position + 1));
224235
}
225236

226237
/**
227238
* Determines whether the string contains an extended mnemonic at the specified position.
239+
* An extended mnemonic is any four-character string similar to "_(x)", where x is any
240+
* character except whitespace.
228241
*/
229242
private boolean isExtendedMnemonic(String s, int position) {
230243
return s.length() > position + 3
231244
&& s.charAt(position) == MNEMONIC_SYMBOL
232245
&& s.charAt(position + 1) == '('
233-
&& Character.isAlphabetic(s.charAt(position + 2))
246+
&& !Character.isWhitespace(s.charAt(position + 2))
234247
&& s.charAt(position + 3) == ')';
235248
}
236249

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (c) 2021, 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.com.sun.javafx.scene.control.behavior;
27+
28+
import com.sun.javafx.scene.control.behavior.TextBinding;
29+
import javafx.scene.input.KeyCombination;
30+
import org.junit.Test;
31+
32+
import static org.junit.Assert.*;
33+
34+
public class TextBindingTest {
35+
36+
private static void assertKeyCombination(String expected, KeyCombination actual) {
37+
if (com.sun.javafx.PlatformUtil.isMac()) {
38+
assertSame(KeyCombination.ModifierValue.DOWN, actual.getMeta());
39+
} else {
40+
assertSame(KeyCombination.ModifierValue.DOWN, actual.getAlt());
41+
}
42+
43+
assertEquals(expected, ((TextBinding.MnemonicKeyCombination)actual).getCharacter());
44+
}
45+
46+
@Test
47+
public void testSimpleMnemonicLetter() {
48+
var binding = new TextBinding("foo _bar");
49+
assertEquals("foo bar", binding.getText());
50+
assertEquals("b", binding.getMnemonic());
51+
assertKeyCombination("b", binding.getMnemonicKeyCombination());
52+
assertEquals(4, binding.getMnemonicIndex());
53+
}
54+
55+
@Test
56+
public void testSimpleMnemonicDigit() {
57+
var binding = new TextBinding("foo _1 bar");
58+
assertEquals("foo 1 bar", binding.getText());
59+
assertEquals("1", binding.getMnemonic());
60+
assertKeyCombination("1", binding.getMnemonicKeyCombination());
61+
assertEquals(4, binding.getMnemonicIndex());
62+
}
63+
64+
@Test
65+
public void testExtendedMnemonicLetter() {
66+
var binding = new TextBinding("foo _(x)bar");
67+
assertEquals("foo bar", binding.getText());
68+
assertEquals("x", binding.getMnemonic());
69+
assertKeyCombination("x", binding.getMnemonicKeyCombination());
70+
assertEquals(4, binding.getMnemonicIndex());
71+
}
72+
73+
@Test
74+
public void testExtendedMnemonicUnderscore() {
75+
var binding = new TextBinding("foo _(_)bar");
76+
assertEquals("foo bar", binding.getText());
77+
assertEquals("_", binding.getMnemonic());
78+
assertKeyCombination("_", binding.getMnemonicKeyCombination());
79+
assertEquals(4, binding.getMnemonicIndex());
80+
}
81+
82+
@Test
83+
public void testExtendedMnemonicClosingBrace() {
84+
var binding = new TextBinding("foo _())bar");
85+
assertEquals("foo bar", binding.getText());
86+
assertEquals(")", binding.getMnemonic());
87+
assertKeyCombination(")", binding.getMnemonicKeyCombination());
88+
assertEquals(4, binding.getMnemonicIndex());
89+
}
90+
91+
@Test
92+
public void testEscapedMnemonicSymbol() {
93+
var binding = new TextBinding("foo __bar");
94+
assertEquals("foo _bar", binding.getText());
95+
assertNull(binding.getMnemonic());
96+
assertNull(binding.getMnemonicKeyCombination());
97+
assertEquals(-1, binding.getMnemonicIndex());
98+
}
99+
100+
@Test
101+
public void testWhitespaceIsNotProcessedAsExtendedMnemonic() {
102+
var binding = new TextBinding("foo _( ) bar");
103+
assertEquals("foo ( ) bar", binding.getText());
104+
assertEquals("(", binding.getMnemonic());
105+
assertKeyCombination("(", binding.getMnemonicKeyCombination());
106+
assertEquals(4, binding.getMnemonicIndex());
107+
}
108+
109+
@Test
110+
public void testUnderscoreNotFollowedByAlphabeticCharIsNotAMnemonic() {
111+
var binding = new TextBinding("foo_ bar");
112+
assertEquals("foo_ bar", binding.getText());
113+
assertNull(binding.getMnemonic());
114+
assertNull(binding.getMnemonicKeyCombination());
115+
assertEquals(-1, binding.getMnemonicIndex());
116+
}
117+
118+
@Test
119+
public void testUnderscoreAtEndOfTextIsNotAMnemonic() {
120+
var binding = new TextBinding("foo_");
121+
assertEquals("foo_", binding.getText());
122+
assertNull(binding.getMnemonic());
123+
assertNull(binding.getMnemonicKeyCombination());
124+
assertEquals(-1, binding.getMnemonicIndex());
125+
}
126+
127+
@Test
128+
public void testMnemonicParsingStopsAfterFirstSimpleMnemonic() {
129+
var binding = new TextBinding("_foo _bar _qux");
130+
assertEquals("foo _bar _qux", binding.getText());
131+
assertEquals("f", binding.getMnemonic());
132+
assertKeyCombination("f", binding.getMnemonicKeyCombination());
133+
assertEquals(0, binding.getMnemonicIndex());
134+
}
135+
136+
@Test
137+
public void testMnemonicParsingStopsAfterFirstExtendedMnemonic() {
138+
var binding = new TextBinding("_(x)foo _bar _qux");
139+
assertEquals("foo _bar _qux", binding.getText());
140+
assertEquals("x", binding.getMnemonic());
141+
assertKeyCombination("x", binding.getMnemonicKeyCombination());
142+
assertEquals(0, binding.getMnemonicIndex());
143+
}
144+
145+
}

modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/LabelSkinTest.java

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,7 +2064,7 @@ public class LabelSkinTest {
20642064
***************************************************************************/
20652065

20662066
@Test
2067-
public void mnemonicSymbolIsRemovedFromDisplayedText() {
2067+
public void testMnemonicIsProcessedWhenParsingIsEnabled() {
20682068
label.setMnemonicParsing(true);
20692069
label.setText("foo _bar");
20702070
label.autosize();
@@ -2073,7 +2073,7 @@ public void mnemonicSymbolIsRemovedFromDisplayedText() {
20732073
}
20742074

20752075
@Test
2076-
public void extendedMnemonicIsRemovedFromDisplayedText() {
2076+
public void testExtendedMnemonicIsProcessedWhenParsingIsEnabled() {
20772077
label.setMnemonicParsing(true);
20782078
label.setText("foo _(x)bar");
20792079
label.autosize();
@@ -2082,41 +2082,23 @@ public void extendedMnemonicIsRemovedFromDisplayedText() {
20822082
}
20832083

20842084
@Test
2085-
public void escapedMnemonicSymbolIsRetainedInDisplayedText() {
2086-
label.setMnemonicParsing(true);
2087-
label.setText("foo __bar");
2085+
public void testMnemonicsAreNotProcessedWhenParsingIsDisabled() {
2086+
label.setMnemonicParsing(false);
2087+
label.setText("foo _bar");
20882088
label.autosize();
20892089
skin.updateDisplayedText();
20902090
assertEquals("foo _bar", LabelSkinBaseShim.getText(label).getText());
20912091
}
20922092

20932093
@Test
2094-
public void escapedMnemonicSymbolIsNotProcessedWhenMnemonicParsingIsDisabled() {
2094+
public void testEscapedMnemonicSymbolIsNotProcessedWhenParsingIsDisabled() {
20952095
label.setMnemonicParsing(false);
20962096
label.setText("foo __bar");
20972097
label.autosize();
20982098
skin.updateDisplayedText();
20992099
assertEquals("foo __bar", LabelSkinBaseShim.getText(label).getText());
21002100
}
21012101

2102-
@Test
2103-
public void underscoreNotFollowedByAlphabeticCharIsNotAMnemonic() {
2104-
label.setMnemonicParsing(true);
2105-
label.setText("foo_ bar");
2106-
label.autosize();
2107-
skin.updateDisplayedText();
2108-
assertEquals("foo_ bar", LabelSkinBaseShim.getText(label).getText());
2109-
}
2110-
2111-
@Test
2112-
public void underscoreAtEndOfTextIsNotAMnemonic() {
2113-
label.setMnemonicParsing(true);
2114-
label.setText("foo_");
2115-
label.autosize();
2116-
skin.updateDisplayedText();
2117-
assertEquals("foo_", LabelSkinBaseShim.getText(label).getText());
2118-
}
2119-
21202102
public static final class LabelSkinMock extends LabelSkin {
21212103
boolean propertyChanged = false;
21222104
int propertyChangeCount = 0;

0 commit comments

Comments
 (0)