123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- # author: Ethosa
- ## Node is the root type of all other nodes.
- import
- strutils,
- ../thirdparty/opengl,
- ../core/vector2,
- ../core/enums,
- ../core/anchor,
- ../core/input
- {.used.}
- type
- NodeHandler* = proc(self: NodeRef)
- NodeEvHandler* = proc(self: NodeRef, event: InputEvent)
- NodeObj* = object of RootObj
- kind*: NodeKind
- type_of_node*: NodeTypes ## default, gui, 2d or 3d.
- visibility*: Visibility ## visible, invisible or gone.
- is_ready*: bool ## true, when scene is ready.
- pausemode*: PauseMode ## Pause mode, by default is INHERIT.
- name*: string ## Node name.
- parent*: NodeRef ## Node parent.
- children*: seq[NodeRef] ## Node children.
- on_enter*: NodeHandler ## This called when scene changed.
- on_exit*: NodeHandler ## This called when exit from the scene.
- on_input*: NodeEvHandler ## This called on user input.
- on_ready*: NodeHandler ## This called when the scene changed and the `enter` was called.
- on_process*: NodeHandler ## This called every frame.
- NodeRef* = ref NodeObj
- var
- handler_default = proc(self: NodeRef) = discard
- event_handler_default = proc(self: NodeRef, event: InputEvent) = discard
- template nodepattern*(nodetype: untyped): untyped =
- ## This used in childs of the NodeObj.
- result = `nodetype`(
- name: name, children: @[],
- on_ready: handler_default,
- on_process: handler_default,
- on_input: event_handler_default,
- on_enter: handler_default,
- on_exit: handler_default,
- is_ready: false, pausemode: INHERIT, visibility: VISIBLE
- )
- result.type_of_node = NODE_TYPE_DEFAULT
- proc Node*(name: string = "Node"): NodeRef =
- ## Creates a new Node.
- nodepattern(NodeRef)
- result.kind = NODE_NODE
- method addChild*(self: NodeRef, child: NodeRef) {.base.} =
- ## Adds new child in current node.
- ##
- ## Arguments:
- ## - `child`: other node.
- ##
- ## See also:
- ## - `addChildren method <#addChildren.e,NodeRef,varargs[NodeRef]>`_
- ## - `getChild method <#getChild.e,NodeRef,int>`_
- self.children.add(child)
- child.parent = self
- method addChildren*(self: NodeRef, childs: varargs[NodeRef]) {.base.} =
- ## Adds new child in current node.
- ##
- ## Arguments:
- ## - `child`: other node.
- ##
- ## See also:
- ## - `addChild method <#addChild.e,NodeRef,NodeRef>`_
- for node in childs:
- self.addChild(node)
- method draw*(self: NodeRef, w, h: GLfloat) {.base.} =
- ## Draws node.
- ## This used in the Window object.
- discard
- method duplicate*(self: NodeRef): NodeRef {.base.} =
- ## Duplicates Node object and create a new Node.
- self.deepCopy()
- method getChild*(self: NodeRef, index: int): NodeRef {.base.} =
- ## Returns child at `index` position, if available.
- ##
- ## Arguments:
- ## - `index`: child index.
- ##
- ## See also:
- ## - `addChild method <#addChild.e,NodeRef,NodeRef>`_
- ## - `getChildCount method <#getChildCount.e,NodeRef>`_
- self.children[index]
- method getChildCount*(self: NodeRef): int {.base, inline.} =
- ## Returns child count.
- ##
- ## See also:
- ## - `getChild method <#getChild.e,NodeRef,int>`_
- self.children.len()
- method getChildIndex*(self: NodeRef, name: string): int {.base.} =
- ## Returns `child` index or -1, if another node is not the child.
- ##
- ## See also:
- ## - `getChildIndex method <#getChildIndex.e,NodeRef,NodeRef>`_
- var i = 0
- for node in self.children:
- if node.name == name:
- return i
- inc i
- -1
- method getChildIndex*(self: NodeRef, child: NodeRef): int {.base.} =
- ## Returns `child` index or -1, if another node is not the child.
- ##
- ## See also:
- ## - `getChildIndex method <#getChildIndex.e,NodeRef,string>`_
- var i = 0
- for node in self.children:
- if child == node:
- return i
- inc i
- -1
- method getChildIter*(self: NodeRef): seq[NodeRef] {.base.} =
- ## Returns all children iter.
- result = @[]
- for child in self.children:
- if child notin result:
- result.add(child)
- if child.children.len() > 0:
- for node in child.getChildIter():
- if node notin result:
- result.add(node)
- method getNode*(self: NodeRef, path: string): NodeRef {.base.} =
- ## Returns child by `path`
- var
- p = path.split("/")
- current: NodeRef = self
- for name in p:
- if current == nil:
- break
- case name
- of "..":
- current = current.parent
- of "":
- continue
- else:
- for child in current.children:
- if child.name == name:
- current = child
- break
- return current
- method getPath*(self: NodeRef): string {.base.} =
- ## Returns node path.
- var current = self
- result = current.name
- while current.parent != nil:
- current = current.parent
- result = current.name & "/" & result
- method getParent*(self: NodeRef): NodeRef {.base.} =
- ## Returns node parent.
- self.parent
- method getPauseMode*(self: NodeRef): PauseMode {.base.} =
- ## Calculates pause mode
- result = self.pausemode
- var current = self
- while result == INHERIT and current.parent != nil:
- current = current.parent
- result = current.pausemode
- method getRootNode*(self: NodeRef): NodeRef {.base.} =
- ## Gets root node.
- result = self
- while result.parent != nil:
- result = result.parent
- method isParentOf*(self, other: NodeRef): bool {.base, inline.} =
- other in self.children
- method handle*(self: NodeRef, event: InputEvent, mouse_on: var NodeRef) {.base.} =
- ## Handles user input.
- ## This used in the Window object.
- discard
- method hasNode*(self: NodeRef, name: string): bool {.base, inline.} =
- ## Returns true, if a node with name `name` in children.
- ##
- ## Arguments:
- ## - `name`: node name.
- self.getChildIndex(name) != -1
- method hasNode*(self: NodeRef, other: NodeRef): bool {.base, inline.} =
- ## Returns true, if `other` in self children.
- ##
- ## Arguments:
- ## - `other`: other node.
- self.getChildIndex(other) != -1
- method hasParent*(self: NodeRef): bool {.base, inline.} =
- ## Returns true, when node has parent.
- not self.parent.isNil()
- method hide*(self: NodeRef) {.base.} =
- self.visibility = INVISIBLE
- method postdraw*(self: NodeRef, w, h: GLfloat) {.base.} =
- ## Draws node.
- ## This used in the Window object.
- discard
- method rename*(self: NodeRef, new_name: string) {.base.} =
- self.name = new_name
- method removeChild*(self: NodeRef, index: int) {.base.} =
- ## Removes node child at a specific position.
- ##
- ## Arguments:
- ## - `index`: child index.
- ##
- ## See also:
- ## - `addChild method <#addChild.e,NodeRef,NodeRef>`_
- self.children[index].parent = nil
- self.children.delete(index)
- method removeChild*(self: NodeRef, other: NodeRef) {.base.} =
- ## Removes another node from `self`, if `other` in `self` children.
- ##
- ## Arguments:
- ## - `other`: other node.
- let index: int = self.getChildIndex(other)
- if index != -1:
- self.removeChild(index)
- method show*(self: NodeRef) {.base.} =
- self.visibility = VISIBLE
- method delete*(self: NodeRef) {.base.} =
- ## Deletes current node.
- if self.parent != nil:
- self.parent.removeChild(self)
- # --- Macros --- #
- import
- macros
- macro expectParams(params_src: NimNode, params: static[seq[string]]): untyped =
- ## Gets values from the NimNode params_src which comes after the `@` when using the `@` macro.
- ## Also checks that the number of parameters lines up
- ## Arguments:
- ## - `params_src` The node that contains the parameters
- ## - `params` List of param variables to get from params_src
- result = newStmtList()
- result.add quote do:
- # This is done so params is only injected once
- let params = `params`
- let src_length = `params_src`.len - 1 # - 1 for name
- if src_length != params.len:
- # Tell the user that there was an unexpected number of params
- let error_msg = block:
- "Expected " & $params.len & " parameters (" & params.join(", ") & ") but got " & $src_length
- error_msg.error(`params_src`)
- # Convert all the passed parameters into variables with corresponding
- # value from the parameters source
- for i, param in params:
- let varName = ident param
- result.add quote do:
- var `varName` = `params_src`[`i` + 1] # + 1 since name is at the 0 index
- when not declared(nimIdentNormalize):
- proc nimIdentNormalize(s: string): string =
- ## Backported from https://github.com/nim-lang/Nim/blob/version-1-4/lib/pure/strutils.nim#L284
- result = newString(s.len)
- if s.len > 0:
- result[0] = s[0]
- var j = 1
- for i in 1..len(s) - 1:
- if s[i] in {'A'..'Z'}:
- result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
- inc j
- elif s[i] != '_':
- result[j] = s[i]
- inc j
- if j != s.len: setLen(result, j)
- macro `@`*(node: NodeRef, event_name, code: untyped): untyped =
- ## It provides a convenient wrapper for the event handler.
- ##
- ## Arguments:
- ## - `node` is an any node pointer.
- ## - `event_name` is an event name, e.g.: process.
- ## - `code` is the proc code.
- ##
- ## ## Examples
- ## .. code-block:: nim
- ##
- ## var
- ## smth_node = Node("Simple node")
- ##
- ## smth_node@on_ready(self):
- ## echo "node is ready!"
- ##
- ## smth_node@on_input(self, event):
- ## if event.isInputEventMouseButton():
- ## echo event
- var ename: string
- # Gets event name.
- # TODO, event_name[0] is used in all the case block so add a better error message for when it isn't called this way
- if event_name.kind == nnkIdent:
- ename = $event_name
- elif event_name.kind == nnkCall:
- ename = $event_name[0]
- # Do style insensitive comparision
- case nimIdentNormalize(ename)
- of "onprocess", "onready", "onenter", "onexit":
- var name = event_name[0]
- event_name.expectParams(@["self"])
- result = quote do:
- `node`.`name` =
- proc(`self`: NodeRef): void =
- `code`
- of "onfocus", "onunfocus":
- var name = event_name[0]
- event_name.expectParams(@["self"])
- result = quote do:
- `node`.`name` =
- proc(`self`: ControlRef): void =
- `code`
- of "ontouch":
- var name = event_name[0]
- event_name.expectParams(@["self", "x", "y"])
- result = quote do:
- `node`.`name` =
- proc(`self`: ButtonRef, `x`, `y`: float) =
- `code`
- of "onmouseexit", "onmouseenter", "onclick", "onrelease", "onpress":
- var name = event_name[0]
- event_name.expectParams(@["self", "x", "y"])
- result = quote do:
- `node`.`name` =
- proc(`self`: ControlRef, `x`, `y`: float) =
- `code`
- of "oninput":
- var name = event_name[0]
- event_name.expectParams(@["self", "arg"])
- result = quote do:
- `node`.`name` =
- proc(`self`: NodeRef, `arg`: InputEvent) =
- `code`
- of "onswitch":
- var name = event_name[0]
- event_name.expectParams(@["self", "arg"])
- result = quote do:
- `node`.`name` =
- proc(`self`: SwitchRef, `arg`: bool) =
- `code`
- of "ontoggle":
- var name = event_name[0]
- event_name.expectParams(@["self", "arg"])
- result = quote do:
- `node`.`name` =
- proc(`self`: CheckBoxRef, `arg`: bool) =
- `code`
- of "onchanged":
- var name = event_name[0]
- event_name.expectParams(@["self", "arg"])
- result = quote do:
- `node`.`name` =
- proc(`self`: SliderRef, `arg`: uint) =
- `code`
- of "onedit":
- var name = event_name[0]
- event_name.expectParams(@["self", "arg"])
- result = quote do:
- `node`.`name` =
- proc(`self`: EditTextRef, `arg`: string) =
- `code`
- else:
- error("Invalid event: " & ename, node)
|