FXCalendar

FXCalendar is a calendar widget that I wrote in JavaFX script. I mainly did it to get a grasp of the language and to familiarize myself with its features. Since it's my first try, and also because I don't have that much experience in writing Java GUI's, I believe the code's probably not as good as it could be. Nevertheless, it was a good learning experience and I did have fun writing it. JavaFX uses a scene graph to render its GUI elements. This is different from the Model/View/Controller pattern that Swing uses. Also, JavaFX uses a declarative syntax for its GUI, which is different from the traditional imperative approach (though both can be used). Here's an example of a "Hello World" program in both styles:

Declarative Syntax:

import javafx.ext.swing.*;

SwingFrame {
   title: "Hello World!";
   width: 100;
   height: 50;
   content: Label {
               text: "Hello World!";
            }
   visible: true;
}

Traditional Syntax:

import javafx.ext.swing.*;

var myFrame:SwingFrame = new SwingFrame();
var myLabel:Label = new Label();

myLabel.text = "Hello World!";
myFrame.width = 200;
myFrame.height = 50;
myFrame.visible = true;
myFrame.content = myLabel;

The declarative style is a lot more intuitive than the traditional approach, and as the complexity of the GUI increases, much easier to read. Though the intent of JavaFX is to create RIA's (Rich Internet Applications), you can use it to do other stuff as well. The language has a bunch of neat features like incremental/lazy evaluation through binding, list comprehensions, triggers, and closures. Also, since JavaFX is built on top of Java, you can easily use Java objects. For example:

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;
import java.text.DateFormatSymbols;
import javafx.ext.swing.*;

var calendar:Calendar = new GregorianCalendar();
var today:Date = new Date();
var dfs:DateFormatSymbols = new DateFormatSymbols();
var monthNames = dfs.getMonths();

calendar.setTime(today);

SwingFrame {
   title: "Today's Date";
   width: 200;
   height: 50;
   content: Label {
               text: "Today is " + monthNames[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.DATE) + 
                     ", " + String.valueOf(calendar.get(Calendar.YEAR));
            }
   visible: true;
}

As you can see, you can easily embed Java objects in your JavaFX code. I won't concentrate much on describing the language right now, because there's quite a bit to go into. If you're interested, you can take a look here for an introduction to the language, and here for the preview API. Anyway, so here's a screenshot of the widget running on Firefox 3 on my Ubuntu laptop (the theme is a Leopard theme):

Calendar widget


The code for that widget is pretty simple:

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;
import java.text.DateFormatSymbols;
import java.lang.System;
import javafx.application.Application;
import javafx.application.Stage;
import javafx.scene.*;
import javafx.scene.geometry.Rectangle;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.effect.*;

var widgetWidth = 400;
var widgetHeight = 350;
var row = 0;

var calendar = new GregorianCalendar();
var today = new Date();
var dfs = new DateFormatSymbols();
calendar.setTime(today);

var todaysDate = calendar.get(Calendar.DATE);
var monthNames = dfs.getMonths();
var daysOfTheWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

Application {
   stage: Stage {
             content: 
                Group {
                   id: "layout"
                   content: [ 
                      Rectangle { 
                         x: 0 y: 0 width: widgetWidth height: widgetHeight arcWidth: 50 arcHeight: 50;
                         fill: LinearGradient {
                                 startX: 0 startY: 0 endX: 0 endY: 1
                                 stops: [
                                          Stop { offset: 0 color: Color.rgb(255,255,255,0.8)},
                                          Stop { offset: 1 color: Color.rgb(0,0,0,0.8) },
                                 ]
                              }
                      },

                      Rectangle { 
                         x: 0 y: 0 width: widgetWidth height: widgetHeight arcWidth: 50 arcHeight: 50;
                         stroke: Color.web("#555");
                         fill: LinearGradient {
                                  startX: 0 startY: 0 endX: 0 endY: 1;
                                  stops: [
                                           Stop { offset: 0 color: Color.web("#b3b3b3") },
                                           Stop { offset: 0.08 color: Color.web("#555555") },
                                           Stop { offset: 0.25 color: Color.web("#131313") },
                                           Stop { offset: 0.75 color: Color.web("#131313") },
                                           Stop { offset: 0.95 color: Color.web("#181818") },
                                           Stop { offset: 1 color: Color.web("#434343") }
                                 ];
                              }
                      },

                      Text {
                         y:15;
                         x:15;
                         textOrigin: TextOrigin.TOP; 
                         content: monthNames[calendar.get(Calendar.MONTH)];
                         font: Font { name: "Arial" size: 20 style: FontStyle.BOLD };
                         fill: Color.web("#FFFFFF");
                         smooth: true;
                         visible: true;
                      },

                      Text {
                         y:15;
                         x:widgetWidth - 20;
                         textOrigin: TextOrigin.TOP;
                         font: Font { name: "Arial" size: 20 style: FontStyle.BOLD };
                         content: String.valueOf(calendar.get(Calendar.YEAR));
                         fill: Color.web("#FFFFFF");
                         smooth: true;
                         visible: true;
                         horizontalAlignment: HorizontalAlignment.RIGHT;
                      },

                      for(dayName in daysOfTheWeek) {

                         Text {
                            y: 50;
                            x: 55 * (indexof dayName + 1) - 22;
                            textOrigin: TextOrigin.TOP;
                            font: Font { name: "Arial" size:15 style: FontStyle.BOLD };
                            content: dayName;
                            fill: Color.web("#FFFFFF");
                            smooth: true;
                            visible: true;
                            horizontalAlignment: HorizontalAlignment.CENTER;
                         }
                      },

                      for(date in [1..calendar.getActualMaximum(Calendar.DAY_OF_MONTH)]) {
                         calendar.set(Calendar.DATE, date);

                         //We don't want to jump to the next row if the 1st is a Sunday 
                         if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY and date != 1) {
                            row++;
                         }

                         var dateText = Text {
                                           y:(40 * row) + 90;
                                           x:55 * calendar.get(Calendar.DAY_OF_WEEK) - 20;
                                           textOrigin: TextOrigin.TOP;
                                           font: Font { name: "Arial" size:15 style: FontStyle.BOLD };
                                           content: String.valueOf(date);
                                           fill: Color.web("#CCCCCC");
                                           smooth: true;
                                           visible: true;
                                           horizontalAlignment: HorizontalAlignment.CENTER;
                                        };                         

                         if(todaysDate == calendar.get(Calendar.DATE)) {
                            dateText.fill = Color.web("#FFFFFF");
                         }

                         //In JavaFX (similar to Groovy) the return-value of a block is the result of
                         //the last evaluated expression

                         dateText;
                     } 
                   ]
                }
          }
}

I know it kinda looks like a "death by braces" thing, but I found it a whole lot easier to picture the GUI in my mind using the declarative syntax. I also think it looks a whole lot simpler than the Swing equivalent. Notice that the API provides you a bunch of nifty things like linear gradients. There's a bunch of other stuff in the API too, including support for animations, effects, and transformations. Now for the widget itself. You can embed the widget in a page by using the <applet> tag. So in a sense, JavaFX applications are just applets, but way better. Thought it may seem like Sun is arriving really late to the game by trying to compete with Flash and Silverlight, I think the fact that JavaFX is built on Java and thus has the support of a large community, and a rich API, gives JavaFX a promising future. The only problem I see right now is the pretty ridiculous (first-time) load-time for the widget. It has a few JavaFX jars to load, which is probably why this is happening. But like I mentioned earlier, this is still a previow so I'm pretty sure Sun will iron out all the kinks by the time 1.0 rolls out. Oh yeah, the widget. Here it is:


Pretty neat, huh? If you're running the latest version of the JRE (JRE 6 update 10, available here) you should be able to drag the applet out of the browser window and onto the desktop by holding down the ALT key while you drag. I hope you found this demo interesting. I'm going to keep playing around with the language to learn more, and if I come up with anything neat, I'll be sure to put it up here.

2 comments:

  1. On friday, september 12, 2008 at 1:30 PM UTC, sheehan said:

    i haven't gotten a chance to mess with JavaFX but how is the load time of the application? One thing I never liked about Java is how heavily the VM taxes your resources. Performance and usability is key when it comes to client-side applications...does it promise?
  2. On monday, september 15, 2008 at 8:21 AM UTC, vivin said:

    Sheehan,

    The load time on the application is pretty high right now. To run a simple application you have to download a series of jars (mainly javafx jars). I assume this is because the whole language is still in preview (the language reference itself is still in draft). I would think that they will be ironing out these issues once the actual release comes out. Well, I'd go so far as saying that they will have to address the load-times if they want to compete with Flash or Silverlight.

    It's true that the VM uses a bunch of resources but I think with processor speed and memory being where they're at today, it really isn't much of an issue. A lot of people say Java is ten times slower than C. But then C is ten times slower than assembly. So far, I would say performance is probably on par with regular Java (javafx is compiled into Java bytecode), but the load-times aren't all that great. In spite of all this, I would still say that JavaFX looks pretty promising.

Leave a comment

Name:
Website:
Email:  Yes, I would like to receive emails when this blog is updated
 
Bold Italic Underline Create Link Insert Email Address Insert Line of Code Insert Code Snippet Insert Quote Insert Dialogue
Comment:
To prove that you are not a script, please enter the text from the image, into the textbox.
If you are unable to read the text, click the image to get a new picture.


captcha
                                 Live Preview
All images and text on this site: Copyright © Vivin Suresh Paliath. All Rights Reserved.