88import static org .fxmisc .wellbehaved .event .template .InputMapTemplate .*;
99import static org .reactfx .EventStreams .*;
1010
11+ import java .util .Arrays ;
1112import java .util .function .Predicate ;
1213
1314import javafx .event .Event ;
@@ -44,6 +45,7 @@ class GenericStyledAreaBehavior {
4445 }
4546
4647 private static final InputMapTemplate <GenericStyledAreaBehavior , ? super Event > EVENT_TEMPLATE ;
48+ private static final Predicate <KeyEvent > controlKeysFilter ;
4749
4850 static {
4951 SelectionPolicy selPolicy = isMac
@@ -65,7 +67,7 @@ class GenericStyledAreaBehavior {
6567 KeyCharacterCombination SHORTCUT_Y = new KeyCharacterCombination ( "y" , SHORTCUT_DOWN );
6668 KeyCharacterCombination SHORTCUT_Z = new KeyCharacterCombination ( "z" , SHORTCUT_DOWN );
6769 KeyCharacterCombination SHORTCUT_SHIFT_Z = new KeyCharacterCombination ( "z" , SHORTCUT_DOWN , SHIFT_DOWN );
68-
70+
6971 InputMapTemplate <GenericStyledAreaBehavior , KeyEvent > editsBase = sequence (
7072 // deletion
7173 consume (keyPressed (DELETE ), GenericStyledAreaBehavior ::deleteForward ),
@@ -167,18 +169,26 @@ class GenericStyledAreaBehavior {
167169 ), (b , e ) -> b .view .copy ()
168170 );
169171
170- Predicate <KeyEvent > noControlKeys = e ->
171- // filter out control keys
172- (!e .isControlDown () && !e .isMetaDown ())
173- // except on Windows allow the Ctrl+Alt combination (produced by AltGr)
174- || (isWindows && !e .isMetaDown () && (!e .isControlDown () || e .isAltDown ()));
172+ controlKeysFilter = e -> {
173+ if (isWindows ) {
174+ //Windows input. ALT + CONTROL accelerators are the same as ALT GR accelerators.
175+ //If ALT + CONTROL are pressed and the given character is valid then print the character.
176+ //Else, don't consume the event. This change allows Windows users to use accelerators and
177+ //printing special characters at the same time.
178+ // (For example: ALT + CONTROL + E prints the euro symbol in the spanish keyboard while ALT + CONTROL + L has assigned an accelerator.)
179+ //Note that this is how several IDEs such JetBrains IDEs or Eclipse behave.
180+ if (e .isControlDown () && e .isAltDown () && !e .isMetaDown () && e .getCharacter ().length () == 1
181+ && e .getCharacter ().getBytes ()[0 ] != 0 ) return true ;
182+ }
183+ return !e .isControlDown () && !e .isAltDown () && !e .isMetaDown ();
184+ };
175185
176186 Predicate <KeyEvent > isChar = e ->
177187 e .getCode ().isLetterKey () ||
178188 e .getCode ().isDigitKey () ||
179189 e .getCode ().isWhitespaceKey ();
180190
181- InputMapTemplate <GenericStyledAreaBehavior , KeyEvent > charPressConsumer = consume (keyPressed ().onlyIf (isChar .and (noControlKeys )));
191+ InputMapTemplate <GenericStyledAreaBehavior , KeyEvent > charPressConsumer = consume (keyPressed ().onlyIf (isChar .and (controlKeysFilter )));
182192
183193 InputMapTemplate <GenericStyledAreaBehavior , ? super KeyEvent > keyPressedTemplate = edits
184194 .orElse (otherNavigation ).ifConsumed ((b , e ) -> b .view .clearTargetCaretOffset ())
@@ -191,7 +201,7 @@ class GenericStyledAreaBehavior {
191201
192202 InputMapTemplate <GenericStyledAreaBehavior , KeyEvent > keyTypedBase = consume (
193203 // character input
194- EventPattern .keyTyped ().onlyIf (noControlKeys .and (e -> isLegal (e .getCharacter ()))),
204+ EventPattern .keyTyped ().onlyIf (controlKeysFilter .and (e -> isLegal (e .getCharacter ()))),
195205 GenericStyledAreaBehavior ::keyTyped
196206 ).ifConsumed ((b , e ) -> b .view .requestFollowCaret ());
197207 InputMapTemplate <GenericStyledAreaBehavior , ? super KeyEvent > keyTypedTemplate = when (b -> b .view .isEditable (), keyTypedBase );
@@ -344,16 +354,6 @@ private void keyTyped(KeyEvent event) {
344354 view .replaceSelection (text );
345355 }
346356
347- private static boolean isLegal (String text ) {
348- int n = text .length ();
349- for (int i = 0 ; i < n ; ++i ) {
350- if (Character .isISOControl (text .charAt (i ))) {
351- return false ;
352- }
353- }
354- return true ;
355- }
356-
357357 private void deleteBackward (KeyEvent ignore ) {
358358 IndexRange selection = view .getSelection ();
359359 if (selection .getLength () == 0 ) {
@@ -585,4 +585,18 @@ private static Point2D project(Point2D p, Bounds bounds) {
585585 private static double clamp (double x , double min , double max ) {
586586 return Math .min (Math .max (x , min ), max );
587587 }
588+
589+ static boolean isControlKeyEvent (KeyEvent event ) {
590+ return controlKeysFilter .test (event );
591+ }
592+
593+ private static boolean isLegal (String text ) {
594+ int n = text .length ();
595+ for (int i = 0 ; i < n ; ++i ) {
596+ if (Character .isISOControl (text .charAt (i ))) {
597+ return false ;
598+ }
599+ }
600+ return true ;
601+ }
588602}
0 commit comments