Markdown.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package giu
  2. import (
  3. "image"
  4. "image/color"
  5. "net/http"
  6. "strings"
  7. "time"
  8. "github.com/AllenDang/imgui-go"
  9. "github.com/faiface/mainthread"
  10. )
  11. // MarkdownWidget implements DearImGui markdown extension
  12. // https://github.com/juliettef/imgui_markdown
  13. // It is like LabelWidget but with md formatting.
  14. type MarkdownWidget struct {
  15. md *string
  16. linkCb func(url string)
  17. headers []imgui.MarkdownHeaderData
  18. }
  19. // Markdown creates new markdown widget.
  20. func Markdown(md *string) *MarkdownWidget {
  21. return &MarkdownWidget{
  22. md: md,
  23. linkCb: OpenURL,
  24. }
  25. }
  26. // OnLink sets another than default link callback.
  27. func (m *MarkdownWidget) OnLink(cb func(url string)) *MarkdownWidget {
  28. m.linkCb = cb
  29. return m
  30. }
  31. // Header sets header formatting
  32. // NOTE: level (counting from 0!) is header level. (for instance, header `# H1` will have level 0).
  33. func (m *MarkdownWidget) Header(level int, font *FontInfo, separator bool) *MarkdownWidget {
  34. // ensure if header data are at least as long as level
  35. if m.headers == nil {
  36. m.headers = make([]imgui.MarkdownHeaderData, level)
  37. }
  38. if level <= len(m.headers) {
  39. m.headers = append(m.headers, make([]imgui.MarkdownHeaderData, len(m.headers)-level+1)...)
  40. }
  41. if font != nil {
  42. if f, ok := extraFontMap[font.String()]; ok {
  43. m.headers[level].Font = *f
  44. }
  45. }
  46. m.headers[level].HasSeparator = separator
  47. return m
  48. }
  49. // Build implements Widget interface.
  50. func (m *MarkdownWidget) Build() {
  51. imgui.Markdown(tStrPtr(m.md), m.linkCb, loadImage, m.headers)
  52. }
  53. func loadImage(path string) imgui.MarkdownImageData {
  54. var img *image.RGBA
  55. var err error
  56. switch {
  57. case strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://"):
  58. // Load image from url
  59. client := &http.Client{Timeout: 5 * time.Second}
  60. resp, respErr := client.Get(path)
  61. if respErr != nil {
  62. return imgui.MarkdownImageData{}
  63. }
  64. defer func() {
  65. closeErr := resp.Body.Close()
  66. Assert((closeErr == nil), "MarkdownWidget", "loadImage", "Could not close http request!")
  67. }()
  68. rgba, _, imgErr := image.Decode(resp.Body)
  69. if imgErr != nil {
  70. return imgui.MarkdownImageData{}
  71. }
  72. img = ImageToRgba(rgba)
  73. default:
  74. img, err = LoadImage(path)
  75. if err != nil {
  76. return imgui.MarkdownImageData{}
  77. }
  78. }
  79. size := img.Bounds()
  80. // nolint:gocritic // TODO/BUG: figure out, why it doesn't work as expected and consider
  81. // if current workaround is save
  82. /*
  83. tex := &Texture{}
  84. NewTextureFromRgba(img, func(t *Texture) {
  85. fmt.Println("creating texture")
  86. tex.id = t.id
  87. })
  88. */
  89. var id imgui.TextureID
  90. mainthread.Call(func() {
  91. var err error
  92. id, err = Context.renderer.LoadImage(img)
  93. if err != nil {
  94. return
  95. }
  96. })
  97. return imgui.MarkdownImageData{
  98. TextureID: &id,
  99. Scale: true,
  100. Size: imgui.Vec2{
  101. X: float32(size.Dx()),
  102. Y: float32(size.Dy()),
  103. },
  104. UseLinkCallback: true,
  105. // default values
  106. Uv0: ToVec2(image.Point{0, 0}),
  107. Uv1: ToVec2(image.Point{1, 1}),
  108. TintColor: ToVec4Color(color.RGBA{255, 255, 255, 255}),
  109. BorderColor: ToVec4Color(color.RGBA{0, 0, 0, 0}),
  110. }
  111. }