Undecorator – “Add a better look to your JavaFX stages” – Part I


Each time, when I start a new JavaFX project all begins with the following generated code:

public class UndecoratorStageDemo extends Application {

@Override
 public void start(final Stage stage) throws Exception {

Parent root = FXMLLoader.load(getClass().getResource("ClientArea.fxml"));

Scene scene = new Scene(root);

stage.setScene(scene);

stage.show();
 }

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

And the visual result is obviously the same as in the Scene Builder once launched from Netbeans:

Classic look under Windows 7

Classic look under Windows 7

The window style is inherited from the system. Since I’d like my application to have a consistent look (not mixing Windows frame decoration and JFX content) I immediately activated the “undecorated” flag of the stage and of course I’ve lost all resize, move, maximize… capabilities.
Currently using JavaFX, there’s no out of the box skin for undecorated window, so I started working on a humble implementation as a learning purpose.

UnDecorate this!

Since I’d like to reuse this work for many project, I wrote kind of an helper class to decorate the main stage. Here is the current usage:

Instead of:

Parent root = FXMLLoader.load(getClass().getResource("ClientArea.fxml";));

Scene scene = new Scene(root);

Simply add the Undecorator jar into your classpath and add Undecorator calls into your code, and set it as root of the scene:

        Parent root = FXMLLoader.load(getClass().getResource("ClientArea.fxml"));
        Undecorator undecorator = new Undecorator(stage, root);

       // Default theme
        undecorator.getStylesheets().add("skin/undecorator.css");

        Scene scene = new Scene(undecorator);

In order to add the nice Drop Shadow effect, adjust your Scene and Stage to be TRANSPARENT:

// Transparent scene and stage
 scene.setFill(Color.TRANSPARENT);
 stage.initStyle(StageStyle.TRANSPARENT);

The complete stage initialization looks like:

public class UndecoratorStageDemo extends Application {

@Override
 public void start(final Stage stage) throws Exception {

 Parent root = FXMLLoader.load(getClass().getResource("ClientArea.fxml"));
 Undecorator undecorator = new Undecorator(stage,root);
 undecorator.getStylesheets().add("skin/undecorator.css");
 Scene scene = new Scene(undecorator);

 // Transparent scene and stage
 scene.setFill(Color.TRANSPARENT);
 stage.initStyle(StageStyle.TRANSPARENT);

// Set minimum size
stage.setMinWidth(500);
stage.setMinHeight(400);

 stage.setTitle("No title bar");
 stage.setScene(scene);

 stage.show();
 }

And? UI make-up!

Simpyl as the undecorator.jar into your classpath and that’s it:

(Un)decoration!

(Un)decoration!

Looks better no :-) ?

Recipe

Four “layers”:

  1. A transparent Stage with Drop shadow effect
  2. Your Content
  3. A transparent decoration layer with all actions and menu.
  4.  A frame for resize capabilities,
Layers of the implementation

Layers of the implementation

The main interest in having layers is it allows to create”non rectangular” user interfaces such as the example below (GitHub Windows client mimic):

Ever seen this before ...

Ever seen this before …

Customize it! Play with skin/undecorator.css

Use your imagination !

.undecorator-background {
  -fx-fill: radial-gradient(focus-angle 45deg, focus-distance 20%, center 25% 25%, radius 50%, reflect, #eeeeee88, #55555588 75%, dimgray);
  -fx-arc-width:25;
  -fx-arc-height:25;
}

Result is:

roundedbordersAndRadialGradientTransparent

You also can  provide your own “stagedecoration.fxml” i.e. all buttons of the transparent decoration pane. As an example,  the built-in one is in  /insidefx/undecorator. Simply copy it, reuse the fx:id, and the built-in controller will handle actions on your buttons.

In you main class, invoke the constuctor with a third parameter:

    Undecorator undecorator = new Undecorator(stage, myClientArea, "mystagedecoration.fxml");

From classic to fancy look:

Code

Test it on your machine by clicking on this executable macJar

Access to the code, project and binaries here:github-logo-transparent

Next Enhancements

  • Bugs
  • API
  • Stage “Utility” style,
  • Win7 window behavior on desktop’s edges
  • i18n
  • Themes

Please share your feedbacks, bugs, request for enhancement in the comment section.

Thanks!

About these ads

  1. #1 by Lakatos Gyula on 04/02/2013 - 15:04

    Why this is not in jfxtras yet? :) Very nice work! When I’ll arrive at home I’ll try it asap. :)

    • #2 by arnaud nouard on 04/02/2013 - 18:39

      I still wonder why :-) !
      Thanks for your feedback so far…

      • #3 by Lakatos Gyula on 05/02/2013 - 22:43

        Hmm pretty impressive! What I would done differently: drop everything under JFX 2.2, because thats bundled with 7u6, so that’s what the big majority of people use. This way you can get rid of the Initializable interface, and the static controllers and load the fxml in the constructor instead (http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm#BABDAAHE).

      • #4 by arnaud nouard on 06/02/2013 - 08:08

        Excellent idea, I didn’t notice this new shortcut of 2.2. Refactoring in progress :-)
        Thanks.

    • #5 by fllprbt on 20/05/2014 - 07:45

      Hello!
      Thanks for the implementation!.I would like to ask you if there is any way to compute whether the screen is maximized. I am changing stages and if one is maximized getWidth/Height do not give me proper values. Any workaround to evaluate the maxWidth/Height?
      Thanks in advance

      • #6 by fllprbt on 20/05/2014 - 08:00

        Actually it is simpler than I thought in the beginning. Calculation seems ok for full screen by dragged corner,, issues appear with maximize and fullscreen button.

      • #7 by arnaud nouard on 20/05/2014 - 09:58

        Hi,
        Do you have any screen captures for me to understand the issue?
        Thanks a lot.

      • #8 by fllprbt on 20/05/2014 - 10:48

        In the first picture you can see a maximized stage:
        http://imageshack.com/a/img844/5301/hhuq.png

        Through it a new stage(mainStage) is created.

        mainStage.setX(oldStage.getX());
        mainStage.setY(oldStage.getY());
        mainStage.setHeight(oldStage.getHeight());
        mainStage.setWidth(oldStage.getWidth());

        The second picture outputs the result.
        http://imageshack.com/a/img834/7210/s68f.png

        Although in neither maximized nor full screened stages the newly created has the exact size and position, in the case of the fullscreened/maximized the new one is a little smaller.

      • #9 by arnaud nouard on 20/05/2014 - 13:24

        Hi,
        I clearly understand now. I have the same problem when invoking a new stage, e.g. a dialog, from my app. I was not able to reproduce the issue with the demo code, but only in my app.
        I still didn’t catch “who” changed the stage’s bounds. The stage width/height property do not fire any event :(.
        I’m still investigating the issue.
        Thanks.

  2. #10 by Daniel Zimmermann on 05/02/2013 - 15:21

    I just read about it on fxexperience.com – I would have needed this earlier during my “JavaFX2 learning session”…
    Whatever: Great job and hopefully it will find a way into jfxtras!?

    If I find soe time, I will definitly will play a bit around with it.
    I still hope, I can convince my company to make use of JavaFX :-p

    • #11 by Lakatos Gyula on 06/02/2013 - 09:11

      I’m also had to think a lot, because it’s not fit into my application on the way I want. (I’m pretty critical.) This is just an idea but: why not extend the Scene class to create an UndecoratedScene. This would open up a lot of possibilities, like using the scene’s stage by default, or override the set/getRoot methods (if this not collapse the underlying drawing API), so the getRoot would return the nodes inside the decoration. (Not the Undecorate class itself, like now.) It would be awesome if we can manage that a ‘new UndecoratedScene(rootnode);’ would be enough to create the undecoration effect. This is just an idea, popped out from my head when I was on the way to work, maybe it works, maybe not. What do you think?

      Also it would be cool that if I set the stage’s resizable property to false, then it would disable the resizeable effect (border). It’s not supported right now.

      Also sorry for my pretty bad english, I rarery write such a long posts. :)

      • #12 by Lakatos Gyula on 06/02/2013 - 09:12

        Ohh I replied to the wrong post, sorry! :S

    • #13 by fllprbt on 20/05/2014 - 17:35

      Well I removed the shadow & reduced the padding around the frame. This was the behavior I wanted from the beginning providing me a resizing solution as well. Thanks for the answers and the tool, it may be the start of an open source credit card manager :). It will be great if at any point you will fix the de-maximize.

      oh that’s the current form after the changes, works fine:
      http://imageshack.com/a/img841/8958/joxw.png

      see you around

      • #14 by arnaud nouard on 21/05/2014 - 09:26

        Hi,
        I think I fixed the issue, at least in my own app.
        Could you check with latest binary or source code I pushed to git?
        Thanks.

  3. #15 by http://yahoo.com on 09/02/2013 - 15:04

    • #16 by fllprbt on 21/05/2014 - 11:41

      great thanks

  4. #17 by devov on 14/02/2013 - 10:58

    Pretty cool, almost exactly what I was looking for.

    A few issues on multimonitor systems:
    -Maximize will always maximize it to the primary screen, rather than the current screen.
    -Moving the window between two screens is inconsistent. It drops the mouse drag event at the screen edges (unless you move the mouse fast enough that you don’t trigger a mouse event right on the screen edge).

  5. #19 by devov on 22/02/2013 - 02:07

    I set up something similar using regions in the scene builder.

    https://nooleanbot.wordpress.com/

    • #20 by arnaud nouard on 22/02/2013 - 20:15

      Nice approach Devov and thanks for the reference in your blog.

  6. #21 by NooleanBot on 25/02/2013 - 02:27

    I was trying to set some custom closing code to execute for different stages using:

    stage.setOnCloseRequest(new EventHandler() {
    @Override
    public void handle(WindowEvent evt) {
    System.out.println(“testing the close operation”);
    }
    });

    In order to fire that event you have to use:

    stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));

    rather than stage.close() or stage.hide() for window closing.

    Not sure if that is the real close operation you’re looking for, or if there is a better way to do it.

    It is nice to be able to do when you want to prompt the user to save, or disconnect from resources, or store the window size and location preferences.

    Thanks for all the updates.

    • #22 by arnaud nouard on 25/02/2013 - 07:18

      Excellent. The window listeners was on my TODO list. I’ll try to test and add this ASAP, as well as the fullscreen and stage icons.
      Thanks!

  7. #23 by Aksel on 08/04/2013 - 09:39

    Hi, very nice exmaple. May i ask if undecorator.jar is available in some maven repo or maybe as binary?

    • #24 by arnaud nouard on 08/04/2013 - 17:48

      Hi,
      Thanks for the feedback. You can take a look at the code and the executable jar by clicking on the image links you will find in he post (GitHub).
      Thanks

  8. #25 by munesh meena on 06/05/2013 - 11:39

    its nice and helpful ..

  9. #26 by Damin on 27/06/2013 - 15:10

    Hello
    I have to say that your idea is amazing! I was wondering if your code is completely open source and if I can use it in a commercial application?
    Thanks for a reply!

    • #27 by arnaud nouard on 27/06/2013 - 15:26

      Hi Damin,
      Thanks a lot for your feedback.
      Feel free to use it for any kind of application, commercial or not, and keep me informed if you can :).
      Thanks.

  10. #28 by douglasparedes on 04/07/2013 - 01:04

    I wanna know how to add many scene. I mean , with a button I want to go to another scene and so

    • #29 by arnaud nouard on 04/07/2013 - 23:33

      Hi,
      The problem is that the Scene and the Stage are linked in the Undecorator scenario (i.e. 1:1). The scene becomes a window with decorations and the Stage becomes transparent.
      So, for your needs you probably have to customize the Undecorator class to support multiple Scene by keeping the decoration layer the same for all different Scenes.
      Hope this helps
      Thanks.

  11. #30 by douglasparedes on 06/07/2013 - 02:29

    wow that could be hard to me hehe , could you make a version to do that?

    • #31 by arnaud nouard on 11/07/2013 - 10:00

      If you directly use the Undecorator class, not the UndecoratorScene, you should be able to do what you need.
      Example:

      public class UndecoratorStageDemo extends Application {
       
      @Override
       public void start(final Stage stage) throws Exception {
       
       Parent root = FXMLLoader.load(getClass().getResource("ClientArea.fxml"));
       Undecorator undecorator = new Undecorator(stage,root);
       undecorator.getStylesheets().add("skin/undecorator.css");
       Scene scene = new Scene(undecorator);
       
       // Transparent scene and stage
       scene.setFill(Color.TRANSPARENT);
       stage.initStyle(StageStyle.TRANSPARENT);
       
      // Set minimum size
      stage.setMinWidth(500);
      stage.setMinHeight(400);
       
       stage.setTitle("No title bar");
       stage.setScene(scene);
       
       stage.show();
       }

      In that scenario the Scene is no longer managed by the Undecorator, so you can switch between your different Scene.
      BTW, I still don’t understand why you change your Scene using a Button instead of changing the Root node of the Scene.
      Thanks.

  12. #32 by Joern on 15/07/2013 - 11:46

    Hi Arnaud!

    First of all, thank you a lot for 4-Layer recipe. I was working on my own on a similar solution. And I think, you can remove the shadow layer completely, by using padding (insets) for the “root”-node of the client-layer. This will create a space (padding) between the stage and the scene. To now drop a shadow, simply use CSS for the root-node.

    For example:
    .root {
    -fx-border-style: none;
    -fx-effect: dropshadow(gaussian,rgba(0,0,0,0.5), 30, 0, 0, 15);
    }

    With this approach, your awesome framework could maybe become even more simpler I hope.

    Best regards,
    Jörn

    • #33 by arnaud nouard on 17/07/2013 - 23:02

      Thanks a lot Joern for your feedback and advice.
      As far as I remember, I’ve seen this technique in the Ensemble demo set.
      But for managing the shadow for focused, maximized and fullscreen mode I don’t see how to handle that from a css value compared to programmatic approach.
      If you have further details, I’m still interesting :).
      Thanks so far!

  13. #34 by Mario on 19/01/2014 - 16:20

    Hey insideFX. I’m a great fan of your Undecorator. I made a plugin for resizing stages comfortably and just had the idea to make a fourth button next to minize which could popup my ScreenFX arranger. just look here: http://vmario89.github.io/ScreenFX/. Greetings, Mario

  1. Java desktop links of the week, February 4 | Jonathan Giles
  2. JavaFX links of the week, February 4 // JavaFX News, Demos and Insight // FX Experience

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: