Vue d’ensemble
Le contrôle Timeline est un conteneur pour la ligne de dates et la ligne d’événements. Il est affiché au-dessus du contrôle Graphismes et fournit plusieurs méthodes de défilement et de zoom, toutes utilisables avec ou sans animation. La timeline suit également l’heure courante (voir TimeTracker).

Navigation
La timeline sert à naviguer dans le temps. Elle fournit des méthodes pour accéder à l’heure courante ou à une heure donnée. On peut lui demander d’afficher une unité de temps spécifique (« afficher les jours ») ou une plage temporelle.
showNow(), showNow(boolean center)- modifie l’heure de début du modèle de timeline de sorte que l’heure courante (déjà stockée dans le TimelineModel) soit affichée soit sur le bord gauche de la ligne de dates, soit exactement au centre.showTime(Instant time), showTime(Instant time, boolean center)- modifie l’heure de début du modèle de timeline de sorte que l’heure donnée soit affichée soit sur le bord gauche de la ligne de dates, soit exactement au centre.showRange(Instant start, Instant end), showRange(Instant start, Duration duration), showRange(TimeInterval range)- modifie l’heure de début et la valeur « millis par pixel » du modèle de timeline de sorte que la plage temporelle donnée devienne entièrement visible dans la ligne de dates.showTemporalUnit(TemporalUnit unit, double width)- modifie l’heure de début et la valeur « millis par pixel » du modèle de timeline de sorte que l’unité temporelle donnée soit utilisée dans la ligne de dates. Chaque cellule de la ligne de dates aura la largeur indiquée.
Il faut noter que la timeline, en coopération avec la ligne de dates, ne peut satisfaire ces requêtes qu’au mieux, car elles dépendent de la disponibilité des résolutions de ligne de dates dans le modèle de ligne de dates.
Les méthodes ci-dessus peuvent être exécutées avec ou sans animation. Cette animation peut être contrôlée à l’aide de deux propriétés : moveAnimated et moveDuration. Les méthodes d’accès et de modification appropriées pour ces propriétés sont disponibles sur Timeline.
Zoom
La timeline est responsable de tout ce qui concerne le zoom. L’utilisateur peut appuyer sur les touches + / - pour augmenter le niveau de zoom selon un facteur spécifique, ou sélectionner un intervalle de temps au moyen d’un « lasso » en faisant glisser la souris tout en maintenant la touche SHIFT enfoncée. Le résultat est un intervalle de temps sélectionné, stocké dans la propriété en lecture seule selectedTimeInterval. La timeline écoute les changements de cette propriété et tente automatiquement d’afficher l’intervalle sélectionné sur toute la largeur disponible, ce qui provoque finalement un zoom avant.
zoomIn(), zoomOut()- amène la timeline à modifier son modèle de sorte que la plage temporelle visible résultante soit la plage courante multipliée ou divisée par le facteur de zoom actuel.zoom(double factor, boolean zoomIn, Instant frozenTime)- effectue une opération de zoom avec le facteur donné (zoom avant ou arrière). La timeline essaiera de conserver l’heure « figée » donnée à son emplacement actuel. Ce type de comportement est très utile pour un zoom par pincement, où l’interface zoome « vers » un instant précis.setZoomLassoEnabled(boolean), boolean isZoomLassoEnabled();- contrôle la disponibilité du lasso de zoom.
Comme les opérations de déplacement, les opérations de zoom peuvent également être exécutées avec ou sans animation. Pour contrôler cela, les deux propriétés zoomAnimated et zoomDuration sont disponibles.
Une autre propriété permet d’affiner le comportement de zoom, car certaines applications préfèrent conserver soit l’heure de début, soit l’heure de fin, soit l’heure centrale pendant le zoom. Pour cela, l’application peut définir la propriété zoomMode. Les valeurs possibles de cette énumération sont KEEP_START_TIME, KEEP_END_TIME ou CENTER.
Défilement
La timeline prend en charge le défilement vers la gauche et vers la droite à deux vitesses différentes.
scrollLeft(),scrollLeftFast()- modifie la propriété d’heure de début du modèle de timeline de sorte que la ligne de dates commence finalement à une heure antérieure.scrollRight(),scrollRightFast()- modifie la propriété d’heure de début du modèle de timeline de sorte que la ligne de dates commence finalement à une heure ultérieure.
Ces méthodes peuvent être invoquées par l’utilisateur au moyen des touches + et -. Le défilement sera rapide si l’utilisateur appuie simultanément sur SHIFT.
TimeTracker
Le contrôle Timeline est responsable du suivi du temps. Cela signifie qu’il met à jour la propriété now du modèle de timeline sous-jacent. La timeline implémente des méthodes pour démarrer et arrêter le suivi du temps ; toutefois, la mise à jour effective de now est déléguée à une classe de suivi du temps.
startTimeTracking(),stopTimeTracking()- démarre et arrête le suivi du temps. Ces méthodes invoquent les méthodes équivalentes de la classe TimeTracker.timeTrackerProperty(),setTimeTracker(TimeTracker tracker),TimeTracker getTimeTracker()- la propriété de suivi du temps ainsi que ses méthodes d’accès et de modification. Le suivi du temps par défaut (qui utilise l’heure système) peut être remplacé par une implémentation personnalisée.
Intervalle de temps visible
Deux propriétés en lecture seule suivent les heures la plus ancienne et la plus récente affichées par la timeline. Elles s’appellent visibleStartTime et visibleEndTime, et les méthodes getVisibleStartTime(), getVisibleEndTime() et getVisibleDuration() permettent de les utiliser.
Modèle de Timeline
Le contrôle Timeline utilise un modèle de type TimelineModel. Ce modèle fournit les paramètres les plus importants pour que la timeline et la ligne de dates fonctionnent correctement. Le modèle de timeline peut être typé pour différentes unités temporelles. FlexGanttFX est fourni avec un ChronoUnitTimelineModel et un SimpleUnitTimelineModel.
Heure de début & millis par pixel
Les deux propriétés les plus importantes du TimelineModel sont startTime et millisPerPixel (MPP). L’heure de début détermine la première heure visible dans le diagramme de Gantt, tandis que la largeur courante de la timeline, combinée à la valeur MPP, détermine la dernière heure visible et donc la plage temporelle visible. Augmenter la valeur MPP amène la timeline à afficher une plage temporelle plus grande ; la réduire produit une plage plus courte. Les méthodes de la classe Timeline pour afficher une heure, faire défiler jusqu’à une heure ou zoomer dans une plage jouent toutes sur ces deux variables pour atteindre leur objectif. Le tableau suivant répertorie les méthodes liées à ces propriétés :
ObjectProperty<Instant> startTimeProperty();setStartTime(Instant time);Instant getStartTime();- stocke, définit et retourne l’heure de début courante, c’est-à-dire la première heure visible dans le diagramme de Gantt. L’heure de début la plus ancienne possible peut être limitée via la propriété horizonStartTime.DoubleProperty millisPerPixel();setMillisPerPixel(double mpp);double getMillisPerPixel();- stocke, définit et retourne la valeur millis par pixel (mpp). La valeur par défaut de mpp est 24 * 60 * 60 * 1000 / 30. Cela donne aux jours une largeur de 30 pixels.
Heure courante / emplacement courant
Les diagrammes de Gantt doivent souvent marquer l’heure « courante ». Cette heure peut être l’heure système (java.time.Instant.now()) ou une valeur arbitraire contrôlée par l’application. Ce dernier cas est fréquent dans les logiciels exécutant une simulation où le diagramme de Gantt sert à suivre le temps simulé. Pour prendre en charge ces cas d’utilisation, le modèle de timeline définit une propriété appelée now.
La valeur de now est généralement mise à jour par un suivi du temps pouvant être contrôlé via la timeline.
ObjectProperty<Instant> nowProperty();void setNow(Instant now);Instant getNow();- stocke, définit et retourne l’heure courante.ReadOnlyDoubleProperty nowLocation();double getNowLocation();- stocke et retourne l’emplacement de l’heure courante. L’emplacement de now est calculé par le modèle à partir de l’heure de début et de la valeur millis par pixel. Cette propriété est en lecture seule, car l’emplacement de now dépend toujours de la valeur de l’heure now. L’emplacement ne peut être modifié qu’en changeant now lui-même.
Calculs de temps & de coordonnées
L’objectif principal du modèle de timeline est de convertir le temps en emplacement, et inversement. Pour cela, le modèle fournit plusieurs méthodes :
double calculateLocationForTime(Instant);- retourne la coordonnée x pour l’heure donnée.Instant calculateTimeForLocation(double);- retourne l’heure pour la coordonnée x donnée.
L’horizon
Les applications de planification travaillent souvent avec un horizon, défini par une heure la plus ancienne et une heure la plus récente. Ces heures peuvent être basées sur le jeu de données chargé (calcul min / max des heures de début et de fin des activités) ou sur l’horizon de planification (Q1, Q2, Q3, Q4). Définir les valeurs de horizonStartTime et horizonEndTime garantit que l’utilisateur ne pourra pas faire défiler vers une heure située hors de l’horizon.
Unité temporelle la plus haute & la plus basse
Toutes les applications n’ont pas besoin de toutes les unités disponibles d’une unité temporelle. java.time.temporal.ChronoUnit, par exemple, définit des unités allant des nanos aux millénaires. Les propriétés highestTemporalUnit et lowestTemporalUnit permettent à l’application de restreindre la plage d’unités à quelque chose de plus pertinent, par exemple des heures aux mois.
Suivi du temps
Un suivi du temps peut être utilisé pour mettre à jour la propriété now du TimelineModel. Dans la plupart des cas, l’heure « now » équivaut à l’heure système, mais ce n’est pas forcément le cas dans un logiciel de simulation. Pour utiliser le suivi du temps, créez simplement une instance de TimeTracker et liez sa timeProperty() à la nowProperty() du modèle de timeline.
Exemple
Voici le code complet de la classe de suivi du temps par défaut.
/**
* 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();
}
}