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:
SomeTroglodyte 2022-04-04 17:06:58 +02:00 committed by GitHub
parent 5876047bda
commit 8385f814a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 144 additions and 108 deletions

View File

@ -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() {

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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 {

View File

@ -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.
* *

View File

@ -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) {