How to detect keys


Question

I want to create table in which I want to configure hot key shortcuts.

I have this simple table:

    public static final String Column1MapKey = "A";
    public static final String Column2MapKey = "B";

    private ObservableList<Map> generateDataInMap() {
        int max = 110;
        ObservableList<Map> allData = FXCollections.observableArrayList();
        for (int i = 1; i < max; i++) {
            Map<String, String> dataRow = new HashMap<>();

            String value1 = "A" + i;
            String value2 = "B" + i;

            dataRow.put(Column1MapKey, value1);
            dataRow.put(Column2MapKey, value2);

            allData.add(dataRow);
        }
        return allData;
    }

public TabPane hotKeysContent(){

        TableColumn<Map, String> firstDataColumn = new TableColumn<>("Actions");
        TableColumn<Map, String> secondDataColumn = new TableColumn<>("Shortcut");

        firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey));
        firstDataColumn.setMinWidth(230);
        secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey));
        secondDataColumn.setMinWidth(230);

        TableView table_view = new TableView<>(generateDataInMap());
        table_view.setPadding(new Insets(5, 5, 5, 5));

        table_view.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);  // Autoresize when window size is changed

        table_view.setEditable(true);
        table_view.getSelectionModel().setCellSelectionEnabled(true);
        table_view.getColumns().setAll(firstDataColumn, secondDataColumn);
        Callback<TableColumn<Map, String>, TableCell<Map, String>>
            cellFactoryForMap = new Callback<TableColumn<Map, String>,
                TableCell<Map, String>>() {
                    @Override
                    public TableCell call(TableColumn p) {
                        return new TextFieldTableCell(new StringConverter() {
                            @Override
                            public String toString(Object t) {
                                return t.toString();
                            }
                            @Override
                            public Object fromString(String string) {
                                return string;
                            }                                    
                        });
                    }
        };
        firstDataColumn.setCellFactory(cellFactoryForMap);
        secondDataColumn.setCellFactory(cellFactoryForMap);

        return null;
    }

I want when I click on a row into the second column to get the combination of keys which I will press and later to use these keys to activate keyboard shortcuts. Any example will be helpful.

P.S Table with the commands:

public static final String Column1MapKey = "A";
    public static final String Column2MapKey = "B";

    private ObservableList<Map> generateDataInMap() {
        int max = 110;
        ObservableList<Map> allData = FXCollections.observableArrayList();
        for (int i = 1; i < max; i++) {
            Map<String, String> dataRow = new HashMap<>();

            String value1 = "A" + i;
            String value2 = "B" + i;

            dataRow.put(Column1MapKey, value1);
            dataRow.put(Column2MapKey, value2);

            allData.add(dataRow);
        }
        return allData;
    }

    public TabPane hotKeysContent(){

        TabPane tabPane = new TabPane();
        //tabPane.setStyle("-fx-font-size: 13pt;"); // Set size of the tab name

        Tab tabA = new Tab();
        Label tabALabel = new Label("Shortcuts");
        //tabALabel.setStyle("-fx-font-size: 12pt;"); // Set size of the tab name
        tabA.setGraphic(tabALabel);
        tabA.setClosable(false); // da se mahne opciqta da se zatvarq tab


        TableColumn<Map, String> firstDataColumn = new TableColumn<>("Actions");
        TableColumn<Map, String> secondDataColumn = new TableColumn<>("Shortcut");

        firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey));
        firstDataColumn.setMinWidth(230);
        secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey));
        secondDataColumn.setMinWidth(230);

        TableView table_view = new TableView<>(generateDataInMap());
        table_view.setPadding(new Insets(5, 5, 5, 5));

        table_view.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);  // Autoresize when window size is changed

        table_view.setEditable(true);
        table_view.getSelectionModel().setCellSelectionEnabled(true);
        table_view.getColumns().setAll(firstDataColumn, secondDataColumn);
        Callback<TableColumn<Map, String>, TableCell<Map, String>>
            cellFactoryForMap = new Callback<TableColumn<Map, String>,
                TableCell<Map, String>>() {
                    @Override
                    public TableCell call(TableColumn p) {
                        return new TextFieldTableCell(new StringConverter() {
                            @Override
                            public String toString(Object t) {
                                return t.toString();
                            }
                            @Override
                            public Object fromString(String string) {
                                return string;
                            }                                    
                        });
                    }
        };
        firstDataColumn.setCellFactory(cellFactoryForMap);
        secondDataColumn.setCellFactory(cellFactoryForMap);

        tabA.setContent(table_view);                
        tabPane.getTabs().add(tabA);       

        return tabPane;
    }
1
4
9/23/2013 8:41:55 PM

Accepted Answer

Below is a SSCCE, though there is no tableView no column or cell. However the logic is similar. Examine it for clues and write your own code according to your needs:

public class ShortCutDemo extends Application {

    private KeyEvent shortcutKeyEvent;
    private EventHandler selectedEventHandler;
    private List<EventHandler> eventHandlers;
    private HBox root;

    @Override
    public void start(Stage primaryStage) {

        root = new HBox(10);
        root.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                // Do not filter for TextFields
                if (event.getTarget() instanceof TextField) {
                    return;
                }
                if (isKeyEventsAreEqual(event, shortcutKeyEvent)) {
                    // then apply shortcut event
                    selectedEventHandler.handle(null);
                    event.consume();
                }
            }
        });

        eventHandlers = new ArrayList<EventHandler>();
        eventHandlers.add(new EventHandler() {
            @Override
            public void handle(Event event) {
                root.setStyle("-fx-background-color: lightgray");
            }
        });

        eventHandlers.add(new EventHandler() {
            @Override
            public void handle(Event event) {
                root.setSpacing(50);
            }
        });

        ChoiceBox cb = new ChoiceBox();
        cb.getItems().addAll("HBox background = gray", "HBox spacing = 50");
        cb.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                selectedEventHandler = eventHandlers.get(newValue.intValue());
            }
        });
        cb.getSelectionModel().selectFirst(); // default value

        final TextField textField = new TextField();
        textField.setEditable(false);

        textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.TAB) {
//                    Platform.runLater(new Runnable() {
//                        @Override
//                        public void run() {
//                            root.requestFocus();
//                        }
//                    });
                } else {
                    // Clear the previous text
                    textField.setText("");
                    // Process only desired key types
                    if (event.getCode().isLetterKey()
                            || event.getCode().isDigitKey()
                            || event.getCode().isFunctionKey()) {
                        String shortcut = event.getCode().getName();
                        if (event.isAltDown()) {
                            shortcut = "Alt + " + shortcut;
                        }
                        if (event.isControlDown()) {
                            shortcut = "Ctrl + " + shortcut;
                        }
                        if (event.isShiftDown()) {
                            shortcut = "Shift + " + shortcut;
                        }
                        textField.setText(shortcut);
                        shortcutKeyEvent = event;
                    } else {
                        shortcutKeyEvent = null;
                    }
                }
            }
        });

        Button button = new Button("Reset");
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                textField.setText("");
                root.setSpacing(10);
                root.setStyle("-fx-background-color: white");
                shortcutKeyEvent = null;
            }
        });

        root.getChildren().addAll(new Label("Define a shortcut for "), cb, textField, button);
        Scene scene = new Scene(root, 900, 150);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private boolean isKeyEventsAreEqual(KeyEvent event1, KeyEvent event2) {
        return event1 != null
                && event2 != null
                && event1.getCode() == event2.getCode()
                && event1.isAltDown() == event2.isAltDown()
                && event1.isControlDown() == event2.isControlDown()
                && event1.isShiftDown() == event2.isShiftDown();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

How it works?
1) Select an action (event) type from choicebox.
2) Focus to Textfield and type the desired shortcut, Alt+F1 for example.
3) Press Tab to focus out from TextField.
4) Press the shortcut (Alt+F1 in this case) again to see the event in action.
5) Press "Reset" button to reset the state surely.

The further improvement should be defining a model class, that have ShortCut key, booleans for Ctrl, Alt and Shift, eventhandler to process and appropriately overriden equals(...) method.

8
9/23/2013 1:34:24 PM

Here's something that should get you started. For simplicity I only handled ctrl and alt meta keys and only one column, but it should be obvious how to continue. My approach was to use a TextFieldTableCell with a custom onKeyPressed listener that listens for key shortcuts and translates them into KeyCharacterCombinations.

Note that this example is not very complete or robust. For example, single characters are not handled properly. Standard shortcuts (Ctrl-C, Ctrl-V, etc) are not overridden. You will probably have to implement your own TableCell class so you can fully control what text is typed.

public class KeyCombinationTableExample extends Application {

    public static void main(String[] args) {
        launch(KeyCombinationTableExample.class);
    }

    @Override
    public void start(Stage stage) throws Exception {
        final ObservableList<KeyCharacterCombination> items = FXCollections.observableArrayList();
        for (int i = 0; i < 110; i++) {
            items.add(null);
        }
        TableView<KeyCharacterCombination> table = new TableView<>(items);
        table.setEditable(true);
        final TableColumn<KeyCharacterCombination, KeyCharacterCombination> column = new TableColumn<>();
        column.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<KeyCharacterCombination, KeyCharacterCombination>, ObservableValue<KeyCharacterCombination>>() {
                                       @Override
                                       public ObservableValue<KeyCharacterCombination> call(TableColumn.CellDataFeatures<KeyCharacterCombination, KeyCharacterCombination> cellDataFeatures) {
                return new ReadOnlyObjectWrapper<>(cellDataFeatures.getValue());
        }
        });
        column.setCellFactory(new Callback<TableColumn<KeyCharacterCombination, KeyCharacterCombination>, TableCell<KeyCharacterCombination, KeyCharacterCombination>>() {
                                  @Override
                                  public TableCell<KeyCharacterCombination, KeyCharacterCombination> call(TableColumn<KeyCharacterCombination, KeyCharacterCombination> tableColumn) {
                final TableCell<KeyCharacterCombination, KeyCharacterCombination> cell = new TextFieldTableCell<KeyCharacterCombination, KeyCharacterCombination>() {
                    @Override
                    public void updateItem(KeyCharacterCombination keyCharacterCombination, boolean b) {
                        super.updateItem(keyCharacterCombination, b);
                        if (this.getItem() == null || b) {
                            setText(null);
                        } else {
                            StringBuilder sb = new StringBuilder();
                            if (keyCharacterCombination.getControl() == KeyCombination.ModifierValue.DOWN) {
                                sb.append("Ctrl + ");
                            }
                            if (keyCharacterCombination.getAlt() == KeyCombination.ModifierValue.DOWN) {
                                sb.append("Alt + ");
                            }
                            sb.append(keyCharacterCombination.getCharacter());
                            setText(sb.toString());
                        }
                    }
                };
                cell.setOnKeyPressed(new EventHandler<KeyEvent>() {
                    Set<KeyCombination.Modifier> keys = new HashSet<>();

                    @Override
                    public void handle(KeyEvent keyEvent) {
                        if (keyEvent.getCode() == KeyCode.CONTROL) {
                            keys.add(KeyCombination.CONTROL_DOWN);
                        } else if (keyEvent.getCode() == KeyCode.ALT) {
                            keys.add(KeyCombination.ALT_DOWN);
                        } else if (keyEvent.getCode().isLetterKey()) {
                            items.set(cell.getIndex(), new KeyCharacterCombination(keyEvent.getCode().getName(),
                                    keys.toArray(new KeyCombination.Modifier[keys.size()])));
                            keys.clear();
                        }
                    }
                });
                return cell;
        }
        });
        table.getColumns().add(column);
        stage.setScene(new Scene(table));
        stage.show();
    }
}

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon