Skip to content

Commit

Permalink
Set Z layer of ViewComponent within scene without re-adding it to the…
Browse files Browse the repository at this point in the history
… parent container (#362)

Co-authored-by: Vadym.Kosin <Vadym.Kosin@tu-dortmund.de>
  • Loading branch information
FrankPabodie and FrankPabodie committed May 22, 2023
1 parent 5e57862 commit 6ab4c8e
Show file tree
Hide file tree
Showing 20 changed files with 364 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ All notable changes to this project will be documented in this file.
## [0.8] - 22.05.2023

### Added
- `setZIndex` ability for components to change there view order in parent components.
- `CameraPane` component.
- `HexagonView` component.
- `HexagonGrid` with two coordinate systems. axial and offset coordinates.
Expand Down
82 changes: 78 additions & 4 deletions bgw-gui/src/main/kotlin/tools/aqua/bgw/components/ComponentView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ package tools.aqua.bgw.components
import kotlin.math.floor
import tools.aqua.bgw.components.container.GameComponentContainer
import tools.aqua.bgw.components.layoutviews.LayoutView
import tools.aqua.bgw.components.layoutviews.Pane
import tools.aqua.bgw.core.Scene
import tools.aqua.bgw.event.*
import tools.aqua.bgw.observable.properties.BooleanProperty
import tools.aqua.bgw.observable.properties.DoubleProperty
import tools.aqua.bgw.observable.properties.LimitedDoubleProperty
import tools.aqua.bgw.observable.properties.Property
import tools.aqua.bgw.observable.properties.*
import tools.aqua.bgw.util.Coordinate
import tools.aqua.bgw.util.CoordinatePlain
import tools.aqua.bgw.visual.Visual
Expand All @@ -54,6 +52,28 @@ import tools.aqua.bgw.visual.Visual
abstract class ComponentView
internal constructor(posX: Number, posY: Number, width: Number, height: Number, visual: Visual) {

/**
* Property for the order of [ComponentView] inside of [parent].#
*
* @see zIndex
*/
val zIndexProperty: IntegerProperty = IntegerProperty(0)

/** for the order of [ComponentView] inside of [parent].# */
var zIndex: Int
get() = zIndexProperty.value
set(value) {
checkNotNull(parent) { "$this does not have a parent" }
if (parent is LayeredContainer<*>) {
try {
@Suppress("UNCHECKED_CAST")
(parent as LayeredContainer<ComponentView>).setZIndex(this, value)
} catch (_: ClassCastException) {
error("$parent is not a compatible container type")
}
}
}

/**
* The parent of this [ComponentView].
*
Expand Down Expand Up @@ -730,4 +750,58 @@ internal constructor(posX: Number, posY: Number, width: Number, height: Number,
*/
@Suppress("FunctionOnlyReturningConstant")
internal open fun getActualChildPosition(child: ComponentView): Coordinate? = null

/**
* Puts the [ComponentView] to the front inside its [parent] and Changes its [zIndex] accordingly.
*
* @throws IllegalStateException if the [ComponentView] does not have a parent
* @throws IllegalStateException if the [parent] is not [LayeredContainer] with the generic type
* [ComponentView]
*/
fun toFront() {
if (parent != null) {
zIndexProperty.value =
when (parent) {
is GameComponentContainer<*> ->
(parent as GameComponentContainer<*>).components.last().zIndex
is Pane<*> -> (parent as Pane<*>).components.last().zIndex
is RootComponent<*> -> (parent as RootComponent<*>).scene.rootComponents.last().zIndex
else -> 0
}
}
checkNotNull(parent) { "$this does not have a parent" }
if (parent is LayeredContainer<*>) {
try {
@Suppress("UNCHECKED_CAST") (parent as LayeredContainer<ComponentView>).toFront(this)
} catch (_: ClassCastException) {
error("$parent is not a compatible container type")
}
}
}

/**
* Puts the [ComponentView] to the back inside its [parent] and Changes its [zIndex] accordingly.
*
* @throws IllegalStateException if the [ComponentView] does not have a parent
* @throws IllegalStateException if the [parent] is not [LayeredContainer] with the generic type
* [ComponentView]
*/
fun toBack() {
zIndexProperty.value =
when (parent) {
is GameComponentContainer<*> ->
(parent as GameComponentContainer<*>).components.first().zIndex
is Pane<*> -> (parent as Pane<*>).components.first().zIndex
is RootComponent<*> -> (parent as RootComponent<*>).scene.rootComponents.first().zIndex
else -> 0
}
checkNotNull(parent) { "$this does not have a parent" }
if (parent is LayeredContainer<*>) {
try {
@Suppress("UNCHECKED_CAST") (parent as LayeredContainer<ComponentView>).toBack(this)
} catch (_: ClassCastException) {
error("$parent is not a compatible container type")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2023 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.components

/** An interface that describes a container which can hold [ComponentView] that can be layered. */
interface LayeredContainer<T : ComponentView> {
/**
* Puts the [component] to the front inside the [LayeredContainer].
*
* @param component Child that is moved to the front.
*/
fun toFront(component: T)

/**
* Puts the [component] to the back inside the [LayeredContainer].
*
* @param component Child that is moved to the back.
*/
fun toBack(component: T)

/**
* Puts the [component] in the appropriate place compared to the other components by the [zIndex].
*
* @param component Child that is moved accordingly.
* @param zIndex The value that is used to compare the order of components.
*/
fun setZIndex(component: T, zIndex: Int)
}
46 changes: 44 additions & 2 deletions bgw-gui/src/main/kotlin/tools/aqua/bgw/components/RootComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ import tools.aqua.bgw.visual.Visual
* @property scene Scene of this root component.
*/
class RootComponent<T : ComponentView> internal constructor(val scene: Scene<T>) :
ComponentView(posX = 0, posY = 0, width = 0, height = 0, visual = Visual.EMPTY) {

ComponentView(posX = 0, posY = 0, width = 0, height = 0, visual = Visual.EMPTY),
LayeredContainer<T> {
/**
* Removes [component] from the [scene].
*
Expand All @@ -47,4 +47,46 @@ class RootComponent<T : ComponentView> internal constructor(val scene: Scene<T>)
throw IllegalArgumentException("$component type is incompatible with scene's type.")
}
}

/**
* Puts the [component] to the front inside the [LayeredContainer] and Changes its [zIndex]
* accordingly.
*
* @param component Child that is moved to the front.
*/
override fun toFront(component: T) {
zIndexProperty.value = this.scene.rootComponents.last().zIndex
if (this.scene.rootComponents.last() != component &&
this.scene.rootComponents.contains(component)) {
this.scene.rootComponents.removeSilent(component)
this.scene.rootComponents.add(component)
}
}

/**
* Puts the [component] to the back inside the [LayeredContainer] and Changes its [zIndex]
* accordingly.
*
* @param component Child that is moved to the back.
*/
override fun toBack(component: T) {
zIndexProperty.value = this.scene.rootComponents.first().zIndex
if (this.scene.rootComponents.first() != component &&
this.scene.rootComponents.contains(component)) {
this.scene.rootComponents.removeSilent(component)
this.scene.rootComponents.add(0, component)
}
}

/**
* Puts the [component] in the appropriate place compared to the other components by the [zIndex]
* and Changes its [zIndex] accordingly.
*
* @param component Child that is moved accordingly.
* @param zIndex The value that is used to compare the order of components.
*/
override fun setZIndex(component: T, zIndex: Int) {
component.zIndexProperty.value = zIndex
this.scene.rootComponents.sort(Comparator.comparingInt { it.zIndex })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package tools.aqua.bgw.components.container

import tools.aqua.bgw.components.ComponentView
import tools.aqua.bgw.components.DynamicComponentView
import tools.aqua.bgw.components.LayeredContainer
import tools.aqua.bgw.components.gamecomponentviews.GameComponentView
import tools.aqua.bgw.observable.ValueObserver
import tools.aqua.bgw.observable.lists.ObservableLinkedList
Expand Down Expand Up @@ -48,7 +49,8 @@ sealed class GameComponentContainer<T : DynamicComponentView>(
visual: Visual
) :
DynamicComponentView(posX = posX, posY = posY, width = width, height = height, visual = visual),
Iterable<T> {
Iterable<T>,
LayeredContainer<T> {
/**
* An [ObservableList] to store the [GameComponentView]s that are contained in this
* [GameComponentContainer].
Expand Down Expand Up @@ -286,4 +288,44 @@ sealed class GameComponentContainer<T : DynamicComponentView>(
* @return Iterator over the elements of this [GameComponentContainer].
*/
override fun iterator(): Iterator<T> = observableComponents.iterator()

/**
* Puts the [component] to the front inside the [LayeredContainer] and Changes its [zIndex]
* accordingly.
*
* @param component Child that is moved to the front.
*/
override fun toFront(component: T) {
component.zIndexProperty.value = observableComponents.last().zIndex
if (observableComponents.last() != component && observableComponents.contains(component)) {
observableComponents.removeSilent(component)
observableComponents.add(component)
}
}

/**
* Puts the [component] to the back inside the [LayeredContainer] and Changes its [zIndex]
* accordingly.
*
* @param component Child that is moved to the back.
*/
override fun toBack(component: T) {
component.zIndexProperty.value = observableComponents.first().zIndex
if (observableComponents.first() != component && observableComponents.contains(component)) {
observableComponents.removeSilent(component)
observableComponents.add(0, component)
}
}

/**
* Puts the [component] in the appropriate place compared to the other [observableComponents] by
* the [zIndex].
*
* @param component Child that is moved accordingly.
* @param zIndex The value that is used to compare the order of [observableComponents].
*/
override fun setZIndex(component: T, zIndex: Int) {
component.zIndexProperty.value = zIndex
observableComponents.sort(Comparator.comparingInt { zIndex })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package tools.aqua.bgw.components.layoutviews

import tools.aqua.bgw.components.ComponentView
import tools.aqua.bgw.components.LayeredContainer
import tools.aqua.bgw.observable.ValueObserver
import tools.aqua.bgw.observable.lists.ObservableArrayList
import tools.aqua.bgw.util.Coordinate
Expand All @@ -45,10 +46,10 @@ open class Pane<T : ComponentView>(
visual: Visual = Visual.EMPTY
) :
LayoutView<T>(posX = posX, posY = posY, width = width, height = height, visual = visual),
LayeredContainer<T>,
Iterable<T> {

internal val observableComponents: ObservableArrayList<T> = ObservableArrayList()

/**
* [onAdd] gets invoked anytime after a [ComponentView] is added to this [Pane] with the added
* [ComponentView] as its receiver.
Expand Down Expand Up @@ -268,4 +269,42 @@ open class Pane<T : ComponentView>(

/** Returns an iterator over the elements of this object. */
override fun iterator(): Iterator<T> = observableComponents.iterator()

/**
* Puts the [component] to the front inside the [LayeredContainer].
*
* @param component Child that is moved to the front.
*/
override fun toFront(component: T) {
component.zIndexProperty.value = observableComponents.last().zIndex
if (observableComponents.last() != component && observableComponents.contains(component)) {
observableComponents.removeSilent(component)
observableComponents.add(component)
}
}

/**
* Puts the [component] to the back inside the [LayeredContainer].
*
* @param component Child that is moved to the back.
*/
override fun toBack(component: T) {
component.zIndexProperty.value = observableComponents.first().zIndex
if (observableComponents.first() != component && observableComponents.contains(component)) {
observableComponents.removeSilent(component)
observableComponents.add(0, component)
}
}

/**
* Puts the [component] in the appropriate place compared to the other [observableComponents] by
* the [zIndex].
*
* @param component Child that is moved accordingly.
* @param zIndex The value that is used to compare the order of [observableComponents].
*/
override fun setZIndex(component: T, zIndex: Int) {
component.zIndexProperty.value = zIndex
observableComponents.sort(Comparator.comparingInt { it.zIndex })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,13 @@ abstract class ObservableList<T> : ReadonlyObservableList<T>() {
list.clear()
list.addAll(elements)
}

/**
* removes [o] from the [ObservableList] silently.
*
* @param o Element to be removed from this list, if present.
*
* @return `true` if this list contained the specified element.
*/
fun removeSilent(o: T): Boolean = list.remove(o)
}
Loading

0 comments on commit 6ab4c8e

Please sign in to comment.