160 lines
4.6 KiB
Nim
160 lines
4.6 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[deques, streams]
|
|
import bumpy, pixie, sdl2
|
|
|
|
type
|
|
Rect = bumpy.Rect
|
|
|
|
Pane* = ref object
|
|
spans: seq[Span]
|
|
arrangement: Arrangement
|
|
image: Image
|
|
texture: TexturePtr
|
|
|
|
Well* = ref object
|
|
panes: Deque[Pane]
|
|
width, height: int
|
|
textWidth, textHeight: int
|
|
|
|
using
|
|
pane: Pane
|
|
well: Well
|
|
|
|
proc newWell*(width, height: int): Well =
|
|
result = Well(width: width, height: height)
|
|
result.panes.addFirst Pane()
|
|
|
|
proc dimensions(well): Vec2 = vec2(float well.width, float well.height)
|
|
|
|
proc margins(well): tuple[w: int, h: int] = (well.width div 9, well.height div 9)
|
|
|
|
proc textArea(well): tuple[w: int, h: int] = (
|
|
well.width - ((well.width div 9) shl 1),
|
|
well.height - ((well.height div 9) shl 1),
|
|
)
|
|
|
|
proc vec2(t: (int, int)): Vec2 = vec2(float t[0], float t[1])
|
|
|
|
proc rect*(well): Rect =
|
|
result.wh = well.dimensions
|
|
|
|
proc rectAt(well; i: int): Rect =
|
|
if i < well.panes.len:
|
|
let
|
|
(quo, rem) = divmod(i, 3)
|
|
shiftOff = succ quo
|
|
fullWh = well.dimensions
|
|
textArea = well.textArea
|
|
paneArea = vec2(float(well.width shr shiftOff), float(well.height shr shiftOff))
|
|
result.w = float(textArea.w shr shiftOff)
|
|
result.h = float(textArea.h shr shiftOff)
|
|
case rem
|
|
of 0:
|
|
result.xy = fullWh - (paneArea * 2.0)
|
|
of 1:
|
|
result.x = fullWh.x - paneArea.x
|
|
result.y = fullWh.y - (paneArea.y * 2.0)
|
|
of 2:
|
|
result.x = fullWh.x - (paneArea.x * 2.0)
|
|
result.y = fullWh.y - paneArea.y
|
|
else: discard
|
|
result.xy = result.xy + (result.wh / 9.0)
|
|
|
|
proc quadAt(well; i: int): Rect =
|
|
if i < well.panes.len:
|
|
let
|
|
(quo, rem) = divmod(i, 3)
|
|
shiftOff = succ quo
|
|
fullWh = well.dimensions
|
|
assert rem == 0
|
|
result.wh = vec2(float(well.width shr shiftOff), float(well.height shr shiftOff))
|
|
result.xy = fullWh - result.wh
|
|
|
|
proc place(well; offset, width, height: int): (Pane, Rect) =
|
|
## Return the `Pane` at `offset` from the top of `well` or `nil` if
|
|
## the offset is too deep. The position and size of the `Pane` is
|
|
## returned as well.
|
|
if offset < well.panes.len:
|
|
result[0] = well.panes[offset]
|
|
result[1] = well.rectAt(offset)
|
|
|
|
proc append*(well; text: string; font: Font) =
|
|
assert well.panes.len > 0
|
|
let span = newSpan(text & "\n", font)
|
|
while true:
|
|
let pane = well.panes.peekLast()
|
|
pane.spans.add(span)
|
|
var
|
|
textArea = well.textArea.vec2
|
|
arrangement = typeset(pane.spans, textArea)
|
|
bounds = layoutBounds arrangement
|
|
if bounds.y <= textArea.y:
|
|
doAssert pane.spans.len > 0, "text does not find on a single pane - " & $bounds & $well.dimensions
|
|
pane.arrangement = arrangement
|
|
break
|
|
else:
|
|
discard pane.spans.pop()
|
|
well.panes.addLast Pane()
|
|
|
|
proc append*(well; stream: Stream; font: Font) =
|
|
var line: string
|
|
while readLine(stream, line):
|
|
append(well, line, font)
|
|
|
|
type Intersection = tuple
|
|
src, dst: Rect
|
|
index: int
|
|
|
|
const
|
|
amask = uint32 0xff000000
|
|
rmask = uint32 0x000000ff
|
|
gmask = uint32 0x0000ff00
|
|
bmask = uint32 0x00ff0000
|
|
|
|
proc texture*(well; index: int; renderer: RendererPtr): TexturePtr =
|
|
if index < well.panes.len:
|
|
let pane = well.panes[index]
|
|
if pane.texture.isNil and not pane.arrangement.isNil:
|
|
assert pane.image.isNil
|
|
let
|
|
textArea = well.textArea
|
|
pane.image = newImage(textArea.w, textArea.h)
|
|
pane.image.fill(rgba(255, 255, 255, 255))
|
|
pane.image.fillText(pane.arrangement)
|
|
var surface = createRGBSurfaceFrom(
|
|
pane.image.data[0].addr,
|
|
pane.image.width.cint,
|
|
pane.image.height.cint,
|
|
cint 32,
|
|
pane.image.width.cint shl 2,
|
|
rmask, gmask, bmask, amask,
|
|
)
|
|
assert(not surface.isNil, $getError())
|
|
pane.texture = createTextureFromSurface(renderer, surface)
|
|
destroy(surface)
|
|
assert(not pane.texture.isNil, $getError())
|
|
result = pane.texture
|
|
|
|
iterator intersectingPanes*(well; view: Rect): Intersection =
|
|
var
|
|
intersect: Intersection
|
|
zoom = 1.0
|
|
while intersect.index < well.panes.len:
|
|
var paneRect = well.rectAt(intersect.index)
|
|
let stepDown = (intersect.index mod 3) == 0
|
|
if stepDown:
|
|
zoom = zoom * 2.0
|
|
if view.overlaps paneRect:
|
|
intersect.dst = view and paneRect
|
|
intersect.src = rect(intersect.dst.xy - paneRect.xy, intersect.dst.wh) * zoom
|
|
yield(intersect)
|
|
if stepDown:
|
|
let quad = well.quadAt(intersect.index)
|
|
if not view.overlaps quad:
|
|
echo "view ", view, " does not overlap quadrant ", quad
|
|
# all further panes are non-intersecting
|
|
break
|
|
inc(intersect.index)
|