func (g *Graph) Draw(self Widget, ctx *nanovgo.Context) { g.WidgetImplement.Draw(self, ctx) x := float32(g.x) y := float32(g.y) w := float32(g.w) h := float32(g.h) ctx.BeginPath() ctx.Rect(x, y, w, h) ctx.SetFillColor(g.backgroundColor) ctx.Fill() if len(g.values) < 2 { return } ctx.BeginPath() ctx.MoveTo(x, y+h) dx := float32(len(g.values) - 1) for i, v := range g.values { vx := x + float32(i)*w/dx vy := y + (1.0-v)*h ctx.LineTo(vx, vy) } ctx.LineTo(x+w, y+h) ctx.SetStrokeColor(nanovgo.MONO(100, 255)) ctx.Stroke() ctx.SetFillColor(g.foregroundColor) ctx.Fill() ctx.SetFontFace(g.theme.FontNormal) ctx.SetFillColor(g.textColor) if g.caption != "" { ctx.SetFontSize(14) ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignTop) ctx.Text(x+3, y+1, g.caption) } if g.header != "" { ctx.SetFontSize(18) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignTop) ctx.Text(x+w-3, y+1, g.header) } if g.footer != "" { ctx.SetFontSize(15) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignBottom) ctx.Text(x+w-3, y+h-1, g.footer) } ctx.BeginPath() ctx.Rect(x, y, w, h) ctx.SetStrokeColor(nanovgo.MONO(100, 255)) ctx.Stroke() }
func (s *Screen) drawWidgets() { if !s.visible { return } s.window.MakeContextCurrent() s.fbW, s.fbH = s.window.GetFramebufferSize() s.w, s.h = s.window.GetSize() gl.Viewport(0, 0, s.fbW, s.fbH) s.pixelRatio = float32(s.fbW) / float32(s.w) s.context.BeginFrame(s.w, s.h, s.pixelRatio) s.Draw(s, s.context) elapsed := GetTime() - s.lastInteraction if elapsed > 0.5 { // Draw tooltips widget := s.FindWidget(s, s.mousePosX, s.mousePosY) if widget != nil && widget.Tooltip() != "" { var tooltipWidth float32 = 150 ctx := s.context ctx.SetFontFace(s.theme.FontNormal) ctx.SetFontSize(15.0) ctx.SetTextAlign(nanovgo.AlignCenter | nanovgo.AlignTop) ctx.SetTextLineHeight(1.1) posX, posY := widget.AbsolutePosition() posX += widget.Width() / 2 posY += widget.Height() + 10 bounds := ctx.TextBoxBounds(float32(posX), float32(posY), tooltipWidth, widget.Tooltip()) ctx.SetGlobalAlpha(minF(1.0, 2*(elapsed-0.5)) * 0.8) ctx.BeginPath() ctx.SetFillColor(nanovgo.MONO(0, 255)) h := (bounds[2] - bounds[0]) / 2 ctx.RoundedRect(bounds[0]-4-h, bounds[1]-4, bounds[2]-bounds[0]+8, bounds[3]-bounds[1]+8, 3) px := (bounds[2]+bounds[0])/2 - h ctx.MoveTo(px, bounds[1]-10) ctx.LineTo(px+7, bounds[1]+1) ctx.LineTo(px-7, bounds[1]+1) ctx.Fill() ctx.SetFillColor(nanovgo.MONO(255, 255)) ctx.SetFontBlur(0.0) ctx.TextBox(float32(posX)-h, float32(posY), tooltipWidth, widget.Tooltip()) } } s.context.EndFrame() }
func (i *ImagePanel) Draw(self Widget, ctx *nanovgo.Context) { cols, _ := i.gridSize() x := float32(i.x) y := float32(i.y) thumbSize := float32(i.thumbSize) for j, image := range i.images { pX := x + float32(i.margin+(j%cols)*(i.thumbSize+i.spacing)) pY := y + float32(i.margin+(j/cols)*(i.thumbSize+i.spacing)) imgW, imgH, _ := ctx.ImageSize(image.ImageID) var iw, ih, ix, iy float32 if imgW < imgH { iw = thumbSize ih = iw * float32(imgH) / float32(imgW) ix = 0 iy = -(ih - thumbSize) * 0.5 } else { ih = thumbSize iw = ih * float32(imgH) / float32(imgW) iy = 0 ix = -(iw - thumbSize) * 0.5 } imgPaint := nanovgo.ImagePattern(pX+ix, pY+iy, iw, ih, 0, image.ImageID, toF(i.mouseIndex == j, 1.0, 0.7)) ctx.BeginPath() ctx.RoundedRect(pX, pY, thumbSize, thumbSize, 5) ctx.SetFillPaint(imgPaint) ctx.Fill() shadowPaint := nanovgo.BoxGradient(pX-1, pY, thumbSize+2, thumbSize+2, 5, 3, nanovgo.MONO(0, 128), nanovgo.MONO(0, 0)) ctx.BeginPath() ctx.Rect(pX-5, pY-5, thumbSize+10, thumbSize+10) ctx.RoundedRect(pX, pY, thumbSize, thumbSize, 6) ctx.PathWinding(nanovgo.Hole) ctx.SetFillPaint(shadowPaint) ctx.Fill() ctx.BeginPath() ctx.RoundedRect(pX+0.5, pY+0.5, thumbSize-1, thumbSize-1, 4-0.5) ctx.SetStrokeWidth(1.0) ctx.SetStrokeColor(nanovgo.MONO(255, 80)) ctx.Stroke() } }
func NewGraph(parent Widget, captions ...string) *Graph { var caption string switch len(captions) { case 0: caption = "Untitled" case 1: caption = captions[0] default: panic("NewGraph can accept only one extra parameter (label)") } graph := &Graph{ caption: caption, backgroundColor: nanovgo.MONO(20, 128), foregroundColor: nanovgo.RGBA(255, 192, 0, 128), textColor: nanovgo.MONO(240, 192), } InitWidget(graph, parent) return graph }
func (p *ProgressBar) Draw(self Widget, ctx *nanovgo.Context) { px := float32(p.x) py := float32(p.y) pw := float32(p.w) ph := float32(p.h) p.WidgetImplement.Draw(self, ctx) paint := nanovgo.BoxGradient(px+1, py+1, pw-2, ph, 3, 4, nanovgo.MONO(0, 32), nanovgo.MONO(0, 92)) ctx.BeginPath() ctx.RoundedRect(px, py, pw, ph, 3) ctx.SetFillPaint(paint) ctx.Fill() value := clampF(p.value, 0.0, 1.0) barPos := (pw - 2) * value barPaint := nanovgo.BoxGradient(px, py, barPos+1.5, ph-1, 3, 4, nanovgo.MONO(220, 100), nanovgo.MONO(128, 100)) ctx.BeginPath() ctx.RoundedRect(px+1, py+1, barPos, ph-2, 3) ctx.SetFillPaint(barPaint) ctx.Fill() }
func (v *VScrollPanel) Draw(self Widget, ctx *nanovgo.Context) { if len(v.children) == 0 { return } x := float32(v.x) y := float32(v.y) w := float32(v.w) h := float32(v.h) child := v.children[0] layout := self.Layout() if layout != nil { _, v.childPreferredHeight = layout.PreferredSize(self, ctx) } else { _, v.childPreferredHeight = child.PreferredSize(child, ctx) } ctx.Save() ctx.Translate(x, y) ctx.Scissor(0, 0, w, h) ctx.Translate(0, -v.scroll*(float32(v.childPreferredHeight)-h)) if child.Visible() { child.Draw(child, ctx) } ctx.Restore() if v.childPreferredHeight > v.h { scrollH := h * minF(1.0, h/float32(v.childPreferredHeight)) scrollH = minF(maxF(20.0, scrollH), h) paint := nanovgo.BoxGradient(x+w-12+1, y+4+1, 8, h-8, 3, 4, nanovgo.MONO(0, 32), nanovgo.MONO(0, 92)) ctx.BeginPath() ctx.RoundedRect(x+w-12, y+4, 8, h-8, 3) ctx.SetFillPaint(paint) ctx.Fill() barPaint := nanovgo.BoxGradient(x+y-12-1, y+4+1+(h-8-scrollH)*v.scroll-1, 8, scrollH, 3, 4, nanovgo.MONO(220, 100), nanovgo.MONO(128, 100)) ctx.BeginPath() ctx.RoundedRect(x+w-12+1, y+4+1+(h-8-scrollH)*v.scroll, 8-2, scrollH-2, 2) ctx.SetFillPaint(barPaint) ctx.Fill() } }
func (c *CheckBox) Draw(self Widget, ctx *nanovgo.Context) { cx := float32(c.x) cy := float32(c.y) ch := float32(c.h) c.WidgetImplement.Draw(self, ctx) fontSize := float32(c.FontSize()) ctx.SetFontSize(fontSize) ctx.SetFontFace(c.theme.FontNormal) if c.enabled { ctx.SetFillColor(c.theme.TextColor) } else { ctx.SetFillColor(c.theme.DisabledTextColor) } ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) ctx.Text(cx+1.2*ch+5, cy+ch*0.5, c.caption) var bgAlpha uint8 if c.pushed { bgAlpha = 100 } else { bgAlpha = 32 } bgPaint := nanovgo.BoxGradient(cx+1.5, cy+1.5, ch-2.0, ch-2.0, 3, 3, nanovgo.MONO(0, bgAlpha), nanovgo.MONO(0, 180)) ctx.BeginPath() ctx.RoundedRect(cx+1.0, cy+1.0, ch-2.0, ch-2.0, 3) ctx.SetFillPaint(bgPaint) ctx.Fill() if c.checked { ctx.SetFontSize(ch) ctx.SetFontFace(c.theme.FontIcons) if c.enabled { ctx.SetFillColor(c.theme.IconColor) } else { ctx.SetFillColor(c.theme.DisabledTextColor) } ctx.SetTextAlign(nanovgo.AlignCenter | nanovgo.AlignMiddle) ctx.Text(cx+ch*0.5+1.0, cy+ch*0.5, string([]rune{rune(IconCheck)})) } }
func (c *ColorWheel) Draw(self Widget, ctx *nanovgo.Context) { c.WidgetImplement.Draw(self, ctx) if !c.visible { return } x := float32(c.x) y := float32(c.y) w := float32(c.w) h := float32(c.h) ctx.Save() defer ctx.Restore() cx := x + w*0.5 cy := y + h*0.5 r1 := toF(w < h, w, h)*0.5 - 5.0 r0 := r1 * 0.75 aeps := 0.7 / r1 // half a pixel arc length in radians (2pi cancels out). for i := 0; i < 6; i++ { a0 := float32(i)/6.0*nanovgo.PI*2.0 - aeps a1 := float32(i+1)/6.0*nanovgo.PI*2.0 + aeps ctx.BeginPath() ctx.Arc(cx, cy, r0, a0, a1, nanovgo.Clockwise) ctx.Arc(cx, cy, r1, a1, a0, nanovgo.CounterClockwise) ctx.ClosePath() sin1, cos1 := sinCosF(a0) sin2, cos2 := sinCosF(a1) ax := cx + cos1*(r0+r1)*0.5 ay := cy + sin1*(r0+r1)*0.5 bx := cx + cos2*(r0+r1)*0.5 by := cy + sin2*(r0+r1)*0.5 color1 := nanovgo.HSLA(a0/(nanovgo.PI*2), 1.0, 0.55, 255) color2 := nanovgo.HSLA(a1/(nanovgo.PI*2), 1.0, 0.55, 255) paint := nanovgo.LinearGradient(ax, ay, bx, by, color1, color2) ctx.SetFillPaint(paint) ctx.Fill() } ctx.BeginPath() ctx.Circle(cx, cy, r0-0.5) ctx.Circle(cx, cy, r1+0.5) ctx.SetStrokeColor(nanovgo.MONO(0, 64)) ctx.Stroke() // Selector ctx.Save() defer ctx.Restore() ctx.Translate(cx, cy) ctx.Rotate(c.hue * nanovgo.PI * 2) // Marker on u := clampF(r1/50, 1.5, 4.0) ctx.SetStrokeWidth(u) ctx.BeginPath() ctx.Rect(r0-1, -2*u, r1-r0+2, 4*u) ctx.SetStrokeColor(nanovgo.MONO(255, 192)) ctx.Stroke() paint := nanovgo.BoxGradient(r0-3, -5, r1-r0+6, 10, 2, 4, nanovgo.MONO(0, 128), nanovgo.MONO(0, 0)) ctx.BeginPath() ctx.Rect(r0-2-10, -4-10, r1-r0+4+20, 8+20) ctx.Rect(r0-2, -4, r1-r0+4, 8) ctx.PathWinding(nanovgo.Hole) ctx.SetFillPaint(paint) ctx.Fill() // Center triangle r := r0 - 6 sin1, cos1 := sinCosF(120.0 / 180.0 * nanovgo.PI) sin2, cos2 := sinCosF(-120.0 / 180.0 * nanovgo.PI) ax := cos1 * r ay := sin1 * r bx := cos2 * r by := sin2 * r ctx.BeginPath() ctx.MoveTo(r, 0) ctx.LineTo(ax, ay) ctx.LineTo(bx, by) ctx.ClosePath() triPaint1 := nanovgo.LinearGradient(r, 0, ax, ay, nanovgo.HSL(c.hue, 1.0, 0.5), nanovgo.MONO(255, 255)) ctx.SetFillPaint(triPaint1) ctx.Fill() triPaint2 := nanovgo.LinearGradient((r+ax)*0.5, ay*0.5, bx, by, nanovgo.MONO(0, 0), nanovgo.MONO(0, 255)) ctx.SetFillPaint(triPaint2) ctx.Fill() // selector circle on triangle px, py := c.calculatePosition() ctx.SetStrokeWidth(u) ctx.BeginPath() ctx.Circle(px, py, 2*u) ctx.SetStrokeColor(nanovgo.MONO(255, 192)) ctx.Stroke() }
func NewStandardTheme(ctx *nanovgo.Context) *Theme { ctx.CreateFontFromMemory("sans", MustAsset("fonts/Roboto-Regular.ttf"), 0) ctx.CreateFontFromMemory("sans-bold", MustAsset("fonts/Roboto-Bold.ttf"), 0) ctx.CreateFontFromMemory("icons", MustAsset("fonts/entypo.ttf"), 0) return &Theme{ StandardFontSize: 16, ButtonFontSize: 20, TextBoxFontSize: 20, WindowCornerRadius: 2, WindowHeaderHeight: 30, WindowDropShadowSize: 10, ButtonCornerRadius: 2, DropShadow: nanovgo.MONO(0, 128), Transparent: nanovgo.MONO(0, 0), BorderDark: nanovgo.MONO(29, 255), BorderLight: nanovgo.MONO(92, 255), BorderMedium: nanovgo.MONO(35, 255), TextColor: nanovgo.MONO(255, 160), DisabledTextColor: nanovgo.MONO(255, 80), TextColorShadow: nanovgo.MONO(0, 160), IconColor: nanovgo.MONO(255, 160), ButtonGradientTopFocused: nanovgo.MONO(64, 255), ButtonGradientBotFocused: nanovgo.MONO(48, 255), ButtonGradientTopUnfocused: nanovgo.MONO(74, 255), ButtonGradientBotUnfocused: nanovgo.MONO(58, 255), ButtonGradientTopPushed: nanovgo.MONO(41, 255), ButtonGradientBotPushed: nanovgo.MONO(29, 255), WindowFillUnfocused: nanovgo.MONO(43, 230), WindowFillFocused: nanovgo.MONO(45, 230), WindowTitleUnfocused: nanovgo.MONO(220, 160), WindowTitleFocused: nanovgo.MONO(255, 190), WindowHeaderGradientTop: nanovgo.MONO(74, 255), WindowHeaderGradientBot: nanovgo.MONO(58, 255), WindowHeaderSepTop: nanovgo.MONO(92, 255), WindowHeaderSepBot: nanovgo.MONO(29, 255), WindowPopup: nanovgo.MONO(50, 255), WindowPopupTransparent: nanovgo.MONO(50, 0), FontNormal: "sans", FontBold: "sans-bold", FontIcons: "icons", } }
func (t *TextBox) Draw(self Widget, ctx *nanovgo.Context) { t.WidgetImplement.Draw(self, ctx) x := float32(t.x) y := float32(t.y) w := float32(t.w) h := float32(t.h) bg := nanovgo.BoxGradient(x+1, y+2, w-2, h-2, 3, 4, nanovgo.MONO(255, 32), nanovgo.MONO(32, 32)) fg1 := nanovgo.BoxGradient(x+1, y+2, w-2, h-2, 3, 4, nanovgo.MONO(150, 32), nanovgo.MONO(32, 32)) fg2 := nanovgo.BoxGradient(x+1, y+2, w-2, h-2, 3, 4, nanovgo.RGBA(255, 0, 0, 100), nanovgo.RGBA(255, 0, 0, 50)) ctx.BeginPath() ctx.RoundedRect(x+1, y+2, w-2, h-2, 3) if t.editable && t.Focused() { if t.validFormat { ctx.SetFillPaint(fg1) } else { ctx.SetFillPaint(fg2) } } else { ctx.SetFillPaint(bg) } ctx.Fill() ctx.BeginPath() ctx.RoundedRect(x+0.5, y+0.5, w-1, h-1, 2.5) ctx.SetStrokeColor(nanovgo.MONO(0, 48)) ctx.Stroke() ctx.SetFontSize(float32(t.FontSize())) ctx.SetFontFace(t.Font()) drawPosX := x drawPosY := y + h*0.5 + 1 xSpacing := h * 0.3 var unitWidth float32 if t.unitImage > 0 { iw, ih, _ := ctx.ImageSize(t.unitImage) unitHeight := float32(ih) * 0.4 unitWidth = float32(iw) * unitHeight / float32(h) imgPaint := nanovgo.ImagePattern(x+w-xSpacing-unitWidth, drawPosY-unitHeight*0.5, unitWidth, unitHeight, 0, t.unitImage, toF(t.enabled, 0.7, 0.35)) ctx.BeginPath() ctx.Rect(x+w-xSpacing-unitWidth, drawPosY-unitHeight*0.5, unitWidth, unitHeight) ctx.SetFillPaint(imgPaint) ctx.Fill() unitWidth += 2 } else if t.units != "" { unitWidth, _ = ctx.TextBounds(0, 0, t.units) ctx.SetFillColor(nanovgo.MONO(255, toB(t.enabled, 64, 32))) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignMiddle) ctx.Text(x+w-xSpacing, drawPosY, t.units) } switch t.alignment { case TextLeft: ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) drawPosX += xSpacing case TextRight: ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignMiddle) drawPosX += w - unitWidth - xSpacing case TextCenter: ctx.SetTextAlign(nanovgo.AlignCenter | nanovgo.AlignMiddle) drawPosX += w * 0.5 } if t.enabled { ctx.SetFillColor(t.theme.TextColor) } else { ctx.SetFillColor(t.theme.DisabledTextColor) } // clip visible text area clipX := x + xSpacing - 1 clipY := y + 1.0 clipWidth := w - unitWidth - 2.0*xSpacing + 2.0 clipHeight := h - 3.0 ctx.Scissor(clipX, clipY, clipWidth, clipHeight) oldDrawPosX := drawPosX drawPosX += t.textOffset if t.committed { ctx.Text(drawPosX, drawPosY, t.value) } else { text := t.editingText() textString := string(text) _, bounds := ctx.TextBounds(drawPosX, drawPosY, textString) lineH := bounds[3] - bounds[1] // find cursor positions glyphs := ctx.TextGlyphPositionsRune(drawPosX, drawPosY, text) t.updateCursor(ctx, bounds[2], glyphs) // compute text offset prevCPos := toI(t.cursorPos > 0, t.cursorPos-1, 0) nextCPos := toI(t.cursorPos < len(glyphs), t.cursorPos+1, len(glyphs)) prevCX := t.textIndex2Position(prevCPos, bounds[2], glyphs) nextCX := t.textIndex2Position(nextCPos, bounds[2], glyphs) if nextCX > clipX+clipWidth { t.textOffset -= nextCX - (clipX + clipWidth) + 1.0 } if prevCX < clipX { t.textOffset += clipX - prevCX + 1.0 } drawPosX = oldDrawPosX + t.textOffset // draw text with offset ctx.TextRune(drawPosX, drawPosY, text) _, bounds = ctx.TextBounds(drawPosX, drawPosY, textString) // recompute cursor position glyphs = ctx.TextGlyphPositionsRune(drawPosX, drawPosY, text) var caretX float32 = -1 if len(t.preeditText) != 0 { // draw preedit text caretX = t.textIndex2Position(t.cursorPos+len(t.preeditText), bounds[2], glyphs) offsetIndex := t.cursorPos offsetX := t.textIndex2Position(t.cursorPos, bounds[2], glyphs) ctx.SetStrokeColor(nanovgo.MONO(255, 160)) ctx.SetFillColor(nanovgo.MONO(255, 80)) ctx.SetStrokeWidth(2.0) for i, blockLength := range t.preeditBlocks { nextOffsetIndex := offsetIndex + blockLength nextOffsetX := t.textIndex2Position(nextOffsetIndex, bounds[2], glyphs) if i != t.preeditFocusedBlock { ctx.BeginPath() ctx.MoveTo(offsetX+2, drawPosY+lineH*0.5-1) ctx.LineTo(nextOffsetX-2, drawPosY+lineH*0.5-1) ctx.Stroke() } else { ctx.BeginPath() ctx.Rect(offsetX, drawPosY-lineH*0.5, nextOffsetX-offsetX, lineH) ctx.Fill() } offsetIndex = nextOffsetIndex offsetX = nextOffsetX } screen := t.FindWindow().Parent().(*Screen) oldCurX, oldCurY, oldCurH := screen.PreeditCursorPos() absX, absY := t.Parent().AbsolutePosition() newCurX := int(caretX) + absX newCurY := int(drawPosY+lineH*0.5) + absY newCurH := int(lineH) if oldCurX != newCurX || oldCurY != newCurY || oldCurH != newCurH { screen.SetPreeditCursorPos(newCurX, newCurY, newCurH) } } else if t.cursorPos > -1 { // regular cursor and selection area caretX = t.textIndex2Position(t.cursorPos, bounds[2], glyphs) if t.selectionPos > -1 { caretX2 := caretX selX := t.textIndex2Position(t.selectionPos, bounds[2], glyphs) if caretX2 > selX { selX, caretX2 = caretX2, selX } // draw selection ctx.BeginPath() ctx.SetFillColor(nanovgo.MONO(255, 80)) ctx.Rect(caretX2, drawPosY-lineH*0.5, selX-caretX2, lineH) ctx.Fill() } } if caretX > 0 { // draw cursor ctx.BeginPath() ctx.MoveTo(caretX, drawPosY-lineH*0.5) ctx.LineTo(caretX, drawPosY+lineH*0.5) ctx.SetStrokeColor(nanovgo.RGBA(255, 192, 0, 255)) ctx.SetStrokeWidth(1.0) ctx.Stroke() } } ctx.ResetScissor() }