package door import ( "bytes" "fmt" "strings" ) // TODO: Break out the progress bar characters into structures. // Write tests to verify that CP437 matches Unicode. // See box_test. ;) type BarStyle int8 const ( SOLID BarStyle = iota HALF_STEP GRADIENT ) type Percent_Style int8 const ( PERCENT_NONE Percent_Style = iota PERCENT PERCENT_SPACE ) type BarRange struct { Percent int64 Color []byte } type BarLine struct { Width int Style BarStyle Percent int64 // percentage * 100 PercentStyle Percent_Style ColorRange []BarRange UpdateP func() int64 Line } type BarCharacters struct { Solid string Half [2]string Gradient [4]string } var BARS_CP437 = BarCharacters{ "\xdb", [2]string{"\xdb", "\xdd"}, [4]string{"\xdb", "\xb0", "\xb1", "\xb2"}, } var BARS_UNICODE = BarCharacters{ "\u2588", [2]string{"\u2588", "\u258c"}, [4]string{"\u2588", "\u2591", "\u2592", "\u2593"}, } var BARS *BarCharacters = &BARS_CP437 func (bl *BarLine) CheckRange() { if len(bl.ColorRange) != 0 { // Ok, there is a color range. Get checking for _, br := range bl.ColorRange { if bl.Percent <= br.Percent { bl.DefaultColor = br.Color break } } } } func (bl *BarLine) Output() []byte { // bl.Text & bl.render & bl.update if bl.render == nil { bl.render = &bytes.Buffer{} } bl.render.Reset() var step_width int64 if bl.UpdateP != nil { bl.Percent = bl.UpdateP() } bl.CheckRange() // CheckRange can change the DefaultColor. bl.render.Write(bl.DefaultColor) if bl.update == nil { bl.update = &bytes.Buffer{} } bl.update.Reset() switch bl.Style { case SOLID: step_width = int64(100 * 100 / bl.Width) var steps int = int(bl.Percent / step_width) bl.update.WriteString(strings.Repeat(BARS.Solid, steps)) // This will work, because we aren't trying to len(output) with unicode. bl.update.WriteString(strings.Repeat(" ", int(bl.Width-steps))) case HALF_STEP: step_width = int64(100 * 100 / bl.Width) var steps int = int(bl.Percent * 2 / step_width) bl.update.WriteString(strings.Repeat(BARS.Half[0], steps/2)) if steps%2 == 1 { bl.update.WriteString(BARS.Half[1]) steps++ } bl.update.WriteString(strings.Repeat(" ", bl.Width-(steps/2))) case GRADIENT: step_width = int64(100 * 100 / bl.Width) var steps int = int(bl.Percent * 4 / step_width) bl.update.WriteString(strings.Repeat(BARS.Gradient[0], steps/4)) if steps%4 != 0 { switch steps % 4 { case 1, 2, 3: bl.update.WriteString(BARS.Gradient[steps%4]) } for steps%4 != 0 { steps++ } } bl.update.WriteString(strings.Repeat(" ", bl.Width-(steps/4))) } if bl.PercentStyle != PERCENT_NONE { var pctbuff [10]byte var percent = pctbuff[0:0] percent = fmt.Appendf(percent, "%d", bl.Percent/100) var pos int = bl.Width/2 - 1 if bytes.Compare(percent, []byte("100")) != 0 { percent = append(percent, '%') if len(percent) < 3 { percent = append(percent, ' ') copy(percent[1:], percent[0:]) percent[0] = ' ' // percent = " " + percent } } if bl.PercentStyle == PERCENT_SPACE { percent = append(percent, ' ') copy(percent[1:], percent[0:]) percent = append(percent, ' ') percent[0] = ' ' // percent = " " + percent + " " pos-- } // to process/slice the string (with unicode) do this: // convert to []rune, slice that, convert back to string // // sliceable := []rune(output) // newString := string(sliceable[:5]) + " new content " + (sliceable[10:]) // fmt.Printf("%d %d [%s] %d [%s]\n", bl.Width, pos, percent, len(output), output) // The % part -- isn't working at the moment. // I need to insert the percent into the buffer. (but how)? // and unicode? hmm! // Note: This takes ownership of update. bl.Text = bytes.NewBuffer(bl.update.Bytes()) var idx int = 0 if Unicode { for { rune, _, err := bl.Text.ReadRune() if err != nil { break } if (idx >= pos) && (idx < len(percent)+pos) { bl.render.WriteByte(percent[idx-pos]) } else { bl.render.WriteRune(rune) } idx++ } /* runes := []rune(output) for idx, b := range percent { runes[pos+idx] = rune(b) } output = string(runes[:pos]) + percent + string(runes[pos+len(percent):]) */ } else { for { b, err := bl.Text.ReadByte() if err != nil { break } if (idx >= pos) && (idx < len(percent)+pos) { bl.render.WriteByte(percent[idx-pos]) } else { bl.render.WriteByte(b) } idx++ } /* for idx, b := range percent { render[pos+idx] = b } */ // output = output[:pos] + percent + output[pos+len(percent):] } _ = idx } else { bl.render.Write(bl.update.Bytes()) } // return bl.DefaultColor + output return bl.render.Bytes() }