mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 13:49:54 +07:00
Fix AutoScrollPane to not steal focus when disabled (#9930)
This commit is contained in:
@ -29,30 +29,91 @@ import com.badlogic.gdx.scenes.scene2d.utils.ClickListener
|
|||||||
e.g. zooming. For panes scrolling only horizontally, using this class is redundant.
|
e.g. zooming. For panes scrolling only horizontally, using this class is redundant.
|
||||||
To make the signature identical, including parameter names, all constructors have
|
To make the signature identical, including parameter names, all constructors have
|
||||||
been replicated functionally by checking the Gdx sources for which defaults to use.
|
been replicated functionally by checking the Gdx sources for which defaults to use.
|
||||||
|
|
||||||
|
** Commented-out code **
|
||||||
|
The FocusLossDetector listener and related lines are intentionally left in.
|
||||||
|
See #9612 - "AutoScrollPanes disable scrolling after clicking on them"
|
||||||
|
Actually, what happens is due to nested ScrollPanes. In the described case, it is
|
||||||
|
PickerPane.scrollPane, disabled in landscape mode, that "steals" the focus.
|
||||||
|
That is solved by removing the MouseOverListener when scrolling is disabled.
|
||||||
|
But there may be other similar situations - in such cases the FocusLossDetector might be a
|
||||||
|
solution, or at least it is valuable as debugging tool - ***who*** are we losing focus to?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
open class AutoScrollPane(widget: Actor?, style: ScrollPaneStyle = ScrollPaneStyle()): ScrollPane(widget,style) {
|
open class AutoScrollPane(
|
||||||
constructor(widget: Actor?, skin: Skin) : this(widget,skin.get(ScrollPaneStyle::class.java))
|
widget: Actor?,
|
||||||
constructor(widget: Actor?, skin: Skin, styleName: String) : this(widget,skin.get(styleName,ScrollPaneStyle::class.java))
|
style: ScrollPaneStyle = ScrollPaneStyle()
|
||||||
|
) : ScrollPane(widget, style) {
|
||||||
|
constructor(widget: Actor?, skin: Skin) : this(widget, skin.get(ScrollPaneStyle::class.java))
|
||||||
|
constructor(widget: Actor?, skin: Skin, styleName: String) : this(widget, skin.get(styleName,ScrollPaneStyle::class.java))
|
||||||
|
|
||||||
private var savedFocus: Actor? = null
|
private var savedFocus: Actor? = null
|
||||||
|
// private var isInMouseOverListener = false
|
||||||
|
|
||||||
class AutoScrollPaneClickListener(val autoScrollPane: AutoScrollPane):ClickListener(){
|
/** This listener "grabs" focus on mouse-over */
|
||||||
override fun enter(event: InputEvent?, x: Float, y: Float, pointer: Int, fromActor: Actor?) {
|
private class MouseOverListener : ClickListener() {
|
||||||
if (autoScrollPane.stage == null) return
|
// Note - using listenerActor is a bit more verbose than making this an inner class, but seems cleaner
|
||||||
if (fromActor?.isDescendantOf(autoScrollPane) == true) return
|
override fun enter(event: InputEvent, x: Float, y: Float, pointer: Int, fromActor: Actor?) {
|
||||||
if (autoScrollPane.savedFocus == null) autoScrollPane.savedFocus = autoScrollPane.stage.scrollFocus
|
val thisScroll = event.listenerActor as AutoScrollPane
|
||||||
autoScrollPane.stage.scrollFocus = autoScrollPane
|
val stage = thisScroll.stage ?: return
|
||||||
|
if (fromActor?.isDescendantOf(thisScroll) == true) return
|
||||||
|
// thisScroll.isInMouseOverListener = true
|
||||||
|
if (thisScroll.savedFocus == null) thisScroll.savedFocus = stage.scrollFocus
|
||||||
|
stage.scrollFocus = thisScroll
|
||||||
|
// thisScroll.isInMouseOverListener = false
|
||||||
}
|
}
|
||||||
override fun exit(event: InputEvent?, x: Float, y: Float, pointer: Int, toActor: Actor?) {
|
override fun exit(event: InputEvent, x: Float, y: Float, pointer: Int, toActor: Actor?) {
|
||||||
if (autoScrollPane.stage == null) return
|
val thisScroll = event.listenerActor as AutoScrollPane
|
||||||
if (toActor?.isDescendantOf(autoScrollPane) == true) return
|
val stage = thisScroll.stage ?: return
|
||||||
if (autoScrollPane.stage.scrollFocus == autoScrollPane) autoScrollPane.stage.scrollFocus = autoScrollPane.savedFocus
|
if (toActor?.isDescendantOf(thisScroll) == true) return
|
||||||
autoScrollPane.savedFocus = null
|
// thisScroll.isInMouseOverListener = true
|
||||||
|
if (stage.scrollFocus == thisScroll) stage.scrollFocus = thisScroll.savedFocus
|
||||||
|
thisScroll.savedFocus = null
|
||||||
|
// thisScroll.isInMouseOverListener = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A listener with the sole task to prevent losing focus un-wanted-ly. */
|
||||||
|
/*
|
||||||
|
private class FocusLossDetector : FocusListener() {
|
||||||
|
override fun scrollFocusChanged(event: FocusEvent, targetActor: Actor, focused: Boolean) {
|
||||||
|
// Here, targetActor is the old focus, relatedActor is the new one something wants to set (but hasn't yet)
|
||||||
|
// So we only want to react to focus losses to someone else...
|
||||||
|
// Note: super is empty, so no need to pass through
|
||||||
|
if (focused || targetActor != event.listenerActor)
|
||||||
|
return
|
||||||
|
// if (event.listenerActor == event.relatedActor) return // empirically shown to be redundant
|
||||||
|
if ((event.listenerActor as AutoScrollPane).isInMouseOverListener) return
|
||||||
|
val mouse = event.stage.screenToStageCoordinates(Vector2(Gdx.input.x.toFloat(), Gdx.input.y.toFloat()))
|
||||||
|
val hitActor = event.stage.hit(mouse.x, mouse.y, false)
|
||||||
|
val hitScroll = hitActor.firstAscendant(ScrollPane::class.java)
|
||||||
|
if (hitScroll == event.relatedActor) return
|
||||||
|
// if (hitScroll != event.listenerActor) return // empirically shown to be redundant
|
||||||
|
|
||||||
|
// Now we're sure it is an unwanted focus stealing attempt
|
||||||
|
event.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.addListener (AutoScrollPaneClickListener(this))
|
ensureListener()
|
||||||
|
// addListener(FocusLossDetector())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setScrollingDisabled(x: Boolean, y: Boolean) {
|
||||||
|
super.setScrollingDisabled(x, y)
|
||||||
|
ensureListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ensureListener() {
|
||||||
|
val existingListener = listeners.firstOrNull { it is MouseOverListener }
|
||||||
|
if (isScrollingDisabledX && isScrollingDisabledY) {
|
||||||
|
if (existingListener != null)
|
||||||
|
removeListener(existingListener)
|
||||||
|
} else {
|
||||||
|
if (existingListener == null)
|
||||||
|
addListener(MouseOverListener())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user