2013-05-20 37 views
6

JavaFX TextAreaを複数行のTextFieldとまったく同じように使用したいと考えています。言い換えると、[Tab]を押すと、フォーム上の次のコントロールに移動したいと思います。[Enter]を押すと、Key.Eventを(TextAreaで消費されるのではなく)defaultButtonコントロールに移動します。JavaFX 2 TextArea:[Enter]と[Tab]の使用を中止する方法

TextAreaのデフォルトの動作は、[Tab]がTextAreaに挿入され、[Enter]が改行文字を挿入することです。

私は、私が望む動作を得るためにEventFiltersを使用する必要があることを知っていますが、それはすべて間違っています。私は、TextAreaがこれらのイベントを消費するのを望んでいません...私はただ彼らが "すぐに行こう"ようにしたいと思います。

答えて

8

解決策は、2つのテキスト領域とデフォルトボタンを表示します。 ユーザーがタブキーを押すと、フォーカスが次のコントロールに移動します。 ユーザーがEnterキーを押すと、デフォルトのボタンが起動します。この動作を達成するために

:各テキスト領域の

  1. Enterキーを押します(デフォルトOKボタンが含まれている)テキスト領域の親ノードにコピーされ、ターゲットに、イベントフィルタでキャッチされています。これにより、フォームの任意の場所でEnterキーが押されたときに、デフォルトの「OK」ボタンが押されます。元のエンターキーを押すと、テキストエリアのテキストに新しい行が追加されないように消費されます。
  2. 各テキスト領域のタブキーがフィルタでキャッチされ、親のフォーカストラバーサルリストが処理され、次にフォーカス可能なコントロールが見つけられ、そのコントロールにフォーカスが要求されます。元のタブキーを押すと、テキスト領域のテキストに新しいタブスペースが追加されないように消費されます。

コードはJava 8で実装されている機能を利用しているため、実行するにはJava 8が必要です。

textareahandler

import javafx.application.Application; 
import static javafx.application.Application.launch; 
import javafx.beans.value.*; 
import javafx.collections.ObservableList; 
import javafx.event.*; 
import javafx.scene.*; 
import javafx.scene.control.*; 
import static javafx.scene.input.KeyCode.ENTER; 
import static javafx.scene.input.KeyCode.TAB; 
import javafx.scene.input.KeyEvent; 
import javafx.scene.layout.VBox; 
import javafx.stage.*; 

public class TextAreaTabAndEnterHandler extends Application { 
    final Label status = new Label(); 

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

    @Override public void start(final Stage stage) { 
    final TextArea textArea1 = new TabAndEnterIgnoringTextArea(); 
    final TextArea textArea2 = new TabAndEnterIgnoringTextArea(); 

    final Button defaultButton = new Button("OK"); 
    defaultButton.setDefaultButton(true); 
    defaultButton.setOnAction(new EventHandler<ActionEvent>() { 
     @Override public void handle(ActionEvent event) { 
     status.setText("Default Button Pressed"); 
     } 
    }); 

    textArea1.textProperty().addListener(new ClearStatusListener()); 
    textArea2.textProperty().addListener(new ClearStatusListener()); 

    VBox layout = new VBox(10); 
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;"); 
    layout.getChildren().setAll(
     textArea1, 
     textArea2, 
     defaultButton, 
     status 
    ); 

    stage.setScene(
     new Scene(layout) 
    ); 
    stage.show(); 
    } 

    class ClearStatusListener implements ChangeListener<String> { 
    @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { 
     status.setText(""); 
    } 
    } 

    class TabAndEnterIgnoringTextArea extends TextArea { 
    final TextArea myTextArea = this; 

    TabAndEnterIgnoringTextArea() { 
     addEventFilter(KeyEvent.KEY_PRESSED, new TabAndEnterHandler()); 
    } 

    class TabAndEnterHandler implements EventHandler<KeyEvent> { 
     private KeyEvent recodedEvent; 

     @Override public void handle(KeyEvent event) { 
     if (recodedEvent != null) { 
      recodedEvent = null; 
      return; 
     } 

     Parent parent = myTextArea.getParent(); 
     if (parent != null) { 
      switch (event.getCode()) { 
      case ENTER: 
       if (event.isControlDown()) { 
       recodedEvent = recodeWithoutControlDown(event); 
       myTextArea.fireEvent(recodedEvent); 
       } else { 
       Event parentEvent = event.copyFor(parent, parent); 
       myTextArea.getParent().fireEvent(parentEvent); 
       } 
       event.consume(); 
       break; 

      case TAB: 
       if (event.isControlDown()) { 
       recodedEvent = recodeWithoutControlDown(event); 
       myTextArea.fireEvent(recodedEvent); 
       } else { 
       ObservableList<Node> children = parent.getChildrenUnmodifiable(); 
       int idx = children.indexOf(myTextArea); 
       if (idx >= 0) { 
        for (int i = idx + 1; i < children.size(); i++) { 
        if (children.get(i).isFocusTraversable()) { 
         children.get(i).requestFocus(); 
         break; 
        } 
        } 
        for (int i = 0; i < idx; i++) { 
        if (children.get(i).isFocusTraversable()) { 
         children.get(i).requestFocus(); 
         break; 
        } 
        } 
       } 
       } 
       event.consume(); 
       break; 
      } 
     } 
     } 

     private KeyEvent recodeWithoutControlDown(KeyEvent event) { 
     return new KeyEvent(
      event.getEventType(), 
      event.getCharacter(), 
      event.getText(), 
      event.getCode(), 
      event.isShiftDown(), 
      false, 
      event.isAltDown(), 
      event.isMetaDown() 
     ); 
     } 
    } 
    } 
} 

代替ソリューションは、新しいキー処理動作を含んTextAreaの独自のカスタマイズスキンを実装することであろう。そのようなプロセスは、ここに提示された解決策よりも複雑になると私は信じています。私はこの問題への私の元のソリューションのように、実際にはなかった

更新

ことの一つは、消費されたタブ回またはキーを入力し、それらのデフォルト処理をトリガーする方法はありませんでしたということでした。そこで、TabキーまたはEnterキーを押したときにコントロールキーを押したままにすると、デフォルトのTabキーまたはEnterキーが実行されるように、ソリューションを更新しました。この更新されたロジックを使用すると、CTRL + EnterまたはCTRL + Tabを押して、テキスト領域に新しい行またはタブスペースを挿入することができます。

+0

素晴らしい優雅なソリューションです。これを投稿する時間をとってくれてありがとう。 – scottb

+1

非常に良い。私の意見では、TextArea APIにデフォルトの処理プロパティがあるはずです。私はTextAreasがデフォルトの動作としてEnterとTabを処理する方法は、多くの状況で望ましくないと信じています。あなたのソリューションは、賢明なものですが、現在は広く利用されておらず、長時間のクライアントコードが必要です。 – scottb

+0

@scottb [JavaFX issue tracker](https://javafx-jira.kenai.com)にこの動作のJavaFXプラットフォーム機能リクエストを記録することができます。 – jewelsea

関連する問題