mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-25 21:03:15 -04:00
Tabbed pager architecture update (#6460)
* Change TabbedPager mechanism to communicate page activation * Change TabbedPager mechanism for fixed content * OptionsPopup better use of TabbedPager * TabbedPager arrow keys * After-merge patch Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
parent
5876047bda
commit
8385f814a6
@ -61,9 +61,7 @@ class DiplomacyOverviewTab (
|
|||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFixedContent(): WidgetGroup {
|
override fun getFixedContent() = fixedContent
|
||||||
return fixedContent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh content and determine landscape/portrait layout
|
// Refresh content and determine landscape/portrait layout
|
||||||
private fun update() {
|
private fun update() {
|
||||||
|
@ -52,10 +52,8 @@ class EmpireOverviewScreen(
|
|||||||
keyPressDispatcher = keyPressDispatcher,
|
keyPressDispatcher = keyPressDispatcher,
|
||||||
capacity = EmpireOverviewCategories.values().size)
|
capacity = EmpireOverviewCategories.values().size)
|
||||||
|
|
||||||
tabbedPager.addPage(Constants.close) {
|
tabbedPager.bindArrowKeys()
|
||||||
_, _ -> game.setWorldScreen()
|
tabbedPager.addClosePage { game.setWorldScreen() }
|
||||||
}
|
|
||||||
tabbedPager.getPageButton(0).setColor(0.75f, 0.1f, 0.1f, 1f)
|
|
||||||
|
|
||||||
for (category in EmpireOverviewCategories.values()) {
|
for (category in EmpireOverviewCategories.values()) {
|
||||||
val tabState = category.stateTester(viewingPlayer)
|
val tabState = category.stateTester(viewingPlayer)
|
||||||
@ -70,17 +68,8 @@ class EmpireOverviewScreen(
|
|||||||
icon, iconSize,
|
icon, iconSize,
|
||||||
disabled = tabState != EmpireOverviewTabState.Normal,
|
disabled = tabState != EmpireOverviewTabState.Normal,
|
||||||
shortcutKey = category.shortcutKey,
|
shortcutKey = category.shortcutKey,
|
||||||
scrollAlign = category.scrollAlign,
|
scrollAlign = category.scrollAlign
|
||||||
fixedContent = pageObject.getFixedContent(),
|
)
|
||||||
onDeactivation = { _, _, scrollY -> pageObject.deactivated(scrollY) }
|
|
||||||
) {
|
|
||||||
index, name ->
|
|
||||||
val scrollY = pageObject.activated()
|
|
||||||
if (scrollY != null) tabbedPager.setPageScrollY(index, scrollY)
|
|
||||||
if (name == "Stats")
|
|
||||||
game.settings.addCompletedTutorialTask("See your stats breakdown")
|
|
||||||
game.settings.lastOverviewPage = name
|
|
||||||
}
|
|
||||||
if (category.name == page)
|
if (category.name == page)
|
||||||
tabbedPager.selectPage(index)
|
tabbedPager.selectPage(index)
|
||||||
}
|
}
|
||||||
@ -99,6 +88,6 @@ class EmpireOverviewScreen(
|
|||||||
|
|
||||||
fun resizePage(tab: EmpireOverviewTab) {
|
fun resizePage(tab: EmpireOverviewTab) {
|
||||||
val category = (pageObjects.entries.find { it.value == tab } ?: return).key
|
val category = (pageObjects.entries.find { it.value == tab } ?: return).key
|
||||||
tabbedPager.replacePage(category.name, tab, tab.getFixedContent())
|
tabbedPager.replacePage(category.name, tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package com.unciv.ui.overviewscreen
|
|||||||
|
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
|
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
import com.unciv.ui.utils.BaseScreen
|
import com.unciv.ui.utils.BaseScreen
|
||||||
|
import com.unciv.ui.utils.TabbedPager
|
||||||
import com.unciv.ui.utils.packIfNeeded
|
import com.unciv.ui.utils.packIfNeeded
|
||||||
import com.unciv.ui.utils.toLabel
|
import com.unciv.ui.utils.toLabel
|
||||||
|
|
||||||
@ -13,17 +13,18 @@ abstract class EmpireOverviewTab (
|
|||||||
val viewingPlayer: CivilizationInfo,
|
val viewingPlayer: CivilizationInfo,
|
||||||
val overviewScreen: EmpireOverviewScreen,
|
val overviewScreen: EmpireOverviewScreen,
|
||||||
persistedData: EmpireOverviewTabPersistableData? = null
|
persistedData: EmpireOverviewTabPersistableData? = null
|
||||||
) : Table(BaseScreen.skin) {
|
) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
|
||||||
open class EmpireOverviewTabPersistableData {
|
open class EmpireOverviewTabPersistableData {
|
||||||
open fun isEmpty() = true
|
open fun isEmpty() = true
|
||||||
}
|
}
|
||||||
open val persistableData = persistedData ?: EmpireOverviewTabPersistableData()
|
open val persistableData = persistedData ?: EmpireOverviewTabPersistableData()
|
||||||
/** Override if your Tab needs to do stuff on activation. @return non-null to scroll the Tab vertically within the TabbedPager. */
|
|
||||||
open fun activated(): Float? = null
|
override fun activated(index: Int, caption: String, pager: TabbedPager) {
|
||||||
/** Override if your Tab needs to do housekeeping when it loses focus. [scrollY] is the Tab's current vertical scroll position. */
|
val settings = overviewScreen.game.settings
|
||||||
open fun deactivated(scrollY: Float) {}
|
if (caption == "Stats")
|
||||||
/** Override to supply content not participating in scrolling */
|
settings.addCompletedTutorialTask("See your stats breakdown")
|
||||||
open fun getFixedContent(): WidgetGroup? = null
|
settings.lastOverviewPage = caption
|
||||||
|
}
|
||||||
|
|
||||||
val gameInfo = viewingPlayer.gameInfo
|
val gameInfo = viewingPlayer.gameInfo
|
||||||
|
|
||||||
|
@ -37,8 +37,7 @@ class ReligionOverviewTab(
|
|||||||
private val statsTable = Table()
|
private val statsTable = Table()
|
||||||
private val beliefsTable = Table()
|
private val beliefsTable = Table()
|
||||||
|
|
||||||
override fun getFixedContent(): WidgetGroup? {
|
override fun getFixedContent() = Table().apply {
|
||||||
return Table().apply {
|
|
||||||
defaults().pad(5f)
|
defaults().pad(5f)
|
||||||
align(Align.top)
|
align(Align.top)
|
||||||
|
|
||||||
@ -49,7 +48,6 @@ class ReligionOverviewTab(
|
|||||||
add(religionButtonLabel)
|
add(religionButtonLabel)
|
||||||
addSeparator()
|
addSeparator()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
defaults().pad(5f)
|
defaults().pad(5f)
|
||||||
|
@ -3,7 +3,6 @@ package com.unciv.ui.overviewscreen
|
|||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
import com.badlogic.gdx.scenes.scene2d.ui.Label
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
|
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
|
@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.Color
|
|||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.badlogic.gdx.scenes.scene2d.Group
|
import com.badlogic.gdx.scenes.scene2d.Group
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
|
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
@ -30,9 +29,13 @@ class UnitOverviewTab(
|
|||||||
}
|
}
|
||||||
override val persistableData = (persistedData as? UnitTabPersistableData) ?: UnitTabPersistableData()
|
override val persistableData = (persistedData as? UnitTabPersistableData) ?: UnitTabPersistableData()
|
||||||
|
|
||||||
override fun activated() = persistableData.scrollY
|
override fun activated(index: Int, caption: String, pager: TabbedPager) {
|
||||||
override fun deactivated(scrollY: Float) {
|
if (persistableData.scrollY != null)
|
||||||
persistableData.scrollY = scrollY
|
pager.setPageScrollY(index, persistableData.scrollY!!)
|
||||||
|
super.activated(index, caption, pager)
|
||||||
|
}
|
||||||
|
override fun deactivated(index: Int, caption: String, pager: TabbedPager) {
|
||||||
|
persistableData.scrollY = pager.getPageScrollY(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val supplyTableWidth = (overviewScreen.stage.width * 0.25f).coerceAtLeast(240f)
|
private val supplyTableWidth = (overviewScreen.stage.width * 0.25f).coerceAtLeast(240f)
|
||||||
@ -40,9 +43,7 @@ class UnitOverviewTab(
|
|||||||
private val unitHeaderTable = Table()
|
private val unitHeaderTable = Table()
|
||||||
private val fixedContent = Table()
|
private val fixedContent = Table()
|
||||||
|
|
||||||
override fun getFixedContent(): WidgetGroup {
|
override fun getFixedContent() = fixedContent
|
||||||
return fixedContent
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fixedContent.add(getUnitSupplyTable()).align(Align.top).padBottom(10f).row()
|
fixedContent.add(getUnitSupplyTable()).align(Align.top).padBottom(10f).row()
|
||||||
|
@ -81,9 +81,7 @@ class WonderOverviewTab(
|
|||||||
private val wonders: Array<WonderInfo> = collectInfo()
|
private val wonders: Array<WonderInfo> = collectInfo()
|
||||||
|
|
||||||
private val fixedContent = Table()
|
private val fixedContent = Table()
|
||||||
override fun getFixedContent(): WidgetGroup {
|
override fun getFixedContent() = fixedContent
|
||||||
return fixedContent
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fixedContent.apply {
|
fixedContent.apply {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.unciv.ui.utils
|
package com.unciv.ui.utils
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Input
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.scenes.scene2d.*
|
import com.badlogic.gdx.scenes.scene2d.*
|
||||||
import com.badlogic.gdx.scenes.scene2d.ui.*
|
import com.badlogic.gdx.scenes.scene2d.ui.*
|
||||||
@ -9,11 +10,8 @@ import com.unciv.Constants
|
|||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
|
||||||
|
|
||||||
|
//TODO If keys are assigned, the widget is in a popup not filling stage width, and a button is
|
||||||
/*
|
// partially visible on the right end, the key tooltip will show outside the parent.
|
||||||
Unimplemented ideas:
|
|
||||||
Use fixedContent for OptionsPopup mod check tab
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a 'Tabs' widget where different pages can be switched by selecting a header button.
|
* Implements a 'Tabs' widget where different pages can be switched by selecting a header button.
|
||||||
@ -72,10 +70,27 @@ class TabbedPager(
|
|||||||
private val fixedContentScroll = LinkedScrollPane(horizontalOnly = true)
|
private val fixedContentScroll = LinkedScrollPane(horizontalOnly = true)
|
||||||
private val fixedContentScrollCell: Cell<ScrollPane>
|
private val fixedContentScrollCell: Cell<ScrollPane>
|
||||||
private val contentScroll = LinkedScrollPane(horizontalOnly = false, linkTo = fixedContentScroll)
|
private val contentScroll = LinkedScrollPane(horizontalOnly = false, linkTo = fixedContentScroll)
|
||||||
|
private var savedScrollListener: EventListener? = null
|
||||||
|
|
||||||
private val deferredSecretPages = ArrayDeque<PageState>(0)
|
private val deferredSecretPages = ArrayDeque<PageState>(0)
|
||||||
private var askPasswordLock = false
|
private var askPasswordLock = false
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region Public Interfaces
|
||||||
|
|
||||||
|
/** Pages added via [addPage] can optionally implement this to get notified when they are
|
||||||
|
* [activated] or [deactivated], or to provide [fixed content][getFixedContent] */
|
||||||
|
interface IPageExtensions {
|
||||||
|
/** Called by [TabbedPager] after a page is shown, whether by user click or programmatically. */
|
||||||
|
fun activated(index: Int, caption: String, pager: TabbedPager)
|
||||||
|
|
||||||
|
/** Called by [TabbedPager] before a page is hidden, whether by user click or programmatically. */
|
||||||
|
fun deactivated(index: Int, caption: String, pager: TabbedPager) {}
|
||||||
|
|
||||||
|
/** @return Optional second content [Actor], will be placed outside the tab's main [ScrollPane] between header and `content`. Scrolls horizontally only. */
|
||||||
|
fun getFixedContent(): Actor? = null
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region Private Classes
|
//region Private Classes
|
||||||
|
|
||||||
@ -84,8 +99,6 @@ class TabbedPager(
|
|||||||
var content: Actor,
|
var content: Actor,
|
||||||
var fixedContent: Actor?,
|
var fixedContent: Actor?,
|
||||||
var disabled: Boolean,
|
var disabled: Boolean,
|
||||||
val onActivation: ((Int, String) -> Unit)?,
|
|
||||||
val onDeactivation: ((Int, String, Float) -> Unit)?,
|
|
||||||
icon: Actor?,
|
icon: Actor?,
|
||||||
iconSize: Float,
|
iconSize: Float,
|
||||||
val shortcutKey: KeyCharAndCode,
|
val shortcutKey: KeyCharAndCode,
|
||||||
@ -247,6 +260,12 @@ class TabbedPager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EmptyClosePage(private val action: ()->Unit) : Actor(), IPageExtensions {
|
||||||
|
override fun activated(index: Int, caption: String, pager: TabbedPager) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region Initialization
|
//region Initialization
|
||||||
|
|
||||||
@ -312,7 +331,7 @@ class TabbedPager(
|
|||||||
|
|
||||||
if (activePage != -1) {
|
if (activePage != -1) {
|
||||||
val page = pages[activePage]
|
val page = pages[activePage]
|
||||||
page.onDeactivation?.invoke(activePage, page.caption, contentScroll.scrollY)
|
(page.content as? IPageExtensions)?.deactivated(activePage, page.caption, this)
|
||||||
page.button.color = Color.WHITE
|
page.button.color = Color.WHITE
|
||||||
fixedContentScroll.actor = null
|
fixedContentScroll.actor = null
|
||||||
page.scrollX = contentScroll.scrollX
|
page.scrollX = contentScroll.scrollX
|
||||||
@ -359,7 +378,8 @@ class TabbedPager(
|
|||||||
else
|
else
|
||||||
// when coming from a tap/click, can we at least ensure no part of it is outside the visible area
|
// when coming from a tap/click, can we at least ensure no part of it is outside the visible area
|
||||||
headerScroll.run { scrollX = scrollX.coerceIn((page.buttonX + page.buttonW - scrollWidth)..page.buttonX) }
|
headerScroll.run { scrollX = scrollX.coerceIn((page.buttonX + page.buttonW - scrollWidth)..page.buttonX) }
|
||||||
page.onActivation?.invoke(index, page.caption)
|
|
||||||
|
(page.content as? IPageExtensions)?.activated(index, page.caption, this)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -393,6 +413,12 @@ class TabbedPager(
|
|||||||
/** Access a page's header button e.g. for unusual formatting */
|
/** Access a page's header button e.g. for unusual formatting */
|
||||||
fun getPageButton(index: Int) = pages[index].button
|
fun getPageButton(index: Int) = pages[index].button
|
||||||
|
|
||||||
|
/** Query the vertical scroll position af a page's contents */
|
||||||
|
fun getPageScrollY(index: Int): Float {
|
||||||
|
if (index == activePage) return contentScroll.scrollY
|
||||||
|
if (index !in 0 until pages.size) return 0f
|
||||||
|
return pages[index].scrollY
|
||||||
|
}
|
||||||
/** Change the vertical scroll position af a page's contents */
|
/** Change the vertical scroll position af a page's contents */
|
||||||
fun setPageScrollY(index: Int, scrollY: Float, animation: Boolean = false) {
|
fun setPageScrollY(index: Int, scrollY: Float, animation: Boolean = false) {
|
||||||
if (index !in 0 until pages.size) return
|
if (index !in 0 until pages.size) return
|
||||||
@ -403,6 +429,32 @@ class TabbedPager(
|
|||||||
if (!animation) contentScroll.updateVisualScroll()
|
if (!animation) contentScroll.updateVisualScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Disable/Enable built-in ScrollPane for content pages, including focus stealing prevention */
|
||||||
|
fun setScrollDisabled(disabled: Boolean) {
|
||||||
|
if (disabled == contentScroll.isScrollingDisabledY) return
|
||||||
|
contentScroll.setScrollingDisabled(disabled, disabled)
|
||||||
|
if (disabled) {
|
||||||
|
savedScrollListener = contentScroll.captureListeners.first()
|
||||||
|
contentScroll.captureListeners.clear()
|
||||||
|
} else {
|
||||||
|
if (savedScrollListener != null)
|
||||||
|
contentScroll.addCaptureListener(savedScrollListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bind arrow keys to navigate pages left/right.
|
||||||
|
* Needs [keyPressDispatcher] to be set on instantiation.
|
||||||
|
* Caller is responsible for cleanup if necessary. */
|
||||||
|
fun bindArrowKeys() {
|
||||||
|
if (keyPressDispatcher == null) return
|
||||||
|
fun cyclePage(direction: Int) {
|
||||||
|
if (activePage == -1) return
|
||||||
|
selectPage((activePage + direction).coerceIn(0 until pages.size))
|
||||||
|
}
|
||||||
|
keyPressDispatcher[KeyCharAndCode(Input.Keys.LEFT)] = { cyclePage(-1) }
|
||||||
|
keyPressDispatcher[KeyCharAndCode(Input.Keys.RIGHT)] = { cyclePage(1) }
|
||||||
|
}
|
||||||
|
|
||||||
/** Remove a page by its index.
|
/** Remove a page by its index.
|
||||||
* @return `true` if page successfully removed */
|
* @return `true` if page successfully removed */
|
||||||
fun removePage(index: Int): Boolean {
|
fun removePage(index: Int): Boolean {
|
||||||
@ -425,18 +477,7 @@ class TabbedPager(
|
|||||||
if (isActive) selectPage(-1)
|
if (isActive) selectPage(-1)
|
||||||
pages[index].let {
|
pages[index].let {
|
||||||
it.content = content
|
it.content = content
|
||||||
measureContent(it)
|
it.fixedContent = (content as? IPageExtensions)?.getFixedContent()
|
||||||
}
|
|
||||||
if (isActive) selectPage(index)
|
|
||||||
}
|
|
||||||
/** Replace a page's [content] and [fixedContent] by its [index]. */
|
|
||||||
fun replacePage(index: Int, content: Actor, fixedContent: Actor?) {
|
|
||||||
if (index !in 0 until pages.size) return
|
|
||||||
val isActive = index == activePage
|
|
||||||
if (isActive) selectPage(-1)
|
|
||||||
pages[index].let {
|
|
||||||
it.content = content
|
|
||||||
it.fixedContent = fixedContent
|
|
||||||
measureContent(it)
|
measureContent(it)
|
||||||
}
|
}
|
||||||
if (isActive) selectPage(index)
|
if (isActive) selectPage(index)
|
||||||
@ -444,22 +485,17 @@ class TabbedPager(
|
|||||||
|
|
||||||
/** Replace a page's [content] by its [caption]. */
|
/** Replace a page's [content] by its [caption]. */
|
||||||
fun replacePage(caption: String, content: Actor) = replacePage(getPageIndex(caption), content)
|
fun replacePage(caption: String, content: Actor) = replacePage(getPageIndex(caption), content)
|
||||||
/** Replace a page's [content] and [fixedContent] by its [caption]. */
|
|
||||||
fun replacePage(caption: String, content: Actor, fixedContent: Actor?) = replacePage(getPageIndex(caption), content, fixedContent)
|
|
||||||
|
|
||||||
/** Add a page!
|
/** Add a page!
|
||||||
* @param caption Text to be shown on the header button (automatically translated), can later be used to reference the page in other calls.
|
* @param caption Text to be shown on the header button (automatically translated), can later be used to reference the page in other calls.
|
||||||
* @param content [Actor] to show in the lower area when this page is selected.
|
* @param content [Actor] to show in the lower area when this page is selected. Can optionally implement [IPageExtensions] to be notified of activation or deactivation.
|
||||||
* @param icon Actor, typically an [Image], to show before the caption on the header button.
|
* @param icon Actor, typically an [Image], to show before the caption on the header button.
|
||||||
* @param iconSize Size for [icon] - if not zero, the icon is wrapped to allow a [setSize] even on [Image] which ignores size.
|
* @param iconSize Size for [icon] - if not zero, the icon is wrapped to allow a [setSize] even on [Image] which ignores size.
|
||||||
* @param insertBefore -1 to add at the end, or index of existing page to insert this before it.
|
* @param insertBefore -1 to add at the end, or index of existing page to insert this before it.
|
||||||
* @param secret Marks page as 'secret'. A password is asked once per [TabbedPager] and if it does not match the has passed in the constructor the page and all subsequent secret pages are dropped.
|
* @param secret Marks page as 'secret'. A password is asked once per [TabbedPager] and if it does not match the has passed in the constructor the page and all subsequent secret pages are dropped.
|
||||||
* @param disabled Initial disabled state. Disabled pages cannot be selected even with [selectPage], their button is dimmed.
|
* @param disabled Initial disabled state. Disabled pages cannot be selected even with [selectPage], their button is dimmed.
|
||||||
* @param shortcutKey Optional keyboard key to associate - goes to the [KeyPressDispatcher] passed in the constructor.
|
* @param shortcutKey Optional keyboard key to associate - goes to the [KeyPressDispatcher] passed in the constructor.
|
||||||
* @param syncScroll If on, the ScrollPanes for [content] and [fixedContent] will synchronize horizontally.
|
* @param syncScroll If on, the ScrollPanes for [content] and [fixed content][IPageExtensions.getFixedContent] will synchronize horizontally.
|
||||||
* @param fixedContent Optional second content [Actor], will be placed outside the tab's main [ScrollPane] between header and [content]. Scrolls horizontally only.
|
|
||||||
* @param onDeactivation _Optional_ callback called when this page is hidden. Lambda arguments are page index and caption, and scrollY of the tab's [ScrollPane].
|
|
||||||
* @param onActivation _Optional_ callback called when this page is shown (per actual change to this page, not per header click). Lambda arguments are page index and caption.
|
|
||||||
* @return The new page's index or -1 if it could not be immediately added (secret).
|
* @return The new page's index or -1 if it could not be immediately added (secret).
|
||||||
*/
|
*/
|
||||||
fun addPage(
|
fun addPage(
|
||||||
@ -472,19 +508,14 @@ class TabbedPager(
|
|||||||
disabled: Boolean = false,
|
disabled: Boolean = false,
|
||||||
shortcutKey: KeyCharAndCode = KeyCharAndCode.UNKNOWN,
|
shortcutKey: KeyCharAndCode = KeyCharAndCode.UNKNOWN,
|
||||||
scrollAlign: Int = Align.top,
|
scrollAlign: Int = Align.top,
|
||||||
syncScroll: Boolean = true,
|
syncScroll: Boolean = true
|
||||||
fixedContent: Actor? = null,
|
|
||||||
onDeactivation: ((Int, String, Float) -> Unit)? = null,
|
|
||||||
onActivation: ((Int, String) -> Unit)? = null
|
|
||||||
): Int {
|
): Int {
|
||||||
// Build page descriptor and header button
|
// Build page descriptor and header button
|
||||||
val page = PageState(
|
val page = PageState(
|
||||||
caption = caption,
|
caption = caption,
|
||||||
content = content ?: Group(),
|
content = content ?: Group(),
|
||||||
fixedContent = fixedContent,
|
fixedContent = (content as? IPageExtensions)?.getFixedContent(),
|
||||||
disabled = disabled,
|
disabled = disabled,
|
||||||
onActivation = onActivation,
|
|
||||||
onDeactivation = onDeactivation,
|
|
||||||
icon = icon,
|
icon = icon,
|
||||||
iconSize = iconSize,
|
iconSize = iconSize,
|
||||||
shortcutKey = shortcutKey,
|
shortcutKey = shortcutKey,
|
||||||
@ -514,6 +545,18 @@ class TabbedPager(
|
|||||||
return addAndShowPage(page, insertBefore)
|
return addAndShowPage(page, insertBefore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a "Close" button tho the Tab headers, with empty content which will invoke [action] when clicked
|
||||||
|
*/
|
||||||
|
fun addClosePage(
|
||||||
|
insertBefore: Int = -1,
|
||||||
|
color: Color = Color(0.75f, 0.1f, 0.1f, 1f),
|
||||||
|
action: ()->Unit
|
||||||
|
) {
|
||||||
|
val index = addPage(Constants.close, EmptyClosePage(action), insertBefore = insertBefore)
|
||||||
|
pages[index].button.color = color
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate any [secret][addPage] pages by asking for the password.
|
* Activate any [secret][addPage] pages by asking for the password.
|
||||||
*
|
*
|
||||||
|
@ -16,6 +16,7 @@ import com.unciv.UncivGame
|
|||||||
import com.unciv.logic.MapSaver
|
import com.unciv.logic.MapSaver
|
||||||
import com.unciv.logic.civilization.PlayerType
|
import com.unciv.logic.civilization.PlayerType
|
||||||
import com.unciv.models.UncivSound
|
import com.unciv.models.UncivSound
|
||||||
|
import com.unciv.models.metadata.BaseRuleset
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.Ruleset.RulesetError
|
import com.unciv.models.ruleset.Ruleset.RulesetError
|
||||||
import com.unciv.models.ruleset.Ruleset.RulesetErrorSeverity
|
import com.unciv.models.ruleset.Ruleset.RulesetErrorSeverity
|
||||||
@ -59,7 +60,6 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
private var modCheckBaseSelect: TranslatedSelectBox? = null
|
private var modCheckBaseSelect: TranslatedSelectBox? = null
|
||||||
private val modCheckResultTable = Table()
|
private val modCheckResultTable = Table()
|
||||||
private val selectBoxMinWidth: Float
|
private val selectBoxMinWidth: Float
|
||||||
private val previousMaxWorldZoom = settings.maxWorldZoomOut
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
tabMaxHeight = (if (isPortrait()) 0.7f else 0.8f) * stage.height
|
tabMaxHeight = (if (isPortrait()) 0.7f else 0.8f) * stage.height
|
||||||
}
|
}
|
||||||
tabs = TabbedPager(tabMinWidth, tabMaxWidth, 0f, tabMaxHeight,
|
tabs = TabbedPager(tabMinWidth, tabMaxWidth, 0f, tabMaxHeight,
|
||||||
headerFontSize = 21, backgroundColor = Color.CLEAR, capacity = 8)
|
headerFontSize = 21, backgroundColor = Color.CLEAR, keyPressDispatcher = this.keyPressDispatcher, capacity = 8)
|
||||||
add(tabs).pad(0f).grow().row()
|
add(tabs).pad(0f).grow().row()
|
||||||
|
|
||||||
tabs.addPage("About", getAboutTab(), ImageGetter.getExternalImage("Icon.png"), 24f)
|
tabs.addPage("About", getAboutTab(), ImageGetter.getExternalImage("Icon.png"), 24f)
|
||||||
@ -91,14 +91,17 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
tabs.addPage("Sound", getSoundTab(), ImageGetter.getImage("OtherIcons/Speaker"), 24f)
|
tabs.addPage("Sound", getSoundTab(), ImageGetter.getImage("OtherIcons/Speaker"), 24f)
|
||||||
tabs.addPage("Multiplayer", getMultiplayerTab(), ImageGetter.getImage("OtherIcons/Multiplayer"), 24f)
|
tabs.addPage("Multiplayer", getMultiplayerTab(), ImageGetter.getImage("OtherIcons/Multiplayer"), 24f)
|
||||||
tabs.addPage("Advanced", getAdvancedTab(), ImageGetter.getImage("OtherIcons/Settings"), 24f)
|
tabs.addPage("Advanced", getAdvancedTab(), ImageGetter.getImage("OtherIcons/Settings"), 24f)
|
||||||
if (RulesetCache.size > 1) {
|
if (RulesetCache.size > BaseRuleset.values().size) {
|
||||||
tabs.addPage("Locate mod errors", getModCheckTab(), ImageGetter.getImage("OtherIcons/Mods"), 24f) { _, _ ->
|
val content = ModCheckTab(this) {
|
||||||
if (modCheckFirstRun) runModChecker()
|
if (modCheckFirstRun) runModChecker()
|
||||||
|
else runModChecker(modCheckBaseSelect!!.selected.value)
|
||||||
}
|
}
|
||||||
|
tabs.addPage("Locate mod errors", content, ImageGetter.getImage("OtherIcons/Mods"), 24f)
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT) && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT))) {
|
if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT) && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT))) {
|
||||||
tabs.addPage("Debug", getDebugTab(), ImageGetter.getImage("OtherIcons/SecretOptions"), 24f, secret = true)
|
tabs.addPage("Debug", getDebugTab(), ImageGetter.getImage("OtherIcons/SecretOptions"), 24f, secret = true)
|
||||||
}
|
}
|
||||||
|
tabs.bindArrowKeys() // If we're sharing WorldScreen's dispatcher that's OK since it does revertToCheckPoint on update
|
||||||
|
|
||||||
addCloseButton {
|
addCloseButton {
|
||||||
previousScreen.game.musicController.onChange(null)
|
previousScreen.game.musicController.onChange(null)
|
||||||
@ -274,8 +277,8 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
settings.save()
|
settings.save()
|
||||||
connectionToServerButton.isEnabled = multiplayerServerTextField.text != Constants.dropboxMultiplayerServer
|
connectionToServerButton.isEnabled = multiplayerServerTextField.text != Constants.dropboxMultiplayerServer
|
||||||
}
|
}
|
||||||
serverIpTable.add(multiplayerServerTextField).width(screen.stage.width / 2)
|
serverIpTable.add(multiplayerServerTextField).minWidth(screen.stage.width / 2).growX()
|
||||||
add(serverIpTable).row()
|
add(serverIpTable).fillX().row()
|
||||||
|
|
||||||
add("Reset to Dropbox".toTextButton().onClick {
|
add("Reset to Dropbox".toTextButton().onClick {
|
||||||
multiplayerServerTextField.text = Constants.dropboxMultiplayerServer
|
multiplayerServerTextField.text = Constants.dropboxMultiplayerServer
|
||||||
@ -287,14 +290,9 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
}
|
}
|
||||||
popup.open(true)
|
popup.open(true)
|
||||||
|
|
||||||
successfullyConnectedToServer { success: Boolean, result: String ->
|
successfullyConnectedToServer { success: Boolean, _: String ->
|
||||||
if (success) {
|
popup.addGoodSizedLabel(if (success) "Success!" else "Failed!").row()
|
||||||
popup.addGoodSizedLabel("Success!").row()
|
popup.addCloseButton()
|
||||||
popup.addCloseButton()
|
|
||||||
} else {
|
|
||||||
popup.addGoodSizedLabel("Failed!").row()
|
|
||||||
popup.addCloseButton()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).row()
|
}).row()
|
||||||
}
|
}
|
||||||
@ -373,27 +371,38 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) {
|
|||||||
addSetUserId()
|
addSetUserId()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getModCheckTab() = Table(BaseScreen.skin).apply {
|
private class ModCheckTab(
|
||||||
defaults().pad(10f).align(Align.top)
|
options: OptionsPopup,
|
||||||
val reloadModsButton = "Reload mods".toTextButton().onClick {
|
private val runAction: ()->Unit
|
||||||
runModChecker(modCheckBaseSelect!!.selected.value)
|
) : Table(), TabbedPager.IPageExtensions {
|
||||||
}
|
private val fixedContent = Table()
|
||||||
add(reloadModsButton).row()
|
|
||||||
|
|
||||||
val labeledBaseSelect = Table(BaseScreen.skin).apply {
|
init {
|
||||||
add("Check extension mods based on:".toLabel()).padRight(10f)
|
defaults().pad(10f).align(Align.top)
|
||||||
val baseMods = listOf(modCheckWithoutBase) + RulesetCache.getSortedBaseRulesets()
|
|
||||||
modCheckBaseSelect = TranslatedSelectBox(baseMods, modCheckWithoutBase, BaseScreen.skin).apply {
|
fixedContent.defaults().pad(10f).align(Align.top)
|
||||||
selectedIndex = 0
|
val reloadModsButton = "Reload mods".toTextButton().onClick(runAction)
|
||||||
onChange {
|
fixedContent.add(reloadModsButton).row()
|
||||||
runModChecker(modCheckBaseSelect!!.selected.value)
|
|
||||||
|
val labeledBaseSelect = Table().apply {
|
||||||
|
add("Check extension mods based on:".toLabel()).padRight(10f)
|
||||||
|
val baseMods = listOf(modCheckWithoutBase) + RulesetCache.getSortedBaseRulesets()
|
||||||
|
options.modCheckBaseSelect = TranslatedSelectBox(baseMods, modCheckWithoutBase, BaseScreen.skin).apply {
|
||||||
|
selectedIndex = 0
|
||||||
|
onChange { runAction() }
|
||||||
}
|
}
|
||||||
|
add(options.modCheckBaseSelect)
|
||||||
}
|
}
|
||||||
add(modCheckBaseSelect)
|
fixedContent.add(labeledBaseSelect).row()
|
||||||
}
|
|
||||||
add(labeledBaseSelect).row()
|
|
||||||
|
|
||||||
add(modCheckResultTable)
|
add(options.modCheckResultTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFixedContent() = fixedContent
|
||||||
|
|
||||||
|
override fun activated(index: Int, caption: String, pager: TabbedPager) {
|
||||||
|
runAction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runModChecker(base: String = modCheckWithoutBase) {
|
private fun runModChecker(base: String = modCheckWithoutBase) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user