2017年9月24日日曜日

テキストエディタをJavaで作成する計画3

さてさて、今回はテキストエディタの「新規作成」機能と「閉じる」機能を実装します。

よく考えたら新規作成にもいろんなパターンがありますね。
①新しいウィンドウが開く
②新しいタブが作成される
③作業中のファイルを閉じてからそのまま開く

個人的には①が好きなので、この方法で実装します。



新しいウィンドウが開く処理はダイアログの表示でも使えるはずなので、どちらにしても作れるようになっておきたい部分です。

結論から言うと、JavaFXで新しくウィンドウを作るのはかなり大変な作業でした。
これはStageクラスの使い方がある程度理解が必要なことと、[閉じる]ボタンを押したときに全ウィンドウ閉じられたり、別のウィンドウが閉じられたりするなど、色々予定外のバグがでました。主にスレッド関係が原因のようでした。

また、ネットの情報は断片的でテキストエディタを作るとなると自分で考えないと無理でした。
と言うことで、ここから先については僕のオリジナルな部分が多いので、設計の良し悪しは不明です。動作確認では一応、違和感なく動作しました。

【ProcessManagerクラス】このクラスのmainメソッドが初めに実行されます。残念なことにApplicationクラスのサブクラスである必要がありました。
public class ProcessManager extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        createTextEditer();
    }

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

    /**
     * 新しいテキストエディタを作成します。
     */
    public void createTextEditer() {
        try {
            new TextEditer01().start(new Stage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 引数で指定されたウィンドウを非表示にします。
     * @param scene 終了対象の画面
     */
    public void terminateTextEditer(Scene scene) {
        try {
            scene.getWindow().hide();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

【TextEditer01クラス】ProcessManagerクラスから呼ばれます。普通はこのクラスのプロパティにSceneがありますが、イベントからSceneをうまく取得できなかったので、代替案としてSceneをFXMLDocumentControllerクラスに持っていき、FXMLに紐づいているコントローラの情報を保持しておきます。
public class TextEditer01 extends Application {

    private FXMLDocumentController controller;
    private ProcessManager pm = new ProcessManager();

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
        Parent root = fxmlLoader.load();
     
        controller = (FXMLDocumentController) fxmlLoader.getController();
        controller.setScene(new Scene(root));
     
        stage.setScene(controller.getScene());
        stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                event.consume();
                try {
                    pm.terminateTextEditer(controller.getScene());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        stage.show();
    }
}


【FXMLDocumentControllerクラス】Sceneをプロパティにするとイベントとうまく紐付けできました。
public class FXMLDocumentController implements Initializable {

    private ProcessManager pm;
    private Scene scene;

    /**
     * 新しいウィンドウを表示します。
     * @param event
     */
    @FXML
    private void newAction(ActionEvent event) {
        pm.createTextEditer();
    }

    /**
     * MenuItem[閉じる] イベントが発生したウィンドウを非表示にします。
     * @param event
     */
    @FXML
    void closeAction(ActionEvent event) {
        pm.terminateTextEditer(getScene());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        pm = new ProcessManager();
    }

    public Scene getScene() {
        return scene;
    }

    public void setScene(Scene scene) {
        this.scene = scene;
    }


こんな感じで「新規作成」と「閉じる」が実装できました。
次は「開く」を追加します。

0 件のコメント:

コメントを投稿