Add IO wrapper for InputManager
This commit is contained in:
parent
4ff54c1373
commit
dd01b070ff
@ -53,8 +53,10 @@ import wow.doge.mygame.game.entities.NpcActorSupervisor
|
|||||||
import wow.doge.mygame.game.entities.NpcMovementActor
|
import wow.doge.mygame.game.entities.NpcMovementActor
|
||||||
import wow.doge.mygame.game.entities.player.PlayerActor
|
import wow.doge.mygame.game.entities.player.PlayerActor
|
||||||
import wow.doge.mygame.game.entities.player.PlayerController
|
import wow.doge.mygame.game.entities.player.PlayerController
|
||||||
import wow.doge.mygame.game.subsystems.input.GameInputHandler
|
import wow.doge.mygame.game.entities.player.PlayerMovementReducer
|
||||||
|
import wow.doge.mygame.game.subsystems.input.InputMappings
|
||||||
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
import wow.doge.mygame.game.subsystems.input.PlayerCameraInput
|
||||||
|
import wow.doge.mygame.game.subsystems.input.PlayerMovementInput
|
||||||
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
import wow.doge.mygame.game.subsystems.level.DefaultGameLevel
|
||||||
import wow.doge.mygame.implicits._
|
import wow.doge.mygame.implicits._
|
||||||
import wow.doge.mygame.launcher.Launcher
|
import wow.doge.mygame.launcher.Launcher
|
||||||
@ -80,6 +82,7 @@ import wow.doge.mygame.utils.IOUtils
|
|||||||
import wow.doge.mygame.utils.MonixDirectoryWatcher
|
import wow.doge.mygame.utils.MonixDirectoryWatcher
|
||||||
import wow.doge.mygame.utils.MonixDirectoryWatcher.ModifyEvent
|
import wow.doge.mygame.utils.MonixDirectoryWatcher.ModifyEvent
|
||||||
import wow.doge.mygame.utils.controls.JFXProgressBar
|
import wow.doge.mygame.utils.controls.JFXProgressBar
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme
|
||||||
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
import wow.doge.mygame.utils.wrappers.jme.AssetManager
|
||||||
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
import wow.doge.mygame.utils.wrappers.jme.PhysicsSpace
|
||||||
|
|
||||||
@ -118,7 +121,7 @@ class MainApp(
|
|||||||
.toIO(
|
.toIO(
|
||||||
obs
|
obs
|
||||||
.doOnNextF(pme =>
|
.doOnNextF(pme =>
|
||||||
Coeval(pprint.log(show"Received event $pme")).void
|
logger.trace(show"Received event $pme").toTask.void
|
||||||
)
|
)
|
||||||
.completedL
|
.completedL
|
||||||
.startAndForget
|
.startAndForget
|
||||||
@ -259,7 +262,8 @@ class MainAppDelegate(
|
|||||||
_ <- level.addToGame(rootNode, physicsSpace)
|
_ <- level.addToGame(rootNode, physicsSpace)
|
||||||
playerActor <- createPlayerController()
|
playerActor <- createPlayerController()
|
||||||
// .onErrorRestart(3)
|
// .onErrorRestart(3)
|
||||||
_ <- wire[GameInputHandler.Props].begin
|
// _ <- wire[GameInputHandler.Props].begin
|
||||||
|
_ <- new InputMappings(new jme.InputManager(inputManager)).setup
|
||||||
// .onErrorRestart(3)
|
// .onErrorRestart(3)
|
||||||
johnActor <- createTestNpc("John")
|
johnActor <- createTestNpc("John")
|
||||||
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
// _ <- johnActor !! NpcActorSupervisor.Move(ImVector3f(0, 0, 20))
|
||||||
@ -394,7 +398,7 @@ class MainAppDelegate(
|
|||||||
new Label("Health") { textFill = Color.White },
|
new Label("Health") { textFill = Color.White },
|
||||||
new Label("100") {
|
new Label("100") {
|
||||||
text <-- statsObs
|
text <-- statsObs
|
||||||
.doOnNextF(i => loggerL.debug(show"Received stats: $i"))
|
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
|
||||||
.map(_.hp.toInt.toString)
|
.map(_.hp.toInt.toString)
|
||||||
textFill = Color.White
|
textFill = Color.White
|
||||||
},
|
},
|
||||||
@ -407,11 +411,7 @@ class MainAppDelegate(
|
|||||||
c += statsObs
|
c += statsObs
|
||||||
.scanEval(me.Task.pure("green-bar")) {
|
.scanEval(me.Task.pure("green-bar")) {
|
||||||
case (a, b) =>
|
case (a, b) =>
|
||||||
me.Task {
|
me.Task(styleClass.removeAll(a)) >>
|
||||||
pprint.log(show"Received $a $b")
|
|
||||||
pprint.log(show"${styleClass.toString}")
|
|
||||||
styleClass.removeAll(a)
|
|
||||||
} >>
|
|
||||||
me.Task.pure(
|
me.Task.pure(
|
||||||
(b.hp.toInt: @switch) match {
|
(b.hp.toInt: @switch) match {
|
||||||
case v if v > 80 => "green-bar"
|
case v if v > 80 => "green-bar"
|
||||||
@ -435,7 +435,7 @@ class MainAppDelegate(
|
|||||||
new Label("100") {
|
new Label("100") {
|
||||||
textFill = Color.White
|
textFill = Color.White
|
||||||
text <-- statsObs
|
text <-- statsObs
|
||||||
.doOnNextF(i => loggerL.debug(show"Received stats: $i"))
|
.doOnNextF(i => loggerL.trace(show"Received stats: $i"))
|
||||||
.map(_.stamina.toInt.toString)
|
.map(_.stamina.toInt.toString)
|
||||||
},
|
},
|
||||||
new JFXProgressBar {
|
new JFXProgressBar {
|
||||||
@ -449,11 +449,7 @@ class MainAppDelegate(
|
|||||||
c += statsObs
|
c += statsObs
|
||||||
.scanEval(me.Task.pure("green-bar")) {
|
.scanEval(me.Task.pure("green-bar")) {
|
||||||
case (a, b) =>
|
case (a, b) =>
|
||||||
me.Task {
|
me.Task(styleClass.removeAll(a)) >>
|
||||||
pprint.log(show"Received $a $b")
|
|
||||||
pprint.log(show"${styleClass.toString}")
|
|
||||||
styleClass.removeAll(a)
|
|
||||||
} >>
|
|
||||||
me.Task.pure(
|
me.Task.pure(
|
||||||
(b.stamina.toInt: @switch) match {
|
(b.stamina.toInt: @switch) match {
|
||||||
case v if v > 80 => "green-bar"
|
case v if v > 80 => "green-bar"
|
||||||
@ -546,9 +542,22 @@ class MainAppDelegate(
|
|||||||
.toIO
|
.toIO
|
||||||
.hideErrors
|
.hideErrors
|
||||||
.startAndForget
|
.startAndForget
|
||||||
|
|
||||||
sched <- UIO.pure(schedulers.async)
|
sched <- UIO.pure(schedulers.async)
|
||||||
fxSched <- UIO.pure(schedulers.fx)
|
fxSched <- UIO.pure(schedulers.fx)
|
||||||
playerActor <- wire[PlayerController.Props].create
|
playerActor <- wire[PlayerController.Props].create
|
||||||
|
playerMovementReducer = new PlayerMovementReducer(playerActor, loggerL)
|
||||||
|
_ <-
|
||||||
|
inputManager
|
||||||
|
.enumObservableAction(PlayerMovementInput)
|
||||||
|
.sample(1.millis)
|
||||||
|
.scanEval(me.Task.pure(PlayerMovementReducer.State.empty))(
|
||||||
|
playerMovementReducer.value
|
||||||
|
)
|
||||||
|
.completedL
|
||||||
|
.toIO
|
||||||
|
.hideErrors
|
||||||
|
.startAndForget
|
||||||
} yield playerActor
|
} yield playerActor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import wow.doge.mygame.subsystems.events.PlayerCameraEvent
|
|||||||
import wow.doge.mygame.subsystems.events.PlayerEvent
|
import wow.doge.mygame.subsystems.events.PlayerEvent
|
||||||
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
import wow.doge.mygame.subsystems.events.PlayerMovementEvent
|
||||||
import wow.doge.mygame.utils.IOUtils._
|
import wow.doge.mygame.utils.IOUtils._
|
||||||
|
import wow.doge.mygame.utils.methodName
|
||||||
|
|
||||||
object GameInputHandler {
|
object GameInputHandler {
|
||||||
|
|
||||||
@ -29,16 +30,16 @@ object GameInputHandler {
|
|||||||
) {
|
) {
|
||||||
def begin =
|
def begin =
|
||||||
for {
|
for {
|
||||||
_ <- UIO(setupMovementKeys(inputManager))
|
_ <- UIO(setupMovementKeys)
|
||||||
// _ <- UIO(setupAnalogMovementKeys)
|
// _ <- UIO(setupAnalogMovementKeys)
|
||||||
_ <- UIO(setupCameraKeys())
|
_ <- UIO(setupCameraKeys())
|
||||||
_ <- toIO(
|
_ <- toIO(
|
||||||
me.Task.parSequence(
|
me.Task.parSequence(
|
||||||
Seq(
|
Seq(
|
||||||
playerMovementInputEventsGenerator(
|
// playerMovementInputEventsGenerator(
|
||||||
inputManager,
|
// inputManager,
|
||||||
playerEventBus
|
// playerEventBus
|
||||||
).completedL,
|
// ).completedL,
|
||||||
// generateAnalogMovementEvents(
|
// generateAnalogMovementEvents(
|
||||||
// inputManager,
|
// inputManager,
|
||||||
// playerEventBus
|
// playerEventBus
|
||||||
@ -55,7 +56,7 @@ object GameInputHandler {
|
|||||||
).startAndForget
|
).startAndForget
|
||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
def setupMovementKeys(inputManager: InputManager) =
|
def setupMovementKeys =
|
||||||
inputManager.withEnumMappings(PlayerMovementInput) {
|
inputManager.withEnumMappings(PlayerMovementInput) {
|
||||||
case PlayerMovementInput.WalkRight =>
|
case PlayerMovementInput.WalkRight =>
|
||||||
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
||||||
@ -128,9 +129,6 @@ object GameInputHandler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def methodName(implicit enclosing: sourcecode.Enclosing) =
|
|
||||||
enclosing.value.split(" ")(0).split("""\.""").last
|
|
||||||
|
|
||||||
def playerMovementInputEventsGenerator(
|
def playerMovementInputEventsGenerator(
|
||||||
inputManager: InputManager,
|
inputManager: InputManager,
|
||||||
playerEventBus: GameEventBus[PlayerEvent]
|
playerEventBus: GameEventBus[PlayerEvent]
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
package wow.doge.mygame.game.subsystems.input
|
||||||
|
|
||||||
|
import com.jme3.input.KeyInput
|
||||||
|
import com.jme3.input.MouseInput
|
||||||
|
import com.jme3.input.controls.KeyTrigger
|
||||||
|
import com.jme3.input.controls.MouseAxisTrigger
|
||||||
|
import monix.{eval => me}
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
import wow.doge.mygame.utils.wrappers.jme.InputManager
|
||||||
|
|
||||||
|
class InputMappings(inputManager: InputManager) {
|
||||||
|
|
||||||
|
def setup =
|
||||||
|
for {
|
||||||
|
_ <- setupMovementKeys
|
||||||
|
_ <- setupAnalogMovementKeys
|
||||||
|
_ <- setupCameraKeys
|
||||||
|
_ <- cursorToggle
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
def setupMovementKeys =
|
||||||
|
inputManager.withEnumMappings(PlayerMovementInput) {
|
||||||
|
case PlayerMovementInput.WalkRight =>
|
||||||
|
new KeyTrigger(KeyInput.KEY_D) :: Nil
|
||||||
|
case PlayerMovementInput.WalkLeft =>
|
||||||
|
new KeyTrigger(KeyInput.KEY_A) :: Nil
|
||||||
|
case PlayerMovementInput.WalkForward =>
|
||||||
|
new KeyTrigger(KeyInput.KEY_W) :: Nil
|
||||||
|
case PlayerMovementInput.WalkBackward =>
|
||||||
|
new KeyTrigger(KeyInput.KEY_S) :: Nil
|
||||||
|
case PlayerMovementInput.Jump =>
|
||||||
|
new KeyTrigger(KeyInput.KEY_SPACE) :: Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
def setupAnalogMovementKeys =
|
||||||
|
inputManager.withEnumMappings(PlayerAnalogMovementInput) {
|
||||||
|
case PlayerAnalogMovementInput.TurnRight =>
|
||||||
|
Seq(new KeyTrigger(KeyInput.KEY_D))
|
||||||
|
case PlayerAnalogMovementInput.TurnLeft =>
|
||||||
|
Seq(new KeyTrigger(KeyInput.KEY_A))
|
||||||
|
}
|
||||||
|
|
||||||
|
def setupCameraKeys =
|
||||||
|
inputManager.withEnumMappings(PlayerCameraInput) {
|
||||||
|
case PlayerCameraInput.CameraRotateLeft =>
|
||||||
|
Seq(
|
||||||
|
new KeyTrigger(KeyInput.KEY_LEFT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_X, false)
|
||||||
|
)
|
||||||
|
case PlayerCameraInput.CameraRotateRight =>
|
||||||
|
Seq(
|
||||||
|
new KeyTrigger(KeyInput.KEY_RIGHT),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_X, true)
|
||||||
|
)
|
||||||
|
case PlayerCameraInput.CameraRotateUp =>
|
||||||
|
Seq(
|
||||||
|
new KeyTrigger(KeyInput.KEY_UP),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_Y, false)
|
||||||
|
)
|
||||||
|
case PlayerCameraInput.CameraRotateDown =>
|
||||||
|
Seq(
|
||||||
|
new KeyTrigger(KeyInput.KEY_DOWN),
|
||||||
|
new MouseAxisTrigger(MouseInput.AXIS_Y, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def cursorToggle =
|
||||||
|
inputManager.withMapping(
|
||||||
|
MiscInput.ToggleCursor,
|
||||||
|
new KeyTrigger(KeyInput.KEY_Z)
|
||||||
|
) >>
|
||||||
|
inputManager
|
||||||
|
.enumEntryObservableAction(MiscInput.ToggleCursor)
|
||||||
|
.doOnNext(action =>
|
||||||
|
action.binding match {
|
||||||
|
case MiscInput.ToggleCursor =>
|
||||||
|
if (action.value)(inputManager.cursorVisible =
|
||||||
|
!inputManager.cursorVisible).toTask
|
||||||
|
else me.Task.unit
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.completedL
|
||||||
|
.toIO
|
||||||
|
.hideErrors
|
||||||
|
.startAndForget
|
||||||
|
|
||||||
|
}
|
@ -40,19 +40,16 @@ object MonixDirectoryWatcher {
|
|||||||
logger = logger.underlying
|
logger = logger.underlying
|
||||||
) {
|
) {
|
||||||
override def onCreate(file: File, count: Int) =
|
override def onCreate(file: File, count: Int) =
|
||||||
// println(show"$file got created")
|
|
||||||
if (sub.onNext(CreateEvent(file, count)) == Ack.Stop)
|
if (sub.onNext(CreateEvent(file, count)) == Ack.Stop)
|
||||||
c.cancel()
|
c.cancel()
|
||||||
override def onModify(file: File, count: Int) = {
|
|
||||||
pprint.log(show"${file.toString} got modified $count times")
|
override def onModify(file: File, count: Int) =
|
||||||
if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop)
|
if (sub.onNext(ModifyEvent(file, count)) == Ack.Stop)
|
||||||
c.cancel()
|
c.cancel()
|
||||||
}
|
|
||||||
override def onDelete(file: File, count: Int) =
|
override def onDelete(file: File, count: Int) =
|
||||||
// println(show"$file got deleted")
|
|
||||||
if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop)
|
if (sub.onNext(DeleteEvent(file, count)) == Ack.Stop)
|
||||||
c.cancel()
|
c.cancel()
|
||||||
|
|
||||||
}
|
}
|
||||||
watcher.start()(scheduler)
|
watcher.start()(scheduler)
|
||||||
c := Cancelable(() => watcher.stop())
|
c := Cancelable(() => watcher.stop())
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package wow.doge.mygame
|
package wow.doge.mygame
|
||||||
|
|
||||||
// import wow.doge.mygame.utils.wrappers.Node
|
|
||||||
|
|
||||||
package object utils {
|
package object utils {
|
||||||
// type AppNode = Node
|
def methodName(implicit enclosing: sourcecode.Enclosing) =
|
||||||
|
enclosing.value.split(" ")(0).split("""\.""").last
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
package wow.doge.mygame.utils.wrappers.jme
|
||||||
|
import com.jme3.input.controls.InputListener
|
||||||
|
import com.jme3.input.controls.Trigger
|
||||||
|
import com.jme3.{input => jmei}
|
||||||
|
import enumeratum._
|
||||||
|
import monix.bio.UIO
|
||||||
|
import monix.reactive.Observable
|
||||||
|
import wow.doge.mygame.ActionEvent
|
||||||
|
import wow.doge.mygame.AnalogEvent
|
||||||
|
import wow.doge.mygame.EnumActionEvent
|
||||||
|
import wow.doge.mygame.EnumAnalogEvent
|
||||||
|
import wow.doge.mygame.implicits._
|
||||||
|
|
||||||
|
final class InputManager(val delegate: jmei.InputManager) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new mapping to the given triggers.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The given mapping will be assigned to the given triggers, when
|
||||||
|
* any of the triggers given raise an event, the listeners
|
||||||
|
* registered to the mappings will receive appropriate events.
|
||||||
|
*
|
||||||
|
* @param mappingName The mapping name to assign.
|
||||||
|
* @param triggers The triggers to which the mapping is to be registered.
|
||||||
|
*/
|
||||||
|
def withMapping(mapping: String, triggers: Trigger*): UIO[Unit] =
|
||||||
|
UIO(delegate.withMapping(mapping, triggers: _*)).void
|
||||||
|
|
||||||
|
def withMapping[T <: EnumEntry](
|
||||||
|
mapping: T,
|
||||||
|
triggers: Trigger*
|
||||||
|
): UIO[Unit] = UIO(delegate.withMapping(mapping, triggers: _*)).void
|
||||||
|
|
||||||
|
def withListener(listener: InputListener, mappings: String*) =
|
||||||
|
UIO(delegate.withListener(listener, mappings: _*))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new mappings from the values of the given Enum
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The given mapping will be assigned to the given triggers, when
|
||||||
|
* any of the triggers given raise an event, the listeners
|
||||||
|
* registered to the mappings will receive appropriate events.
|
||||||
|
*
|
||||||
|
* @param mappingName The mapping name to assign.
|
||||||
|
* @param mappingFn Function from enum values to the sequence of trigers.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* {{{
|
||||||
|
*
|
||||||
|
* sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
|
||||||
|
* object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
|
||||||
|
* val values = findValues
|
||||||
|
* case object TurnRight extends PlayerAnalogMovementInput
|
||||||
|
* case object TurnLeft extends PlayerAnalogMovementInput
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* inputManager.withEnumMappings(PlayerAnalogMovementInput) {
|
||||||
|
* case PlayerAnalogMovementInput.TurnRight =>
|
||||||
|
* Seq(new KeyTrigger(KeyInput.KEY_RIGHT))
|
||||||
|
* case PlayerAnalogMovementInput.TurnLeft =>
|
||||||
|
* Seq(new KeyTrigger(KeyInput.KEY_LEFT))
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }}}
|
||||||
|
*/
|
||||||
|
def withEnumMappings[T <: EnumEntry](
|
||||||
|
mappingEnum: Enum[T]
|
||||||
|
)(mappingFn: T => Seq[Trigger]) =
|
||||||
|
UIO(delegate.withEnumMappings(mappingEnum)(mappingFn))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an observable which emits the given mappings as elements of an observable
|
||||||
|
*
|
||||||
|
* @param mappingNames
|
||||||
|
* @return Observable of action events
|
||||||
|
*
|
||||||
|
* @see [[ActionEvent]]
|
||||||
|
* @see [[enumObservableAction]]
|
||||||
|
*/
|
||||||
|
def observableAction(mappingNames: String*): Observable[ActionEvent] =
|
||||||
|
delegate.observableAction(mappingNames: _*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Create an observable which emits the values of the given
|
||||||
|
* enum as elements of an observable
|
||||||
|
*
|
||||||
|
* @param mappingNames
|
||||||
|
* @return Observable of enum values
|
||||||
|
*
|
||||||
|
* @example {{{
|
||||||
|
* inputManager
|
||||||
|
* .enumObservableAction(PlayerMovementInput)
|
||||||
|
* .doOnNext { action =>
|
||||||
|
* action.binding match {
|
||||||
|
* case PlayerMovementInput.WalkLeft => Task {/* your actions */}
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }}}
|
||||||
|
*
|
||||||
|
* @see [[EnumActionEvent]]
|
||||||
|
* @see [[enumAnalogObservable]]
|
||||||
|
*/
|
||||||
|
def enumObservableAction[T <: EnumEntry](
|
||||||
|
mappingEnum: Enum[T]
|
||||||
|
): Observable[EnumActionEvent[T]] =
|
||||||
|
delegate.enumObservableAction(mappingEnum)
|
||||||
|
|
||||||
|
def enumEntryObservableAction[T <: EnumEntry](
|
||||||
|
mappingEnumEntry: T
|
||||||
|
) = delegate.enumEntryObservableAction(mappingEnumEntry)
|
||||||
|
|
||||||
|
def analogObservable(mappingNames: String*): Observable[AnalogEvent] =
|
||||||
|
delegate.analogObservable(mappingNames: _*)
|
||||||
|
|
||||||
|
def enumAnalogObservable[T <: EnumEntry](
|
||||||
|
mappingEnum: Enum[T]
|
||||||
|
): Observable[EnumAnalogEvent[T]] = delegate.enumAnalogObservable(mappingEnum)
|
||||||
|
|
||||||
|
def cursorVisible_=(value: Boolean) = UIO(delegate.setCursorVisible(value))
|
||||||
|
def cursorVisible = delegate.isCursorVisible
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user