123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- # author: Ethosa
- import
- ../thirdparty/opengl,
- ../core/color,
- ../core/stylesheet,
- ../core/image,
- ../core/vector2,
- ../core/tools,
- strutils,
- re
- type
- DrawableObj* = object of RootObj
- shadow*: bool
- border_width*: 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
- shadow_color*: ColorRef
- background_color*: ColorRef
- texture*: GlTextureObj
- DrawableRef* = ref DrawableObj
- let standard_shadow_color: ColorRef = Color(0f, 0f, 0f, 0.5f)
- template drawablepattern*(`type`: untyped): untyped =
- result = `type`(
- texture: GlTextureObj(), border_width: 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,
- shadow_color: standard_shadow_color
- )
- proc Drawable*: DrawableRef =
- drawablepattern(DrawableRef)
- template vd* =
- ## void template
- discard
- 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[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[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[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[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[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)
- glColor(self.shadow_color)
- glVertex2f(x + i.x, y + i.y)
- # right top
- 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(xw + i.x + self.shadow_offset.x, y + i.y - self.shadow_offset.y)
- glColor(self.shadow_color)
- glVertex2f(xw + i.x, y + i.y)
- # right bottom
- 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(xw + i.x + self.shadow_offset.x, yh - i.y - self.shadow_offset.y)
- glColor(self.shadow_color)
- glVertex2f(xw + i.x, yh - i.y)
- # left bottom
- 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, yh + i.y - self.shadow_offset.y)
- glColor(self.shadow_color)
- glVertex2f(x + i.x, yh + i.y)
- glColor4f(0, 0, 0, 0)
- glVertex2f(x + self.shadow_offset.x, y - self.border_radius[0] - self.shadow_offset.y)
- glColor(self.shadow_color)
- glVertex2f(x, y - self.border_radius[0])
- glEnd()
- template draw_template*(drawtype, color, function, secondfunc: untyped): untyped =
- ## Draws colorized vertexes
- ##
- ## Arguments:
- ## - `drawtype` - draw type, like `GL_POLYGON`
- ## - `color` - color for border drawing.
- ## - `function` - function called before `glBegin`
- ## - `secondfunc` - function called after `glEnd`
- glColor4f(`color`.r, `color`.g, `color`.b, `color`.a)
- `function`
- glBegin(`drawtype`)
- for i in vertex:
- glVertex2f(i.x, i.y)
- glEnd()
- `secondfunc`
- template draw_texture_template*(drawtype, color, function, secondfunc: untyped): untyped =
- glEnable(GL_TEXTURE_2D)
- glBindTexture(GL_TEXTURE_2D, self.texture.texture)
- glColor4f(`color`.r, `color`.g, `color`.b, `color`.a)
- `function`
- glBegin(`drawtype`)
- var
- texture_size = self.texture.size
- h = height
- w = width
- if texture_size.x < width:
- let q = width / texture_size.x
- texture_size.x *= q
- texture_size.y *= q
- if texture_size.y < height:
- let q = height / texture_size.y
- texture_size.x *= q
- texture_size.y *= q
- # crop .. :eyes:
- let q = width / texture_size.x
- texture_size.x *= q
- texture_size.y *= q
- h /= height/width
- for i in vertex:
- glTexCoord2f((-x + i.x - w + texture_size.x) / texture_size.x,
- (y - i.y - h + texture_size.y) / texture_size.y)
- glVertex2f(i.x, i.y)
- glEnd()
- `secondfunc`
- glDisable(GL_TEXTURE_2D)
- method enableShadow*(self: DrawableRef, val: bool = true) {.base.} =
- ## Enables shadow, when `val` is true.
- self.shadow = val
- method draw*(self: DrawableRef, x1, y1, width, height: float) {.base.} =
- var
- vertex: seq[Vector2Obj] = @[]
- x = x1
- y = y1
- if self.shadow:
- recalc(true)
- recalc()
- if self.texture.texture > 0'u32:
- draw_texture_template(GL_POLYGON, self.background_color, vd(), vd())
- else:
- draw_template(GL_POLYGON, self.background_color, vd(), vd())
- if self.border_width > 0f:
- draw_template(GL_LINE_LOOP, self.border_color, glLineWidth(self.border_width), glLineWidth(1))
- method getColor*(self: DrawableRef): ColorRef {.base.} =
- ## Returns background color.
- self.background_color
- method loadTexture*(self: DrawableRef, path: string) {.base.} =
- ## Loads texture from the file.
- ##
- ## Arguments:
- ## - `path` is an image path.
- self.texture = load(path)
- self.background_color = Color(1f, 1f, 1f, 1f)
- method setBorderColor*(self: DrawableRef, color: ColorRef) {.base.} =
- ## Changes border color.
- self.border_color = color
- method setBorderWidth*(self: DrawableRef, width: float) {.base.} =
- ## Changes border width.
- self.border_width = width
- method setColor*(self: DrawableRef, color: ColorRef) {.base.} =
- ## Changes background color.
- self.background_color = color
- method setCornerRadius*(self: DrawableRef, radius: float) {.base.} =
- ## Changes corners radius.
- ##
- ## Arguments:
- ## - `radius` is a new corner radius.
- self.border_radius = [radius, radius, radius, radius]
- method setCornerRadius*(self: DrawableRef, r1, r2, r3, r4: float) {.base.} =
- ## Changes corners radius.
- ##
- ## Arguments:
- ## - `r1` is a new left-top radius.
- ## - `r2` is a new right-top radius.
- ## - `r3` is a new right-bottm radius.
- ## - `r4` is a new left-bottm radius.
- self.border_radius = [r1, r2, r3, r4]
- method setCornerDetail*(self: DrawableRef, detail: int) {.base.} =
- ## Changes corners details.
- ##
- ## Arguments:
- ## - `detail` is a new corner detail.
- self.border_detail = [detail, detail, detail, detail]
- method setCornerDetail*(self: DrawableRef, d1, d2, d3, d4: int) {.base.} =
- ## Changes corners details.
- ##
- ## Arguments:
- ## - `d1` is a new left-top detail.
- ## - `d2` is a new right-top detail.
- ## - `d3` is a new right-bottm detail.
- ## - `d4` is a new left-bottm detail.
- self.border_detail = [d1, d2, d3, d4]
- method setTexture*(self: DrawableRef, texture: GlTextureObj) {.base.} =
- ## Changes drawable texture.
- self.texture = texture
- self.background_color = Color(1f, 1f, 1f, 1f)
- method setShadowColor*(self: DrawableRef, clr: ColorRef) {.base.} =
- self.shadow_color = clr
- method setShadowOffset*(self: DrawableRef, offset: Vector2Obj) {.base.} =
- ## Changes shadow offset.
- self.shadow_offset = offset
- method setStyle*(self: DrawableRef, s: StyleSheetRef) {.base.} =
- ## Sets new stylesheet.
- ##
- ## Styles:
- ## - `background-color` - `rgb(1, 1, 1)`; `rgba(1, 1, 1, 1)`; `#ffef`.
- ## - `background-image` - `"assets/image.jpg".
- ## - `background` - the same as `background-image` and `background-color`.
- ## - `border-color` - the same as `background-color`.
- ## - `border-width` - `0`; `8`.
- ## - `border-detail` - corners details. `8`; `8 8 8 8`.
- ## - `border-radius` - corners radius. same as `border-detail`.
- ## - `border` - corners radius and border color. `8 #ffef`.
- ## - `shadow` - enables shadow. `true`; `yes`; `on`; `off`; `no`; `false`.
- ## - `shadow-offset` - XY shadow offset. `3`; `5 10`.
- for i in s.dict:
- var matches: array[20, string]
- case i.key
- # background-color: rgb(51, 100, 255)
- of "background-color":
- var clr = Color(i.value)
- if not clr.isNil():
- self.setColor(clr)
- # background-image: "assets/img.jpg"
- of "background-image":
- self.loadTexture(i.value)
- # 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])
- if not tmpclr.isNil():
- self.setColor(tmpclr)
- # border-color: rgba(55, 255, 177, 0.1)
- of "border-color":
- var clr = Color(i.value)
- if not clr.isNil():
- self.setBorderColor(clr)
- # border-radius: 5
- # border-radius: 2 4 8 16
- of "border-radius":
- let tmp = i.value.split(" ")
- if tmp.len() == 1:
- self.setCornerRadius(parseFloat(tmp[0]))
- elif tmp.len() == 4:
- self.setCornerRadius(parseFloat(tmp[0]), parseFloat(tmp[1]), parseFloat(tmp[2]), parseFloat(tmp[3]))
- # border-detail: 5
- # border-detail: 5 50 64 128
- of "border-detail":
- let tmp = i.value.split(" ")
- if tmp.len() == 1:
- self.setCornerDetail(parseInt(tmp[0]))
- elif tmp.len() == 4:
- self.setCornerDetail(parseInt(tmp[0]), parseInt(tmp[1]), parseInt(tmp[2]), parseInt(tmp[3]))
- # border-width: 5
- of "border-width":
- self.setBorderWidth(parseFloat(i.value))
- # border: 2 turquoise
- of "border":
- let tmp = i.value.rsplit(Whitespace, 1)
- self.setCornerRadius(parseFloat(tmp[0]))
- self.setBorderColor(Color(tmp[1]))
- # shadow: true
- of "shadow":
- self.enableShadow(parseBool(i.value))
- # shadow-offset: 3 3
- of "shadow-offset":
- let tmp = i.value.split(Whitespace, 1)
- if tmp.len() == 1:
- self.setShadowOffset(Vector2(parseFloat(tmp[0])))
- elif tmp.len() == 2:
- self.setShadowOffset(Vector2(parseFloat(tmp[0]), parseFloat(tmp[1])))
- of "shadow-color":
- let clr = Color(i.value)
- if not clr.isNil():
- self.setShadowColor(clr)
- else:
- discard
|