Отключить выпадающий список ComboBox при нажатии F4 - PullRequest
2 голосов
/ 25 октября 2019

В настоящее время JavaFX предоставляет функцию выпадающего списка со списком при нажатии F4. Мы хотим отключить эту функцию и обработать другие функции для F4. Сначала я подумал, что это довольно просто. Моя идея состоит в том, что я добавлю фильтр ключевых событий и использую его при нажатии клавиши F4.

Но, к сожалению, это не сработало !! После исследования я заметил, что в ComboBoxPopupControl есть часть кода для обработки ключевого события, которая установлена ​​как KeyEvent.ANY filter . Странно то, что они потребляют событие после показа / скрытия.

Часть кода выглядит следующим образом:

private void handleKeyEvent(KeyEvent ke, boolean doConsume) {
        // When the user hits the enter or F4 keys, we respond before
        // ever giving the event to the TextField.
        if (ke.getCode() == KeyCode.ENTER) {
            setTextFromTextFieldIntoComboBoxValue();

            if (doConsume && comboBoxBase.getOnAction() != null) {
                ke.consume();
            } else {
                forwardToParent(ke);
            }
        } else if (ke.getCode() == KeyCode.F4) {
            if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
                if (comboBoxBase.isShowing()) comboBoxBase.hide();
                else comboBoxBase.show();
            }
            ke.consume(); // we always do a consume here (otherwise unit tests fail)
        }
    }

Это делает меня совершенно беспомощным, так как теперь я больше не могу контролировать эту часть цепочки событий, просто потребляя фильтры / обработчики. Ни один из приведенных ниже фильтров не помог мне перестать показывать выпадающий список.

comboBox.addEventFilter(KeyEvent.ANY, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});
comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});
comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
    if (e.getCode() == KeyCode.F4) {
        e.consume(); // Didn't stopped showing the drop down
    }
});

Единственный способ остановить это - использовать событие для его родителя и не разрешать делегировать его в ComboBox. Но это определенно накладные расходы, поскольку в приложении уже есть десятки ComboBox (ов) и еще много.

У меня вопрос: почему они реализовали функцию, которая тесно интегрирована, не позволяя пользователю отключитьэто?

Есть ли альтернатива, которую я могу реализовать на уровне ComboBox, чтобы перестать показывать / скрывать раскрывающийся список при нажатии клавиши F4.

Я попробовал приведенный ниже подход, чтобы заставить его работать. Но я не уверен, насколько я могу положиться на решение на основе временной шкалы: (

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ComboBoxF4_Demo extends Application {
    Timeline f4PressedTimeline = new Timeline(new KeyFrame(Duration.millis(100), e1 -> {
    }));

    @Override
    public void start(Stage stage) throws Exception {
        HBox root = new HBox();
        root.setSpacing(15);
        root.setPadding(new Insets(25));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 600, 600);
        stage.setScene(scene);

        final ComboBox<String> comboBox = new ComboBox<String>() {
            @Override
            public void show() {
                if (f4PressedTimeline.getStatus() != Animation.Status.RUNNING) {
                    super.show();
                }
            }
        };
        comboBox.setItems(FXCollections.observableArrayList("One", "Two", "Three"));
        comboBox.addEventFilter(KeyEvent.ANY, e -> {
            if (e.getCode() == KeyCode.F4) {
                if (e.getEventType() == KeyEvent.KEY_RELEASED) {
                    f4PressedTimeline.playFromStart();
                }
            }
        });

        // NONE OF THE BELOW FILTERS WORKED :(
        /*comboBox.addEventFilter(KeyEvent.ANY, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        comboBox.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        comboBox.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
            if (e.getCode() == KeyCode.F4) {
                e.consume(); // Didn't stopped showing the drop down
            }
        });
        */
        root.getChildren().addAll(comboBox);
        stage.show();
    }
}

1 Ответ

2 голосов
/ 25 октября 2019

Как упомянуто @kleopatra в комментариях к вопросу, потребление события не останавливает его распространение в пределах одного и того же "уровня" той же фазы. Другими словами, все фильтры событий, зарегистрированные с ComboBox (для EventType и его супертипов), все равно будут уведомлены, даже если один из них использует событие. Кроме того, существует проблема изменения поведения элементов управления по умолчанию, которое может быть неожиданным и недооцененным конечными пользователями.

Если вы по-прежнему хотите изменить поведение элемента управления и не обнаружите, что оно потребляет событиена предке удовлетворительно, вы можете перехватить событие в пользовательском EventDispatcher вместо фильтра событий:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {

  @Override
  public void start(Stage primaryStage) {
    var comboBox = new ComboBox<String>();
    for (int i = 0; i < 20; i++) {
      comboBox.getItems().add("Item #" + i);
    }
    comboBox.getSelectionModel().select(0);

    var oldDispatcher = comboBox.getEventDispatcher();
    comboBox.setEventDispatcher((event, tail) -> {
      if (event.getEventType() == KeyEvent.KEY_RELEASED
          && ((KeyEvent) event).getCode() == KeyCode.F4) {
        return null; // returning null indicates the event was consumed
      }
      return oldDispatcher.dispatchEvent(event, tail);
    });

    primaryStage.setScene(new Scene(new StackPane(comboBox), 500, 300));
    primaryStage.show();
  }

}
...