imgui_markdown.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package imgui
  2. // #include "imgui_markdown_wrapper.h"
  3. import "C"
  4. type MarkdownHeaderData struct {
  5. Font Font
  6. HasSeparator bool
  7. }
  8. type MarkdownImageData struct {
  9. TextureID *TextureID
  10. Scale bool
  11. Size Vec2
  12. UseLinkCallback bool
  13. Uv0, Uv1 Vec2
  14. TintColor, BorderColor Vec4
  15. }
  16. // markdownImageCallbackCache stores user-definied image loader
  17. // it is only way to share it with C callback
  18. var markdownImageCallbackCache func(url string) MarkdownImageData
  19. // markdownImageCache stores markdown image data
  20. // TODO: meybe it should be done on client-side?...
  21. var markdownImageCache map[string]*MarkdownImageData
  22. func init() {
  23. markdownImageCache = make(map[string]*MarkdownImageData)
  24. }
  25. // Markdown implements imgui_markdown.h
  26. // NOTE about arguments:
  27. // - data is pointer to markdown text data
  28. // - linkCB is callback called when link is clicked (it should most likely open link in a web browser)
  29. // - imageCB is expected to load an image at `path` and return POINTER to its texture with some other
  30. // stats. BE AWARE that imageCB MUST NOT pause goroutine. It could e.g.:
  31. // - create variable with TextureID = 0
  32. // - invoge a new goroutine and load the texture there and set pointers value
  33. // - return pointer to declared variable
  34. // - headers are headers formatting data. Note, that first index of slice will be applied
  35. // to top-level (H1), second for H2 and so on.
  36. func Markdown(data *string, linkCB func(s string), imageCB func(path string) MarkdownImageData, headers []MarkdownHeaderData) {
  37. // share imageCB with C callback (goMarkdownImageCallback)
  38. markdownImageCallbackCache = imageCB
  39. state := newInputTextState(*data, nil)
  40. defer func() {
  41. *data = state.buf.toGo()
  42. state.release()
  43. }()
  44. // prepare headers for C
  45. cHeaders := []C.iggMarkdownHeaderData{}
  46. if headers != nil {
  47. for _, data := range headers {
  48. cHeaders = append(cHeaders,
  49. C.iggMarkdownHeaderData{
  50. font: data.Font.handle(),
  51. separator: castBool(data.HasSeparator),
  52. },
  53. )
  54. }
  55. }
  56. var cHeadersPtr *C.iggMarkdownHeaderData
  57. if len(cHeaders) > 0 {
  58. // this trick allows to pass go slice into C
  59. // documentation: https://coderwall.com/p/m_ma7q/pass-go-slices-as-c-array-parameters
  60. cHeadersPtr = &cHeaders[0]
  61. }
  62. linkData := C.iggMarkdown(
  63. (*C.char)(state.buf.ptr),
  64. cHeadersPtr, (C.int)(len(cHeaders)),
  65. )
  66. // Read link callback
  67. s := C.GoString(linkData.link)
  68. s = s[:int(linkData.link_len)]
  69. if s != "" {
  70. linkCB(s)
  71. }
  72. }
  73. // goMarkdownImageCallback is exported to C callback for loading markdown images.
  74. // in short, it calls user-definied cached in markdownImageCallbackCache function.
  75. //export goMarkdownImageCallback
  76. func goMarkdownImageCallback(data C.iggMarkdownLinkCallbackData) (result C.iggMarkdownImageData) {
  77. if markdownImageCallbackCache == nil {
  78. return result
  79. }
  80. path := C.GoString(data.link)
  81. path = path[:int(data.link_len)]
  82. // it calls user-definied function only at first time when this is called.
  83. if _, found := markdownImageCache[path]; !found {
  84. markdownImageCache[path] = &MarkdownImageData{}
  85. go func() {
  86. d := markdownImageCallbackCache(path)
  87. *markdownImageCache[path] = d
  88. }()
  89. }
  90. d := markdownImageCache[path]
  91. // check if texture id isn't nil
  92. if d.TextureID == nil {
  93. return result
  94. }
  95. sizeArg, _ := d.Size.wrapped()
  96. uv0, _ := d.Uv0.wrapped()
  97. uv1, _ := d.Uv1.wrapped()
  98. tintColor, _ := d.TintColor.wrapped()
  99. borderColor, _ := d.BorderColor.wrapped()
  100. result = C.iggMarkdownImageData{
  101. texture: d.TextureID.handle(),
  102. shouldScale: castBool(d.Scale),
  103. size: *sizeArg,
  104. useLinkCallback: castBool(d.UseLinkCallback),
  105. uv0: *uv0,
  106. uv1: *uv1,
  107. tintColor: *tintColor,
  108. borderColor: *borderColor,
  109. }
  110. // return to C
  111. return result
  112. }