Skip to content

Commit

Permalink
Fixed #469: Auto-Resize (#584)
Browse files Browse the repository at this point in the history
* Adds a new resize event and a resize policy to config

* Adds new resize capabilities natively to Indigo

* Fixes the performance example

* Fixes aspect ratio calculations

* Updates templates to remove debounce and centre the canvas

Now the canvas can resize itself we centre the canvas so that if the aspect
of the window is not the same as that of the canvas then it will always
appear in the correct position

* Adds documentation for the new `ApplicationResize` event

* Remove `ApplicationResize` - it just complicates things for no clear benefit

* Ensures that the canvas size can never be bigger than the container
  • Loading branch information
hobnob authored Jul 31, 2023
1 parent bea2cc2 commit 76a5477
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object IndigoBuild {
os.write(directoryStructure.base / "cordova.js", "")

// Write support js script
val support = SupportScriptTemplate.template(false)
val support = SupportScriptTemplate.template()
os.write(directoryStructure.base / "scripts" / "indigo-support.js", support)

// Fill out html template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object IndigoCordova {

// Write support js script
val supportFile = outputDir / "www" / "scripts" / "indigo-support.js"
val support = SupportScriptTemplate.template(true)
val support = SupportScriptTemplate.template()
os.remove(supportFile)
os.write(supportFile, support)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object IndigoRun {

// Write support js script
val supportFile = outputDir / "scripts" / "indigo-support.js"
val support = SupportScriptTemplate.template(true)
val support = SupportScriptTemplate.template()
os.remove(supportFile)
os.write(supportFile, support)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ object HtmlTemplate {
| background-color: $backgroundColor;
| }
| #indigo-container {
| display: flex;
| align-items: center;
| justify-content: center;
| padding:0px;
| margin:0px;
| width: 100vw;
| height: 100vh;
| }
|
| ${if (!showCursor) "canvas { cursor: none }" else ""}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,8 @@ package indigoplugin.templates

object SupportScriptTemplate {

def template(autoSize: Boolean): String =
def template(): String =
s"""
|// Shamelessly borrowed/modified from: https://davidwalsh.name/javascript-debounce-function
|function debounce(func, wait) {
| var timeout;
| return function() {
| var context = this
| var args = arguments;
| var later = function() {
| timeout = null;
| func.apply(context, args);
| };
| clearTimeout(timeout);
| timeout = setTimeout(later, wait);
| };
|};
|
|
|function resizeCanvas() {
| var c = document.getElementById("indigo-container-[indigo-canvas]");
| c.height = window.innerHeight;
| c.width = window.innerWidth;
|}
|
|${if (autoSize) "" else "// "}window.onresize = debounce(resizeCanvas, 1000);
|
|window.onload = function () {
| if (typeof history.pushState === "function") {
| history.pushState("jibberish", null, null);
Expand Down
3 changes: 3 additions & 0 deletions indigo/indigo/src/main/scala/indigo/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ val Flip: shared.datatypes.Flip.type = shared.datatypes.Flip
type AssetType = shared.assets.AssetType
val AssetType: shared.assets.AssetType.type = shared.assets.AssetType

type ResizePolicy = shared.config.ResizePolicy
val ResizePolicy: shared.config.ResizePolicy.type = shared.config.ResizePolicy

type GameConfig = shared.config.GameConfig
val GameConfig: shared.config.GameConfig.type = shared.config.GameConfig

Expand Down
8 changes: 7 additions & 1 deletion indigo/indigo/src/main/scala/indigo/platform/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,13 @@ class Platform(
Outcome {
if firstRun then
IndigoLogger.info("Starting world events")
_worldEvents.init(canvas, gameConfig.magnification, gameConfig.advanced.disableContextMenu, globalEventStream)
_worldEvents.init(
canvas,
gameConfig.resizePolicy,
gameConfig.magnification,
gameConfig.advanced.disableContextMenu,
globalEventStream
)
GamepadInputCaptureImpl.init()
else IndigoLogger.info("Re-using existing world events")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package indigo.platform.events

import indigo.shared.collections.Batch
import indigo.shared.config.ResizePolicy
import indigo.shared.constants.Key
import indigo.shared.datatypes.Point
import indigo.shared.datatypes.Radians
import indigo.shared.datatypes.Size
import indigo.shared.events.ApplicationGainedFocus
import indigo.shared.events.ApplicationLostFocus
import indigo.shared.events.CanvasGainedFocus
Expand Down Expand Up @@ -45,6 +47,7 @@ final class WorldEvents:

final case class Handlers(
canvas: html.Canvas,
resizePolicy: ResizePolicy,
onClick: dom.MouseEvent => Unit,
onWheel: dom.WheelEvent => Unit,
onKeyDown: dom.KeyboardEvent => Unit,
Expand All @@ -59,7 +62,8 @@ final class WorldEvents:
onBlur: dom.FocusEvent => Unit,
onFocus: dom.FocusEvent => Unit,
onOnline: dom.Event => Unit,
onOffline: dom.Event => Unit
onOffline: dom.Event => Unit,
resizeObserver: dom.ResizeObserver
) {
canvas.addEventListener("click", onClick)
canvas.addEventListener("wheel", onWheel)
Expand All @@ -78,6 +82,7 @@ final class WorldEvents:
document.addEventListener("keyup", onKeyUp)
window.addEventListener("online", onOnline)
window.addEventListener("offline", onOffline)
resizeObserver.observe(canvas.parentElement)

def unbind(): Unit = {
canvas.removeEventListener("click", onClick)
Expand All @@ -97,17 +102,20 @@ final class WorldEvents:
document.removeEventListener("keyup", onKeyUp)
window.removeEventListener("online", onOnline)
window.removeEventListener("offline", onOffline)
resizeObserver.disconnect()
}
}

object Handlers {
def apply(
canvas: html.Canvas,
resizePolicy: ResizePolicy,
magnification: Int,
disableContextMenu: Boolean,
globalEventStream: GlobalEventStream
): Handlers = Handlers(
canvas = canvas,
resizePolicy,
// onClick only supports the left mouse button
onClick = { e =>
MouseButton.fromOrdinalOpt(e.button).foreach { button =>
Expand Down Expand Up @@ -428,7 +436,58 @@ final class WorldEvents:
},
onOffline = { e =>
globalEventStream.pushGlobalEvent(NetworkEvent.Offline)
}
},
resizeObserver = new dom.ResizeObserver((entries, _) =>
entries.foreach { entry =>
entry.target.childNodes.foreach { child =>
if child.attributes.getNamedItem("id").value == canvas.attributes.getNamedItem("id").value then
val containerSize = new Size(
Math.floor(entry.contentRect.width).toInt,
Math.floor(entry.contentRect.height).toInt
)
val canvasSize = new Size(canvas.width, canvas.height)
if resizePolicy != ResizePolicy.NoResize then
val newSize = resizePolicy match {
case ResizePolicy.Resize => containerSize
case ResizePolicy.ResizePreserveAspect =>
val width = canvas.width.toDouble
val height = canvas.height.toDouble
val aspectRatio = Math.min(width, height) / Math.max(width, height)

if width > height then
val newHeight = containerSize.width.toDouble * aspectRatio
if newHeight > containerSize.height then
Size(
(containerSize.height / aspectRatio).toInt,
containerSize.height
)
else
Size(
containerSize.width,
newHeight.toInt
)
else
val newWidth = containerSize.height.toDouble * aspectRatio
if newWidth > containerSize.width then
Size(
containerSize.width,
(containerSize.width / aspectRatio).toInt
)
else
Size(
newWidth.toInt,
containerSize.height
)
case _ => canvasSize
}

if (newSize != canvasSize) {
canvas.width = Math.min(newSize.width, containerSize.width)
canvas.height = Math.min(newSize.height, containerSize.height)
}
}
}
)
)
}

Expand All @@ -437,11 +496,13 @@ final class WorldEvents:

def init(
canvas: html.Canvas,
resizePolicy: ResizePolicy,
magnification: Int,
disableContextMenu: Boolean,
globalEventStream: GlobalEventStream
): Unit =
if (_handlers.isEmpty) _handlers = Some(Handlers(canvas, magnification, disableContextMenu, globalEventStream))
if (_handlers.isEmpty)
_handlers = Some(Handlers(canvas, resizePolicy, magnification, disableContextMenu, globalEventStream))

def kill(): Unit = _handlers.foreach { x =>
x.unbind()
Expand Down
18 changes: 18 additions & 0 deletions indigo/indigo/src/main/scala/indigo/shared/config/GameConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import indigo.shared.time.FPS

import scala.annotation.targetName

enum ResizePolicy derives CanEqual:
case NoResize, Resize, ResizePreserveAspect

/** All the base settings needed to get a game up and running.
*
* @param viewport
Expand All @@ -28,6 +31,7 @@ final case class GameConfig(
frameRateLimit: Option[FPS],
clearColor: RGBA,
magnification: Int,
resizePolicy: ResizePolicy,
transparentBackground: Boolean,
advanced: AdvancedGameConfig
) derives CanEqual:
Expand All @@ -45,6 +49,7 @@ final case class GameConfig(
|- Clear color: {red: ${clearColor.r.toString()}, green: ${clearColor.g.toString()}, blue: ${clearColor.b
.toString()}, alpha: ${clearColor.a.toString()}}
|- Magnification: ${magnification.toString()}
|- Resize Policy: ${resizePolicy.toString()}
|${advanced.asString}
|""".stripMargin

Expand Down Expand Up @@ -87,6 +92,15 @@ final case class GameConfig(
def noTransparentBackground: GameConfig =
withTransparentBackground(false)

def withResizePolicy(resizePolicy: ResizePolicy): GameConfig =
this.copy(resizePolicy = resizePolicy)
def noResize: GameConfig =
withResizePolicy(ResizePolicy.NoResize)
def autoResize: GameConfig =
withResizePolicy(ResizePolicy.Resize)
def autoResizePreserveAspect: GameConfig =
withResizePolicy(ResizePolicy.ResizePreserveAspect)

object GameConfig:

val default: GameConfig =
Expand All @@ -96,6 +110,7 @@ object GameConfig:
clearColor = RGBA.Black,
magnification = 1,
transparentBackground = false,
resizePolicy = ResizePolicy.Resize,
advanced = AdvancedGameConfig.default
)

Expand All @@ -106,6 +121,7 @@ object GameConfig:
clearColor = RGBA.Black,
magnification = 1,
transparentBackground = false,
resizePolicy = ResizePolicy.Resize,
advanced = AdvancedGameConfig.default
)

Expand All @@ -116,6 +132,7 @@ object GameConfig:
clearColor = clearColor,
magnification = magnification,
transparentBackground = false,
resizePolicy = ResizePolicy.Resize,
advanced = AdvancedGameConfig.default
)

Expand All @@ -126,5 +143,6 @@ object GameConfig:
clearColor = clearColor,
magnification = magnification,
transparentBackground = false,
resizePolicy = ResizePolicy.Resize,
advanced = AdvancedGameConfig.default
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import indigo.shared.datatypes.BindingKey
import indigo.shared.datatypes.Point
import indigo.shared.datatypes.RGBA
import indigo.shared.datatypes.Radians
import indigo.shared.datatypes.Size

/** A trait that tells Indigo to allow this instance into the event loop for the duration of one frame.
*/
Expand Down
1 change: 1 addition & 0 deletions indigo/perf/src/main/scala/com/example/perf/PerfGame.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ object PerfGame extends IndigoDemo[Unit, Dude, DudeModel, Unit] {
frameRateLimit = None,
clearColor = RGBA(0.4, 0.2, 0.5, 1),
magnification = magnificationLevel,
resizePolicy = ResizePolicy.NoResize,
transparentBackground = false,
advanced = AdvancedGameConfig(
renderingTechnology = RenderingTechnology.WebGL2,
Expand Down

0 comments on commit 76a5477

Please sign in to comment.