JDK-8090275 : KeyCharacterCombination doesn't match the alternative keys
  • Type: Bug
  • Component: javafx
  • Sub-Component: scenegraph
  • Affected Version: 8
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2013-01-10
  • Updated: 2025-01-16
  • Resolved: 2025-01-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdResolved
Related Reports
Blocks :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
KeyCharacterCombination matches only one of the possible keys producing the character. If, for instance, a KeyCharacterCombination with "+" is created, it should match both the key-pad and num-pad "+" keys.
Comments
a) It says that ("+", {CONTROL}) matches "Shift Ctrl =". Why did we choose to consume shift by the character modification and not control? Perhaps because "Ctrl =" doesn't produce any character? But other keys do. So for "Shift Ctrl A" do we use shift to transform the character and match ("A", {CONTROL}) or do we use control to transform the character and match ("\u0001", {SHIFT}), or both to match ("\u0001", {})? Or, do we try all possible combinations to match all of the above, or to search for the ones that actually produce letters? To me this seems really confusing. I think that what might work is to set Shift modifier aside. Throw an exception when user tries to specify Shift state. Use the Shift state and the key to produce a letter and match this letter and the state of the other modifiers. I'm not sure whether there are keyboards that produce actual letters with other modifiers than Shift, but I tried it briefly and it doesn't seem so, so I think it may work and may be quite clear (still not intuitive though). b) Pressing a key with Alt or Meta usually doesn't even produce KEY_TYPED event, does it? I think that pretty much kills this option.
03-10-2014

> So for "Shift Ctrl A" do we use shift to transform the character and match ("A", {CONTROL}) or do we use control to transform the character and match ("\u0001", {SHIFT}), or both to match ("\u0001", {})? The idea was to take the character produced by the whole combination (here "\u0001") and then consume the minimal set of modifiers needed to produce this character (here, consume {CONTROL}). An ambiguity would only arise if there were more than one minimal set of modifiers, e.g. if pressing either "Shift A" or "Control A" produced the same character, but just pressing "A" produced something else. Anyway, I didn't realize that "Ctrl Shift A" produces something else than "A", thus this approach is impractical. Let me propose something else: c) combination.match(event) := combination.modifiers ⊆ event.modifiers && resultOf(event.code, event.modifiers \ combination.modifiers) == combination.character; In words: All modifiers specified in the combination must be pressed. Only the additional pressed modifiers (not specified in the combination) participate in producing a character. This character is then compared to the one specified in the combination. Plus I would add an option to ignore case when comparing characters, probably even make this the default (to make the combinations be independent of the CapsLock). Your proposal that gives Shift a special treatment sounds good to me as well. b) I didn't know that. b) is out, then.
03-10-2014

Here are 2 proposals. Let me know if any of them makes sense to you and I will file a separate Jira issue. a) Provide a new KeyCombination subclass, call it LetterCombination for now, with constructor LetterCombination(String letter, Modifier... modifiers) and the following matching algorighm: 1. Take the event's key code and the set of pressed modifier keys, e.g. (KeyCode.EQUALS, {CONTROL, SHIFT}), and transform it into the produced character and a set of modifier keys which does not contain the modifier keys that were necessary to produce the character. In this case, it would be ("+", {CONTROL}), i.e. SHIFT was consumed by the transformation, because it was required to produce "+" from EQUALS. 2. Compare the resulting character and modifiers to the ones provided in the LetterCombination constructor. Note that, for example, LetterCombination("+", SHIFT_DOWN) would not match "Shift =", but LetterCombination("+") would. It might be somewhat confusing that the modifier keys get consumed, but the behavior is well-defined. b) Provide a new KeyCombination subclass KeyTypedCombination with constructor KeyTypedCombination(String character, Modifier... modifiers) and the following matching algorithm: public boolean match(KeyEvent event) { return super.match(event) // match the modifiers && event.getCharacter().equals(this.character); } Thus, KeyTypedCombination would be designed to match KEY_TYPED events. To make use of this, it would be necessary that scene accelerators are fired also for KEY_TYPED events, not just for KEY_PRESSED events.
30-09-2014

The problem this represents is that MenuItem can take exactly one accelerator. CTRL+PLUS is an impossible combination on a US keyboard, so developers will need to know to use CTRL+ADD instead. They must also then install a separate accelerator, assuming a specific keyboard layout, to cover the CTRL+SHIFT+EQUALS case. I don't have the solution off-hand, but I feel it should be possible to make this less awkward. I had thought that KeyCharacterCombination would be smart about keyboard layout and create something that matched. That an additional modifier is required to produce the character (e.g. SHIFT+EQUALS) would be something that it dealt with, producing the equivalent to KeyCodeCombination(KeyCode.EQUALS, KeyCombination.SHIFT_DOWN, KeyCombination.CTRL_DOWN). There are a lot of things to consider. For example if the modifiers are separate, what is the difference on a US keyboard between KeyCode.PLUS and KeyCode.EQUALS ?
30-09-2014

Exactly. KeyCharacterCombination("+") will never match "Shift =", because when you call getShift() on it, you'll get "UP", so it just can't match anything with Shift. It would be hard to define an intuitive behavior in case of specifying both the modifiers and the modifier-dependent character. I think your code is optimal. You're welcome to file feature request for yet another key combination matcher if you feel it would help though.
30-09-2014

Then that means, for example, that KeyCharacterCombination("+") will never match "Shift =" (KeyCode EQUALS) on en_US keyboard layout, although "Shift =" produces the character "+". The question then is, how should we go about binding a keyboard shortcut to "+" (e.g. zoom in) that is independent of the keyboard layout? The best I can come up with is scene.addEventHandler(KeyEvent.KEY_TYPED, e -> { if(e.getCharacter().equals("+") && !e.isControlDown() && !e.isAltDown() && !e.isMetaDown()) { zoomIn(); } }); which is not using KeyCombination or accelerator at all.
30-09-2014

It can't be done this way, because the event character depends on the modifiers, but the method must treat modifiers separately. I don't remember the concrete cases, but there are cases where pressing modifier+letter produces a different letter. We need to match the letter-that-would-be-produced-without-modifiers and the modifiers.
30-09-2014

Should the algorithm of KeyCharacterCombination.match() be the other way, i.e. convert the event to the character and then compare it to the character from the KeyCharacterCombination? (Instead of (arbitrarily) choosing one key code capable of producing the character and then compare key codes.)
27-09-2014

This depends on an issue that was deferred to Van Ness.
15-10-2013

Other keys affected are "/", "*", "-", 0-9. Also relevant: How do you do this for Insert and Delete or cursor keys which have dedicated keys and also appear on the numeric keypad (with numlock off) ?
10-01-2013