From 9fcacd3b2ebbb95a4faac8e4145b1c25249a54fd Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 9 Mar 2026 16:56:47 -0600 Subject: [PATCH 1/2] support windows decorations and stuff --- src/figdraw/common/fontglyphs.nim | 89 ++++++++++++++++------------- src/figdraw/common/imgutils.nim | 2 +- src/figdraw/figrender.nim | 16 ++++-- src/figdraw/windowing/siwinshim.nim | 68 +++++++++++++++++++--- 4 files changed, 121 insertions(+), 54 deletions(-) diff --git a/src/figdraw/common/fontglyphs.nim b/src/figdraw/common/fontglyphs.nim index 0fd47db..bec2a84 100644 --- a/src/figdraw/common/fontglyphs.nim +++ b/src/figdraw/common/fontglyphs.nim @@ -72,7 +72,12 @@ proc hash*( let variant = clampGlyphVariantSubpixelStep(subpixelVariant) result = hash((2344, glyph.fontId, glyph.rune, lcdFiltering, variant)) -proc generateGlyph*(glyph: GlyphPosition, lcdFiltering = false, subpixelVariant = 0) = +proc generateGlyph*( + glyph: GlyphPosition, + lcdFiltering = false, + subpixelVariant = 0, + force = false, +) = if unicode.isWhiteSpace(glyph.rune): return @@ -80,46 +85,48 @@ proc generateGlyph*(glyph: GlyphPosition, lcdFiltering = false, subpixelVariant variant = clampGlyphVariantSubpixelStep(subpixelVariant) hashFill = glyph.hash(lcdFiltering = lcdFiltering, subpixelVariant = variant) - if not hasImage(hashFill.ImageId): - let - fontId = glyph.fontId - font = getPixieFont(fontId) - - var - text = $glyph.rune - arrangement = pixie.typeset( - @[newSpan(text, font)], - bounds = glyph.rect.wh.scaled(), - hAlign = CenterAlign, - vAlign = TopAlign, - wrap = false, - ) - if variant > 0: - let subpixelOffset = variant.float32 / glyphVariantSubpixelSteps.float32 - for i in 0 ..< arrangement.positions.len: - arrangement.positions[i].x += subpixelOffset - - let snappedBounds = arrangement.computeBounds().snapToPixels() - - let - lh = font.defaultLineHeight() - bounds = rect(0, 0, scaled(snappedBounds.w + snappedBounds.x), scaled(lh)) - - if bounds.w == 0 or bounds.h == 0: - error "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds - return - - try: - font.paint = parseHex"FFFFFF" - var image = newImage(bounds.w.int, bounds.h.int) - image.fillText(arrangement) - if lcdFiltering: - image.applyLcdFilter() - - # put into cache - loadImage(hashFill.ImageId, image) - except PixieError: - discard + if (not force) and hasImage(hashFill.ImageId): + return + + let + fontId = glyph.fontId + font = getPixieFont(fontId) + + var + text = $glyph.rune + arrangement = pixie.typeset( + @[newSpan(text, font)], + bounds = glyph.rect.wh.scaled(), + hAlign = CenterAlign, + vAlign = TopAlign, + wrap = false, + ) + if variant > 0: + let subpixelOffset = variant.float32 / glyphVariantSubpixelSteps.float32 + for i in 0 ..< arrangement.positions.len: + arrangement.positions[i].x += subpixelOffset + + let snappedBounds = arrangement.computeBounds().snapToPixels() + + let + lh = font.defaultLineHeight() + bounds = rect(0, 0, scaled(snappedBounds.w + snappedBounds.x), scaled(lh)) + + if bounds.w == 0 or bounds.h == 0: + error "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds + return + + try: + font.paint = parseHex"FFFFFF" + var image = newImage(bounds.w.int, bounds.h.int) + image.fillText(arrangement) + if lcdFiltering: + image.applyLcdFilter() + + # put into cache + loadImage(hashFill.ImageId, image) + except PixieError: + discard iterator glyphs*(arrangement: GlyphArrangement): GlyphPosition = var idx = 0 diff --git a/src/figdraw/common/imgutils.nim b/src/figdraw/common/imgutils.nim index 35d7eec..8941d24 100644 --- a/src/figdraw/common/imgutils.nim +++ b/src/figdraw/common/imgutils.nim @@ -105,4 +105,4 @@ proc loadImage*(filePath: string): ImageId = proc loadImage*(id: ImageId, image: Image) = var imgObj = ImgObj(id: id, kind: PixieImg, pimg: image) - sendImageCached(imgObj) + sendImage(imgObj) diff --git a/src/figdraw/figrender.nim b/src/figdraw/figrender.nim index cbe75b6..50aca87 100644 --- a/src/figdraw/figrender.nim +++ b/src/figdraw/figrender.nim @@ -344,12 +344,18 @@ proc renderText(ctx: BackendContext, node: Fig) {.forbids: [AppMainThreadEff].} ctx.setTextSubpixelShift(subpixelShift) if glyphId notin ctx.entries: glyph.generateGlyph( - lcdFiltering = lcdFiltering, subpixelVariant = subpixelVariant + lcdFiltering = lcdFiltering, subpixelVariant = subpixelVariant, force = true ) - warn "missing glyph image in context", - glyphId = glyphId, glyphRune = $glyph.rune, glyphRuneRepr = repr(glyph.rune) - ctx.setTextSubpixelShift(0.0'f32) - continue + var img: ImgObj + while imageChan.tryRecv(img): + ctx.putImage(img) + if glyphId in ctx.entries: + discard + else: + warn "missing glyph image in context", + glyphId = glyphId, glyphRune = $glyph.rune, glyphRuneRepr = repr(glyph.rune) + ctx.setTextSubpixelShift(0.0'f32) + continue var drawPos = glyphPos if invertText: diff --git a/src/figdraw/windowing/siwinshim.nim b/src/figdraw/windowing/siwinshim.nim index 59d3424..ff86b2f 100644 --- a/src/figdraw/windowing/siwinshim.nim +++ b/src/figdraw/windowing/siwinshim.nim @@ -99,28 +99,72 @@ proc siwinWindowTitle*[BackendState]( "figdraw: " & backend & " + " & suffix proc newSiwinWindow*( - size: IVec2, fullscreen = false, title = "FigDraw", vsync = true, msaa = 0'i32 + size: IVec2, + fullscreen = false, + title = "FigDraw", + vsync = true, + msaa = 0'i32, + resizable = true, + frameless = false, + transparent = false, ): Window = let forceOpenGl = runtimeForceOpenGlRequested() let window = when UseMetalBackend and not NeedSiwinOpenGLContext: when defined(macosx): - newMetalWindowCocoa(size = size, title = title) + newMetalWindowCocoa( + size = size, + title = title, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: {.error: "siwinshim: Metal backend requires macOS".} else: when defined(macosx): - newOpenglWindowCocoa(size = size, title = title, vsync = vsync, msaa = msaa) + newOpenglWindowCocoa( + size = size, + title = title, + vsync = vsync, + msaa = msaa, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: let globals = sharedSiwinGlobals() when UseVulkanBackend: if forceOpenGl: - newOpenglWindow(globals, size = size, title = title, vsync = vsync) + newOpenglWindow( + globals, + size = size, + title = title, + vsync = vsync, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: # Use a non-GL window for Vulkan so siwin's GL swap path does not flicker. - newSoftwareRenderingWindow(globals, size = size, title = title) + newSoftwareRenderingWindow( + globals, + size = size, + title = title, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) else: - newOpenglWindow(globals, size = size, title = title, vsync = vsync) + newOpenglWindow( + globals, + size = size, + title = title, + vsync = vsync, + resizable = resizable, + frameless = frameless, + transparent = transparent, + ) when NeedSiwinOpenGLContext: when UseVulkanBackend: if forceOpenGl: @@ -140,6 +184,9 @@ proc newSiwinWindow*( title = "FigDraw", vsync = true, msaa = 0'i32, + resizable = true, + frameless = false, + transparent = false, ): Window = ## Compatibility overload. Prefer creating a window first, then renderer. let forceOpenGl = runtimeForceOpenGlRequested() or renderer.forceOpenGlByEnv() @@ -173,7 +220,14 @@ proc newSiwinWindow*( discard renderer result = newSiwinWindow( - size = size, fullscreen = fullscreen, title = title, vsync = vsync, msaa = msaa + size = size, + fullscreen = fullscreen, + title = title, + vsync = vsync, + msaa = msaa, + resizable = resizable, + frameless = frameless, + transparent = transparent, ) proc backingSize*(window: Window): IVec2 = From 4b1e1f83912381bbd23c782432f7688732a6e28f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 9 Mar 2026 21:31:50 -0600 Subject: [PATCH 2/2] setup combobox down --- src/figdraw/common/fontglyphs.nim | 17 ++++++++++------- src/figdraw/figrender.nim | 14 ++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/figdraw/common/fontglyphs.nim b/src/figdraw/common/fontglyphs.nim index bec2a84..bb8f829 100644 --- a/src/figdraw/common/fontglyphs.nim +++ b/src/figdraw/common/fontglyphs.nim @@ -77,16 +77,17 @@ proc generateGlyph*( lcdFiltering = false, subpixelVariant = 0, force = false, -) = + upload = true, +): Image {.discardable.} = if unicode.isWhiteSpace(glyph.rune): - return + return nil let variant = clampGlyphVariantSubpixelStep(subpixelVariant) hashFill = glyph.hash(lcdFiltering = lcdFiltering, subpixelVariant = variant) if (not force) and hasImage(hashFill.ImageId): - return + return nil let fontId = glyph.fontId @@ -113,8 +114,8 @@ proc generateGlyph*( bounds = rect(0, 0, scaled(snappedBounds.w + snappedBounds.x), scaled(lh)) if bounds.w == 0 or bounds.h == 0: - error "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds - return + debug "GEN IMG: ", rune = $glyph.rune, wh = repr wh, snapped = repr snappedBounds + return nil try: font.paint = parseHex"FFFFFF" @@ -124,9 +125,11 @@ proc generateGlyph*( image.applyLcdFilter() # put into cache - loadImage(hashFill.ImageId, image) + if upload: + loadImage(hashFill.ImageId, image) + return image except PixieError: - discard + return nil iterator glyphs*(arrangement: GlyphArrangement): GlyphPosition = var idx = 0 diff --git a/src/figdraw/figrender.nim b/src/figdraw/figrender.nim index 50aca87..24f31ab 100644 --- a/src/figdraw/figrender.nim +++ b/src/figdraw/figrender.nim @@ -343,16 +343,14 @@ proc renderText(ctx: BackendContext, node: Fig) {.forbids: [AppMainThreadEff].} ctx.setTextSubpixelShift(subpixelShift) if glyphId notin ctx.entries: - glyph.generateGlyph( - lcdFiltering = lcdFiltering, subpixelVariant = subpixelVariant, force = true + let img = glyph.generateGlyph(lcdFiltering = lcdFiltering, + subpixelVariant = subpixelVariant, + force = true, + upload = false, ) - var img: ImgObj - while imageChan.tryRecv(img): - ctx.putImage(img) + ctx.putImage(glyphId, img) if glyphId in ctx.entries: - discard - else: - warn "missing glyph image in context", + debug "missing glyph image in context", glyphId = glyphId, glyphRune = $glyph.rune, glyphRuneRepr = repr(glyph.rune) ctx.setTextSubpixelShift(0.0'f32) continue