mpb
mpb copied to clipboard
Add Marquee decorator for long message scrolling

Add Marquee decorator that scrolls text from right to left, it is useful when displaying long messages. The codes are not carefully written, modifications are welcomed.
Implementation assumes that (string width = bytes width), therefore it's going to fail with non Latin strings. Try it for example with Россия великая страна! string to see what I mean.
@vbauerster Da, I know what you mean, I noticed that issue too. After opened this PR and some investigation of mpb source code, I found the go-runewidth library that you used may be the solution.
I implement this feature in my project like this (https://github.com/DeathKing/pico/blob/master/cmd/pdf2image/bar.go#L14-L28):
func Marquee(textGetter func() string, ws uint, wcc ...decor.WC) decor.Decorator {
var count uint
f := func(s decor.Statistics) string {
text := textGetter()
runes := []rune(text)
msg := string(runes[int(count)%len(runes):])
count++
return runewidth.FillRight(
runewidth.Truncate(msg, int(ws), ""),
int(ws))
}
return decor.Any(f, wcc...)
}
https://github.com/vbauerster/mpb/assets/895809/95bf73d0-7628-49b2-881a-18d78ed0d7dd
For this specific implementation, the API requires a getter function to obtain the text for display, which may not suitable for most case, a string parameter should be used instead as I suppose.
I'd like to know your opinion about this feature.
Try this?🤔 It works well for me.
func Marquee(t string, ws int, divider string, wcc ...decor.WC) decor.Decorator {
var count int
var f = func(s decor.Statistics) string {
length := runewidth.StringWidth(t + divider)
if runewidth.StringWidth(t) < ws {
return runewidth.FillRight(t, ws)
}
text := t + divider
var msg string
if count-ws > length {
msg = TruncateLeft(Truncate(text, count+ws), ws)
} else {
msg = TruncateLeft(Truncate(text, count+ws), count)
}
if count+ws > length {
msg += Truncate(text, count+ws-length)
}
count++
if count+ws >= len(t)+len(divider)-1 {
count = 0
}
return runewidth.FillRight(msg, ws)
}
return decor.Any(f, wcc...)
}
func Truncate(s string, size int) string {
return runewidth.Truncate(s, size, "")
}
func TruncateLeft(s string, size int) string {
return runewidth.TruncateLeft(s, size, "")
}