All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.org.openrndr.WindowProgram.kt Maven / Gradle / Ivy

package org.openrndr

import org.openrndr.animatable.Animatable
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.Drawer
import org.openrndr.events.Event
import org.openrndr.internal.Driver
import org.openrndr.math.Vector2

open class WindowProgram(val suspend: Boolean = false) : Program {
    override var width = 0
    override var height = 0

    override val program: Program by lazy { this }
    override var userProperties: MutableMap = mutableMapOf()


    override var name = rootClassName()

    private val animator by lazy { Animatable() }

    override lateinit var drawer: Drawer
    override lateinit var driver: Driver

    override lateinit var application: Application
    lateinit var applicationWindow: ApplicationWindow

    /** This is checked at runtime to disallow nesting [extend] blocks. */
    override var isNested: Boolean = false

    /**
     * background color that is used to clear the background every frame
     */
    override var backgroundColor: ColorRGBa? = ColorRGBa.BLACK
    override val dispatcher = Dispatcher()

    /**
     * program ended event
     *
     * The [ended] event is emitted when the program is ended by closing the application window
     */
    override var ended = Event()


    private var firstFrameTime = Double.POSITIVE_INFINITY

    /**
     * clock function. defaults to returning the application time.
     */
    override var clock =
        { if (firstFrameTime == Double.POSITIVE_INFINITY || frameCount <= 0) 0.0 else (application.seconds - firstFrameTime) }

    override var assetProperties = mutableMapOf()
    override var assetMetadata = {
        AssetMetadata(this.name, namedTimestamp(), assetProperties)
    }

    final override val requestAssets = Event()
    final override val produceAssets = Event()

    init {
        requestAssets.listen {
            produceAssets.trigger(
                ProduceAssetsEvent(
                    it.origin, it.program,
                    assetMetadata()
                )
            )
        }
    }


    private var frameSeconds = 0.0
    private var deltaSeconds: Double = 0.0
    private var lastSeconds: Double = -1.0

    override var frameCount = 0
    override val clipboard: ProgramImplementation.ApplicationClipboard
        get() = TODO("Not yet implemented")

    /**
     * The number of [seconds] since program start, or the time from a custom [clock].
     * value is updated at the beginning of the frame only.
     */
    override val seconds: Double
        get() = frameSeconds

    /**
     * The elapsed time since the last draw loop
     */
    val deltaTime: Double
        get() = deltaSeconds

    inner class ApplicationClipboard : Clipboard {
        override var contents: String?
            get() {
                return application.clipboardContents
            }
            set(value) {
                application.clipboardContents = value
            }
    }


    /**
     * list of installed extensions
     */
    override val extensions = mutableListOf()
        get() {
            if (field.isEmpty()) isNested = false
            return field
        }

    /**
     * install an [Extension]
     * @param extension the [Extension] to install
     */
    override fun  extend(extension: T): T {
        extensions.add(extension)
        extension.setup(this)
        return extension
    }

    /**
     * install an [Extension] and configure it
     * @param extension the [Extension] to install
     * @param configure a configuration function to called with [extension] as its receiver
     * @return the installed [Extension]
     */
    override fun  extend(extension: T, configure: T.() -> Unit): T {
        extensions.add(extension)
        extension.configure()
        extension.setup(this)
        return extension
    }

    /**
     * install an extension function for the given [ExtensionStage]
     */
    override fun extend(stage: ExtensionStage, userDraw: Program.() -> Unit) {
        if (isNested) error("Cannot nest extend blocks within extend blocks")
        val functionExtension = when (stage) {
            ExtensionStage.SETUP ->
                object : Extension {
                    override var enabled: Boolean = true
                    override fun setup(program: Program) {
                        program.isNested = true
                        program.userDraw()
                    }
                }

            ExtensionStage.BEFORE_DRAW ->
                object : Extension {
                    override var enabled: Boolean = true
                    override fun beforeDraw(drawer: Drawer, program: Program) {
                        program.isNested = true
                        program.userDraw()
                    }
                }

            ExtensionStage.AFTER_DRAW ->
                object : Extension {
                    override var enabled: Boolean = true
                    override fun afterDraw(drawer: Drawer, program: Program) {
                        program.isNested = true
                        program.userDraw()
                    }
                }
        }
        extensions.add(functionExtension)
    }

    /**
     * Simplified window interface
     */
    inner class Window : org.openrndr.Window {
        override var title: String
            get() = applicationWindow.windowTitle
            set(value) {
                applicationWindow.windowTitle = value
            }

        override var size
            get() = applicationWindow.windowSize
            set(value) {
                applicationWindow.windowSize = value
            }

        override var contentScale
            get() = application.windowContentScale
            set(value) {
                application.windowContentScale = value
            }

        override var presentationMode: PresentationMode
            get() = application.presentationMode
            set(value) {
                application.presentationMode = value
            }

        override var multisample: WindowMultisample
            get() {
                return applicationWindow.windowMultisample
            }
            set(value) {
                application.windowMultisample = value
            }

        override var resizable: Boolean
            get() {
                return application.windowResizable
            }
            set(value) {
                application.windowResizable = value
            }

        override fun requestFocus() = application.requestFocus()

        override fun requestDraw() = application.requestDraw()

        /**
         * Window focused event, triggered when the window receives focus
         */
        override val focused = Event("window-focused", postpone = true)

        /**
         * Window focused event, triggered when the window loses focus
         */
        override val unfocused = Event("window-unfocused", postpone = true)

        /**
         * Window moved event
         */
        override val moved = Event("window-moved", postpone = true)

        /**
         * Window sized event
         */
        override val sized = Event("window-sized", postpone = true)

        /**
         * Window minimized event
         */
        override val minimized = Event("window-minimized", postpone = true)

        /**
         * Window restored (from minimization) event
         */
        override val restored = Event("window-restored", postpone = true)

        /**
         * Window restored (from minimization) event
         */
        override val closed = Event("window-closed", postpone = true)

        /**
         * Drop event, triggered when a file is dropped on the window
         */
        override val drop = Event("window-drop", postpone = true)

        /**
         * Window position
         */
        override var position: Vector2
            get() = application.windowPosition
            set(value) {
                application.windowPosition = value
            }
    }

    override val window = Window()


    override val keyboard by lazy { Keyboard() }
    override val mouse by lazy { ApplicationWindowMouse(applicationWindow = { applicationWindow }) }
    override val pointers by lazy { Pointers(application = { application }) }

    /**
     * This runs exactly once before the first call to draw()
     */
    override suspend fun setup() {}

    /**
     * This is the draw call that is called by Application. It takes care of handling extensions.
     */
    override fun drawImpl() {
        if (frameCount == 0) {
            firstFrameTime = application.seconds
        }
        animator.updateAnimation()
        updateFrameSecondsFromClock()

        if (lastSeconds == -1.0) lastSeconds = seconds

        deltaSeconds = frameSeconds - lastSeconds
        lastSeconds = frameSeconds


        backgroundColor?.let {
            drawer.clear(it)
        }
        extensions.filter { it.enabled }.forEach { it.beforeDraw(drawer, this) }
        draw()
        extensions.reversed().filter { it.enabled }.forEach { it.afterDraw(drawer, this) }
        frameCount++
    }

    fun animate(animationFunction: Animatable.() -> Unit) {
        animator.animationFunction()
    }

    /**
     * This is the user facing draw call. It should be overridden by the user.
     */
    override fun draw() {}

    override fun updateFrameSecondsFromClock() {
        frameSeconds = clock()
    }
}
fun Program.window(
    configuration: WindowConfiguration = WindowConfiguration(),
    init: suspend Program.() -> Unit): ApplicationWindow {

    val program  = object : WindowProgram() {
        override suspend fun setup() {
            init()
        }
    }
    return application.createChildWindow(configuration, program)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy