Projects STRLCPY termdash Commits 0fb44ec8
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    align/align.go
    skipped 18 lines
    19 19   "fmt"
    20 20   "image"
    21 21   "strings"
    22  - "unicode/utf8"
     22 + 
     23 + runewidth "github.com/mattn/go-runewidth"
    23 24  )
    24 25   
    25 26  // Horizontal indicates the type of horizontal alignment.
    skipped 121 lines
    147 148   return image.ZP, fmt.Errorf("the provided text contains a newline character: %q", text)
    148 149   }
    149 150   
    150  - runes := utf8.RuneCountInString(text)
     151 + cells := runewidth.StringWidth(text)
    151 152   var textLen int
    152  - if runes < rect.Dx() {
    153  - textLen = runes
     153 + if cells < rect.Dx() {
     154 + textLen = cells
    154 155   } else {
    155 156   textLen = rect.Dx()
    156 157   }
    skipped 17 lines
  • ■ ■ ■ ■ ■
    align/align_test.go
    skipped 274 lines
    275 275   want: image.Point{1, 2},
    276 276   },
    277 277   {
    278  - desc: "aligns text middle and center",
     278 + desc: "aligns half-width text rune middle and center",
    279 279   rect: image.Rect(1, 1, 4, 4),
    280 280   text: "a",
    281 281   hAlign: HorizontalCenter,
    282 282   vAlign: VerticalMiddle,
    283 283   want: image.Point{2, 2},
     284 + },
     285 + {
     286 + desc: "aligns full-width text rune middle and center",
     287 + rect: image.Rect(1, 1, 4, 4),
     288 + text: "界",
     289 + hAlign: HorizontalCenter,
     290 + vAlign: VerticalMiddle,
     291 + want: image.Point{1, 2},
    284 292   },
    285 293   {
    286 294   desc: "aligns text middle and right",
    skipped 57 lines
  • ■ ■ ■ ■ ■
    draw/border_test.go
    skipped 34 lines
    35 35   wantErr bool
    36 36   }{
    37 37   {
    38  - desc: "border is larger than canvas",
    39  - canvas: image.Rect(0, 0, 1, 1),
    40  - border: image.Rect(0, 0, 2, 2),
     38 + desc: "border is larger than canvas",
     39 + canvas: image.Rect(0, 0, 1, 1),
     40 + border: image.Rect(0, 0, 2, 2),
     41 + want: func(size image.Point) *faketerm.Terminal {
     42 + return faketerm.MustNew(size)
     43 + },
    41 44   wantErr: true,
    42 45   },
    43 46   {
    44  - desc: "border is too small",
    45  - canvas: image.Rect(0, 0, 2, 2),
    46  - border: image.Rect(0, 0, 1, 1),
     47 + desc: "border is too small",
     48 + canvas: image.Rect(0, 0, 2, 2),
     49 + border: image.Rect(0, 0, 1, 1),
     50 + want: func(size image.Point) *faketerm.Terminal {
     51 + return faketerm.MustNew(size)
     52 + },
    47 53   wantErr: true,
    48 54   },
    49 55   {
    skipped 2 lines
    52 58   border: image.Rect(0, 0, 2, 2),
    53 59   opts: []BorderOption{
    54 60   BorderLineStyle(LineStyle(-1)),
     61 + },
     62 + want: func(size image.Point) *faketerm.Terminal {
     63 + return faketerm.MustNew(size)
    55 64   },
    56 65   wantErr: true,
    57 66   },
    skipped 136 lines
    194 203   border: image.Rect(0, 0, 4, 4),
    195 204   opts: []BorderOption{
    196 205   BorderTitle("abc", OverrunModeStrict),
     206 + },
     207 + want: func(size image.Point) *faketerm.Terminal {
     208 + return faketerm.MustNew(size)
    197 209   },
    198 210   wantErr: true,
    199 211   },
    skipped 230 lines
  • ■ ■ ■ ■ ■ ■
    draw/rectangle.go
    skipped 50 lines
    51 51   })
    52 52  }
    53 53   
    54  -// DefaultRectChar is the default value for the RectChar option.
     54 +// DefaultRectChar is the default value for the RectChar option.
    55 55  const DefaultRectChar = ' '
    56 56   
    57 57  // RectChar sets the character used in each of the cells of the rectangle.
    skipped 5 lines
    63 63   
    64 64  // Rectangle draws a filled rectangle on the canvas.
    65 65  func Rectangle(c *canvas.Canvas, r image.Rectangle, opts ...RectangleOption) error {
    66  - opt := &rectOptions{}
     66 + opt := &rectOptions{
     67 + char: DefaultRectChar,
     68 + }
    67 69   for _, o := range opts {
    68 70   o.set(opt)
    69 71   }
    skipped 23 lines
  • ■ ■ ■ ■ ■ ■
    draw/text.go
    skipped 103 lines
    104 104   })
    105 105  }
    106 106   
    107  -// trimToCells trims the provided text so that it fits the specified amount of cells.
    108  -func trimToCells(text string, maxCells int, om OverrunMode) string {
     107 +// TrimText trims the provided text so that it fits the specified amount of cells.
     108 +func TrimText(text string, maxCells int, om OverrunMode) (string, error) {
    109 109   if maxCells < 1 {
    110  - return ""
     110 + return "", fmt.Errorf("maxCells(%d) cannot be less than one", maxCells)
     111 + }
     112 + 
     113 + textCells := runewidth.StringWidth(text)
     114 + if textCells <= maxCells {
     115 + // Nothing to do if the text fits.
     116 + return text, nil
     117 + }
     118 + 
     119 + switch om {
     120 + case OverrunModeStrict:
     121 + return "", fmt.Errorf("the requested text %q takes %d cells to draw, space is available for only %d cells and overrun mode is %v", text, textCells, maxCells, om)
     122 + case OverrunModeTrim, OverrunModeThreeDot:
     123 + default:
     124 + return "", fmt.Errorf("unsupported overrun mode %d", om)
    111 125   }
    112 126   
    113 127   var b bytes.Buffer
    114  - cells := 0
     128 + cur := 0
    115 129   for _, r := range text {
    116 130   rw := runewidth.RuneWidth(r)
    117  - if cells+rw >= maxCells {
     131 + if cur+rw >= maxCells {
    118 132   switch {
    119  - case om == OverrunModeTrim && rw == 1:
    120  - b.WriteRune(r)
     133 + case om == OverrunModeTrim:
     134 + // Only write the rune if it still fits, i.e. don't cut
     135 + // full-width runes in half.
     136 + if cur+rw == maxCells {
     137 + b.WriteRune(r)
     138 + }
    121 139   case om == OverrunModeThreeDot:
    122 140   b.WriteRune('…')
    123 141   }
    124 142   break
    125 143   }
    126  - b.WriteRune(r)
    127  - cells += rw
    128  - }
    129  - return b.String()
    130  -}
    131 144   
    132  -// bounds enforces the text bounds based on the specified overrun mode.
    133  -// Returns test that can be safely drawn within the bounds.
    134  -func bounds(text string, maxCells int, om OverrunMode) (string, error) {
    135  - cells := runewidth.StringWidth(text)
    136  - if cells <= maxCells {
    137  - return text, nil
    138  - }
    139  - 
    140  - switch om {
    141  - case OverrunModeStrict:
    142  - return "", fmt.Errorf("the requested text %q takes %d cells to draw, space is available for only %d cells and overrun mode is %v", text, cells, maxCells, om)
    143  - case OverrunModeTrim, OverrunModeThreeDot:
    144  - return trimToCells(text, maxCells, om), nil
    145  - default:
    146  - return "", fmt.Errorf("unsupported overrun mode %v", om)
     145 + b.WriteRune(r)
     146 + cur += rw
    147 147   }
     148 + return b.String(), nil
    148 149  }
    149 150   
    150 151  // Text prints the provided text on the canvas starting at the provided point.
    skipped 20 lines
    171 172   }
    172 173   
    173 174   maxCells := wantMaxX - start.X
    174  - trimmed, err := bounds(text, maxCells, opt.overrunMode)
     175 + trimmed, err := TrimText(text, maxCells, opt.overrunMode)
    175 176   if err != nil {
    176 177   return err
    177 178   }
    skipped 12 lines
  • ■ ■ ■ ■ ■ ■
    draw/text_test.go
    skipped 23 lines
    24 24   "github.com/mum4k/termdash/terminal/faketerm"
    25 25  )
    26 26   
     27 +func TestTrimText(t *testing.T) {
     28 + tests := []struct {
     29 + desc string
     30 + text string
     31 + maxCells int
     32 + om OverrunMode
     33 + want string
     34 + wantErr bool
     35 + }{
     36 + {
     37 + desc: "text is empty",
     38 + text: "",
     39 + maxCells: 1,
     40 + om: OverrunModeTrim,
     41 + want: "",
     42 + },
     43 + {
     44 + desc: "zero max cells",
     45 + text: "ab",
     46 + maxCells: 0,
     47 + om: OverrunModeTrim,
     48 + wantErr: true,
     49 + },
     50 + {
     51 + desc: "unsupported overrun mode",
     52 + text: "ab",
     53 + maxCells: 1,
     54 + om: OverrunMode(-1),
     55 + wantErr: true,
     56 + },
     57 + {
     58 + desc: "half-width runes, OverrunModeStrict, text fits exactly",
     59 + text: "ab",
     60 + maxCells: 2,
     61 + om: OverrunModeStrict,
     62 + want: "ab",
     63 + },
     64 + {
     65 + desc: "half-width runes, OverrunModeTrim, text fits exactly",
     66 + text: "ab",
     67 + maxCells: 2,
     68 + om: OverrunModeTrim,
     69 + want: "ab",
     70 + },
     71 + {
     72 + desc: "half-width runes, OverrunModeThreeDot, text fits exactly",
     73 + text: "ab",
     74 + maxCells: 2,
     75 + om: OverrunModeThreeDot,
     76 + want: "ab",
     77 + },
     78 + {
     79 + desc: "full-width runes, OverrunModeStrict, text fits exactly",
     80 + text: "你好",
     81 + maxCells: 4,
     82 + om: OverrunModeStrict,
     83 + want: "你好",
     84 + },
     85 + {
     86 + desc: "full-width runes, OverrunModeTrim, text fits exactly",
     87 + text: "你好",
     88 + maxCells: 4,
     89 + om: OverrunModeTrim,
     90 + want: "你好",
     91 + },
     92 + {
     93 + desc: "full-width runes, OverrunModeThreeDot, text fits exactly",
     94 + text: "你好",
     95 + maxCells: 4,
     96 + om: OverrunModeThreeDot,
     97 + want: "你好",
     98 + },
     99 + {
     100 + desc: "half-width runes, OverrunModeStrict, text overruns",
     101 + text: "ab",
     102 + maxCells: 1,
     103 + om: OverrunModeStrict,
     104 + wantErr: true,
     105 + },
     106 + {
     107 + desc: "half-width runes, OverrunModeTrim, text overruns",
     108 + text: "ab",
     109 + maxCells: 1,
     110 + om: OverrunModeTrim,
     111 + want: "a",
     112 + },
     113 + {
     114 + desc: "half-width runes, OverrunModeThreeDot, text overruns at the beginning",
     115 + text: "ab",
     116 + maxCells: 1,
     117 + om: OverrunModeThreeDot,
     118 + want: "…",
     119 + },
     120 + {
     121 + desc: "half-width runes, OverrunModeThreeDot, text overruns in the middle",
     122 + text: "abc",
     123 + maxCells: 2,
     124 + om: OverrunModeThreeDot,
     125 + want: "a…",
     126 + },
     127 + {
     128 + desc: "full-width runes, OverrunModeStrict, text overruns",
     129 + text: "你好",
     130 + maxCells: 1,
     131 + om: OverrunModeStrict,
     132 + wantErr: true,
     133 + },
     134 + {
     135 + desc: "full-width runes, OverrunModeTrim, text overruns at the beginning on rune boundary",
     136 + text: "你好",
     137 + maxCells: 2,
     138 + om: OverrunModeTrim,
     139 + want: "你",
     140 + },
     141 + {
     142 + desc: "full-width runes, OverrunModeThreeDot, text overruns at the beginning on rune boundary",
     143 + text: "你好",
     144 + maxCells: 2,
     145 + om: OverrunModeThreeDot,
     146 + want: "…",
     147 + },
     148 + {
     149 + desc: "full-width runes, OverrunModeTrim, text overruns in the middle on rune boundary",
     150 + text: "你好你好",
     151 + maxCells: 4,
     152 + om: OverrunModeTrim,
     153 + want: "你好",
     154 + },
     155 + {
     156 + desc: "full-width runes, OverrunModeThreeDot, text overruns in the middle on rune boundary",
     157 + text: "你好你好",
     158 + maxCells: 4,
     159 + om: OverrunModeThreeDot,
     160 + want: "你…",
     161 + },
     162 + {
     163 + desc: "full-width runes, OverrunModeTrim, text overruns at the beginning, cuts rune in half",
     164 + text: "你好",
     165 + maxCells: 1,
     166 + om: OverrunModeTrim,
     167 + want: "",
     168 + },
     169 + {
     170 + desc: "full-width runes, OverrunModeThreeDot, text overruns at the beginning, cuts rune in half",
     171 + text: "你好",
     172 + maxCells: 1,
     173 + om: OverrunModeThreeDot,
     174 + want: "…",
     175 + },
     176 + {
     177 + desc: "full-width runes, OverrunModeTrim, text overruns in the middle, cuts rune in half",
     178 + text: "你好你好",
     179 + maxCells: 3,
     180 + om: OverrunModeTrim,
     181 + want: "你",
     182 + },
     183 + {
     184 + desc: "full-width runes, OverrunModeThreeDot, text overruns in the middle, cuts rune in half",
     185 + text: "你好你好",
     186 + maxCells: 3,
     187 + om: OverrunModeThreeDot,
     188 + want: "你…",
     189 + },
     190 + }
     191 + 
     192 + for _, tc := range tests {
     193 + t.Run(tc.desc, func(t *testing.T) {
     194 + got, err := TrimText(tc.text, tc.maxCells, tc.om)
     195 + if (err != nil) != tc.wantErr {
     196 + t.Fatalf("TrimText => unexpected error: %v, wantErr: %v", err, tc.wantErr)
     197 + }
     198 + if err != nil {
     199 + return
     200 + }
     201 + 
     202 + if got != tc.want {
     203 + t.Errorf("TrimText =>\n got: %q\nwant: %q", got, tc.want)
     204 + }
     205 + })
     206 + }
     207 +}
     208 + 
    27 209  func TestText(t *testing.T) {
    28 210   tests := []struct {
    29 211   desc string
    skipped 393 lines
  • images/gaugedemo.gif
  • ■ ■ ■ ■ ■
    terminal/faketerm/diff.go
    skipped 55 lines
    56 56   var optDiffs []*optDiff
    57 57   for row := 0; row < size.Y; row++ {
    58 58   for col := 0; col < size.X; col++ {
     59 + p := image.Point{col, row}
     60 + partial, err := got.BackBuffer().IsPartial(p)
     61 + if err != nil {
     62 + panic(fmt.Errorf("unable to determine if point %v is a partial rune: %v", p, err))
     63 + }
     64 + 
    59 65   gotCell := got.BackBuffer()[col][row]
    60 66   wantCell := want.BackBuffer()[col][row]
    61 67   r := gotCell.Rune
    62 68   if r != wantCell.Rune {
    63 69   r = '࿃'
    64  - } else if r == 0 {
     70 + } else if r == 0 && !partial {
    65 71   r = ' '
    66 72   }
    67 73   b.WriteRune(r)
    skipped 23 lines
  • ■ ■ ■ ■ ■
    terminal/faketerm/faketerm.go
    skipped 120 lines
    121 121   for row := 0; row < size.Y; row++ {
    122 122   for col := 0; col < size.X; col++ {
    123 123   r := t.buffer[col][row].Rune
    124  - if r == 0 {
     124 + p := image.Point{col, row}
     125 + partial, err := t.buffer.IsPartial(p)
     126 + if err != nil {
     127 + panic(fmt.Errorf("unable to determine if point %v is a partial rune: %v", p, err))
     128 + }
     129 + if r == 0 && !partial {
    125 130   r = ' '
    126 131   }
    127 132   b.WriteRune(r)
    skipped 79 lines
  • ■ ■ ■ ■
    widgets/gauge/demo/gaugedemo.go
    skipped 107 lines
    108 108   go playGauge(ctx, noProgress, 5, 250*time.Millisecond, playTypePercent)
    109 109   withLabel := gauge.New(
    110 110   gauge.Height(3),
    111  - gauge.TextLabel("with text label and no border"),
     111 + gauge.TextLabel("! text label and no border"),
    112 112   gauge.Color(cell.ColorRed),
    113 113   gauge.FilledTextColor(cell.ColorBlack),
    114 114   gauge.EmptyTextColor(cell.ColorYellow),
    skipped 47 lines
  • ■ ■ ■ ■ ■ ■
    widgets/gauge/gauge.go
    skipped 20 lines
    21 21   "fmt"
    22 22   "image"
    23 23   "sync"
    24  - "unicode/utf8"
    25 24   
     25 + runewidth "github.com/mattn/go-runewidth"
    26 26   "github.com/mum4k/termdash/align"
    27 27   "github.com/mum4k/termdash/area"
    28 28   "github.com/mum4k/termdash/canvas"
    skipped 1 lines
    30 30   "github.com/mum4k/termdash/draw"
    31 31   "github.com/mum4k/termdash/terminalapi"
    32 32   "github.com/mum4k/termdash/widgetapi"
    33  - "golang.org/x/exp/utf8string"
    34 33  )
    35 34   
    36 35  // progressType indicates how was the current progress provided by the caller.
    skipped 147 lines
    184 183  }
    185 184   
    186 185  // drawText draws the text enumerating the progress and the text label.
    187  -func (g *Gauge) drawText(cvs *canvas.Canvas) error {
     186 +func (g *Gauge) drawText(cvs *canvas.Canvas, progress image.Rectangle) error {
    188 187   text := g.gaugeText()
    189 188   if text == "" {
    190 189   return nil
    191 190   }
    192 191   
    193 192   ar := g.usable(cvs)
    194  - textStart, err := align.Text(ar, text, g.opts.hTextAlign, g.opts.vTextAlign)
     193 + trimmed, err := draw.TrimText(text, ar.Dx(), draw.OverrunModeThreeDot)
    195 194   if err != nil {
    196 195   return err
    197 196   }
    198  - textEndX := textStart.X + utf8.RuneCountInString(text)
    199  - if textEndX >= ar.Max.X { // The text will be trimmed.
    200  - textEndX = ar.Max.X - 1
     197 + 
     198 + cur, err := align.Text(ar, trimmed, g.opts.hTextAlign, g.opts.vTextAlign)
     199 + if err != nil {
     200 + return err
    201 201   }
    202  - gaugeEndX := g.width(ar)
    203  - 
    204  - switch {
    205  - case gaugeEndX < textStart.X:
    206  - // The text entirely falls outside of the drawn gauge.
    207  - return draw.Text(cvs, text, textStart,
    208  - draw.TextOverrunMode(draw.OverrunModeThreeDot),
    209  - draw.TextCellOpts(cell.FgColor(g.opts.emptyTextColor)),
    210  - draw.TextMaxX(ar.Max.X),
    211  - )
    212 202   
    213  - case gaugeEndX >= textEndX:
    214  - // The text entirely falls inside of the drawn gauge.
    215  - return draw.Text(cvs, text, textStart,
    216  - draw.TextOverrunMode(draw.OverrunModeThreeDot),
    217  - draw.TextCellOpts(cell.FgColor(g.opts.filledTextColor)),
    218  - draw.TextMaxX(ar.Max.X),
    219  - )
    220  - 
    221  - default:
    222  - // Part of the text falls inside of the drawn gauge and part outside.
    223  - utfText := utf8string.NewString(text)
    224  - insideCount := ar.Min.X + gaugeEndX - textStart.X
    225  - insideText := utfText.Slice(0, insideCount)
    226  - outsideText := utfText.Slice(insideCount, utfText.RuneCount())
    227  - 
    228  - if err := draw.Text(cvs, insideText, textStart,
    229  - draw.TextOverrunMode(draw.OverrunModeTrim),
    230  - draw.TextCellOpts(cell.FgColor(g.opts.filledTextColor)),
    231  - ); err != nil {
    232  - return err
     203 + for _, r := range trimmed {
     204 + if !cur.In(ar) {
     205 + break
    233 206   }
    234 207   
    235  - outsideStart := image.Point{textStart.X + insideCount, textStart.Y}
    236  - if outsideStart.In(ar) {
    237  - if err := draw.Text(cvs, outsideText, outsideStart,
    238  - draw.TextOverrunMode(draw.OverrunModeThreeDot),
    239  - draw.TextCellOpts(cell.FgColor(g.opts.emptyTextColor)),
    240  - draw.TextMaxX(ar.Max.X),
     208 + next := image.Point{cur.X + 1, cur.Y}
     209 + rw := runewidth.RuneWidth(r)
     210 + // If the current rune is full-width and only one of its cells falls
     211 + // within the filled area of the gauge, extend the gauge by one cell to
     212 + // fully cover the full-width rune.
     213 + if rw == 2 && next.In(ar) && cur.In(progress) && !next.In(progress) {
     214 + fixup := image.Rect(
     215 + next.X,
     216 + ar.Min.Y,
     217 + next.X+1,
     218 + ar.Max.Y,
     219 + )
     220 + if err := draw.Rectangle(cvs, fixup,
     221 + draw.RectChar(g.opts.gaugeChar),
     222 + draw.RectCellOpts(cell.BgColor(g.opts.color)),
    241 223   ); err != nil {
    242 224   return err
    243 225   }
     226 + 
    244 227   }
     228 + 
     229 + var cellOpts []cell.Option
     230 + if cur.In(progress) {
     231 + cellOpts = append(cellOpts, cell.FgColor(g.opts.filledTextColor))
     232 + } else {
     233 + cellOpts = append(cellOpts, cell.FgColor(g.opts.emptyTextColor))
     234 + }
     235 + 
     236 + cells, err := cvs.SetCell(cur, r, cellOpts...)
     237 + if err != nil {
     238 + return err
     239 + }
     240 + 
     241 + cur = image.Point{cur.X + cells, cur.Y}
    245 242   }
    246 243   return nil
    247 244  }
    skipped 30 lines
    278 275   return err
    279 276   }
    280 277   }
    281  - return g.drawText(cvs)
     278 + return g.drawText(cvs, progress)
    282 279  }
    283 280   
    284 281  // Keyboard input isn't supported on the Gauge widget.
    skipped 43 lines
  • ■ ■ ■ ■ ■ ■
    widgets/gauge/gauge_test.go
    skipped 367 lines
    368 368   },
    369 369   },
    370 370   {
    371  - desc: "gauge with text label",
     371 + desc: "gauge with text label, half-width runes",
    372 372   gauge: New(
    373 373   Char('o'),
    374 374   HideTextProgress(),
    skipped 11 lines
    386 386   )
    387 387   testdraw.MustText(c, "(label)", image.Point{1, 1},
    388 388   draw.TextCellOpts(cell.FgColor(cell.ColorBlack)),
     389 + )
     390 + testcanvas.MustApply(c, ft)
     391 + return ft
     392 + },
     393 + },
     394 + {
     395 + desc: "gauge with text label, full-width runes",
     396 + gauge: New(
     397 + Char('o'),
     398 + HideTextProgress(),
     399 + TextLabel("你好"),
     400 + ),
     401 + percent: &percentCall{p: 100},
     402 + canvas: image.Rect(0, 0, 10, 3),
     403 + want: func(size image.Point) *faketerm.Terminal {
     404 + ft := faketerm.MustNew(size)
     405 + c := testcanvas.MustNew(ft.Area())
     406 + 
     407 + testdraw.MustRectangle(c, image.Rect(0, 0, 10, 3),
     408 + draw.RectChar('o'),
     409 + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
     410 + )
     411 + testdraw.MustText(c, "(你好)", image.Point{2, 1},
     412 + draw.TextCellOpts(cell.FgColor(cell.ColorBlack)),
     413 + )
     414 + testcanvas.MustApply(c, ft)
     415 + return ft
     416 + },
     417 + },
     418 + {
     419 + desc: "gauge with text label, full-width runes, gauge falls on rune boundary",
     420 + gauge: New(
     421 + Char('o'),
     422 + HideTextProgress(),
     423 + TextLabel("你好"),
     424 + ),
     425 + percent: &percentCall{p: 50},
     426 + canvas: image.Rect(0, 0, 10, 3),
     427 + want: func(size image.Point) *faketerm.Terminal {
     428 + ft := faketerm.MustNew(size)
     429 + c := testcanvas.MustNew(ft.Area())
     430 + 
     431 + testdraw.MustRectangle(c, image.Rect(0, 0, 5, 3),
     432 + draw.RectChar('o'),
     433 + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
     434 + )
     435 + testdraw.MustText(c, "(你", image.Point{2, 1},
     436 + draw.TextCellOpts(cell.FgColor(cell.ColorBlack)),
     437 + )
     438 + testdraw.MustText(c, "好)", image.Point{5, 1},
     439 + draw.TextCellOpts(cell.FgColor(cell.ColorDefault)),
     440 + )
     441 + testcanvas.MustApply(c, ft)
     442 + return ft
     443 + },
     444 + },
     445 + {
     446 + desc: "gauge with text label, full-width runes, gauge extended to cover full rune",
     447 + gauge: New(
     448 + Char('o'),
     449 + HideTextProgress(),
     450 + TextLabel("你好"),
     451 + ),
     452 + percent: &percentCall{p: 40},
     453 + canvas: image.Rect(0, 0, 10, 3),
     454 + want: func(size image.Point) *faketerm.Terminal {
     455 + ft := faketerm.MustNew(size)
     456 + c := testcanvas.MustNew(ft.Area())
     457 + 
     458 + testdraw.MustRectangle(c, image.Rect(0, 0, 5, 3),
     459 + draw.RectChar('o'),
     460 + draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
     461 + )
     462 + testdraw.MustText(c, "(你", image.Point{2, 1},
     463 + draw.TextCellOpts(cell.FgColor(cell.ColorBlack)),
     464 + )
     465 + testdraw.MustText(c, "好)", image.Point{5, 1},
     466 + draw.TextCellOpts(cell.FgColor(cell.ColorDefault)),
    389 467   )
    390 468   testcanvas.MustApply(c, ft)
    391 469   return ft
    skipped 354 lines
  • ■ ■ ■ ■ ■
    widgets/gauge/options.go
    skipped 48 lines
    49 49  // newOptions returns options with the default values set.
    50 50  func newOptions() *options {
    51 51   return &options{
     52 + gaugeChar: DefaultChar,
    52 53   hTextAlign: DefaultHorizontalTextAlign,
    53 54   vTextAlign: DefaultVerticalTextAlign,
    54 55   color: DefaultColor,
    skipped 138 lines
Please wait...
Page is in error, reload to recover