diff --git a/_examples/richtext/main.go b/_examples/richtext/main.go index 8c7236c..b5cea95 100644 --- a/_examples/richtext/main.go +++ b/_examples/richtext/main.go @@ -130,9 +130,9 @@ func updatefn(w *nucular.Window) { switch selected { case 0: c.Align(richtext.Align(align)) - c.SetStyle(richtext.TextStyle{Face: header}) + c.SetStyle(richtext.TextStyle{Face: header, Cursor: font.TextCursor}) c.Text("Vispa Teresa\n") - c.SetStyle(richtext.TextStyle{Face: proportional}) + c.SetStyle(richtext.TextStyle{Face: proportional, Cursor: font.TextCursor}) c.Text("\n") c.Text("La vispa Teresa\navea tra l'erbetta\na volo sorpresa\ngentil farfalletta\n\n") c.Text("E tutta giuliva\nstringendola viva\ngridava a distesa\nl'ho presa! l'ho presa!\n\n") diff --git a/command/command.go b/command/command.go index 727ea5b..b025085 100644 --- a/command/command.go +++ b/command/command.go @@ -31,6 +31,7 @@ type Command struct { CircleFilled CircleFilled Image Image Text Text + Cursor font.Cursor } type CommandKind uint8 @@ -43,6 +44,7 @@ const ( CircleFilledCmd ImageCmd TextCmd + CursorCmd ) type Line struct { @@ -179,3 +181,15 @@ func (b *Buffer) DrawImage(r rect.Rect, img *image.RGBA) { cmd.Image.Img = img b.Commands = append(b.Commands, cmd) } + +func (b *Buffer) Cursor(r rect.Rect, cursor font.Cursor) { + if !r.Intersect(&b.Clip) { + return + } + + var cmd Command + cmd.Kind = CursorCmd + cmd.Rect = r + cmd.Cursor = cursor + b.Commands = append(b.Commands, cmd) +} diff --git a/font/cursor.go b/font/cursor.go new file mode 100644 index 0000000..3db3859 --- /dev/null +++ b/font/cursor.go @@ -0,0 +1,11 @@ +package font + +type Cursor uint8 + +const ( + DefaultCursor Cursor = iota + NoCursor + TextCursor + PointerCursor + ProgressCursor +) diff --git a/gio.go b/gio.go index 2310b23..e228872 100644 --- a/gio.go +++ b/gio.go @@ -581,6 +581,12 @@ func (ctx *context) Draw(ops *op.Ops, size image.Point, perf bool, perfString st drawText(ops, charAtlas, icmd.Text.Face, icmd.Text.Foreground, icmd.Rect, icmd.Text.String) stack.Pop() + case command.CursorCmd: + stack := gioclip.Rect(icmd.Rect.Rectangle()).Push(ops) + c := font2pointerCursor[icmd.Cursor] + c.Add(ops) + stack.Pop() + default: panic(UnknownCommandErr) } @@ -589,6 +595,14 @@ func (ctx *context) Draw(ops *op.Ops, size image.Point, perf bool, perfString st return len(ctx.cmds) } +var font2pointerCursor = map[font.Cursor]pointer.Cursor{ + font.DefaultCursor: pointer.CursorDefault, + font.NoCursor: pointer.CursorNone, + font.TextCursor: pointer.CursorText, + font.PointerCursor: pointer.CursorPointer, + font.ProgressCursor: pointer.CursorProgress, +} + func drawText(ops *op.Ops, charAtlas map[charAtlasKey]map[rune]renderedGlyph, fontFace font.Face, foreground color.RGBA, rect rect.Rect, str string) { m := charAtlas[charAtlasKey{fontFace, foreground}] if m == nil { diff --git a/masterwindow.go b/masterwindow.go index 9a36e45..1a2bb7a 100644 --- a/masterwindow.go +++ b/masterwindow.go @@ -192,12 +192,7 @@ func (w *masterWindowCommon) drawChanged() bool { pcmd := &w.prevCmds[i] switch cmds[i].Kind { - case command.ScissorCmd: - if *pcmd != *cmd { - return true - } - - case command.LineCmd: + case command.ScissorCmd, command.LineCmd, command.TriangleFilledCmd, command.CircleFilledCmd, command.ImageCmd, command.TextCmd, command.CursorCmd: if *pcmd != *cmd { return true } @@ -210,26 +205,6 @@ func (w *masterWindowCommon) drawChanged() bool { return true } - case command.TriangleFilledCmd: - if *pcmd != *cmd { - return true - } - - case command.CircleFilledCmd: - if *pcmd != *cmd { - return true - } - - case command.ImageCmd: - if *pcmd != *cmd { - return true - } - - case command.TextCmd: - if *pcmd != *cmd { - return true - } - default: panic(UnknownCommandErr) } diff --git a/richtext/draw.go b/richtext/draw.go index 57e0d55..d42f8e0 100644 --- a/richtext/draw.go +++ b/richtext/draw.go @@ -201,22 +201,22 @@ func (rtxt *RichText) drawRows(w *nucular.Window, viewporth int) *Ctor { s := rtxt.Sel.S - line.off[i] chunk1 := chunk.sub(0, s) w1 := line.chunkWidthEx(i, 0, chunk1, rtxt.adv) - drawChunk(w, out, &p, chunk1, siter.styleSel, w1, line.h, line.asc) + drawChunk(w, out, &p, chunk1, siter.styleSel, w1, line.h, line.asc, rowSpacing) if chunkrng.contains(rtxt.Sel.E) { e := rtxt.Sel.E - line.off[i] chunk2 := chunk.sub(s, e) w2 := line.chunkWidthEx(i, s, chunk2, rtxt.adv) - rtxt.drawSelectedChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc) + rtxt.drawSelectedChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc, rowSpacing) chunk3 := chunk.sub(e, chunk.len()) w3 := line.chunkWidthEx(i, e, chunk3, rtxt.adv) - drawChunk(w, out, &p, chunk3, siter.styleSel, w3, line.h, line.asc) + drawChunk(w, out, &p, chunk3, siter.styleSel, w3, line.h, line.asc, rowSpacing) insel = selAfter } else { chunk2 := chunk.sub(s, chunk.len()) w2 := line.chunkWidthEx(i, s, chunk2, rtxt.adv) - rtxt.drawSelectedChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc) + rtxt.drawSelectedChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc, rowSpacing) insel = selInside } } else { @@ -228,17 +228,17 @@ func (rtxt *RichText) drawRows(w *nucular.Window, viewporth int) *Ctor { e := rtxt.Sel.E - line.off[i] chunk1 := chunk.sub(0, e) w1 := line.chunkWidthEx(i, 0, chunk1, rtxt.adv) - rtxt.drawSelectedChunk(w, out, &p, chunk1, siter.styleSel, w1, line.h, line.asc) + rtxt.drawSelectedChunk(w, out, &p, chunk1, siter.styleSel, w1, line.h, line.asc, rowSpacing) chunk2 := chunk.sub(e, chunk.len()) w2 := line.chunkWidthEx(i, e, chunk2, rtxt.adv) - drawChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc) + drawChunk(w, out, &p, chunk2, siter.styleSel, w2, line.h, line.asc, rowSpacing) insel = selAfter } else if chunkrng.S >= rtxt.Sel.E { insel = selAfter simpleDrawChunk = true } else { - rtxt.drawSelectedChunk(w, out, &p, chunk, siter.styleSel, line.w[i], line.h, line.asc) + rtxt.drawSelectedChunk(w, out, &p, chunk, siter.styleSel, line.w[i], line.h, line.asc, rowSpacing) } case selAfter: @@ -249,7 +249,7 @@ func (rtxt *RichText) drawRows(w *nucular.Window, viewporth int) *Ctor { } if simpleDrawChunk { - drawChunk(w, out, &p, chunk, siter.styleSel, line.w[i], line.h, line.asc) + drawChunk(w, out, &p, chunk, siter.styleSel, line.w[i], line.h, line.asc, rowSpacing) if insel == selTick && (rtxt.flags&ShowTick != 0) && (rtxt.wasFocused || (rtxt.flags&Keyboard == 0 && rtxt.flags&Editable == 0)) && chunkrng.contains(rtxt.Sel.S) { x := p.X - line.w[i] + line.chunkWidth(i, rtxt.Sel.S-line.off[i], rtxt.adv) rtxt.drawTick(w, out, image.Point{x, p.Y}, line.h, siter.styleSel.Color, lineidx) @@ -323,13 +323,13 @@ func (rtxt *RichText) drawTick(w *nucular.Window, out *command.Buffer, p image.P out.StrokeLine(image.Point{p.X, p.Y}, image.Point{p.X, p.Y + lineh}, linethick, color) } -func (rtxt *RichText) drawSelectedChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chunk, styleSel styleSel, width, lineh, lineasc int) { +func (rtxt *RichText) drawSelectedChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chunk, styleSel styleSel, width, lineh, lineasc int, rowSpacing int) { styleSel.BgColor = rtxt.selColor styleSel.Color = styleSel.SelFgColor - drawChunk(w, out, p, chunk, styleSel, width, lineh, lineasc) + drawChunk(w, out, p, chunk, styleSel, width, lineh, lineasc, rowSpacing) } -func drawChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chunk, styleSel styleSel, width, lineh, lineasc int) { +func drawChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chunk, styleSel styleSel, width, lineh, lineasc int, rowSpacing int) { if chunk.isspacing() { if debugDrawBoundingBoxes && width > 0 { yoff := alignBaseline(lineh, lineasc, styleSel.Face) @@ -340,6 +340,10 @@ func drawChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chu r := rect.Rect{X: p.X, Y: p.Y, H: lineh, W: width} out.FillRect(r, 0, styleSel.BgColor) } + if styleSel.Cursor != 0 { + b2 := rect.Rect{X: p.X, Y: p.Y, H: lineh + rowSpacing, W: width} + out.Cursor(b2, styleSel.Cursor) + } } else { r := rect.Rect{X: p.X, Y: p.Y, H: lineh, W: width} @@ -374,6 +378,17 @@ func drawChunk(w *nucular.Window, out *command.Buffer, p *image.Point, chunk chu y := p.Y + lineasc + m.Descent.Ceil() - m.Ascent.Ceil()/2 out.StrokeLine(image.Point{p.X, y}, image.Point{p.X + width, y}, linethick, styleSel.Color) } + + b2 := r + b2.H += rowSpacing + + if styleSel.isLink && styleSel.Cursor == 0 { + out.Cursor(b2, font.PointerCursor) + } + + if styleSel.Cursor != 0 { + out.Cursor(b2, styleSel.Cursor) + } } p.X += width diff --git a/richtext/rtxt.go b/richtext/rtxt.go index 949ed62..324debb 100644 --- a/richtext/rtxt.go +++ b/richtext/rtxt.go @@ -148,8 +148,9 @@ func (chunk chunk) str() string { } type TextStyle struct { - Face font.Face - Flags FaceFlags + Face font.Face + Cursor font.Cursor + Flags FaceFlags Color, SelFgColor, BgColor color.RGBA // foreground color, selected foreground color, background color diff --git a/shiny.go b/shiny.go index b0fbd94..10bba0e 100644 --- a/shiny.go +++ b/shiny.go @@ -642,6 +642,8 @@ func (ctx *context) Draw(wimg *image.RGBA) int { if perfUpdate { txttim += time.Since(t0) } + case command.CursorCmd: + // not supported by shiny default: panic(UnknownCommandErr) } diff --git a/text.go b/text.go index 994a79a..00c7f24 100644 --- a/text.go +++ b/text.go @@ -1409,6 +1409,7 @@ func (d *drawableTextEditor) Draw(z *nstyle.Style, out *command.Buffer) { state := d.State style := d.Style bounds := d.Bounds + out.Cursor(d.Area, font.TextCursor) font := z.Font area := d.Area row_height := d.RowHeight