tools.aqua.bgw.core.Scene.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bgw-core Show documentation
Show all versions of bgw-core Show documentation
A framework for board game applications.
/*
* 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.
*/
@file:Suppress("unused")
package tools.aqua.bgw.core
import javafx.scene.layout.StackPane
import tools.aqua.bgw.animation.Animation
import tools.aqua.bgw.builder.DragDataObject
import tools.aqua.bgw.builder.DragTargetObject
import tools.aqua.bgw.components.ComponentView
import tools.aqua.bgw.components.DynamicComponentView
import tools.aqua.bgw.components.RootComponent
import tools.aqua.bgw.observable.DoubleProperty
import tools.aqua.bgw.observable.ObservableArrayList
import tools.aqua.bgw.observable.ObservableList
import tools.aqua.bgw.observable.Property
import tools.aqua.bgw.util.CoordinatePlain
import tools.aqua.bgw.visual.Visual
/**
* Baseclass for BGW scenes.
*
* @param width [Scene] width in virtual coordinates.
* @param height [Scene] height in virtual coordinates.
* @param background [Scene] [background] [Visual].
*
* @see BoardGameScene
* @see MenuScene
*/
sealed class Scene(width: Number, height: Number, background: Visual) {
/**
* [MutableList] containing all [ComponentView]s currently below mouse position while performing a drag gesture.
*/
internal val dragTargetsBelowMouse: MutableList = mutableListOf()
/**
* [Property] for the currently dragged [ComponentView] encapsulated in a [DragDataObject]
* or null if no [DynamicComponentView] is currently dragged.
*/
internal val draggedDataProperty: Property = Property(null)
/**
* Currently dragged [ComponentView] encapsulated in a [DragDataObject]
* or null if no [DynamicComponentView] is currently dragged.
*/
val draggedComponent: DynamicComponentView?
get() = draggedDataProperty.value?.draggedComponent
/**
* The root node of this [Scene].
* Use it to compare the parent [Property] of any [ComponentView]
* to find out whether it was directly added to the [Scene].
*/
@Suppress("LeakingThis")
val rootNode: RootComponent = RootComponent(this)
/**
* The width of this [Scene] in virtual coordinates.
*/
val width: Double = width.toDouble()
/**
* The height of this [Scene] in virtual coordinates.
*/
val height: Double = height.toDouble()
/**
* All [ComponentView]s on the root node.
*/
internal val rootComponents: ObservableList = ObservableArrayList()
/**
* [Property] for the [background] [Visual] of this [Scene].
*/
internal val backgroundProperty: Property = Property(background)
/**
* The background [Visual] of this [Scene].
*/
var background: Visual
get() = backgroundProperty.value
set(value) {
backgroundProperty.value = value
}
/**
* [Property] for the [opacity] of the [background] of this [Scene].
*/
internal val opacityProperty = DoubleProperty(1.0)
/**
* Opacity of the [background] of this [Scene].
*/
var opacity: Double
get() = opacityProperty.value
set(value) {
require(value in 0.0..1.0) { "Value must be between 0 and 1 inclusive." }
opacityProperty.value = value
}
/**
* [Property] for the currently displayed zoom detail of this [Scene].
*/
internal val zoomDetailProperty: Property = Property(
CoordinatePlain(0, 0, width, height)
)
/**
* The currently displayed zoom detail of this [Scene].
*/
internal var zoomDetail
get() = zoomDetailProperty.value
set(value) {
zoomDetailProperty.value = value
}
/**
* [Map] for all [ComponentView]s to their [StackPane]s.
*/
internal val componentsMap: MutableMap = HashMap()
/**
* All [Animation]s currently playing.
*/
internal val animations: ObservableList = ObservableArrayList()
/**
* Adds all given [ComponentView]s to the root node and [rootComponents] list.
*
* @param components components to add.
*/
fun addComponents(vararg components: T) {
rootComponents.addAll(components.toList().onEach {
check(it.parent == null) { "Component $it is already contained in another container." }
it.parent = rootNode
})
}
/**
* Removes all given [ComponentView]s from the root node and [rootComponents] list.
*
* @param components components to remove.
*/
fun removeComponents(vararg components: T) {
rootComponents.removeAll(components.toList().onEach {
it.parent = null
})
}
/**
* Removes all [ComponentView]s from the root node and [rootComponents] list.
*/
fun clearComponents() {
rootComponents.forEach { it.parent = null }
rootComponents.clear()
}
/**
* Plays given [Animation].
*
* @param animation animation to play.
*/
fun playAnimation(animation: Animation) {
animations.add(animation)
}
// /**
// * Zooms [Scene] to given bounds.
// *
// * @param fromX left bound.
// * @param fromY top bound.
// * @param toX right bound.
// * @param toY bottom bound.
// */
// fun zoomTo(fromX: Number, fromY: Number, toX: Number, toY: Number): Unit =
// zoomTo(fromX.toDouble(), fromY.toDouble(), toX.toDouble(), toY.toDouble())
// /**
// * Zooms [Scene] to given bounds.
// *
// * @param from top left [Coordinate].
// * @param to bottom right [Coordinate].
// */
// fun zoomTo(from: Coordinate, to: Coordinate): Unit =
// zoomTo(from.xCoord, from.yCoord, to.xCoord, to.yCoord)
// /**
// * Zooms [Scene] to given bounds.
// *
// * @param to layout bounds.
// */
// fun zoomTo(to: CoordinatePlain): Unit =
// zoomTo(to.topLeft, to.bottomRight)
// /**
// * Zooms scene out to max bounds.
// */
// fun zoomOut() {
// zoomDetail = CoordinatePlain(0, 0, width, height)
// }
// /**
// * Sets [zoomDetailProperty] to given bounds.
// * Checks for targets out of layout bounds.
// */
// private fun zoomTo(fromX: Double, fromY: Double, toX: Double, toY: Double) {
// when {
// fromX < 0 || fromY < 0 || toX < 0 || toY < 0 ->
// throw IllegalArgumentException("All bounds must be positive.")
// fromX >= toX ->
// throw IllegalArgumentException("Right X bound is not greater than left X bound.")
// fromY >= toY ->
// throw IllegalArgumentException("Bottom Y bound is not greater than top Y bound.")
// toX > width ->
// throw IllegalArgumentException("Right X bound is greater than scene width.")
// toY > height ->
// throw IllegalArgumentException("Bottom Y bound is greater than scene height.")
// }
//
// zoomDetail = CoordinatePlain(fromX, fromY, toX, toY)
// }
/**
* Searches [node] recursively through the visual tree and logs path where the [node] appears
* as first component and the [rootNode] as last.
*
* @param node child to find.
*
* @return path to child.
*
* @throws IllegalStateException if child was not contained in this [Scene].
*/
fun findPathToChild(node: ComponentView): List {
if (node is RootComponent<*>) {
check(node == rootNode) { "Child is contained in another scene" }
return listOf(rootNode)
}
checkNotNull(node.parent) { "Encountered component $node that is not contained in a scene." }
return mutableListOf(node) + findPathToChild(node.parent!!)
}
/**
* Removes [child] from the root.
*/
internal fun removeChild(child: ComponentView) {
try {
@Suppress("UNCHECKED_CAST")
this.removeComponents(child as T)
} catch (_: ClassCastException) {
}
}
}