From f55e0104515b70538317476d4b248f7d0a264139 Mon Sep 17 00:00:00 2001 From: vegeta1k95 <32207817+vegeta1k95@users.noreply.github.com> Date: Sat, 4 Mar 2023 18:22:09 +0100 Subject: [PATCH] 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 --- .../jsons/translations/template.properties | 2 +- android/src/com/unciv/app/AndroidDisplay.kt | 83 +++++++++++++++++++ android/src/com/unciv/app/AndroidLauncher.kt | 32 ++----- core/src/com/unciv/UncivGame.kt | 3 +- .../com/unciv/models/metadata/GameSettings.kt | 27 +----- .../com/unciv/ui/popups/options/DisplayTab.kt | 28 ++++--- core/src/com/unciv/utils/Display.kt | 33 ++++++++ .../com/unciv/app/desktop/DesktopDisplay.kt | 79 ++++++++++++++++++ .../com/unciv/app/desktop/DesktopLauncher.kt | 5 +- 9 files changed, 227 insertions(+), 65 deletions(-) create mode 100644 android/src/com/unciv/app/AndroidDisplay.kt create mode 100644 core/src/com/unciv/utils/Display.kt create mode 100644 desktop/src/com/unciv/app/desktop/DesktopDisplay.kt diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 4f27aea02f..df4cfc3083 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -1380,7 +1380,7 @@ Movement cost = for = Missing translations: = Screen Size = -Screen Window = +Screen Mode = Windowed = Fullscreen = Tileset = diff --git a/android/src/com/unciv/app/AndroidDisplay.kt b/android/src/com/unciv/app/AndroidDisplay.kt new file mode 100644 index 0000000000..8e659d073c --- /dev/null +++ b/android/src/com/unciv/app/AndroidDisplay.kt @@ -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 = 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 { + 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 + } + } + + } + +} diff --git a/android/src/com/unciv/app/AndroidLauncher.kt b/android/src/com/unciv/app/AndroidLauncher.kt index 1593f915c0..ec52dde01a 100644 --- a/android/src/com/unciv/app/AndroidLauncher.kt +++ b/android/src/com/unciv/app/AndroidLauncher.kt @@ -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. diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt index 757fc2f877..e019bd761b 100644 --- a/core/src/com/unciv/UncivGame.kt +++ b/core/src/com/unciv/UncivGame.kt @@ -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() diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index 1ff8927203..c7ffec2a83 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -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() var tutorialTasksCompleted = HashSet() @@ -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) { diff --git a/core/src/com/unciv/ui/popups/options/DisplayTab.kt b/core/src/com/unciv/ui/popups/options/DisplayTab.kt index d8ce9839ec..a4f01ff8f8 100644 --- a/core/src/com/unciv/ui/popups/options/DisplayTab.kt +++ b/core/src/com/unciv/ui/popups/options/DisplayTab.kt @@ -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(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) { diff --git a/core/src/com/unciv/utils/Display.kt b/core/src/com/unciv/utils/Display.kt new file mode 100644 index 0000000000..af0d2c9db6 --- /dev/null +++ b/core/src/com/unciv/utils/Display.kt @@ -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 { return hashMapOf() } + fun getDefaultMode(): ScreenMode + +} + +object Display { + + lateinit var platform: PlatformDisplay + + fun getDefaultMode(): ScreenMode { + return platform.getDefaultMode() + } + + fun getScreenModes(): Map { + return platform.getScreenModes() + } + + fun setScreenMode(id: Int, settings: GameSettings) { + platform.setScreenMode(id, settings) + } + +} diff --git a/desktop/src/com/unciv/app/desktop/DesktopDisplay.kt b/desktop/src/com/unciv/app/desktop/DesktopDisplay.kt new file mode 100644 index 0000000000..fad93c9c82 --- /dev/null +++ b/desktop/src/com/unciv/app/desktop/DesktopDisplay.kt @@ -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() + + 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 { + 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) + ) + } + + } + } +} diff --git a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt index f71e1e4786..f2f2cb46f6 100644 --- a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt +++ b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt @@ -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()