FX GlassFish Monitorの解説 (見映え編)

Java The Nightのデモでお見せしたFX GlassFish Monitorの作りについての解説です。まずは反響が大きかった見映えのところから説明したいと思います。

一番目に付いたのはウィンドウ枠だと思います。OSのウィンドウ枠は全く見えず、周囲が何か光っていますね。
JavaFXではコンポーネントにドロップシャドウエフェクトを追加することができ、今回もそれを利用しているのですが、OSのウィンドウ枠に相当する Stage クラスには適用することはできません。

そこで次のような方法で実現しました。

  • Stage 及び Scene は透明にする。
  • その上に、ドロップシャドウエフェクトを効かせた Rectangle を貼り付ける。
    • この Rectangle の大きさは Scene よりシャドウの幅の分だけ小さくする。
  • その上にレイアウトコンテナ (BorderPaneAnchorPane) を貼り付ける。

図示するとこんな感じです。

メインウィンドウ (監視項目のツリーが並ぶウィンドウ) について、Stage の上に Scene を組み立てている部分のJavaコードと、FXML のコードを示しておきます。

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("view/MainView.fxml"));
        loader.load();
        Parent root = loader.getRoot();
        MainViewController controller = loader.getController();
        controller.setParentStage(stage);
        Scene scene = new Scene(root, 924, 700, Color.TRANSPARENT);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.show();
    }
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="924.0" xmlns:fx="http://javafx.com/fxml" fx:controller="aoetk.fxglassfishmonitor.view.MainViewController">
  <children>
    <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#1e90ff00" height="690.0" stroke="$x1" strokeType="INSIDE" width="914.0">
      <effect>
        <DropShadow blurType="GAUSSIAN" spread="0.7">
          <color>
            <Color blue="0.878" green="1.000" red="0.000" fx:id="x1" />
          </color>
        </DropShadow>
      </effect>
    </Rectangle>
    <BorderPane fx:id="containerPane" onMouseDragged="#handleMouseDragged" onMousePressed="#handleMousePressed" prefHeight="200.0" prefWidth="200.0" styleClass="container">
      <center>
        <ScrollPane prefHeight="200.0" prefWidth="200.0">
          <content>
            <Pane fx:id="drawRegion" prefHeight="200.0" prefWidth="200.0" />
          </content>
        </ScrollPane>
      </center>
      <top>
        <HBox fx:id="boxTitle" prefHeight="50.0" prefWidth="200.0">
          <children>
            <Text fill="$x1" fontSmoothingType="LCD" stroke="WHITE" strokeType="OUTSIDE" strokeWidth="0.0" text="FX GlassFish Monitor" HBox.hgrow="NEVER">
              <font>
                <Font name="System Bold Italic" size="28.0" />
              </font>
            </Text>
            <Region prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
            <Button fx:id="btnExit" mnemonicParsing="false" onAction="#handleBtnExitAction" styleClass="close-button" text="Exit" HBox.hgrow="NEVER" />
          </children>
          <padding>
            <Insets bottom="20.0" left="10.0" right="10.0" top="10.0" />
          </padding>
        </HBox>
      </top>
      <StackPane.margin>
        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
      </StackPane.margin>
    </BorderPane>
  </children>
  <stylesheets>
    <URL value="@../asset/style.css" />
  </stylesheets>
</StackPane>

Rectangle とレイアウトコンテナ (BorderPane) の貼り合わせには StackPaneを使っています。
StackPane にマージンを指定していて、その上に置いたコンテンツがドロップシャドウの幅の分だけ隙間を空けるようにしています。

BorderPane の onMousePressed と onMouseDragged にイベントハンドラを設定しています。これは Stage を透明にしたために、そのままではウィンドウ枠が消えてウィンドウの移動ができなくなるため、自分でドラッグ移動のイベントを実装しているのです。

スタイルシートはこんな感じです。

.root {
    -fx-base: #212020;
}

.container {
    -fx-background-color: rgba(0, 0, 0, 0.7);
}

.close-button {
    -fx-border-color: white;
    -fx-border-width: 2px;
    -fx-border-radius: 5px;
    -fx-background-color: transparent;
    -fx-text-fill: white;
    -fx-font-size: 16px;
    -fx-cursor: hand;
}

container クラスがレイアウトコンテナに適用するもので、色を黒で透明度を 70% に設定しているだけです。
close-button クラスはExitボタンに適用したスタイルで、背景を透明にしてあのような見た目にしているのが分かりますね。

と言うわけで見映えについての解説でした。大して手数を掛けずに実現可能だと言うことが分かりますね。

コード全体についてはもう少し整理してからGithubのURLを公開する予定です。もうしばらくお待ちを。

おまけ

解説に使った図はScene Bulderを使って描きました。が、やっぱりお絵かきにはまだまだ機能不足ですね。Rectangle を平行四辺形に変形する部分は直接FXMLを手打ちしました...。