org.refcodes.checkerboard.alt.javafx.impls.FxCheckerboardViewerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refcodes-checkerboard-alt-javafx Show documentation
Show all versions of refcodes-checkerboard-alt-javafx Show documentation
Artifact for checkerboard related implementations with the Java FX
framework.
package org.refcodes.checkerboard.alt.javafx.impls;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.refcodes.checkerboard.ChangePositionEvent;
import org.refcodes.checkerboard.Checkerboard;
import org.refcodes.checkerboard.CheckerboardEvent;
import org.refcodes.checkerboard.CheckerboardObserver;
import org.refcodes.checkerboard.DraggabilityChangedEvent;
import org.refcodes.checkerboard.GridDimensionChangedEvent;
import org.refcodes.checkerboard.GridModeChangedEvent;
import org.refcodes.checkerboard.Player;
import org.refcodes.checkerboard.PlayerAddedEvent;
import org.refcodes.checkerboard.PlayerEvent;
import org.refcodes.checkerboard.PlayerRemovedEvent;
import org.refcodes.checkerboard.PositionChangedEvent;
import org.refcodes.checkerboard.StateChangedEvent;
import org.refcodes.checkerboard.ViewportDimensionChangedEvent;
import org.refcodes.checkerboard.ViewportOffsetChangedEvent;
import org.refcodes.checkerboard.VisibilityChangedEvent;
import org.refcodes.checkerboard.alt.javafx.FxBackgroundFactory;
import org.refcodes.checkerboard.alt.javafx.FxCheckerboardViewer;
import org.refcodes.checkerboard.alt.javafx.FxSpriteFactory;
import org.refcodes.checkerboard.impls.AbstractGraphicalCheckerboardViewer;
import org.refcodes.component.ConfigureException;
import org.refcodes.component.InitializeException;
import org.refcodes.exception.ExceptionUtility;
import org.refcodes.exception.VetoException;
import org.refcodes.exception.VetoException.VetoRuntimeException;
import org.refcodes.graphical.FieldDimension;
import org.refcodes.graphical.GridDimension;
import org.refcodes.graphical.GridMode;
import org.refcodes.graphical.MoveMode;
import org.refcodes.graphical.Position;
import org.refcodes.graphical.ViewportDimension;
import org.refcodes.graphical.ViewportOffset;
import org.refcodes.graphical.ext.javafx.impls.FxViewportPaneImpl;
import org.refcodes.logger.RuntimeLogger;
import org.refcodes.logger.impls.RuntimeLoggerFactorySingleton;
import org.refcodes.mixin.Disposable;
import javafx.animation.FadeTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FxCheckerboardViewerImpl extends AbstractGraphicalCheckerboardViewer, FxBackgroundFactory, FxCheckerboardViewer> implements FxCheckerboardViewer, CheckerboardObserver {
private static RuntimeLogger LOGGER = RuntimeLoggerFactorySingleton.createRuntimeLogger();
// /////////////////////////////////////////////////////////////////////////
// STATICS:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// CONSTANTS:
// /////////////////////////////////////////////////////////////////////////
private static final int TIMER_PERIOD_MS = 100;
private static final int TIMER_DELAY_MS = 1000;
private static final int RESIZE_FINISHED_DELAY_IN_MS = 150;
// /////////////////////////////////////////////////////////////////////////
// VARIABLES:
// /////////////////////////////////////////////////////////////////////////
private Group _checkers = new Group();
private FxViewportPaneImpl _viewportPane = new FxViewportPaneImpl();
private int _movePlayerDurationInMillis = 150;
private int _adjustPlayerDurationInMillis = 50;
private int _addPlayerDurationInMillis = 1000;
private int _removePlayerDurationInMillis = 1000;
private int _playerVisibilityDurationInMillis = 100;
private int _changePlayerStateInMillis = 300;
private int _resizeGridInMillis = 500;
private int _initGridInMillis = 1500;
private boolean _isVisible;
private Node _backgroundNode = null;
private Scene _scene = null;
private Stage _stage = null;
private Map, Node> _playerToSprite = new HashMap, Node>();
private Map, DragPlayerEventHandler> _playerToDragEventHandler = new HashMap, DragPlayerEventHandler>();
private FxCheckerboardViewerImpl.ResizeEventHandler _stageHandler;
private long _lastResizeTimeInMs = -1;
private Timer _viewTimer = null;
private double _bordersH = Double.NaN;
private double _bordersV = Double.NaN;
private double _windowDecorationH = Double.NaN;
private double _windowDecorationV = Double.NaN;
// /////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS:
// /////////////////////////////////////////////////////////////////////////
public FxCheckerboardViewerImpl( Checkerboard aCheckerboard ) {
aCheckerboard.subscribeObserver( this );
}
// /////////////////////////////////////////////////////////////////////////
// INJECTION:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// METHODS:
// /////////////////////////////////////////////////////////////////////////
@Override
public FxCheckerboardViewer withMovePlayerDurationInMillis( int aMovePlayerDurationInMillis ) {
setMovePlayerDurationInMillis( aMovePlayerDurationInMillis );
return this;
}
@Override
public FxCheckerboardViewer withAddPlayerDurationInMillis( int aAddPlayerDurationInMillis ) {
setAddPlayerDurationInMillis( aAddPlayerDurationInMillis );
return this;
}
@Override
public FxCheckerboardViewer withRemovePlayerDurationInMillis( int aRemovePlayerDurationInMillis ) {
setRemovePlayerDurationInMillis( aRemovePlayerDurationInMillis );
return this;
}
@Override
public FxCheckerboardViewer withChangePlayerStateInMillis( int aChangePlayerStateInMillis ) {
setChangePlayerStateInMillis( aChangePlayerStateInMillis );
return this;
}
@Override
public void setMovePlayerDurationInMillis( int aMovePlayerDurationInMillis ) {
_movePlayerDurationInMillis = aMovePlayerDurationInMillis;
}
@Override
public int getAddPlayerDurationInMillis() {
return _addPlayerDurationInMillis;
}
@Override
public void setAddPlayerDurationInMillis( int aAddPlayerDurationInMillis ) {
_addPlayerDurationInMillis = aAddPlayerDurationInMillis;
}
@Override
public int getRemovePlayerDurationInMillis() {
return _removePlayerDurationInMillis;
}
@Override
public void setRemovePlayerDurationInMillis( int aRemovePlayerDurationInMillis ) {
_removePlayerDurationInMillis = aRemovePlayerDurationInMillis;
}
@Override
public int getChangePlayerStateInMillis() {
return _changePlayerStateInMillis;
}
@Override
public void setChangePlayerStateInMillis( int changePlayerStateInMillis ) {
_changePlayerStateInMillis = changePlayerStateInMillis;
}
@Override
public int getMovePlayerDurationInMillis() {
return _movePlayerDurationInMillis;
}
@Override
public void setVisible( boolean isVisible ) {
if ( _stage != null ) {
if ( isVisible ) {
if ( Platform.isFxApplicationThread() ) {
_stage.show();
}
else {
Platform.runLater( new Runnable() {
@Override
public void run() {
_stage.show();
}
} );
}
}
else {
if ( Platform.isFxApplicationThread() ) {
_stage.hide();
}
else {
Platform.runLater( new Runnable() {
@Override
public void run() {
_stage.hide();
}
} );
}
}
}
_isVisible = isVisible;
}
@Override
public boolean isVisible() {
return _isVisible;
}
@Override
public int getViewportOffsetX() {
return _viewportPane.getViewportOffsetX();
}
@Override
public int getViewportOffsetY() {
return _viewportPane.getViewportOffsetY();
}
// /////////////////////////////////////////////////////////////////////////
// LIFECYCLE:
// /////////////////////////////////////////////////////////////////////////
@Override
public synchronized void initialize() throws InitializeException {
if ( _viewTimer == null ) {
synchronized ( this ) {
if ( _viewTimer == null ) {
_viewTimer = new Timer( true );
_viewTimer.schedule( new ViewDaemon(), TIMER_DELAY_MS, TIMER_PERIOD_MS );
}
}
}
if ( Platform.isFxApplicationThread() ) {
runInitialize();
if ( _stage != null ) {
runInitialize( _stage );
}
}
else {
Platform.runLater( new Runnable() {
@Override
public void run() {
try {
runInitialize();
if ( _stage != null ) {
runInitialize( _stage );
}
}
catch ( InitializeException e ) {
LOGGER.error( "Exception during initialization: " + ExceptionUtility.toMessage( e ), e );
}
}
} );
}
}
@Override
public void initialize( Stage aContext ) throws ConfigureException {
if ( Platform.isFxApplicationThread() ) {
try {
initialize();
}
catch ( InitializeException e ) {
throw new ConfigureException( "Exception during initialization: " + ExceptionUtility.toMessage( e ), e );
}
runInitialize( aContext );
}
else {
Platform.runLater( new Runnable() {
@Override
public void run() {
try {
runInitialize();
runInitialize( aContext );
}
catch ( InitializeException e ) {
LOGGER.error( "Exception during initialization: " + ExceptionUtility.toMessage( e ), e );
}
}
} );
}
}
@Override
public FxCheckerboardViewer withInitialize( Stage aContext ) throws ConfigureException {
initialize( aContext );
return this;
}
@Override
public void destroy() {
LOGGER.debug( "Destroying ..." );
if ( _viewTimer != null ) {
synchronized ( this ) {
if ( _viewTimer != null ) {
_viewTimer.cancel();
_viewTimer.purge();
_viewTimer = null;
}
}
}
}
private void runInitialize() throws InitializeException {
try {
LOGGER.debug( "Initializing ..." );
if ( getViewportWidth() == -1 ) {
setViewportWidth( getGridWidth() );
}
if ( getViewportHeight() == -1 ) {
setViewportHeight( getGridHeight() );
}
if ( getViewportWidth() == -1 || getViewportHeight() == -1 ) { throw new IllegalStateException( "The viewport dimension for the checkerboard must be set!" ); }
fxResizeViewport( this, null, this, _initGridInMillis );
fxResizeGrid( this, null, _initGridInMillis );
switch ( getScaleMode() ) {
case SCALE_GRID:
case SCALE_FIELDS: {
_viewportPane.setContent( _checkers );
_viewportPane.setViewportOffset( this );
_viewportPane.setFieldDimension( this );
_scene = new Scene( _viewportPane, Color.BLACK );
break;
}
case NONE: {
StackPane thePane = new StackPane();
thePane.getChildren().add( _checkers );
StackPane.setAlignment( _checkers, Pos.CENTER );
_scene = new Scene( thePane, Color.BLACK );
break;
}
}
}
catch ( Exception e ) {
throw new InitializeException( "Exception during initialization: " + ExceptionUtility.toMessage( e ), e );
}
}
private void runInitialize( Stage aContext ) throws ConfigureException {
try {
if ( _stage != aContext ) {
if ( _stageHandler != null ) {
_stageHandler.dispose();
}
_stage = aContext;
_stageHandler = new ResizeEventHandler( aContext );
}
fxResizeStage();
}
catch ( Exception e ) {
throw new ConfigureException( aContext, "Exception during initialization: " + ExceptionUtility.toMessage( e ), e );
}
}
// /////////////////////////////////////////////////////////////////////////
// HOOKS:
// /////////////////////////////////////////////////////////////////////////
@Override
public void onPlayerEvent( PlayerEvent aEvent, Checkerboard aCheckerboard ) {}
@Override
public void onCheckerboardEvent( CheckerboardEvent aEvent ) {}
@Override
public void onChangePositionEvent( ChangePositionEvent aEvent, Checkerboard aCheckerboard ) throws VetoException {
LOGGER.debug( aEvent.toString() );
}
@Override
public void onPositionChangedEvent( PositionChangedEvent aEvent, Checkerboard aCheckerboard ) {
LOGGER.debug( aEvent.toString() );
fxMovePlayer( aEvent.getSource(), aEvent.getPrecedingPosition(), _movePlayerDurationInMillis );
}
@Override
public void onVisibilityChangedEvent( VisibilityChangedEvent aEvent, Checkerboard aCheckerboard ) {
LOGGER.debug( aEvent.toString() );
fxPlayerVisibility( aEvent.getSource(), _playerVisibilityDurationInMillis );
}
@Override
public void onDraggabilityChangedEvent( DraggabilityChangedEvent aEvent, Checkerboard aCheckerboard ) {
LOGGER.debug( aEvent.toString() );
fxPlayerDraggability( aEvent.getSource() );
}
@Override
public void onStateChangedEvent( StateChangedEvent aEvent, Checkerboard aCheckerboard ) {
LOGGER.debug( aEvent.toString() );
fxRemovePlayer( aEvent.getSource(), _changePlayerStateInMillis / 2 );
fxAddPlayer( aEvent.getSource(), _changePlayerStateInMillis / 2 );
}
@Override
public void onPlayerAddedEvent( PlayerAddedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
fxAddPlayer( aEvent.getPlayer(), _addPlayerDurationInMillis );
}
@Override
public void onPlayerRemovedEvent( PlayerRemovedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
fxRemovePlayer( aEvent.getPlayer(), _removePlayerDurationInMillis );
}
@Override
public void onGridModeChangedEvent( GridModeChangedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
_viewportPane.setGridMode( aEvent.getGridMode() );
initMinStageWidth();
initMinStageHeight();
}
@Override
public void onGridDimensionChangedEvent( GridDimensionChangedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
fxResizeGrid( aEvent, aEvent.getPrecedingGridDimension(), _resizeGridInMillis );
}
@Override
public void onViewportOffsetChangedEvent( ViewportOffsetChangedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
_viewportPane.setViewportOffset( this );
}
@Override
public void onViewportDimensionChangedEvent( ViewportDimensionChangedEvent aEvent ) {
LOGGER.debug( aEvent.toString() );
fxResizeViewport( aEvent, aEvent.getPrecedingViewportDimension(), aEvent.getViewportOffset(), _resizeGridInMillis );
}
// /////////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////////
protected void resetPlayers( int aDurationInMillis ) {
for ( Player ePlayer : getCheckerboard().getPlayers() ) {
fxResetPlayer( ePlayer, aDurationInMillis );
}
}
protected void scalePlayers( FieldDimension aFieldDimension, FieldDimension aPrecedingFieldDimension ) {
for ( Player ePlayer : getCheckerboard().getPlayers() ) {
fxScalePlayer( ePlayer, aFieldDimension, aPrecedingFieldDimension );
}
}
protected void hidePlayers( int aDurationInMillis ) {
for ( Player ePlayer : getCheckerboard().getPlayers() ) {
fxHidePlayer( ePlayer, aDurationInMillis );
}
}
private int toPixelPositionX( Position aPosition ) {
return (getFieldWidth() + getFieldGap()) * aPosition.getPositionX() + (getGridMode() == GridMode.CLOSED ? getFieldGap() : 0);
}
private int toPixelPositionY( Position aPosition ) {
return (getFieldHeight() + getFieldGap()) * aPosition.getPositionY() + (getGridMode() == GridMode.CLOSED ? getFieldGap() : 0);
}
// @formatter:off
/*
private int toPixelOffsetX() {
return (getFieldWidth() + getFieldGap()) * getViewportOffsetX();
}
*/
// @formatter:on
// @formatter:off
/*
private int toPixelOffsetY() {
return (getFieldHeight() + getFieldGap()) * getViewportOffsetY();
}
*/
// @formatter:on
private boolean isViewportPosX( int aPositionX ) {
return (aPositionX >= getViewportOffsetX() && aPositionX < getViewportWidth() + getViewportOffsetX());
}
private boolean isViewportPosY( int aPositionY ) {
return (aPositionY >= getViewportOffsetY() && aPositionY < getViewportHeight() + getViewportOffsetY());
}
// @formatter:off
/*
private boolean isViewportPosition( int aPositionX, int aPositionY ) {
return isViewportPosX( aPositionX ) && isViewportPosY( aPositionY );
}
*/
// @formatter:on
private boolean isViewport( Position aPosition ) {
return isViewportPosX( aPosition.getPositionX() ) && isViewportPosY( aPosition.getPositionY() );
}
private void initBordersH( double aWindowWidth ) {
if ( Double.isNaN( _bordersH ) ) {
double theBoardersH = aWindowWidth - ((getViewportWidth() - 1) * getFieldGap());
if ( getCheckerboard().getGridMode() == GridMode.CLOSED ) theBoardersH -= getFieldGap() * 2;
theBoardersH = theBoardersH - (getViewportWidth() * getFieldWidth());
// -------------------------------------------------------------
// Adjust one (phantom) gap for accurate calculation:
// -------------------------------------------------------------
if ( getCheckerboard().getGridMode() == GridMode.CLOSED ) theBoardersH += getFieldGap();
else theBoardersH -= getFieldGap();
// -------------------------------------------------------------
LOGGER.debug( "Horizontal (left and right) [phantom] borders from width <" + aWindowWidth + "> := " + theBoardersH );
_windowDecorationH = _stage.getWidth() - _scene.getWidth();
_bordersH = theBoardersH;
}
}
private void initBordersV( double aWindowHeight ) {
if ( Double.isNaN( _bordersV ) ) {
double theBoardersV = aWindowHeight - ((getViewportHeight() - 1) * getFieldGap());
if ( getCheckerboard().getGridMode() == GridMode.CLOSED ) theBoardersV -= getFieldGap() * 2;
theBoardersV = theBoardersV - (getViewportHeight() * getFieldHeight());
// -------------------------------------------------------------
// Adjust one (phantom) gap for accurate calculation:
// -------------------------------------------------------------
if ( getCheckerboard().getGridMode() == GridMode.CLOSED ) theBoardersV += getFieldGap();
else theBoardersV -= getFieldGap();
// -------------------------------------------------------------
LOGGER.debug( "Vertical (top and bottom) [phantom] borders from height <" + aWindowHeight + "> := " + theBoardersV );
_windowDecorationV = _stage.getHeight() - _scene.getHeight();
_bordersV = theBoardersV;
}
}
private void initMinStageWidth() {
if ( _stage != null && _scene != null ) {
if ( Double.isNaN( _bordersH ) ) {
initBordersH( _scene.getWidth() );
}
if ( !Double.isNaN( _bordersH ) ) {
int theWidth = (int) _bordersH;
theWidth += (getViewportWidth() * getFieldWidth() + getViewportWidth() * getFieldGap());
_stage.setMinWidth( theWidth + _windowDecorationH );
}
}
}
private void initMinStageHeight() {
if ( _stage != null && _scene != null ) {
if ( Double.isNaN( _bordersV ) ) {
initBordersV( _scene.getHeight() );
}
if ( !Double.isNaN( _bordersV ) ) {
int theHeight = (int) _bordersV;
theHeight += (getViewportHeight() * getFieldHeight() + getViewportHeight() * getFieldGap());
_stage.setMinHeight( theHeight + _windowDecorationV );
}
}
}
// /////////////////////////////////////////////////////////////////////////
// FX-THREAD MANAGEMENT:
// /////////////////////////////////////////////////////////////////////////
private void fxMovePlayer( Player aPlayer, Position aPrecedingPosition, int aDurationInMillis ) {
Node theSprite;
synchronized ( this ) {
theSprite = _playerToSprite.get( aPlayer );
}
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( _checkers.getChildren().remove( theSprite ) ) {
_checkers.getChildren().add( theSprite );
if ( getMoveMode() == MoveMode.SMOOTH ) {
TranslateTransition theTransition = new TranslateTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setByX( toPixelPositionX( aPlayer ) - theSprite.getTranslateX() );
theTransition.setByY( toPixelPositionY( aPlayer ) - theSprite.getTranslateY() );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
else {
theSprite.setTranslateX( toPixelPositionX( aPlayer ) );
theSprite.setTranslateY( toPixelPositionY( aPlayer ) );
}
}
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private void fxScalePlayer( Player aPlayer, FieldDimension aFieldDimension, FieldDimension aPrecedingFieldDimension ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.get( aPlayer );
}
theSprite.setScaleX( aFieldDimension.getFieldWidth() / theSprite.getBoundsInLocal().getWidth() );
theSprite.setScaleY( aFieldDimension.getFieldHeight() / theSprite.getBoundsInLocal().getHeight() );
double theLayoutX = (aFieldDimension.getFieldWidth() - theSprite.getBoundsInLocal().getWidth()) / 2;
double theLayoutY = (aFieldDimension.getFieldHeight() - theSprite.getBoundsInLocal().getHeight()) / 2;
theSprite.setLayoutX( theLayoutX );
theSprite.setLayoutY( theLayoutY );
theSprite.setTranslateX( toPixelPositionX( aPlayer ) );
theSprite.setTranslateY( toPixelPositionY( aPlayer ) );
}
private void fxResetPlayer( Player aPlayer, int aDurationInMillis ) {
Node theSprite;
Node thePrevSprite;
synchronized ( _playerToSprite ) {
theSprite = getSpriteFactory().createInstance( aPlayer.getState(), this );
thePrevSprite = _playerToSprite.put( aPlayer, theSprite );
}
if ( aPlayer.isDraggable() ) {
DragPlayerEventHandler theDragEventHandler = new DragPlayerEventHandler( aPlayer, theSprite );
_playerToDragEventHandler.put( aPlayer, theDragEventHandler );
}
theSprite.setOpacity( 0 );
Point2D thePoint = theSprite.localToParent( 0, 0 );
theSprite.setLayoutX( -(thePoint.getX()) );
theSprite.setLayoutY( -(thePoint.getY()) );
theSprite.setTranslateX( toPixelPositionX( aPlayer ) );
theSprite.setTranslateY( toPixelPositionY( aPlayer ) );
_checkers.getChildren().add( theSprite );
if ( thePrevSprite != null ) _checkers.getChildren().remove( thePrevSprite );
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( aPlayer.isVisible() && theSprite.getOpacity() == 0 ) {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( 0 );
theTransition.setToValue( 1 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private void fxHidePlayer( Player aPlayer, int aDurationInMillis ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.get( aPlayer );
}
if ( theSprite != null ) {
if ( aDurationInMillis == 0 ) {
theSprite.setOpacity( 0 );
}
else if ( theSprite.getOpacity() != 0 ) {
Runnable theRunner = new Runnable() {
@Override
public void run() {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( theSprite.getOpacity() );
theTransition.setToValue( 0 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
}
}
private void fxAddPlayer( Player aPlayer, int aDurationInMillis ) {
Node theSprite = getSpriteFactory().createInstance( aPlayer.getState(), this );
synchronized ( _playerToSprite ) {
_playerToSprite.put( aPlayer, theSprite );
}
if ( aPlayer.isDraggable() ) {
DragPlayerEventHandler theDragEventHandler = new DragPlayerEventHandler( aPlayer, theSprite );
_playerToDragEventHandler.put( aPlayer, theDragEventHandler );
}
Point2D thePoint = theSprite.localToParent( 0, 0 );
theSprite.setLayoutX( -(thePoint.getX()) );
theSprite.setLayoutY( -(thePoint.getY()) );
theSprite.setTranslateX( toPixelPositionX( aPlayer ) );
theSprite.setTranslateY( toPixelPositionY( aPlayer ) );
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( isViewport( aPlayer ) ) {
theSprite.setOpacity( 0 );
_checkers.getChildren().add( theSprite );
if ( aPlayer.isVisible() ) {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( 0 );
theTransition.setToValue( 1 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
}
else {
theSprite.setVisible( false );
_checkers.getChildren().add( theSprite );
}
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private void fxRemovePlayer( Player aPlayer, int aDurationInMillis ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.remove( aPlayer );
}
DragPlayerEventHandler theDragEventHandler = _playerToDragEventHandler.remove( aPlayer );
if ( theDragEventHandler != null ) {
theDragEventHandler.dispose();
}
if ( theSprite != null ) {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( 1 );
theTransition.setToValue( 0 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.setOnFinished( new EventHandler() {
@Override
public void handle( ActionEvent event ) {
_checkers.getChildren().remove( theSprite );
}
} );
theTransition.play();
}
}
private void fxPlayerDraggability( Player aPlayer ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.get( aPlayer );
}
if ( theSprite != null ) {
if ( aPlayer.isDraggable() && !_playerToDragEventHandler.containsKey( aPlayer ) ) {
DragPlayerEventHandler theDragEventHandler = new DragPlayerEventHandler( aPlayer, theSprite );
_playerToDragEventHandler.put( aPlayer, theDragEventHandler );
}
else {
DragPlayerEventHandler theDragEventHandler = _playerToDragEventHandler.remove( aPlayer );
if ( theDragEventHandler != null ) {
theDragEventHandler.dispose();
}
}
}
else {
throw new IllegalStateException( "The player <" + aPlayer + "> is unknwon by this checkerboard." );
}
}
// @formatter:off
/*
private void fxPlayerVisibility( Player aPlayer, boolean isVisible, int aDurationInMillis ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.get( aPlayer );
}
if ( theSprite != null ) {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( isVisible ? 0 : 1 );
theTransition.setToValue( isVisible ? 1 : 0 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
if ( !isVisible ) {
theTransition.setOnFinished( new EventHandler() {
@Override
public void handle( ActionEvent event ) {
theSprite.setVisible( false );
}
} );
}
if ( isVisible ) {
if ( !theSprite.visibleProperty().getValue() ) theSprite.setVisible( true );
}
theTransition.play();
}
}
*/
// @formatter:on
private void fxPlayerVisibility( Player aPlayer, int aDurationInMillis ) {
Node theSprite;
synchronized ( _playerToSprite ) {
theSprite = _playerToSprite.get( aPlayer );
}
if ( theSprite != null ) {
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ), theSprite );
theTransition.setFromValue( aPlayer.isVisible() ? 0 : 1 );
theTransition.setToValue( aPlayer.isVisible() ? 1 : 0 );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
}
// @formatter:off
/*
private synchronized void fxResizeFields( FieldDimensionChangedEvent> aEvent, FieldDimension precedingFieldDimension ) {
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( getBackground() != null ) {
int index = 0;
Node theOldBackgroundNode = _backgroundNode;
_backgroundNode = getBackground().createInstance( FxCheckerboardViewerImpl.this );
if ( theOldBackgroundNode != null ) {
index = _checkers.getChildren().indexOf( theOldBackgroundNode );
_checkers.getChildren().remove( _checkers.getChildren().remove( theOldBackgroundNode ) );
}
_checkers.getChildren().add( index, _backgroundNode );
}
fxResizeStage();
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
*/
// @formatter:on
private synchronized void fxResizeViewport( ViewportDimension aDimension, ViewportDimension aPrecedingDimension, ViewportOffset aOffset, int aDurationInMillis ) {
Runnable theRunner = new Runnable() {
@Override
public void run() {
_viewportPane.setFieldDimension( FxCheckerboardViewerImpl.this );
_viewportPane.setViewportDimension( aDimension );
fxResizeStage();
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private synchronized void fxResizeGrid( GridDimension aDimension, GridDimension aPrecedingDimension, int aDurationInMillis ) {
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( getBackgroundFactory() != null ) {
fxUpdateBackground( aDimension, aPrecedingDimension, aDurationInMillis );
}
fxResizeStage();
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private void fxResizeStage() {
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( _stage != null ) {
switch ( getScaleMode() ) {
case SCALE_GRID:
case SCALE_FIELDS: {
if ( _stage != null ) {
_stage.setScene( _scene );
}
}
break;
case NONE: {
if ( _stage != null ) {
_stage.setScene( _scene );
_stage.sizeToScene();
_stage.setResizable( false );
}
break;
}
}
}
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
private void fxUpdateBackground( GridDimension aDimension, GridDimension aPrecedingDimension, int aDurationInMillis ) {
int index = 0;
Node theOldBackgroundNode = _backgroundNode;
Node theNewBackgroundNode = getBackgroundFactory().createInstance( FxCheckerboardViewerImpl.this );
FadeTransition theTransition = new FadeTransition( Duration.millis( aDurationInMillis ) );
if ( theOldBackgroundNode != null ) {
index = _checkers.getChildren().indexOf( theOldBackgroundNode );
theTransition.setOnFinished( new EventHandler() {
@Override
public void handle( ActionEvent event ) {
_checkers.getChildren().remove( _checkers.getChildren().remove( theOldBackgroundNode ) );
fxResizeStage();
}
} );
}
Node theTransitionNode = null;
double theFromValue = 0;
double theToValue = 1;
if ( aPrecedingDimension != null && aDimension.getGridWidth() >= aPrecedingDimension.getGridWidth() && aDimension.getGridHeight() >= aPrecedingDimension.getGridHeight() ) {
theTransitionNode = theNewBackgroundNode;
theNewBackgroundNode.setOpacity( 0 );
theFromValue = 0;
theToValue = 1;
index++;
}
else if ( aPrecedingDimension != null && aDimension.getGridWidth() <= aPrecedingDimension.getGridWidth() && aDimension.getGridHeight() <= aPrecedingDimension.getGridHeight() ) {
theTransitionNode = theOldBackgroundNode;
theFromValue = 1;
theToValue = 0;
}
if ( aPrecedingDimension == null ) {
theTransitionNode = theNewBackgroundNode;
theNewBackgroundNode.setOpacity( 0 );
}
_backgroundNode = theNewBackgroundNode;
_checkers.getChildren().add( index, _backgroundNode );
theTransition.setNode( theTransitionNode );
theTransition.setFromValue( theFromValue );
theTransition.setToValue( theToValue );
theTransition.setCycleCount( 1 );
theTransition.setAutoReverse( false );
theTransition.play();
}
// /////////////////////////////////////////////////////////////////////////
// INNER CLASSES:
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// VIEW DAEMON:
// /////////////////////////////////////////////////////////////////////////
private class ViewDaemon extends TimerTask {
@Override
public void run() {
if ( _stage != null && _lastResizeTimeInMs != -1 && _lastResizeTimeInMs + RESIZE_FINISHED_DELAY_IN_MS < System.currentTimeMillis() ) {
_lastResizeTimeInMs = -1;
Runnable theRunner = new Runnable() {
@Override
public void run() {
if ( Double.isNaN( _bordersH ) ) {
initMinStageWidth();
}
if ( Double.isNaN( _bordersV ) ) {
initMinStageHeight();
}
}
};
if ( Platform.isFxApplicationThread() ) {
theRunner.run();
}
else {
Platform.runLater( theRunner );
}
}
}
}
// /////////////////////////////////////////////////////////////////////////
// DRAG PLAYER EVENT HANDLER:
// /////////////////////////////////////////////////////////////////////////
private class DragPlayerEventHandler implements Disposable {
private double _sceneX;
private double _sceneY;
private double _translateX;
private double _translateY;
private Player _player;
private Node _sprite;
public DragPlayerEventHandler( Player aPlayer, Node aSprite ) {
aSprite.setOnMousePressed( _onMousePressedEventHandler );
aSprite.setOnMouseDragged( _onMouseDraggedEventHandler );
aSprite.setOnMouseReleased( _onMouseReleasedEventHandler );
_player = aPlayer;
_sprite = aSprite;
}
private EventHandler _onMousePressedEventHandler = new EventHandler() {
@Override
public void handle( MouseEvent aEvent ) {
if ( _player.isVisible() ) {
Node theSprite = (Node) (aEvent.getSource());
// _checkerboard.getChildren().remove( theSprite );
// _checkerboard.getChildren().add( theSprite );
theSprite.setOpacity( 0.5 );
_sceneX = aEvent.getSceneX();
_sceneY = aEvent.getSceneY();
_translateX = theSprite.getTranslateX();
_translateY = theSprite.getTranslateY();
LOGGER.debug( "Player mouse press X := " + aEvent.getSceneX() );
LOGGER.debug( "Player mouse press Y := " + aEvent.getSceneY() );
aEvent.consume();
}
}
};
private EventHandler _onMouseDraggedEventHandler = new EventHandler() {
@Override
public void handle( MouseEvent aEvent ) {
if ( _player.isVisible() ) {
Node theSprite = (Node) (aEvent.getSource());
double theSceneOffsetX = aEvent.getSceneX() - _sceneX;
double theSceneOffsetY = aEvent.getSceneY() - _sceneY;
double theTranslateX = _translateX + theSceneOffsetX;
double theTranslateY = _translateY + theSceneOffsetY;
// if ( theTranslateX < -(_bordersH / 2) ) return;
// if ( theTranslateY < -(_bordersV / 2) ) return;
int theBorderWidth = (int) _bordersH / 2;
int theBorderHeight = (int) _bordersV / 2;
// @formatter:off
/*
LOGGER.debug( "Translate X := " + theTranslateX );
LOGGER.debug( "Translate Y := " + theTranslateY );
LOGGER.debug( "Field width = " + getFieldWidth() );
LOGGER.debug( "Field height = " + getFieldHeight() );
LOGGER.debug( "Field gap = " + getFieldGap() );
LOGGER.debug( "Viewport offset X = " + getViewportOffsetX() );
LOGGER.debug( "Viewport offset Y = " + getViewportOffsetY() );
LOGGER.debug( "Grid width = " + getGridWidth() );
LOGGER.debug( "Grid height = " + getGridHeight() );
LOGGER.debug( "Border width := " + theBorderWidth );
LOGGER.debug( "Border height := " + theBorderHeight );
LOGGER.debug( "Absolute Field width := " + (getFieldWidth() + getFieldGap()) );
LOGGER.debug( "Absolute Field height := " + (getFieldHeight() + getFieldGap()) );
LOGGER.debug( "Offset X + grid with - 1 := " + (getViewportOffsetX() + getGridWidth() - 1) );
LOGGER.debug( "Offset Y + grid height - 1 := " + (getViewportOffsetY() + getGridHeight() - 1) );
LOGGER.debug( "... for X := " + ( getFieldWidth() + getFieldGap()) * (getViewportOffsetX() + getGridWidth() - 1) );
LOGGER.debug( "... for Y := " + ( getFieldHeight() + getFieldGap()) * (getViewportOffsetY() + getGridHeight() - 1) );
*/
// @formatter:on
if ( theTranslateX >= -theBorderWidth && theTranslateX <= (getFieldWidth() + getFieldGap()) * (getGridWidth() - 1) + theBorderWidth ) theSprite.setTranslateX( theTranslateX );
if ( theTranslateY >= -theBorderHeight && theTranslateY <= (getFieldHeight() + getFieldGap()) * (getGridHeight() - 1) + theBorderHeight ) theSprite.setTranslateY( theTranslateY );
aEvent.consume();
}
}
};
private EventHandler _onMouseReleasedEventHandler = new EventHandler() {
@Override
public void handle( MouseEvent aEvent ) {
Node theSprite = (Node) (aEvent.getSource());
if ( _player.isVisible() ) {
double theSceneOffsetX = aEvent.getSceneX() - _sceneX;
double theSceneOffsetY = aEvent.getSceneY() - _sceneY;
double theStepX = Math.round( theSceneOffsetX / (getFieldWidth() + getFieldGap()) );
double theStepY = Math.round( theSceneOffsetY / (getFieldHeight() + getFieldGap()) );
int thePosX = (int) (_player.getPositionX() + theStepX);
int thePosY = (int) (_player.getPositionY() + theStepY);
if ( thePosX >= getGridWidth() || thePosX < 0 || thePosY >= getGridHeight() || thePosY < 0 ) {
theStepY = 0;
theStepX = 0;
thePosY = (int) (_player.getPositionY() + theStepY);
thePosX = (int) (_player.getPositionX() + theStepX);
}
try {
_player.setPosition( thePosX, thePosY );
}
catch ( VetoRuntimeException e ) {
LOGGER.warn( "Change player <" + _player + "> position to (" + thePosX + ", " + thePosY + ") has been vetoed: " + ExceptionUtility.toMessage( e ), e );
theStepX = 0;
theStepY = 0;
}
if ( theStepX == 0 && theStepY == 0 ) fxMovePlayer( _player, _player, _adjustPlayerDurationInMillis );
theSprite.setOpacity( 1 );
// @formatter:off
/*
LOGGER.debug( "Player mouse release X := " + aEvent.getSceneX() );
LOGGER.debug( "Player mouse release Y := " + aEvent.getSceneY() );
LOGGER.debug( "Player mouse release offset X := " + theSceneOffsetX );
LOGGER.debug( "Player mouse release offset Y := " + theSceneOffsetY );
LOGGER.debug( "Player grid step X := " + theStepX );
LOGGER.debug( "Player grid step Y := " + theStepY );
LOGGER.debug( "Player new pos X := " + thePosX );
LOGGER.debug( "Player new pos Y := " + thePosY );
*/
// @formatter:on
aEvent.consume();
}
// else {
// theSprite.setTranslateX( toPositionX( _player ) );
// theSprite.setTranslateY( toPositionY( _player ) );
// }
}
};
@Override
public void dispose() {
_sprite.setOnMousePressed( null );
_sprite.setOnMouseDragged( null );
_sprite.setOnMouseReleased( null );
_sprite = null;
_player = null;
}
}
// /////////////////////////////////////////////////////////////////////////
// RESIZE EVENT HANDLER:
// /////////////////////////////////////////////////////////////////////////
private class ResizeEventHandler implements Disposable {
private Stage _window;
public ResizeEventHandler( Stage aWindow ) {
aWindow.widthProperty().addListener( _onWindowWidthChangedEventHandler );
aWindow.heightProperty().addListener( _onWindowHeightChangedEventHandler );
_window = aWindow;
}
// ---------------------------------------------------------------------
// WINDOW WIDTH CHANGE HANDLER:
// ---------------------------------------------------------------------
private ChangeListener _onWindowWidthChangedEventHandler = new ChangeListener() {
@Override
public void changed( ObservableValue extends Number> observable, Number aOldValue, Number aNewValue ) {
_lastResizeTimeInMs = System.currentTimeMillis();
if ( Math.abs( aNewValue.doubleValue() - aOldValue.doubleValue() ) > 10 ) return;
// -------------------------------------------------------------
// Calculate the sum of the H-borders:
// -------------------------------------------------------------
if ( Double.isNaN( _bordersH ) && !Double.isNaN( aOldValue.doubleValue() ) ) {
initBordersH( aOldValue.doubleValue() );
initMinStageWidth();
}
// -------------------------------------------------------------
switch ( getScaleMode() ) {
case SCALE_GRID:
int theNewViewportWidth = toScaledViewportDimension( aNewValue.doubleValue(), getViewportWidth(), getFieldWidth(), getFieldGap(), _bordersH + _windowDecorationH );
if ( theNewViewportWidth != -1 ) {
LOGGER.debug( "Viewport width changed to := " + theNewViewportWidth );
setViewportWidth( theNewViewportWidth );
}
break;
case SCALE_FIELDS:
int theNewFieldWidth = toScaledFieldDimension( aNewValue.doubleValue(), getViewportWidth(), getFieldWidth(), getFieldGap(), _bordersH + _windowDecorationH );
if ( theNewFieldWidth != -1 ) {
LOGGER.debug( "Field width changed to := " + theNewFieldWidth );
setFieldWidth( theNewFieldWidth );
}
break;
case NONE:
break;
}
}
};
// ---------------------------------------------------------------------
// WINDOW HEIGHT CHANGE HANDLER:
// ---------------------------------------------------------------------
private ChangeListener _onWindowHeightChangedEventHandler = new ChangeListener() {
@Override
public void changed( ObservableValue extends Number> observable, Number aOldValue, Number aNewValue ) {
_lastResizeTimeInMs = System.currentTimeMillis();
if ( Math.abs( aNewValue.doubleValue() - aOldValue.doubleValue() ) > 10 ) return;
// -------------------------------------------------------------
// Calculate the sum of the H-borders:
// -------------------------------------------------------------
if ( Double.isNaN( _bordersV ) && !Double.isNaN( aOldValue.doubleValue() ) ) {
initBordersV( aOldValue.doubleValue() );
initMinStageHeight();
}
// -------------------------------------------------------------
switch ( getScaleMode() ) {
case SCALE_GRID:
int theNewViewportHeight = toScaledViewportDimension( aNewValue.doubleValue(), getViewportHeight(), getFieldHeight(), getFieldGap(), _bordersV + _windowDecorationV );
if ( theNewViewportHeight != -1 ) {
LOGGER.debug( "Viewport height changed to := " + theNewViewportHeight );
setViewportHeight( theNewViewportHeight );
}
break;
case SCALE_FIELDS:
int theNewFieldHeight = toScaledFieldDimension( aNewValue.doubleValue(), getViewportHeight(), getFieldHeight(), getFieldGap(), _bordersV + _windowDecorationV );
if ( theNewFieldHeight != -1 ) {
LOGGER.debug( "Field height changed to := " + theNewFieldHeight );
setFieldHeight( theNewFieldHeight );
}
break;
case NONE:
break;
}
}
};
// /////////////////////////////////////////////////////////////////////
// LIFECYLCE:
// /////////////////////////////////////////////////////////////////////
@Override
public void dispose() {
_window.widthProperty().removeListener( _onWindowWidthChangedEventHandler );
_window.heightProperty().removeListener( _onWindowHeightChangedEventHandler );
_window = null;
}
// /////////////////////////////////////////////////////////////////////
// HELPER:
// /////////////////////////////////////////////////////////////////////
private int toScaledViewportDimension( double aNewWindowDim, int aViewportDim, int aFieldDim, int aFieldGap, double aBordersDim ) {
if ( !Double.isNaN( aBordersDim ) && aNewWindowDim != 1.0 ) {
double theCheckerboardDim = aNewWindowDim - aBordersDim;
// Just consider top (left) gap as we drag:
theCheckerboardDim -= aFieldGap;
int theViewportDim = (int) Math.round( theCheckerboardDim / (aFieldDim + aFieldGap) );
if ( theViewportDim != aViewportDim ) {
try {
return theViewportDim;
}
catch ( VetoRuntimeException e ) {
fxResizeStage();
}
}
}
return -1;
}
private int toScaledFieldDimension( double aNewWindowDim, int aViewportDim, int aFieldDim, int aFieldGap, double aBordersDim ) {
if ( !Double.isNaN( aBordersDim ) && aNewWindowDim != 1.0 ) {
double theCheckerboardDim = aNewWindowDim - aBordersDim;
// Just consider top (left) gap as we drag:
theCheckerboardDim -= aFieldGap;
int theFieldDim = (int) Math.round( (theCheckerboardDim - ((aViewportDim - 1) * aFieldGap)) / aViewportDim );
if ( theFieldDim != aFieldDim ) {
try {
return theFieldDim;
}
catch ( VetoRuntimeException e ) {
fxResizeStage();
}
}
}
return -1;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy