172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
|
package uix
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
"git.kirsle.net/apps/doodle/balance"
|
||
|
"git.kirsle.net/apps/doodle/render"
|
||
|
"git.kirsle.net/apps/doodle/ui"
|
||
|
)
|
||
|
|
||
|
// Present the canvas.
|
||
|
func (w *Canvas) Present(e render.Engine, p render.Point) {
|
||
|
var (
|
||
|
S = w.Size()
|
||
|
Viewport = w.Viewport()
|
||
|
)
|
||
|
// w.MoveTo(p) // TODO: when uncommented the canvas will creep down the Workspace frame in EditorMode
|
||
|
w.DrawBox(e, p)
|
||
|
e.DrawBox(w.Background(), render.Rect{
|
||
|
X: p.X + w.BoxThickness(1),
|
||
|
Y: p.Y + w.BoxThickness(1),
|
||
|
W: S.W - w.BoxThickness(2),
|
||
|
H: S.H - w.BoxThickness(2),
|
||
|
})
|
||
|
|
||
|
// Draw the wallpaper.
|
||
|
if w.wallpaper.Valid() {
|
||
|
err := w.PresentWallpaper(e, p)
|
||
|
if err != nil {
|
||
|
log.Error(err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get the chunks in the viewport and cache their textures.
|
||
|
for coord := range w.chunks.IterViewportChunks(Viewport) {
|
||
|
if chunk, ok := w.chunks.GetChunk(coord); ok {
|
||
|
var tex render.Texturer
|
||
|
if w.MaskColor != render.Invisible {
|
||
|
tex = chunk.TextureMasked(e, w.MaskColor)
|
||
|
} else {
|
||
|
tex = chunk.Texture(e)
|
||
|
}
|
||
|
src := render.Rect{
|
||
|
W: tex.Size().W,
|
||
|
H: tex.Size().H,
|
||
|
}
|
||
|
|
||
|
// If the source bitmap is already bigger than the Canvas widget
|
||
|
// into which it will render, cap the source width and height.
|
||
|
// This is especially useful for Doodad buttons because the drawing
|
||
|
// is bigger than the button.
|
||
|
if src.W > S.W {
|
||
|
src.W = S.W
|
||
|
}
|
||
|
if src.H > S.H {
|
||
|
src.H = S.H
|
||
|
}
|
||
|
|
||
|
dst := render.Rect{
|
||
|
X: p.X + w.Scroll.X + w.BoxThickness(1) + (coord.X * int32(chunk.Size)),
|
||
|
Y: p.Y + w.Scroll.Y + w.BoxThickness(1) + (coord.Y * int32(chunk.Size)),
|
||
|
|
||
|
// src.W and src.H will be AT MOST the full width and height of
|
||
|
// a Canvas widget. Subtract the scroll offset to keep it bounded
|
||
|
// visually on its right and bottom sides.
|
||
|
W: src.W,
|
||
|
H: src.H,
|
||
|
}
|
||
|
|
||
|
// TODO: all this shit is in TrimBox(), make it DRY
|
||
|
|
||
|
// If the destination width will cause it to overflow the widget
|
||
|
// box, trim off the right edge of the destination rect.
|
||
|
//
|
||
|
// Keep in mind we're dealing with chunks here, and a chunk is
|
||
|
// a small part of the image. Example:
|
||
|
// - Canvas is 800x600 (S.W=800 S.H=600)
|
||
|
// - Chunk wants to render at 790,0 width 100,100 or whatever
|
||
|
// dst={790, 0, 100, 100}
|
||
|
// - Chunk box would exceed 800px width (X=790 + W=100 == 890)
|
||
|
// - Find the delta how much it exceeds as negative (800 - 890 == -90)
|
||
|
// - Lower the Source and Dest rects by that delta size so they
|
||
|
// stay proportional and don't scale or anything dumb.
|
||
|
if dst.X+src.W > p.X+S.W {
|
||
|
// NOTE: delta is a negative number,
|
||
|
// so it will subtract from the width.
|
||
|
delta := (p.X + S.W - w.BoxThickness(1)) - (dst.W + dst.X)
|
||
|
src.W += delta
|
||
|
dst.W += delta
|
||
|
}
|
||
|
if dst.Y+src.H > p.Y+S.H {
|
||
|
// NOTE: delta is a negative number
|
||
|
delta := (p.Y + S.H - w.BoxThickness(1)) - (dst.H + dst.Y)
|
||
|
src.H += delta
|
||
|
dst.H += delta
|
||
|
}
|
||
|
|
||
|
// The same for the top left edge, so the drawings don't overlap
|
||
|
// menu bars or left side toolbars.
|
||
|
// - Canvas was placed 80px from the left of the screen.
|
||
|
// Canvas.MoveTo(80, 0)
|
||
|
// - A texture wants to draw at 60, 0 which would cause it to
|
||
|
// overlap 20 pixels into the left toolbar. It needs to be cropped.
|
||
|
// - The delta is: p.X=80 - dst.X=60 == 20
|
||
|
// - Set destination X to p.X to constrain it there: 20
|
||
|
// - Subtract the delta from destination W so we don't scale it.
|
||
|
// - Add 20 to X of the source: the left edge of source is not visible
|
||
|
if dst.X < p.X {
|
||
|
// NOTE: delta is a positive number,
|
||
|
// so it will add to the destination coordinates.
|
||
|
delta := p.X - dst.X
|
||
|
dst.X = p.X + w.BoxThickness(1)
|
||
|
dst.W -= delta
|
||
|
src.X += delta
|
||
|
}
|
||
|
if dst.Y < p.Y {
|
||
|
delta := p.Y - dst.Y
|
||
|
dst.Y = p.Y + w.BoxThickness(1)
|
||
|
dst.H -= delta
|
||
|
src.Y += delta
|
||
|
}
|
||
|
|
||
|
// Trim the destination width so it doesn't overlap the Canvas border.
|
||
|
if dst.W >= S.W-w.BoxThickness(1) {
|
||
|
dst.W = S.W - w.BoxThickness(1)
|
||
|
}
|
||
|
|
||
|
e.Copy(tex, src, dst)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
w.drawActors(e, p)
|
||
|
|
||
|
// XXX: Debug, show label in canvas corner.
|
||
|
if balance.DebugCanvasLabel {
|
||
|
rows := []string{
|
||
|
w.Name,
|
||
|
|
||
|
// XXX: debug options, uncomment for more details
|
||
|
|
||
|
// Size of the canvas
|
||
|
// fmt.Sprintf("S=%d,%d", S.W, S.H),
|
||
|
|
||
|
// Viewport of the canvas
|
||
|
// fmt.Sprintf("V=%d,%d:%d,%d",
|
||
|
// Viewport.X, Viewport.Y,
|
||
|
// Viewport.W, Viewport.H,
|
||
|
// ),
|
||
|
}
|
||
|
if w.actor != nil {
|
||
|
rows = append(rows,
|
||
|
fmt.Sprintf("WP=%s", w.actor.Point),
|
||
|
)
|
||
|
}
|
||
|
label := ui.NewLabel(ui.Label{
|
||
|
Text: strings.Join(rows, "\n"),
|
||
|
Font: render.Text{
|
||
|
FontFilename: balance.ShellFontFilename,
|
||
|
Size: balance.ShellFontSizeSmall,
|
||
|
Color: render.White,
|
||
|
},
|
||
|
})
|
||
|
label.SetBackground(render.RGBA(0, 0, 50, 150))
|
||
|
label.Compute(e)
|
||
|
label.Present(e, render.Point{
|
||
|
X: p.X + S.W - label.Size().W - w.BoxThickness(1),
|
||
|
Y: p.Y + w.BoxThickness(1),
|
||
|
})
|
||
|
}
|
||
|
}
|