git.haldean.org e / master comp.nim
master

Tree @master (Download .tar.gz)

comp.nim @masterraw · history · blame

import blit

import bitops
import illwill
import math
import unicode

type
  LayoutTag* {.pure.} = enum
    center, left, right,
    floatCenter

  Layer* = ref object
    blit*: Blit
    src*: Pt
    dst*: Rect
    tag*: LayoutTag

  Comp* = ref object
    size: Size
    tb: illwill.TerminalBuffer
    layers: seq[Layer]
    focus: int

proc size*(l: Layer): Size = l.dst.size()
proc size*(c: Comp): Size = c.size

proc contains*(l: Layer, p: Pt): bool =
  l.dst.contains(p) and l.blit.contains(p - l.src)

proc get*(l: Layer, dst: Pt): Char =
  var src = Pt(
    x: l.src.x + (dst.x - l.dst.lo.x),
    y: l.src.y + (dst.y - l.dst.lo.y))
  if l.blit.contains(src):
    return l.blit.get(src)
  return NO_CHAR

proc get*(c: Comp, p: Pt): TerminalChar =
  return c.tb[p.x, p.y]

proc newLayer*(size: Size): Layer =
  Layer(blit: newBlit(size), tag: LayoutTag.center)

proc newLayer*(blit: Blit): Layer =
  Layer(blit: blit, tag: LayoutTag.center)

proc newComp*(size: Size): Comp =
  Comp(size: size,
       tb: newTerminalBuffer(size.w, size.h),
       layers: newSeq[Layer]())

proc toplayers(c: Comp): array[LayoutTag, Layer] =
  var res: array[LayoutTag, Layer]
  for layer in c.layers:
    if res[layer.tag] == nil:
      res[layer.tag] = layer
  return res

proc layout(c: var Comp) =
  let ls = toplayers(c)

  var present = 0
  let full = rectFromSize(c.size)
  if ls[left] != nil:
    present = bitor(present, 0b100)
    ls[left].dst = full
  if ls[center] != nil:
    present = bitor(present, 0b010)
    ls[center].dst = full
  if ls[right] != nil:
    present = bitor(present, 0b001)
    ls[right].dst = full

  case present
  of 0b010, 0b100, 0b001: discard
  of 0b110:
    let split = int(ceil(c.size.w.float * 0.33))
    ls[left].dst.hi.x = split - 1
    ls[center].dst.lo.x = split
  of 0b011:
    let split = int(ceil(c.size.w.float * 0.67))
    ls[center].dst.hi.x = split - 1
    ls[right].dst.lo.x = split
  of 0b101:
    let split = int(c.size.w.float * 0.5)
    ls[left].dst.hi.x = split - 1
    ls[right].dst.lo.x = split
  of 0b111:
    let lsplit = int(ceil(c.size.w.float * 0.25))
    let rsplit = int(ceil(c.size.w.float * 0.75))
    ls[left].dst.hi.x = lsplit - 1
    ls[center].dst.lo.x = lsplit
    ls[center].dst.hi.x = rsplit - 1
    ls[right].dst.lo.x = rsplit
  else: discard

proc paint*(c: var Comp) =
  c.layout()
  c.tb.clear()
  for index, layer in c.layers:
    for point in pointsInside(@[layer.dst]):
      let ch = layer.get(point)
      c.tb[point.x, point.y] = TerminalChar(
        ch: ch[0],
        fg: fgNone,
        bg: if index == c.focus: bgBlack else: bgWhite,
        style: {})
  c.tb.display()

proc pushlayer*(c: var Comp, l: Layer) =
  c.layers.insert(l, 0)
  c.focus = c.layers.len - 1

proc poplayer*(c: var Comp) =
  let i = c.layers.len - 1
  c.layers.delete(i)
  c.focus = 0

proc focused*(c: Comp): Layer =
  return c.layers[c.focus]

proc focus*(c: var Comp, layer: Layer) =
  c.focus = find(c.layers, layer)

proc `size=`*(c: var Comp, s: Size) =
  c.tb = newTerminalBuffer(s.w, s.h)
  c.size = s

proc print*(b: Comp) =
  for j in 0..(b.size.h - 1):
    for i in 0..(b.size.w - 1):
      var c = b.get(newPt(i, j))
      stdout.write(c.ch.toUTF8)
    stdout.write('\n')

proc printdebug*(c: Comp) =
  echo c.layers[0].src, " -> ", c.layers[0].dst