The timeline control is a container for the Dateline and the Eventline. It is displayed above the Graphics control and provides several methods for scrolling and zooming, both of which can be done with or without animation. The timeline also keeps track of the current time (see TimeTracker).

Navigation
The timeline is used to navigate through time. It provides methods to jump to the current time or a given time. It can be requested to show a specific time unit (“show days”), or a time range.
showNow(), showNow(boolean center) - changes the start time of the timeline model in such a way that the current time (as already stored in the TimelineModel) will be displayed either on the left edge of the dateline or right in the middle.showTime(Instant time), showTime(Instant time, boolean center) - changes the start time of the timeline model in such a way that the given time will be displayed either on the left edge of the dateline or right in the middle.showRange(Instant start, Instant end), showRange(Instant start, Duration duration), showRange(TimeInterval range) - changes the start time and the “millis per pixel” value of the timeline model in such a way that the given time range will become fully visible in the dateline.showTemporalUnit(TemporalUnit unit, double width) - changes the start time and the “millis per pixel” value of the timeline model in such a way that the given time unit will be used in the dateline. Each cell in the dateline will be as wide as the given width.It should be noted that the timeline in cooperation with the dateline can only make a best-effort attempt at fulfilling these requests as they depend on the availability of dateline resolutions in the dateline model.
The methods above can be executed with or without animation. This animation can be controlled via the help of two
properties: moveAnimated and moveDuration. The appropriate getter and setter methods for these properties are available
on Timeline.
Zooming
The timeline is responsible for managing anything related to zooming. The user can press the + / - keys to increase the
zoom level by a specific zoom factor or he can select a time interval via a “lasso” by dragging the mouse and holding
down the SHIFT key. The result will be a selected time interval which is stored in the read-only property selectedTimeInterval.
The timeline listens for changes to this property and will automatically try to display the selected time interval across
the entire available width, ultimately causing a zoom in operation.
zoomIn(), zoomOut() - makes the timeline modify the timeline model in such a way that the resulting visible time range will be the current time range multiplied by / divided by the current zoom factor.zoom(double factor, boolean zoomIn, Instant frozenTime) - performs a zoom operation with the given zoom factor (either zoom in or out). The timeline will try to keep the given “frozen” time at its current location. This kind of behaviour is very useful for a pinch-based zoom, where the UI zooms “into” a specific time.setZoomLassoEnabled(boolean), boolean isZoomLassoEnabled(); - controls the availability of the zoom lasso.Just like the moving operations the zoom operations can also be executed in an animated or non-animated way. To control
this the two properties zoomAnimated and zoomDuration are available.
Another property is used to fine-tune the zooming behaviour as some applications prefer to either keep the start time,
the end time, or the center time while zooming. For this the application can set the zoomMode property. Possible values
of this enum are KEEP_START_TIME, KEEP_END_TIME, or CENTER.
Scrolling
The timeline supports scrolling to the left and right in two different speeds.
scrollLeft(), scrollLeftFast() - changes the start time property of the timeline model in such a way that the dateline will end up starting with an earlier time.scrollRight(), scrollRightFast() - changes the start time property of the timeline model in such a way that the dateline will end up starting with a later time.These methods can be invoked by the user via the + and - keys. Scrolling will be fast if the user presses SHIFT
at the same time.
TimeTracker
The timeline control is responsible for tracking time. This means that it updates the property now of the underlying timeline model. The timeline implements methods for starting and stopping time tracking, however the actual update of now will be delegated to a time tracker class.
startTimeTracking(), stopTimeTracking() - starts and stops time tracking. These methods invoke the equivalent methods on the TimeTracker class.timeTrackerProperty(), setTimeTracker(TimeTracker tracker), TimeTracker getTimeTracker() - the time tracker property and its getter and setter methods. The default time tracker (uses the system time) can be replaced with a custom one.Visible Time Interval
Two read-only properties are keeping track of the earliest and latest times shown by the timeline . They are called
visibleStartTime and visibleEndTime and the methods getVisibleStartTime(), getVisibleEndTime(), and getVisibleDuration()
can be used to work with them.
The timeline control uses a model of type TimelineModel. This model provides the most important parameters for the
timeline and the dateline in order for them to work properly. The timeline model can by typed for different temporal units.
FlexGanttFX ships with a ChronoUnitTimelineModel and a SimpleUnitTimelineModel.
Start Time & Millis Per Pixel
The two most important properties of the TimelineModel are the startTime and the millisPerPixel (MPP) properties. The
start time determines the first visible time in the Gantt chart while the current width of the timeline in combination
with the MPP value determine the last visible time and hence the visible time range. Increasing the MPP value will
cause the timeline to show a larger time range while reducing this value will result in a shorter time range. The
methods found in the Timeline class for showing a time, scrolling to a time, zooming into a range are all playing with
these two variables to achieve their purpose. The following table lists the methods related to these properties:
ObjectProperty<Instant> startTimeProperty(); setStartTime(Instant time); Instant getStartTime(); - stores, sets, and retrieves the current start time, the first visible time in the Gantt chart. The earliest possible start time can be restricted via the horizonStartTime property.DoubleProperty millisPerPixel(); setMillisPerPixel(double mpp); double getMillisPerPixel(); - stores, sets, and retrieves the millis per pixel value (mpp). The default value of mpp is 24 * 60 * 60 * 1000 / 30. This results in days having the width of 30 pixels.Now Time / Now Location
Gantt charts often have a requirement to mark the “current” time. This time can either be the system time (java.time.Instant.now()) or an arbitrary value controlled by the application. The latter is often the case in software that runs some kind of simulation and the Gantt chart is used to track the simulation time. To support these use cases the timeline model defines a property called now.
The value of now is usually updated by a time tracker that can be controlled via the timeline.
ObjectProperty<Instant> nowProperty(); void setNow(Instant now); Instant getNow(); - stores, sets, and retrieves the current time.ReadOnlyDoubleProperty nowLocation(); double getNowLocation(); - stores and retrieves the location of the current time. The now location is calculated by the model based on the start time, and the millis per pixel value. This property is a read-only property as the now location is always dependent on the value of the now time. The location can only changed by changing now itself.Time & Coordinate Calculations
The primary purpose of the timeline model is to convert time into a location and vice versa. For this the model provides several methods:
double calculateLocationForTime(Instant); - returns the x coordinate for the given time.Instant calculateTimeForLocation(double); - returns the time for the given x coordinate.The Horizon
Scheduling applications often work with a horizon, defined by an earliest and latest time. These times might be based on
the loaded dataset (min / max calculation of the start and end times of the activities) or the planning horizon
(Q1, Q2, Q3, Q4). Setting the values of horizonStartTime and horizonEndTime ensures that the user will not be able to
scroll to a time outside the horizon.
Highest & Lowest Temporal Unit
Not all applications require all available units of a temporal unit. java.time.temporal.ChronoUnit for example defines
units for nanos until millennia. The highestTemporalUnit and the lowestTemporalUnit property enable the application to
restrict the unit range to something more sensible, e.g. hours to months.
A time tracker can be used to update the now property of the TimelineModel. In most cases the time “now” will be
equivalent to the system time but in simulation software this might not be the case. To use the time tracker simply
create an instance of TimeTracker and bind its timeProperty() to the nowProperty() of the timeline model.
Example
The following is the entire code of the default time tracker class.
/**
* Copyright (C) 2014 - 2016 Dirk Lemmermann Software & Consulting (dlsc.com)
*
* This file is part of FlexGanttFX.
*/
package com.flexganttfx.view.timeline;
import java.time.Instant;
import java.util.logging.Level;
import com.flexganttfx.core.LoggingDomain;
import com.flexganttfx.model.timeline.TimelineModel;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
/**
* A time tracker can be used to update the property
* {@link TimelineModel#nowProperty()}. In most cases the time "now" will be
* equivalent to the system time but in simulations this might not be the case.
* The time tracker can be used in combination with the {@link TimelineModel} by
* binding the {@link TimelineModel#nowProperty()} to the
* {@link TimeTracker#timeProperty()}.
*
* @since 1.0
*/
public class TimeTracker extends Thread {
private boolean running = true;
private long delay = 1000;
private boolean stopped;
/**
* Constructs a new tracker.
*
* @since 1.0
*/
public TimeTracker() {
setName("Time Tracker");
setDaemon(true);
}
private final ReadOnlyObjectWrapper<Instant> time = new ReadOnlyObjectWrapper<>(
this, "time", Instant.now());
public final ReadOnlyObjectProperty<Instant> timeProperty() {
return time.getReadOnlyProperty();
}
public final Instant getTime() {
return time.get();
}
/**
* Returns the delay in milliseconds between updates of
* {@link TimelineModel#nowProperty()}. The default is 1000 millis.
*
* @return the default delay between update calls
* @since 1.0
*/
public final long getDelay() {
return delay;
}
/**
* Sets the delay between updates of {@link TimelineModel#nowProperty()}.
* The default is 1000 millis.
*
* @param millis
* the new delay
* @throws IllegalArgumentException
* if the delay is zero or smaller
* @since 1.0
*/
public final void setDelay(long millis) {
if (millis <= 0) {
throw new IllegalArgumentException(
"delay must be larger than zero but was" + millis); //$NON-NLS-1$
}
this.delay = millis;
}
/**
* Starts the tracking of the time.
*
* @since 1.0
*/
public final void startTracking() {
if (stopped) {
throw new IllegalStateException(
"Time tracker has already been stopped and can not be started again.");
} else {
running = true;
start();
}
}
@Override
public void run() {
while (running) {
Platform.runLater(() -> time.set(getNow()));
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
LoggingDomain.CONFIG.log(Level.WARNING,
"problem in update thread", e); //$NON-NLS-1$
}
}
}
/**
* Stops the tracking of the time.
*
* @since 1.0
*/
public final void stopTracking() {
stopped = true;
running = false;
}
/**
* Override to return the instant that will be set as "now" on the timeline
* model. The default implementation uses {@link Instant#now()}.
*
* @see TimelineModel#setNow(Instant)
*
* @return the "now" instant
*/
protected Instant getNow() {
return Instant.now();
}
}