Thursday, July 19, 2012

MatrixPanel, a new JavaFX UI control. First part

Hi, this is my first JavaFX post, so don't be too cruel on me, please!

In this two part series post, I'd like to share with you the work I've been doing on a new JavaFX 2.x control, in close collaboration with Gerrit Grunwald (@hansolo_, http://www.harmonic-code.org/). 

In fact, the control is based in his awesome well-known Gauge's collection, which recently he has upgraded from Swing to JavaFX 2.x. 

It is a mix of a Linear gauge and an extension on his DotMatrixSegment control which, as you may know, are available at the JFXtras project.

So let me introduce you the brand new MatrixPanel control.

For those anxious to find out what is this about, if the name isn't quite self-explanatory, here you have a pic:

As you can see, the control is based on a rectangular matrix of Circles (JavaFX nodes), each one acting as a LED, so the control is a digital version of those LED displays you can find everywhere nowadays.

So its main features are:
  • Create any size of matrix (width and height)
  • Monochromatic or color (3 tones of R, G & B) panel
  • Display both text (several dotted fonts) or images (in BMP format)
  • Creation of several areas within the panel to display independent content in each one of them
  • Animation effects of the content of each area. Adjustable speed, and pause time between next effect is displayed.
  • Rotation of contents in the same area.
To go in full detail with all the properties involved in the creation of a MatrixPanel control, let's have a look at the code to build it, using the proper builder class,  MatrixPanelBuilder:

   private final MatrixPanel panelDemo = 
          MatrixPanelBuilder.create()
                            .ledWidth(140)
                            .ledHeight(40)
                            .prefWidth(760.0)
                            .prefHeight(760.0)
                            .frameDesign(Gauge.FrameDesign.DARK_GLOSSY)
                            .frameVisible(true)
                            .contents(new Content[] {
                                new ContentBuilder().create()
                                    .color(Content.MatrixColor.RED)
                                    .type(Content.Type.TEXT)
                                    .origin(0, 10)
                                    .area(0, 0, 140, 40)
                                    .txtContent("MATRIXPANEL")
                                    .font(Content.MatrixFont.FF_10x16)
                                    .fontGap(Content.Gap.DOUBLE)
                                    .align(Content.Align.CENTER)
                                    .effect(Content.Effect.NONE)
                                    .postEffect(Content.PostEffect.STOP)
                                    .order(Content.RotationOrder.SINGLE)
                                    .build()
                            })
                            .build();
And you can load it in a JavaFX scene simply by:

public void start(Stage primaryStage) {
        primaryStage.setTitle("MatrixPanel Demo");

        StackPane root = new StackPane();
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);
        root.getChildren().add(panelDemo);
        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.centerOnScreen();
        primaryStage.show();
    }

Basically, first you have to set the number of LED columns  (ledWidth) and rows (ledHeigth) and the size of the control in the scene. In order to keep the LEDs shape as circles, one dimension (prefWidth or prefHeight) will be recalculated, so you don't need to worry, and just set the maximum space available within the scene for the control. Then you can select the frame design (DARK_GLOSSY or GLOSSY_METAL, for now), or simply hide it.


And now we deal with the contents to display. You can add as many contents as you want, creating each one by its builder class ContentBuilder.


First you choose the color: RED, GREEN, BLUE or RGB, and then you set the content type: IMAGE or TEXT

For text content, these are the four colors available:
For bmp images, three tones of each color are considered, so the bmp rawdata is filtered accordingly. Let's load this 32x32 bmp:
in four areas to show you the four possibilities of coloring:
The image is loaded with the option bmpName, which requieres the bmp name of the image if it is locally stored in resources, or a full external path and its name, otherwise.

For those of you disappointed with the poor performance of the LEDs matrix style, let me tell you that first I used css styling for each circle, with gradients and inner shadows. But there were serious CPU issues loading the panel and applying animation effects. So following Gerrit's advice I removed css styling and provided the same effects by code. Although there were some improvements, the best solution was to remove any effect, and just leave a simple filling of each circle with the proper color. Anyway, as the size of the LEDs decrease, the difficult to appreciate any lack of shadowing effect increase.

You may have noticed that the former matrixpanels contained four areas, so let's talk about origin and area options. An area is defined by its left-top and right-bottom LED coordinates. You shouldn't overlap differents areas. The origin is the left-top LED coordinate where the content will start being displayed. By LED coordenate I mean number of LEDs from the left and the top, instead of pixels.


For text content, by using the option txtContent you set the string of text. It will be displayed in one line. If it's wider than the visible area, you should use a scrolling effect to visualice it completely, as we'll see in the second part of this post. 

Then you have to choose a font. For now there is a bunch of proportional dotted fonts, with the usual ASCII range of printable characteres (A to Z, a to z, 0 to 9, +, -, *, /, \,...) available.  The next matrixpanel shows their name, which stands for width and height of each character:

The fontGap option allows to insert a column of 0 (NONE), 1 (SIMPLE) or 2 (DOUBLE) leds between each character. And the align option allows LEFT, CENTER or RIGHT justification for the text in its area.

To end this first part of the post, let me just finish with effect option set to NONEpostEffect option set to STOP and order option set to SINGLE. This means the content will be static, no further action will be done after been displayed and no other content will supply it.

The next post will be dedicated to animation, so stay tuned!

Meanwhile, any comment or suggestion will be really appreciated!!

Finally, thank you Gerrit, for your inspiring work.

1 comment: