JavaFX Missing Features Survey: Table View

The TableView (and TreeTableView) of JavaFX has won the prize for being the control that was mentioned the most in my recent “JavaFX Missing Features” survey and also in many follow-up discussions (including and especially the guys in our Zurich JavaFX Meetup group). I guess one of the reasons is the simple fact that almost every application needs a table view.

The two most requested features / improvements for the TableView were freezing rows / columns and better editing support.

Freezing Rows / Columns

  • Freeze / lock rows – the ability to have one or more rows to stay at the top or bottom of the table view. Often this feature is needed when trying to display the sum of the values in the columns.
  • Freeze / lock columns – the ability to have one or more columns stay on the left or right-hand side of the table view. Again, to show the sum of the values in the row or to display some kind of header for the row. In the case of my FlexGanttFX framework I like to have a column on the left-hand side to show the row number (yes, like in Excel).

I believe that freezing columns / rows was a feature that was planned for the TableView but that did not make it into the final release because of time and resource constraints. If I remember correctly then there is actually some left-over code / comments in the TableView code base that was intended for this purpose.

Editing Support

  • Ability to edit cell values by simply starting to type the new value – currently the user has to double click a cell first.
  • Fluent keyboard navigation to navigate from one cell to another via TAB, SHIFT_TAB, ENTER, arrow keys.
  • Cell validation when editing a value. The user should not be able to leave the cell without either entering a valid value or cancelling the edit.

The current editing support is probably the one thing that annoys developers the most when using the TableView as they know that their users expect more. I have already worked on two projects myself where we had to hack the TableView so that it would let the user edit values directly.

Miscellaneous

Other features that were requested included:

  • Cell / row spanning – the ability to have a cell go across multiple rows and or columns.
  • Filter UI – table views often let the user select one or more filters per column. The collection of all filters then determines the visible rows. In JavaFX this can be easily done in the model (via SortedList and FilteredList) but developers wished there were built-in controls directly inside the TableView (inside the headers).
  • Automatic column sizing – a way to adjust the width of a column to its content, to ensure readability of all values. This feature is actually implemented inside the TableView codebase. It is used to resize the column when the user double clicks on the edges of the column headers. However, the code for this is not public. I have recently posted an article showing how to do it.

I think that all of the above features are valid things to expect from a good table control but I am pretty sure that we will not see any progress in this area from the JavaFX team itself. I believe that from Oracle’s point of view the current table implementation is considered “good enough”, so I assume that it will be up to a third-party to come up with a good alternative, either open-source or commercial. I have brainstormed a lot about how to implement my own table view control but have reached the conclusion that this is too big to be a simple side-project. This is bad, but it also means that there is a potential for somebody else to create a commercial product and to actually make some money with it. Because like I said in the beginning: almost all applications need a table view.

The next “Missing Features” blog will be about performance and quality.

Stay tuned!

P.S.: for some of the features above you might want to try out the SpreadsheetView in the ControlsFX project.

 

FlexGanttFX 1.4.0 Released!

I am very happy to announce that I have released version 1.4.0 of FlexGanttFX for JavaFX. This release contains bug fixes and small improvements. I am glad to say that FlexGanttFX has proven itself in the field and is operational in various large enterprise applications. Some of its more prominent users are:

  • Emirates Airlines
  • European Broadcasting Union
  • Airbus
  • PSI

The following are a couple of screenshots from these applications.

ScreenShot005

PSI Logistics

Bildschirmfoto 2015-12-19 um 12.28.15

European Broadcasting Union

Gantt-Split-4up

Emirates Airlines

mintpage

MINT Software Systems

 

JavaFX “Missing Features” Survey Results

I recently conducted a survey asking the community to tell me which features they are missing the most in JavaFX 8. The survey has been closed by now. It received over 100 submissions from various people, various companies, various industries. Overall the participants very quite satisfied with JavaFX so I would argue that the feedback was submitted by people who really care about the platform and would like to see it evolve over time.

The following chart shows the exact distribution of the answers given by the participants when asked how satisfied they were with JavaFX.

Bildschirmfoto 2016-01-25 um 20.04.45

Categories / Issue Areas

The feature requests can be roughly grouped into these categories:

  • Table View
  • Performance
  • Quality
  • Properties
  • CSS & FXML
  • OpenGL & 3D
  • Media
  • Missing Controls
  • Missing API
  • Other

I will try to create a blog for each one of these categories over the next couple of weeks. So stay tuned!

JavaFX Real-World Apps: PSI Advanced Scheduling and Monitoring

I am happy to announce that there is a new entry for the “Real-World Apps” list. The company PSI has developed an application for “advanced scheduling and monitoring”.

csm_psi-logo_10dd240e3c

The software will be used by the manufacturing industry. The screenshots look quite polished and attractive. PSI is one of the earliest adopters of FlexGanttFX, my JavaFX framework for visualizing schedules of any kind.

PSIasm_01

PSI – Operation Gantt

PSIasm_02

PSI – Operation Detail

PSIasm_03

PSI – Production Order

PSIasm_04

PSI – Report

PSIasm_05

PSI – Structural Gantt

As usual I have done an interview with the developers. Michal Bocian (Development Manager) and Dobieslaw Chabrzyk (MES Product Manager) of PSI were kind enough to provide the answers.

General Questions

What is the name of your product / project?

PSI Advanced Scheduling and Monitoring

Who are your users / customers?

Customers: manufacturing industry in following areas:

  • Serial production
  • Metal re-working
  • Made-to-order constructions

Users:

  • Production schedulers
  • Shop floor stuff
  • Production managers

What is the purpose of your software? What are its benefits?

Purpose:

  • Scheduling and execution monitoring of production process

Benefits:

  • Reduce manufacturing costs
  • Reduce work in process
  • Improve customer service
  • Lower implementation and maintenance costs compared to existing IT solutions

Is the application operational? If yes, since when. If not when do you plan to go live?

Official go to market is planned for April 2016.

How big is the budget for your project?

A few hundred thousand euros.

Development

How did you get the necessary JavaFX Know-How into your team? (Consultants, Internal / External training courses)?

Mostly from the Internet (StackOverflow, Official APIs etc).

With which version of JavaFX did you start? 1, 2, 8?

JavaFX 8.

When did you start developing the application and how long did it take?

We started the development around April 2014. The development lasts till now.

How many developers worked on it? In total and on the UI.

We have 5 developers working on both server and client side.
We have 1 developer who is adjusting our CSS.

How big is the application? Lines of code, Number of classes.

230,000 Lines of Code, 2000 Classes.

How big is the JavaFX client? Lines of code, Number of classes.

190,000 Lines of Code, 1600 Classes.

Why did you choose JavaFX as frontend technology? And very importantly: why did you not choose HTML / Web?

PSI is specialized in creating applications based on the Java platform. JavaFX is the natural successor of Swing. We were considering HTML / Web for the frontend – but because of limited performance we had to give up for now. Our application needs to hold about 500,000 complex domain objects in memory to perform extraordinary parallel calculations on them.

Was it difficult to convince decision makers to agree on JavaFX?

No, it was easy. Our company is specialized in Java technology – especially in Swing. JavaFX is the choice for company like ours.

What were the biggest challenges / problems / issues / bugs you faced in the JavaFX part and how did you solve them?

We’ve found some bugs in JavaFX but none of them were critical nor major. We reported them on JavaFX’s bugtracker, and we are waiting for the fixes. Meanwhile we did some temporary workarounds.

Which 3rd-party products / frameworks / tools (open source and commercial) did you use and why did you choose them?

  • FlexGanttFX – because of modularity, quality and performance.
  • Eclipse E4 – because of window manager and dependency injection.

Did you mix JavaFX and Swing code?

We’ve tried it once, but we had to give up. It is stable when used in static layouts, but when the user tries to perform complex interactions with the UI, many unusual bugs take place.

Outlook

Would you use JavaFX again for your next project? Please elaborate why or why not.

In our case JavaFX was the only choice we had. Our application needs a lot of memory and it has to be multithreaded. If you combine these two requirements with the server written in JavaEE – JavaFX is the only choice. If our application had different non-functional requirements we would probably create our application in HTML/Web.

Which recommendations do you have related to JavaFX for other companies / projects?

If you consider JavaFX as your potential platform, check if all the special and non-standard UI requirements are meet.

Which features would you like to see being added to JavaFX?

At the moment we are suffering from a lack of good date and time picker.

Do you plan to provide a mobile version of your application or a mobile addition?

Yes, but since our product needs a lot of resources, we plan to deliver simplified application (e.g. without interactive Gantt chart).

JavaFX Real-World Playlist

I finally found some time to create a YouTube playlist with all the “Real-World” JavaFX applications that Alexander Casall and I presented at JavaOne 2015. Maybe you can find some inspiration.

The videos show the following applications:

  • EIZO – Curator Caliop
  • Emirates Airlines – Network Capacity Optimization
  • AISO – HRC-Matic Business Registry
  • European Broadcasting Union – NEOS
  • MINT Software Systems – Training and Resource Management (TRMS)

Enjoy!

JavaFX: The Power of CSS

I recently presented the NEOS application that was developed for the European Broadcasting Union. Now that a few weeks have passed the UI has been polished and a lot of work was invested into styling it via the CSS support of JavaFX. I really like the results and thought it would be good to show you.

After

Bildschirmfoto 2015-12-19 um 12.28.15

Editing a Transmission

Bildschirmfoto 2015-12-19 um 12.36.12

Searching for Transmissions

Bildschirmfoto 2015-12-19 um 12.28.54

Editing a Resource

Before

EBU-BEFORE

JavaFX Tip 22: Autosize (Tree) Table Columns

One of the first things mentioned as a “missing feature” in the JavaFX “Missing Features Survey” was the ability to auto-resize columns in tables / tree tables. It is correct that there is no public API for it, but when you pay close attention then you will notice that there must be code for doing this somewhere inside JavaFX, because the user can auto-resize a column by double clicking on the divider line between the column and the next column to the right.

But like most people I felt that this was not good enough for my code. I wanted an API for FlexGanttFX that would allow the user to auto resize one or all columns inside the Gantt charts. So I searched for the code that was hidden somewhere in the tree table  or tree table skin (can’t actually remember where) and reused it with some minor modifications in my classes.

The following is the result of this work. It targets the TreeTableView and not the TableView, but making it work for the standard table is straight-forward. Simply replace all TreeTableColumn occurrences with TableColumn. Please notice that resizing all rows can have a serious performance impact, so you might have to limit the number of rows that will be considered for the calculations via the maxRows parameter.

	/**
	 * This method will resize all columns in the tree table view to ensure that
	 * the content of all cells will be completely visible. Note: this is a very
	 * expensive operation and should only be used when the number of rows is
	 * small.
	 *
	 * @see #resizeColumn(TreeTableColumn, int)
	 */
	public final void resizeColumns() {
		resizeColumns(-1);
	}

	/**
	 * This method will resize all columns in the tree table view to ensure that
	 * the content of all cells will be completely visible. Note: this is a very
	 * expensive operation and should only be used with a small number of rows.
	 *
	 * @param maxRows
	 *            the maximum number of rows that will be considered for the
	 *            width calculations
	 *
	 * @see #resizeColumn(TreeTableColumn, int)
	 */
	public final void resizeColumns(int maxRows) {
		for (TreeTableColumn<R, ?> column : getTreeTable().getColumns()) {
			resizeColumn(column, maxRows);
		}
	}

	/**
	 * This method will resize the given column in the tree table view to ensure
	 * that the content of the column cells will be completely visible. Note:
	 * this is a very expensive operation and should only be used when the
	 * number of rows is small.
	 *
	 * @see #resizeColumn(TreeTableColumn, int)
	 */
	public final void resizeColumn(TreeTableColumn<R, ?> column) {
		resizeColumn(column, -1);
	}

	/**
	 * This method will resize the given column in the tree table view to ensure
	 * that the content of the column cells will be completely visible. Note:
	 * this is a very expensive operation and should only be used when the
	 * number of rows is small.
	 *
	 * @see #resizeColumn(TreeTableColumn, int)
	 */
	public final void resizeColumn(TreeTableColumn<R, ?> tc, int maxRows) {
		final TreeTableColumn col = tc;

		List<?> items = getItems();
		if (items == null || items.isEmpty()) {
			return;
		}

		Callback cellFactory = tc.getCellFactory();
		if (cellFactory == null) {
			return;
		}

		TreeTableCell<R, ?> cell = (TreeTableCell<R, ?>) cellFactory.call(tc);
		if (cell == null) {
			return;
		}

		// set this property to tell the TableCell we want to know its actual
		// preferred width, not the width of the associated TableColumnBase
		cell.getProperties().put("deferToParentPrefWidth", Boolean.TRUE); //$NON-NLS-1$

		// determine cell padding
		double padding = 10;
		Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
		if (n instanceof Region) {
			Region r = (Region) n;
			padding = r.snappedLeftInset() + r.snappedRightInset();
		}

		TreeTableRow<R> treeTableRow = new TreeTableRow<>();
		treeTableRow.updateTreeTableView(treeTableView);

		int rows = maxRows == -1 ? items.size()
				: Math.min(items.size(), maxRows);
		double maxWidth = 0;
		for (int row = 0; row < rows; row++) {
			treeTableRow.updateIndex(row);
			treeTableRow.updateTreeItem(treeTableView.getTreeItem(row));

			cell.updateTreeTableColumn(col);
			cell.updateTreeTableView(treeTableView);
			cell.updateTreeTableRow(treeTableRow);
			cell.updateIndex(row);

			if ((cell.getText() != null && !cell.getText().isEmpty())
					|| cell.getGraphic() != null) {
				getChildren().add(cell);
				cell.impl_processCSS(false);

				double w = cell.prefWidth(-1);

				maxWidth = Math.max(maxWidth, w);
				getChildren().remove(cell);
			}
		}

		// dispose of the cell to prevent it retaining listeners (see RT-31015)
		cell.updateIndex(-1);

		// RT-23486
		double widthMax = maxWidth + padding;
		if (treeTableView
				.getColumnResizePolicy() == TreeTableView.CONSTRAINED_RESIZE_POLICY) {
			widthMax = Math.max(widthMax, tc.getWidth());
		}

		tc.impl_setWidth(widthMax);
	}