فهرست منبع

refactor font and edittext

Ethosa 4 سال پیش
والد
کامیت
05a59dc02c

+ 4 - 0
README.md

@@ -9,6 +9,10 @@ The Nim GUI/2D framework based on OpenGL and SDL2.
 [![time tracker](https://wakatime.com/badge/github/Ethosa/nodesnim.svg)](https://wakatime.com/badge/github/Ethosa/nodesnim)
 [![test](https://github.com/Ethosa/nodesnim/workflows/test/badge.svg)](https://github.com/Ethosa/nodesnim/actions)
 
+
+[![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)
+
 <h4>Stable version - 0.3.2</h4>
 </div>
 

+ 1 - 0
src/nodesnim/core/exceptions.nim

@@ -1,5 +1,6 @@
 # author: Ethosa
 
 type
+  ResourceError* {.size: sizeof(int8).} = object of ValueError
   SceneError* {.size: sizeof(int8).} = object of ValueError
   WindowError* {.size: sizeof(int8).} = object of ValueError

+ 122 - 62
src/nodesnim/core/font.nim

@@ -10,11 +10,9 @@ import
   anchor,
   color,
   nodes_os,
+  exceptions,
   unicode
 
-when defined(debug):
-  import logging
-
 
 type
   StyleUnicode* = ref object
@@ -41,6 +39,62 @@ proc stext*(text: string, color: ColorRef = Color(1f, 1f, 1f), underline: bool =
   result.rendered = false
 
 
+# ------ Operators ------ #
+
+proc len*(text: StyleText): int =
+  text.chars.len()
+
+proc `$`*(c: StyleUnicode): string =
+  c.c
+
+proc `$`*(text: StyleText): string =
+  for i in text.chars:
+    result &= $i
+
+proc `&`*(text: StyleText, c: StyleUnicode): StyleText =
+  result = text
+  result.chars.add(c)
+
+proc `&`*(text, t: StyleText): StyleText =
+  result = text
+  for c in t.chars:
+    result.chars.add(c)
+
+proc `&`*(text: StyleText, t: string): StyleText =
+  text & stext(t)
+
+proc `&`*(text: string, c: StyleUnicode): 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 string, c: StyleUnicode) =
+  text = text & $c
+
+proc `&=`*(text: var string, t: StyleText) =
+  text = text & $t
+
+proc `&=`*(text: var StyleText, t: string) =
+  text &= stext(t)
+
+proc `[]`*(text: StyleText, index: int): StyleUnicode =
+  text.chars[index]
+
+proc `[]`*[T, U](text: StyleText, slice: HSlice[T, U]): StyleText =
+  result = stext""
+  for i in text.chars[slice.a..slice.b]:
+    result &= i
+
+
+# ------ Funcs ------ #
+
 proc toUpper*(text: StyleText): StyleText =
   result = text.deepCopy()
   for i in result.chars:
@@ -64,7 +118,6 @@ 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
 
@@ -89,49 +142,7 @@ proc loadFont*(font: cstring, size: cint): FontPtr =
   openFont(font, size)
 
 
-proc len*(text: StyleText): int =
-  text.chars.len()
-
-proc `$`*(c: StyleUnicode): string =
-  c.c
-
-proc `$`*(text: StyleText): string =
-  for i in text.chars:
-    result &= $i
-
-proc `&`*(text: StyleText, c: StyleUnicode): StyleText =
-  result = text
-  result.chars.add(c)
-
-proc `&`*(text, t: StyleText): StyleText =
-  result = text
-  for c in t.chars:
-    result.chars.add(c)
-
-proc `&`*(text: StyleText, t: string): StyleText =
-  text & stext(t)
-
-proc `&`*(text: string, c: StyleUnicode): 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 string, c: StyleUnicode) =
-  text = text & $c
-
-proc `&=`*(text: var string, t: StyleText) =
-  text = text & $t
-
-proc `&=`*(text: var StyleText, t: string) =
-  text &= stext(t)
-
+# ------ Utils ------ #
 
 proc splitLines*(text: StyleText): seq[StyleText] =
   result = @[stext""]
@@ -144,6 +155,17 @@ proc splitLines*(text: StyleText): seq[StyleText] =
         result.add(stext"")
         inc line
 
+proc split*(text: StyleText, splitval: string): seq[StyleText] =
+  result = @[stext""]
+  let size = splitval.len
+  var i = 0
+  while i+size-1 < text.len:
+    if $text[i..i+size-1] != splitval:
+      result[^1].chars.add(text[i])
+      inc i
+    else:
+      result.add(stext"")
+      inc i, size
 
 proc getTextSize*(text: StyleText): Vector2Obj =
   result = Vector2()
@@ -189,12 +211,49 @@ proc getCaretPos*(text: StyleText, pos: uint32): tuple[a: Vector2Obj, b: uint16]
         return result
     result[0].y -= text.spacing
 
-proc renderSurface*(text: StyleText, anchor: AnchorObj): SurfacePtr =
+proc getPosUnderPoint*(text: StyleText, global_pos, text_pos: Vector2Obj): uint32 =
+  ## Returns caret position under mouse.
+  if not text.font.isNil():
+    let
+      local_pos = global_pos - text_pos
+      lines = text.splitLines()
+    var
+      w: cint
+      h: cint
+      x: float = 0f
+      y: float = 0f
+      position = Vector2(-1, -1)
+
+    for line in lines:
+      discard text.font.sizeUtf8(($line).cstring, addr w, addr h)
+      if local_pos.y >= y and local_pos.y <= y + h.float:
+        position.y = y
+      x = 0
+      for c in line.chars:
+        discard text.font.sizeUtf8(($c).cstring, addr w, addr h)
+        x += w.float
+        result += 1
+        if local_pos.x >= x and local_pos.x <= x+w.float and position.y != -1f:
+          position.x = x
+          break
+      if position.x != -1f:
+        break
+      y += text.spacing + h.float
+      result += 1
+    if position.x == -1f:
+      result = 0
+
+
+# ------ Render ------ #
+
+proc renderSurface*(text: StyleText, align: AnchorObj): SurfacePtr =
+  ## Renders the surface and returns it, if available.
+  ##
+  ## Arguments:
+  ##   - `align` -- text align.
   when defined(debug):
     if text.font.isNil():
-      error("Font is not loaded!")
-      text.rendered = true
-      return
+      raise newException(ResourceError, "Font isn't loaded!")
 
   if not text.font.isNil() and $text != "":
     let
@@ -210,7 +269,7 @@ proc renderSurface*(text: StyleText, anchor: AnchorObj): SurfacePtr =
 
     for line in lines:
       discard text.font.sizeUtf8(($line).cstring, addr w, addr h)
-      var x = (textsize.x * anchor.x1 - w.float * anchor.x2).cint
+      var x = (textsize.x * align.x1 - w.float * align.x2).cint
       for c in line.chars:
         discard text.font.sizeUtf8(($c).cstring, addr w, addr h)
         var
@@ -224,8 +283,9 @@ proc renderSurface*(text: StyleText, anchor: AnchorObj): SurfacePtr =
       y += h + text.spacing.cint
     return surface
 
-proc render*(text: StyleText, size: Vector2Obj, anchor: AnchorObj) =
-  var surface = renderSurface(text, anchor)
+proc render*(text: StyleText, size: Vector2Obj, align: AnchorObj) =
+  ## Translates SDL2 surface to OpenGL texture and frees surface memory.
+  var surface = renderSurface(text, align)
 
   if not surface.isNil():
     text.texture.size.x = surface.w.float
@@ -247,10 +307,10 @@ proc render*(text: StyleText, size: Vector2Obj, anchor: AnchorObj) =
     surface = nil
   text.rendered = true
 
-proc renderTo*(text: StyleText, pos, size: Vector2Obj, anchor: AnchorObj) =
+proc renderTo*(text: StyleText, pos, size: Vector2Obj, align: AnchorObj) =
     # Show text
     if not text.rendered:
-      text.render(size, anchor)
+      text.render(size, align)
     var
       pos1 = Vector2(pos)
       size1 = Vector2(size)
@@ -260,27 +320,27 @@ proc renderTo*(text: StyleText, pos, size: Vector2Obj, anchor: AnchorObj) =
 
     if textsize.x < size1.x:
       size1.x = textsize.x
-      pos1.x += size.x*anchor.x1 - textsize.x*anchor.x2
+      pos1.x += size.x*align.x1 - textsize.x*align.x2
     if textsize.y < size1.y:
       size1.y = textsize.y
-      pos1.y -= size.y*anchor.y1 - textsize.y*anchor.y2
+      pos1.y -= size.y*align.y1 - textsize.y*align.y2
 
     if textsize.x > size1.x:
       let
-        x1 = (size1.x*anchor.x1 - textsize.x*anchor.x2) / textsize.x
+        x1 = (size1.x*align.x1 - textsize.x*align.x2) / textsize.x
         x2 =
           if x1 > 0.5:
-            1f - ((size1.x*anchor.x1 - textsize.x*anchor.x2 + textsize.x) / textsize.x)
+            1f - ((size1.x*align.x1 - textsize.x*align.x2 + textsize.x) / textsize.x)
           else:
             x1 + (size1.x / textsize.x)
       texcord[0] = abs(x2)
       texcord[2] = abs(x1)
     if textsize.y > size1.y:
       let
-        y1 = (size1.y*anchor.y1 - textsize.y*anchor.y2) / textsize.y
+        y1 = (size1.y*align.y1 - textsize.y*align.y2) / textsize.y
         y2 =
           if y1 > 0.5:
-            1f - ((size1.y*anchor.y1 - textsize.y*anchor.y2 + textsize.y) / textsize.y)
+            1f - ((size1.y*align.y1 - textsize.y*align.y2 + textsize.y) / textsize.y)
           else:
             y1 + (size1.y / textsize.y)
       texcord[1] = abs(y2)

+ 5 - 7
src/nodesnim/core/image.nim

@@ -4,10 +4,8 @@ import
   ../thirdparty/sdl2,
   ../thirdparty/sdl2/image,
 
-  vector2
-
-when defined(debug):
-  import logging
+  vector2,
+  exceptions
 
 
 type
@@ -16,7 +14,7 @@ type
     size*: Vector2Obj
 
 
-proc load*(file: cstring, x, y: var float, mode: Glenum = GL_RGB): Gluint =
+proc load*(file: string, x, y: var float, mode: Glenum = GL_RGB): Gluint =
   ## Loads image from file and returns texture ID.
   ##
   ## Arguments:
@@ -26,7 +24,7 @@ proc load*(file: cstring, x, y: var float, mode: Glenum = GL_RGB): Gluint =
     textureid: Gluint
   when defined(debug):
     if surface.isNil():
-      error("image \"", file, "\" not loaded!")
+      raise newException(ResourceError, "image \"" & file & "\" not loaded!")
   x = surface.w.float
   y = surface.h.float
 
@@ -49,7 +47,7 @@ proc load*(file: cstring, x, y: var float, mode: Glenum = GL_RGB): Gluint =
   textureid
 
 
-proc load*(file: cstring, mode: Glenum = GL_RGB): GlTextureObj =
+proc load*(file: string, mode: Glenum = GL_RGB): GlTextureObj =
   ## Loads GL texture.
   ##
   ## Arguments:

+ 3 - 5
src/nodesnim/core/tileset.nim

@@ -4,10 +4,9 @@ import
   ../thirdparty/sdl2,
   ../thirdparty/sdl2/image,
 
-  vector2
+  vector2,
+  exceptions
 
-when defined(debug):
-  import logging
 
 type
   TileSetObj* = ref object
@@ -22,8 +21,7 @@ proc TileSet*(img: string, tile_size: Vector2Obj, mode: Glenum = GL_RGB): TileSe
     textureid: Gluint = 0
   when defined(debug):
     if surface.isNil():
-      error("image \"", img, "\" not loaded!")
-      return
+      raise newException(ResourceError, "image \"" & img & "\" not loaded!")
 
   glGenTextures(1, textureid.addr)
   glBindTexture(GL_TEXTURE_2D, textureid)

+ 1 - 1
src/nodesnim/nodes2d/sprite.nim

@@ -98,7 +98,7 @@ method getGlobalMousePosition*(self: SpriteRef): Vector2Obj {.inline.} =
   ## Returns mouse position.
   Vector2Obj(x: last_event.x, y: last_event.y)
 
-method loadTexture*(self: SpriteRef, file: cstring, mode = GL_RGB) {.base.} =
+method loadTexture*(self: SpriteRef, file: string, mode = GL_RGB) {.base.} =
   ## Loads a new texture from file.
   ##
   ## Arguments:

+ 23 - 20
src/nodesnim/nodescontrol/edittext.nim

@@ -16,8 +16,7 @@ import
   ../nodes/canvas,
 
   label,
-  control,
-  unicode
+  control
 
 
 type
@@ -31,7 +30,7 @@ type
     on_edit*: proc(pressed_key: string): void  ## This called when user press any key.
 
 const
-  BLINK_TIME: uint8 = 20
+  BLINK_TIME: uint8 = 15
   BLINK_WIDTH: float = 2
 
 proc EditText*(name: string = "EditText", hint: string = "Edit text ..."): EditTextRef =
@@ -136,6 +135,11 @@ method handle*(self: EditTextRef, event: InputEvent, mouse_on: var NodeRef) =
     else:
       setCursor(createSystemCursor(SDL_SYSTEM_CURSOR_ARROW))
 
+  if event.kind == MOUSE and event.pressed and self.hovered:
+    self.caret_pos = self.text.getPosUnderPoint(
+      self.getGlobalMousePosition(),
+      self.global_position + self.rect_size/2 - self.text.getTextSize()/2)
+
   if self.focused:
     if event.kind == TEXT and not event.pressed:
       # Other keys
@@ -151,24 +155,23 @@ method handle*(self: EditTextRef, event: InputEvent, mouse_on: var NodeRef) =
         self.setText(($self.text) & event.key)
         self.caret_pos += 1
         self.on_edit(event.key)
-    elif event.kind == KEYBOARD:
+    elif event.kind == KEYBOARD and event.key in pressed_keys:
       # Arrows
-      if event.key_int == K_LEFT and self.caret_pos > 0 and event.key_int in pressed_keys_ints:
+      if event.key_int == K_LEFT and self.caret_pos > 0:
         self.caret_pos -= 1
-      elif event.key_int == K_RIGHT and self.caret_pos < self.text.len().uint32 and event.key_int in pressed_keys_ints:
+      elif event.key_int == K_RIGHT and self.caret_pos < self.text.len().uint32:
         self.caret_pos += 1
 
-      elif event.key in pressed_keys:  # Normal chars
-        if event.key_int == 8:  # Backspace
-          if self.caret_pos > 1 and self.caret_pos < self.text.len().uint32:
-            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).toRunes()[0..^2].`$`)
-            self.caret_pos -= 1
-          elif self.caret_pos == 1:
-            self.setText(($self.text).toRunes()[1..^1].`$`)
-            self.caret_pos -= 1
-        elif event.key_int == 13:  # Next line
-          self.setText($self.text & "\n")
-          self.caret_pos += 1
+      elif event.key_int == 8:  # Backspace
+        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.caret_pos -= 1
+        elif self.caret_pos == self.text.len().uint32 and self.caret_pos > 0:
+          self.setText($self.text[0..^2])
+          self.caret_pos -= 1
+        elif self.caret_pos == 1:
+          self.setText($self.text[1..^1])
+          self.caret_pos -= 1
+      elif event.key_int == 13:  # Next line
+        self.setText($self.text[0..self.caret_pos-1] & "\n" & $self.text[self.caret_pos..^1])
+        self.caret_pos += 1

+ 1 - 1
src/nodesnim/nodescontrol/subwindow.nim

@@ -247,7 +247,7 @@ method setIcon*(self: SubWindowRef, gltexture: GlTextureObj) {.base.} =
   self.icon = gltexture
 
 
-method setIcon*(self: SubWindowRef, file: cstring) {.base.} =
+method setIcon*(self: SubWindowRef, file: string) {.base.} =
   ## Loads icon from file.
   ##
   ## Arguments:

+ 1 - 1
src/nodesnim/nodescontrol/texture_rect.nim

@@ -122,7 +122,7 @@ method duplicate*(self: TextureRectRef): TextureRectRef {.base.} =
   ## Duplicates TextureRect and create a new TextureRect.
   self.deepCopy()
 
-method loadTexture*(self: TextureRectRef, file: cstring) {.base.} =
+method loadTexture*(self: TextureRectRef, file: string) {.base.} =
   ## Loads texture from file.
   ##
   ## Arguments:

+ 1 - 1
tests/README.md

@@ -15,7 +15,7 @@
 - [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 TextEdit node.](https://github.com/Ethosa/nodesnim/blob/master/tests/test16.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)