diff --git a/src/zua.nim b/src/zua.nim index d03a95c..966f5ea 100644 --- a/src/zua.nim +++ b/src/zua.nim @@ -1,16 +1,16 @@ # SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway # SPDX-License-Identifier: Unlicense -import std/[asyncdispatch, hashes, os, strutils, tables, xmlparser, xmltree] -import preserves, preserves/parse +import std/[asyncdispatch, hashes, os, tables] +import preserves import syndicate, syndicate/[actors, capabilities, dataspaces, patterns, relay], syndicate/protocols/[simpleChatProtocol] import nimsvg -import pixie, pixie/fileformats/svg +import bumpy, pixie, pixie/fileformats/svg -import math, sdl2, sdl2/gfx +import sdl2 import svui @@ -39,23 +39,20 @@ type Pane = ref object svg: string texture: TexturePtr + rect: bumpy.Rect -type App = ref object screen: Image - # ctx: Context window: WindowPtr renderer: RendererPtr - # surface: SurfacePtr - # texture: TexturePtr panes: Table[Hash, Pane] - viewPort: sdl2.Rect + viewPoint: Vec2 zoomFactor: float proc newPane(app: App; svg: string): Pane = - result = Pane(svg: svg) + let (w, h) = app.window.getSize + result = Pane(svg: svg, rect: rect(0, 0, float w, float h)) try: - let (w, h) = app.window.getSize var image = decodeSvg(svg, w, h) dataPtr = image.data[0].addr @@ -76,61 +73,70 @@ proc newPane(app: App; svg: string): Pane = destroyTexture(result.texture) stderr.writeLine "Failed to render SVG, ", e.msg -proc newApp(width, height: int): App = +proc newApp(length: cint): App = + ## Create a new square plane of `length` pixels. + var wh = vec2(float length, float length) result = App(zoomFactor: 1.0) discard createWindowAndRenderer( - cint width, cint height, - SDL_WINDOW_RESIZABLE, + length, length, + SDL_WINDOW_SHOWN, + # SDL_WINDOW_RESIZABLE, result.window, result.renderer) var info: RendererInfo check getRendererInfo(result.renderer, addr info) echo "SDL Renderer: ", info.name +func toSdl(rect: bumpy.Rect): sdl2.Rect = + (result.x, result.y, result.w, result.h) = + (cint rect.x, cint rect.y, cint rect.w, cint rect.h) + +proc viewPort(app: App; wh: Vec2): bumpy.Rect = + result.wh = wh / app.zoomFactor + result.xy = app.viewPoint - (result.wh * 0.5) + proc redraw(app: App) = - var srcRect, dstRect: sdl2.Rect - (srcRect.w, srcRect.h) = app.window.getSize - dstRect.w = cint(srcRect.w.float * app.zoomFactor) - dstRect.h = cint(srcRect.h.float * app.zoomFactor) - app.renderer.setDrawColor(128, 128, 128) + assert app.zoomFactor != 0.0 + var + (w, h) = app.window.getSize + sdlViewPort = rect(-float(w shr 1), -float(h shr 1), float w, float h) + viewPort = app.viewPort(sdlViewPort.wh) + app.renderer.setDrawColor(0xff, 0xff, 0xff) app.renderer.clear() - app.renderer.setViewport(addr app.viewPort) for pane in app.panes.values: - app.renderer.copy(pane.texture, nil, addr dstRect) + if overlaps(viewPort, pane.rect): + var + overlap = viewPort and pane.rect + src = rect(overlap.xy - pane.rect.xy, overlap.wh) + dst: bumpy.Rect + dst.xy = (overlap.xy - viewPort.xy) * (sdlViewPort.w / viewPort.w) + dst.wh = + if app.zoomFactor == 1.0: + overlap.wh # correct + elif app.zoomFactor > 1.0: + sdlViewPort.wh - dst.xy # correct? + else: + overlap.wh * app.zoomFactor + var (sdlSrc, sdlDst) = (src.toSdl, dst.toSdl) + app.renderer.copy(pane.texture, addr sdlSrc, addr sdlDst) app.renderer.present() -proc update(app: App) = - let (w, h) = app.window.getSize - if app.screen.isNil or app.screen.width != w or app.screen.height != h: - # app.screen = newImage(w, h) - check app.renderer.setLogicalSize(w, h) - app.viewPort = app.renderer.getViewport - - #[ - app.screen.fill(rgba(255, 255, 255, 255)) - - var dataPtr = app.screen.data[0].addr - var mainSurface = createRGBSurfaceFrom( - dataPtr, cint app.screen.width, cint app.screen.height, cint 32, cint 4*w, - rmask, gmask, bmask, amask) - destroy app.texture - app.texture = app.renderer.createTextureFromSurface(mainSurface) - ]# - app.redraw() - proc zoom(app: App; change: float) = - # TODO: how does renderer scaling work? - app.zoomFactor = app.zoomFactor * (1.0 - (0.1 * change)) + app.zoomFactor = app.zoomFactor * (1.0 + ((1 / 8) * change)) app.redraw() -proc pan(app: App; x, y: int) = - app.viewPort.x.inc x - app.viewPort.y.inc y +proc pan(app: App; xy: Vec2) = + app.viewPoint.xy = app.viewPoint.xy + (xy / app.zoomFactor) + app.redraw() + +proc recenter(app: App) = + reset app.viewPoint + app.zoomFactor = 1.0 app.redraw() proc main() = discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS) - let app = newApp(1024, 768) - app.update() + let app = newApp(512) + app.redraw() let cap = mint() @@ -142,7 +148,7 @@ proc main() = onPublish(turn, ds, Svui ? {0: drop(), 1: grab()}) do (svg: string): onRetract: app.panes.del(hash svg) - app.update() + app.redraw() # TODO: keep dead panes around until they are cleaned up. # If something crashes then the last state should be visable. var pane = app.panes.getOrDefault(hash svg) @@ -151,11 +157,7 @@ proc main() = app.panes[hash svg] = pane else: pane.svg = svg - app.update() # TODO: wait til end of turn - - var - svgHandle: Handle - groups: Table[Hash, Nodes] + app.redraw() # TODO: wait til end of turn const sdlTimeout = 500 @@ -172,15 +174,11 @@ proc main() = app.zoom(evt.wheel.y.float) of WindowEvent: if evt.window.event == WindowEvent_Resized: - app.update() + app.redraw() of MouseMotion: if mousePanning: - if (getModState() and (KMOD_LCTRL or KMOD_RCTRL)) == KMOD_NONE: - app.pan(-2 * evt.motion.xrel, -2 * evt.motion.yrel) - # TODO: panning accelation curve - # ../../../genode/repos/os/src/server/event_filter/accelerate_source.h - else: - app.pan(evt.motion.xrel, evt.motion.yrel) + var xy = vec2(evt.motion.xrel.float, evt.motion.yrel.float) + app.pan(xy * 2.0) of MouseButtonDown: case evt.button.button of BUTTON_MIDDLE: @@ -191,7 +189,15 @@ proc main() = of BUTTON_MIDDLE: mousePanning = false else: discard - of KeyUp: discard + of KeyUp: + try: + let code = evt.key.keysym.scancode.Scancode + echo "code: ", code + if code in {SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE}: + app.recenter() + except: + # invalid event.key.keysym.sym sometimes arrive + discard of KeyDown: discard of QuitEvent: quit(0) else: