fix: remove duplicate scale storage, read from State via ScaleProvider interface
- Remove Renderer.scale field and SetScale method - Add ScaleProvider interface to avoid editor/ui import cycle - Add Scale() method to State, rename Scale field to scale - Renderer reads scale through r.scale.Scale() - Fix icons.go blank import preventing embed.FS usage - Update main.go to pass State as ScaleProvider to Renderer
This commit is contained in:
parent
fa43bf2402
commit
93a7a0a0b2
|
|
@ -32,18 +32,19 @@ func run(w *app.Window) error {
|
||||||
var ops op.Ops
|
var ops op.Ops
|
||||||
|
|
||||||
shaper := text.NewShaper(text.WithCollection(gofont.Collection()))
|
shaper := text.NewShaper(text.WithCollection(gofont.Collection()))
|
||||||
renderer := ui.New(ui.Theme{FontSize: 14}, shaper)
|
|
||||||
|
|
||||||
// Initial DP size from app.Size() — used before we know the scale factor
|
// Initial DP size from app.Size() — used before we know the scale factor
|
||||||
initialDPW := ui.Dp(390)
|
initialDPW := ui.Dp(390)
|
||||||
initialDPH := ui.Dp(844)
|
initialDPH := ui.Dp(844)
|
||||||
|
|
||||||
// Track pixel dimensions from ConfigEvent (for resize handling)
|
|
||||||
var pixelW, pixelH int
|
|
||||||
|
|
||||||
// Create logic instance with initial DP size and scale=1.0
|
// Create logic instance with initial DP size and scale=1.0
|
||||||
logic := editor.NewLogic(initialDPW, initialDPH)
|
logic := editor.NewLogic(initialDPW, initialDPH)
|
||||||
|
|
||||||
|
renderer := ui.New(ui.Theme{FontSize: 14}, shaper, logic.State())
|
||||||
|
|
||||||
|
// Track pixel dimensions from ConfigEvent (for resize handling)
|
||||||
|
var pixelW, pixelH int
|
||||||
|
|
||||||
// Shared state protected by mutex
|
// Shared state protected by mutex
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var elems []ui.Element
|
var elems []ui.Element
|
||||||
|
|
@ -53,6 +54,7 @@ func run(w *app.Window) error {
|
||||||
|
|
||||||
// Start frame receiver goroutine
|
// Start frame receiver goroutine
|
||||||
go frameReceiver(w, &mu, &elems, logic.FrameChan())
|
go frameReceiver(w, &mu, &elems, logic.FrameChan())
|
||||||
|
scale := float32(1.0)
|
||||||
|
|
||||||
// Main event loop
|
// Main event loop
|
||||||
for {
|
for {
|
||||||
|
|
@ -63,9 +65,9 @@ func run(w *app.Window) error {
|
||||||
// Store pixel dimensions from ConfigEvent
|
// Store pixel dimensions from ConfigEvent
|
||||||
pixelW = e.Config.Size.X
|
pixelW = e.Config.Size.X
|
||||||
pixelH = e.Config.Size.Y
|
pixelH = e.Config.Size.Y
|
||||||
// Convert to DP using current scale (starts at 1.0)
|
// Convert to DP using current scale
|
||||||
dpW := ui.ToDp(ui.Px(pixelW), 1.0)
|
dpW := ui.ToDp(ui.Px(pixelW), scale)
|
||||||
dpH := ui.ToDp(ui.Px(pixelH), 1.0)
|
dpH := ui.ToDp(ui.Px(pixelH), scale)
|
||||||
// Send config event to logic goroutine (in DP)
|
// Send config event to logic goroutine (in DP)
|
||||||
logic.ConfigChan() <- editor.ConfigEvent{
|
logic.ConfigChan() <- editor.ConfigEvent{
|
||||||
Width: dpW,
|
Width: dpW,
|
||||||
|
|
@ -75,28 +77,21 @@ func run(w *app.Window) error {
|
||||||
gtx := app.NewContext(&ops, e)
|
gtx := app.NewContext(&ops, e)
|
||||||
|
|
||||||
// Get scale from State — this is updated by ScaleEvent
|
// Get scale from State — this is updated by ScaleEvent
|
||||||
newScale := logic.Scale()
|
newScale := gtx.Metric.PxPerDp
|
||||||
|
|
||||||
// Convert pixel dimensions to DP using actual scale
|
if newScale != scale {
|
||||||
dpW := ui.ToDp(ui.Px(pixelW), newScale)
|
// Send updated scale to logic goroutine
|
||||||
dpH := ui.ToDp(ui.Px(pixelH), newScale)
|
logic.ConfigChan() <- editor.ScaleEvent{newScale}
|
||||||
|
scale = newScale
|
||||||
// Send updated config to logic goroutine
|
|
||||||
logic.ConfigChan() <- editor.ConfigEvent{
|
|
||||||
Width: dpW,
|
|
||||||
Height: dpH,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update renderer with current scale from State
|
|
||||||
renderer.SetScale(newScale)
|
|
||||||
|
|
||||||
// Acquire mutex, read frame, draw, release mutex
|
// Acquire mutex, read frame, draw, release mutex
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
currentElems := elems
|
currentElems := elems
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
renderer.Draw(gtx, currentElems)
|
renderer.Draw(gtx, currentElems)
|
||||||
e.Frame(&ops)
|
e.Frame(&ops)
|
||||||
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
go.mod
6
go.mod
|
|
@ -2,13 +2,15 @@ module pad
|
||||||
|
|
||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require gioui.org v0.9.0
|
require (
|
||||||
|
gioui.org v0.9.0
|
||||||
|
golang.org/x/image v0.26.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gioui.org/shader v1.0.8 // indirect
|
gioui.org/shader v1.0.8 // indirect
|
||||||
github.com/go-text/typesetting v0.3.0 // indirect
|
github.com/go-text/typesetting v0.3.0 // indirect
|
||||||
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
golang.org/x/image v0.26.0 // indirect
|
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.24.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -98,9 +98,7 @@ func (l *Logic) ScreenSize() (ui.Dp, ui.Dp) {
|
||||||
|
|
||||||
// Scale returns the current scale factor (pixels per DP).
|
// Scale returns the current scale factor (pixels per DP).
|
||||||
func (l *Logic) Scale() float32 {
|
func (l *Logic) Scale() float32 {
|
||||||
l.mu.Lock()
|
return l.state.Scale()
|
||||||
defer l.mu.Unlock()
|
|
||||||
return l.state.Scale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the logic goroutine loop.
|
// Run runs the logic goroutine loop.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
type State struct {
|
type State struct {
|
||||||
ScreenWidth ui.Dp
|
ScreenWidth ui.Dp
|
||||||
ScreenHeight ui.Dp
|
ScreenHeight ui.Dp
|
||||||
Scale float32 // pixels per DP, default 1.0
|
scale float32 // pixels per DP, default 1.0
|
||||||
Elems []ui.Element
|
Elems []ui.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,17 +20,22 @@ func NewState(screenWidth, screenHeight ui.Dp) *State {
|
||||||
return &State{
|
return &State{
|
||||||
ScreenWidth: screenWidth,
|
ScreenWidth: screenWidth,
|
||||||
ScreenHeight: screenHeight,
|
ScreenHeight: screenHeight,
|
||||||
Scale: 1.0,
|
scale: 1.0,
|
||||||
Elems: EditorLayout(screenWidth, screenHeight),
|
Elems: EditorLayout(screenWidth, screenHeight),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScale updates the scale factor and recomputes layout.
|
// SetScale updates the scale factor and recomputes layout.
|
||||||
func (s *State) SetScale(scale float32) {
|
func (s *State) SetScale(scale float32) {
|
||||||
s.Scale = scale
|
s.scale = scale
|
||||||
s.Elems = EditorLayout(s.ScreenWidth, s.ScreenHeight)
|
s.Elems = EditorLayout(s.ScreenWidth, s.ScreenHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scale returns the current scale factor (pixels per DP).
|
||||||
|
func (s *State) Scale() float32 {
|
||||||
|
return s.scale
|
||||||
|
}
|
||||||
|
|
||||||
// EditorLayout computes regions for the editor page.
|
// EditorLayout computes regions for the editor page.
|
||||||
// It takes screen dimensions in Dp and returns []Element with regions in Dp.
|
// It takes screen dimensions in Dp and returns []Element with regions in Dp.
|
||||||
// This function works exclusively in Dp — no pixel conversions.
|
// This function works exclusively in Dp — no pixel conversions.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
package icons
|
package icons
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
"embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed copy.svg cut.svg paste.svg
|
//go:embed copy.svg cut.svg paste.svg
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,18 @@ var iconFS embed.FS
|
||||||
type Renderer struct {
|
type Renderer struct {
|
||||||
theme Theme
|
theme Theme
|
||||||
shp *text.Shaper
|
shp *text.Shaper
|
||||||
scale float32 // pixels per DP, from State.Scale
|
scale ScaleProvider
|
||||||
icons map[string]image.Image
|
icons map[string]image.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScaleProvider provides access to the current scale factor.
|
||||||
|
type ScaleProvider interface {
|
||||||
|
Scale() float32
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new Renderer.
|
// New creates a new Renderer.
|
||||||
func New(th Theme, shp *text.Shaper) *Renderer {
|
func New(th Theme, shp *text.Shaper, scale ScaleProvider) *Renderer {
|
||||||
r := &Renderer{theme: th, shp: shp, scale: 1.0, icons: make(map[string]image.Image)}
|
r := &Renderer{theme: th, shp: shp, scale: scale, icons: make(map[string]image.Image)}
|
||||||
r.loadIcons()
|
r.loadIcons()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
@ -49,25 +54,19 @@ func (r *Renderer) loadIcons() {
|
||||||
r.icons["cut"] = img
|
r.icons["cut"] = img
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScale updates the renderer's scale factor from State.Scale.
|
// toPx converts Dp to physical pixels using State's scale.
|
||||||
func (r *Renderer) SetScale(scale float32) {
|
|
||||||
r.scale = scale
|
|
||||||
}
|
|
||||||
|
|
||||||
// toPx converts Dp to physical pixels using the renderer's scale.
|
|
||||||
func (r *Renderer) toPx(dp Dp) Px {
|
func (r *Renderer) toPx(dp Dp) Px {
|
||||||
return ToPx(dp, r.scale)
|
return ToPx(dp, r.scale.Scale())
|
||||||
}
|
}
|
||||||
|
|
||||||
// toDp converts physical pixels to Dp using the renderer's scale.
|
// toDp converts physical pixels to Dp using State's scale.
|
||||||
func (r *Renderer) toDp(px Px) Dp {
|
func (r *Renderer) toDp(px Px) Dp {
|
||||||
return ToDp(px, r.scale)
|
return ToDp(px, r.scale.Scale())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw iterates elements and draws each in slice order (back-to-front).
|
// Draw iterates elements and draws each in slice order (back-to-front).
|
||||||
// It captures the initial constraints once at the start, then passes them
|
// It captures the initial constraints once at the start, then passes them
|
||||||
// to each render function for consistent positioning.
|
// to each render function for consistent positioning.
|
||||||
// The scale is set externally via SetScale() from State.Scale.
|
|
||||||
func (r *Renderer) Draw(gtx layout.Context, elems []Element) {
|
func (r *Renderer) Draw(gtx layout.Context, elems []Element) {
|
||||||
// Capture initial constraints once — before any clips modify them.
|
// Capture initial constraints once — before any clips modify them.
|
||||||
// These are in physical pixels and serve as the window bounds for all positioning.
|
// These are in physical pixels and serve as the window bounds for all positioning.
|
||||||
|
|
@ -262,7 +261,6 @@ func (r *Renderer) drawBottomBar(gtx layout.Context, bb BottomBar, constraints W
|
||||||
|
|
||||||
// Use captured constraints for positioning
|
// Use captured constraints for positioning
|
||||||
windowW := constraints.Max.X
|
windowW := constraints.Max.X
|
||||||
windowH := constraints.Max.Y
|
|
||||||
|
|
||||||
regXPx := r.toPx(reg.X)
|
regXPx := r.toPx(reg.X)
|
||||||
regWPx := r.toPx(reg.W)
|
regWPx := r.toPx(reg.W)
|
||||||
|
|
@ -275,24 +273,26 @@ func (r *Renderer) drawBottomBar(gtx layout.Context, bb BottomBar, constraints W
|
||||||
|
|
||||||
bottomBarHeightPx := r.toPx(Dp(24))
|
bottomBarHeightPx := r.toPx(Dp(24))
|
||||||
marginPx := r.toPx(Dp(10))
|
marginPx := r.toPx(Dp(10))
|
||||||
drawYPx := Px(windowH) - bottomBarHeightPx - marginPx
|
drawYPx := Px(constraints.Max.Y) - bottomBarHeightPx - marginPx
|
||||||
drawY := r.toDp(drawYPx)
|
drawY := r.toDp(drawYPx)
|
||||||
|
|
||||||
// ── Layout: shape all items ──
|
col := color.NRGBA{R: 0, G: 0, B: 0, A: 255}
|
||||||
|
|
||||||
|
// Item 1: cursor position (left-aligned) — shape, position, draw
|
||||||
cursorShaped := r.ShapeText(gtx, bb.CursorPos, r.theme.FontSize)
|
cursorShaped := r.ShapeText(gtx, bb.CursorPos, r.theme.FontSize)
|
||||||
|
cursorXDp := r.toDp(regXPx + r.toPx(Dp(8)))
|
||||||
|
r.DrawShapedText(gtx, cursorShaped, cursorXDp, drawY, col)
|
||||||
|
|
||||||
|
// Item 2: byte position (centered) — shape, position, draw
|
||||||
bytePosShaped := r.ShapeText(gtx, bb.BytePos, r.theme.FontSize)
|
bytePosShaped := r.ShapeText(gtx, bb.BytePos, r.theme.FontSize)
|
||||||
|
byteXDp := r.toDp(regXPx + barW/2 - Px(bytePosShaped.Width/2))
|
||||||
|
r.DrawShapedText(gtx, bytePosShaped, byteXDp, drawY, col)
|
||||||
|
|
||||||
|
// Item 3: wrap (right-aligned) — shape, position, draw
|
||||||
wrapText := "Wrap:" + boolToString(bb.WordWrap)
|
wrapText := "Wrap:" + boolToString(bb.WordWrap)
|
||||||
wrapShaped := r.ShapeText(gtx, wrapText, r.theme.FontSize)
|
wrapShaped := r.ShapeText(gtx, wrapText, r.theme.FontSize)
|
||||||
|
wrapXDp := r.toDp(regXPx + barW - r.toPx(Dp(8)) - Px(wrapShaped.Width))
|
||||||
// ── Position based on alignment ──
|
r.DrawShapedText(gtx, wrapShaped, wrapXDp, drawY, col)
|
||||||
cursorXDp := r.toDp(regXPx + r.toPx(Dp(8)))
|
|
||||||
byteXDp := r.toDp(regXPx + barW/2 - Px(cursorShaped.Width/2))
|
|
||||||
wordWrapXDp := r.toDp(regXPx + barW - r.toPx(Dp(8)) - Px(wrapShaped.Width))
|
|
||||||
|
|
||||||
// ── Render ──
|
|
||||||
r.DrawShapedText(gtx, cursorShaped, cursorXDp, drawY, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
|
|
||||||
r.DrawShapedText(gtx, bytePosShaped, byteXDp, drawY, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
|
|
||||||
r.DrawShapedText(gtx, wrapShaped, wordWrapXDp, drawY, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Renderer) drawButton(gtx layout.Context, btn Button, _ WindowConstraints) {
|
func (r *Renderer) drawButton(gtx layout.Context, btn Button, _ WindowConstraints) {
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,18 @@ import (
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShapedText holds layout results from a single ShapeText call.
|
// ShapedText holds the result from ShapeText: width for positioning,
|
||||||
// The PathSpec and BitmapsCall can be drawn without further shaper access.
|
// plus the path data ready to draw. Must be drawn before the next ShapeText call.
|
||||||
type ShapedText struct {
|
type ShapedText struct {
|
||||||
Width int // total width in pixels
|
Width int // total width in pixels
|
||||||
PathSpec clip.PathSpec // vector glyph path, ready to draw
|
PathSpec clip.PathSpec // glyph path data
|
||||||
BitmapsCall op.CallOp // bitmap glyph call (empty if none)
|
Bitmaps op.CallOp // bitmap glyphs (may be empty)
|
||||||
FirstX fixed.Int26_6 // first glyph X offset
|
FirstX fixed.Int26_6 // first glyph X offset
|
||||||
FirstY int32 // first glyph Y offset
|
FirstY int32 // first glyph Y offset
|
||||||
Text string
|
|
||||||
FontSize unit.Sp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShapeText layouts text, measures width, and returns a ShapedText
|
// ShapeText layouts text and returns width + path data for drawing.
|
||||||
// with an immutable path ready for drawing. The shaper is called once.
|
// The returned ShapedText must be drawn before the next ShapeText call.
|
||||||
func (r *Renderer) ShapeText(gtx layout.Context, str string, size unit.Sp) ShapedText {
|
func (r *Renderer) ShapeText(gtx layout.Context, str string, size unit.Sp) ShapedText {
|
||||||
r.shp.LayoutString(text.Parameters{
|
r.shp.LayoutString(text.Parameters{
|
||||||
PxPerEm: fixed.I(gtx.Sp(size)),
|
PxPerEm: fixed.I(gtx.Sp(size)),
|
||||||
|
|
@ -38,71 +36,55 @@ func (r *Renderer) ShapeText(gtx layout.Context, str string, size unit.Sp) Shape
|
||||||
// Collect glyphs and measure width
|
// Collect glyphs and measure width
|
||||||
var glyphs []text.Glyph
|
var glyphs []text.Glyph
|
||||||
var cumWidth fixed.Int26_6
|
var cumWidth fixed.Int26_6
|
||||||
|
var firstX fixed.Int26_6
|
||||||
|
var firstY int32
|
||||||
for {
|
for {
|
||||||
g, ok := r.shp.NextGlyph()
|
g, ok := r.shp.NextGlyph()
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if len(glyphs) == 0 {
|
||||||
|
firstX = g.X
|
||||||
|
firstY = g.Y
|
||||||
|
}
|
||||||
cumWidth += g.Advance
|
cumWidth += g.Advance
|
||||||
glyphs = append(glyphs, g)
|
glyphs = append(glyphs, g)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build lines and collect the last line's path spec
|
// capture path spec
|
||||||
var lastSpec clip.PathSpec
|
var spec clip.PathSpec
|
||||||
var lastBitmaps op.CallOp
|
var bitmaps op.CallOp
|
||||||
var firstX fixed.Int26_6
|
|
||||||
var firstY int32
|
spec = r.shp.Shape(glyphs)
|
||||||
var line []text.Glyph
|
bitmaps = r.shp.Bitmaps(glyphs)
|
||||||
for _, g := range glyphs {
|
|
||||||
if len(line) == 0 {
|
|
||||||
firstX = g.X
|
|
||||||
firstY = g.Y
|
|
||||||
}
|
|
||||||
line = append(line, g)
|
|
||||||
if g.Flags&text.FlagLineBreak != 0 || cap(line)-len(line) == 0 {
|
|
||||||
lastSpec = r.shp.Shape(line)
|
|
||||||
lastBitmaps = r.shp.Bitmaps(line)
|
|
||||||
line = line[:0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(line) > 0 {
|
|
||||||
lastSpec = r.shp.Shape(line)
|
|
||||||
lastBitmaps = r.shp.Bitmaps(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShapedText{
|
return ShapedText{
|
||||||
Width: int(cumWidth >> 6),
|
Width: int(cumWidth >> 6),
|
||||||
PathSpec: lastSpec,
|
PathSpec: spec,
|
||||||
BitmapsCall: lastBitmaps,
|
Bitmaps: bitmaps,
|
||||||
FirstX: firstX,
|
FirstX: firstX,
|
||||||
FirstY: firstY,
|
FirstY: firstY,
|
||||||
Text: str,
|
|
||||||
FontSize: size,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawShapedText draws a pre-shaped text at the given position.
|
// DrawShapedText draws a ShapedText at the given position.
|
||||||
// x, y are in Dp. Uses the stored path spec — zero shaper calls.
|
|
||||||
func (r *Renderer) DrawShapedText(gtx layout.Context, st ShapedText, x, y Dp, col color.NRGBA) {
|
func (r *Renderer) DrawShapedText(gtx layout.Context, st ShapedText, x, y Dp, col color.NRGBA) {
|
||||||
xPx := r.toPx(x)
|
xPx := r.toPx(x)
|
||||||
yPx := r.toPx(y)
|
yPx := r.toPx(y)
|
||||||
|
|
||||||
m := op.Record(gtx.Ops)
|
m := op.Record(gtx.Ops)
|
||||||
|
|
||||||
// Compute offset: position + first glyph offset
|
|
||||||
offX := float32(xPx) + float32(st.FirstX)/64.0
|
offX := float32(xPx) + float32(st.FirstX)/64.0
|
||||||
offY := float32(yPx) + float32(st.FirstY)
|
offY := float32(yPx) + float32(st.FirstY)
|
||||||
t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(offX, offY))).Push(gtx.Ops)
|
t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(offX, offY))).Push(gtx.Ops)
|
||||||
|
|
||||||
// Draw vector path
|
|
||||||
outline := clip.Outline{Path: st.PathSpec}.Op().Push(gtx.Ops)
|
outline := clip.Outline{Path: st.PathSpec}.Op().Push(gtx.Ops)
|
||||||
paint.ColorOp{Color: col}.Add(gtx.Ops)
|
paint.ColorOp{Color: col}.Add(gtx.Ops)
|
||||||
paint.PaintOp{}.Add(gtx.Ops)
|
paint.PaintOp{}.Add(gtx.Ops)
|
||||||
outline.Pop()
|
outline.Pop()
|
||||||
|
|
||||||
// Draw bitmap glyphs
|
if st.Bitmaps != (op.CallOp{}) {
|
||||||
if st.BitmapsCall != (op.CallOp{}) {
|
st.Bitmaps.Add(gtx.Ops)
|
||||||
st.BitmapsCall.Add(gtx.Ops)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Pop()
|
t.Pop()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user