Browse Source

global refactoring

Ethosa 4 years ago
parent
commit
6cfe47d638

+ 0 - 2
README.md

@@ -22,14 +22,12 @@
       ```
 2. Install dependencies
    -  Linux (tested on Ubuntu and Mint):
-      - `sudo apt install -y freeglut3 freeglut3-dev`
       - `sudo apt install --fix-missing -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev`
    -  Windows / MacOS:
       -  [SDL2](https://www.libsdl.org/download-2.0.php)
       -  [SDL2_image](https://www.libsdl.org/projects/SDL_image/)
       -  [SDL2_mixer](https://www.libsdl.org/projects/SDL_mixer/)
       -  [SDL2_ttf](https://www.libsdl.org/projects/SDL_ttf/)
-      -  [freeGLUT](http://freeglut.sourceforge.net/)
       -  Put Runtime binaries in the `.nimble/bin/` folder
 
 ## Features

+ 3 - 1
src/nodesnim/core/font.nim

@@ -163,7 +163,6 @@ proc getCaretPos*(text: StyleText, pos: uint32): tuple[a: Vector2Obj, b: uint16]
       lines = text.splitLines()
       w: cint
       h: cint
-    echo lines
 
     for line in lines:
       result[0].x = 0f
@@ -234,9 +233,12 @@ proc render*(text: StyleText, size: Vector2Obj, anchor: AnchorObj) =
     # free memory
     surface.freeSurface()
     surface = nil
+  text.rendered = true
 
 proc renderTo*(text: StyleText, pos, size: Vector2Obj, anchor: AnchorObj) =
     # Show text
+    if not text.rendered:
+      text.render(size, anchor)
     var
       pos1 = Vector2(pos)
       size1 = Vector2(size)

+ 10 - 51
src/nodesnim/core/input.nim

@@ -13,11 +13,12 @@ type
     MOTION,    ## Mouse motion.
     WHEEL,     ## Mouse wheel.
     KEYBOARD,  ## Keyboard input
+    TEXT,      ## Text input.
     UNKNOWN    ## Unknown event.
 
   InputAction* = object
     kind*: InputEventType
-    key_int*: int8
+    key_int*: cint
     key_cint*: cint
     button_index*: cint
     name*, key*: string
@@ -27,7 +28,7 @@ type
   InputEvent* = ref object
     kind*: InputEventType
     pressed*: bool
-    key_int*: int8
+    key_int*: cint
     key_cint*: cint
     button_index*: cint
     x*, y*, xrel*, yrel*: float
@@ -49,58 +50,10 @@ const
   BUTTON_RELEASE* = 0
   BUTTON_CLICK* = 1
 
-  K_F1* = 1
-  K_F2* = 2
-  K_F3* = 3
-  K_F4* = 4
-  K_F5* = 5
-  K_F6* = 6
-  K_F7* = 7
-  K_F8* = 8
-  K_F9* = 9
-  K_TAB* = 9
-  K_F10* = 10
-  K_F11* = 11
-  K_F12* = 12
-  K_ENTER* = 13
-  K_ESCAPE* = 27
-  K_SPACE* = 32
-  K_NUM_MUL* = 42
-  K_NUM_SUB* = 45
-  K_NUM_ADD* = 43
-  K_NUM_POINT* = 46
-  K_NUM_DIV* = 47
-  K_0* = 48
-  K_1* = 49
-  K_2* = 50
-  K_3* = 51
-  K_4* = 52
-  K_5* = 53
-  K_6* = 54
-  K_7* = 55
-  K_8* = 56
-  K_9* = 57
-  K_LEFT* = 100
-  K_UP* = 101
-  K_RIGHT* = 102
-  K_DOWN* = 103
-  K_PAGE_UP* = 104
-  K_PAGE_DOWN* = 105
-  K_HOME* = 106
-  K_END* = 107
-  K_INSERT* = 108
-  K_SHIFT* = 112
-  K_RIGHT_SHIFT* = 113
-  K_CTRL* = 114
-  K_RIGHT_CTRL* = 115
-  K_ALT* = 116
-  K_RIGHT_ALT* = 117
-  K_DELETE* = 127
-
 
 var
   pressed_keys*: seq[string] = @[]
-  pressed_keys_ints*: seq[int8] = @[]
+  pressed_keys_ints*: seq[cint] = @[]
   pressed_keys_cints*: seq[cint] = @[]
   last_event*: InputEvent = InputEvent()
   last_key_state*: bool = false
@@ -134,6 +87,10 @@ proc isInputEventKeyboard*(a: InputEvent): bool =
   ## Returns true, when `a` kind is a KEYBOARD.
   a.kind == KEYBOARD
 
+proc isInputEventText*(a: InputEvent): bool =
+  ## Returns true, when `a` kind is a KEYBOARD.
+  a.kind == TEXT
+
 
 proc addButtonAction*(a: type Input, name: string, button: cint) =
   ## Adds a new action on button.
@@ -244,3 +201,5 @@ proc `$`*(event: InputEvent): string =
     "InputEventTouchScreen(x: " & $event.x & ", y: " & $event.y & ")"
   of KEYBOARD:
     "InputEventKeyboard(key: " & event.key & ", pressed:" & $event.pressed & ")"
+  of TEXT:
+    "InputEventKeyboard(key: " & event.key & ", pressed:" & $event.pressed & ")"

+ 10 - 1
src/nodesnim/environment.nim

@@ -4,10 +4,14 @@ import core/color
 
 
 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
+    screen_mode*: ScreenMode
   EnvironmentRef* = ref EnvironmentObj
 
 
@@ -17,7 +21,7 @@ proc newEnvironment*(color: ColorRef, brightness: float): EnvironmentRef =
   ## Arguments:
   ## - `color`: ColorRef object for background environment color.
   ## - `brightness` - window brightness with value in range `0..1`
-  EnvironmentRef(color: color, delay: 17, brightness: brightness)
+  EnvironmentRef(color: color, delay: 17, brightness: brightness, screen_mode: SCREEN_MODE_NONE)
 
 proc newEnvironment*(): EnvironmentRef {.inline.} =
   ## Creates a new EnvironmentRef object.
@@ -51,3 +55,8 @@ proc setDelay*(env: EnvironmentRef, delay: int) =
   ## Arguments:
   ## - `delay`: should be ``1000 div FPS``, e.g.: ``1000 div 60 for 60`` frames per second.
   env.delay = delay
+
+proc setScreenMode*(env: EnvironmentRef, mode: ScreenMode) =
+  ## Changes screen mode.
+  ## `mode` should be `SCREEN_MODE_NONE` or `SCREEN_MODE_EXPANDED`.
+  env.screen_mode = mode

+ 1 - 2
src/nodesnim/nodescontrol.nim

@@ -18,7 +18,6 @@ import
   nodescontrol/counter,
   nodescontrol/switch,
   nodescontrol/subwindow,
-  nodescontrol/lineedit,
   nodescontrol/vslider,
   nodescontrol/checkbox
 
@@ -26,4 +25,4 @@ 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, lineedit, vslider, checkbox
+  counter, switch, subwindow, vslider, checkbox

+ 31 - 31
src/nodesnim/nodescontrol/edittext.nim

@@ -1,7 +1,7 @@
 # author: Ethosa
 import
   ../thirdparty/opengl,
-  ../thirdparty/opengl/glut,
+  ../thirdparty/sdl2,
 
   ../core/font,
   ../core/color,
@@ -16,7 +16,8 @@ import
   ../nodes/canvas,
 
   label,
-  control
+  control,
+  unicode
 
 
 type
@@ -44,7 +45,7 @@ proc EditText*(name: string = "EditText", hint: string = "Edit text ..."): EditT
   result.text = stext("")
   result.hint = stext(hint)
   result.hint.setColor(Color("#ccc"))
-  result.text.setColor(Color(0xffffffff'u32))
+  result.text.setColor(Color("#555"))
   result.text_align = Anchor(0, 0, 0, 0)
   result.on_edit = proc(key: string) = discard
   result.kind = EDIT_TEXT_NODE
@@ -65,7 +66,6 @@ method draw*(self: EditTextRef, w, h: Glfloat) =
     caret = self.text.getCaretPos(self.caret_pos)
     xalign = x + self.rect_size.x*self.text_align.x1 - self.rect_min_size.x*self.text_align.x2
     yalign = y - self.rect_size.y*self.text_align.y1 + self.rect_min_size.y*self.text_align.y2
-  echo caret
 
   dec self.blink_time
   if self.blink_time == 0:
@@ -121,44 +121,44 @@ method handle*(self: EditTextRef, event: InputEvent, mouse_on: var NodeRef) =
 
   when not defined(android) and not defined(ios):
     if self.hovered:  # Change cursor, if need
-      glutSetCursor(GLUT_CURSOR_TEXT)
+      setCursor(createSystemCursor(SDL_SYSTEM_CURSOR_IBEAM))
     else:
-      glutSetCursor(GLUT_CURSOR_LEFT_ARROW)
+      setCursor(createSystemCursor(SDL_SYSTEM_CURSOR_ARROW))
 
   if self.focused:
-    if event.kind == KEYBOARD:
-      if event.key_cint == K_LEFT and self.caret_pos > 0 and event.key_cint in pressed_keys_cints:
+    if event.kind == TEXT and not event.pressed:
+      # Other keys
+      if self.caret_pos > 0 and self.caret_pos < self.text.len().uint32:  # insert in caret pos
+        self.setText(($self.text)[0..self.caret_pos-1] & event.key & ($self.text)[self.caret_pos..^1])
+        self.caret_pos += 1
+        self.on_edit(event.key)
+      elif self.caret_pos == 0:  # insert in start of text.
+        self.setText(event.key & ($self.text))
+        self.caret_pos += 1
+        self.on_edit(event.key)
+      elif self.caret_pos == self.text.len().uint32:  # insert in end of text.
+        self.setText(($self.text) & event.key)
+        self.caret_pos += 1
+        self.on_edit(event.key)
+    elif event.kind == KEYBOARD:
+      # Arrows
+      if event.key_int == K_LEFT and self.caret_pos > 0 and event.key_int in pressed_keys_ints:
         self.caret_pos -= 1
-        echo self.caret_pos
-      elif event.key_cint == K_RIGHT and self.caret_pos < self.text.len().uint32 and event.key_cint in pressed_keys_cints:
+      elif event.key_int == K_RIGHT and self.caret_pos < self.text.len().uint32 and event.key_int in pressed_keys_ints:
         self.caret_pos += 1
-        echo self.caret_pos
+
       elif event.key in pressed_keys:  # Normal chars
         if event.key_int == 8:  # Backspace
+          echo ($self.text, ", ", self.caret_pos)
           if self.caret_pos > 1 and self.caret_pos < self.text.len().uint32:
-            self.setText(($self.text)[0..self.caret_pos-2] & ($self.text)[self.caret_pos..^1])
+            self.setText(($self.text).toRunes()[0..self.caret_pos-2].`$` & ($self.text).toRunes()[self.caret_pos..^1].`$`)
             self.caret_pos -= 1
           elif self.caret_pos == self.text.len().uint32 and self.caret_pos > 0:
-            self.setText(($self.text)[0..^2])
+            self.setText(($self.text).toRunes()[0..^2].`$`)
             self.caret_pos -= 1
           elif self.caret_pos == 1:
-            self.setText(($self.text)[1..^1])
+            self.setText(($self.text).toRunes()[1..^1].`$`)
             self.caret_pos -= 1
-        elif event.key_int == 13:
+        elif event.key_int == 13:  # Next line
           self.setText($self.text & "\n")
-          self.caret_pos += 1
-
-        # Other keys
-        elif self.caret_pos > 0 and self.caret_pos < self.text.len().uint32:
-          self.setText(($self.text)[0..self.caret_pos-1] & event.key & ($self.text)[self.caret_pos..^1])
-          self.caret_pos += 1
-          self.on_edit(event.key)
-          echo event.key
-        elif self.caret_pos == 0:
-          self.setText(event.key & ($self.text))
-          self.caret_pos += 1
-          self.on_edit(event.key)
-        elif self.caret_pos == self.text.len().uint32:
-          self.setText(($self.text) & event.key)
-          self.caret_pos += 1
-          self.on_edit(event.key)
+          self.caret_pos += 1

+ 0 - 199
src/nodesnim/nodescontrol/lineedit.nim

@@ -1,199 +0,0 @@
-# author: Ethosa
-## It provides primitive text input.
-import
-  ../thirdparty/opengl,
-  ../thirdparty/opengl/glut,
-
-  ../core/vector2,
-  ../core/rect2,
-  ../core/anchor,
-  ../core/input,
-  ../core/enums,
-  ../core/color,
-
-  ../nodes/node,
-  ../graphics/drawable,
-  control
-
-
-type
-  LineEditObj* = object of ControlRef
-    blit_caret*: bool
-    blit_speed*: float
-    blit_time*: float
-    caret_position*: int
-    font*: pointer          ## Glut font data.
-    spacing*: float         ## Font spacing.
-    size*: float            ## Font size.
-    text*: string           ## LineEdit text.
-    hint_text*: string
-    color*: ColorRef        ## Text color.
-    hint_color*: ColorRef   ## Hint color.
-    caret_color*: ColorRef
-    text_align*: AnchorObj  ## Text align.
-    on_edit*: proc(pressed_key: string): void  ## This called when user press any key.
-  LineEditRef* = ref LineEditObj
-
-
-proc LineEdit*(name: string = "LineEdit"): LineEditRef =
-  ## Creates a new LineEdit.
-  ##
-  ## Arguments:
-  ## - `name` is a node name.
-  runnableExamples:
-    var edit = LineEdit("LineEdit")
-  nodepattern(LineEditRef)
-  controlpattern()
-  result.rect_size.x = 120
-  result.rect_size.y = 32
-  result.text = ""
-  result.font = GLUT_BITMAP_HELVETICA_12
-  result.size = 12
-  result.spacing = 2
-  result.text_align = Anchor(0.5, 0.5, 0.5, 0.5)
-  result.color = Color(1f, 1f, 1f)
-  result.background.setColor(Color(0x454545ff))
-  result.hint_color = Color(0.8, 0.8, 0.8)
-  result.hint_text = "Edit text ..."
-  result.caret_position = 0
-  result.blit_caret = true
-  result.caret_color = Color(1f, 1f, 1f, 0.7)
-  result.blit_speed = 0.05
-  result.blit_time = 0f
-  result.on_edit = proc(key: string) = discard
-  result.kind = LINE_EDIT_NODE
-
-
-method getTextSize*(self: LineEditRef): Vector2Obj {.base.} =
-  ## Returns text size.
-  result = Vector2(0, self.size)
-  for c in self.text:
-    result.x += self.font.glutBitmapWidth(c.int).float
-
-
-method getCharPositionUnderMouse*(self: LineEditRef): int {.base.} =
-  ## Returns char position under mouse.
-  let
-    size = self.getTextSize()
-    textlen = self.text.len()
-    pos = Vector2Obj(x: last_event.x, y: last_event.y) - self.global_position
-  if pos.y > size.y:
-    return textlen
-  else:
-    var
-      res = Vector2()
-      caret_pos = 0
-      current_pos = 0
-      x: float = 0f
-    current_pos = 0
-    res.y += self.spacing + self.size
-    for c in self.text:
-      x += self.font.glutBitmapWidth(c.int).float
-      inc caret_pos
-      inc current_pos
-      if res.y >= pos.y:
-        if current_pos < textlen and x <= pos.x:
-          continue
-        return caret_pos
-
-
-method draw*(self: LineEditRef, w, h: GLfloat) =
-  ## This method uses in the `window.nim`
-  let
-    x = -w/2 + self.global_position.x
-    y = h/2 - self.global_position.y
-    text =
-      if self.text.len() > 0:
-        self.text
-      else:
-        self.hint_text
-    color =
-      if self.text.len() > 0:
-        self.color
-      else:
-        self.hint_color
-    tw = self.font.glutBitmapLength(text).float
-
-  self.background.draw(x, y, self.rect_size.x, self.rect_size.y)
-
-  var
-    char_num = 0
-    tx = x + self.rect_size.x*self.text_align.x1 - tw * self.text_align.x2
-    ty = y - self.rect_size.y*self.text_align.y1 + self.size * self.text_align.y2 - self.size
-  for c in text:
-    glColor4f(color.r, color.g, color.b, color.a)
-    let
-      cw = self.font.glutBitmapWidth(c.int).float
-      right = if self.text_align.x2 > 0.9 and self.text_align.x1 > 0.9: 1f else: 0f
-    if tx >= x and tx < x + self.rect_size.x+right:
-      glRasterPos2f(tx, ty)  # set char position
-      self.font.glutBitmapCharacter(c.int)  # render char
-
-      inc char_num
-      if char_num == self.caret_position and self.blit_caret and self.blit_time > 0.8 and self.focused:
-        glColor4f(self.caret_color.r, self.caret_color.g, self.caret_color.b, self.caret_color.a)
-        glRectf(tx+cw, ty, tx+cw+1.5, ty+self.size-2)
-        if self.blit_time > 2f:
-          self.blit_time = 0f
-    tx += cw
-  self.blit_time += self.blit_speed
-
-  # Press
-  if self.pressed:
-    self.on_press(self, last_event.x, last_event.y)
-
-
-method duplicate*(self: LineEditRef): LineEditRef {.base.} =
-  ## Duplicates LineEdit object and create a new LineEdit.
-  self.deepCopy()
-
-
-method handle*(self: LineEditRef, event: InputEvent, mouse_on: var NodeRef) =
-  ## Handles user input. Thi uses in the `window.nim`.
-  procCall self.ControlRef.handle(event, mouse_on)
-
-  when not defined(android) and not defined(ios):
-    if self.hovered:  # Change cursor, if need
-      glutSetCursor(GLUT_CURSOR_TEXT)
-    else:
-      glutSetCursor(GLUT_CURSOR_LEFT_ARROW)
-
-  if event.kind == MOUSE and event.pressed:
-    self.caret_position = self.getCharPositionUnderMouse()
-
-  if self.focused:
-    if event.kind == KEYBOARD:
-      if event.key_cint == K_LEFT and event.key_cint in pressed_keys_cints and self.caret_position > 0:
-        self.caret_position -= 1
-      elif event.key_cint == K_RIGHT and event.key_cint in pressed_keys_cints and self.caret_position < self.text.len():
-        self.caret_position += 1
-      elif event.key in pressed_keys:  # Normal chars
-        if event.key_int == 8:  # Backspace
-          if self.caret_position > 1 and self.caret_position < self.text.len():
-            self.text = self.text[0..self.caret_position-2] & self.text[self.caret_position..^1]
-            self.caret_position -= 1
-          elif self.caret_position == self.text.len() and self.caret_position > 0:
-            self.text = self.text[0..^2]
-            self.caret_position -= 1
-          elif self.caret_position == 1:
-            self.text = self.text[1..^1]
-            self.caret_position -= 1
-
-        # Other keys
-        elif self.caret_position > 0 and self.caret_position < self.text.len():
-          self.text = self.text[0..self.caret_position-1] & event.key & self.text[self.caret_position..^1]
-          self.caret_position += 1
-          self.on_edit(event.key)
-        elif self.caret_position == 0:
-          self.text = event.key & self.text
-          self.caret_position += 1
-          self.on_edit(event.key)
-        elif self.caret_position == self.text.len():
-          self.text &= event.key
-          self.caret_position += 1
-          self.on_edit(event.key)
-
-
-method setText*(self: LineEditRef, value: string) {.base.} =
-  ## Changes LineEdit text.
-  self.text = value

+ 1 - 0
src/nodesnim/nodescontrol/scroll.nim

@@ -2,6 +2,7 @@
 ## It provides primitive scroll box.
 import
   ../thirdparty/opengl,
+  ../thirdparty/sdl2,
 
   ../core/vector2,
   ../core/rect2,

+ 104 - 109
src/nodesnim/window.nim

@@ -1,46 +1,44 @@
 # author: Ethosa
 import
   thirdparty/opengl,
-  thirdparty/opengl/glut,
   thirdparty/opengl/glu,
+  thirdparty/sdl2,
+  thirdparty/sdl2/image,
 
   core/color,
-  core/exceptions,
   core/input,
+  core/exceptions,
 
   nodes/node,
   nodes/scene,
 
   environment,
+  strutils,
+  unicode,
   os
 
 when defined(debug):
   import logging
 
-var
-  cmdLine {.importc: "cmdLine".}: array[0..255, cstring]
-  cmdCount {.importc: "cmdCount".}: cint
-
+discard sdl2.init(INIT_EVERYTHING)
 
-when not defined(ios) and not defined(android) and not defined(useGlew):
-  when defined(debug):
-    debug("Try to load OpenGL ...")
-  loadExtensions()  # Load OpenGL extensions.
-
-when defined(debug):
-  debug("Try to load freeGLUT ...")
-glutInit(addr cmdCount, addr cmdLine) # Initializ glut lib.
-glutInitDisplayMode(GLUT_DOUBLE)
+discard glSetAttribute(SDL_GL_DOUBLEBUFFER, 1)
+discard glSetAttribute(SDL_GL_RED_SIZE, 5)
+discard glSetAttribute(SDL_GL_GREEN_SIZE, 6)
+discard glSetAttribute(SDL_GL_BLUE_SIZE, 5)
 
 
 var
   env*: EnvironmentRef = newEnvironment()
   width, height: cint
-  max_distance*: GLdouble = int64.high.GLdouble
   main_scene*: SceneRef = nil
+  windowptr: WindowPtr
+  glcontext: GlContextPtr
   current_scene*: SceneRef = nil
   scenes*: seq[SceneRef] = @[]
   paused*: bool = false
+  running*: bool = true
+  event = sdl2.defaultEvent
 
 
 # --- Callbacks --- #
@@ -50,8 +48,7 @@ var
 
 proc display {.cdecl.} =
   ## Displays window.
-  let (r, g, b, a) = env.color.toFloatTuple()
-  glClearColor(r*env.brightness, g*env.brightness, b*env.brightness, a*env.brightness)
+  glClearColor(env.color.r, env.color.g, env.color.b, env.color.a)
   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
   glEnable(GL_BLEND)
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
@@ -63,7 +60,7 @@ proc display {.cdecl.} =
 
   # Update window.
   glFlush()
-  glutSwapBuffers()
+  windowptr.glSwapWindow()
   os.sleep(env.delay)
 
 
@@ -90,39 +87,35 @@ template check(event, condition, conditionelif: untyped): untyped =
   else:
     press_state = 0
 
-proc mouse(button, state, x, y: cint) {.cdecl.} =
+proc mouse(button, x, y: cint, pressed: bool) {.cdecl.} =
   ## Handle mouse input.
-  check(InputEventMouseButton, last_event.pressed and state == GLUT_DOWN, state == GLUT_DOWN)
+  check(InputEventMouseButton, last_event.pressed and pressed, pressed)
   last_event.button_index = button
   last_event.x = x.float
   last_event.y = y.float
   last_event.kind = MOUSE
-  mouse_pressed = state == GLUT_DOWN
-  last_event.pressed = state == GLUT_DOWN
+  mouse_pressed = pressed
+  last_event.pressed = pressed
 
   current_scene.handleScene(last_event, mouse_on, paused)
 
-proc wheel(button, dir, x, y: cint) {.cdecl.} =
+proc wheel(x, y: cint) {.cdecl.} =
   ## Handle mouse wheel input.
   check(InputEventMouseWheel, false, false)
-  last_event.button_index = button
-  last_event.x = x.float
-  last_event.y = y.float
   last_event.kind = WHEEL
-  last_event.yrel = dir.float
+  last_event.xrel = x.float
+  last_event.yrel = y.float
 
   current_scene.handleScene(last_event, mouse_on, paused)
 
-proc keyboardpress(c: int8, x, y: cint) {.cdecl.} =
+proc keyboardpress(c: cint) {.cdecl.} =
   ## Called when press any key on keyboard.
   if c < 0:
     return
-  let key = $c.char
+  let key = $c
   check(InputEventKeyboard, last_event.pressed, true)
   last_event.key = key
   last_event.key_int = c
-  last_event.x = x.float
-  last_event.y = y.float
   if key notin pressed_keys:
     pressed_keys.add(key)
     pressed_keys_ints.add(c)
@@ -132,16 +125,23 @@ proc keyboardpress(c: int8, x, y: cint) {.cdecl.} =
 
   current_scene.handleScene(last_event, mouse_on, paused)
 
-proc keyboardup(c: int8, x, y: cint) {.cdecl.} =
+proc textinput(c: TextInputEventPtr) {.cdecl.} =
+  ## Called when start text input
+  last_event.key = toRunes(join(c.text[0..<32]))[0].toUtf8()
+  last_event.kind = TEXT
+  last_key_state = key_state
+  key_state = true
+
+  current_scene.handleScene(last_event, mouse_on, paused)
+
+proc keyboardup(c: cint) {.cdecl.} =
   ## Called when any key no more pressed.
   if c < 0:
     return
-  let key = $c.char
+  let key = $c
   check(InputEventKeyboard, false, false)
   last_event.key = key
   last_event.key_int = c
-  last_event.x = x.float
-  last_event.y = y.float
   last_event.kind = KEYBOARD
   last_key_state = key_state
   key_state = false
@@ -155,43 +155,6 @@ proc keyboardup(c: int8, x, y: cint) {.cdecl.} =
 
   current_scene.handleScene(last_event, mouse_on, paused)
 
-proc keyboardspecialpress(c: cint, x, y: cint) {.cdecl.} =
-  ## Called when press any key on keyboard.
-  if c < 0:
-    return
-  check(InputEventKeyboard, last_event.pressed, true)
-  last_event.key = $c
-  last_event.key_cint = c
-  last_event.x = x.float
-  last_event.y = y.float
-  if c notin pressed_keys_cints:
-    pressed_keys_cints.add(c)
-  last_event.kind = KEYBOARD
-  last_key_state = key_state
-  key_state = true
-
-  current_scene.handleScene(last_event, mouse_on, paused)
-
-proc keyboardspecialup(c: cint, x, y: cint) {.cdecl.} =
-  ## Called when any key no more pressed.
-  if c < 0:
-    return
-  check(InputEventKeyboard, false, false)
-  last_event.key_cint = c
-  last_event.x = x.float
-  last_event.y = y.float
-  last_event.kind = KEYBOARD
-  last_key_state = key_state
-  key_state = false
-  var i = 0
-  for k in pressed_keys_cints:
-    if k == c:
-      pressed_keys_cints.delete(i)
-      break
-    inc i
-
-  current_scene.handleScene(last_event, mouse_on, paused)
-
 proc motion(x, y: cint) {.cdecl.} =
   ## Called on any mouse motion.
   last_event.kind = MOTION
@@ -211,8 +174,6 @@ proc addScene*(scene: SceneRef) =
   ## - `scene` - pointer to the Scene object.
   if scene notin scenes:
     scenes.add(scene)
-  scene.rect_size.x = width.float
-  scene.rect_size.y = height.float
 
 proc addMainScene*(scene: SceneRef) =
   ## Adds a new scene in the app and set it mark it as main scene.
@@ -223,7 +184,7 @@ proc addMainScene*(scene: SceneRef) =
     scenes.add(scene)
   main_scene = scene
 
-proc changeScene*(name: string, extra: seq[tuple[k: string, v: string]] = @[]): bool {.discardable.} =
+proc changeScene*(name: string): bool {.discardable.} =
   ## Changes current scene.
   ##
   ## Arguments:
@@ -233,16 +194,12 @@ proc changeScene*(name: string, extra: seq[tuple[k: string, v: string]] = @[]):
     if scene.name == name:
       if current_scene != nil:
         current_scene.exit()
-        current_scene.data = @[]
       current_scene = nil
       current_scene = scene
-      current_scene.data = extra
       current_scene.enter()
       current_scene.reAnchorScene(width.GLfloat, height.GLfloat, paused)
       result = true
       break
-  when defined(debug):
-    debug("result of `changeScene` is ", result)
 
 proc setMainScene*(name: string) =
   ## Set up main scene.
@@ -256,12 +213,17 @@ proc setMainScene*(name: string) =
 
 proc setTitle*(title: cstring) =
   ## Changes window title.
-  if not window_created:
-    raise newException(WindowError, "Window not created!")
-  glutSetWindowTitle(title)
+  if window_created:
+    windowptr.setTitle(title)
+  else:
+    raise newException(WindowError, "Window not launched!")
 
-proc setMaxDistance*(distance: GLdouble) =
-  max_distance = distance
+proc setIcon*(icon_path: cstring) =
+  ## Changes window title.
+  if window_created:
+    windowptr.setIcon(image.load(icon_path))
+  else:
+    raise newException(WindowError, "Window not launched!")
 
 proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} =
   ## Creates a new window pointer
@@ -269,16 +231,17 @@ proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} =
   ## Arguments:
   ## - `title` - window title.
   # Set up window.
-  glutInitWindowSize(w, h)
-  glutInitWindowPosition(100, 100)
-  when defined(debug):
-    debug("result of `glutCreateWindow` is ", glutCreateWindow(title))
-  else:
-    discard glutCreateWindow(title)
+  once:
+    when not defined(android) and not defined(ios):
+      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)
+  glcontext = windowptr.glCreateContext()
 
   # Set up OpenGL
-  let (r, g, b, a) = env.color.toFloatTuple()
-  glClearColor(r, g, b, a)
   glShadeModel(GL_SMOOTH)
   glClear(GL_COLOR_BUFFER_BIT)
   glEnable(GL_COLOR_MATERIAL)
@@ -288,26 +251,58 @@ proc Window*(title: cstring, w: cint = 640, h: cint = 360) {.cdecl.} =
   window_created = true
 
 
+proc onReshape(userdata: pointer; event: ptr Event): Bool32 {.cdecl.} =
+  if event.kind == WindowEvent:
+    case evWindow(event[]).event
+    of WindowEvent_Resized, WindowEvent_SizeChanged, WindowEvent_Minimized, WindowEvent_Maximized, WindowEvent_Restored:
+      windowptr.getSize(width, height)
+      if env.screen_mode == SCREEN_MODE_NONE:
+        reshape(width, height)
+        display()
+    else:
+      discard
+  False32
+
 proc windowLaunch* =
-  ## Start main window loop.
-  when defined(debug):
-    info("launch window ...")
-  glutDisplayFunc(display)
-  glutIdleFunc(display)
-  when not defined(android) and not defined(ios):
-    glutReshapeFunc(reshape)
-    glutMouseWheelFunc(wheel)
-  glutMouseFunc(mouse)
-  glutKeyboardFunc(keyboardpress)
-  glutKeyboardUpFunc(keyboardup)
-  glutSpecialFunc(keyboardspecialpress)
-  glutSpecialUpFunc(keyboardspecialup)
-  glutMotionFunc(motion)
-  glutPassiveMotionFunc(motion)
   if main_scene == nil:
     raise newException(SceneError, "Main scene is not indicated!")
   changeScene(main_scene.name)
   when defined(debug):
     info("window launched")
-  glutMainLoop()
+
+  addEventWatch(onReshape, windowptr)
+
+  while running:
+    while sdl2.pollEvent(event):
+      case event.kind
+      of QuitEvent:
+        running = false
+      of KeyDown:
+        let e = evKeyboard(event)
+        keyboardpress(e.keysym.sym)
+      of KeyUp:
+        let e = evKeyboard(event)
+        keyboardup(e.keysym.sym)
+      of TextInput:
+        let e = text(event)
+        textinput(e)
+      of MouseMotion:
+        let e = evMouseMotion(event)
+        motion(e.x, e.y)
+      of MouseButtonDown:
+        let e = evMouseButton(event)
+        mouse(e.button.cint, e.x, e.y, true)
+      of MouseButtonUp:
+        let e = evMouseButton(event)
+        mouse(e.button.cint, e.x, e.y, false)
+      of MouseWheel:
+        let e = evMouseWheel(event)
+        wheel(e.x, e.y)
+      else:
+        discard
+    display()
+
   current_scene.exit()
+  sdl2.glDeleteContext(glcontext)
+  sdl2.destroy(windowptr)
+  sdl2.quit()

+ 7 - 8
tests/README.md

@@ -38,11 +38,10 @@
 - [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 LineEdit node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test39.nim)
-- [Use Scene builder.](https://github.com/Ethosa/nodesnim/blob/master/tests/test40.nim)
-- [Use AnimationPlayer node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test41.nim)
-- [Use StyleSheet object.](https://github.com/Ethosa/nodesnim/blob/master/tests/test42.nim)
-- [Use Drawable and Control.](https://github.com/Ethosa/nodesnim/blob/master/tests/test43.nim)
-- [Use CheckBox node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test44.nim)
-- [Use GradientDrawable and Control.](https://github.com/Ethosa/nodesnim/blob/master/tests/test45.nim)
-- [Use TileMap node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test46.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)

+ 10 - 9
tests/test39.nim

@@ -1,16 +1,17 @@
-# --- Test 39. Use LineEdit node. --- #
+# --- Test 39. Use Scene builder. --- #
 import nodesnim
 
+Window("scene builder")
 
-Window("smth here")
+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
 
-var
-  scene = Scene()
-  line = LineEdit()
-
-line.setAnchor(0.5, 0.5, 0.5, 0.5)
-
-scene.addChild(line)
 addMainScene(scene)
 windowLaunch()

+ 30 - 9
tests/test40.nim

@@ -1,17 +1,38 @@
-# --- Test 40. Use Scene builder. --- #
+# --- Test 40. Use AnimationPlayer. --- #
 import nodesnim
 
-Window("scene builder")
+Window("AnimationPlayer")
 
 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
+    - 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()

+ 18 - 34
tests/test41.nim

@@ -1,38 +1,22 @@
-# --- Test 41. Use AnimationPlayer. --- #
+# --- Test 41. Use StyleSheet object. --- #
 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)
+var mystyle = style(
+  {
+    background-color: rgba(255, 125, 255, 0.7),
+    color: rgb(34, 34, 34),
+    font-size: 1,
+    text-align: center
+  })
+echo mystyle
 
-addMainScene(scene)
-windowLaunch()
+var background = Color(mystyle["background-color"])
+
+assert background == Color(255, 125, 255, 0.7)
+
+echo StyleSheet(
+  {
+    "background-color": "#f2f2f7"
+  }
+)

+ 26 - 14
tests/test42.nim

@@ -1,22 +1,34 @@
-# --- Test 42. Use StyleSheet object. --- #
+# --- Test 42. Use Drawable and Control. --- #
 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
+Window("drawable oops")
+
+build:
+  - Scene scene:
+    - Control ctrl
+    - Control ctrl1:
+      call move(350, 100)
+      call setSizeAnchor(0.2, 0.2)
 
-var background = Color(mystyle["background-color"])
+ctrl1.background.setTexture(load("assets/sharp.jpg"))
+ctrl1.background.setCornerRadius(25)
+ctrl1.background.setCornerDetail(25)
 
-assert background == Color(255, 125, 255, 0.7)
 
-echo StyleSheet(
+ctrl.resize(256, 96)
+ctrl.move(64, 64)
+ctrl.setStyle(style(
   {
-    "background-color": "#f2f2f7"
+    background-color: rgb(33, 65, 87),
+    border-radius: 8,
+    border-width: 1,
+    border-color: rgb(0, 0, 0),
+    shadow: true,
+    shadow-offset: 3,
+    size-anchor: "0.5 0.7"
   }
-)
+))
+
+addMainScene(scene)
+windowLaunch()

+ 11 - 22
tests/test43.nim

@@ -1,4 +1,4 @@
-# --- Test 43. Use Drawable and Control. --- #
+# --- Test 43. Use CheckBox node. --- #
 import nodesnim
 
 
@@ -6,29 +6,18 @@ Window("drawable oops")
 
 build:
   - Scene scene:
-    - Control ctrl
-    - Control ctrl1:
-      call move(350, 100)
-      call setSizeAnchor(0.2, 0.2)
+    - CheckBox box:
+      call setText("smth checkbox")
+      call enable()
+    - ColorRect rect:
+      call move(100, 100)
 
-ctrl1.background.setTexture(load("assets/sharp.jpg"))
-ctrl1.background.setCornerRadius(25)
-ctrl1.background.setCornerDetail(25)
+box@on_toggle(self, value):
+  if value:
+    rect.color.a = 1f
+  else:
+    rect.color.a = 0f
 
 
-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: 3,
-    size-anchor: "0.5 0.7"
-  }
-))
-
 addMainScene(scene)
 windowLaunch()

+ 15 - 11
tests/test44.nim

@@ -1,4 +1,4 @@
-# --- Test 44. Use CheckBox node. --- #
+# --- Test 44. Use GradientDrawable and Control. --- #
 import nodesnim
 
 
@@ -6,17 +6,21 @@ Window("drawable oops")
 
 build:
   - Scene scene:
-    - CheckBox box:
-      call setText("smth checkbox")
-      call enable()
-    - ColorRect rect:
-      call move(100, 100)
+    - Control ctrl:
+      call resize(100, 150)
+      call move(150, 50)
 
-box@on_toggle(self, value):
-  if value:
-    rect.color.a = 1f
-  else:
-    rect.color.a = 0f
+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)

+ 15 - 20
tests/test45.nim

@@ -1,27 +1,22 @@
-# --- Test 45. Use GradientDrawable and Control. --- #
+# --- Test 45. Use TileMap node. --- #
 import nodesnim
 
+Window("Tilemap test")
 
-Window("drawable oops")
+var
+  tileset = TileSet("assets/tilesets/land.png", Vector2(64, 64), GL_RGBA)
 
 build:
-  - Scene scene:
-    - Control ctrl:
-      call resize(100, 150)
-      call move(150, 50)
+  - Scene main:
+    - TileMap map:
+      call setTileSet(tileset)
+      call resizeMap(newVector2(8096, 512))
+      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))
 
-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)
+addMainScene(main)
 windowLaunch()

+ 0 - 22
tests/test46.nim

@@ -1,22 +0,0 @@
-# --- Test 46. 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)
-      call resizeMap(newVector2(8096, 512))
-      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()