This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

JavaFX + Android + Live Preview

We had a lot of fun this week - with JavaFX :)

My last article was about JavaFX on mobile devices with JavaFXPorts. It was awesome that JavaFX worked without bigger problems on mobile devices but the number of needed re-deployments was high. The app build process needs some time (~2-3 mins) and if you do a lot of UI related changes in your application (CSS, layout, ...) you lose development time.

How to save deployment time?

Simply don't deploy!

We have our product VisionX and it has a Live Preview for applications while developing. Our idea was that we could use the existing functionality to show changes on mobile devices immediately. It should work with Android devices because dynamic class loading is possible with custom classloaders.

But as so often, things won't work as expected.

Android has support for URLClassLoader but it doesn't work because the implementation is "legacy" . This means that loading classes with URLClassLoader didn't work. Android has a DexClassLoader!

The DexClassLoader works great if you have the archive on the device. But it has no support for remote class loading. The other thing was that DexClassLoader needs a jar that contains a classes.dex archive. The classes.dex contains all class files (in dex format).

The first problem was: How to create the DEX archive?

Not too hard because Android comes with dx.bat and dx.jar. It's super easy to convert a jar file or a classes folder to a dex archive. After the hardest part was solved, it was simple to implement remote class loading with Android, for our JavaFX application! If you need more information, leave a comment!

Here's the result of our Live Preview experiment as Video:

Window shutter web control

It works great and reduces deployments to a bare minimum: 1

JVx' JavaFX UI with JavaFXPorts

A few hours ago, we published a screenshot from our Demo ERP application running on Nexus 9 (an Android tablet).
It wasn't a fake!

Before we'll show you more screenshots, we want say that we've reached a long awaited goal!

Use JVx and write an application for all polpular platforms and devices.
This is real "write once run anywhere".

Create an application for Windows, Linux, Mac, iOS, or Android, PS4, OUYA or WiiU. Run it as desktop application, as html5 application in web browsers, as native iOS or Android application or as JavaFX application on Android and iOS devices. There's nothing like JVx in the world - not with only one framework with such a small footprint as JVx and certainly not Open Source!

Here are the screenshots:

Maximized frame with charts

Maximized frame with charts

Frame with tree

Frame with tree

Also in portrait mode:

Portrait mode

Portrait mode


And now a very strange one. It's the application with our styled stage. It's crazy because you can drag the window around (makes no sense, but it just works):

Styled stage with tabset desktop

Styled stage

Sure, the application wasn't optimized for mobile devices. It has the same style as the desktop application. The mobile version should have bigger frame buttons and a bigger frame header :) To be honest - MDI on mobile devices? We were positively surprised about the usability. But, whatever. You have the option and decide what's best for your application (but please don't use MDI for smartphones).

Are you interested in some details?

We've used Netbeans IDE because there's a very useful plugin from gluon. We're Eclipse users but it was easier to use a preconfigured environment without doing everything manually. It was simple to configure Netbeans with gluon plugin and there's a video for your first steps. A "bigger" problem was gradle because the project was a gradle project and we've never used gradle. We love using ant.

We had a bad start with Hello World application because it didn't work. There were different problems: Missing ANDROID_HOME or DEX compiler problems. After reading the documentation it was possible to start compilation. The DEX problem was very strange because we had JVM 1.8 u31 and JVM 1.8 u40 configured in Netbeans, but Netbeans didn't use our configured JVM 1.8 u40. We removed the JVM 1.8 u31 from Netbeans and after that we successfully compiled our Hello World application. Very strange!

The next step was using JVx as library dependency for our project, but how did this work with gradle? We had the library in the libs directory of the project, in the file system. We didn't use a maven lib because it was easier to replace the library after recreation. We tried to find a solution in the official gradle documentation and found Chapter 51. C'mon gradle is a build tool and not a programming language! So much documentation means: complexity. Sure, ant wasn't the holy grale but it it's simple to understand and doesn't hide anything.

Our current gradle script:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.0.0-b7'

    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
}

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    runtime fileTree(dir: 'libs', include: '*.jar')
}

mainClassName = 'com.sibvisions.gluon.JVxGluonApp'
   
jfxmobile {
   
    ios {
        forceLinkClasses = [ 'com.sibvisions.gluon.**.*' ]
    }
   
    android {
      androidSdk = 'C:/tools/android/android-sdk_r04-windows'
      compileSdkVersion = 21  
    }
}

It's short but we ddin't know what happened behind the scenes without additional research. But if it works it's good enough for many developers.

The integration of JVx was simple because JVx was compiled with JVM 1.6. The Dalvik VM or DART do support 1.6 and 1.7 syntax. It was cool to see that JVx and especially the communication worked without problems - Great job from Johan Vos and all people behind JavaFXPorts!

Our next step was the integration of additional dependencies like our JavaFX UI. It was based on JVM 1.8 u40 and heavily used lambdas. But lambda expressions weren't supported from Dalvik or ART. We thought that gluon plugin solves the problem for us, but it didn't. It contains retrolambda but only for project source files and it doesn't touch your dependencies. There was no additional gradle build task for that, so we had to convert the libs manually. Not a problem but you must be aware of it.

After we solved all dependency problems, we had the first version of our desktop ERP running on our android device. But don't think everything was trouble-free. We had problems with method references, Java Preferences because of write problems, new Date API, forEach calls, scene size and much more. We did solve most problems but not all - yet.

Charts with JVx and JavaFX

In my last article about JavaFX, JVx and data binding you saw a first impression of our chart binding. We have additional screenshots for you. The first two images show a Swing application with different charts. The next two images show the same application, with JavaFX. Which UI do you like more?

Bar chart with Swing

Bar chart with Swing

Line chart with Swing

Line chart with Swing

Bar chart with JavaFX

Bar chart with JavaFX

Line chart with JavaFX

Line chart with JavaFX

The JavaFX UI is clean and fresh. It looks modern compared to Swing. A desktop application should look modern and not dusty!

How about the data binding?

Our screen has two charts. Every chart gets its records from a data book (our model class). The labels and values are dynamic and every change in the databook updates the chart. Here's the source code for the chart definition:

//BAR chart
UIChart chartBar.setChartStyle(UIChart.STYLE_BARS);
chartBar.setTitle("Annual Statistics");
chartBar.setXAxisTitle("Year");
chartBar.setYAxisTitle("Grosstotalprice");
chartBar.setDataBook(rdbV_statistic_order_offer_year);
chartBar.setXColumnName("YEAR_");
chartBar.setYColumnNames(new String[] {"OFFER_GROSSTOTALPRICE", "ORDER_GROSSTOTALPRICE"});

//LINE chart
UIChart chartLineArea.setChartStyle(UIChart.STYLE_LINES);
chartLineArea.setTitle("Monthly Statistics");
chartLineArea.setXAxisTitle("Month");
chartLineArea.setYAxisTitle("Grosstotalprice");
chartLineArea.setDataBook(rdbV_statistic_order_offer_month);
chartLineArea.setXColumnName("MONTH");
chartLineArea.setYColumnNames(new String[] { "OFFER_GROSSTOTALPRICE", "ORDER_GROSSTOTALPRICE" });

The complete source code is available on sourceforge. It's from our Demo ERP application.

The databooks are remote databooks because our Demo ERP is a 3-tier application. The databook definition:

//BAR Chart
RemoteDataBook rdbV_statistic_order_offer_year = new RemoteDataBook();
rdbV_statistic_order_offer_year.setName("v_statistic_order_offer_year");
rdbV_statistic_order_offer_year.setDataSource(getDataSource());
rdbV_statistic_order_offer_year.setReadOnly(true);
rdbV_statistic_order_offer_year.open();

//LINE Chart
RemoteDataBook rdbV_statistic_order_offer_month = new RemoteDataBook();
rdbV_statistic_order_offer_month.setName("v_statistic_order_offer_month");
rdbV_statistic_order_offer_month.setDataSource(getDataSource());
rdbV_statistic_order_offer_month.setReadOnly(true);
rdbV_statistic_order_offer_month.setMasterReference(
                                      new ReferenceDefinition(new String[] { "YEAR_" },
                                                              rdbV_statistic_order_offer_year,
                                                              new String[] { "YEAR_" }));
rdbV_statistic_order_offer_month.open();

Both remote databooks are connected to a remote storage, identified by name (v_statistic_order_offer_year, v_statistic_order_offer_month). The storages are defined as server objects:

//Name: v_statistic_order_offer_year (BAR CHART)
DBStorage dbsV_statistic_order_offer_year = new DBStorage();
dbsV_statistic_order_offer_year.setWritebackTable("v_statistic_order_offer_year");
dbsV_statistic_order_offer_year.setDBAccess(getDBAccess());
dbsV_statistic_order_offer_year.open();

//Name: v_statistic_order_offer_month (LINE CHART)
DBStorage dbsV_statistic_order_offer_month = new DBStorage();
dbsV_statistic_order_offer_month.setWritebackTable("v_statistic_order_offer_month");
dbsV_statistic_order_offer_month.setDBAccess(getDBAccess());
dbsV_statistic_order_offer_month.open();

Both storages read data from database views (instead of tables). The views are not magic and could also be simple tables, but our Demo ERP has views for demonstration purposes.

Here's the definition of the view, used for the BAR chart:

CREATE OR REPLACE VIEW v_statistic_order_offer_year AS
SELECT ofe.YEAR_ AS YEAR_,
       sum(ofe.GROSSTOTALPRICE) AS OFFER_GROSSTOTALPRICE,
       sum(case when ord.GROSSTOTALPRICE IS NULL
                then 0
                else ord.GROSSTOTALPRICE
           end) AS ORDER_GROSSTOTALPRICE
  FROM (v_statistic_offer ofe
        LEFT JOIN v_statistic_order ord
               ON (((ofe.YEAR_ = ord.YEAR_) AND (ofe.MONTH = ord.MONTH))))
 GROUP BY ofe.YEAR_;

That's it. It's really simple to bind a JavaFX chart to a database table or view.

Style vaadin menubar popups

Our new application frame has two different vaadin menubars. One for the application menu and one as "hidden" menubar for the user options.

Here's a merged screenshot of both menubars:

Menubar popup

Menubar popup

As you can see, the left popup is dark and the right popup is bright. Usually, vaadin doesn't allow custom "additional" style definitions for menubar popups. The default style name is:

v-menubar-popup

The name is defined in the class com.vaadin.shared.ui.menubar.MenuBarState as

primaryStyleName = "v-menubar"

If you want to add an additional style class, it's simply not possible because the client implementation (com.vaadin.client.ui.VMenuBar) sets the primary style name, from the menubar, as style name for the popup overlay. The only thing you can do is, to change the primary stylename of your menubar. The problem is that you have to define all styles for the menubar-popup and menuitems, submenus, ... again in your style file. We tried it but it was horrible because you can't easily change the theme.

We tried to find a solution without changing vaadin client. We tried to set a primary style name as e.g. customstylepopup v-window-popup. But the client menu implementation has following code in getPrimaryStyleName:

protected static String getStylePrimaryName(Element elem) {
  String fullClassName = getStyleName(elem);

  // The primary style name is always the first token of the full CSS class
  // name. There can be no leading whitespace in the class name, so it's not
  // necessary to trim() it.
  int spaceIdx = fullClassName.indexOf(' ');
  if (spaceIdx >= 0) {
    return fullClassName.substring(0, spaceIdx);
  }
  return fullClassName;
}

(it makes sense to do this!)

So, it wasn't possible with simple tricks. The only solution - we found - was an extension of VMenuBar to override getPrimaryStyleName and return a custom style class concatenated with the default style class, something like this:

@Override
public String getStylePrimaryName()
{
    if (popupStyle != null)
    {
        return popupStyle + " " + super.getStylePrimaryName();
    }
       
    return super.getStylePrimaryName();
}

It wasn't our preferred solution, but we had another hack for setting ids of menu items. So it wasn't hard to add another hack for the style name :)

With our modification, it'll be possible to add custom style classes to menubar popups:

Custom style names

Custom style names

The source code is available on sourceforge.

ARC Welder - what else :)

Some days ago, ARC Welder popped up in the news. I guess it was Thursday?

What is ARC? (see Getting started with ARC)

The App Runtime for Chrome (Beta), or ARC, lets you run your favorite Android apps on Chrome OS.

and also in your Chrome browser.

It's strange, but a logical consequence.

We tried our native Android client with ARC Welder and it was working without bigger problems:

ARC Welder - JVx Android Client

ARC Welder - JVx Android Client

It wasn't possible to use the integrated MAPS API, maybe a problem of the beta version or our build?

I'm not sure if it's useful to run an android app on a desktop because the app was designed for mobile devices, usually. But as test environment...

But it could be interesting for JavaFX applications which were converted to Android apps? Not really for desktop pcs, but for Chromebooks.
While Applets will be banned from browsers, ARC could be something similar :)

It's still strange but we'll be ready for it.

JavaFX, JVx and data binding

We made great progress with our JavaFX UI for JVx. The whole UI stuff was already implemented and worked with some known problems. The next thing was data binding because this should fill the gap between UI and business logic. This means: bindings for a table, tree, chart, editors, cell editors, cell renderers, ... (hard work).

The data controls of JavaFX were very useful and we didn't implement our own controls because standard controls were working. Sure, we had a different model in JVx than the "model" in JavaFX, but also Swing and vaadin had different models. We're experts in such things.

We started with the table, because a table implementation should be easy without CRUD operations and of course, it was easy. The next thing was the integration of our cell renderers and cell editors because a date field should automatically use a date editor and a number field should use a number(only) editor. The same applies for checkboxes and comboboxes. This was a hard job because JavaFX' table had different concepts than e.g. swing. We're still working on the last bits but most functionality was implemented.

It's already possible to bind a database table/view to a JavaFX TableView without additional JDBC, ORM. Thanks to JVx, this works with all supported architectures (mem, 2-tier, 3-tier). Here's a first impression, followed by the source code:

TableView bound to JVx' DataBook

TableView bound to JVx' DataBook

UITable table = new UITable();
table.setDataBook(dataBook);

UIFormLayout editorsPaneLayout = new UIFormLayout();
editorsPaneLayout.setNewlineCount(2);

UIPanel editorsPane = new UIPanel();
editorsPane.setLayout(editorsPaneLayout);

addEditor(editorsPane, dataBook, "ID");
addEditor(editorsPane, dataBook, "BOOLEAN");
addEditor(editorsPane, dataBook, "STRING");
addEditor(editorsPane, dataBook, "CHOICE");
addEditor(editorsPane, dataBook, "DATETIME");
addEditor(editorsPane, dataBook, "NUMBER");
addEditor(editorsPane, dataBook, "TYPE_ID");
addEditor(editorsPane, dataBook, "TYPE_NAME");

UISplitPanel splitPanel = new UISplitPanel(UISplitPanel.SPLIT_LEFT_RIGHT);
splitPanel.setDividerAlignment(UISplitPanel.DIVIDER_BOTTOM_RIGHT);
splitPanel.setFirstComponent(table);
splitPanel.setSecondComponent(editorsPane);

UIPanel content = new UIPanel();
content.setLayout(new UIBorderLayout());
content.add(splitPanel, UIBorderLayout.CENTER);

(see Kitchensink application, hosted on github)

This was the whole source code for the table binding (= UI). The missing piece is the model. In our example, we've used the member dataBook. A databook is the model and controller of JVx. We have different implementations: MemDataBook, RemoteDataBook. A mem is like a database table, but in memory. A remote databook is connected to a remote/local storage. The storage provides data (from a database, filesystem, twitter, ...).

We didn't use a database in our kitchensink, so the dataBook was defined as MemDataBook:

RowDefinition rowdef = new RowDefinition();            
rowdef.addColumnDefinition(new ColumnDefinition("ID",
                                new BigDecimalDataType()));
rowdef.addColumnDefinition(new ColumnDefinition("STRING",
                                new StringDataType(new UITextCellEditor())));
rowdef.addColumnDefinition(new ColumnDefinition("BOOLEAN",
                                new BooleanDataType(new UICheckBoxCellEditor(
                                                    Boolean.TRUE, Boolean.FALSE))));
rowdef.addColumnDefinition(new ColumnDefinition("CHOICE",
                                new StringDataType(choiceCellEditor)));
rowdef.addColumnDefinition(new ColumnDefinition("DATETIME",
                                new TimestampDataType(new UIDateCellEditor("dd.MM.yyyy"))));
rowdef.addColumnDefinition(new ColumnDefinition("NUMBER",
                                new BigDecimalDataType(new UINumberCellEditor())));
rowdef.addColumnDefinition(new ColumnDefinition("TYPE_ID",
                                new BigDecimalDataType()));
rowdef.addColumnDefinition(new ColumnDefinition("TYPE_NAME",
                                new StringDataType(
                                    new UILinkedCellEditor(referenceDefinition))));
               
IDataBook dataBook = new MemDataBook(rowdef);
dataBook.setName("DATABINDING");               
dataBook.open();

It's the definition of a table with some columns and different column types, like String, Date, Number, ComboBox. It's easy to use a real database table if you read following article or this one.

The difference

What's the difference to standard JavaFX without JVx and why should you use JVx?

Here's the official documentation of Table View from Oracle. It's very complex to work with tables ;-) (seems to be so). The example in the official documentation doesn't use a database, like our example above!

The first difference: Save time and LoC.
This will reduce complexity and saves your dev time. It'll be easier to maintain an application with 1.000 LoC instead of 50.000.

Second difference: JVx already is a framework and library - don't create your own and waste dev time.

Advantage of JVx: Simply bind a database table with 10 LoC:

DBAccess dba = DBAccess.getDBAccess("jdbc:hsqldb:hsql://localhost/testdb");
dba.setUsername("sa");
dba.setPassword("");
dba.open();

DBStorage dbs = new DBStorage();
dbs.setDBAccess(dba);
dbs.setWritebackTable("testtable");
dbs.open();

StorageDataBook dataBook = new StorageDataBook(dbs);
dataBook.open();

This is enough to get full CRUD support for your tables.

This article was the first intro of our databinding implementation for JavaFX. The next artile will cover our chart implementation. Here's first a impression:

FX Chart binding

FX Chart binding

We love the clean UI of JavaFX and our maximized internal frames :)

Vaadin application frame design

Many of you probably know our application frame, for web applications: One application, different styles

We had a MDI variant for legacy applications with internal frames for screens. This is useful for applications with many screens and many records. The internal frames help to compare records from one screen with records from other screens.

For modern user interfaces, we use the SDI option. It looks fresh and simple and is great for applications with a small number of screens, because a scrolling menu is not nice - sure it will scroll if needed.

But what about application with e.g. 60 screens. Is MDI the only option? Yes and No, because we didn't have a variant for that kind of applications.... but now we have.

It's a mix of MDI and SDI, with a little bit CI:

Application frame SDI with menu

Application frame SDI with menu

The application frame is still the same as before, but with a different option. The name of the option is corporation, because the UI is meant for applications with many screens. It's still SDI with modal popups, because it shouldn't be too legacy!

Oh, do you see the small buttons on top/right? We use the FontAwesome feature of vaadin 7.2. We're already using vaadin 7.4.2 for our JVx vaadin UI!

We now have one application frame for many different application styles (I don't talk about visual attributes). The application frame is responsive and works well on mobile devices.

JavaFX FlowPane vs. FXFluidFlowPane

We're still working on our JavaFX UI implementation for JVx. Some weeks ago, we worked with standard JavaFX layouts (layout panes) like BorderPane and FlowPane. Both layouts are useful but don't work like BorderLayout or FlowLayout from AWT/Swing. There are small differences, e.g.

It was possible to resize a BorderLayout to any size. The BorderPane checks minimum and maximum size of its nodes and doesn't resize if bounds were reached. Sure, that's useful in theory but bad in practice because the content of a screen should always resize to the screen size (e.g internal frames).
The requirement wasn't hard to implement. We now have our own FXBorderPane which has its own min. and max. calculation.

The standard BorderPane was very useful but the standard FlowPane wasn't, because it has bigger problems/limitations:

  • Overlapping of Wrapped FlowPanes with other nodes
    Overlapping

    Overlapping

  • Size calculation depends on prefWrapLength if not stretched to full-size (BorderPane, SplitPane, ...). This means that the pane doesn't grow automatically if the parent has enough space.
    ABC

    Width calculation

  • The FlowPane doesn't support alignment of managed nodes
    Standard FlowPane (centered nodes)

    Standard FlowPane (centered nodes)

    but should:

    fluid flow pane (bottom aligned)

    Fluid flow pane (bottom aligned)

    fluid flow pane (stretched)

    Fluid flow pane (stretched)

We solved all problems with our FXFluidFlowPane because our applications won't work with standard FlowPane.

In JVx applications, we have more than two layouts. The most common layout is our FormLayout. We already have JavaFX implementations for all JVx layouts, like FXFormPane or FXNullPane.

Here's screenshot of our FXFormPane test application:

Form Pane

Form Pane

JavaFX, JVx, CalendarFX and Exchange Server

It's friday, and it's (still) sunny :)

Some days ago, CalendarFX was released. It's a commercial product and looks promising. Now and then, we play around with new commercial products/libraries because our dev teams should know which product will work in commercial projects. A calendar control is always useful and especially if you organize "something". Many ERP products do this.

In good old swing applications, we did use migcalendar for better UX and visualization. But it's not available for JavaFX, so we tried CalendarFX.

The control is still in an early development stage and has some bugs or missing APIs, but it's very polished and works great with JVx and our JavaFX UI:

JVx, JavaFX UI and CalendarFX

JVx, JavaFX UI and CalendarFX

We tried to implement a simple JavaFX calendar screen, for Outlook appointments. We already had a connector for Exchange servers, based on EWS Java API and our JVx' storage implementation.

The screen code was simple, and more or less a simply copy/paste of a CalendarFX tutorial application. Here it is:

private void initializeUI() throws ModelException
    {
        CalendarView calendarView = new CalendarView();
       
        Calendar work = new Calendar("Work");
        Calendar home = new Calendar("Home");

        work.setStyle(Style.STYLE1);
        home.setStyle(Style.STYLE2);

        CalendarSource calSources = new CalendarSource("Private");
        calSources.getCalendars().addAll(work, home);

        calendarView.getCalendarSources().addAll(calSources);

        calendarView.setRequestedTime(LocalTime.now());

        Thread updateTimeThread = new Thread("Calendar: Update Time Thread")
        {
            @Override
            public void run()
            {
                while (true)
                {
                    Platform.runLater(() ->
                    {
                        calendarView.setToday(LocalDate.now());
                        calendarView.setTime(LocalTime.now());
                    });

                    try
                    {
                        // update every 10 seconds
                        sleep(10000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }

                }
            };
        };

        Thread thLoadData = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    RemoteDataBook rdb = new RemoteDataBook(apps);
                    rdb.setDataSource(getDataSource());
                    rdb.setName("calendar");
                    rdb.open();
                    rdb.fetchAll();
                   
                    ZonedDateTime zdt;
                   
                    for (int i = 0; i < rdb.getRowCount(); i++)
                    {
                        IDataRow row = rdb.getDataRow(i);
                       
                        Entry entry = new Entry(row.getValueAsString("SUBJECT"));
                       
                        zdt = ((Date)row.getValue("BEGIN")).toInstant().
                                     atZone(ZoneId.systemDefault());
                       
                        entry.setStartDate(zdt.toLocalDate());
                        entry.setStartTime(zdt.toLocalTime());
                       
                        if (((Boolean)row.getValue("ALLDAY")))
                        {
                            entry.setFullDay(true);
                            entry.setEndDate(entry.getStartDate());
                        }
                        else
                        {
                            zdt = ((Date)row.getValue("END")).toInstant().
                                         atZone(ZoneId.systemDefault());                                                        
                            entry.setEndDate(zdt.toLocalDate());
                            entry.setEndTime(zdt.toLocalTime());
                        }
                       
                        if (((Boolean)row.getValue("PRIVATE")))
                        {
                            Platform.runLater(() -> home.addEntry(entry));
                        }
                        else
                        {
                            Platform.runLater(() -> work.addEntry(entry));
                        }
                    }
                }      
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
       
        thLoadData.start();
        updateTimeThread.start();
       
        ((FXInternalWindow)getResource()).setContent(calendarView);
       
        setTitle("Calendar FX Demo");
    }

The storage definition for fetching appointments was trivial:

AppointmentsStorage apps = new AppointmentsStorage();
apps.setURL(new URI("<url>"));
apps.setUserName("<username>");
apps.setPassword("<password>");
apps.open();

Not more code!

The special thing in our screen was the integration of a custom control. Usually, we would integrate it like this:

add(new UICustomComponent(calendarView));

but our custom component integration for JavaFX UI wasn't ready. No problem, because it's always possible to access the real UI resource in JVx. So we did:

((FXInternalWindow)getResource()).setContent(calendarView);

and everything was as expected. Sure, the screen won't work with Swing because we made a direct access to a JavaFX resource, but this was not relevant for our test :)

JavaFX: Styled stage and MDI system

I'm happy to show you a first screenshot of our new Stage style and our MDI system:

Scene styling and MDI

Scene style and MDI

Compared to the standard stage:

Standard scene and MDI

Standard scene and MDI

Sure, the default stage is OK, but if you want to style the whole application, it won't work with standard stage. If you want a unique style, you need a custom solution. Our style is part of our JavaFX UI for JVx and already available in our repository at sourceforge.

The MDI system is already stable. It can be styled via css and works similar to JDesktopPane and JInternalFrame of Swing. Most problems were solved and we use the implementation in our dev projects.