From 73e397c2c2cdfdba23a552a9c4d1ae93f94422fd Mon Sep 17 00:00:00 2001 From: sulai <6741822+sulai@users.noreply.github.com> Date: Sat, 18 May 2019 22:09:02 +0200 Subject: [PATCH] Improved city button (#771) * improved CityButton * keep city button out of the way if a unit on the city tile is selected * fixed city button to be clickable always. however this hides units behind it * added click area for city buttons in an own layer, so the units render in front and the city button click area handles the clicks * city button: simplified code --- .../src/com/unciv/ui/tilegroups/CityButton.kt | 105 ++++++++++-------- core/src/com/unciv/ui/tilegroups/TileGroup.kt | 3 + .../unciv/ui/utils/CameraStageBaseScreen.kt | 19 ++-- .../com/unciv/ui/worldscreen/TileGroupMap.kt | 3 + 4 files changed, 75 insertions(+), 55 deletions(-) diff --git a/core/src/com/unciv/ui/tilegroups/CityButton.kt b/core/src/com/unciv/ui/tilegroups/CityButton.kt index 321f985d6a..38172b5ec9 100644 --- a/core/src/com/unciv/ui/tilegroups/CityButton.kt +++ b/core/src/com/unciv/ui/tilegroups/CityButton.kt @@ -2,9 +2,10 @@ package com.unciv.ui.tilegroups import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Interpolation +import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.Touchable -import com.badlogic.gdx.scenes.scene2d.actions.FloatAction +import com.badlogic.gdx.scenes.scene2d.actions.Actions import com.badlogic.gdx.scenes.scene2d.ui.Skin import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align @@ -24,9 +25,8 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski touchable= Touchable.disabled } - var offset: Float = 0f; - var isButtonMoved = false - var isLabelClicked = false + var buttonDownClickArea: Actor? = null + fun isButtonMoved() = buttonDownClickArea != null fun update(isCityViewable:Boolean) { val cityButtonText = city.population.population.toString() + " | " + city.name @@ -36,61 +36,37 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski label.setFontColor(city.civInfo.getNation().getSecondaryColor()) clear() + val unitTable = tileGroup.worldScreen.bottomBar.unitTable if (UnCivGame.Current.viewEntireMapForDebug || city.civInfo.isCurrentPlayer()) { // So you can click anywhere on the button to go to the city touchable = Touchable.enabled - label.touchable = Touchable.enabled - label.onClick { - isLabelClicked = true - // clicking on the label swings that label a little down to allow selection of units there. - // second tap on the label will go to the city screen - if (tileGroup.selectCity(city)) { - val floatAction = object : FloatAction(0f, 1f, 0.4f) { - override fun update(percent: Float) { - offset = -height*percent - update(isCityViewable) - } - - override fun end() { - isButtonMoved=true - } - } - floatAction.interpolation = Interpolation.swingOut - tileGroup.addAction(floatAction) - } - else { - UnCivGame.Current.screen = CityScreen(city) - } - } // clicking anywhere else on the button opens the city screen immediately - onClick { - // we need to check if the label was just clicked, as onClick will propagate - // the click event to its touchable parent. - if(!isLabelClicked) - UnCivGame.Current.screen = CityScreen(city) - isLabelClicked=false + onClickEvent { _, x, y -> + if (!isButtonMoved()) { + if (hit(x, y, true) == label) { + // clicking on the label swings that label a little down to allow selection of units there. + // this also allows to target selected units to move to the city tile from elsewhere. + // second tap on the label will go to the city screen + moveButtonDown() + if (unitTable.selectedUnit == null || unitTable.selectedUnit!!.currentMovement==0f) + tileGroup.selectCity(city) + } else { + UnCivGame.Current.screen = CityScreen(city) + } + } } } // when deselected, move city button to its original position - val unitTable = tileGroup.worldScreen.bottomBar.unitTable - if (isButtonMoved - && unitTable.selectedCity == null + if (isButtonMoved() + && unitTable.selectedCity != city && unitTable.selectedUnit?.currentTile != city.ccenterTile) { - isButtonMoved = false - val floatAction = object : FloatAction(0f, 1f, 0.4f) { - override fun update(percent: Float) { - offset = -height*(1-percent) - update(isCityViewable) - } - } - floatAction.interpolation = Interpolation.sine - tileGroup.addAction(floatAction) + moveButtonUp() } if (isCityViewable && city.health < city.getMaxHealth().toFloat()) { @@ -129,12 +105,47 @@ class CityButton(val city: CityInfo, internal val tileGroup: WorldTileGroup, ski add(iconTable).row() pack() setOrigin(Align.center) - center(tileGroup) - y += offset // for animated shifting of City button + centerX(tileGroup) touchable = Touchable.enabled + updateClickArea() } + private fun moveButtonDown() { + val floatAction = Actions.sequence( + Actions.moveBy(0f, -height, 0.4f, Interpolation.swingOut), + Actions.run { + buttonDownClickArea = Actor().onClick { + UnCivGame.Current.screen = CityScreen(city) + } + tileGroup.cityButtonLayerGroup.addActor(buttonDownClickArea) + updateClickArea() + } + ) + tileGroup.addAction(floatAction) + } + + private fun moveButtonUp() { + val floatAction = Actions.sequence( + Actions.moveBy(0f, height, 0.4f, Interpolation.sine), + Actions.run { + buttonDownClickArea?.remove() + buttonDownClickArea = null + } + ) + tileGroup.addAction(floatAction) + } + + private fun updateClickArea() { + buttonDownClickArea?.let { clickArea -> + clickArea.setSize(width, height) + clickArea.setScale(scaleX, scaleY) + clickArea.setOrigin(Align.center) + clickArea.centerX(tileGroup.cityButtonLayerGroup) + clickArea.y = y-height + clickArea.touchable = Touchable.enabled + } + } private fun getConstructionGroup(cityConstructions: CityConstructions): Group { val group= Group() diff --git a/core/src/com/unciv/ui/tilegroups/TileGroup.kt b/core/src/com/unciv/ui/tilegroups/TileGroup.kt index 80cb23e28f..c0ec2417f4 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroup.kt @@ -55,6 +55,8 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { protected var civilianUnitImage: UnitGroup? = null protected var militaryUnitImage: UnitGroup? = null + val cityButtonLayerGroup = Group().apply { isTransform=true; setSize(groupSize,groupSize);touchable=Touchable.childrenOnly } + val circleCrosshairFogLayerGroup = Group().apply { isTransform=false; setSize(groupSize,groupSize) } private val circleImage = ImageGetter.getCircle() // for blue and red circles on the tile private val crosshairImage = ImageGetter.getImage("OtherIcons/Crosshair.png") // for when a unit is targete @@ -76,6 +78,7 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { this.addActor(featureLayerGroup) this.addActor(miscLayerGroup) this.addActor(unitLayerGroup) + this.addActor(cityButtonLayerGroup) this.addActor(circleCrosshairFogLayerGroup) updateTileImage(false) diff --git a/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt b/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt index 34e706e860..0c93d72930 100644 --- a/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt +++ b/core/src/com/unciv/ui/utils/CameraStageBaseScreen.kt @@ -122,20 +122,23 @@ fun Label.setFontSize(size:Int): Label { } - -// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries - -fun Actor.onClick(sound:String,function: () -> Unit){ +/** same as [onClick], but sends the [InputEvent] and coordinates along */ +fun Actor.onClickEvent(sound: String = "click", function: (event: InputEvent?, x: Float, y: Float) -> Unit) { this.addListener(object : ClickListener() { override fun clicked(event: InputEvent?, x: Float, y: Float) { - if(sound!="") Sounds.play(sound) - function() + if (sound != "") Sounds.play(sound) + function(event, x, y) } - } ) + }) +} + +// If there are other buttons that require special clicks then we'll have an onclick that will accept a string parameter, no worries +fun Actor.onClick(sound: String = "click", function: () -> Unit) { + onClickEvent(sound) { _, _, _ -> function() } } fun Actor.onClick(function: () -> Unit): Actor { - onClick("click",function) + onClick("click", function) return this } diff --git a/core/src/com/unciv/ui/worldscreen/TileGroupMap.kt b/core/src/com/unciv/ui/worldscreen/TileGroupMap.kt index 0668006af4..9221079294 100644 --- a/core/src/com/unciv/ui/worldscreen/TileGroupMap.kt +++ b/core/src/com/unciv/ui/worldscreen/TileGroupMap.kt @@ -31,6 +31,7 @@ class TileGroupMap(val tileGroups:Collection, padding:Float): G val featureLayers = ArrayList() val miscLayers = ArrayList() val unitLayers = ArrayList() + val cityButtonLayers = ArrayList() val circleCrosshairFogLayers = ArrayList() for(group in tileGroups.sortedByDescending { it.tileInfo.position.x + it.tileInfo.position.y }){ @@ -39,6 +40,7 @@ class TileGroupMap(val tileGroups:Collection, padding:Float): G featureLayers.add(group.featureLayerGroup.apply { setPosition(group.x,group.y) }) miscLayers.add(group.miscLayerGroup.apply { setPosition(group.x,group.y) }) unitLayers.add(group.unitLayerGroup.apply { setPosition(group.x,group.y) }) + cityButtonLayers.add(group.cityButtonLayerGroup.apply { setPosition(group.x,group.y) }) circleCrosshairFogLayers.add(group.circleCrosshairFogLayerGroup.apply { setPosition(group.x,group.y) }) } for(group in baseLayers) addActor(group) @@ -46,6 +48,7 @@ class TileGroupMap(val tileGroups:Collection, padding:Float): G for(group in miscLayers) addActor(group) for(group in circleCrosshairFogLayers) addActor(group) for(group in tileGroups) addActor(group) // The above layers are for the visual layers, this is for the clickability + for(group in cityButtonLayers) addActor(group) // city buttons clickability for(group in unitLayers) addActor(group) // Aaand units above everything else.