All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tools.aqua.bgw.builder.LayoutNodeBuilder.kt Maven / Gradle / Ivy

There is a newer version: 0.5
Show newest version
/*
 *    Copyright 2021 The BoardGameWork Authors
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package tools.aqua.bgw.builder

import javafx.scene.Node
import javafx.scene.layout.Region
import tools.aqua.bgw.builder.NodeBuilder.Companion.buildChildren
import tools.aqua.bgw.components.ComponentView
import tools.aqua.bgw.components.layoutviews.GridPane
import tools.aqua.bgw.components.layoutviews.GridPane.Companion.COLUMN_WIDTH_AUTO
import tools.aqua.bgw.components.layoutviews.GridPane.Companion.ROW_HEIGHT_AUTO
import tools.aqua.bgw.components.layoutviews.LayoutView
import tools.aqua.bgw.components.layoutviews.Pane
import tools.aqua.bgw.core.Scene
import tools.aqua.bgw.util.ComponentViewGrid
import javafx.scene.layout.Pane as FXPane

/**
 * LayoutNodeBuilder.
 * Factory for all BGW layout components.
 */
internal class LayoutNodeBuilder {
	companion object {
		/**
		 * Switches between LayoutComponents.
		 */
		internal fun buildLayoutView(
			scene: Scene,
			layoutViewView: LayoutView
		): Region =
			when (layoutViewView) {
				is GridPane<*> ->
					buildGrid(scene, layoutViewView)
				is Pane<*> ->
					buildPane(scene, layoutViewView)
			}
		
		private fun buildPane(scene: Scene, pane: Pane): Region =
			FXPane().apply {
				pane.observableComponents.setGUIListenerAndInvoke(listOf()) { oldValue, _ ->
					buildChildren(scene, pane.observableComponents, oldValue.toSet())
				}
			}
		
		/**
		 * Builds [GridPane].
		 */
		private fun buildGrid(scene: Scene, gridView: GridPane): Region =
			FXPane().apply {
				gridView.updateGui = { refreshGrid(scene, gridView) }.also { it.invoke() }
			}
		
		/**
		 * Refreshes [GridPane].
		 */
		private fun FXPane.refreshGrid(scene: Scene, gridView: GridPane) {
			val grid: ComponentViewGrid = gridView.grid
			
			children.clear()
			
			//Build Nodes
			val nodes = ArrayList, ComponentView, Node>>()
			grid.getColumns().forEachIndexed { colIndex, col ->
				for (rowIndex in col.indices) {
					val gameComponent = col[rowIndex] ?: continue
					
					nodes.add(
						Triple(
							Pair(colIndex, rowIndex),
							gameComponent,
							NodeBuilder.build(scene = scene, componentView = gameComponent)
						)
					)
				}
			}
			
			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 }
			}
			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 }
			}
			
			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
				
				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
				})
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy