Skip to content

Commit

Permalink
Merge Dev for v0.4 deploy (#191)
Browse files Browse the repository at this point in the history
* Fixed NullPointerException due to race condition in Frontend#sizeChanged. (#177)
* Fix recursive position search in containers for MovementAnimation#toComponentView (#175)
* Fixed recursive child position search for GridPanes.
* Fixed FileDialogs returning list of nulls instead of empty optional.
* Empty grid columns and rows no longer get rendered size 0.0 in case of fixed dimensions. (#180)
* BoardGameScene getting shown blurred if showGameScene gets called after hideMenuScene. (#184)
* Removed all non-null assertions replacing them with safe calls or state checks. (#185)
* Fixed wrong parent in rollback search for containers after drag and drop. (#189)
* Cleanup code fixing detect findings.
* Fix toComponentView animation missing layoutFromCenter offset for Grid. (#190)
* Fix toComponentView animation missing layoutFromCenter offset for Grid; toComponentView animation now working with scale.
* Fix drag and drop for grid.
* Fix drag and drop target for custom inter-cell-alignments
  • Loading branch information
dominikmaeckel committed Sep 22, 2021
1 parent 7e051b8 commit ab4db9e
Show file tree
Hide file tree
Showing 20 changed files with 245 additions and 134 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[0.4]: https://github.com/tudo-aqua/bgw/releases/tag/v0.4
[0.3]: https://github.com/tudo-aqua/bgw/releases/tag/v0.3
[0.2]: https://github.com/tudo-aqua/bgw/releases/tag/v0.2
[0.1]: https://github.com/tudo-aqua/bgw/releases/tag/v0.1
Expand All @@ -11,6 +12,16 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.4] - To be released

### Fixed
- Empty grid columns and rows no longer get rendered size 0.0 in case of fixed dimensions.
- Race condition while changing GameScenes caused by slow renderer.
- FileDialogs returning list of nulls instead of empty optional.
- Snap back from Drag and Drop.
- MovementAnimation#toComponentView offset when animating to GridPane.
- BoardGameScene getting shown blurred if showGameScene gets called after hideMenuScene.

## [0.3] - 09. Sep. 2021

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ class MovementAnimation<T : ComponentView>(
*/
constructor(componentView: T, byX: Number = 0.0, byY: Number = 0.0, duration: Int = 1000) : this(
componentView = componentView,
toX = componentView.posX + byX.toDouble(),
toY = componentView.posY + byY.toDouble(),
fromX = componentView.parent?.getChildPosition(componentView)?.xCoord?:componentView.posX,
fromY = componentView.parent?.getChildPosition(componentView)?.yCoord?:componentView.posY,
toX = (componentView.parent?.getChildPosition(componentView)?.xCoord?:componentView.posX) + byX.toDouble(),
toY = (componentView.parent?.getChildPosition(componentView)?.yCoord?:componentView.posY) + byY.toDouble(),
duration = duration
)

Expand All @@ -102,11 +104,37 @@ class MovementAnimation<T : ComponentView>(
val pathToComponent = scene.findPathToChild(componentView).dropLast(1)
val pathToDestination = scene.findPathToChild(toComponentViewPosition).dropLast(1)

//Sum relative positions
val componentAbsoluteX = pathToComponent.sumOf { it.posX }
val componentAbsoluteY = pathToComponent.sumOf { it.posY }
val destinationAbsoluteX = pathToDestination.sumOf { it.posX }
val destinationAbsoluteY = pathToDestination.sumOf { it.posY }
//Sum relative positions and rotation and multiply scale
var componentAbsoluteX = 0.0
var componentAbsoluteY = 0.0
var destinationAbsoluteX = 0.0
var destinationAbsoluteY = 0.0

var componentScaleX = 1.0
var componentScaleY = 1.0
var destinationScaleX = 1.0
var destinationScaleY = 1.0

var componentRotation = 0.0
var destinationRotation = 0.0

pathToComponent.forEach {
val pos = it.parent?.getActualChildPosition(it)
componentAbsoluteX += pos?.xCoord?:it.actualPosX
componentAbsoluteY += pos?.yCoord?:it.actualPosY
componentScaleX *= it.scaleX
componentScaleY *= it.scaleY
componentRotation += it.rotation
}

pathToDestination.forEach {
val pos = it.parent?.getActualChildPosition(it)
destinationAbsoluteX += pos?.xCoord?:it.actualPosX
destinationAbsoluteY += pos?.yCoord?:it.actualPosY
destinationScaleX *= it.scaleX
destinationScaleY *= it.scaleY
destinationRotation += it.rotation
}

return MovementAnimation(
componentView = componentView,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal class AnimationBuilder {
scene: Scene<out ComponentView>,
anim: MovementAnimation<*>
): javafx.animation.Animation {
val node = scene.componentsMap[anim.componentView]!!
val node = mapNode(scene, anim.componentView)

//Move node to initial position
node.layoutX = anim.fromX
Expand Down Expand Up @@ -84,7 +84,7 @@ internal class AnimationBuilder {
scene: Scene<out ComponentView>,
anim: RotationAnimation<*>
): javafx.animation.Animation {
val node = scene.componentsMap[anim.componentView]!!
val node = mapNode(scene, anim.componentView)

//Move node to initial position
node.rotate = anim.fromAngle
Expand All @@ -110,7 +110,7 @@ internal class AnimationBuilder {
scene: Scene<out ComponentView>,
anim: ScaleAnimation<*>
): javafx.animation.Animation {
val node = scene.componentsMap[anim.componentView]!!
val node = mapNode(scene, anim.componentView)

//Set initial scale
node.scaleX = anim.fromScaleX
Expand All @@ -134,7 +134,7 @@ internal class AnimationBuilder {
scene: Scene<out ComponentView>,
anim: FadeAnimation<*>
): javafx.animation.Animation {
val node = scene.componentsMap[anim.componentView]!!
val node = mapNode(scene, anim.componentView)

//Set initial opacity
node.opacity = anim.fromOpacity
Expand All @@ -156,7 +156,7 @@ internal class AnimationBuilder {
scene: Scene<out ComponentView>,
anim: FlipAnimation<*>
): javafx.animation.Animation {
val node = scene.componentsMap[anim.componentView]!!
val node = mapNode(scene, anim.componentView)
val fromVisual = VisualBuilder.buildVisual(anim.fromVisual)
val toVisual = VisualBuilder.buildVisual(anim.toVisual).apply { scaleX = 0.0 }

Expand Down Expand Up @@ -258,5 +258,11 @@ internal class AnimationBuilder {
anim.onFinished?.invoke(AnimationFinishedEvent())
}
}

/**
* Maps [ComponentView] to FX node.
*/
private fun mapNode(scene: Scene<out ComponentView>, componentView : ComponentView) =
checkNotNull(scene.componentsMap[componentView]) { "Creating animation for node that is not in scene." }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ internal class DialogBuilder {

//Add expandable content for exception stack trace in case of AlertType.EXCEPTION
if (dialog.dialogType == DialogType.EXCEPTION) {
dialogPane.expandableContent = TextArea(dialog.exception!!.stackTraceToString()).apply {
dialogPane.expandableContent = TextArea(dialog.exception.stackTraceToString()).apply {
isEditable = false
isWrapText = true
maxWidth = Double.MAX_VALUE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ internal class FXConverters private constructor() {
/**
* Converts the [javafx.scene.input.KeyCode] to [KeyCode].
*/
@Suppress("LongMethod")
internal fun FXKeyCode.toKeyCode(): KeyCode = when (this) {
FXKeyCode.SHIFT -> KeyCode.SHIFT
FXKeyCode.CONTROL -> KeyCode.CONTROL
Expand Down
30 changes: 15 additions & 15 deletions bgw-core/src/main/kotlin/tools/aqua/bgw/builder/Frontend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,11 @@ internal class Frontend : Application() {
* @return [gamePane] for [boardGameScene], [menuPane] for [menuScene] and `null` for other parameters.
*/
internal fun tools.aqua.bgw.core.Scene<*>.mapToPane(): Pane =
when (this) {
checkNotNull(when (this) {
boardGameScene -> gamePane
menuScene -> menuPane
else -> null
}?:throw IllegalStateException()
})

/**
* Returns scene associated to pane.
Expand All @@ -339,7 +339,7 @@ internal class Frontend : Application() {
* @param stage application stage.
*/
internal fun startApplication(stage: Stage) {
primaryStage = stage.apply {
val primaryStage = stage.apply {
//Initialize default DECORATED stage style allowing minimizing
initStyle(StageStyle.DECORATED)

Expand Down Expand Up @@ -367,6 +367,7 @@ internal class Frontend : Application() {
if (!isFullScreen && !isMaximized)
height = nV
}
titleProperty.setGUIListenerAndInvoke(titleProperty.value) { _, nV -> title = nV }

maximizedProperty().addListener { _, _, nV -> maximizedProperty.setSilent(nV) }
fullScreenProperty().addListener { _, _, nV -> fullscreenProperty.setSilent(nV) }
Expand All @@ -386,18 +387,15 @@ internal class Frontend : Application() {
menuScene?.let { menuPane = buildMenu(it) }
boardGameScene?.let { gamePane = buildGame(it) }

titleProperty.setGUIListenerAndInvoke(titleProperty.value) { _, nV ->
primaryStage?.title = nV
}

backgroundProperty.setGUIListenerAndInvoke(backgroundProperty.value) { _, nV ->
backgroundPane.children.clear()
backgroundPane.children.add(VisualBuilder.buildVisual(nV).apply {
prefWidthProperty().bind(primaryStage!!.widthProperty())
prefHeightProperty().bind(primaryStage!!.heightProperty())
prefWidthProperty().bind(primaryStage.widthProperty())
prefHeightProperty().bind(primaryStage.heightProperty())
})
}

this.primaryStage = primaryStage
updateScene()
}

Expand All @@ -422,13 +420,13 @@ internal class Frontend : Application() {
Optional.ofNullable(
when (dialog.mode) {
OPEN_FILE ->
listOf(FileChooserBuilder.buildFileChooser(dialog).showOpenDialog(primaryStage))
FileChooserBuilder.buildFileChooser(dialog).showOpenDialog(primaryStage)?.let { listOf(it) }
OPEN_MULTIPLE_FILES ->
FileChooserBuilder.buildFileChooser(dialog).showOpenMultipleDialog(primaryStage)
SAVE_FILE ->
listOf(FileChooserBuilder.buildFileChooser(dialog).showSaveDialog(primaryStage))
FileChooserBuilder.buildFileChooser(dialog).showSaveDialog(primaryStage)?.let { listOf(it) }
CHOOSE_DIRECTORY ->
listOf(FileChooserBuilder.buildDirectoryChooser(dialog).showDialog(primaryStage))
FileChooserBuilder.buildDirectoryChooser(dialog).showDialog(primaryStage)?.let { listOf(it) }
}
)

Expand Down Expand Up @@ -456,14 +454,16 @@ internal class Frontend : Application() {
*/
private fun fadeMenu(fadeIn: Boolean, fadeTime: Double) {
menuPane?.apply {
FadeTransition(Duration.millis(fadeTime / 2), menuPane).apply {
if (!fadeIn)
menuPane = null

FadeTransition(Duration.millis(fadeTime / 2), this).apply {
fromValue = if (fadeIn) 0.0 else 1.0
toValue = if (fadeIn) 1.0 else 0.0
interpolator = Interpolator.EASE_OUT
onFinished = EventHandler {
if (!fadeIn) {
menuPane = null
if (boardGameScene != null) boardGameScene!!.unlock()
boardGameScene?.unlock()
updateScene()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,59 +89,74 @@ internal class LayoutNodeBuilder {
}
}

//Calculate row heights
gridView.renderedRowHeights = DoubleArray(grid.rows) {
grid.getRow(it).maxOf { entry -> entry?.let { t ->
val fixedHeight = grid.getRowHeight(it)
if(fixedHeight == ROW_HEIGHT_AUTO)
t.layoutBounds.height + t.posY
else
fixedHeight
} ?: 0.0 }
var height = grid.getRowHeight(it)

if(height == ROW_HEIGHT_AUTO) {
height = grid.getRow(it).filterNotNull().maxOfOrNull { entry ->
entry.let { t -> t.layoutBounds.height + t.posY }
}?:0.0
}

height
}

//Calculate column widths
gridView.renderedColWidths = DoubleArray(grid.columns) {
grid.getColumn(it).maxOf { entry -> entry?.let { t ->
val fixedWidth = grid.getColumnWidth(it)
if(fixedWidth == COLUMN_WIDTH_AUTO)
t.layoutBounds.width + t.posX
else
fixedWidth
} ?: 0.0 }
var width = grid.getColumnWidth(it)

if(width == COLUMN_WIDTH_AUTO) {
width = grid.getColumn(it).filterNotNull().maxOfOrNull { entry ->
entry.let { t -> t.layoutBounds.width + t.posX }
}?:0.0
}

width
}

gridView.width =
gridView.renderedColWidths.sum() + (gridView.renderedColWidths.size - 1) * gridView.spacing
gridView.height =
gridView.renderedRowHeights.sum() + (gridView.renderedRowHeights.size - 1) * gridView.spacing

nodes.forEach { triple ->
val colIndex = triple.first.first
val rowIndex = triple.first.second
val component = triple.second
val node = triple.third
val posX = (0 until colIndex).sumOf { gridView.renderedColWidths[it] } + colIndex * gridView.spacing
val posY = (0 until rowIndex).sumOf { gridView.renderedRowHeights[it] } + rowIndex * gridView.spacing
nodes.forEach { triple -> refreshGridNode(gridView, triple) }
}

/**
* Refreshes grid node.
*/
private fun FXPane.refreshGridNode(
gridView: GridPane<out ComponentView>,
triple: Triple<Pair<Int, Int>, ComponentView, Node>) {

val colIndex = triple.first.first
val rowIndex = triple.first.second
val component = triple.second
val node = triple.third
val posX = (0 until colIndex).sumOf { gridView.renderedColWidths[it] } + colIndex * gridView.spacing
val posY = (0 until rowIndex).sumOf { gridView.renderedRowHeights[it] } + rowIndex * gridView.spacing

children.add(node.apply {
val nodeWidth = component.layoutBounds.width
val nodeHeight = component.layoutBounds.height

children.add(node.apply {
val nodeWidth = component.layoutBounds.width
val nodeHeight = component.layoutBounds.height

//Calculate delta due to scale and rotation
val deltaX = (nodeWidth - component.width) / 2
val deltaY = (nodeHeight - component.height) / 2

//Calculate anchor point for flush TOP_LEFT placement
val anchorX = posX + component.posX + deltaX
val anchorY = posY + component.posY + deltaY

//Account for centering
val centerMode = gridView.getCellCenterMode(columnIndex = colIndex, rowIndex = rowIndex)
val remainingSpaceX = gridView.renderedColWidths[colIndex] - nodeWidth - component.posX
val remainingSpaceY = gridView.renderedRowHeights[rowIndex] - nodeHeight - component.posY

layoutX = anchorX + remainingSpaceX * centerMode.horizontalAlignment.positionMultiplier
layoutY = anchorY + remainingSpaceY * centerMode.verticalAlignment.positionMultiplier
})
}
//Calculate delta due to scale and rotation
val deltaX = (nodeWidth - component.width) / 2
val deltaY = (nodeHeight - component.height) / 2

//Calculate anchor point for flush TOP_LEFT placement
val anchorX = posX + component.posX + deltaX
val anchorY = posY + component.posY + deltaY

//Account for centering
val centerMode = gridView.getCellCenterMode(columnIndex = colIndex, rowIndex = rowIndex)
val remainingSpaceX = gridView.renderedColWidths[colIndex] - nodeWidth - component.posX
val remainingSpaceY = gridView.renderedRowHeights[rowIndex] - nodeHeight - component.posY

layoutX = anchorX + remainingSpaceX * centerMode.horizontalAlignment.positionMultiplier
layoutY = anchorY + remainingSpaceY * centerMode.verticalAlignment.positionMultiplier
})
}
}
}
Loading

0 comments on commit ab4db9e

Please sign in to comment.