gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package badge
  4. import (
  5. "strings"
  6. "sync"
  7. "unicode"
  8. actions_model "code.gitea.io/gitea/models/actions"
  9. )
  10. // The Badge layout: |offset|label|message|
  11. // We use 10x scale to calculate more precisely
  12. // Then scale down to normal size in tmpl file
  13. type Text struct {
  14. text string
  15. width int
  16. x int
  17. }
  18. func (t Text) Text() string {
  19. return t.text
  20. }
  21. func (t Text) Width() int {
  22. return t.width
  23. }
  24. func (t Text) X() int {
  25. return t.x
  26. }
  27. func (t Text) TextLength() int {
  28. return int(float64(t.width-defaultOffset) * 10)
  29. }
  30. type Badge struct {
  31. IDPrefix string
  32. FontFamily string
  33. Color string
  34. FontSize int
  35. Label Text
  36. Message Text
  37. }
  38. func (b Badge) Width() int {
  39. return b.Label.width + b.Message.width
  40. }
  41. // Style follows https://shields.io/badges
  42. const (
  43. StyleFlat = "flat"
  44. StyleFlatSquare = "flat-square"
  45. )
  46. const (
  47. defaultOffset = 10
  48. defaultFontSize = 11
  49. DefaultColor = "#9f9f9f" // Grey
  50. DefaultFontFamily = "DejaVu Sans,Verdana,Geneva,sans-serif"
  51. DefaultStyle = StyleFlat
  52. )
  53. var GlobalVars = sync.OnceValue(func() (ret struct {
  54. StatusColorMap map[actions_model.Status]string
  55. DejaVuGlyphWidthData map[rune]uint8
  56. AllStyles []string
  57. },
  58. ) {
  59. ret.StatusColorMap = map[actions_model.Status]string{
  60. actions_model.StatusSuccess: "#4c1", // Green
  61. actions_model.StatusSkipped: "#dfb317", // Yellow
  62. actions_model.StatusUnknown: "#97ca00", // Light Green
  63. actions_model.StatusFailure: "#e05d44", // Red
  64. actions_model.StatusCancelled: "#fe7d37", // Orange
  65. actions_model.StatusWaiting: "#dfb317", // Yellow
  66. actions_model.StatusRunning: "#dfb317", // Yellow
  67. actions_model.StatusBlocked: "#dfb317", // Yellow
  68. }
  69. ret.DejaVuGlyphWidthData = dejaVuGlyphWidthDataFunc()
  70. ret.AllStyles = []string{StyleFlat, StyleFlatSquare}
  71. return ret
  72. })
  73. // GenerateBadge generates badge with given template
  74. func GenerateBadge(label, message, color string) Badge {
  75. lw := calculateTextWidth(label) + defaultOffset
  76. mw := calculateTextWidth(message) + defaultOffset
  77. lx := lw * 5
  78. mx := lw*10 + mw*5 - 10
  79. return Badge{
  80. FontFamily: DefaultFontFamily,
  81. Label: Text{
  82. text: label,
  83. width: lw,
  84. x: lx,
  85. },
  86. Message: Text{
  87. text: message,
  88. width: mw,
  89. x: mx,
  90. },
  91. FontSize: defaultFontSize * 10,
  92. Color: color,
  93. }
  94. }
  95. func calculateTextWidth(text string) int {
  96. width := 0
  97. widthData := GlobalVars().DejaVuGlyphWidthData
  98. for _, char := range strings.TrimSpace(text) {
  99. charWidth, ok := widthData[char]
  100. if !ok {
  101. // use the width of 'm' in case of missing glyph width data for a printable character
  102. if unicode.IsPrint(char) {
  103. charWidth = widthData['m']
  104. } else {
  105. charWidth = 0
  106. }
  107. }
  108. width += int(charWidth)
  109. }
  110. return width
  111. }