func (c *CheckBox) PreferredSize(self Widget, ctx *nanovgo.Context) (int, int) { fw, fh := c.FixedSize() if fw > 0 || fh > 0 { return fw, fh } fontSize := float32(c.FontSize()) ctx.SetFontSize(fontSize) ctx.SetFontFace(c.theme.FontNormal) w, _ := ctx.TextBounds(0, 0, c.caption) return int(w + 1.7*fontSize), int(fontSize * 1.3) }
func (w *Window) PreferredSize(self Widget, ctx *nanovgo.Context) (int, int) { if w.buttonPanel != nil { w.buttonPanel.SetVisible(false) } width, height := w.WidgetImplement.PreferredSize(self, ctx) if w.buttonPanel != nil { w.buttonPanel.SetVisible(true) } ctx.SetFontSize(18.0) ctx.SetFontFace(w.theme.FontBold) _, bounds := ctx.TextBounds(0, 0, w.title) return maxI(width, int(bounds[2]-bounds[0])+20), maxI(height, int(bounds[3]-bounds[1])) }
func drawButton(ctx *nanovgo.Context, preicon int, text string, x, y, w, h float32, col nanovgo.Color) { var cornerRadius float32 = 4.0 var iw float32 var alpha uint8 if isBlack(col) { alpha = 16 } else { alpha = 32 } bg := nanovgo.LinearGradient(x, y, x, y+h, nanovgo.RGBA(255, 255, 255, alpha), nanovgo.RGBA(0, 0, 0, alpha)) ctx.BeginPath() ctx.RoundedRect(x+1, y+1, w-2, h-2, cornerRadius-1) if !isBlack(col) { ctx.SetFillColor(col) ctx.Fill() } ctx.SetFillPaint(bg) ctx.Fill() ctx.BeginPath() ctx.RoundedRect(x+0.5, y+0.5, w-1, h-1, cornerRadius-0.5) ctx.SetStrokeColor(nanovgo.RGBA(0, 0, 0, 48)) ctx.Stroke() ctx.SetFontSize(20.0) ctx.SetFontFace("sans-bold") tw, _ := ctx.TextBounds(0, 0, text) if preicon != 0 { ctx.SetFontSize(h * 1.3) ctx.SetFontFace("icons") iw, _ = ctx.TextBounds(0, 0, cpToUTF8(preicon)) iw += h * 0.15 ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, 96)) ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) ctx.Text(x+w*0.5-tw*0.5-iw*0.75, y+h*0.5, cpToUTF8(preicon)) } ctx.SetFontSize(20.0) ctx.SetFontFace("sans-bold") ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) ctx.SetFillColor(nanovgo.RGBA(0, 0, 0, 160)) ctx.Text(x+w*0.5-tw*0.5+iw*0.25, y+h*0.5-1, text) ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, 160)) ctx.Text(x+w*0.5-tw*0.5+iw*0.25, y+h*0.5, text) }
func drawEditBoxNum(ctx *nanovgo.Context, text, units string, x, y, w, h float32) { drawEditBoxBase(ctx, x, y, w, h) uw, _ := ctx.TextBounds(0, 0, units) ctx.SetFontSize(18.0) ctx.SetFontFace("sans") ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, 64)) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignMiddle) ctx.Text(x+w-h*0.3, y+h*0.5, units) ctx.SetFontSize(20.0) ctx.SetFontFace("sans") ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, 128)) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignMiddle) ctx.Text(x+w-uw-h*0.5, y+h*0.5, text) }
func (t *TextBox) PreferredSize(self Widget, ctx *nanovgo.Context) (int, int) { sizeH := float32(t.FontSize()) * 1.4 var unitWidth, textWidth float32 ctx.SetFontSize(float32(t.FontSize())) if t.unitImage > 0 { w, h, _ := ctx.ImageSize(t.unitImage) unitHeight := sizeH * 0.4 unitWidth = float32(w) * unitHeight / float32(h) } else if t.units != "" { unitWidth, _ = ctx.TextBounds(0, 0, t.units) } textWidth, _ = ctx.TextBounds(0, 0, string(t.editingText())) sizeW := sizeH + textWidth + unitWidth return int(sizeW), int(sizeH) }
func (p *PopupButton) Draw(self Widget, ctx *nanovgo.Context) { if !p.enabled && p.pushed { p.pushed = false } p.popup.SetVisible(p.pushed) p.Button.Draw(self, ctx) if p.chevronIcon != 0 { ctx.SetFillColor(p.TextColor()) ctx.SetFontSize(float32(p.FontSize())) ctx.SetFontFace(p.theme.FontIcons) ctx.SetTextAlign(nanovgo.AlignMiddle | nanovgo.AlignLeft) fontString := string([]rune{rune(p.chevronIcon)}) iw, _ := ctx.TextBounds(0, 0, fontString) px, py := p.Position() w, h := p.Size() ix := px + w - int(iw) - 8 iy := py + h/2 - 1 ctx.Text(float32(ix), float32(iy), fontString) } }
func (b *Button) PreferredSize(self Widget, ctx *nanovgo.Context) (int, int) { fontSize := float32(b.FontSize()) ctx.SetFontSize(fontSize) ctx.SetFontFace(b.theme.FontBold) tw, _ := ctx.TextBounds(0, 0, b.caption) var iw float32 ih := fontSize if b.icon > 0 { ih *= 1.5 / 2 ctx.SetFontFace(b.theme.FontIcons) ctx.SetFontSize(ih) iw, _ = ctx.TextBounds(0, 0, string([]rune{rune(b.icon)})) iw += float32(b.y) * 0.15 } else if b.imageIcon > 0 { ih *= 0.9 w, h, _ := ctx.ImageSize(b.imageIcon) iw = float32(w) * ih / float32(h) } return int(tw + iw + 20), int(fontSize) + 10 }
func (l *Label) PreferredSize(self Widget, ctx *nanovgo.Context) (int, int) { if l.caption == "" { return 0, 0 } ctx.SetFontSize(float32(l.FontSize())) ctx.SetFontFace(l.Font()) width := 0 if l.FixedWidth() > 0 { width = l.FixedWidth() } else if l.columnWidth > 0 && l.wrap { width = l.columnWidth } if width > 0 { ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignTop) bounds := ctx.TextBoxBounds(0, 0, float32(width), l.caption) return width, int(bounds[3] - bounds[1]) } else { ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignTop) w, _ := ctx.TextBounds(0, 0, l.caption) return int(w), l.Theme().StandardFontSize } }
func drawParagraph(ctx *nanovgo.Context, x, y, width, height, mx, my float32) { text := "This is longer chunk of text.\n \n Would have used lorem ipsum but she was busy jumping over the lazy dog with the fox and all the men who came to the aid of the party." ctx.Save() defer ctx.Restore() ctx.SetFontSize(18.0) ctx.SetFontFace("sans") ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignTop) _, _, lineh := ctx.TextMetrics() // The text break API can be used to fill a large buffer of rows, // or to iterate over the text just few lines (or just one) at a time. // The "next" variable of the last returned item tells where to continue. runes := []rune(text) var gx, gy float32 var gutter int lnum := 0 for _, row := range ctx.TextBreakLinesRune(runes, width) { hit := mx > x && mx < (x+width) && my >= y && my < (y+lineh) ctx.BeginPath() var alpha uint8 if hit { alpha = 64 } else { alpha = 16 } ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, alpha)) ctx.Rect(x, y, row.Width, lineh) ctx.Fill() ctx.SetFillColor(nanovgo.RGBA(255, 255, 255, 255)) ctx.TextRune(x, y, runes[row.StartIndex:row.EndIndex]) if hit { var caretX float32 if mx < x+row.Width/2 { caretX = x } else { caretX = x + row.Width } px := x lineRune := runes[row.StartIndex:row.EndIndex] glyphs := ctx.TextGlyphPositionsRune(x, y, lineRune) for j, glyph := range glyphs { x0 := glyph.X var x1 float32 if j+1 < len(glyphs) { x1 = glyphs[j+1].X } else { x1 = x + row.Width } gx = x0*0.3 + x1*0.7 if mx >= px && mx < gx { caretX = glyph.X } px = gx } ctx.BeginPath() ctx.SetFillColor(nanovgo.RGBA(255, 192, 0, 255)) ctx.Rect(caretX, y, 1, lineh) ctx.Fill() gutter = lnum + 1 gx = x - 10 gy = y + lineh/2 } lnum++ y += lineh } if gutter > 0 { txt := strconv.Itoa(gutter) ctx.SetFontSize(13.0) ctx.SetTextAlign(nanovgo.AlignRight | nanovgo.AlignMiddle) _, bounds := ctx.TextBounds(gx, gy, txt) ctx.BeginPath() ctx.SetFillColor(nanovgo.RGBA(255, 192, 0, 255)) ctx.RoundedRect( float32(int(bounds[0]-4)), float32(int(bounds[1]-2)), float32(int(bounds[2]-bounds[0])+8), float32(int(bounds[3]-bounds[1])+4), float32(int(bounds[3]-bounds[1])+4)/2.0-1.0) ctx.Fill() ctx.SetFillColor(nanovgo.RGBA(32, 32, 32, 255)) ctx.Text(gx, gy, txt) } y += 20.0 ctx.SetFontSize(13.0) ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignTop) ctx.SetTextLineHeight(1.2) bounds := ctx.TextBoxBounds(x, y, 150, "Hover your mouse over the text to see calculated caret position.") // Fade the tooltip out when close to it. gx = absF((mx - (bounds[0]+bounds[2])*0.5) / (bounds[0] - bounds[2])) gy = absF((my - (bounds[1]+bounds[3])*0.5) / (bounds[1] - bounds[3])) a := maxF(gx, gy) - 0.5 a = clampF(a, 0, 1) ctx.SetGlobalAlpha(a) ctx.BeginPath() ctx.SetFillColor(nanovgo.RGBA(220, 220, 220, 255)) ctx.RoundedRect(bounds[0]-2, bounds[1]-2, float32(int(bounds[2]-bounds[0])+4), float32(int(bounds[3]-bounds[1])+4), 3) px := float32(int((bounds[2] + bounds[0]) / 2)) 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.RGBA(0, 0, 0, 220)) ctx.TextBox(x, y, 150, "Hover your mouse over the text to see calculated caret position.") }
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() }
func (b *Button) Draw(self Widget, ctx *nanovgo.Context) { b.WidgetImplement.Draw(self, ctx) bx := float32(b.x) by := float32(b.y) bw := float32(b.w) bh := float32(b.h) var gradTop nanovgo.Color var gradBot nanovgo.Color if b.pushed { gradTop = b.theme.ButtonGradientTopPushed gradBot = b.theme.ButtonGradientBotPushed } else if b.mouseFocus && b.enabled { gradTop = b.theme.ButtonGradientTopFocused gradBot = b.theme.ButtonGradientBotFocused } else { gradTop = b.theme.ButtonGradientTopUnfocused gradBot = b.theme.ButtonGradientBotUnfocused } ctx.BeginPath() ctx.RoundedRect(bx+1.0, by+1.0, bw-2.0, bh-2.0, float32(b.theme.ButtonCornerRadius-1)) if b.backgroundColor.A != 0.0 { bgColor := b.backgroundColor bgColor.A = 1.0 ctx.SetFillColor(bgColor) ctx.Fill() if b.pushed { gradTop.A = 0.8 gradBot.A = 0.8 } else { a := 1 - b.backgroundColor.A if !b.enabled { a = a*0.5 + 0.5 } gradTop.A = a gradBot.A = a } } bg := nanovgo.LinearGradient(bx, by, bx, by+bh, gradTop, gradBot) ctx.SetFillPaint(bg) ctx.Fill() ctx.BeginPath() var pOff float32 = 0.0 if b.pushed { pOff = 1.0 } ctx.RoundedRect(bx+0.5, by+1.5-pOff, bw-1.0, bh-2+pOff, float32(b.theme.ButtonCornerRadius)) ctx.SetStrokeColor(b.theme.BorderLight) ctx.Stroke() ctx.BeginPath() ctx.RoundedRect(bx+0.5, by+0.5, bw-1.0, bh-2, float32(b.theme.ButtonCornerRadius)) ctx.SetStrokeColor(b.theme.BorderDark) ctx.Stroke() fontSize := float32(b.FontSize()) ctx.SetFontSize(fontSize) ctx.SetFontFace(b.theme.FontBold) caption := b.caption tw, _ := ctx.TextBounds(0, 0, caption) centerX := bx + bw*0.5 centerY := by + bh*0.5 textPosX := centerX - tw*0.5 textPosY := centerY - 1.0 textColor := b.TextColor() if b.icon > 0 || b.imageIcon > 0 { var iw, ih float32 if b.icon > 0 { ih = fontSize * 1.5 / 2 ctx.SetFontSize(ih) ctx.SetFontFace(b.theme.FontIcons) iw, _ = ctx.TextBounds(0, 0, string([]rune{rune(b.icon)})) } else if b.imageIcon > 0 { ih = fontSize * 0.9 w, h, _ := ctx.ImageSize(b.imageIcon) iw = float32(w) * ih / float32(h) } if b.caption != "" { iw += float32(b.h) * 0.15 } ctx.SetFillColor(textColor) ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) iconPosX := centerX iconPosY := centerY - 1 switch b.iconPosition { case ButtonIconLeftCentered: iconPosX -= (tw + iw) * 0.5 textPosX += iw * 0.5 case ButtonIconRightCentered: iconPosX -= iw * 0.5 textPosX += tw * 0.5 case ButtonIconLeft: iconPosX = bx + 8.0 case ButtonIconRight: iconPosX = bx + bw - iw - 8 } if b.icon > 0 { ctx.TextRune(iconPosX, iconPosY, []rune{rune(b.icon)}) } else { var eOff float32 = 0.25 if b.enabled { eOff = 0.5 } imgPaint := nanovgo.ImagePattern(iconPosX, iconPosY-ih*0.5, iw, ih, 0, b.imageIcon, eOff) ctx.SetFillPaint(imgPaint) ctx.Fill() } } ctx.SetFontSize(fontSize) ctx.SetFontFace(b.theme.FontBold) ctx.SetTextAlign(nanovgo.AlignLeft | nanovgo.AlignMiddle) ctx.SetFillColor(b.theme.TextColorShadow) ctx.Text(textPosX, textPosY, caption) ctx.SetFillColor(textColor) ctx.Text(textPosX, textPosY+1.0, caption) }