mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-29 06:09:24 +07:00
Android: "Screen Mode" option (#8785)
* Android: "Screen Mode" option * Fix broken tests, code cleanup * Fix broken tests, code cleanup * Fix broken tests, code cleanup --------- Co-authored-by: vegeta1k95 <vfylfhby>
This commit is contained in:
@ -1380,7 +1380,7 @@ Movement cost =
|
||||
for =
|
||||
Missing translations: =
|
||||
Screen Size =
|
||||
Screen Window =
|
||||
Screen Mode =
|
||||
Windowed =
|
||||
Fullscreen =
|
||||
Tileset =
|
||||
|
83
android/src/com/unciv/app/AndroidDisplay.kt
Normal file
83
android/src/com/unciv/app/AndroidDisplay.kt
Normal file
@ -0,0 +1,83 @@
|
||||
package com.unciv.app
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.view.Display
|
||||
import android.view.Display.Mode
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.PlatformDisplay
|
||||
import com.unciv.utils.ScreenMode
|
||||
|
||||
|
||||
class AndroidScreenMode(
|
||||
private val modeId: Int) : ScreenMode {
|
||||
private var name: String = "Default"
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
constructor(mode: Mode) : this(mode.modeId) {
|
||||
name = "${mode.physicalWidth}x${mode.physicalHeight} (${mode.refreshRate.toInt()}HZ)"
|
||||
}
|
||||
|
||||
override fun getId(): Int {
|
||||
return modeId
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name.tr()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
|
||||
private var display: Display? = null
|
||||
private var displayModes: HashMap<Int, ScreenMode> = hashMapOf()
|
||||
|
||||
init {
|
||||
|
||||
// Fetch current display
|
||||
display = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> activity.display
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> activity.windowManager.defaultDisplay
|
||||
else -> null
|
||||
}
|
||||
|
||||
// Add default mode
|
||||
displayModes[0] = AndroidScreenMode(0)
|
||||
|
||||
// Add other supported modes
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
fetchScreenModes()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
private fun fetchScreenModes() {
|
||||
val display = display ?: return
|
||||
for (mode in display.supportedModes)
|
||||
displayModes[mode.modeId] = AndroidScreenMode(mode)
|
||||
}
|
||||
|
||||
override fun getScreenModes(): Map<Int, ScreenMode> {
|
||||
return displayModes
|
||||
}
|
||||
|
||||
override fun getDefaultMode(): ScreenMode {
|
||||
return displayModes[0]!!
|
||||
}
|
||||
|
||||
override fun setScreenMode(id: Int, settings: GameSettings) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.runOnUiThread {
|
||||
val params = activity.window.attributes
|
||||
params.preferredDisplayModeId = id
|
||||
activity.window.attributes = params
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -25,13 +25,14 @@ import com.unciv.logic.event.EventBus
|
||||
import com.unciv.ui.components.Fonts
|
||||
import com.unciv.ui.screens.basescreen.UncivStage
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.utils.Display
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.concurrency.Concurrency
|
||||
import java.io.File
|
||||
|
||||
open class AndroidLauncher : AndroidApplication() {
|
||||
|
||||
private var game: UncivGame? = null
|
||||
private var game: AndroidGame? = null
|
||||
private var deepLinkedMultiplayerGame: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -40,6 +41,9 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
// Setup Android logging
|
||||
Log.backend = AndroidLogBackend()
|
||||
|
||||
// Setup Android display
|
||||
Display.platform = AndroidDisplay(this)
|
||||
|
||||
// Setup Android fonts
|
||||
Fonts.fontImplementation = AndroidFont()
|
||||
|
||||
@ -67,9 +71,6 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
val glView = (Gdx.graphics as AndroidGraphics).view as GLSurfaceView
|
||||
|
||||
addScreenObscuredListener(glView)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
addScreenRefreshRateListener(glView)
|
||||
}
|
||||
|
||||
fun allowPortrait(allow: Boolean) {
|
||||
@ -89,29 +90,6 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Request the best available device frame rate for
|
||||
* the game, as soon as OpenGL surface is created */
|
||||
private fun addScreenRefreshRateListener(surfaceView: GLSurfaceView) {
|
||||
surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val modes = display?.supportedModes ?: return
|
||||
val bestRefreshRate = modes.maxOf { it.refreshRate }
|
||||
holder.surface.setFrameRate(bestRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val display = windowManager.defaultDisplay
|
||||
val modes = display?.supportedModes ?: return
|
||||
val bestMode = modes.maxBy { it.refreshRate }
|
||||
val params = window.attributes
|
||||
params.preferredDisplayModeId = bestMode.modeId
|
||||
window.attributes = params
|
||||
}
|
||||
}
|
||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addScreenObscuredListener(surfaceView: GLSurfaceView) {
|
||||
surfaceView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
/** [onGlobalLayout] gets triggered not only when the [windowVisibleDisplayFrame][View.getWindowVisibleDisplayFrame] changes, but also on other things.
|
||||
|
@ -42,6 +42,7 @@ import com.unciv.ui.screens.worldscreen.WorldMapHolder
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.ui.screens.worldscreen.unit.UnitTable
|
||||
import com.unciv.utils.DebugUtils
|
||||
import com.unciv.utils.Display
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.PlatformSpecific
|
||||
import com.unciv.utils.concurrency.Concurrency
|
||||
@ -158,7 +159,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
* - Font (hence Fonts.resetFont() inside setSkin())
|
||||
*/
|
||||
settings = files.getGeneralSettings() // needed for the screen
|
||||
settings.refreshScreenMode()
|
||||
Display.setScreenMode(settings.screenMode, settings)
|
||||
setAsRootScreen(GameStartScreen()) // NOT dependent on any atlas or skin
|
||||
GameSounds.init()
|
||||
|
||||
|
@ -9,6 +9,8 @@ import com.unciv.logic.multiplayer.FriendList
|
||||
import com.unciv.models.UncivSound
|
||||
import com.unciv.ui.components.FontFamilyData
|
||||
import com.unciv.ui.components.Fonts
|
||||
import com.unciv.utils.Display
|
||||
import com.unciv.utils.ScreenMode
|
||||
import java.text.Collator
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
@ -25,11 +27,6 @@ enum class ScreenSize(val virtualWidth:Float, val virtualHeight:Float){
|
||||
Huge(1500f,1000f)
|
||||
}
|
||||
|
||||
enum class ScreenWindow {
|
||||
Windowed,
|
||||
Fullscreen
|
||||
}
|
||||
|
||||
class GameSettings {
|
||||
|
||||
var mapAutoScroll: Boolean = false
|
||||
@ -47,7 +44,7 @@ class GameSettings {
|
||||
@Deprecated("Since 4.3.6 - replaces with screenSize")
|
||||
var resolution: String = "900x600"
|
||||
var screenSize:ScreenSize = ScreenSize.Small
|
||||
var screenWindow: ScreenWindow = ScreenWindow.Windowed
|
||||
var screenMode: Int = 0
|
||||
var tutorialsShown = HashSet<String>()
|
||||
var tutorialTasksCompleted = HashSet<String>()
|
||||
|
||||
@ -154,24 +151,6 @@ class GameSettings {
|
||||
fun getCollatorFromLocale(): Collator {
|
||||
return Collator.getInstance(getCurrentLocale())
|
||||
}
|
||||
|
||||
fun refreshScreenMode() {
|
||||
|
||||
if (Gdx.app.type != Application.ApplicationType.Desktop)
|
||||
return
|
||||
|
||||
when (screenWindow) {
|
||||
ScreenWindow.Windowed -> {
|
||||
Gdx.graphics.setWindowedMode(
|
||||
windowState.width.coerceAtLeast(120),
|
||||
windowState.height.coerceAtLeast(80))
|
||||
}
|
||||
|
||||
ScreenWindow.Fullscreen -> {
|
||||
Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class LocaleCode(var language: String, var country: String) {
|
||||
|
@ -10,7 +10,6 @@ import com.unciv.GUI
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.metadata.ScreenSize
|
||||
import com.unciv.models.metadata.ScreenWindow
|
||||
import com.unciv.models.skins.SkinCache
|
||||
import com.unciv.models.tilesets.TileSetCache
|
||||
import com.unciv.models.translations.tr
|
||||
@ -25,8 +24,8 @@ import com.unciv.ui.components.extensions.onChange
|
||||
import com.unciv.ui.components.extensions.onClick
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.extensions.toTextButton
|
||||
|
||||
private val resolutionArray = com.badlogic.gdx.utils.Array(arrayOf("750x500", "900x600", "1050x700", "1200x800", "1500x1000"))
|
||||
import com.unciv.utils.Display
|
||||
import com.unciv.utils.ScreenMode
|
||||
|
||||
fun displayTab(
|
||||
optionsPopup: OptionsPopup,
|
||||
@ -37,8 +36,9 @@ fun displayTab(
|
||||
|
||||
val settings = optionsPopup.settings
|
||||
|
||||
addScreenModeSelectBox(this, settings, optionsPopup.selectBoxMinWidth)
|
||||
|
||||
if (Gdx.app.type == Application.ApplicationType.Desktop) {
|
||||
addFullscreenSelectBox(this, settings, optionsPopup.selectBoxMinWidth)
|
||||
optionsPopup.addCheckbox(this, "Map mouse auto-scroll", settings.mapAutoScroll, true) {
|
||||
settings.mapAutoScroll = it
|
||||
if (GUI.isWorldLoaded())
|
||||
@ -140,16 +140,22 @@ private fun addUnitIconAlphaSlider(table: Table, settings: GameSettings, selectB
|
||||
table.add(unitIconAlphaSlider).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
}
|
||||
|
||||
private fun addFullscreenSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float) {
|
||||
table.add("Screen Window".toLabel()).left().fillX()
|
||||
private fun addScreenModeSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float) {
|
||||
table.add("Screen Mode".toLabel()).left().fillX()
|
||||
|
||||
val screenSizeSelectBox = TranslatedSelectBox(ScreenWindow.values().map { it.name }, settings.screenWindow.name,table.skin)
|
||||
table.add(screenSizeSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
val modes = Display.getScreenModes()
|
||||
val current: ScreenMode? = modes[settings.screenMode]
|
||||
|
||||
screenSizeSelectBox.onChange {
|
||||
settings.screenWindow = ScreenWindow.valueOf(screenSizeSelectBox.selected.value)
|
||||
settings.refreshScreenMode()
|
||||
val selectBox = SelectBox<ScreenMode>(table.skin)
|
||||
selectBox.items = Array(modes.values.toTypedArray())
|
||||
selectBox.selected = current
|
||||
selectBox.onChange {
|
||||
val mode = selectBox.selected
|
||||
settings.screenMode = mode.getId()
|
||||
Display.setScreenMode(mode.getId(), settings)
|
||||
}
|
||||
|
||||
table.add(selectBox).minWidth(selectBoxMinWidth).pad(10f).row()
|
||||
}
|
||||
|
||||
private fun addScreenSizeSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float, onResolutionChange: () -> Unit) {
|
||||
|
33
core/src/com/unciv/utils/Display.kt
Normal file
33
core/src/com/unciv/utils/Display.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package com.unciv.utils
|
||||
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
|
||||
interface ScreenMode {
|
||||
fun getId(): Int
|
||||
}
|
||||
|
||||
interface PlatformDisplay {
|
||||
|
||||
fun setScreenMode(id: Int, settings: GameSettings) {}
|
||||
fun getScreenModes(): Map<Int, ScreenMode> { return hashMapOf() }
|
||||
fun getDefaultMode(): ScreenMode
|
||||
|
||||
}
|
||||
|
||||
object Display {
|
||||
|
||||
lateinit var platform: PlatformDisplay
|
||||
|
||||
fun getDefaultMode(): ScreenMode {
|
||||
return platform.getDefaultMode()
|
||||
}
|
||||
|
||||
fun getScreenModes(): Map<Int, ScreenMode> {
|
||||
return platform.getScreenModes()
|
||||
}
|
||||
|
||||
fun setScreenMode(id: Int, settings: GameSettings) {
|
||||
platform.setScreenMode(id, settings)
|
||||
}
|
||||
|
||||
}
|
79
desktop/src/com/unciv/app/desktop/DesktopDisplay.kt
Normal file
79
desktop/src/com/unciv/app/desktop/DesktopDisplay.kt
Normal file
@ -0,0 +1,79 @@
|
||||
package com.unciv.app.desktop
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.utils.PlatformDisplay
|
||||
import com.unciv.utils.ScreenMode
|
||||
|
||||
enum class ScreenWindowType {
|
||||
Windowed,
|
||||
Borderless,
|
||||
Fullscreen
|
||||
}
|
||||
|
||||
|
||||
class DesktopScreenMode(
|
||||
private val modeId: Int,
|
||||
val windowType: ScreenWindowType) : ScreenMode {
|
||||
|
||||
override fun getId(): Int {
|
||||
return modeId
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return when (windowType) {
|
||||
ScreenWindowType.Windowed -> "Windowed".tr()
|
||||
ScreenWindowType.Borderless -> "Borderless".tr()
|
||||
ScreenWindowType.Fullscreen -> "Fullscreen".tr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopDisplay : PlatformDisplay {
|
||||
|
||||
private val modes = HashMap<Int, DesktopScreenMode>()
|
||||
|
||||
init {
|
||||
modes[0] = DesktopScreenMode(0, ScreenWindowType.Windowed)
|
||||
modes[1] = DesktopScreenMode(1, ScreenWindowType.Fullscreen)
|
||||
modes[2] = DesktopScreenMode(2, ScreenWindowType.Borderless)
|
||||
}
|
||||
|
||||
override fun getDefaultMode(): ScreenMode {
|
||||
return modes[0]!!
|
||||
}
|
||||
|
||||
override fun getScreenModes(): Map<Int, ScreenMode> {
|
||||
return modes
|
||||
}
|
||||
|
||||
override fun setScreenMode(id: Int, settings: GameSettings) {
|
||||
|
||||
val mode = modes[id] ?: return
|
||||
|
||||
when (mode.windowType) {
|
||||
|
||||
ScreenWindowType.Fullscreen -> {
|
||||
Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode)
|
||||
}
|
||||
|
||||
ScreenWindowType.Windowed -> {
|
||||
Gdx.graphics.setUndecorated(false)
|
||||
Gdx.graphics.setWindowedMode(
|
||||
settings.windowState.width.coerceAtLeast(120),
|
||||
settings.windowState.height.coerceAtLeast(80)
|
||||
)
|
||||
}
|
||||
|
||||
ScreenWindowType.Borderless -> {
|
||||
Gdx.graphics.setUndecorated(true)
|
||||
Gdx.graphics.setWindowedMode(
|
||||
settings.windowState.width.coerceAtLeast(120),
|
||||
settings.windowState.height.coerceAtLeast(80)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import com.unciv.logic.files.UncivFiles
|
||||
import com.unciv.models.metadata.ScreenSize
|
||||
import com.unciv.models.metadata.WindowState
|
||||
import com.unciv.ui.components.Fonts
|
||||
import com.unciv.utils.Display
|
||||
import com.unciv.utils.Log
|
||||
import java.awt.GraphicsEnvironment
|
||||
|
||||
@ -21,6 +22,9 @@ internal object DesktopLauncher {
|
||||
// Setup Desktop logging
|
||||
Log.backend = DesktopLogBackend()
|
||||
|
||||
// Setup Desktop display
|
||||
Display.platform = DesktopDisplay()
|
||||
|
||||
// Setup Desktop font
|
||||
Fonts.fontImplementation = DesktopFont()
|
||||
|
||||
@ -63,7 +67,6 @@ internal object DesktopLauncher {
|
||||
FileHandle(SETTINGS_FILE_NAME).writeString(json().toJson(settings), false) // so when we later open the game we get fullscreen
|
||||
}
|
||||
|
||||
|
||||
if (!isRunFromJAR) {
|
||||
UniqueDocsWriter().write()
|
||||
UiElementDocsWriter().write()
|
||||
|
Reference in New Issue
Block a user