diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1deb543..04e62390 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,7 +57,7 @@ jobs: - name: Build examples run: | cd examples - for dir in hello_world calculator novel snake screensaver roguelike; do + for dir in hello_world calculator novel snake screensaver roguelike builder_features; do ( cd "$dir" nim c main.nim diff --git a/LICENSE b/LICENSE index 05c06944..1c85d715 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Ethosa +Copyright (c) 2021 Ethosa Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2450013e..3be9abf4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ The Nim GUI/2D framework based on OpenGL and SDL2. [![channel icon](https://patrolavia.github.io/telegram-badge/follow.png)](https://t.me/nim1love) [![channel icon](https://patrolavia.github.io/telegram-badge/chat.png)](https://t.me/nodesnim) -

Stable version - 0.3.2

+

Stable version - 0.4.0

## Install @@ -67,7 +67,7 @@ This section contains links to documentation for all nodes. |[Anchor][] |[Node][] |[Control][] |[Node2D][] |[Node3D][] |[Drawable][] | |[Color][] |[Canvas][] |[ColorRect][] |[Sprite][] |[GeometryInstance][]|[GradientDrawable][]| |[Font][] |[Scene][] |[TextureRect][] |[AnimatedSprite][] |[Camera3D][] | | -|[Enums][] |[AudioStreamPlayer][]|[Label][] |[YSort][] | | | +|[Enums][] |[AudioStreamPlayer][]|[Label][] |[YSort][] |[Sprite3D][] | | |[Exceptions][] |[AnimationPlayer][] |[Button][] |[CollisionShape2D][]| | | |[Image][] | |[EditText][] |[Camera2D][] | | | |[Input][] | |[Box][] |[TileMap][] | | | @@ -84,6 +84,7 @@ This section contains links to documentation for all nodes. | | |[Switch][] | | | | | | |[SubWindow][] | | | | | | |[CheckBox][] | | | | +| | |[ToolTip][] | | | | @@ -154,6 +155,7 @@ Also use [`niminst`](https://github.com/nim-lang/niminst) tool for generate an i [Font]:https://ethosa.github.io/nodesnim/nodesnim/core/font.html [StyleSheet]:https://ethosa.github.io/nodesnim/nodesnim/core/stylesheet.html [TileSet]:https://ethosa.github.io/nodesnim/nodesnim/core/tileset.html +[Scripts]:https://ethosa.github.io/nodesnim/nodesnim/core/scripts.html [Node]:https://ethosa.github.io/nodesnim/nodesnim/nodes/node.html [Canvas]:https://ethosa.github.io/nodesnim/nodesnim/nodes/canvas.html @@ -183,6 +185,7 @@ Also use [`niminst`](https://github.com/nim-lang/niminst) tool for generate an i [Switch]:https://ethosa.github.io/nodesnim/nodesnim/nodescontrol/switch.html [SubWindow]:https://ethosa.github.io/nodesnim/nodesnim/nodescontrol/subwindow.html [CheckBox]:https://ethosa.github.io/nodesnim/nodesnim/nodescontrol/checkbox.html +[ToolTip]:https://ethosa.github.io/nodesnim/nodesnim/nodescontrol/tooltip.html [Node2D]:https://ethosa.github.io/nodesnim/nodesnim/nodes2d/node2d.html [Sprite]:https://ethosa.github.io/nodesnim/nodesnim/nodes2d/sprite.html @@ -197,6 +200,7 @@ Also use [`niminst`](https://github.com/nim-lang/niminst) tool for generate an i [Node3D]:https://ethosa.github.io/nodesnim/nodesnim/nodes3d/node3d.html [GeometryInstance]:https://ethosa.github.io/nodesnim/nodesnim/nodes3d/geometry_instance.html [Camera3D]:https://ethosa.github.io/nodesnim/nodesnim/nodes3d/camera3d.html +[Sprite3D]:https://ethosa.github.io/nodesnim/nodesnim/nodes3d/sprite3d.html [Drawable]:https://ethosa.github.io/nodesnim/nodesnim/graphics/drawable.html [GradientDrawable]:https://ethosa.github.io/nodesnim/nodesnim/graphics/gradient_drawable.html diff --git a/examples/builder_features/main.nim b/examples/builder_features/main.nim new file mode 100644 index 00000000..3dbbe72a --- /dev/null +++ b/examples/builder_features/main.nim @@ -0,0 +1,41 @@ +# author: Ethosa +import nodesnim + + +Window("SceneBuilder") + + +build: + # Create node. + # var main = Scene(name = "main") + - Scene main: + # Create node with params. + # var rect = ColorRect(name = "rect") + # rect.color = Color(0.6, 0.5, 1) + - ColorRect rect(color: Color(0.6, 0.5, 1)): + # handle Mouse press event. + # rect.on_press = proc(self: NodeRef, x, y: float) = + @onPress(x, y): + rect.color.r -= 0.01 + # handle Mouse release event. + # rect.on_release = proc(self: NodeRef, x, y: float) = + @onRelease(x, y): + rect.color.r = 0.6 + + # Create a new Label with params. + # var hw = Label(name = "hw") + # hw.anchor = Anchor(0.5, 0.5, 0.5, 0.5) + - Label hw(anchor: Anchor(0.5, 0.5, 0.5, 0.5)): + # Call Label method: + # hw.setText("Hello, world!") + call setText("Hello, world!") + + # Repeating nodes can be written briefly: + - Node node0(is_ready: true, call hide()) + - Node2D node1(is_ready: true, call hide()) + - Node3D node2(is_ready: true, call hide()) + - Control node3(is_ready: true, call hide()) + + +addMainScene(main) +windowLaunch() diff --git a/examples/builder_features/nim.cfg b/examples/builder_features/nim.cfg new file mode 100644 index 00000000..cf9b2a26 --- /dev/null +++ b/examples/builder_features/nim.cfg @@ -0,0 +1 @@ +--path:"../../src" \ No newline at end of file diff --git a/examples/builder_features/readme.md b/examples/builder_features/readme.md new file mode 100644 index 00000000..8b7be07f --- /dev/null +++ b/examples/builder_features/readme.md @@ -0,0 +1 @@ +# SceneBuilder features \ No newline at end of file diff --git a/examples/calculator/material_ui.nim b/examples/calculator/material_ui.nim new file mode 100644 index 00000000..4adcb376 --- /dev/null +++ b/examples/calculator/material_ui.nim @@ -0,0 +1,204 @@ +# --- Material UI calculator --- # +import + strutils, + nodesnim + + +type + TokenType {.size: sizeof(int8).} = enum + NUMBER, + OPERATOR, + RPAR, + LPAR + Token = ref object + token_type: TokenType + token_value: string + TokenTree = seq[Token] + +proc parseQuery(query: string): TokenTree = + result = @[] + for c in query: + if c.isDigit() or c == '.': + if result.len > 0 and not result[^1].isNil() and result[^1].token_type == NUMBER: + result[^1].token_value &= $c + else: + result.add(Token(token_type: NUMBER, token_value: $c)) + elif c in "+-/*": + result.add(Token(token_type: OPERATOR, token_value: $c)) + when false: + for i in result: + echo i.token_value + +proc findHigh(tree: TokenTree): int = + result = -1 + let tokens = "/*-+" + for token in tokens: + for i in tree.low..tree.high: + if tree[i].token_type == OPERATOR and tree[i].token_value == $token: + return i-1 + + +proc calculate(tree: TokenTree): float = + result = 0f + + var + t = tree + index = t.findHigh() + + while index != -1: + case t[index+1].token_value + of "/": + t[index].token_value = $(t[index].token_value.parseFloat() / t[index+2].token_value.parseFloat()) + of "*": + t[index].token_value = $(t[index].token_value.parseFloat() * t[index+2].token_value.parseFloat()) + of "-": + t[index].token_value = $(t[index].token_value.parseFloat() - t[index+2].token_value.parseFloat()) + of "+": + t[index].token_value = $(t[index].token_value.parseFloat() + t[index+2].token_value.parseFloat()) + t.del(index+2) + t.del(index+1) + index = t.findHigh() + if t.len == 1: + result = parseFloat(t[0].token_value) + +Window("material ui calculator", ((64+32)*4)+16, 480) +env.setBackgroundColor(Color("#FAFAFA")) + + +var + query: string = "" + big_font = loadFont(standard_font_path, 32) + medium = loadFont(standard_font_path, 24) + small = loadFont(standard_font_path, 16) + +build: + - Button number_button: + call setStyle(style({color: "#EEEEEE"})) + call resize(64+32, 64) + call setTextFont(loadFont(standard_font_path, 24)) + - Button operator_button: + call setStyle(style({color: "#F5F5F5"})) + call resize(64+32, 51.2f) + call setTextFont(loadFont(standard_font_path, 22)) + +number_button.normal_background.setStyle(style({background-color: "#424242"})) +number_button.hover_background.setStyle(style({background-color: "#616161"})) +number_button.press_background.setStyle(style({background-color: "#757575"})) + +operator_button.normal_background.setStyle(style({background-color: "#616161"})) +operator_button.hover_background.setStyle(style({background-color: "#757575"})) +operator_button.press_background.setStyle(style({background-color: "#9E9E9E"})) + + +when false: + query = "123+1*5-200/10" + var + parsed = parseQuery(query) + calculated = calculate(parsed) + + +build: + - Scene main: + - HBox hbox: + separator: 0 + call setPadding(8, 8, 8, 8) + call move(0, 200) + - GridBox numbers: + separator: 0 + call setRow(3) + - Vbox operators: + separator: 0 + - Control result_back: + call resize(((64+32)*4), 200) + call move(8, 8) + call setStyle(style({ + background-color: "#4DD0E1", + shadow: true, + shadow-offset: "0 8" + })) + - Label text: + call setTextFont(loadFont(standard_font_path, 32)) + call setTextColor(Color("#fff")) + call setTextAlign(1, 1, 1, 1) + call setAnchor(1, 1, 1, 1) + call setSizeAnchor(1, 0.5) + call setPadding(16, 16, 16, 16) + + +for i in 0..11: + numbers.addChild(number_button.duplicate()) + if i < 9: + numbers.getChild(i).ButtonRef.setText($(i+1)) + numbers.getChild(i).ButtonRef@onClick(self, x, y): + query &= self.ButtonRef.getText() + text.setText(query) + elif i == 9: + numbers.getChild(i).ButtonRef.setText(".") + numbers.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0 and query[^1] != '.': + query &= self.ButtonRef.getText() + text.setText(query) + elif i == 10: + numbers.getChild(i).ButtonRef.setText("0") + numbers.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + query &= self.ButtonRef.getText() + text.setText(query) + elif i == 11: + numbers.getChild(i).ButtonRef.setText("=") + numbers.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0 and query[^1] in "/*-+": + query = "0" + var calculated = parseQuery(query).calculate() + query = $calculated + text.setText(query) + +for i in 0..4: + operators.addChild(operator_button.duplicate()) + case i + of 0: + operators.getChild(i).ButtonRef.setText("DEL") + operators.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + query = query[0..^2] + text.setText(query) + of 1: + operators.getChild(i).ButtonRef.setText("+") + operators.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + if query[^1] notin "-+/*": + query &= "+" + else: + query = query[0..^2] & "+" + text.setText(query) + of 2: + operators.getChild(i).ButtonRef.setText("−") + operators.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + if query[^1] notin "-+/*": + query &= "-" + else: + query = query[0..^2] & "-" + text.setText(query) + of 3: + operators.getChild(i).ButtonRef.setText("÷") + operators.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + if query[^1] notin "-+/*": + query &= "/" + else: + query = query[0..^2] & "/" + text.setText(query) + else: + operators.getChild(i).ButtonRef.setText("×") + operators.getChild(i).ButtonRef@onClick(self, x, y): + if query.len > 0: + if query[^1] notin "-+/*": + query &= "*" + else: + query = query[0..^2] & "*" + text.setText(query) + + +addMainScene(main) +windowLaunch() diff --git a/examples/novel/main.nim b/examples/novel/main.nim index d9233964..573a60b3 100644 --- a/examples/novel/main.nim +++ b/examples/novel/main.nim @@ -34,7 +34,7 @@ build: call setTexture(akiko_default) call setTextureAnchor(0.5, 0.5, 0.5, 0.5) texture_mode: TEXTURE_KEEP_ASPECT_RATIO - visible: GONE + visibility: GONE - Label dialog_text: call setSizeAnchor(0.8, 0.3) call setAnchor(0.1, 0.6, 0, 0) @@ -65,7 +65,7 @@ foreground_rect@on_input(self, event): if stage < dialog.len(): name_charapter.setText(dialog[stage][0]) dialog_text.setText(dialog[stage][1]) - charapter.visible = dialog[stage][2] + charapter.visibility = dialog[stage][2] inc stage diff --git a/examples/readme.md b/examples/readme.md index deeeb5ce..915f8e0f 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -1,22 +1,24 @@ # Examples -## [Hello world](https://github.com/Ethosa/nodesnim/blob/master/examples/hello_world) +## [Hello world](https://github.com/Ethosa/nodesnim/blob/nightly/examples/hello_world) ![Hello world](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/1.png) -## [Calculator](https://github.com/Ethosa/nodesnim/blob/master/examples/calculator) +## [Calculator](https://github.com/Ethosa/nodesnim/blob/nightly/examples/calculator) ![Calculator](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/2.png) -## [Snake game](https://github.com/Ethosa/nodesnim/blob/master/examples/snake) +## [Snake game](https://github.com/Ethosa/nodesnim/blob/nightly/examples/snake) ![Snake game](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/3.png) -## [Screen saver](https://github.com/Ethosa/nodesnim/blob/master/examples/screensaver) +## [Screen saver](https://github.com/Ethosa/nodesnim/blob/nightly/examples/screensaver) ![Screen saver](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/4.png) -## [Novel](https://github.com/Ethosa/nodesnim/blob/master/examples/novel) +## [Novel](https://github.com/Ethosa/nodesnim/blob/nightly/examples/novel) ![Novel](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/5.png) -## [Roguelike](https://github.com/Ethosa/nodesnim/blob/master/examples/roguelike) +## [Roguelike](https://github.com/Ethosa/nodesnim/blob/nightly/examples/roguelike) ![Roguelike](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/6.png) -## [Sample messenger](https://github.com/Ethosa/nodesnim/blob/master/examples/sample_messenger) +## [Sample messenger](https://github.com/Ethosa/nodesnim/blob/nightly/examples/sample_messenger) ![Sample messenger](https://github.com/Ethosa/nodesnim/blob/nightly/screenshots/7.png) + +## [Builder features](https://github.com/Ethosa/nodesnim/blob/nightly/examples/builder_features) diff --git a/examples/roguelike/main.nim b/examples/roguelike/main.nim index 107815b2..01cf1227 100644 --- a/examples/roguelike/main.nim +++ b/examples/roguelike/main.nim @@ -18,7 +18,7 @@ build: - Scene main: - TileMap map: call setTileSet(tileset) - call resizeMap(newVector2(LEVEL_WIDTH, 15), 2) + call resizeMap(Vector2(LEVEL_WIDTH, 15), 2) # Player - KinematicBody2D player: @@ -51,18 +51,18 @@ build: # Draw grass for i in 0..512: - map.drawTile(rand(99), rand(14), newVector2(0, 1), 1) + map.drawTile(rand(99), rand(14), Vector2(0, 1), 1) # Draw trees for i in 0..128: - map.drawTile(rand(99), rand(14), newVector2(rand(13..16).float, 0), 1) + map.drawTile(rand(99), rand(14), Vector2(rand(13..16).float, 0), 1) # Draw houses for i in 0..32: var pos = Vector2(rand(99).float, rand(14).float) collider = CollisionShape2D() - map.drawTile(pos.x.int, pos.y.int, newVector2(rand(13..16).float, 2), 1) + map.drawTile(pos.x.int, pos.y.int, Vector2(rand(13..16).float, 2), 1) collider.resize(PLAYER_SIZE, PLAYER_SIZE) collider.move(pos.x*PLAYER_SIZE, pos.y*PLAYER_SIZE) main.addChild(collider) @@ -74,7 +74,7 @@ addKeyAction("backward", "s") addKeyAction("right", "d") addKeyAction("left", "a") -player@onInput(self, event): +player@onProcess(self): if isActionPressed("right"): player.moveAndCollide(Vector2(PLAYER_SPEED, 0)) elif isActionPressed("left"): diff --git a/nodesnim.nimble b/nodesnim.nimble index 30b70d80..f65975a8 100644 --- a/nodesnim.nimble +++ b/nodesnim.nimble @@ -1,10 +1,11 @@ [Package] name = "nodesnim" author = "Ethosa" -version = "0.3.2" +version = "0.4.0" description = "The Nim GUI/2D framework based on OpenGL and SDL2." license = "MIT" srcDir = "src" [Deps] Requires: "nim >= 1.0.0" +Requires: "compiler >= 1.0.0" diff --git a/src/nodesnim.nim b/src/nodesnim.nim index bf8cae41..21a9090b 100644 --- a/src/nodesnim.nim +++ b/src/nodesnim.nim @@ -10,9 +10,9 @@ when defined(debug): info("Compiled in debug mode.") +import nodesnim/thirdparty/sdl2 except Color import nodesnim/thirdparty/opengl, - nodesnim/thirdparty/sdl2, nodesnim/window, nodesnim/environment, diff --git a/src/nodesnim/core/audio_stream.nim b/src/nodesnim/core/audio_stream.nim index 505cd8b6..460bf60a 100644 --- a/src/nodesnim/core/audio_stream.nim +++ b/src/nodesnim/core/audio_stream.nim @@ -7,7 +7,6 @@ import discard mixer.init(MIX_INIT_OGG) discard mixer.openAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) - type AudioStreamRef* = ref object of RootObj chunk*: ptr Chunk diff --git a/src/nodesnim/core/circle.nim b/src/nodesnim/core/circle.nim index ae6fa2ff..bfcae233 100644 --- a/src/nodesnim/core/circle.nim +++ b/src/nodesnim/core/circle.nim @@ -77,7 +77,7 @@ proc contains*(self: CircleObj, a, b: Vector2Obj): bool = let tmin = if -b - discr > 0: -b - discr else: 0 if tmin > d: return false - return true + true # --- Operators --- # diff --git a/src/nodesnim/core/color.nim b/src/nodesnim/core/color.nim index 54f8348e..b68b7780 100644 --- a/src/nodesnim/core/color.nim +++ b/src/nodesnim/core/color.nim @@ -49,11 +49,12 @@ proc Color*(src: uint32): ColorRef = echo clr3 echo clr4 + let clr = src.int ColorRef( - r: ((src shr 24) and 255).int / 255, - g: ((src shr 16) and 255).int / 255, - b: ((src shr 8) and 255).int / 255, - a: (src and 255).int / 255 + r: ((clr shr 24) and 255) / 255, + g: ((clr shr 16) and 255) / 255, + b: ((clr shr 8) and 255) / 255, + a: (clr and 255) / 255 ) proc Color*(src: string): ColorRef = @@ -73,16 +74,23 @@ proc Color*(src: string): ColorRef = target = src matched: array[20, string] - # #FFFFFFFF, #FFF, #FFFFFF, etc + # #FFFFFFFF, #FFF, #FFFFFF, #FFFF, etc if target.startsWith('#') or target.startsWith("0x") or target.startsWith("0X"): target = target[1..^1] - if target[0] == 'x' or target[0] == 'X': + if target[0] in {'x', 'X'}: target = target[1..^1] - if target.len() == 3: # #fff -> #ffffffff + let length = target.len + case length + of 3: target = target[0] & target[0] & target[1] & target[1] & target[2] & target[2] & "ff" - elif target.len() == 6: # #ffffff -> #ffffffff + of 4: # #1234 -> #11223344 + target = target[0] & target[0] & target[1] & target[1] & target[2] & target[2] & target[3] & target[3] + of 6: # #ffffff -> #ffffffff target &= "ff" + else: + discard + return Color(parseHexInt(target).uint32) # rgba(255, 255, 255, 1.0) @@ -97,6 +105,9 @@ proc Color*(): ColorRef {.inline.} = ## Creates a new Color object with RGBA value (0, 0, 0, 0) ColorRef(r: 0, g: 0, b: 0, a: 0) +proc Color*(clr: ColorRef): ColorRef {.inline.} = + ColorRef(r: clr.r, g: clr.g, b: clr.b, a: clr.a) + proc getBrightness*(self: ColorRef): float {.inline.} = (self.r + self.g + self.b) / 3f diff --git a/src/nodesnim/core/enums.nim b/src/nodesnim/core/enums.nim index 359a3085..31efb7b3 100644 --- a/src/nodesnim/core/enums.nim +++ b/src/nodesnim/core/enums.nim @@ -1,18 +1,21 @@ # author: Ethosa +{.push pure, size: sizeof(int8).} + type - MouseMode* {.size: sizeof(int8).} = enum + MouseMode* = enum MOUSEMODE_IGNORE = 0x00000001 ## Igore mouse input. This used in Control nodes MOUSEMODE_SEE = 0x00000002 ## Handle mouse input. - PauseMode* {.size: sizeof(int8).} = enum + PauseMode* = enum PROCESS, ## Continue to work when the window paused. PAUSE, ## Pause work when the window paused. INHERIT ## Take parent value. - TextureMode* {.size: sizeof(int8).} = enum + TextureMode* = enum TEXTURE_FILL_XY, ## Fill texture without keeping the aspect ratio. TEXTURE_KEEP_ASPECT_RATIO, ## Fill texture with keeping the aspect ratio. TEXTURE_CROP ## Crop and fill texture. - NodeKind* {.pure, size: sizeof(int8).} = enum + + NodeKind* = enum NODE_NODE, CANVAS_NODE, SCENE_NODE, @@ -50,13 +53,50 @@ type # 3D nodes NODE3D_NODE, GEOMETRY_INSTANCE_NODE, - CAMERA_3D_NODE - NodeTypes* {.pure, size: sizeof(int8).} = enum + CAMERA_3D_NODE, + SPRITE_3D_NODE + + NodeTypes* = enum NODE_TYPE_DEFAULT, NODE_TYPE_CONTROL, NODE_TYPE_2D, NODE_TYPE_3D - Visibility* {.pure, size: sizeof(int8).} = enum + + Visibility* = enum VISIBLE, INVISIBLE, GONE + + ProgressBarType* = enum + PROGRESS_BAR_HORIZONTAL, + PROGRESS_BAR_VERTICAL, + PROGRESS_BAR_CIRCLE + + SliderType* = enum + SLIDER_HORIZONTAL, + SLIDER_VERTICAL + + GeometryType* = enum + GEOMETRY_CUBE, ## Uses for cube rendering. + GEOMETRY_CYLINDER, ## Uses for cylinder rendering. + GEOMETRY_SPHERE ## Uses for sphere rendering. + + TileMapMode* = enum + TILEMAP_2D, ## Default 2D mode. + TILEMAP_ISOMETRIC ## Isometric mode. + + CollisionShape2DType* = enum + COLLISION_SHAPE_2D_RECTANGLE, ## Uses for handle rect collision. + COLLISION_SHAPE_2D_CIRCLE, ## Uses for handle circle collision. + COLLISION_SHAPE_2D_POLYGON ## Uses for handle polygon collision. + + AnimationMode* = enum + ANIMATION_NORMAL, ## specific speed per second. + ANIMATION_EASE, ## ease mode. + ANIMATION_BEZIER ## specific bezier curve for speed. + + ScreenMode* = enum + SCREEN_MODE_NONE, ## default mode. + SCREEN_MODE_EXPANDED ## Keep screen size. + +{.pop.} diff --git a/src/nodesnim/core/exceptions.nim b/src/nodesnim/core/exceptions.nim index 5ca6af3c..2ac0ebd9 100644 --- a/src/nodesnim/core/exceptions.nim +++ b/src/nodesnim/core/exceptions.nim @@ -1,6 +1,16 @@ # author: Ethosa +import logging +{.push pure, size: sizeof(int8).} type - ResourceError* {.size: sizeof(int8).} = object of ValueError - SceneError* {.size: sizeof(int8).} = object of ValueError - WindowError* {.size: sizeof(int8).} = object of ValueError + ResourceError* = object of ValueError + SceneError* = object of ValueError + VMError* = object of ValueError + WindowError* = object of ValueError +{.pop.} + + +template throwError*(err: typedesc, msg: string) = + when defined(debug): + error(msg) + raise newException(err, msg) diff --git a/src/nodesnim/core/font.nim b/src/nodesnim/core/font.nim index 131cff39..c5963451 100644 --- a/src/nodesnim/core/font.nim +++ b/src/nodesnim/core/font.nim @@ -1,7 +1,7 @@ # author: Ethosa ## Provides TTF text rendering. Use SDL2_ttf. +import ../thirdparty/sdl2 except Color import - ../thirdparty/sdl2, ../thirdparty/sdl2/ttf, ../thirdparty/opengl, @@ -16,8 +16,9 @@ import type StyleUnicode* = ref object - underline*: bool - c*: string + is_url*: bool + style*: cint + c*, url*: string color*: ColorRef StyleText* = ref object font*: FontPtr @@ -27,14 +28,18 @@ type texture*: GlTextureObj chars*: seq[StyleUnicode] +let URL_COLOR = Color(0.45, 0.45, 0.9) -proc schar*(c: string, color: ColorRef = Color(1f, 1f, 1f), underline: bool = false): StyleUnicode = - StyleUnicode(c: c, color: color, underline: underline) -proc stext*(text: string, color: ColorRef = Color(1f, 1f, 1f), underline: bool = false): StyleText = +proc schar*(c: string, color: ColorRef = Color(1f, 1f, 1f), + style: cint = TTF_STYLE_NORMAL, is_url: bool = false): StyleUnicode = + StyleUnicode(c: c, color: color, style: style, is_url: is_url, url: "") + +proc stext*(text: string, color: ColorRef = Color(1f, 1f, 1f), + style: cint = TTF_STYLE_NORMAL): StyleText = result = StyleText(texture: GlTextureObj(size: Vector2()), spacing: 2, max_lines: -1) for i in text.utf8(): - result.chars.add(schar(i, color, underline)) + result.chars.add(schar(i, color, style)) result.font = standard_font result.rendered = false @@ -63,28 +68,19 @@ proc `&`*(text, t: StyleText): StyleText = proc `&`*(text: StyleText, t: string): StyleText = text & stext(t) -proc `&`*(text: string, c: StyleUnicode): string = +proc `&`*(text: string, c: StyleUnicode | StyleText): string = text & $c -proc `&`*(text: string, t: StyleText): string = - text & $t - -proc `&=`*(text: var StyleText, c: StyleUnicode) = - text = text & c - -proc `&=`*(text: var StyleText, t: StyleText) = - text = text & t +proc `&=`*(text: var StyleText, c: StyleUnicode | StyleText) = + text &= c -proc `&=`*(text: var string, c: StyleUnicode) = - text = text & $c - -proc `&=`*(text: var string, t: StyleText) = - text = text & $t +proc `&=`*(text: var string, c: StyleUnicode | StyleText) = + text &= $c proc `&=`*(text: var StyleText, t: string) = text &= stext(t) -proc `[]`*(text: StyleText, index: int): StyleUnicode = +proc `[]`*(text: StyleText, index: int | BackwardsIndex): StyleUnicode = text.chars[index] proc `[]`*[T, U](text: StyleText, slice: HSlice[T, U]): StyleText = @@ -94,6 +90,11 @@ proc `[]`*[T, U](text: StyleText, slice: HSlice[T, U]): StyleText = # ------ Funcs ------ # +proc applyStyle*(symbol: StyleUnicode, style: cint, enabled: bool = true) = + if (symbol.style and style) == 0 and enabled: + symbol.style = symbol.style or style + elif (symbol.style and style) != 0 and not enabled: + symbol.style = symbol.style xor style proc toUpper*(text: StyleText): StyleText = result = text.deepCopy() @@ -118,19 +119,29 @@ proc setColor*(text: StyleText, s, e: int, color: ColorRef) = for i in s..e: text.chars[i].color = color -proc setUnderline*(c: StyleUnicode, val: bool) = - c.underline = val - -proc setUnderline*(text: StyleText, val: bool) = - for i in text.chars: - i.underline = val - -proc setUnderline*(text: StyleText, index: int, val: bool) = - text.chars[index].underline = val - -proc setUnderline*(text: StyleText, s, e: int, val: bool) = +template styleFunc(setter, style_type: untyped): untyped = + proc `setter`*(c: StyleUnicode, val: bool = true) = + c.applyStyle(`style_type`, val) + proc `setter`*(text: StyleText, val: bool) = + for i in text.chars: + i.`setter`(val) + proc `setter`*(text: StyleText, index: int, val: bool) = + text.chars[index].`setter`(val) + proc `setter`*(text: StyleText, s, e: int, val: bool) = + for i in s..e: + text.chars[i].`setter`(val) + +styleFunc(setNormal, TTF_STYLE_NORMAL) +styleFunc(setBold, TTF_STYLE_BOLD) +styleFunc(setItalic, TTF_STYLE_ITALIC) +styleFunc(setUnderline, TTF_STYLE_UNDERLINE) +styleFunc(setStrikethrough, TTF_STYLE_STRIKETHROUGH) + +proc setURL*(text: StyleText, s, e: int, url: string) = for i in s..e: - text.chars[i].underline = val + text.chars[i].is_url = true + text.chars[i].url = url + text.chars[i].color = URL_COLOR proc setFont*(text: StyleText, font: cstring, size: cint) = text.font = openFont(font, size) @@ -245,6 +256,14 @@ proc getPosUnderPoint*(text: StyleText, global_pos, text_pos: Vector2Obj, if position.x == -1f: result = 0 +proc getCharUnderPoint*(text: StyleText, global_pos, text_pos: Vector2Obj, + text_align: AnchorObj = Anchor(0, 0, 0, 0)): tuple[c: StyleUnicode, pos: uint32] = + let pos = text.getPosUnderPoint(global_pos, text_pos, text_align) + if pos > 0: + (c: text.chars[pos-1], pos: pos-1) + else: + (c: text.chars[pos], pos: pos) + # ------ Render ------ # @@ -255,7 +274,7 @@ proc renderSurface*(text: StyleText, align: AnchorObj): SurfacePtr = ## - `align` -- text align. when defined(debug): if text.font.isNil(): - raise newException(ResourceError, "Font isn't loaded!") + throwError(ResourceError, "Font isn't loaded!") if not text.font.isNil() and $text != "": let @@ -263,7 +282,7 @@ proc renderSurface*(text: StyleText, align: AnchorObj): SurfacePtr = textsize = text.getTextSize() var surface = createRGBSurface( - 0, textsize.x.cint, textsize.y.cint, 32, + 0, textsize.x.cint + 8, textsize.y.cint, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000u32) y: cint = 0 w: cint @@ -273,6 +292,7 @@ proc renderSurface*(text: StyleText, align: AnchorObj): SurfacePtr = discard text.font.sizeUtf8(($line).cstring, addr w, addr h) var x = (textsize.x * align.x1 - w.float * align.x2).cint for c in line.chars: + text.font.setFontStyle(c.style) discard text.font.sizeUtf8(($c).cstring, addr w, addr h) var rendered = text.font.renderUtf8Blended( @@ -353,6 +373,7 @@ proc renderTo*(text: StyleText, pos, size: Vector2Obj, align: AnchorObj) = glBindTexture(GL_TEXTURE_2D, text.texture.texture) glEnable(GL_TEXTURE_2D) glBegin(GL_QUADS) + glTexCoord2f(texcord[0], texcord[3]) glVertex2f(pos1.x + size1.x, pos1.y) glTexCoord2f(texcord[0], texcord[1]) glVertex2f(pos1.x + size1.x, pos1.y - size1.y) @@ -360,7 +381,6 @@ proc renderTo*(text: StyleText, pos, size: Vector2Obj, align: AnchorObj) = glVertex2f(pos1.x, pos1.y - size1.y) glTexCoord2f(texcord[2], texcord[3]) glVertex2f(pos1.x, pos1.y) - glTexCoord2f(texcord[0], texcord[3]) glEnd() glDisable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, 0) diff --git a/src/nodesnim/core/image.nim b/src/nodesnim/core/image.nim index 1c38c84c..eb3b28f4 100644 --- a/src/nodesnim/core/image.nim +++ b/src/nodesnim/core/image.nim @@ -22,9 +22,8 @@ proc load*(file: string, x, y: var float, mode: Glenum = GL_RGB): Gluint = var surface = image.load(file) # load image from file textureid: Gluint - when defined(debug): - if surface.isNil(): - raise newException(ResourceError, "image \"" & file & "\" not loaded!") + if surface.isNil(): + throwError(ResourceError, "image \"" & file & "\" not loaded!") x = surface.w.float y = surface.h.float diff --git a/src/nodesnim/core/input.nim b/src/nodesnim/core/input.nim index 0f57db2f..c76d7976 100644 --- a/src/nodesnim/core/input.nim +++ b/src/nodesnim/core/input.nim @@ -20,7 +20,6 @@ type InputAction* = object kind*: InputEventType key_int*: cint - key_cint*: cint button_index*: cint name*, key*: string @@ -28,7 +27,6 @@ type kind*: InputEventType pressed*: bool key_int*: cint - key_cint*: cint button_index*: cint x*, y*, xrel*, yrel*: float key*: string @@ -52,9 +50,7 @@ var mouse_pressed*: bool = false press_state*: int = 0 last_event*: InputEvent = InputEvent() - pressed_keys*: seq[string] = @[] - pressed_keys_ints*: seq[cint] = @[] - pressed_keys_cints*: seq[cint] = @[] + pressed_keys_cint*: seq[cint] = @[] actionlist*: seq[InputAction] = @[] @@ -101,7 +97,7 @@ proc addKeyAction*(name, key: string) {.inline.} = ## Arguments: ## - `name` - action name. ## - `key` - key, e.g.: "w", "1", etc. - actionlist.add(InputAction(kind: KEYBOARD, name: name, key: key, key_int: ord(key[0]).cint)) + actionlist.add(InputAction(kind: KEYBOARD, name: name, key_int: ord(key[0]).cint)) proc addKeyAction*(name: string, key: cint) {.inline.} = ## Adds a new action on keyboard. @@ -127,7 +123,7 @@ proc isActionJustPressed*(name: string): bool = result = false for action in actionlist: if action.name == name: - if action.kind == MOUSE and (last_event.kind == MOUSE or last_event.kind == MOTION): + if action.kind == MOUSE and last_event.kind in [MOUSE, MOTION]: if action.button_index == last_event.button_index and mouse_pressed: if press_state == 0: result = true @@ -155,10 +151,9 @@ proc isActionPressed*(name: string): bool = elif action.kind == TOUCH and last_event.kind == TOUCH: if press_state > 0: result = true - elif action.kind == KEYBOARD and last_event.kind == KEYBOARD: - if action.key_int in pressed_keys_ints or action.key in pressed_keys: - if press_state > 0: - result = true + elif action.kind == KEYBOARD: + if action.key_int in pressed_keys_cint: + result = true proc isActionReleased*(name: string): bool = ## Returns true, when action no more active. @@ -168,12 +163,14 @@ proc isActionReleased*(name: string): bool = result = false for action in actionlist: if action.name == name: - if action.kind == MOUSE and (last_event.kind == MOUSE or last_event.kind == MOTION): + if action.kind == MOUSE and last_event.kind in [MOUSE, MOTION]: if action.button_index == last_event.button_index and not mouse_pressed: if press_state == 0: result = true elif action.kind == KEYBOARD and last_event.kind == KEYBOARD: - if action.key notin pressed_keys or action.key_int notin pressed_keys_ints: + if last_event.key_int > 255: + continue + if action.key == $chr(last_event.key_int) or action.key_int == last_event.key_int: if press_state == 0: result = true @@ -200,3 +197,15 @@ proc `$`*(event: InputEvent): string = "InputEventKeyboard(key: " & event.key & ", pressed:" & $event.pressed & ")" of TEXT: "InputEventText(key: " & event.key & ", pressed:" & $event.pressed & ")" + + +proc `$`*(action: InputAction): string = + case action.kind + of MOUSE: + "InputAction[Mouse](button: " & $action.button_index & ")" + of KEYBOARD: + "InputAction[Keyboard](key: " & $action.key_int & ")" + of TOUCH: + "InputAction[Touch]()" + else: + "" diff --git a/src/nodesnim/core/nodes_os.nim b/src/nodesnim/core/nodes_os.nim index e62ae6c8..393032b1 100644 --- a/src/nodesnim/core/nodes_os.nim +++ b/src/nodesnim/core/nodes_os.nim @@ -6,13 +6,21 @@ import discard ttfInit() -const +let home_folder* = getHomeDir() nodesnim_folder* = home_folder / "NodesNim" - saves_folder* = "NodesNim" / "saves" + saves_folder* = nodesnim_folder / "saves" discard existsOrCreateDir(nodesnim_folder) -discard existsOrCreateDir(home_folder / saves_folder) +discard existsOrCreateDir(saves_folder) + +let standard_font_path* = + when defined(windows): + "C://Windows/Fonts/segoeuib.ttf" + elif defined(android): + "/system/fonts/DroidSans.ttf" + else: + currentSourcePath().parentDir() / "unifont.ttf" var standard_font*: FontPtr = nil @@ -21,9 +29,12 @@ proc setStandardFont*(path: cstring, size: cint) = standard_font.close() standard_font = openFont(path, size) -when defined(windows): - setStandardFont("C://Windows/Fonts/segoeuib.ttf", 16) -elif defined(android): - setStandardFont("/system/fonts/DroidSans.ttf", 16) -else: - setStandardFont(currentSourcePath().parentDir() / "unifont.ttf", 16) +proc norm*(a, b, c: float): float = + if c < a: + a + elif c > b: + b + else: + c + +setStandardFont(standard_font_path, 16) diff --git a/src/nodesnim/core/scene_builder.nim b/src/nodesnim/core/scene_builder.nim index f711747a..b2cfeaaf 100644 --- a/src/nodesnim/core/scene_builder.nim +++ b/src/nodesnim/core/scene_builder.nim @@ -4,47 +4,76 @@ import proc addNode(level: var seq[NimNode], code: NimNode): NimNode {.compileTime.} = result = newStmtList() - if code.kind in [nnkStmtList, nnkObjConstr]: + if code.kind in [nnkStmtList, nnkObjConstr, nnkCall]: for line in code.children(): - if line.kind == nnkPrefix: - if line[0].kind == nnkIdent and line[1].kind == nnkCommand: - if $line[0] == "-": - if line[1][1].kind == nnkIdent: - result.add(newVarStmt(line[1][1], newCall($line[1][0], newStrLitNode($line[1][1])))) - elif line[1][1].kind == nnkObjConstr: - result.add(newVarStmt(line[1][1][0], newCall($line[1][0], newStrLitNode($line[1][1][0])))) - elif line[1][1].kind == nnkPar: - result.add(newVarStmt(postfix(line[1][1][0], "*"), newCall($line[1][0], newStrLitNode($line[1][1][0])))) - if level.len() > 0: - # - Scene main_scene: - if line[1][1].kind == nnkIdent: - result.add(newCall("addChild", level[^1], line[1][1])) - elif line[1][1].kind == nnkPar: - result.add(newCall("addChild", level[^1], line[1][1][0])) - elif line[1][1].kind == nnkObjConstr: - result.add(newCall("addChild", level[^1], line[1][1][0])) - level.add(line[1][1][0]) - var nodes = addNode(level, line[1][1]) - for i in nodes.children(): - result.add(i) + if line.kind == nnkPrefix and line[0].kind == nnkIdent and line[1].kind == nnkCommand: + # - Node name: + if $line[0] == "-": + case line[1][1].kind + of nnkIdent: # - Node name: + result.add(newVarStmt(line[1][1], newCall($line[1][0], newStrLitNode($line[1][1])))) + of nnkObjConstr: # - Node name(a: .., b: ...) + result.add(newVarStmt(line[1][1][0], newCall($line[1][0], newStrLitNode($line[1][1][0])))) + of nnkPar: # Node (name): + result.add(newVarStmt(postfix(line[1][1][0], "*"), newCall($line[1][0], newStrLitNode($line[1][1][0])))) + of nnkCall: # Node name(call smth()): + result.add(newVarStmt(line[1][1][0], newCall($line[1][0], newStrLitNode($line[1][1][0])))) + else: + discard + + if level.len() > 0: + case line[1][1].kind: + of nnkIdent: + result.add(newCall("addChild", level[^1], line[1][1])) + of nnkPar, nnkObjConstr, nnkCall: + result.add(newCall("addChild", level[^1], line[1][1][0])) + else: + discard + + if line[1][1].kind == nnkObjConstr: # - Node node(...) + level.add(line[1][1][0]) + let nodes = addNode(level, line[1][1]) + for i in nodes.children(): + result.add(i) # call methodName(arg1, arg2) -> currentNode.methodName(arg1, arg2) - elif line.kind == nnkCommand and $line[0] == "call" and level.len() > 0: + elif line.kind == nnkCommand and $line[0] == "call" and level.len > 0: line[1].insert(1, level[^1]) result.add(line[1]) + # @onProcess() -> parent@onProcess(self) + # @onPress(x, y) -> parent@onPress(self, x, y) + elif line.kind == nnkCall and line[0].kind == nnkPrefix and level.len > 0: + if $line[0][0] == "@": + var tmp = newCall(line[0][1], ident"self") + for arg in 1..line.len-2: + tmp.add(line[arg]) + result.add(newCall("@", level[^1], tmp, line[^1])) # property: value -> currentNode.property = value - elif line.kind in [nnkCall, nnkExprColonExpr] and level.len() > 0: - var attr = newNimNode(nnkAsgn) + elif line.kind in [nnkCall, nnkExprColonExpr] and level.len > 0: + let attr = newNimNode(nnkAsgn) attr.add(newNimNode(nnkDotExpr)) attr[0].add(level[^1]) attr[0].add(line[0]) attr.add(line[1]) result.add(attr) - if len(line) == 3 and line[2].kind == nnkStmtList and line[1].kind == nnkCommand: - level.add(line[1][1]) - var nodes = addNode(level, line[2]) + + if line.len == 3 and line[2].kind == nnkStmtList and line[1].kind == nnkCommand: + case line[1][1].kind + of nnkIdent: + level.add(line[1][1]) + of nnkObjConstr: + level.add(line[1][1][0]) + else: + discard + let nodes = addNode(level, line[2]) + for i in nodes.children(): + result.add(i) + + elif line.len == 2 and line[1].kind == nnkCommand and line[1][1].kind == nnkCall: + level.add(line[1][1][0]) + let nodes = addNode(level, line[1][1]) for i in nodes.children(): result.add(i) - if level.len() > 0: + if level.len > 0: discard level.pop() @@ -56,9 +85,10 @@ macro build*(code: untyped): untyped = ## ## build: ## - Scene scene: - ## Node test_node - ## Label text: + ## - Node test_node + ## - Label text: ## call setText("Hello, world!") + ## - Button btn(call setText("")) result = newStmtList() var current_level: seq[NimNode] = @[] diff --git a/src/nodesnim/core/tileset.nim b/src/nodesnim/core/tileset.nim index 4e6740b1..643299d3 100644 --- a/src/nodesnim/core/tileset.nim +++ b/src/nodesnim/core/tileset.nim @@ -19,9 +19,8 @@ proc TileSet*(img: string, tile_size: Vector2Obj, mode: Glenum = GL_RGB): TileSe var surface = image.load(img) # load image from file textureid: Gluint = 0 - when defined(debug): - if surface.isNil(): - raise newException(ResourceError, "image \"" & img & "\" not loaded!") + if surface.isNil(): + throwError(ResourceError, "image \"" & img & "\" not loaded!") glGenTextures(1, textureid.addr) glBindTexture(GL_TEXTURE_2D, textureid) @@ -41,7 +40,7 @@ proc TileSet*(img: string, tile_size: Vector2Obj, mode: Glenum = GL_RGB): TileSe surface.freeSurface() surface = nil -proc draw*(self: TileSetObj, tilex, tiley, x, y: float) = +proc draw*(self: TileSetObj, tilex, tiley, x, y, z: float) = ## Draws tile at position `tilex`,`tiley` to `x`,`y` position. if self.texture > 0: let @@ -49,15 +48,18 @@ proc draw*(self: TileSetObj, tilex, tiley, x, y: float) = texy1 = self.grid.y*tiley / self.size.y texx2 = self.grid.x*(tilex+1f) / self.size.x texy2 = self.grid.y*(tiley+1f) / self.size.y + glPushMatrix() + glTranslatef(x, y, z) glBindTexture(GL_TEXTURE_2D, self.texture) glBegin(GL_QUADS) glTexCoord2f(texx1, texy1) - glVertex2f(x, y) + glVertex3f(0, 0, 0) glTexCoord2f(texx1, texy2) - glVertex2f(x, y - self.grid.y) + glVertex3f(0, -self.grid.y, 0) glTexCoord2f(texx2, texy2) - glVertex2f(x + self.grid.x, y - self.grid.y) + glVertex3f(self.grid.x, -self.grid.y, 0) glTexCoord2f(texx2, texy1) - glVertex2f(x + self.grid.x, y) + glVertex3f(self.grid.x, 0, 0) glEnd() glBindTexture(GL_TEXTURE_2D, 0) + glPopMatrix() diff --git a/src/nodesnim/core/vector2.nim b/src/nodesnim/core/vector2.nim index f154e25c..70f79867 100644 --- a/src/nodesnim/core/vector2.nim +++ b/src/nodesnim/core/vector2.nim @@ -4,9 +4,8 @@ import math type - Vector2Obj* = object + Vector2Obj* = ref object x*, y*: float - Vector2Ref* = ref Vector2Obj proc Vector2*(x, y: float): Vector2Obj {.inline.} = @@ -15,26 +14,13 @@ proc Vector2*(x, y: float): Vector2Obj {.inline.} = proc Vector2*(b: Vector2Obj): Vector2Obj {.inline.} = Vector2Obj(x: b.x, y: b.y) -proc Vector2*(num: float): Vector2Obj {.inline.} = - Vector2Obj(x: num, y: num) +proc Vector2*(x: float): Vector2Obj {.inline.} = + Vector2Obj(x: x, y: x) proc Vector2*(): Vector2Obj {.inline.} = Vector2Obj(x: 0, y: 0) -proc newVector2*(x, y: float): Vector2Ref {.inline.} = - Vector2Ref(x: x, y: y) - -proc newVector2*(o: Vector2Obj): Vector2Ref {.inline.} = - Vector2Ref(x: o.x, y: o.y) - -proc newVector2*(o: float): Vector2Ref {.inline.} = - Vector2Ref(x: o, y: o) - -proc newVector2*(): Vector2Ref {.inline.} = - Vector2Ref(x: 0f, y: 0f) - - proc abs*(a: Vector2Obj): Vector2Obj = Vector2(abs(a.x), abs(a.y)) diff --git a/src/nodesnim/core/vector3.nim b/src/nodesnim/core/vector3.nim index c3dc8286..5ed70b69 100644 --- a/src/nodesnim/core/vector3.nim +++ b/src/nodesnim/core/vector3.nim @@ -3,7 +3,7 @@ import math type - Vector3Obj* = object + Vector3Obj* = ref object x*, y*, z*: float diff --git a/src/nodesnim/environment.nim b/src/nodesnim/environment.nim index 0465e360..0c0c553e 100644 --- a/src/nodesnim/environment.nim +++ b/src/nodesnim/environment.nim @@ -1,31 +1,29 @@ # author: Ethosa -import core/color +import + core/color, + core/enums {.used.} type - ScreenMode* {.pure.} = enum - SCREEN_MODE_NONE, ## default mode. - SCREEN_MODE_EXPANDED ## Keep screen size. EnvironmentObj* = object - delay*: int ## window delay. - color*: ColorRef ## background environment color. - brightness*: float + color*: ColorRef ## background environment color. screen_mode*: ScreenMode + delay*: int ## window delay. EnvironmentRef* = ref EnvironmentObj -proc newEnvironment*(color: ColorRef, brightness: float): EnvironmentRef = +proc newEnvironment*(color: ColorRef): EnvironmentRef = ## Creates a new EnvironmentRef object. ## ## Arguments: ## - `color`: ColorRef object for background environment color. ## - `brightness` - window brightness with value in range `0..1` - EnvironmentRef(color: color, delay: 17, brightness: brightness, screen_mode: SCREEN_MODE_NONE) + EnvironmentRef(color: color, delay: 17,screen_mode: SCREEN_MODE_NONE) proc newEnvironment*(): EnvironmentRef {.inline.} = ## Creates a new EnvironmentRef object. - newEnvironment(Color(0x313131ff), 1.0) + newEnvironment(Color(0x313131ff)) proc setBackgroundColor*(env: EnvironmentRef, color: ColorRef) = @@ -42,13 +40,6 @@ proc setBackgroundColor*(env: EnvironmentRef, color: uint32) = ## - `color`: uint32 color, e.g.: 0xFF64FF env.color = Color(color) -proc setBrightness*(env: EnvironmentRef, brightness: float) = - ## Changes window brightness. - ## - ## Arguments: - ## - `brightness` - window brightness with value in range `0..1` - env.brightness = brightness - proc setDelay*(env: EnvironmentRef, delay: int) = ## Changes window delay. ## diff --git a/src/nodesnim/graphics/drawable.nim b/src/nodesnim/graphics/drawable.nim index 073ccafd..fc446722 100644 --- a/src/nodesnim/graphics/drawable.nim +++ b/src/nodesnim/graphics/drawable.nim @@ -17,14 +17,8 @@ type DrawableObj* = object of RootObj shadow*: bool border_width*: float - border_detail_lefttop*: int - border_detail_righttop*: int - border_detail_leftbottom*: int - border_detail_rightbottom*: int - border_radius_lefttop*: float - border_radius_righttop*: float - border_radius_leftbottom*: float - border_radius_rightbottom*: float + border_detail*: array[4, int] ## left-top, right-top, right-bottom, left-bottom + border_radius*: array[4, float] ## left-top, right-top, right-bottom, left-bottom shadow_offset*: Vector2Obj border_color*: ColorRef background_color*: ColorRef @@ -35,14 +29,8 @@ type template drawablepattern*(`type`: untyped): untyped = result = `type`( texture: GlTextureObj(), border_width: 0, - border_detail_lefttop: 20, - border_detail_righttop: 20, - border_detail_leftbottom: 20, - border_detail_rightbottom: 20, - border_radius_lefttop: 0, - border_radius_righttop: 0, - border_radius_leftbottom: 0, - border_radius_rightbottom: 0, + border_detail: [8, 8, 8, 8], + border_radius: [0.float, 0, 0, 0], border_color: Color(0, 0, 0, 0), background_color: Color(0, 0, 0, 0), shadow_offset: Vector2(0, 0), shadow: false @@ -60,64 +48,65 @@ template vd* = template recalc*(shadow: bool = false) = ## Calculates vertex positions. + let (xw, yh) = (x + width, y - height) when not shadow: # left top - for i in bezier_iter(1f/self.border_detail_lefttop.float, Vector2(0, -self.border_radius_lefttop), - Vector2(0, 0), Vector2(self.border_radius_lefttop, 0)): - vertex.add(Vector2(x+i.x, y+i.y)) + for i in bezier_iter(1f/self.border_detail[0].float, Vector2(0, -self.border_radius[0]), + Vector2(0, 0), Vector2(self.border_radius[0], 0)): + vertex.add(Vector2(x + i.x, y + i.y)) # right top - for i in bezier_iter(1f/self.border_detail_righttop.float, Vector2(-self.border_radius_righttop, 0), - Vector2(0, 0), Vector2(0, -self.border_radius_righttop)): - vertex.add(Vector2(x+width+i.x, y+i.y)) + for i in bezier_iter(1f/self.border_detail[1].float, Vector2(-self.border_radius[01], 0), + Vector2(0, 0), Vector2(0, -self.border_radius[1])): + vertex.add(Vector2(xw + i.x, y + i.y)) # right bottom - for i in bezier_iter(1f/self.border_detail_rightbottom.float, Vector2(0, -self.border_radius_rightbottom), - Vector2(0, 0), Vector2(-self.border_radius_rightbottom, 0)): - vertex.add(Vector2(x+width+i.x, y-height-i.y)) + for i in bezier_iter(1f/self.border_detail[2].float, Vector2(0, -self.border_radius[2]), + Vector2(0, 0), Vector2(-self.border_radius[2], 0)): + vertex.add(Vector2(xw + i.x, yh - i.y)) # left bottom - for i in bezier_iter(1f/self.border_detail_leftbottom.float, Vector2(self.border_radius_leftbottom, 0), - Vector2(0, 0), Vector2(0, self.border_radius_leftbottom)): - vertex.add(Vector2(x+i.x, y-height+i.y)) + for i in bezier_iter(1f/self.border_detail[3].float, Vector2(self.border_radius[3], 0), + Vector2(0, 0), Vector2(0, self.border_radius[3])): + vertex.add(Vector2(x + i.x, yh + i.y)) else: glBegin(GL_QUAD_STRIP) # left top - for i in bezier_iter(1f/self.border_detail_lefttop.float, Vector2(0, -self.border_radius_lefttop), - Vector2(0, 0), Vector2(self.border_radius_lefttop, 0)): + for i in bezier_iter(1f/self.border_detail[0].float, Vector2(0, -self.border_radius[0]), + Vector2(0, 0), Vector2(self.border_radius[0], 0)): glColor4f(0, 0, 0, 0) - glVertex2f(x+i.x+self.shadow_offset.x, y+i.y-self.shadow_offset.y) + glVertex2f(x + i.x + self.shadow_offset.x, y + i.y - self.shadow_offset.y) glColor4f(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a) - glVertex2f(x+i.x, y+i.y) + glVertex2f(x + i.x, y + i.y) # right top - for i in bezier_iter(1f/self.border_detail_righttop.float, Vector2(-self.border_radius_righttop, 0), - Vector2(0, 0), Vector2(0, -self.border_radius_righttop)): + for i in bezier_iter(1f/self.border_detail[1].float, Vector2(-self.border_radius[1], 0), + Vector2(0, 0), Vector2(0, -self.border_radius[1])): glColor4f(0, 0, 0, 0) - glVertex2f(x+width+i.x+self.shadow_offset.x, y+i.y-self.shadow_offset.y) + glVertex2f(xw + i.x + self.shadow_offset.x, y + i.y - self.shadow_offset.y) glColor4f(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a) - glVertex2f(x+width+i.x, y+i.y) + glVertex2f(xw + i.x, y + i.y) # right bottom - for i in bezier_iter(1f/self.border_detail_rightbottom.float, Vector2(0, -self.border_radius_rightbottom), - Vector2(0, 0), Vector2(-self.border_radius_rightbottom, 0)): + for i in bezier_iter(1f/self.border_detail[2].float, Vector2(0, -self.border_radius[2]), + Vector2(0, 0), Vector2(-self.border_radius[2], 0)): glColor4f(0, 0, 0, 0) - glVertex2f(x+width+i.x+self.shadow_offset.x, y-height-i.y-self.shadow_offset.y) + glVertex2f(xw + i.x + self.shadow_offset.x, yh - i.y - self.shadow_offset.y) glColor4f(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a) - glVertex2f(x+width+i.x, y-height-i.y) + glVertex2f(xw + i.x, yh - i.y) # left bottom - for i in bezier_iter(1f/self.border_detail_leftbottom.float, Vector2(self.border_radius_leftbottom, 0), - Vector2(0, 0), Vector2(0, self.border_radius_leftbottom)): + for i in bezier_iter(1f/self.border_detail[3].float, Vector2(self.border_radius[3], 0), + Vector2(0, 0), Vector2(0, self.border_radius[3])): glColor4f(0, 0, 0, 0) - glVertex2f(x+i.x+self.shadow_offset.x, y-height+i.y-self.shadow_offset.y) + glVertex2f(x + i.x + self.shadow_offset.x, yh + i.y - self.shadow_offset.y) glColor4f(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a) - glVertex2f(x+i.x, y-height+i.y) + glVertex2f(x + i.x, yh + i.y) glColor4f(0, 0, 0, 0) - glVertex2f(x+self.shadow_offset.x, y-self.border_radius_lefttop-self.shadow_offset.y) + glVertex2f(x + self.shadow_offset.x, y - self.border_radius[0] - self.shadow_offset.y) glColor4f(shadow_color.r, shadow_color.g, shadow_color.b, shadow_color.a) - glVertex2f(x, y-self.border_radius_lefttop) + glVertex2f(x, y - self.border_radius[0]) glEnd() @@ -166,7 +155,8 @@ template draw_texture_template*(drawtype, color, function, secondfunc: untyped): h -= texture_size.y/2 for i in vertex: - glTexCoord2f((-x + i.x - w + texture_size.x) / width, 1f - ((-y + i.y - h + texture_size.y) / texture_size.y)) + glTexCoord2f((-x + i.x - w + texture_size.x) / width, + 1f - ((-y + i.y - h + texture_size.y) / texture_size.y)) glVertex2f(i.x, i.y) glEnd() @@ -174,7 +164,7 @@ template draw_texture_template*(drawtype, color, function, secondfunc: untyped): glDisable(GL_TEXTURE_2D) -method enableShadow*(self: DrawableRef, val: bool) {.base.} = +method enableShadow*(self: DrawableRef, val: bool = true) {.base.} = ## Enables shadow, when `val` is true. self.shadow = val @@ -225,10 +215,7 @@ method setCornerRadius*(self: DrawableRef, radius: float) {.base.} = ## ## Arguments: ## - `radius` is a new corner radius. - self.border_radius_lefttop = radius - self.border_radius_righttop = radius - self.border_radius_leftbottom = radius - self.border_radius_rightbottom = radius + self.border_radius = [radius, radius, radius, radius] method setCornerRadius*(self: DrawableRef, r1, r2, r3, r4: float) {.base.} = ## Changes corner radius. @@ -238,20 +225,14 @@ method setCornerRadius*(self: DrawableRef, r1, r2, r3, r4: float) {.base.} = ## - `r2` is a new right-top radius. ## - `r3` is a new right-bottm radius. ## - `r4` is a new left-bottm radius. - self.border_radius_lefttop = r1 - self.border_radius_righttop = r2 - self.border_radius_rightbottom = r3 - self.border_radius_leftbottom = r4 + self.border_radius = [r1, r2, r3, r4] method setCornerDetail*(self: DrawableRef, detail: int) {.base.} = ## Changes corner detail. ## ## Arguments: ## - `detail` is a new corner detail. - self.border_detail_lefttop = detail - self.border_detail_righttop = detail - self.border_detail_leftbottom = detail - self.border_detail_rightbottom = detail + self.border_detail = [detail, detail, detail, detail] method setCornerDetail*(self: DrawableRef, d1, d2, d3, d4: int) {.base.} = ## Changes corner detail. @@ -261,10 +242,7 @@ method setCornerDetail*(self: DrawableRef, d1, d2, d3, d4: int) {.base.} = ## - `d2` is a new right-top detail. ## - `d3` is a new right-bottm detail. ## - `d4` is a new left-bottm detail. - self.border_detail_lefttop = d1 - self.border_detail_righttop = d2 - self.border_detail_leftbottom = d4 - self.border_detail_rightbottom = d3 + self.border_detail = [d1, d2, d3, d4] method setTexture*(self: DrawableRef, texture: GlTextureObj) {.base.} = ## Changes drawable texture. @@ -289,16 +267,19 @@ method setStyle*(self: DrawableRef, s: StyleSheetRef) {.base.} = # background-image: "assets/img.jpg" of "background-image": self.loadTexture(i.value) - # background: "path/to/img.jpg" - # background: rgb(125, 82, 196) - # background: "img.jpg" #f6f + # background: "url(path/to/img.jpg)" + # background: "rgb(125, 82, 196)" + # background: "url(img.jpg) #f6f" of "background": + # #fff | rgba(1, 1, 1) if i.value.match(re"\A\s*(rgba?\([^\)]+\)\s*|#[a-f0-9]{3,8})\s*\Z", matches): let tmpclr = Color(matches[0]) if not tmpclr.isNil(): self.setColor(tmpclr) + # url(path/to/image) elif i.value.match(re"\A\s*url\(([^\)]+)\)\s*\Z", matches): self.loadTexture(matches[0]) + # url(path to image) #fff | rgba(1, 1, 1) elif i.value.match(re"\A\s*url\(([^\)]+)\)\s+(rgba?\([^\)]+\)\s*|#[a-f0-9]{3,8})\s*\Z", matches): self.loadTexture(matches[0]) let tmpclr = Color(matches[1]) diff --git a/src/nodesnim/graphics/gradient_drawable.nim b/src/nodesnim/graphics/gradient_drawable.nim index bbd05817..f15e81a2 100644 --- a/src/nodesnim/graphics/gradient_drawable.nim +++ b/src/nodesnim/graphics/gradient_drawable.nim @@ -12,18 +12,16 @@ import type GradientDrawableObj* = object of DrawableObj - corners*: tuple[p0, p1, p2, p3: ColorRef] + corners*: array[4, ColorRef] GradientDrawableRef* = ref GradientDrawableObj proc GradientDrawable*: GradientDrawableRef = drawablepattern(GradientDrawableRef) - result.corners = (Color(1f, 1f, 1f, 1.0), + result.corners = [Color(1f, 1f, 1f, 1.0), Color(1f, 1f, 1f, 1.0), Color(1f, 1f, 1f, 1.0), - Color(1f, 1f, 1f, 1.0)) - -let shadow_color: ColorRef = Color(0f, 0f, 0f, 0.5f) + Color(1f, 1f, 1f, 1.0)] template draw_template*(drawtype, color, function, secondfunc: untyped, is_gradient: bool = true): untyped = @@ -76,9 +74,6 @@ method draw*(self: GradientDrawableRef, x1, y1, width, height: float) = if self.border_width > 0f: draw_template(GL_LINE_LOOP, self.border_color, glLineWidth(self.border_width), glLineWidth(1), false) -method setCornerColors*(self: GradientDrawableRef, corners: tuple[p0, p1, p2, p3: ColorRef]) {.base.} = - self.corners = corners - method setCornerColors*(self: GradientDrawableRef, c0, c1, c2, c3: ColorRef) {.base.} = ## Changes corners colors ## @@ -87,10 +82,23 @@ method setCornerColors*(self: GradientDrawableRef, c0, c1, c2, c3: ColorRef) {.b ## - `c1` is right-top color. ## - `c2` is right-bottom color. ## - `c3` is left-bottom color. - self.corners[0] = c0 - self.corners[1] = c1 - self.corners[2] = c2 - self.corners[3] = c3 + self.corners = [c0, c1, c2, c3] + +method setCornerColors*(self: GradientDrawableRef, corners: array[4, ColorRef]) {.base.} = + ## Changes corners colors + ## + ## See also: + ## * `setCornerColors method <#setCornerColors.e,GradientDrawableRef,ColorRef,ColorRef,ColorRef,ColorRef>`_ + ## * `setCornerColors(GradientDrawableRef, ColorRef) method <#setCornerColors.e,GradientDrawableRef,ColorRef>`_ + self.corners = corners + +method setCornerColors*(self: GradientDrawableRef, clr: ColorRef) {.base.} = + ## Changes corners colors + ## + ## See also: + ## * `setCornerColors method <#setCornerColors.e,GradientDrawableRef,ColorRef,ColorRef,ColorRef,ColorRef>`_ + ## * `setCornerColors(GradientDrawableRef, array[4, ColorRef]) method <#setCornerColors.e,GradientDrawableRef,array[,ColorRef]>`_ + self.corners = [Color(clr), Color(clr), Color(clr), Color(clr)] method setStyle*(self: GradientDrawableRef, s: StyleSheetRef) = ## Sets a new stylesheet. @@ -102,7 +110,7 @@ method setStyle*(self: GradientDrawableRef, s: StyleSheetRef) = of "corner-color": let tmp = i.value.split(" ") if tmp.len() == 1: - self.setCornerColors(Color(tmp[0]), Color(tmp[0]), Color(tmp[0]), Color(tmp[0])) + self.setCornerColors(Color(tmp[0])) elif tmp.len() == 4: self.setCornerColors(Color(tmp[0]), Color(tmp[1]), Color(tmp[2]), Color(tmp[3])) else: diff --git a/src/nodesnim/nodes/animation_player.nim b/src/nodesnim/nodes/animation_player.nim index 8c2c8373..12d65220 100644 --- a/src/nodesnim/nodes/animation_player.nim +++ b/src/nodesnim/nodes/animation_player.nim @@ -8,10 +8,6 @@ import type - AnimationMode* {.pure, size: sizeof(int8).} = enum - ANIMATION_NORMAL, - ANIMATION_EASE, - ANIMATION_BEZIER AnimationObject* = object states: seq[tuple[tick: int, value: float]] obj: ptr float @@ -41,23 +37,28 @@ proc AnimationPlayer*(name: string = "AnimationPlayer"): AnimationPlayerRef = result.mode = ANIMATION_NORMAL -proc ease(self: AnimationPlayerRef, states: seq[tuple[tick: int, value: float]]): float = - var time = (self.tick - states[0].tick).float / ((states[1].tick - states[0].tick).float / 2.0) +# --- Private --- # +proc ease(self: AnimationPlayerRef, + states: seq[tuple[tick: int, value: float]]): float = + var time = (self.tick - states[0].tick).float / + ((states[1].tick - states[0].tick).float / 2.0) let diff = states[1].value - states[0].value if time < 1: return diff / 2 * time * time + states[0].value time -= 1 - return -diff / 2 * (time * (time - 2) - 1) + states[0].value + -diff / 2 * (time * (time - 2) - 1) + states[0].value -proc bezier(self: AnimationPlayerRef, states: seq[tuple[tick: int, value: float]], current: float): float = +proc bezier(self: AnimationPlayerRef, + states: seq[tuple[tick: int, value: float]], current: float): float = let step = 1f / (states[1].tick - states[0].tick).float t = step * (self.tick - states[0].tick).float diff = states[1].value - states[0].value result = cubic_bezier(t, 0.0, self.bezier[0], self.bezier[1], 1.0) - result = states[0].value + diff*result + return states[0].value + diff*result +# --- Public --- # method addState*(self: AnimationPlayerRef, obj: ptr float, states: seq[tuple[tick: int, value: float]]) {.base.} = ## Adds a new state to AnimationPlayer. self.objects.add(AnimationObject( @@ -65,14 +66,15 @@ method addState*(self: AnimationPlayerRef, obj: ptr float, states: seq[tuple[tic obj: obj )) + method draw*(self: AnimationPlayerRef, w: GLfloat, h: GLfloat) = ## This uses in the `window.nim`. if self.is_played: if self.tick > self.duration: - self.tick = 0 - if not self.loop: - self.is_played = false - return + self.tick = 0 + if not self.loop: + self.is_played = false + return var current_states: seq[tuple[tick: int, value: float]] = @[] @@ -85,21 +87,22 @@ method draw*(self: AnimationPlayerRef, w: GLfloat, h: GLfloat) = if self.tick == obj.states[i].tick: current[] = obj.states[i].value break - if current_states.len() == 1: + if current_states.len == 1: for i in 0..obj.states.high: if current_states[0].tick < obj.states[i].tick: current_states.add(obj.states[i]) break - if current_states.len() == 2: - if self.mode == ANIMATION_NORMAL: + if current_states.len == 2: + case self.mode + of ANIMATION_NORMAL: let diff_time: float = (current_states[1].tick - current_states[0].tick).float diff: float = current_states[1].value - current_states[0].value current[] = current_states[0].value + (self.tick - current_states[0].tick).float/diff_time * diff - elif self.mode == ANIMATION_EASE: - current[] = self.ease(current_states) - elif self.mode == ANIMATION_BEZIER: + of ANIMATION_EASE: + current[] = ease(self, current_states) + of ANIMATION_BEZIER: current[] = bezier(self, current_states, current[]) current_states = @[] diff --git a/src/nodesnim/nodes/canvas.nim b/src/nodesnim/nodes/canvas.nim index 45cd77ab..370478a0 100644 --- a/src/nodesnim/nodes/canvas.nim +++ b/src/nodesnim/nodes/canvas.nim @@ -2,10 +2,10 @@ ## Canvas is the root type of all 2D and Control nodes. ## ## Canvas used for drawing primitive geometry. +import ../thirdparty/sdl2 except Color import math, ../thirdparty/opengl, - ../thirdparty/sdl2, ../thirdparty/sdl2/image, ../core/vector2, @@ -53,7 +53,7 @@ proc Canvas*(name: string = "Canvas"): CanvasRef = result.canvas_texture = 0 result.surface = createRGBSurface( 0, 40, 40, 32, - 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000'u32) + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000u32) result.renderer = result.surface.createSoftwareRenderer() result.kind = CANVAS_NODE result.type_of_node = NODE_TYPE_CONTROL @@ -120,6 +120,10 @@ method move*(self: CanvasRef, vec2: Vector2Obj) {.base, inline.} = ## ## Arguments: ## - `vec2`: how much to add to the position on the X,Y axes. + ## + ## See also: + ## - `move method <#move.e,CanvasRef,float,float>`_ + ## - `moveTo method <#moveTo.e,CanvasRef,Vector2Obj>`_ self.position += vec2 self.anchor.clear() @@ -129,9 +133,30 @@ method move*(self: CanvasRef, x, y: float) {.base, inline.} = ## Arguments: ## - `x`: how much to add to the position on the X axis. ## - `y`: how much to add to the position on the Y axis. + ## + ## See also: + ## - `move method <#move.e,CanvasRef,Vector2Obj>`_ + ## - `moveTo method <#moveTo.e,CanvasRef,float,float>`_ self.position += Vector2(x, y) self.anchor.clear() +method moveTo*(self: CanvasRef, x, y: float) {.base, inline.} = + ## Change node position. + ## + ## Arguments: + ## - `x`: how much to add to the position on the X axis. + ## - `y`: how much to add to the position on the Y axis. + self.position = Vector2(x, y) + self.anchor.clear() + +method moveTo*(self: CanvasRef, vec2: Vector2Obj) {.base, inline.} = + ## Change node position. + ## + ## Arguments: + ## - `vec2`: how much to add to the position on the X,Y axes. + self.position = vec2 + self.anchor.clear() + method resize*(self: CanvasRef, w, h: GLfloat, save_anchor: bool = false) {.base.} = ## Resizes canvas. ## @@ -178,15 +203,25 @@ method setAnchor*(self: CanvasRef, x1, y1, x2, y2: float) {.base.} = method setSizeAnchor*(self: CanvasRef, anchor: Vector2Obj) {.base.} = + ## Changes the size anchor to the size of the parent. self.size_anchor = anchor method setSizeAnchor*(self: CanvasRef, x, y: float) {.base.} = + ## Changes the size anchor to the size of the parent. self.size_anchor = Vector2(x, y) # --- Draw functions --- # proc bezier*(canvas: CanvasRef, x1, y1, x2, y2, x3, y3: GLfloat, color: ColorRef) = ## Draws a quadric bezier curve at 3 points. + ## + ## Arguments: + ## - `x1` `y1` - first point. + ## - `x2` `y2` - second point. + ## - `x3` `y3` - third point. + ## + ## See also: + ## - `cubicBezier proc <#cubicBezier,CanvasRef,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,ColorRef>`_ loadColor(color) for pnt in bezier_iter(0.001, Vector2(x1, y1), Vector2(x2, y2), Vector2(x3, y3)): canvas.renderer.drawPoint(pnt.x.cint, pnt.y.cint) @@ -207,8 +242,17 @@ proc circle*(canvas: CanvasRef, x, y, radius: GLfloat, color: ColorRef, quality: canvas.renderer.drawPoint((x + radius*cos(angle)).cint, (y + radius*sin(angle)).cint) loadGL(canvas) -proc cubic_bezier*(canvas: CanvasRef, x1, y1, x2, y2, x3, y3, x4, y4: GLfloat, color: ColorRef) = - ## Draws a quadric bezier curve at 3 points. +proc cubicBezier*(canvas: CanvasRef, x1, y1, x2, y2, x3, y3, x4, y4: GLfloat, color: ColorRef) = + ## Draws a quadric bezier curve at 4 points. + ## + ## Arguments: + ## - `x1` `y1` - first point. + ## - `x2` `y2` - second point. + ## - `x3` `y3` - third point. + ## - `x4` `y4` - fourth point. + ## + ## See also: + ## - `bezier proc <#bezier,CanvasRef,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,GLfloat,ColorRef>`_ loadColor(color) for pnt in cubic_bezier_iter(0.001, Vector2(x1, y1), Vector2(x2, y2), Vector2(x3, y3), Vector2(x4, y4)): canvas.renderer.drawPoint(pnt.x.cint, pnt.y.cint) diff --git a/src/nodesnim/nodes/node.nim b/src/nodesnim/nodes/node.nim index 4b6455cf..6b1a231f 100644 --- a/src/nodesnim/nodes/node.nim +++ b/src/nodesnim/nodes/node.nim @@ -13,20 +13,22 @@ import type + NodeHandler* = proc(self: NodeRef) + NodeEvHandler* = proc(self: NodeRef, event: InputEvent) NodeObj* = object of RootObj kind*: NodeKind - type_of_node*: NodeTypes - visible*: Visibility - is_ready*: bool - pausemode*: PauseMode ## Pause mode, by default is INHERIT. - name*: string ## Node name. - parent*: NodeRef ## Node parent. - children*: seq[NodeRef] ## Node children. - on_enter*: proc(self: NodeRef) ## This called when scene changed. - on_exit*: proc(self: NodeRef) ## This called when exit from the scene. - on_input*: proc(self: NodeRef, event: InputEvent) ## This called on user input. - on_ready*: proc(self: NodeRef) ## This called when the scene changed and the `enter` was called. - on_process*: proc(self: NodeRef) ## This called every frame. + 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 @@ -39,7 +41,7 @@ template nodepattern*(nodetype: untyped): untyped = on_input: proc(self: NodeRef, event: InputEvent) = discard, on_enter: proc(self: NodeRef) = discard, on_exit: proc(self: NodeRef) = discard, - is_ready: false, pausemode: INHERIT, visible: VISIBLE + is_ready: false, pausemode: INHERIT, visibility: VISIBLE ) result.type_of_node = NODE_TYPE_DEFAULT @@ -54,15 +56,22 @@ method addChild*(self: NodeRef, child: NodeRef) {.base.} = ## ## 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 addChilds*(self: NodeRef, childs: varargs[NodeRef]) {.base.} = +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) @@ -80,29 +89,42 @@ method getChild*(self: NodeRef, index: int): NodeRef {.base.} = ## ## 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 - return -1 + -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 - return -1 + -1 method getChildIter*(self: NodeRef): seq[NodeRef] {.base.} = ## Returns all children iter. @@ -171,14 +193,14 @@ method handle*(self: NodeRef, event: InputEvent, mouse_on: var NodeRef) {.base.} ## This used in the Window object. discard -method hasNode*(self: NodeRef, name: string): bool {.base.} = +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.} = +method hasNode*(self: NodeRef, other: NodeRef): bool {.base, inline.} = ## Returns true, if `other` in self children. ## ## Arguments: @@ -187,10 +209,10 @@ method hasNode*(self: NodeRef, other: NodeRef): bool {.base.} = method hasParent*(self: NodeRef): bool {.base, inline.} = ## Returns true, when node has parent. - self.parent != nil + not self.parent.isNil() method hide*(self: NodeRef) {.base.} = - self.visible = INVISIBLE + self.visibility = INVISIBLE method postdraw*(self: NodeRef, w, h: GLfloat) {.base.} = ## Draws node. @@ -205,6 +227,9 @@ method removeChild*(self: NodeRef, index: int) {.base.} = ## ## Arguments: ## - `index`: child index. + ## + ## See also: + ## - `addChild method <#addChild.e,NodeRef,NodeRef>`_ self.children[index].parent = nil self.children.delete(index) @@ -213,12 +238,12 @@ method removeChild*(self: NodeRef, other: NodeRef) {.base.} = ## ## Arguments: ## - `other`: other node. - var index: int = self.getChildIndex(other) + let index: int = self.getChildIndex(other) if index != -1: self.removeChild(index) method show*(self: NodeRef) {.base.} = - self.visible = VISIBLE + self.visibility = VISIBLE method delete*(self: NodeRef) {.base.} = ## Deletes current node. diff --git a/src/nodesnim/nodes/scene.nim b/src/nodesnim/nodes/scene.nim index 44a7129a..74ddb22b 100644 --- a/src/nodesnim/nodes/scene.nim +++ b/src/nodesnim/nodes/scene.nim @@ -36,10 +36,11 @@ proc Scene*(name: string = "Scene"): SceneRef = method drawScene*(scene: SceneRef, w, h: GLfloat, paused: bool) {.base.} = ## Draws scene ## This used in the window.nim. + scene.on_process(scene) for child in scene.getChildIter(): if paused and child.getPauseMode() != PROCESS: continue - if child.visible != GONE: + if child.visibility != GONE: # load opengl if child.type_of_node != NODE_TYPE_DEFAULT: glLoadIdentity() @@ -57,17 +58,13 @@ method drawScene*(scene: SceneRef, w, h: GLfloat, paused: bool) {.base.} = child.Node3DRef.calcGlobalPosition3() gluPerspective(45.0, w/h, 1.0, 5000.0) if current_camera.isNil(): - gluLookAt(0, 0, -1, - 0, 0, 1, - 0, 1, 0) + gluLookAt(0, 0, -1, 0, 0, 1, 0, 1, 0) else: let pos = current_camera.global_translation front = current_camera.front + pos up = current_camera.up - gluLookAt(pos.x, pos.y, pos.z, - front.x, front.y, front.z, - up.x, up.y, up.z) + gluLookAt(pos.x, pos.y, pos.z, front.x, front.y, front.z, up.x, up.y, up.z) if not child.is_ready: child.on_ready(child) @@ -77,7 +74,7 @@ method drawScene*(scene: SceneRef, w, h: GLfloat, paused: bool) {.base.} = for child in scene.getChildIter(): if paused and child.getPauseMode() != PROCESS: continue - if child.visible != GONE: + if child.visibility != GONE: child.postdraw(w, h) scene.calcGlobalPosition() @@ -101,11 +98,12 @@ method exit*(scene: SceneRef) {.base.} = method handleScene*(scene: SceneRef, event: InputEvent, mouse_on: var NodeRef, paused: bool) {.base.} = ## Handles user input. This called on any input. + scene.on_input(scene, event) var childs = scene.getChildIter() for i in countdown(childs.len()-1, 0): if paused and childs[i].getPauseMode() != PROCESS: continue - if childs[i].visible != GONE: + if childs[i].visibility != GONE: childs[i].handle(event, mouse_on) childs[i].on_input(childs[i], event) diff --git a/src/nodesnim/nodes2d/animated_sprite.nim b/src/nodesnim/nodes2d/animated_sprite.nim index 49562a94..7bf37408 100644 --- a/src/nodesnim/nodes2d/animated_sprite.nim +++ b/src/nodesnim/nodes2d/animated_sprite.nim @@ -55,11 +55,8 @@ method draw*(self: AnimatedSpriteRef, w, h: GLfloat) = self.rect_size = texture.size # Recalculate position. - self.position = self.timed_position - if self.centered: - self.position = self.timed_position - self.rect_size/2 - else: - self.position = self.timed_position + procCall self.Node2DRef.draw(w, h) + self.calcGlobalPosition() let x = -w/2 + self.global_position.x @@ -69,6 +66,7 @@ method draw*(self: AnimatedSpriteRef, w, h: GLfloat) = if frame >= 0 and frame < frames_count: var texture = self.animations[self.animation].frames[frame] if texture.texture > 0'u32: + glPushMatrix() if self.centered: glTranslatef(x + (self.rect_size.x / 2), y - (self.rect_size.y / 2), self.z_index_global) self.position = self.rect_size / 2 @@ -94,13 +92,7 @@ method draw*(self: AnimatedSpriteRef, w, h: GLfloat) = glEnd() glDisable(GL_DEPTH_TEST) glDisable(GL_TEXTURE_2D) - glRotatef(-self.rotation, 0, 0, 1) - if self.centered: - glTranslatef(-x - (self.rect_size.x / 2), -y + (self.rect_size.y / 2), -self.z_index_global) - self.position = self.timed_position - self.rect_size/2 - else: - glTranslatef(-x, -y, -self.z_index_global) - self.position = self.timed_position + glPopMatrix() else: self.rect_size = Vector2() @@ -126,10 +118,6 @@ method duplicate*(self: AnimatedSpriteRef): AnimatedSpriteRef {.base.} = ## Duplicates AnimatedSprite object and create a new AnimatedSprite. self.deepCopy() -method getGlobalMousePosition*(self: AnimatedSpriteRef): Vector2Obj {.inline.} = - ## Returns mouse position. - Vector2Obj(x: last_event.x, y: last_event.y) - method addAnimation*(self: AnimatedSpriteRef, name: string, speed: float = 2f) {.base.} = ## Adds a new animation to the AnimatedSprite animations. ## diff --git a/src/nodesnim/nodes2d/camera2d.nim b/src/nodesnim/nodes2d/camera2d.nim index ba73bae6..2f9c3e45 100644 --- a/src/nodesnim/nodes2d/camera2d.nim +++ b/src/nodesnim/nodes2d/camera2d.nim @@ -15,14 +15,14 @@ import type Camera2DObj* = object of Node2DObj - current*, smooth*: bool + smooth*: bool smooth_speed*: float target*: NodeRef - limit*: AnchorObj + limit*: AnchorObj ## left, top, right, bottom Camera2DRef* = ref Camera2DObj -var nodes: seq[Camera2DRef] = @[] +var current_camera: Camera2DRef = nil proc Camera2D*(name: string = "Camera2D"): Camera2DRef = @@ -35,11 +35,9 @@ proc Camera2D*(name: string = "Camera2D"): Camera2DRef = nodepattern(Camera2DRef) node2dpattern() result.limit = Anchor(-100000, -100000, 100000, 100000) - result.current = false result.smooth = false result.smooth_speed = 0.1 result.kind = CAMERA_2D_NODE - nodes.add(result) method changeTarget*(self: Camera2DRef, target: NodeRef) {.base.} = @@ -57,6 +55,9 @@ method changeSmoothSpeed*(self: Camera2DRef, speed: float) {.base.} = method disableSmooth*(self: Camera2DRef) {.base.} = ## Disables smooth mode. + ## + ## See also: + ## - `enableSmooth method <#enableSmooth.e,Camera2DRef,float>`_ self.smooth = false @@ -65,7 +66,7 @@ method draw*(self: Camera2DRef, w, h: GLfloat) = {.warning[LockLevel]: off.} procCall self.Node2DRef.draw(w, h) - if self.target != nil and self.current: + if self.target != nil and self == current_camera: var root = self.getRootNode().CanvasRef let x = self.target.CanvasRef.position.x @@ -93,15 +94,20 @@ method enableSmooth*(self: Camera2DRef, speed: float = 0.1) {.base.} = ## ## Arguments: ## - `speed` is a smooth speed. The closer to 0, the smoother the camera. The default is 0.1. + ## + ## See also: + ## - `disableSmooth method <#disableSmooth.e,Camera2DRef>`_ self.smooth = true self.smooth_speed = speed +method isCurrent*(self: Camera2DRef): bool {.base.} = + self == current_camera + + method setCurrent*(self: Camera2DRef) {.base.} = ## Changes the current camera. It also automatically disable other cameras. - for c in nodes: - c.current = false - self.current = true + current_camera = self method setLimit*(self: Camera2DRef, x1, y1, x2, y2: float) {.base.} = diff --git a/src/nodesnim/nodes2d/collision_shape2d.nim b/src/nodesnim/nodes2d/collision_shape2d.nim index 5bbb5e4d..182a534b 100644 --- a/src/nodesnim/nodes2d/collision_shape2d.nim +++ b/src/nodesnim/nodes2d/collision_shape2d.nim @@ -22,10 +22,6 @@ when defined(debug): type - CollisionShape2DType* {.size: sizeof(int8), pure.} = enum - COLLISION_SHAPE_2D_RECTANGLE, - COLLISION_SHAPE_2D_CIRCLE, - COLLISION_SHAPE_2D_POLYGON CollisionShape2DObj* = object of Node2DObj disable*: bool x1*, y1*, radius*: float @@ -99,6 +95,7 @@ when defined(debug): glColor4f(0.5, 0.5, 0.5, 0.7) else: glColor4f(0.5, 0.6, 0.9, 0.7) + glEnable(GL_DEPTH_TEST) case self.shape_type of COLLISION_SHAPE_2D_RECTANGLE: @@ -116,6 +113,7 @@ when defined(debug): glVertex3f(x + vec2.x, y - vec2.y, self.z_index_global) glEnd() glColor4f(1, 1, 1, 1) + glDisable(GL_DEPTH_TEST) method duplicate*(self: CollisionShape2DRef): CollisionShape2DRef {.base.} = diff --git a/src/nodesnim/nodes2d/kinematic_body2d.nim b/src/nodesnim/nodes2d/kinematic_body2d.nim index f07e46dc..161185ea 100644 --- a/src/nodesnim/nodes2d/kinematic_body2d.nim +++ b/src/nodesnim/nodes2d/kinematic_body2d.nim @@ -96,7 +96,7 @@ method moveAndCollide*(self: KinematicBody2DRef, vel: Vector2Obj) {.base.} = ## - `vel` is a velocity vector. # TODO: normal algorithn let - biggest = abs(if max(vel.x, vel.y) == 0.0: min(vel.x, vel.y) else: max(vel.x, vel.y)) + biggest = max(vel.x.abs(), vel.y.abs()) step = Vector2(vel.x/biggest, vel.y/biggest) vec = vel.abs() scene = self.getRootNode() diff --git a/src/nodesnim/nodes2d/sprite.nim b/src/nodesnim/nodes2d/sprite.nim index a17569df..880dcc02 100644 --- a/src/nodesnim/nodes2d/sprite.nim +++ b/src/nodesnim/nodes2d/sprite.nim @@ -47,7 +47,7 @@ method draw*(self: SpriteRef, w, h: GLfloat) = # Recalculate position. procCall self.Node2DRef.draw(w, h) - self.CanvasRef.calcGlobalPosition() + self.calcGlobalPosition() let x = -w/2 + self.global_position.x @@ -55,6 +55,7 @@ method draw*(self: SpriteRef, w, h: GLfloat) = # Draw if self.texture.texture > 0'u32: + glPushMatrix() if self.centered: glTranslatef(x + (self.rect_size.x / 2), y - (self.rect_size.y / 2), self.z_index_global) self.position = self.rect_size / 2 @@ -80,13 +81,7 @@ method draw*(self: SpriteRef, w, h: GLfloat) = glEnd() glDisable(GL_DEPTH_TEST) glDisable(GL_TEXTURE_2D) - glRotatef(-self.rotation, 0, 0, 1) - if self.centered: - glTranslatef(-x - (self.rect_size.x / 2), -y + (self.rect_size.y / 2), -self.z_index_global) - self.position = self.timed_position - self.rect_size/2 - else: - glTranslatef(-x, -y, -self.z_index_global) - self.position = self.timed_position + glPopMatrix() else: self.rect_size = Vector2() diff --git a/src/nodesnim/nodes2d/tilemap.nim b/src/nodesnim/nodes2d/tilemap.nim index 532ab63f..14383547 100644 --- a/src/nodesnim/nodes2d/tilemap.nim +++ b/src/nodesnim/nodes2d/tilemap.nim @@ -12,14 +12,11 @@ import type - TileMapMode* {.pure, size: sizeof(int8).} = enum - TILEMAP_2D, ## Default 2D mode. - TILEMAP_ISOMETRIC ## Isometric mode. TileMapObj* = object of Node2DObj mode*: TileMapMode map_size*: tuple[x, y, z: int] tileset*: TileSetObj - map*: seq[Vector2Ref] + map*: seq[Vector2Obj] TileMapRef = ref TileMapObj @@ -51,8 +48,10 @@ method draw*(self: TileMapRef, w, h: GLfloat) = viewport[2] = self.map_size.x-1 glEnable(GL_TEXTURE_2D) + glColor4f(1, 1, 1, 1) case self.mode of TILEMAP_2D: + glEnable(GL_DEPTH_TEST) for z in 0.. 0'u32: + let height = (self.texture.size.y/self.texture.size.x).float + glPushMatrix() + glTranslatef(self.global_translation.x, self.global_translation.y, self.global_translation.z) + glRotatef(self.global_rotation.x, 1, 0, 0) + glRotatef(self.global_rotation.y, 0, 1, 0) + glRotatef(self.global_rotation.z, 0, 0, 1) + glScalef(self.scale.x, self.scale.y, self.scale.z) + glColor4f(self.filter.r, self.filter.g, self.filter.b, self.filter.a) + + glEnable(GL_TEXTURE_2D) + glEnable(GL_DEPTH_TEST) + glBindTexture(GL_TEXTURE_2D, self.texture.texture) + + glBegin(GL_QUADS) + glTexCoord2f(0, 0) + glVertex3f(-1, height, 0) + glTexCoord2f(0, 1) + glVertex3f(-1, -height, 0) + glTexCoord2f(1, 1) + glVertex3f(1, -height, 0) + glTexCoord2f(1, 0) + glVertex3f(1, height, 0) + glEnd() + + glDisable(GL_DEPTH_TEST) + glDisable(GL_TEXTURE_2D) + glPopMatrix() + +method duplicate*(self: Sprite3DRef): Sprite3DRef {.base.} = + ## Duplicates Sprite object and create a new Sprite. + self.deepCopy() + +method loadTexture*(self: Sprite3DRef, file: string, mode = GL_RGB) {.base.} = + ## Loads a new texture from file. + ## + ## Arguments: + ## - `file` is a texture path. + ## - `mode` is a GLenum. can be GL_RGB or GL_RGBA. + self.texture = load(file, mode) + +method setTexture*(self: Sprite3DRef, texture: GlTextureObj) {.base.} = + ## Loads a new texture from file. + ## + ## Arguments: + ## - `texture` is a GlTexture object. + self.texture = texture diff --git a/src/nodesnim/nodescontrol.nim b/src/nodesnim/nodescontrol.nim index 64034585..5117a765 100644 --- a/src/nodesnim/nodescontrol.nim +++ b/src/nodesnim/nodescontrol.nim @@ -18,10 +18,11 @@ import nodescontrol/counter, nodescontrol/switch, nodescontrol/subwindow, - nodescontrol/checkbox + nodescontrol/checkbox, + nodescontrol/tooltip export control, color_rect, texture_rect, label, button, box, hbox, vbox, grid_box, edittext, scroll, progress_bar, slider, popup, texture_button, texture_progress_bar, - counter, switch, subwindow, checkbox + counter, switch, subwindow, checkbox, tooltip diff --git a/src/nodesnim/nodescontrol/box.nim b/src/nodesnim/nodescontrol/box.nim index 41ff9e71..c74a987d 100644 --- a/src/nodesnim/nodescontrol/box.nim +++ b/src/nodesnim/nodescontrol/box.nim @@ -95,6 +95,9 @@ method setChildAnchor*(self: BoxRef, anchor: AnchorObj) {.base.} = ## ## Arguments: ## - `anchor` - Anchor object. + ## + ## See also: + ## - `setChildAnchor method <#setChildAnchor.e,BoxRef,float,float,float,float>`_ self.child_anchor = anchor method setChildAnchor*(self: BoxRef, x1, y1, x2, y2: float) {.base.} = @@ -103,4 +106,7 @@ method setChildAnchor*(self: BoxRef, x1, y1, x2, y2: float) {.base.} = ## Arguments: ## - `x1` and `y1` is an anchor relative to Box size. ## - `x2` and `y2` is an anchor relative to child size. + ## + ## See also: + ## - `setChildAnchor method <#setChildAnchor.e,BoxRef,AnchorObj>`_ self.child_anchor = Anchor(x1, y1, x2, y2) diff --git a/src/nodesnim/nodescontrol/button.nim b/src/nodesnim/nodescontrol/button.nim index 48339fd6..a4faf0c2 100644 --- a/src/nodesnim/nodescontrol/button.nim +++ b/src/nodesnim/nodescontrol/button.nim @@ -1,8 +1,8 @@ # author: Ethosa ## Handles mouse clicks. +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../core/vector2, ../core/rect2, diff --git a/src/nodesnim/nodescontrol/control.nim b/src/nodesnim/nodescontrol/control.nim index 83b2afcb..e851e009 100644 --- a/src/nodesnim/nodescontrol/control.nim +++ b/src/nodesnim/nodescontrol/control.nim @@ -19,23 +19,25 @@ import type + ControlXYHandler* = proc(self: ControlRef, x, y: float) + ControlHandler* = proc(self: ControlRef) ControlObj* = object of CanvasObj hovered*: bool pressed*: bool focused*: bool mousemode*: MouseMode - padding*: AnchorObj ## Only for Box, VBox, HBox, GridBox and Label objects! - margin*: AnchorObj ## Only for HBox and VBox objects! + padding*: AnchorObj ## Only for Box, VBox, HBox, GridBox and Label objects! + margin*: AnchorObj ## Only for HBox and VBox objects! background*: DrawableRef - on_mouse_enter*: proc(self: ControlRef, x, y: float): void ## This called when the mouse enters the Control node. - on_mouse_exit*: proc(self: ControlRef, x, y: float): void ## This called when the mouse exit from the Control node. - on_click*: proc(self: ControlRef, x, y: float): void ## This called when the user clicks on the Control node. - on_press*: proc(self: ControlRef, x, y: float): void ## This called when the user holds on the mouse on the Control node. - on_release*: proc(self: ControlRef, x, y: float): void ## This called when the user no more holds on the mouse. - on_focus*: proc(self: ControlRef): void ## This called when the Control node gets focus. - on_unfocus*: proc(self: ControlRef): void ## This called when the Control node loses focus. + on_mouse_enter*: ControlXYHandler ## This called when the mouse enters the Control node. + on_mouse_exit*: ControlXYHandler ## This called when the mouse exit from the Control node. + on_click*: ControlXYHandler ## This called when the user clicks on the Control node. + on_press*: ControlXYHandler ## This called when the user holds on the mouse on the Control node. + on_release*: ControlXYHandler ## This called when the user no more holds on the mouse. + on_focus*: ControlHandler ## This called when the Control node gets focus. + on_unfocus*: ControlHandler ## This called when the Control node loses focus. ControlRef* = ref ControlObj @@ -143,31 +145,66 @@ method handle*(self: ControlRef, event: InputEvent, mouse_on: var NodeRef) = self.pressed = false self.on_release(self, event.x, event.y) -method setBackground*(self: ControlRef, drawable: DrawableRef) {.base.} = +method setBackground*(self: ControlRef, drawable: DrawableRef) {.base, inline.} = + ## Changes background drawable. + ## + ## Arguments: + ## - `drawable` - can be `DrawableRef` or `GradientDrawableRef`. self.background = drawable -method setBackgroundColor*(self: ControlRef, color: ColorRef) {.base.} = +method setBackgroundColor*(self: ControlRef, color: ColorRef) {.base, inline.} = ## Changes Control background color. self.background.setColor(color) -method setMargin*(self: ControlRef, margin: AnchorObj) {.base.} = +method setMargin*(self: ControlRef, margin: AnchorObj) {.base, inline.} = ## Changes Control margin. + ## + ## See also: + ## - `setMargin method <#setMargin.e,ControlRef,float,float,float,float>`_ self.margin = margin -method setMargin*(self: ControlRef, x1, y1, x2, y2: float) {.base.} = +method setMargin*(self: ControlRef, x1, y1, x2, y2: float) {.base, inline.} = ## Changes Control margin. - self.setMargin(Anchor(x1, y1, x2, y2)) + ## + ## Arguments: + ## - `x1` - left margin. + ## - `y1` - top margin. + ## - `x2` - right margin. + ## - `y2` - bottom margin. + ## + ## See also: + ## - `setMargin method <#setMargin.e,ControlRef,AnchorObj>`_ + self.margin = Anchor(x1, y1, x2, y2) -method setPadding*(self: ControlRef, padding: AnchorObj) {.base.} = +method setPadding*(self: ControlRef, padding: AnchorObj) {.base, inline.} = ## Changes Control padding. + ## + ## See also: + ## - `setPadding method <#setPadding.e,ControlRef,float,float,float,float>`_ self.padding = padding -method setPadding*(self: ControlRef, x1, y1, x2, y2: float) {.base.} = +method setPadding*(self: ControlRef, x1, y1, x2, y2: float) {.base, inline.} = ## Changes Control padding. - self.setPadding(Anchor(x1, y1, x2, y2)) + ## + ## Arguments: + ## - `x1` - left padding. + ## - `y1` - top padding. + ## - `x2` - right padding. + ## - `y2` - bottom padding. + ## + ## See also: + ## - `setPadding method <#setPadding.e,ControlRef,AnchorObj>`_ + self.padding = Anchor(x1, y1, x2, y2) method setStyle*(self: ControlRef, style: StyleSheetRef) {.base.} = + ## Changes control node style. + ## + ## Styles: + ## - `size-anchor` - `1`, `1.0`, `0.5 1` + ## - `position-anchor` - `1`, `0.5 1 0.5 1` + ## - `margin` - `8`, `2 4 6 8` + ## - `padding` - `8`, `2 4 6 8` self.background.setStyle(style) for i in style.dict: case i.key diff --git a/src/nodesnim/nodescontrol/counter.nim b/src/nodesnim/nodescontrol/counter.nim index 9242757e..6aeb5d7a 100644 --- a/src/nodesnim/nodescontrol/counter.nim +++ b/src/nodesnim/nodescontrol/counter.nim @@ -1,8 +1,8 @@ # author: Ethosa ## Number counter box. +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../core/vector2, ../core/rect2, @@ -10,6 +10,7 @@ import ../core/input, ../core/enums, ../core/color, + ../core/font, ../nodes/node, ../nodes/canvas, @@ -44,6 +45,7 @@ proc Counter*(name: string = "Counter"): CounterRef = result.label = Label() result.label.mousemode = MOUSEMODE_IGNORE result.label.parent = result + result.label.setTextAlign(0.1, 0.5, 0.1, 0.5) result.background.setColor(Color(0x212121ff)) result.kind = COUNTER_NODE @@ -66,14 +68,13 @@ method draw*(self: CounterRef, w, h: GLfloat) = self.background.draw(x, y, self.rect_size.x, self.rect_size.y) - self.label.calcGlobalPosition() - self.label.resize(self.rect_size.x - 40, self.rect_size.y) - self.label.setTextAlign(0.5, 0.5, 0.5, 0.5) if self.as_int: self.label.setText($self.value.int) else: self.label.setText($self.value) - self.label.draw(w, h) + self.label.text.renderTo( + Vector2(x + self.label.padding.x1, y - self.label.padding.y1), + self.rect_size, self.label.text_align) glColor4f(1f, 1f, 1f, 1f) @@ -102,7 +103,6 @@ method duplicate*(self: CounterRef): CounterRef {.base.} = method handle*(self: CounterRef, event: InputEvent, mouse_on: var NodeRef) = procCall self.ControlRef.handle(event, mouse_on) - let first_button = Rect2( self.global_position.x + self.rect_size.x - 20, self.global_position.y, @@ -129,10 +129,16 @@ method handle*(self: CounterRef, event: InputEvent, mouse_on: var NodeRef) = method setMaxValue*(self: CounterRef, value: float) {.base.} = ## Changes max value, if it more then current `value`. + ## + ## See also: + ## - `setMinValue method <#setMinValue.e,CounterRef,float>`_ if value > self.value: self.max_value = value method setMinValue*(self: CounterRef, value: float) {.base.} = ## Changes max value, if it less then current `value`. + ## + ## See also: + ## - `setMaxValue method <#setMaxValue.e,CounterRef,float>`_ if value < self.value: self.min_value = value diff --git a/src/nodesnim/nodescontrol/edittext.nim b/src/nodesnim/nodescontrol/edittext.nim index 0a74e0c0..1ff07cc4 100644 --- a/src/nodesnim/nodescontrol/edittext.nim +++ b/src/nodesnim/nodescontrol/edittext.nim @@ -1,7 +1,7 @@ # author: Ethosa +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../thirdparty/sdl2/ttf, ../core/font, @@ -122,7 +122,7 @@ template changeText(self, `text`, `save_properties`, t: untyped): untyped = for i in 0.. 0: self.moveCursorBy(-1) diff --git a/src/nodesnim/nodescontrol/label.nim b/src/nodesnim/nodescontrol/label.nim index 489e4b5b..44d0a00c 100644 --- a/src/nodesnim/nodescontrol/label.nim +++ b/src/nodesnim/nodescontrol/label.nim @@ -19,6 +19,7 @@ import ../nodes/node, ../nodes/canvas, + browsers, control @@ -59,14 +60,46 @@ method duplicate*(self: LabelRef): LabelRef {.base.} = self.deepCopy() method getText*(self: LabelRef): string {.base.} = + ## Returns `StyleText` as `string`. + ## + ## See also: + ## * `setText method <#setText.e,LabelRef,string,bool>`_ $self.text +method handle*(self: LabelRef, event: InputEvent, mouse_on: var NodeRef) = + ## Handles user input. Thi uses in the `window.nim`. + procCall self.ControlRef.handle(event, mouse_on) + + if event.kind in [MOUSE, MOTION]: + let (c, pos) = self.text.getCharUnderPoint( + self.getGlobalMousePosition(), self.global_position + self.rect_size/2 - self.text.getTextSize()/2, + self.text_align) + + if c.is_url: + var (i, j) = (pos.int, pos.int) + while i - 1 > 0 and self.text.chars[i - 1].is_url: + dec i + while j + 1 < self.text.len and self.text.chars[j + 1].is_url: + inc j + self.text.setUnderline(i, j, true) + self.text.rendered = false + if last_event.pressed and last_event.kind == MOUSE: + openDefaultBrowser(c.url) + else: + for i in self.text.chars: + if i.is_url: + i.setUnderline(false) + self.text.rendered = false + method setText*(self: LabelRef, text: string, save_properties: bool = false) {.base.} = ## Changes text. ## ## Arguments: ## - `text` is a new Label text. ## - `save_properties` - saves old text properties, if `true`. + ## + ## See also: + ## * `getText method <#getText.e,LabelRef>`_ var st = stext(text) if self.text.font.isNil(): self.text.font = standard_font @@ -76,25 +109,45 @@ method setText*(self: LabelRef, text: string, save_properties: bool = false) {.b for i in 0..`_ self.text_align = Anchor(x1, y1, x2, y2) self.text.rendered = false method setTextAlign*(self: LabelRef, align: AnchorObj) {.base.} = + ## Changes text alignment. + ## + ## See also: + ## * `setTextAlign method <#setTextAlign.e,LabelRef,float,float,float,float>`_ self.text_align = align self.text.rendered = false method setTextColor*(self: LabelRef, color: ColorRef) {.base.} = + ## Changes text color. + ## + ## Arguments: + ## - `color` - new text color. self.text.setColor(color) self.text.rendered = false method setTextFont*(self: LabelRef, font: FontPtr) {.base.} = + ## Changes text font. + ## + ## Arguments: + ## - `font` - new text font. self.text.font = font self.text.rendered = false diff --git a/src/nodesnim/nodescontrol/popup.nim b/src/nodesnim/nodescontrol/popup.nim index fc03bfcb..b95a8d07 100644 --- a/src/nodesnim/nodescontrol/popup.nim +++ b/src/nodesnim/nodescontrol/popup.nim @@ -1,5 +1,5 @@ # author: Ethosa -## By default popup visible is false. Popup, unlike other nodes, changes children visible when calling show() and hide(). +## By default popup visibility is false. Popup, unlike other nodes, changes children visibility when calling show() and hide(). import ../core/vector2, ../core/rect2, @@ -30,30 +30,36 @@ proc Popup*(name: string = "Popup"): PopupRef = result.background.setColor(Color(0x212121ff)) result.rect_size.x = 160 result.rect_size.y = 160 - result.visible = GONE + result.visibility = GONE result.kind = POPUP_NODE template recalc = for child in self.getChildIter(): - if child.visible != GONE and self.visible == GONE: - child.visible = GONE - elif child.visible != VISIBLE and self.visible == VISIBLE: - child.visible = VISIBLE + if child.visibility != GONE and self.visibility == GONE: + child.visibility = GONE + elif child.visibility != VISIBLE and self.visibility == VISIBLE: + child.visibility = VISIBLE method hide*(self: PopupRef) = ## Hides popup. {.warning[LockLevel]: off.} - self.visible = GONE + self.visibility = GONE recalc() method show*(self: PopupRef) = ## Shws popup. {.warning[LockLevel]: off.} - self.visible = VISIBLE + self.visibility = VISIBLE recalc() +method toggle*(self: PopupRef) {.base.} = + if self.visibility == GONE: + self.show() + else: + self.hide() + method calcPositionAnchor*(self: PopupRef) = ## This uses in the `scene.nim`. {.warning[LockLevel]: off.} diff --git a/src/nodesnim/nodescontrol/progress_bar.nim b/src/nodesnim/nodescontrol/progress_bar.nim index d374eb18..28ef86f9 100644 --- a/src/nodesnim/nodescontrol/progress_bar.nim +++ b/src/nodesnim/nodescontrol/progress_bar.nim @@ -9,6 +9,7 @@ import ../core/input, ../core/color, ../core/enums, + ../core/nodes_os, ../nodes/node, ../nodes/canvas, @@ -19,10 +20,6 @@ import const CIRCLE_STEP: float = TAU * 0.01 type - ProgressBarType* {.pure.} = enum - PROGRESS_BAR_HORIZONTAL, - PROGRESS_BAR_VERTICAL, - PROGRESS_BAR_CIRCLE ProgressBarObj* = object of ControlRef max_value*, value*: float progress_color*: ColorRef @@ -72,8 +69,8 @@ method draw*(self: ProgressBarRef, w, h: GLfloat) = else: self.indeterminate_val = -progress_width glRectf( - normalize(x + self.indeterminate_val, x, x + self.rect_size.x), y, - normalize(x + self.indeterminate_val + progress_width, x, x + self.rect_size.x), y - self.rect_size.y) + norm(x, x + self.rect_size.x, x + self.indeterminate_val), y, + norm(x, x + self.rect_size.x, x + self.indeterminate_val + progress_width), y - self.rect_size.y) else: glRectf(x, y, x + progress_width, y - self.rect_size.y) @@ -87,8 +84,8 @@ method draw*(self: ProgressBarRef, w, h: GLfloat) = else: self.indeterminate_val = -progress_width glRectf( - x, normalize(y - self.indeterminate_val, y - self.rect_size.y, y), - x + self.rect_size.x, normalize(y - self.indeterminate_val - progress_width, y - self.rect_size.y, y)) + x, norm(y - self.rect_size.y, y, y - self.indeterminate_val), + x + self.rect_size.x, norm(y - self.rect_size.y, y, y - self.indeterminate_val - progress_width)) else: glRectf(x, y, x + self.rect_size.x, y - progress_width) diff --git a/src/nodesnim/nodescontrol/scroll.nim b/src/nodesnim/nodescontrol/scroll.nim index 7bedad32..e8dd8118 100644 --- a/src/nodesnim/nodescontrol/scroll.nim +++ b/src/nodesnim/nodescontrol/scroll.nim @@ -1,8 +1,8 @@ # author: Ethosa ## It provides primitive scroll box. +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../core/vector2, ../core/rect2, @@ -68,16 +68,6 @@ method duplicate*(self: ScrollRef): ScrollRef {.base.} = self.deepCopy() -method resize*(canvas: ScrollRef, w, h: GLfloat, save_anchor: bool = false) = - ## Resizes scroll. - ## - ## Arguments: - ## - `w` is a new width. - ## - `h` is a new height. - canvas.rect_size.x = w - canvas.rect_size.y = h - - method draw*(self: ScrollRef, w, h: GLfloat) = ## This uses in the `window.nim`. let @@ -150,10 +140,10 @@ method handle*(self: ScrollRef, event: InputEvent, mouse_on: var NodeRef) = if mouse_in: # Keyboard movement if event.kind == KEYBOARD: - if event.key_cint in pressed_keys_cints: # Special chars - if event.key_cint == K_UP: + if event.key_int in pressed_keys_cint: # Special chars + if event.key_int == K_UP: self.scrollBy(0, -40) - elif event.key_cint == K_DOWN: + elif event.key_int == K_DOWN: self.scrollBy(0, 40) elif event.kind == WHEEL: self.scrollBy(0, -20 * event.yrel) diff --git a/src/nodesnim/nodescontrol/slider.nim b/src/nodesnim/nodescontrol/slider.nim index 743c9c34..04302521 100644 --- a/src/nodesnim/nodescontrol/slider.nim +++ b/src/nodesnim/nodescontrol/slider.nim @@ -16,9 +16,6 @@ import type - SliderType*{.pure, size: sizeof(int8).} = enum - SLIDER_HORIZONTAL, - SLIDER_VERTICAL SliderObj* = object of ControlRef slider_type*: SliderType max_value*, value*: uint @@ -76,7 +73,7 @@ method draw*(self: SliderRef, w, h: GLfloat) = glRectf(x, y - self.rect_size.y + progress, x + self.rect_size.x, y - self.rect_size.y) # Thumb - self.thumb.draw(x, progress-10, self.rect_size.x, 10) + self.thumb.draw(x, y-self.rect_size.y+progress, self.rect_size.x, 10) # Press if self.pressed: diff --git a/src/nodesnim/nodescontrol/subwindow.nim b/src/nodesnim/nodescontrol/subwindow.nim index 48f8661a..dba82761 100644 --- a/src/nodesnim/nodescontrol/subwindow.nim +++ b/src/nodesnim/nodescontrol/subwindow.nim @@ -1,8 +1,8 @@ # author: Ethosa ## Extended version of SubWindow node. +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../core/vector2, ../core/rect2, @@ -48,7 +48,7 @@ proc SubWindow*(name: string = "SubWindow"): SubWindowRef = result.background.setBorderWidth(1) result.rect_size.x = 320 result.rect_size.y = 220 - result.visible = GONE + result.visibility = GONE result.title = Label("Title") result.title.setText("Title") result.title.parent = result @@ -83,15 +83,15 @@ method draw*(self: SubWindowRef, w, h: GLfloat) = for child in self.getChildIter(): child.CanvasRef.calcGlobalPosition() if child.CanvasRef.global_position.x > self.global_position.x + self.rect_size.x: - child.visible = GONE + child.visibility = GONE elif child.CanvasRef.global_position.y > self.global_position.y + self.rect_size.y: - child.visible = GONE + child.visibility = GONE elif child.CanvasRef.global_position.x + child.CanvasRef.rect_size.x < self.global_position.x: - child.visible = GONE + child.visibility = GONE elif child.CanvasRef.global_position.y + child.CanvasRef.rect_size.y < self.global_position.y: - child.visible = GONE + child.visibility = GONE else: - child.visible = VISIBLE + child.visibility = VISIBLE self.title_bar.draw(x, y, self.rect_size.x, 32) diff --git a/src/nodesnim/nodescontrol/texture_button.nim b/src/nodesnim/nodescontrol/texture_button.nim index 6e58a5b8..31c73afe 100644 --- a/src/nodesnim/nodescontrol/texture_button.nim +++ b/src/nodesnim/nodescontrol/texture_button.nim @@ -1,8 +1,8 @@ # author: Ethosa ## It is the convenient alternative of the Button node. +import ../thirdparty/sdl2 except Color import ../thirdparty/opengl, - ../thirdparty/sdl2, ../core/vector2, ../core/rect2, @@ -68,6 +68,7 @@ method draw*(self: TextureButtonRef, w, h: GLfloat) = # Texture if texture.texture > 0'u32: + glColor4f(0.8, 0.8, 0.8, 1f) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, texture.texture) diff --git a/src/nodesnim/nodescontrol/texture_progress_bar.nim b/src/nodesnim/nodescontrol/texture_progress_bar.nim index c29980c6..121c2539 100644 --- a/src/nodesnim/nodescontrol/texture_progress_bar.nim +++ b/src/nodesnim/nodescontrol/texture_progress_bar.nim @@ -69,6 +69,7 @@ method draw*(self: TextureProgressBarRef, w, h: GLfloat) = glVertex2f(x + self.rect_size.x, y) glEnd() + glBindTexture(GL_TEXTURE_2D, 0) glDisable(GL_TEXTURE_2D) if self.progress_texture.texture > 0'u32: @@ -86,6 +87,7 @@ method draw*(self: TextureProgressBarRef, w, h: GLfloat) = glVertex2f(x + progress, y) glEnd() + glBindTexture(GL_TEXTURE_2D, 0) glDisable(GL_TEXTURE_2D) # Press diff --git a/src/nodesnim/nodescontrol/tooltip.nim b/src/nodesnim/nodescontrol/tooltip.nim new file mode 100644 index 00000000..1aba8859 --- /dev/null +++ b/src/nodesnim/nodescontrol/tooltip.nim @@ -0,0 +1,77 @@ +# author: Ethosa +import + ../thirdparty/opengl, + + ../core/vector2, + ../core/font, + ../core/enums, + ../core/color, + ../core/anchor, + + ../nodes/node, + ../nodes/canvas, + + ../graphics/drawable, + + ../window, + + control + + +type + ToolTipObj* = object of ControlObj + text*: StyleText + ToolTipRef* = ref ToolTipObj + +const TOOLTIP_SPACE: float = 32f + + +proc ToolTip*(name: string = "ToolTip", + tooltip: string = "Tooltip"): ToolTipRef = + nodepattern(ToolTipRef) + controlpattern() + result.text = stext(tooltip) + result.background.setColor(Color("#444")) + result.background.setBorderColor(Color("#555")) + result.background.setBorderWidth(0.5) + result.background.setCornerRadius(4) + result.background.setCornerDetail(4) + result.background.enableShadow(true) + result.background.setShadowOffset(Vector2(0, 5)) + result.mousemode = MOUSEMODE_IGNORE + result.hide() + + +method postdraw*(self: ToolTipRef, w, h: GLfloat) = + {.warning[LockLevel]: off.} + procCall self.ControlRef.draw(w, h) + let + x = -w/2 + self.global_position.x + y = h/2 - self.global_position.y + + self.text.renderTo(Vector2(x, y), self.rect_size, Anchor(0, 0, 0, 0)) + +method showAt*(self: ToolTipRef, x, y: float) {.base.} = + self.moveTo(x, y) + self.rect_min_size = self.text.getTextSize() + self.resize(self.rect_size.x, self.rect_size.y, true) + self.show() + +method showAtMouse*(self: ToolTipRef) {.base.} = + let + pos = self.getGlobalMousePosition() + textsize = self.text.getTextSize() + windowsize = getWindowSize() + self.moveTo(pos) + + if pos.x + TOOLTIP_SPACE + textsize.x > windowsize.x: + self.move(-TOOLTIP_SPACE - textsize.x, 0) + + if pos.y - TOOLTIP_SPACE - textsize.y < 0: + self.move(0, TOOLTIP_SPACE) + else: + self.move(0, -TOOLTIP_SPACE) + + self.rect_min_size = self.text.getTextSize() + self.resize(self.rect_size.x, self.rect_size.y, true) + self.show() diff --git a/src/nodesnim/runtime/scripts.nim b/src/nodesnim/runtime/scripts.nim new file mode 100644 index 00000000..a6aa36d2 --- /dev/null +++ b/src/nodesnim/runtime/scripts.nim @@ -0,0 +1,126 @@ +# author: Ethosa +## It provides runtime scripts loader. +import + compiler / [ + astalgo, ast, vmdef, vm, options, + modulegraphs, idents, modules, + pathutils, llstream, passes, sem, + condsyms + ], + + ../core/exceptions, + ../core/vector2, + scripts_impl, + + os + + +var + ident_cache = newIdentCache() + cfg = newConfigRef() + +once: + let std = AbsoluteDir(getHomeDir() / ".nimble" / "lib") + # Search .nimble/lib folder 👀 + cfg.libpath = std + cfg.searchPaths.add(cfg.libpath) + cfg.searchPaths.add(AbsoluteDir($std / "pure")) + cfg.searchPaths.add(AbsoluteDir($std / "pure" / "collections")) + cfg.searchPaths.add(AbsoluteDir($std / "core")) + cfg.searchPaths.add(AbsoluteDir($std / "impure")) + cfg.searchPaths.add(AbsoluteDir($std / "std")) + cfg.implicitIncludes.add(getCurrentDir() / "scripts_api.nim") + + +# --- Convert default Nim types to PNode --- # +converter toNode*(x: float): PNode = newFloatNode(nkFloatLit, x) +converter toNode*(x: int): PNode = newIntNode(nkIntLit, x) +converter toNode*(x: string): PNode = newStrNode(nkStrLit, x) +converter toNode*(x: bool): PNode = x.ord.toNode() +converter toNode*(x: enum): PNode = x.ord.toNode() + +converter toNode*(x: openarray[int|float|string|bool|enum]): PNode = + result = newNode(nkBracket) + result.sons.initialize(x.len) + for i in x.low..x.high: + result[i] = x[i].toNode() + +converter toNode*(x: tuple | object): PNode = + result = newTree(nkPar) + for field in x.fields: + result.sons.add(field.toNode()) + +converter toNode*(x: ref tuple | ref object): PNode = + result = newTree(nkPar) + if x.isNil(): + return result + for field in x.fields: + result.sons.add(field.toNode()) + + +proc setupModule(self: CompiledScript) = + self.graph.connectCallbacks() + initDefines(cfg.symbols) + defineSymbol(cfg.symbols, "nimscript") + defineSymbol(cfg.symbols, "nimconfig") + self.graph.registerPass(semPass) + self.graph.registerPass(evalPass) + + +proc cleanupModule(self: CompiledScript) = + initDefines(cfg.symbols) + undefSymbol(cfg.symbols, "nimscript") + undefSymbol(cfg.symbols, "nimconfig") + clearPasses(self.graph) + + +proc compileScript*(file: string): CompiledScript = + ## Compiles script with std module. + new result + result.filename = file + result.graph = newModuleGraph(ident_cache, cfg) + result.setupModule() + result.module = makeModule(result.graph, file) + + # Create context + incl(result.module.flags, sfMainModule) + result.pctx = newCtx(result.module, identCache, result.graph) + result.pctx.mode = emRepl + + # Setup context + setupGlobalCtx(result.module, result.graph) + registerAdditionalOps(result.pctx) + + # Compile std + compileSystemModule(result.graph) + + # Compile module + if not processModule(result.graph, result.module, llStreamOpen(AbsoluteFile(file), fmRead)): + raise newException(VMError, "Failed to process `" & file & "`") + + # Cleanup + setupGlobalCtx(nil, result.graph) + result.cleanupModule() + + +proc getProc*(self: CompiledScript, routine: string): PSym = + strTableGet(self.module.tab, getIdent(identCache, routine)) + +proc hasProc*(self: CompiledScript, routine: string): bool = + ## Returns true, if `routine` available. + not self.getProc(routine).isNil() + + +proc call*(self: CompiledScript, routine: string, + args: varargs[PNode]): PNode {.discardable.} = + ## Calls routine by name + # Setup context + setupGlobalCtx(self.module, self.graph) + + # Find routine + let prc = self.getProc(routine) + if prc.isNil(): + raise newException(VMError, "\nUnable to locate proc `" & routine & "` in `" & self.filename & "`") + + # Call routine + result = execProc(self.pctx, prc, args) diff --git a/src/nodesnim/runtime/scripts_api.nim b/src/nodesnim/runtime/scripts_api.nim new file mode 100644 index 00000000..3f39c1ed --- /dev/null +++ b/src/nodesnim/runtime/scripts_api.nim @@ -0,0 +1,2 @@ +# author: Ethosa +## Built-ins for writing scripts. diff --git a/src/nodesnim/runtime/scripts_impl.nim b/src/nodesnim/runtime/scripts_impl.nim new file mode 100644 index 00000000..3322f0af --- /dev/null +++ b/src/nodesnim/runtime/scripts_impl.nim @@ -0,0 +1,24 @@ +import + compiler / [vmdef, modulegraphs, vm, ast] + + +type + CompiledScript* = ref object + pctx*: PCtx + graph*: ModuleGraph + module*: PSym + filename*: string + + +proc exposeScriptApi* (self: CompiledScript) = + template expose (routine, body: untyped) {.dirty.} = + self.pctx.registerCallback self.filename & "." & astToStr(routine), + proc (a: VmArgs) = + body + + expose add: + # We need to use procs like getInt to retrieve the argument values from VmArgs + # Instead of using the return statement we need to use setResult + setResult(a, + getInt(a, 0) + + getInt(a, 1)) diff --git a/src/nodesnim/window.nim b/src/nodesnim/window.nim index a3ac028c..a4a1e702 100644 --- a/src/nodesnim/window.nim +++ b/src/nodesnim/window.nim @@ -1,13 +1,15 @@ # author: Ethosa +import thirdparty/sdl2 except Color import thirdparty/opengl, thirdparty/opengl/glu, - thirdparty/sdl2, thirdparty/sdl2/image, core/color, core/input, core/exceptions, + core/vector2, + core/enums, nodes/node, nodes/scene, @@ -112,13 +114,10 @@ proc keyboardpress(c: cint) {.cdecl.} = ## Called when press any key on keyboard. if c < 0: return - var key = $c check(InputEventKeyboard, last_event.pressed, true) - last_event.key = key last_event.key_int = c - if key notin pressed_keys: - pressed_keys.add(key) - pressed_keys_ints.add(c) + if c notin pressed_keys_cint: + pressed_keys_cint.add(c) last_event.kind = KEYBOARD last_key_state = key_state key_state = true @@ -138,28 +137,23 @@ proc keyboardup(c: cint) {.cdecl.} = ## Called when any key no more pressed. if c < 0: return - let key = $c check(InputEventKeyboard, false, false) - last_event.key = key last_event.key_int = c last_event.kind = KEYBOARD last_key_state = key_state key_state = false - var i = 0 - for k in pressed_keys: - if k == key: - pressed_keys.delete(i) - pressed_keys_ints.delete(i) + for i in pressed_keys_cint.low..pressed_keys_cint.high: + if pressed_keys_cint[i] == c: + pressed_keys_cint.delete(i) break - inc i current_scene.handleScene(last_event, mouse_on, paused) -proc motion(x, y: cint) {.cdecl.} = +proc motion(x, y, xrel, yrel: cint) {.cdecl.} = ## Called on any mouse motion. last_event.kind = MOTION - last_event.xrel = last_event.x - x.float - last_event.yrel = last_event.y - y.float + last_event.xrel = xrel.float + last_event.yrel = yrel.float last_event.x = x.float last_event.y = y.float @@ -189,6 +183,7 @@ proc addMainScene*(scene: SceneRef) = main_scene = scene proc centeredWindow* = + ## Moves window to the display center. var dm: DisplayMode discard getCurrentDisplayMode(0, dm) windowptr.setPosition((dm.w/2 - width/2).cint, (dm.h/2 - height/2).cint) @@ -214,7 +209,18 @@ proc changeScene*(name: string, extra: seq[tuple[k: string, v: string]] = @[]): when defined(debug): debug("result of `changeScene` is ", result) +proc getSceneByName*(name: string): SceneRef = + for scene in scenes: + if scene.name == name: + return scene + +proc getWindowSize*(): Vector2Obj = + Vector2(width.float, height.float) + proc resizeWindow*(x, y: cint) = + ## Resizes window. + width = x + height = y windowptr.setSize(x, y) proc setMainScene*(name: string) = @@ -232,14 +238,14 @@ proc setTitle*(title: cstring) = if window_created: windowptr.setTitle(title) else: - raise newException(WindowError, "Window not launched!") + throwError(WindowError, "Window not launched!") proc setIcon*(icon_path: cstring) = ## Changes window title. if window_created: windowptr.setIcon(image.load(icon_path)) else: - raise newException(WindowError, "Window not launched!") + throwError(WindowError, "Window not launched!") proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} = ## Creates a new window pointer @@ -248,13 +254,14 @@ proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} = ## - `title` - window title. # Set up window. once: - when not defined(android) and not defined(ios): + when not defined(android) and not defined(ios) and not defined(useGlew): loadExtensions() # Load OpenGL extensions. discard captureMouse(True32) windowptr = createWindow( title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, SDL_WINDOW_SHOWN or SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE or - SDL_WINDOW_ALLOW_HIGHDPI or SDL_WINDOW_FOREIGN or SDL_WINDOW_INPUT_FOCUS or SDL_WINDOW_MOUSE_FOCUS) + SDL_WINDOW_ALLOW_HIGHDPI or SDL_WINDOW_FOREIGN or + SDL_WINDOW_INPUT_FOCUS or SDL_WINDOW_MOUSE_FOCUS) glcontext = windowptr.glCreateContext() # Set up OpenGL @@ -280,8 +287,8 @@ proc onReshape(userdata: pointer; event: ptr Event): Bool32 {.cdecl.} = False32 proc windowLaunch* = - if main_scene == nil: - raise newException(SceneError, "Main scene is not indicated!") + if main_scene.isNil(): + throwError(SceneError, "Main scene is not indicated!") changeScene(main_scene.name) when defined(debug): info("window launched") @@ -304,7 +311,7 @@ proc windowLaunch* = textinput(e) of MouseMotion: let e = evMouseMotion(event) - motion(e.x, e.y) + motion(e.x, e.y, e.xrel, e.yrel) of MouseButtonDown: let e = evMouseButton(event) mouse(e.button.cint, e.x, e.y, true) diff --git a/tests/README.md b/tests/README.md index cb20f083..b7fd556d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,52 +1,23 @@

Tests

-- [Create a window and set up the main scene.](https://github.com/Ethosa/nodesnim/blob/master/tests/test1.nim) -- [Use Canvas node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test2.nim) -- [Window events handling.](https://github.com/Ethosa/nodesnim/blob/master/tests/test3.nim) -- [Use ColorRect node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test4.nim) -- [Handle Control node events.](https://github.com/Ethosa/nodesnim/blob/master/tests/test5.nim) -- [Anchor setting.](https://github.com/Ethosa/nodesnim/blob/master/tests/test6.nim) -- [Change scenes.](https://github.com/Ethosa/nodesnim/blob/master/tests/test7.nim) -- [Use TextureRect node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test8.nim) -- [Use Label node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test9.nim) -- [Use Button node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test10.nim) -- [Environment setting.](https://github.com/Ethosa/nodesnim/blob/master/tests/test11.nim) -- [Use Box node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test12.nim) -- [Use HBox node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test13.nim) -- [Use VBox node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test14.nim) -- [Use GridBox node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test15.nim) -- [Use EditText node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test16.nim) -- [Duplicate nodes.](https://github.com/Ethosa/nodesnim/blob/master/tests/test17.nim) -- [Use Scroll node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test18.nim) -- [Use ProgressBar node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test19.nim) -- [Use Slider node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test20.nim) -- [Use Popup node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test21.nim) -- [Use AudioStreamPlayer node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test22.nim) -- [Use Node2D node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test23.nim) -- [Use Sprite node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test24.nim) -- [Use TextureButton node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test25.nim) -- [Use AnimatedSprite node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test26.nim) -- [Use TextureProgressBar node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test27.nim) -- [Use YSort node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test28.nim) -- [Use Counter node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test29.nim) -- [Use CollisionShape2D node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test30.nim) -- [Use KinematicBody2D node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test31.nim) -- [Use Switch node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test32.nim) -- [Event handlers with macros.](https://github.com/Ethosa/nodesnim/blob/master/tests/test33.nim) -- [Rotation.](https://github.com/Ethosa/nodesnim/blob/master/tests/test34.nim) -- [Use Camera2D node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test35.nim) -- [Use Node3D.](https://github.com/Ethosa/nodesnim/blob/master/tests/test36.nim) -- [Use GeometryInstance node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test37.nim) -- [Use SubWindow node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test38.nim) -- [Use Scene builder.](https://github.com/Ethosa/nodesnim/blob/master/tests/test39.nim) -- [Use AnimationPlayer node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test40.nim) -- [Use StyleSheet object.](https://github.com/Ethosa/nodesnim/blob/master/tests/test41.nim) -- [Use Drawable and Control.](https://github.com/Ethosa/nodesnim/blob/master/tests/test42.nim) -- [Use CheckBox node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test43.nim) -- [Use GradientDrawable and Control.](https://github.com/Ethosa/nodesnim/blob/master/tests/test44.nim) -- [Use TileMap node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test45.nim) -- [Use padding.](https://github.com/Ethosa/nodesnim/blob/master/tests/test46.nim) -- [Use margin.](https://github.com/Ethosa/nodesnim/blob/master/tests/test47.nim) -- [Use Camera3D node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test48.nim) -- [Use TileMap Isometric mode](https://github.com/Ethosa/nodesnim/blob/master/tests/test49.nim) -- [Make your own node](https://github.com/Ethosa/nodesnim/blob/master/tests/test50.nim) +- [Work with window](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test1.nim) + + [![test1](https://user-images.githubusercontent.com/49402667/136655791-7a2f8b93-a76a-42cf-bada-ee8cb23df658.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test1.nim) +- [Work with default nodes](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test2.nim) + + [![test2](https://user-images.githubusercontent.com/49402667/136655905-bb1d4e83-ff2f-49a9-908d-45877a2be706.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test2.nim) +- [Work with Control nodes](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test3.nim) + + [![test3](https://user-images.githubusercontent.com/49402667/136655975-ceb7cf35-7400-42cf-bb78-07f0e355ad99.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test3.nim) +- [Work with core](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test4.nim) + +- [Work with graphics](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test5.nim) + + [![test5](https://user-images.githubusercontent.com/49402667/136656020-dd29843d-faad-481a-a431-dd252cc91eff.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test5.nim) +- [Work with 2D nodes.](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test6.nim) + + [![test6](https://user-images.githubusercontent.com/49402667/136656056-f87ce658-131b-490b-b975-85f143a14b8e.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test6.nim) +- [Work with 3D nodes.](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test7.nim) + + [![test7](https://user-images.githubusercontent.com/49402667/136656082-d09dd623-40f7-4a01-99d2-407d2055a1bf.png)](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test7.nim) +- [Make your own node.](https://github.com/Ethosa/nodesnim/blob/nightly/tests/test8.nim) diff --git a/tests/assets/canvas.png b/tests/assets/canvas.png index 11a2ebab..a177206b 100644 Binary files a/tests/assets/canvas.png and b/tests/assets/canvas.png differ diff --git a/tests/assets/tilesets/foreground.png b/tests/assets/tilesets/foreground.png deleted file mode 100644 index 6fab88f8..00000000 Binary files a/tests/assets/tilesets/foreground.png and /dev/null differ diff --git a/tests/assets/tilesets/isometric_desert.png b/tests/assets/tilesets/isometric_desert.png index 3e11c6e9..b6b87fb5 100644 Binary files a/tests/assets/tilesets/isometric_desert.png and b/tests/assets/tilesets/isometric_desert.png differ diff --git a/tests/test1.nim b/tests/test1.nim index 4e859fbf..030fb4a6 100644 --- a/tests/test1.nim +++ b/tests/test1.nim @@ -1,16 +1,44 @@ -# --- Test 1. Create a window and set up the main scene. --- # -import nodesnim +# --- Test 1. Work with Window.. --- # +import + nodesnim, + unittest -Window( - "hello world", # Window name - 640, # Window width, - 360 # Window height -) +suite "Work with Window": -var main = Scene("Main") # Create a new Scene object. + test "Create window": + # Window(title, width, height) + Window(title = "MyWindow", + w = 720, h = 480) + test "Change Window title": + setTitle("My own window ^^") -addScene(main) # Add new scene in window. -setMainScene("Main") # Set main scene. -windowLaunch() # Start main loop. + test "Change Window icon": + setIcon("assets/sharp.jpg") + + test "Resize and centered window": + resizeWindow(1024, 640) + centeredWindow() + + test "Setup environment": + env.color = Color(1, 0.6, 1) # window background color. + env.delay = 1000 div 120 # 120 frames per second. + + test "Setup window": + build: # Node builder + - Scene main # Create an empty Scene node with the name "main". + + test "Register events": + addKeyAction("forward", "w") + addKeyAction("backward", "s") + + test "Handle events": + main@onProcess(self): + if isActionJustPressed("forward"): + echo "forward pressed!" + elif isActionJustPressed("backward"): + echo "backward pressed!" + + addMainScene(main) # Adds scene to window and + windowLaunch() diff --git a/tests/test10.nim b/tests/test10.nim deleted file mode 100644 index 6d354040..00000000 --- a/tests/test10.nim +++ /dev/null @@ -1,25 +0,0 @@ -# --- Test 10. Use Button node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - button = Button() - -main.addChild(button) - -button.setText("Press me!") -button.resize(256, 64) -button.setAnchor(0.5, 0.5, 0.5, 0.5) - -button.on_touch = - proc(self: ButtonRef, x, y: float) = # This called when user clicks on the button - button.setText("Clicked in " & $x & ", " & $y & " position.") - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test11.nim b/tests/test11.nim deleted file mode 100644 index e1dfaf53..00000000 --- a/tests/test11.nim +++ /dev/null @@ -1,16 +0,0 @@ -# --- Test 11. Environment setting. --- # -import nodesnim - - -Window("hello world") - -var main = Scene("Main") - -env.color = Color(1, 0.6, 1) # window background color. -env.brightness = 0.5 -env.delay = 1000 div 30 # 30 frames per second. - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test12.nim b/tests/test12.nim deleted file mode 100644 index 817a53c1..00000000 --- a/tests/test12.nim +++ /dev/null @@ -1,38 +0,0 @@ -# --- Test 12. Use Box node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - box = Box() # Create the BoxObj. - - red = ColorRect() # #ff6699 - pink = ColorRect() #ff64ff - orange = ColorRect() # #ffaa00 - - -red.color = Color(0xff6699ff'u32) -pink.color = Color(0xff64ffff'u32) -orange.color = Color(0xffaa00ff'u32) - -red.resize(128, 128) -pink.resize(64, 64) -orange.resize(32, 32) - -# Add rects in the Box node. -box.addChild(red) -box.addChild(pink) -box.addChild(orange) - -main.addChild(box) -box.setAnchor(0, 0.5, 0, 0.5) # Box anchor in the scene. - -# Box node keeps child nodes in the Box center. - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test13.nim b/tests/test13.nim deleted file mode 100644 index 74c212ed..00000000 --- a/tests/test13.nim +++ /dev/null @@ -1,36 +0,0 @@ -# --- Test 13. Use HBox node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - hbox = HBox() # Create the HBoxObj. - - red = ColorRect() # #ff6699 - pink = ColorRect() #ff64ff - orange = ColorRect() # #ffaa00 - - -red.color = Color(0xff6699ff'u32) -pink.color = Color(0xff64ffff'u32) -orange.color = Color(0xffaa00ff'u32) - -red.resize(128, 128) -pink.resize(64, 64) -orange.resize(32, 32) - -# Add rects in the Box node. -hbox.addChild(red) -hbox.addChild(pink) -hbox.addChild(orange) - -main.addChild(hbox) -hbox.setAnchor(0, 0.5, 0, 0.5) # Box anchor in the scene. - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test14.nim b/tests/test14.nim deleted file mode 100644 index f99eec30..00000000 --- a/tests/test14.nim +++ /dev/null @@ -1,38 +0,0 @@ -# --- Test 14. Use VBox node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - vbox = VBox() # Create the VBoxObj. - - red = ColorRect() # #ff6699 - pink = ColorRect() #ff64ff - orange = ColorRect() # #ffaa00 - - -red.color = Color(0xff6699ff'u32) -pink.color = Color(0xff64ffff'u32) -orange.color = Color(0xffaa00ff'u32) - -red.resize(128, 128) -pink.resize(64, 64) -orange.resize(32, 32) - -# Add rects in the Box node. -vbox.addChild(red) -vbox.addChild(pink) -vbox.addChild(orange) - -main.addChild(vbox) -vbox.setAnchor(0, 0.5, 0, 0.5) # Box anchor in the scene. -vbox.setChildAnchor(0, 1, 0, 1) -vbox.setSizeAnchor(1, 1) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test15.nim b/tests/test15.nim deleted file mode 100644 index db4c4cdc..00000000 --- a/tests/test15.nim +++ /dev/null @@ -1,49 +0,0 @@ -# --- Test 15. Use GridBox node. --- # -import nodesnim - - -Window("hello world") -var - main = Scene("Main") - - grid_box = GridBox() # Create the GridBoxObj. - - red = ColorRect() # #ff6699 - pink = ColorRect() #ff64ff - orange = ColorRect() # #ffaa00 - mango = ColorRect() # #ffcc33 - yellow = ColorRect() # #ffcc66 - red2 = ColorRect() # #ff6655 - - -red.color = Color(0xff6699ff'u32) -pink.color = Color(0xff64ffff'u32) -orange.color = Color(0xffaa00ff'u32) -mango.color = Color(0xffcc33ff'u32) -yellow.color = Color(0xffcc66ff'u32) -red2.color = Color(0xff6655ff'u32) - -red.resize(128, 128) -pink.resize(128, 128) -orange.resize(128, 128) -mango.resize(128, 128) -yellow.resize(128, 128) -red2.resize(128, 128) - -# Add rects in the Box node. -grid_box.addChild(red) -grid_box.addChild(pink) -grid_box.addChild(orange) -grid_box.addChild(mango) -grid_box.addChild(yellow) -grid_box.addChild(red2) - -main.addChild(grid_box) -grid_box.setAnchor(0.5, 0.5, 0.5, 0.5) -grid_box.setRow(3) -grid_box.setSeparator(16) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test16.nim b/tests/test16.nim deleted file mode 100644 index f9258ca6..00000000 --- a/tests/test16.nim +++ /dev/null @@ -1,22 +0,0 @@ -# --- Test 16. Use EditText node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - edittext = EditText() - -main.addChild(edittext) - -edittext.resize(512, 256) -edittext.setBackgroundColor(Color(0x212121ff)) -edittext.setTextAlign(0.5, 0.5, 0.5, 0.5) -edittext.setAnchor(0.5, 0.5, 0.5, 0.5) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test17.nim b/tests/test17.nim deleted file mode 100644 index e77ec092..00000000 --- a/tests/test17.nim +++ /dev/null @@ -1,23 +0,0 @@ -# --- Test 17. Duplicate nodes. --- # -import nodesnim - -var - node1 = Node("Node1") - node2 = node1.duplicate() - - control1 = Control("Control1") - control2 = control1.duplicate() - -node2.name = "Node2" - -echo node1.name -echo node2.name - -control2.name = "Control2" -control2.rect_size = Vector2(100, 100) - -echo control1.name -echo control2.name - -echo control1.rect_size -echo control2.rect_size diff --git a/tests/test18.nim b/tests/test18.nim deleted file mode 100644 index f8f35a67..00000000 --- a/tests/test18.nim +++ /dev/null @@ -1,52 +0,0 @@ -# --- Test 18. Use Scroll node. --- # -import nodesnim - - -Window("scroll") - -var - main = Scene("Main") - - scroll = Scroll() - - grid_box = GridBox() - red = ColorRect() # #ff6699 - pink = ColorRect() #ff64ff - orange = ColorRect() # #ffaa00 - mango = ColorRect() # #ffcc33 - yellow = ColorRect() # #ffcc66 - red2 = ColorRect() # #ff6655 - - -red.color = Color(0xff6699ff'u32) -pink.color = Color(0xff64ffff'u32) -orange.color = Color(0xffaa00ff'u32) -mango.color = Color(0xffcc33ff'u32) -yellow.color = Color(0xffcc66ff'u32) -red2.color = Color(0xff6655ff'u32) - -red.resize(150, 150) -pink.resize(50, 50) -orange.resize(50, 50) -mango.resize(50, 50) -yellow.resize(50, 50) -red2.resize(150, 150) - -# Add rects in the Box node. -grid_box.addChild(red) -grid_box.addChild(pink) -grid_box.addChild(orange) -grid_box.addChild(mango) -grid_box.addChild(yellow) -grid_box.addChild(red2) - -main.addChild(scroll) -grid_box.setAnchor(0.5, 0.5, 0.5, 0.5) -grid_box.setRow(3) -grid_box.setSeparator(2) -scroll.addChild(grid_box) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test19.nim b/tests/test19.nim deleted file mode 100644 index c34c826a..00000000 --- a/tests/test19.nim +++ /dev/null @@ -1,39 +0,0 @@ -# --- Test 19. Use ProgressBar node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - progressbar = ProgressBar() - vprogressbar = ProgressBar() - cprogressbar = ProgressBar() - -vprogressbar.progress_type = PROGRESS_BAR_VERTICAL -cprogressbar.progress_type = PROGRESS_BAR_CIRCLE - - -main.addChild(progressbar) -main.addChild(vprogressbar) -main.addChild(cprogressbar) - -progressbar.setProgress(50) # default max progress value is 100. -progressbar.setMaxValue(150) - -vprogressbar.setProgress(2) # default max progress value is 100. -vprogressbar.setMaxValue(5) -vprogressbar.move(0, 64) -vprogressbar.resize(20, 80) - -cprogressbar.move(64, 64) -cprogressbar.resize(80, 80) -cprogressbar.indeterminate = true -cprogressbar.setMaxValue(15) -cprogressbar.setProgress(5) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test2.nim b/tests/test2.nim index 8c78b5a0..ad1ff399 100644 --- a/tests/test2.nim +++ b/tests/test2.nim @@ -1,29 +1,80 @@ -# --- Test 2. Use Canvas node. --- # -import nodesnim +# --- Test 2. Work with default nodes. --- # +import + nodesnim, + unittest -Window("hello world") +suite "Work with default nodes.": + + test "Setup window": + Window("default nodes test") -var - main = Scene("Main") + test "Setup scene": + build: + - Scene main + addMainScene(main) - canvas = Canvas() + test "Canvas test": + build: + - Canvas canvas: + call resize(256, 256) + call fill(Color("#ffaacc")) + call point(5, 5, Color("#64ffff")) + call line(8, 16, 128, 64, Color("#ffff64ff")) + call circle(0, 240, 32, Color("#aaff6456")) + call line(200, -150, 0, 256, Color("#0e1317ff")) + call bezier(0, 0, 256, 0, 256, 256, Color("#227")) + call cubic_bezier(0, 0, 256, 0, 0, 256, 256, 256, Color("#272")) + call move(74.4, 89.4) + call text("hello!,\nworld!", 64, 64, Vector2(1, 1)) + call saveAs("assets/canvas.png") # save result in file. + getSceneByName("main").addChild(canvas) -main.addChild(canvas) + test "AudioStreamPlayer test": + build: + - AudioStreamPlayer audio1: + stream: loadAudio("assets/vug_ost_Weh.ogg") + call setVolume(64) # 64/100 + call play() + getSceneByName("main").addChild(audio1) -canvas.resize(256, 256) -canvas.fill(Color(0xffaaccff'u32)) -canvas.point(5, 5, Color("#64ffffff")) -canvas.line(8, 16, 128, 64, Color("#ffff64ff")) -canvas.circle(0, 240, 32, Color("#aaff6456")) -canvas.line(200, -150, 0, 256, Color("#0e1317ff")) -canvas.bezier(0, 0, 256, 0, 256, 256, Color("#227")) -canvas.cubic_bezier(0, 0, 256, 0, 0, 256, 256, 256, Color("#272")) -canvas.move(74.4, 89.4) -canvas.text("hello!,\nworld!", 64, 64, Vector2(1, 1)) -canvas.saveAs("assets/canvas.png") # save result in file. + test "Duplicate nodes": + build: + - Node node(name: "MyOwnNode") + var node1 = node.duplicate() + assert node.name == node1.name + test "AnimationPlayer node": + build: + - ColorRect rect: + color: Color(0, 0, 0) + call resize(100, 100) + - ColorRect rect1: + color: Color(0, 0, 0) + call resize(100, 100) + call move(0, 150) + - ColorRect rect2: + color: Color(0.5, 0.8, 0.5) + call resize(100, 100) + call move(0, 300) + - AnimationPlayer animation: + call addState(rect.color.r.addr, @[(tick: 0, value: 0.0), (tick: 200, value: 1.0)]) + call addState(rect.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) + call setDuration(200) + call play() + mode: ANIMATION_NORMAL # Default animation mode. + - AnimationPlayer animation1: + call addState(rect1.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) + call setDuration(200) + call play() + mode: ANIMATION_EASE + - AnimationPlayer animation2: + call addState(rect2.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) + call setDuration(200) + call play() + mode: ANIMATION_BEZIER + bezier: (0.8, 0.9) + getSceneByName("main").addChildren(rect, rect1, rect2, animation, animation1, animation2) -addScene(main) -setMainScene("Main") -windowLaunch() + test "Launch window": + windowLaunch() diff --git a/tests/test20.nim b/tests/test20.nim deleted file mode 100644 index 292eb64c..00000000 --- a/tests/test20.nim +++ /dev/null @@ -1,38 +0,0 @@ -# --- Test 20. Use Slider node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - slider = Slider() - vslider = Slider() - -main.addChild(slider) -main.addChild(vslider) -vslider.move(64, 64) -vslider.setMaxValue(4) -vslider.slider_type = SLIDER_VERTICAL -slider.resize(256, 32) -vslider.resize(32, 128) -slider.setMaxValue(1000) - -vslider.on_changed = - proc(self: SliderRef, v: uint) = - if v > 2: - vslider.setProgressColor(Color(0xccaaffff'u32)) - else: - vslider.setProgressColor(Color(0xffaaccff'u32)) - -slider.on_changed = - proc(self: SliderRef, v: uint) = - slider.setProgressColor(Color( - 1f - v.float / slider.max_value.float, v.float / slider.max_value.float, 0 - )) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test21.nim b/tests/test21.nim deleted file mode 100644 index 832b01f8..00000000 --- a/tests/test21.nim +++ /dev/null @@ -1,42 +0,0 @@ -# --- Test 21. Use Popup node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - popup = Popup() # Create Popup node pointer. - - box = VBox() - - label = Label() - - smthnode = Node() - - -label.setText("Hello") -label.setTextAlign(0.5, 0.5, 0.5, 0.5) -box.setChildAnchor(0.5, 0.1, 0.5, 0.1) -box.setSizeAnchor(1, 1) - -popup.addChild(box) -box.addChild(label) -main.addChild(popup) -main.addChild(smthnode) - - -addKeyAction("space", K_SPACE) -smthnode.on_process = - proc(self: NodeRef) = - if isActionJustPressed("space"): - if popup.visible == VISIBLE: - popup.hide() - else: - popup.show() - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test22.nim b/tests/test22.nim deleted file mode 100644 index 20209b22..00000000 --- a/tests/test22.nim +++ /dev/null @@ -1,28 +0,0 @@ -# --- Test 22. Use AudioStreamPlayer node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - stream1 = loadAudio("assets/vug_ost_Weh.ogg") - stream2 = loadAudio("assets/vug_ost_Movement.ogg") - - audio = AudioStreamPlayer() - audio1 = AudioStreamPlayer() - -audio.stream = stream1 -audio.setVolume(64) -audio.play() - -when false: # use more than one channel - audio1.stream = stream2 - audio1.setVolume(64) - audio1.play() - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test23.nim b/tests/test23.nim deleted file mode 100644 index f8d09f8b..00000000 --- a/tests/test23.nim +++ /dev/null @@ -1,18 +0,0 @@ -# --- Test 23. Use Node2D node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - node = Node2D() - - -main.addChild(node) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test24.nim b/tests/test24.nim deleted file mode 100644 index d697d721..00000000 --- a/tests/test24.nim +++ /dev/null @@ -1,24 +0,0 @@ -# --- Test 24. Use Sprite node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - sprite = Sprite() - - icon = load("assets/smile.png", GL_RGBA) - - -main.addChild(sprite) - -sprite.move(128, 128) # Move sprite. -sprite.setTexture(icon) # Change sprite image. - -sprite.centered = true # The default value is true. Try to change it. - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test25.nim b/tests/test25.nim deleted file mode 100644 index 72daaa07..00000000 --- a/tests/test25.nim +++ /dev/null @@ -1,34 +0,0 @@ -# --- Test 25. Use TextureButton node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - button = TextureButton() - - norm_texture = load("assets/button_normal.png", GL_RGBA) - hover_texture = load("assets/button_hover.png", GL_RGBA) - press_texture = load("assets/button_press.png", GL_RGBA) - -main.addChild(button) -env.setBackgroundColor(Color(0xf2f2f7ff'u32)) - -button.setText("Press me!") -button.resize(256, 64) -button.setAnchor(0.5, 0.5, 0.5, 0.5) - -button.setNormalTexture(norm_texture) -button.setHoverTexture(hover_texture) -button.setPressTexture(press_texture) - -button.on_touch = - proc(self: TextureButtonRef, x, y: float) = # This called when user clicks on the button - button.setText("Clicked in " & $x & ", " & $y & " position.") - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test26.nim b/tests/test26.nim deleted file mode 100644 index e9a76f47..00000000 --- a/tests/test26.nim +++ /dev/null @@ -1,32 +0,0 @@ -# --- Test 26. Use AnimatedSprite node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - sprite = AnimatedSprite() - - img0 = load("assets/anim/0.jpg") - img1 = load("assets/anim/1.jpg") - img2 = load("assets/anim/2.jpg") - img3 = load("assets/anim/3.jpg") - img4 = load("assets/anim/4.jpg") - -sprite.addFrame("default", img0) -sprite.addFrame("default", img1) -sprite.addFrame("default", img2) -sprite.addFrame("default", img3) -sprite.addFrame("default", img4) - - -sprite.play("", false) # if `name` is "" than plays current animation. -sprite.centered = false # disable centered. - - -main.addChild(sprite) -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test27.nim b/tests/test27.nim deleted file mode 100644 index 6a5029fa..00000000 --- a/tests/test27.nim +++ /dev/null @@ -1,27 +0,0 @@ -# --- Test 27. Use TextureProgressBar node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - progressbar = TextureProgressBar() - - back = load("assets/texture_progress_0.png", GL_RGBA) - progress = load("assets/texture_progress_1.png", GL_RGBA) - -main.addChild(progressbar) - -progressbar.setProgress(50) # default max progress value is 100. -progressbar.setMaxValue(150) -progressbar.resize(256, 85) - -progressbar.setProgressTexture(progress) -progressbar.setBackgroundTexture(back) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test28.nim b/tests/test28.nim deleted file mode 100644 index 10c73641..00000000 --- a/tests/test28.nim +++ /dev/null @@ -1,46 +0,0 @@ -# --- Test 28. Use YSort node. --- # -import nodesnim - - -Window("hello world", 1024, 640) - -var - main = Scene("Main") - - ysort = Ysort() - - sprite0 = Sprite("0") - sprite1 = Sprite("1") - sprite2 = Sprite("2") - sprite3 = Sprite("3") - sprite4 = Sprite("4") - - img0 = load("assets/anim/2.jpg") - -sprite0.setTexture(img0) -sprite1.setTexture(img0) -sprite2.setTexture(img0) -sprite3.setTexture(img0) -sprite4.setTexture(img0) - - -sprite0.filter = Color(0xffccaaff'u32) -sprite1.filter = Color(0xffaaccff'u32) -sprite2.filter = Color(0xaaffccff'u32) -sprite3.filter = Color(0xccffaaff'u32) -sprite4.filter = Color(0xaaccffff'u32) - - -sprite4.move(92, 92) -sprite0.move(128, 128) -sprite3.move(160, 160) -sprite2.move(192, 192) -sprite1.move(224, 224) - -ysort.addChilds(sprite0, sprite1, sprite2, sprite3, sprite4) - - -main.addChild(ysort) -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test29.nim b/tests/test29.nim deleted file mode 100644 index ff807658..00000000 --- a/tests/test29.nim +++ /dev/null @@ -1,18 +0,0 @@ -# --- Test 29. Use Counter node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - counter = Counter() - -main.addChild(counter) -counter.move(128, 64) - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test3.nim b/tests/test3.nim index 95c3762b..ce5d13b0 100644 --- a/tests/test3.nim +++ b/tests/test3.nim @@ -1,47 +1,337 @@ -# --- Test 3. Window events handling. --- # -import nodesnim +# --- Test 3. Work with Control nodes. --- # +import + nodesnim, + unittest -Window("newwindow") +suite "Work with Control nodes.": + + test "Setup window": + Window("Control test", 1280, 640) -var - main = Scene("Main") - node = Node("My node") + test "Setup scenes": + build: + - Scene main + - Scene second_scene + addMainScene(main) + addScene(second_scene) -main.addChild(node) -# Bind actions: -addKeyAction("forward", "w") -addKeyAction("backward", "s") -addKeyAction("left", "a") -addKeyAction("right", "d") -addButtonAction("click", BUTTON_LEFT) -addButtonAction("release", BUTTON_RIGHT) + test "Register events": + addButtonAction("click", BUTTON_LEFT) + addKeyAction("space", K_SPACE) + addKeyAction("enter", 13) -node.on_process = - proc(self: NodeRef) = # This called every frame. - if isActionJustPressed("click"): # returns true, when the user clicks the left button one time. - echo "clicked!" + test "Control test": + build: + - Control control + getSceneByName("main").addChild(control) - if isActionReleased("release"): # returns true, when the user no more press on the right button. - echo "release!" -node.on_input = - proc(self: NodeRef, event: InputEvent) = # This called only on user input. - if event.isInputEventMouseButton() and event.pressed: - echo "hi" - if isActionPressed("forward"): # returns true, when user press "w" - echo "forward" - if isActionPressed("backward"): # returns true, when user press "s" - echo "backward" - if isActionPressed("left"): # returns true, when user press "a" - echo "left" - if isActionPressed("right"): # returns true, when user press "d" - echo "right" + test "ColorRect test": + build: + - ColorRect rect: + color: Color(1, 0.8, 0.95, 0.8) + test "Handle Control events": + rect@onFocus(self): + echo "focused" + rect@onUnfocus(self): + echo "unfocused" -addScene(main) -setMainScene("Main") -windowLaunch() + rect@onClick(self, x, y): + echo "clicked in (", x, ", ", y, ")" + rect@onRelease(self, x, y): + echo "released in (", x, ", ", y, ")" + rect.color.r = 1f + rect@onPress(self, x, y): + rect.color.r -= 0.01 + + rect@onMouseEnter(self, x, y): + rect.color.g = 0.5f + rect@onMouseExit(self, x, y): + rect.color.g = 0.8f + + getSceneByName("main").addChild(rect) + + + test "Anchor settings test": + build: + - ColorRect rect1: + color: Color(0.2, 0.3, 0.4) + call move(40, 0) + call resize(80, 80) + - ColorRect rect2: + color: Color(0.4, 0.3, 0.2) + call setSizeAnchor(0.25, 0.5) + call setAnchor(1, 1, 1, 1) # anchor to bottom-right + getSceneByName("main").addChild(rect1) + + + test "Change scenes test": # Press Space to change scene + getSceneByName("main")@onProcess(self): + if isActionJustPressed("space"): + changeScene("second_scene") + getSceneByName("second_scene")@onProcess(self): + if isActionJustPressed("space"): + changeScene("main") + + + test "TextureRect test": + build: + - TextureRect texturerect1: + texture_mode: TEXTURE_KEEP_ASPECT_RATIO + texture_anchor: Anchor(0.5, 0.5, 0.5, 0.5) + call setTexture(load("assets/sharp.jpg")) + call resize(100, 100) + call move(120, 0) + - TextureRect texturerect2: + texture_mode: TEXTURE_CROP + texture_anchor: Anchor(0.5, 0.5, 0.5, 0.5) + call setTexture(load("assets/sharp.jpg")) + call resize(100, 100) + call move(220, 0) + - TextureRect texturerect3: + texture_mode: TEXTURE_FILL_XY + call setTexture(load("assets/sharp.jpg")) + call resize(100, 100) + call move(320, 0) + getSceneByName("main").addChildren(texturerect1, texturerect2, texturerect3) + + + test "Label test": + build: + - Label label: + call setText("hello,\nworld!\n VK!") + call move(0, 40) + label.text.setColor(6, 12, Color("#664fff")) + label.text.setURL(18, 19, "https://vk.com") + label.text.setUnderline(0, 2, true) + label.text.setStrikethrough(1, 3, true) + label.text.setItalic(0, true) + getSceneByName("main").addChild(label) + + + test "Button test": + build: + - Button btn: + call setText("Press me ^^") + call resize(196, 32) + call move(420, 0) + btn@onTouch(self, x, y): + echo "clicked btn!" + getSceneByName("main").addChild(btn) + + + test "Box test": + build: + - Box box: + call setChildAnchor(0.5, 0.5, 0.5, 0.5) + call setPadding(2, 4, 8, 16) + call move(420, 30) + call setBackgroundColor(Color(1f, 1f, 1f)) + - ColorRect first: + color: Color(0xff6699ff'u32) + call resize(80, 80) + - ColorRect second: + color: Color(0xff64ffff'u32) + call resize(60, 60) + - ColorRect third: + color: Color(0xffaa00ff'u32) + getSceneByName("main").addChild(box) + + + test "HBox test": + build: + - HBox hbox: + call setChildAnchor(1, 1, 1, 1) + call setPadding(2, 4, 8, 16) + call move(520, 30) + call setBackgroundColor(Color(1f, 1f, 1f)) + - ColorRect first: + color: Color(0xff6699ff'u32) + call resize(80, 80) + - ColorRect second: + color: Color(0xff64ffff'u32) + call resize(60, 60) + - ColorRect third: + color: Color(0xffaa00ff'u32) + getSceneByName("main").addChild(hbox) + + + test "VBox test": + build: + - VBox vbox: + call setChildAnchor(1, 1, 1, 1) + call setPadding(2, 4, 8, 16) + call move(420, 144) + call setBackgroundColor(Color(1f, 1f, 1f)) + - ColorRect first: + color: Color(0xff6699ff'u32) + call resize(80, 80) + - ColorRect second: + color: Color(0xff64ffff'u32) + call resize(60, 60) + - ColorRect third: + color: Color(0xffaa00ff'u32) + getSceneByName("main").addChild(vbox) + + + test "GridBox test": + build: + - GridBox grid: + call setPadding(2, 4, 8, 16) + call move(530, 144) + call setRow(3) + call setBackgroundColor(Color(1f, 1f, 1f)) + - ColorRect first(color: Color(0xff6699ff'u32)) + - ColorRect second(color: Color(0xff64ffff'u32)) + - ColorRect third(color: Color(0xffaa00ff'u32)) + - ColorRect fourth(color: Color(0xffcc33ff'u32)) + - ColorRect fifth(color: Color(0xffcc66ff'u32)) + - ColorRect sixth(color: Color(0xff6655ff'u32)) + getSceneByName("main").addChild(grid) + + + test "EditText test": + build: + - EditText edit: + call move(0, 150) + getSceneByName("main").addChild(edit) + + + test "Scroll test": + # TODO: Fix it with glFramebuffer + build: + - Scroll scroll: + call setAnchor(1, 0, 1, 0) + - GridBox grid: + call setPadding(2, 4, 8, 16) + call setRow(3) + call setBackgroundColor(Color(1f, 1f, 1f)) + - ColorRect first(color: Color(0xff6699ff'u32), rect_size: Vector2(100, 200)) + - ColorRect second(color: Color(0xff64ffff'u32)) + - ColorRect third(color: Color(0xffaa00ff'u32)) + - ColorRect fourth(color: Color(0xffcc33ff'u32)) + - ColorRect fifth(color: Color(0xffcc66ff'u32)) + - ColorRect sixth(color: Color(0xff6655ff'u32)) + getSceneByName("main").addChild(scroll) + + + test "ProgressBar test": + build: + - ProgressBar bar1: + progress_type: PROGRESS_BAR_HORIZONTAL + indeterminate: true + call setProgress(50) + call move(120, 110) + call resize(100, 20) + - ProgressBar bar2: + progress_type: PROGRESS_BAR_VERTICAL + indeterminate: true + call setProgress(50) + call move(220, 100) + call resize(20, 110) + - ProgressBar bar3: + progress_type: PROGRESS_BAR_CIRCLE + indeterminate: true + call setProgress(50) + call move(320, 110) + call resize(100, 100) + getSceneByName("main").addChildren(bar1, bar2, bar3) + + + test "Popup test": + build: + - Popup popup: + call setAnchor(0, 1, 0, 1) + - Label text: + call setText("popup!") + getSceneByName("main").getNode("control")@onProcess(self): + if isActionJustPressed("enter"): + popup.toggle() + getSceneByName("main").addChild(popup) + + + test "TextureButton test": + build: + - TextureButton button: + call setNormalTexture(load("assets/button_normal.png", GL_RGBA)) + call setHoverTexture(load("assets/button_hover.png", GL_RGBA)) + call setPressTexture(load("assets/button_press.png", GL_RGBA)) + call resize(256, 64) + call move(120, 220) + call setText("Texture button") + getSceneByName("main").addChild(button) + + + test "TextureProgressBar test": + build: + - TextureProgressBar progress: + call setProgressTexture(load("assets/texture_progress_1.png", GL_RGBA)) + call setBackgroundTexture(load("assets/texture_progress_0.png", GL_RGBA)) + call setProgress(50) + call resize(256, 85) + call move(100, 300) + getSceneByName("main").addChild(progress) + + + test "Counter test": + build: + - Counter counter: + call move(360, 360) + call setMaxValue(100) + getSceneByName("main").addChild(counter) + + + test "Switch test": + build: + - Switch switch: + call move(360, 400) + getSceneByName("main").addChild(switch) + + + test "CheckBox test": + build: + - CheckBox check: + call setText("smth checkbox") + call enable() + call move(700, 300) + getSceneByName("main").addChild(check) + + + test "SubWindow test": + build: + - SubWindow window1: + call setIcon("assets/anim/0.jpg") + call setTitle("subwindow") + call move(500, 400) + call open() + getSceneByName("main").addChild(window1) + + + test "Slider test": + build: + - Slider slider1: + slider_type: SLIDER_HORIZONTAL + call resize(100, 10) + call move(600, 300) + - Slider slider2: + slider_type: SLIDER_VERTICAL + call resize(10, 100) + call move(600, 310) + getSceneByName("main").addChildren(slider1, slider2) + + test "ToolTip test": + build: + - ToolTip tooltip: + call showAtMouse() + @onProcess(): + tooltip.showAtMouse() + getSceneByName("main").addChild(tooltip) + + + test "Launch window": + windowLaunch() diff --git a/tests/test30.nim b/tests/test30.nim deleted file mode 100644 index 1be24213..00000000 --- a/tests/test30.nim +++ /dev/null @@ -1,33 +0,0 @@ -# --- Test 30. use CollisionShape2D. --- # -# Please, compile with `--define:debug` or with `-d:debug` for see collision shapes. -import nodesnim - - -Window("hello world") - - -var - main = Scene("Main") - - shape1 = CollisionShape2D() - shape2 = CollisionShape2D() - shape3 = CollisionShape2D() - - -shape1.move(100, 100) -shape2.move(125, 125) -shape3.move(170, 125) -shape3.setShapeTypeCircle(0, 0, 35) # by default shape type is a rect, but you can change it at any time. -shape2.disable = true # by default shape enabled, but you can change it at any time. - -echo shape1.isCollide(shape2) # if one of two shapes is disabled - return false. -echo shape1.isCollide(shape3) -echo shape2.isCollide(shape3) - - -main.addChild(shape1) -main.addChild(shape2) -main.addChild(shape3) -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test31.nim b/tests/test31.nim deleted file mode 100644 index bfa469e9..00000000 --- a/tests/test31.nim +++ /dev/null @@ -1,49 +0,0 @@ -# --- Test 31. use KinematicBody2D node. --- # -# Please, compile with `--define:debug` or with `-d:debug` for see collision shapes. -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - shape1 = CollisionShape2D() - shape2 = CollisionShape2D() - shape3 = CollisionShape2D() - shape4 = CollisionShape2D() - - body = KinematicBody2D() - - -shape1.move(100, 100) -shape2.move(125, 125) -shape4.move(360, 25) -shape1.setShapeTypeCircle(0, 0, 35) -shape2.resize(150, 50) -# shape3.setShapeTypeCircle(0, 0, 35) -shape3.setShapeTypePolygon(Vector2(0, 0), Vector2(15, 5), Vector2(28, 15), Vector2(35, 25), Vector2(5, 45)) -shape4.setShapeTypePolygon(Vector2(0, 0), Vector2(150, 65), Vector2(25, 150)) - - -addButtonAction("left", BUTTON_LEFT) -body.on_process = - proc(self: NodeRef) = - if isActionPressed("left"): - let - mouse_pos = body.getGlobalMousePosition() - distance = body.global_position.distance(mouse_pos) - direction = body.global_position.directionTo(mouse_pos) - speed = 3f - if distance >= 5: - body.moveAndCollide(direction*speed) - - -main.addChild(shape1) -main.addChild(shape2) -main.addChild(shape4) -main.addChild(body) -body.addChild(shape3) -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test32.nim b/tests/test32.nim deleted file mode 100644 index 98abc2a6..00000000 --- a/tests/test32.nim +++ /dev/null @@ -1,22 +0,0 @@ -# --- Test 32. Use Switch node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - switch = Switch() - -main.addChild(switch) -switch.move(128, 64) - -switch.on_switch = - proc(self: SwitchRef, toggled: bool) = # this called when the user toggles switch. - echo toggled - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test33.nim b/tests/test33.nim deleted file mode 100644 index 3a0f2888..00000000 --- a/tests/test33.nim +++ /dev/null @@ -1,28 +0,0 @@ -# --- Test 33. Event handlers with macros. --- # -import nodesnim - - -Window("test35") - -var - main = Scene("Main") - node = Button() - -node.setText("Hello") -node.setAnchor(0.5, 0.5, 0.5, 0.5) - - -node@onReady(self): - echo "hello!" - -node@onInput(self, event): - if event.isInputEventMouseButton() and event.pressed: - echo "clicked" - -node@onClick(self, x, y): - node.setText("clicked in " & $x & "," & $y & ".") - - -main.addChild(node) -addMainScene(main) -windowLaunch() diff --git a/tests/test34.nim b/tests/test34.nim deleted file mode 100644 index 3a225fba..00000000 --- a/tests/test34.nim +++ /dev/null @@ -1,26 +0,0 @@ -# --- Test 34. Rotation. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - sprite = Sprite() - -main.addChild(sprite) - -sprite.loadTexture("assets/anim/2.jpg") - -sprite.move(128, 128) -sprite.centered = false - - -sprite@on_process(self): - sprite.rotation += 0.1 - - -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test35.nim b/tests/test35.nim deleted file mode 100644 index 4e2d001b..00000000 --- a/tests/test35.nim +++ /dev/null @@ -1,50 +0,0 @@ -# --- Test 35. use Camera2D node. --- # -import nodesnim - - -Window("hello world") - - -var - main = Scene("Main") - - body = KinematicBody2D() - - sprite = Sprite() - - sprite1 = Sprite() - - camera = Camera2D() - - img = load("assets/anim/2.jpg") - img1 = load("assets/anim/4.jpg") - -sprite.setTexture(img) -sprite1.setTexture(img1) -body.addChild(sprite) -body.addChild(camera) - -camera.setTarget(body) -camera.setLimit(-600, -400, 600, 400) -camera.setCurrent() -camera.enableSmooth() - - - -addButtonAction("left", BUTTON_LEFT) -body.on_process = - proc(self: NodeRef) = - if isActionPressed("left"): - let - mouse_pos = body.getGlobalMousePosition() - distance = body.global_position.distance(mouse_pos) - direction = body.global_position.directionTo(mouse_pos) - speed = 4f - if distance >= 5: - body.moveAndCollide(direction*speed) - -main.addChild(body) -main.addChild(sprite1) -addScene(main) -setMainScene("Main") -windowLaunch() diff --git a/tests/test36.nim b/tests/test36.nim deleted file mode 100644 index f8f2ba59..00000000 --- a/tests/test36.nim +++ /dev/null @@ -1,14 +0,0 @@ -# --- Test 36. Use Node3D. --- # -import nodesnim - -Window("smth here") - -var - scene = Scene("Main") - node = Node3D() - -scene.addChild(node) - - -addMainScene(scene) -windowLaunch() diff --git a/tests/test37.nim b/tests/test37.nim deleted file mode 100644 index 74e16598..00000000 --- a/tests/test37.nim +++ /dev/null @@ -1,37 +0,0 @@ -# --- Test 37. Use GeometryInstance node. --- # -import nodesnim - - -Window("smth") - -build: - - Scene scene: - - GeometryInstance geometry1: - translation: Vector3(1, 0, 5) - color: Color(144, 133, 122, 0.8) - - Sprite sprite: - call setTexture(load("assets/anim/2.jpg")) - call move(96, 96) - - GeometryInstance geometry2: - translation: Vector3(-1, 0, 2) - color: Color(122, 133, 144, 0.8) - - GeometryInstance geometry3: - translation: Vector3(1, 0, 2) - color: Color(144, 111, 144) - geometry: GEOMETRY_SPHERE - - Button button: - text: stext"Hello! ^^" - call resize(256, 64) - call setAnchor(0.5, 0.5, 0.5, 0.5) - -geometry1@on_input(self, event): - if event.isInputEventMouseMotion() and event.pressed: - geometry1.rotateX(-event.yrel) - geometry1.rotateY(-event.xrel) - geometry2.rotateX(event.yrel) - geometry2.rotateY(-event.xrel) - geometry3.rotateX(event.yrel) - geometry3.rotateY(-event.xrel) - -addMainScene(scene) -windowLaunch() diff --git a/tests/test38.nim b/tests/test38.nim deleted file mode 100644 index 926d77f4..00000000 --- a/tests/test38.nim +++ /dev/null @@ -1,36 +0,0 @@ -# --- Test 38. Use SubWindow node. --- # -import nodesnim - - -Window("subwindow ._.") - -var - main = Scene() - window = SubWindow() - window1 = SubWindow() - window2 = SubWindow() - - -main.addChild(window) -main.addChild(window1) -main.addChild(window2) - -window.move(64, 64) -window.setIcon("assets/anim/0.jpg") -window.open() - -window1.move(64, 64) -window1.setIcon("assets/anim/0.jpg") -window1.setBackgroundColor(Color("#efefef")) -window1.setTitle("Hello, lol") -window1.open() - -window2.move(64, 64) -window2.setIcon("assets/anim/0.jpg") -window2.setBackgroundColor(Color("#212112")) -window2.setTitle("Aboba") -window2.open() - - -addMainScene(main) -windowLaunch() diff --git a/tests/test39.nim b/tests/test39.nim deleted file mode 100644 index 09b7b8e1..00000000 --- a/tests/test39.nim +++ /dev/null @@ -1,17 +0,0 @@ -# --- Test 39. Use Scene builder. --- # -import nodesnim - -Window("scene builder") - -build: - - Scene scene: - name: "Main scene" - - Vbox background: # Instead of var background = Vbox() - call setBackgroundColor(Color(21, 33, 48)) # You can change params without `objname`.param = value syntax. - call setSizeAnchor(1.0, 0.1) # You can also call any method without `objname`.method(args) syntax. :eyes: - call setAnchor(0.5, 0.5, 0.5, 0.5) - -echo background.size_anchor - -addMainScene(scene) -windowLaunch() diff --git a/tests/test4.nim b/tests/test4.nim index 4fae0606..d85eb6a3 100644 --- a/tests/test4.nim +++ b/tests/test4.nim @@ -1,19 +1,41 @@ -# --- Test 4. Use ColorRect node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - colorrect = ColorRect() - -main.addChild(colorrect) - -colorrect.color = Color(1f, 1f, 1f) # default ColorRect color. - - -addScene(main) -setMainScene("Main") -windowLaunch() +# --- Test 4. Work with core. --- # +import + nodesnim, + unittest + + +suite "Work with core": + test "Anchor": + var anchor = Anchor(1, 0.5, 1, 0.5) + echo anchor + + test "Color": + var + clr1 = Color(255, 100, 155, 1f) # RGBA + clr2 = Color(255, 100, 155, 255) + clr3 = Color(0xAACCFFFF'u32) + clr4 = Color("#AACCFFFF") + clr5 = Color("rgb(255, 100, 155)") + clr6 = Color("#acf") + assert clr1 == clr2 + assert clr1 == clr5 + assert clr3 == clr4 + assert clr3 == clr6 + + test "Vector2": + var + vec1 = Vector2() + vec2 = Vector2(0, 0) + vec3 = Vector2(1, 2).normalized() + assert vec1 == vec2 + echo vec3 + + test "stylesheet": + var + s = style({ + nums: 0 0 0 0 0 0 0 1, + clr: rgba(255, 100, 255, 0.1), + padding: 0 0 0 0, + background-color: "#fff" + }) + echo s diff --git a/tests/test40.nim b/tests/test40.nim deleted file mode 100644 index bbd2aa21..00000000 --- a/tests/test40.nim +++ /dev/null @@ -1,38 +0,0 @@ -# --- Test 40. Use AnimationPlayer. --- # -import nodesnim - -Window("AnimationPlayer") - -build: - - Scene scene: - - ColorRect rect: - color: Color(0, 0, 0) - call resize(100, 100) - - ColorRect rect1: - color: Color(0, 0, 0) - call resize(100, 100) - call move(0, 150) - - ColorRect rect2: - color: Color(0.5, 0.8, 0.5) - call resize(100, 100) - call move(0, 300) - - AnimationPlayer animation: - call addState(rect.color.r.addr, @[(tick: 0, value: 0.0), (tick: 200, value: 1.0)]) - call addState(rect.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) - call setDuration(200) - call play() - mode: ANIMATION_NORMAL # Default animation mode. - - AnimationPlayer animation1: - call addState(rect1.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) - call setDuration(200) - call play() - mode: ANIMATION_EASE - - AnimationPlayer animation2: - call addState(rect2.position.x.addr, @[(tick: 0, value: 0.0), (tick: 100, value: 250.0)]) - call setDuration(200) - call play() - mode: ANIMATION_BEZIER - bezier: (0.8, 0.9) - -addMainScene(scene) -windowLaunch() diff --git a/tests/test41.nim b/tests/test41.nim deleted file mode 100644 index 57474f42..00000000 --- a/tests/test41.nim +++ /dev/null @@ -1,22 +0,0 @@ -# --- Test 41. Use StyleSheet object. --- # -import nodesnim - - -var mystyle = style( - { - background-color: rgba(255, 125, 255, 0.7), - color: rgb(34, 34, 34), - font-size: 1, - text-align: center - }) -echo mystyle - -var background = Color(mystyle["background-color"]) - -assert background == Color(255, 125, 255, 0.7) - -echo StyleSheet( - { - "background-color": "#f2f2f7" - } -) diff --git a/tests/test42.nim b/tests/test42.nim deleted file mode 100644 index 9e1f3881..00000000 --- a/tests/test42.nim +++ /dev/null @@ -1,36 +0,0 @@ -# --- Test 42. Use Drawable and Control. --- # -import nodesnim - - -Window("drawable oops") - -build: - - Scene scene: - - Control ctrl - - Control ctrl1: - call move(350, 100) - call setSizeAnchor(0.2, 0.2) - -ctrl1.background.setTexture(load("assets/sharp.jpg")) -ctrl1.background.setCornerRadius(25) -ctrl1.background.setCornerDetail(8) -ctrl1.background.enableShadow(true) -ctrl1.background.setShadowOffset(Vector2(0, 8)) - - -ctrl.resize(256, 96) -ctrl.move(64, 64) -ctrl.setStyle(style( - { - background-color: rgb(33, 65, 87), - border-radius: 8, - border-width: 1, - border-color: rgb(0, 0, 0), - shadow: true, - shadow-offset: 8, - size-anchor: 0.5 0.7 - } -)) - -addMainScene(scene) -windowLaunch() diff --git a/tests/test43.nim b/tests/test43.nim deleted file mode 100644 index 2dbeab50..00000000 --- a/tests/test43.nim +++ /dev/null @@ -1,23 +0,0 @@ -# --- Test 43. Use CheckBox node. --- # -import nodesnim - - -Window("drawable oops") - -build: - - Scene scene: - - CheckBox box: - call setText("smth checkbox") - call enable() - - ColorRect rect: - call move(100, 100) - -box@on_toggle(self, value): - if value: - rect.color.a = 1f - else: - rect.color.a = 0f - - -addMainScene(scene) -windowLaunch() diff --git a/tests/test44.nim b/tests/test44.nim deleted file mode 100644 index 06fe299f..00000000 --- a/tests/test44.nim +++ /dev/null @@ -1,27 +0,0 @@ -# --- Test 44. Use GradientDrawable and Control. --- # -import nodesnim - - -Window("drawable oops") - -build: - - Scene scene: - - Control ctrl: - call resize(256, 256) - call move(150, 50) - -var gradient = GradientDrawable() -gradient.setCornerRadius(16) -gradient.setCornerDetail(16) -gradient.enableShadow(true) -gradient.setShadowOffset(Vector2(15, 15)) -gradient.setBorderColor(Color(1.0, 0.5, 0.5, 0.1)) -gradient.setBorderWidth(5) -gradient.setStyle(style({ - corner-color: "#ff7 #ff7 #f77 #f77" - })) -ctrl.setBackground(gradient) - - -addMainScene(scene) -windowLaunch() diff --git a/tests/test45.nim b/tests/test45.nim deleted file mode 100644 index dfa7e0ba..00000000 --- a/tests/test45.nim +++ /dev/null @@ -1,23 +0,0 @@ -# --- Test 45. Use TileMap node. --- # -import nodesnim - -Window("Tilemap test") - -var - tileset = TileSet("assets/tilesets/land.png", Vector2(64, 64), GL_RGBA) - -build: - - Scene main: - - TileMap map: - call setTileSet(tileset) - # map size layer count - call resizeMap(newVector2(8096, 512), 1) - call fill(newVector2(1, 0)) - call drawRect(3, 3, 10, 5, newVector2(9, 7)) - call drawTile(0, 0, newVector2(3, 0)) - call drawTile(1, 0, newVector2(7, 4.5)) - call drawTile(0, 1, newVector2(6.5, 5)) - call drawTile(1, 1, newVector2(7, 5)) - -addMainScene(main) -windowLaunch() diff --git a/tests/test46.nim b/tests/test46.nim deleted file mode 100644 index c1df5090..00000000 --- a/tests/test46.nim +++ /dev/null @@ -1,62 +0,0 @@ -# --- Test 46. Use padding. --- # -import nodesnim - - -Window("Padding") - - -build: - - Scene main: - - Box box: - call setPadding(8, 16, 2, 8) - call setBackgroundColor(Color("#5aa")) - - ColorRect rect1: - color: Color("#ff7") - call resize(64, 64) - - ColorRect rect2: - color: Color("#f7f") - - VBox vbox: - call setPadding(2, 4, 8, 16) - call move(100, 64) - call setChildAnchor(1, 1, 1, 1) - call setBackgroundColor(Color("#5aa")) - - ColorRect rect3: - color: Color("#ff7") - call resize(64, 64) - - ColorRect rect4: - color: Color("#f7f") - - HBox hbox: - call setPadding(2, 4, 8, 16) - call move(200, 64) - call setChildAnchor(1, 1, 1, 1) - call setBackgroundColor(Color("#5aa")) - - ColorRect rect5: - color: Color("#ff7") - call resize(64, 64) - - ColorRect rect6: - color: Color("#f7f") - - GridBox gridbox: - call setPadding(2, 4, 8, 16) - call move(300, 200) - call setChildAnchor(1, 1, 1, 1) - call setBackgroundColor(Color("#5aa")) - - ColorRect rect7: - color: Color("#ff7") - call resize(64, 64) - - ColorRect rect8: - color: Color("#f7f") - - ColorRect rect9: - color: Color("#ff7") - call resize(64, 64) - - ColorRect rect10: - color: Color("#f7f") - - Label text: - call setText("Hello, world!") - call setBackgroundColor(Color("#324")) - call resize(0, 0) - call setPadding(8, 8, 8, 8) - call move(32, 200) - - -addMainScene(main) -windowLaunch() diff --git a/tests/test47.nim b/tests/test47.nim deleted file mode 100644 index f921873e..00000000 --- a/tests/test47.nim +++ /dev/null @@ -1,39 +0,0 @@ -# --- Test 47. Use margin. --- # -import nodesnim - - -Window("Margin test") - - -build: - - Scene main: - - HBox box1: - call setBackgroundColor(Color("#55c")) - call setPadding(8, 8, 8, 8) - call move(64, 64) - separator: 0 - - ColorRect rect1: - color: Color("#ee5") - call setMargin(0, 16, 0, 0) - - ColorRect rect2: - color: Color("#5ee") - call setMargin(8, 0, 32, 0) - - ColorRect rect3: - color: Color("#e5e") - - VBox box2: - call setBackgroundColor(Color("#55c")) - call setPadding(8, 8, 8, 8) - call move(256, 64) - separator: 0 - - ColorRect rect4: - color: Color("#ee5") - call setMargin(16, 0, 0, 0) - - ColorRect rect5: - color: Color("#5ee") - call setMargin(0, 8, 0, 32) - - ColorRect rect6: - color: Color("#e5e") - - -addMainScene(main) -windowLaunch() diff --git a/tests/test48.nim b/tests/test48.nim deleted file mode 100644 index 55de4d9b..00000000 --- a/tests/test48.nim +++ /dev/null @@ -1,53 +0,0 @@ -# --- Test 48. Use Camera3D node. --- # -import nodesnim - -Window("camera 3d test", 1024, 640) - - -build: - - Scene main: - - Node3D root: - call translate(2, 2, -5) - - Camera3D camera: - call setCurrent() - call changeTarget(root) - - GeometryInstance cube: - translation: Vector3(-1, 0, 2) - color: Color(122, 133, 144, 0.8) - - GeometryInstance cube1: - translation: Vector3(2, 0, -2) - color: Color(144, 144, 122, 0.8) - - GeometryInstance cube2: - translation: Vector3(1, 2.5, 1) - color: Color(144, 111, 144, 0.8) - - GeometryInstance sphere: - translation: Vector3(-1, -1, 1) - color: Color(144, 77, 144, 1.0) - geometry: GEOMETRY_SPHERE - - ProgressBar health: - call resize(256, 48) - call setAnchor(0, 1, 0, 1) - call setProgress(50) - call setProgressColor(Color("#a77")) - call setBackgroundColor(Color(222, 222, 222, 0.5)) - -addKeyAction("forward", "w") -addKeyAction("back", "s") -addKeyAction("left", "a") -addKeyAction("right", "d") - -root@on_input(self, event): - if event.isInputEventMouseMotion() and event.pressed: - camera.rotate(-event.xrel*0.1, event.yrel*0.1) - if isActionPressed("left"): - root.translate(camera.right * -0.1) - if isActionPressed("right"): - root.translate(camera.right * 0.1) - if isActionPressed("forward"): - root.translate(camera.front*0.1) - if isActionPressed("back"): - root.translate(camera.front*(-0.1)) - - -addMainScene(main) -windowLaunch() diff --git a/tests/test49.nim b/tests/test49.nim deleted file mode 100644 index 495d5ffd..00000000 --- a/tests/test49.nim +++ /dev/null @@ -1,38 +0,0 @@ -# --- Use TileMap ISOMETRIC mode --- # -import nodesnim - -Window("Tilemap test", 1024, 640) - -var - tileset = TileSet("assets/tilesets/isometric_desert.png", Vector2(64, 32), GL_RGBA) - -build: - - Scene main: - - TileMap map: - call setTileSet(tileset) - call resizeMap(newVector2(8096, 512), layer_count=4) - call setMode(TILEMAP_ISOMETRIC) - call fill(newVector2(1, 0)) - call drawRect(3, 3, 10, 5, newVector2(15, 1)) - - # platform - call drawTile(2, 4, newVector2(0, 27), 1) - call drawTile(1, 5, newVector2(0, 28), 1) - - # cross - call drawTile(4, 6, newVector2(14, 13), 1) - call drawTile(3, 7, newVector2(14, 14), 1) - - # sign - call drawTile(4, 5, newVector2(11, 12), 1) - call drawTile(4, 5, newVector2(11, 13), 2) - call drawTile(4, 5, newVector2(11, 14), 3) - - # magic - call drawTile(5, 10, newVector2(2, 33), 1) - call drawTile(6, 11, newVector2(3, 33), 1) - call drawTile(4, 11, newVector2(2, 34), 1) - call drawTile(5, 12, newVector2(3, 34), 1) - -addMainScene(main) -windowLaunch() diff --git a/tests/test5.nim b/tests/test5.nim index c8c3a900..df7106fc 100644 --- a/tests/test5.nim +++ b/tests/test5.nim @@ -1,49 +1,76 @@ -# --- Test 5. Handle Control node events. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - colorrect = ColorRect() - - colorrect1 = ColorRect() - -main.addChild(colorrect) -colorrect.addChild(colorrect1) - - -colorrect1.on_click = - proc(self: ControlRef, x, y: float) = # This called when the user clicks on the Control node (ColorRect in this case). - colorrect1.move(3, 3) - -colorrect.on_press = - proc(self: ControlRef, x, y: float) = # This called when the user holds on the mouse on the Control node. - colorrect.color.r -= 0.001 - -colorrect.on_release = - proc(self: ControlRef, x, y: float) = # This called when the user no more holds on the mouse. - colorrect.color.r = 1 - -colorrect.on_focus = - proc(self: ControlRef) = # This called when the Control node gets focus. - echo "hello ^^." - -colorrect.on_unfocus = - proc(self: ControlRef) = # This called when the Control node loses focus. - echo "bye :(" - -colorrect1.on_mouse_enter = - proc(self: ControlRef, x, y: float) = # This called when the mouse enters the Control node. - colorrect1.color = Color(1, 0.6, 1, 0.5) - -colorrect1.on_mouse_exit = - proc(self: ControlRef, x, y: float) = # This called when the mouse exit from the Control node. - colorrect1.color = Color(1f, 1f, 1f) - - -addScene(main) -setMainScene("Main") -windowLaunch() +# --- Test 5. Work with graphics. --- # +import + nodesnim, + unittest + + +suite "Work with graphics.": + + test "Setup window": + Window("graphics test") + + + test "Setup scene": + build: + - Scene main + + addMainScene(main) + + test "Color background": + build: + - Control ctrl + + ctrl.resize(256, 96) + ctrl.move(64, 64) + ctrl.setStyle(style( + { + background-color: rgb(33, 65, 87), + border-radius: 8, + border-width: 1, + border-color: rgb(0, 0, 0), + shadow: true, + shadow-offset: 8, + size-anchor: 0.5 0.7 + } + )) + + getSceneByName("main").addChild(ctrl) + + + test "Image background": + build: + - Control ctrl1: + call move(350, 100) + call setSizeAnchor(0.2, 0.2) + + ctrl1.background.setTexture(load("assets/sharp.jpg")) + ctrl1.background.setCornerRadius(25) + ctrl1.background.setCornerDetail(8) + ctrl1.background.enableShadow(true) + ctrl1.background.setShadowOffset(Vector2(0, 8)) + + getSceneByName("main").addChild(ctrl1) + + + test "Gradient background": + build: + - Control ctrl2: + call resize(96, 96) + call setAnchor(0, 0.5, 0, 0.5) + var gradient = GradientDrawable() + gradient.setCornerRadius(16) + gradient.setCornerDetail(16) + gradient.enableShadow(true) + gradient.setShadowOffset(Vector2(15, 15)) + gradient.setBorderColor(Color(1.0, 0.5, 0.5, 0.1)) + gradient.setBorderWidth(5) + gradient.setStyle(style({ + corner-color: "#ff7 #ff7 #f77 #f77" + })) + ctrl2.setBackground(gradient) + + getSceneByName("main").addChild(ctrl2) + + + test "Launch window": + windowLaunch() diff --git a/tests/test50.nim b/tests/test50.nim deleted file mode 100644 index 0322e8e9..00000000 --- a/tests/test50.nim +++ /dev/null @@ -1,21 +0,0 @@ -# --- Make your own node --- # -import nodesnim - - -type - MyOwnNodeRef = ref MyOwnNodeObj - MyOwnNodeObj = object of NodeRef # NodeRef/Node2DRef/ControlRef/Node3DRef - property: int - - -proc MyOwnNode(name: string = "MyOwnNode"): MyOwnNodeRef = - nodepattern(MyOwnNodeRef) - # controlpattern()/node2dpattern()/node3dpattern() - result.property = 100 - - -build: - - MyOwnNode node: - property: 10 - -echo node.property diff --git a/tests/test6.nim b/tests/test6.nim index 357ec11c..079ccabc 100644 --- a/tests/test6.nim +++ b/tests/test6.nim @@ -1,38 +1,182 @@ -# --- Test 6. Anchor setting. --- # -import nodesnim +# --- Test 6. Work with 2D nodes. --- # +import + nodesnim, + unittest -Window("hello world") +suite "Work with 2D nodes.": + + test "Setup window": + Window("2D nodes test", 1024, 640) -var - main = Scene("Main") - lightblue = ColorRect() - violet = ColorRect() + test "Register events": + addButtonAction("left", BUTTON_LEFT) + addKeyAction("w", "w") + addKeyAction("a", "a") + addKeyAction("s", "s") + addKeyAction("d", "d") -main.addChild(lightblue) -lightblue.addChild(violet) + test "Setup scene": + build: + - Scene main + addMainScene(main) -lightblue.resize(256, 128) -lightblue.move(128, 64) -violet.setAnchor( # Try to change it! ^^ - 0.5, # parent anchor at X-axis. - 0.5, # parent anchor at Y-axis. - 0.5, # anchor at X-axis. - 0.5 # anchor at Y-axis. -) -lightblue.setAnchor(1, 1, 1, 1) -lightblue.setSizeAnchor( - 0, # size anchor at X-axis. If 0 then not used. - 1 # size anchor at Y-axis. If 0 then not used. -) + test "Node2D test": + build: + - Node2D node2d(name: "2d node") + getSceneByName("main").addChild(node2d) -lightblue.color = Color(0xaaccffff'u32) -violet.color = Color(0xccaaffff'u32) + test "Sprite test": + build: + - Sprite sprite: + centered: true + call loadTexture("assets/anim/2.jpg") + call move(80, 80) + getSceneByName("main").addChild(sprite) -addScene(main) -setMainScene("Main") -windowLaunch() + + test "AnimatedSprite test": + build: + - AnimatedSprite animation: + centered: false + z_index: -10 + call addFrame("default", load("assets/anim/0.jpg")) + call addFrame("default", load("assets/anim/1.jpg")) + call addFrame("default", load("assets/anim/2.jpg")) + call addFrame("default", load("assets/anim/3.jpg")) + call addFrame("default", load("assets/anim/4.jpg")) + call play(name = "", backward = false) + call setSpeed("default", 5) # name, frames-per-second + getSceneByName("main").addChild(animation) + + + test "YSort test": + var image = load("assets/anim/2.jpg") + build: + - YSort sort: + z_index: 1 + call move(720, 80) + - Sprite s1: + filter: Color("#6644ff") + call setTexture(image) + - Sprite s2: + filter: Color("#997799") + call setTexture(image) + call move(0, 80) + - Sprite s3: + filter: Color("#9f9") + call setTexture(image) + call move(0, 160) + getSceneByName("main").addChild(sort) + + test "KinematicBody2D test": + build: + - KinematicBody2D body: + - CollisionShape2D collision: + call setShapeTypePolygon(Vector2(0, 0), Vector2(15, 5), Vector2(28, 15), + Vector2(35, 25), Vector2(5, 45)) + - CollisionShape2D rect_collision: + call resize(160, 40) + call move(100, 200) + - CollisionShape2D polygon_collision: + call setShapeTypePolygon(Vector2(0, 0), Vector2(150, 65), Vector2(25, 150)) + call move(300, 200) + - CollisionShape2D circle_collision: + call setShapeTypeCircle(0, 0, 64) + call move(100, 300) + body@onProcess(self): + if isActionPressed("left"): + let + mouse_pos = body.getGlobalMousePosition() + distance = body.global_position.distance(mouse_pos) + direction = body.global_position.directionTo(mouse_pos) + speed = 3f + if distance >= 5: + body.moveAndCollide(direction*speed) + getSceneByName("main").addChildren(body, rect_collision, polygon_collision, circle_collision) + + + test "Camera2D test": + build: + - KinematicBody2D player: + z_index: 50 + - Sprite player_sprite: + z_index: 50 + centered: true + filter: Color("#555") + call loadTexture("assets/anim/2.jpg") + - Camera2D camera: + call setTarget(player) + call setLimit(-2048, -1024, 2048, 1024) + call setCurrent() + call enableSmooth() + player@onProcess(self): + if isActionPressed("w"): + player.move(0, -10) + elif isActionPressed("s"): + player.move(0, 10) + if isActionPressed("a"): + player.move(-10, 0) + elif isActionPressed("d"): + player.move(10, 0) + getSceneByName("main").addChildren(player, camera) + + + test "TileMap 2d test": + var tileset = TileSet("assets/tilesets/land.png", Vector2(64, 64), GL_RGBA) + build: + - TileMap map: + z_index: -100 + call setTileSet(tileset) + call move(-2048, -1024) + # map size layer count + call resizeMap(Vector2(512, 128), 1) + call fill(Vector2(1, 0)) + call drawRect(3, 3, 10, 5, Vector2(9, 7)) + call drawTile(0, 0, Vector2(3, 0)) + call drawTile(1, 0, Vector2(7, 4.5)) + call drawTile(0, 1, Vector2(6.5, 5)) + call drawTile(1, 1, Vector2(7, 5)) + getSceneByName("main").addChild(map) + + + test "TileMap isometric test": + var tileset = TileSet("assets/tilesets/isometric_desert.png", Vector2(64, 32), GL_RGBA) + build: + - TileMap map: + z_index: -80 + call setMode(TILEMAP_ISOMETRIC) + call setTileSet(tileset) + call move(-2048, -1024) + # map size layer count + call resizeMap(Vector2(32, 32), layer_count=4) + call fill(Vector2(1, 0)) + call drawRect(3, 3, 10, 5, Vector2(15, 1)) + + # platform + call drawTile(2, 4, Vector2(0, 27), 1) + call drawTile(1, 5, Vector2(0, 28), 1) + + # cross + call drawTile(4, 6, Vector2(14, 13), 1) + call drawTile(3, 7, Vector2(14, 14), 1) + + # sign + call drawTile(4, 5, Vector2(11, 12), 1) + call drawTile(4, 5, Vector2(11, 13), 2) + call drawTile(4, 5, Vector2(11, 14), 3) + + # magic + call drawTile(5, 10, Vector2(2, 33), 1) + call drawTile(6, 11, Vector2(3, 33), 1) + call drawTile(4, 11, Vector2(2, 34), 1) + call drawTile(5, 12, Vector2(3, 34), 1) + getSceneByName("main").addChild(map) + + + test "Launch window": + windowLaunch() diff --git a/tests/test7.nim b/tests/test7.nim index 7bad2d73..0080eb44 100644 --- a/tests/test7.nim +++ b/tests/test7.nim @@ -1,41 +1,83 @@ -# --- Test 7. Change scenes. --- # -import nodesnim +# --- Test 7. Work with 3D nodes. --- # +import + nodesnim, + unittest -Window("hello world") +suite "Work with 3D nodes.": + + test "Setup window": + Window("3D nodes test", 1024, 640) -var - main = Scene("Main") - second = Scene("Second scene") - lightblue = ColorRect() + test "Setup scene": + build: + - Scene main + addMainScene(main) - violet = ColorRect() + test "Register events": + addKeyAction("forward", "w") + addKeyAction("back", "s") + addKeyAction("left", "a") + addKeyAction("right", "d") -addKeyAction("change_scene", K_SPACE) -main.addChild(violet) -second.addChild(lightblue) + test "GeometryInstance test": + build: + - GeometryInstance cube: + translation: Vector3(-1, 0, 2) + color: Color(122, 133, 144, 0.8) + - GeometryInstance cube1: + translation: Vector3(2, 0, -2) + color: Color(144, 144, 122, 0.8) + - GeometryInstance cube2: + translation: Vector3(1, 2.5, 1) + color: Color(144, 111, 144, 0.8) + - GeometryInstance sphere: + translation: Vector3(-1, -1, 1) + color: Color(144, 77, 144, 1.0) + geometry: GEOMETRY_SPHERE + - GeometryInstance cylinder: + translation: Vector3(2, -1, 1) + color: Color(144, 77, 144, 1.0) + geometry: GEOMETRY_CYLINDER + getSceneByName("main").addChildren(cube, cube1, cube2, sphere, cylinder) -violet.color = Color(0xccaaffff'u32) -lightblue.color = Color(0xaaccffff'u32) -lightblue.setAnchor(0.5, 0.5, 0.5, 0.5) -violet.on_process = - proc(self: NodeRef) = - if isActionJustPressed("change_scene"): - echo "bye from main scene :(" - changeScene("Second scene") # This function changes current scene. + test "Camera3D test": + build: + - Node3D root: + call translate(2, 2, -5) + - Camera3D camera: + call setCurrent() + call changeTarget(root) + root@onInput(self, event): + if event.isInputEventMouseMotion() and event.pressed: + camera.rotate(-event.xrel*0.25, event.yrel*0.25) -lightblue.on_process = - proc(self: NodeRef) = - if isActionJustPressed("change_scene"): - echo "bye from second scene :(" - changeScene("Main") + root@onProcess(self): + if isActionPressed("left"): + root.translate(camera.right * -0.1) + if isActionPressed("right"): + root.translate(camera.right * 0.1) + if isActionPressed("forward"): + root.translate(camera.front*0.1) + if isActionPressed("back"): + root.translate(camera.front*(-0.1)) + getSceneByName("main").addChild(root) -addScene(main) -addScene(second) -setMainScene("Main") -windowLaunch() + test "Sprite3D test": + build: + - Sprite3D sprite: + call loadTexture("assets/anim/2.jpg", GL_RGB) + call translate(-3, -2, 2) + + sprite@onProcess(self): + sprite.rotateY(0.5) + getSceneByName("main").addChild(sprite) + + + test "Launch window": + windowLaunch() diff --git a/tests/test8.nim b/tests/test8.nim index dc91b45c..69e49456 100644 --- a/tests/test8.nim +++ b/tests/test8.nim @@ -1,26 +1,21 @@ -# --- Test 8. Use TextureRect node. --- # +# --- Test 8. Make your own node. --- # import nodesnim -Window("hello world") +type + MyOwnNodeRef = ref MyOwnNodeObj + MyOwnNodeObj = object of NodeRef # NodeRef/Node2DRef/ControlRef/Node3DRef + property: int -var - main = Scene("Main") - texturerect = TextureRect() +proc MyOwnNode(name: string = "MyOwnNode"): MyOwnNodeRef = + nodepattern(MyOwnNodeRef) + # controlpattern()/node2dpattern()/node3dpattern() + result.property = 100 -main.addChild(texturerect) -var texture = load("assets/sharp.jpg") # Load image from file. -texturerect.setTexture(texture) -texturerect.resize(256, 256) -texturerect.setBackgroundColor(Color(1, 0.6, 1, 0.6)) -texturerect.setTextureFilter(Color(1f, 1f, 1f)) -# texture mode can be TEXTURE_CROP, TEXTURE_KEEP_ASPECT_RATIO or TEXTURE_FILL_XY -texturerect.texture_mode = TEXTURE_CROP -texturerect.texture_anchor = Anchor(0.5, 1, 0.5, 1) +build: + - MyOwnNode node: + property: 10 - -addScene(main) -setMainScene("Main") -windowLaunch() +echo node.property diff --git a/tests/test9.nim b/tests/test9.nim deleted file mode 100644 index 59b736da..00000000 --- a/tests/test9.nim +++ /dev/null @@ -1,21 +0,0 @@ -# --- Test 9. Use Label node. --- # -import nodesnim - - -Window("hello world") - -var - main = Scene("Main") - - label = Label() - -main.addChild(label) - -label.setText("Hello, world!\nsecondline\nThis is a long sentence.") # Change label text. -label.setTextAlign(0.2, 0.5, 0.2, 0.5) # try to change it ^^. -label.setSizeAnchor(1, 1) -label.setTextColor(Color(1f, 1f, 1f)) # default text color. - -addScene(main) -setMainScene("Main") -windowLaunch()