mpb icon indicating copy to clipboard operation
mpb copied to clipboard

Add Marquee decorator for long message scrolling

Open DeathKing opened this issue 2 years ago • 3 comments

2023-03-22-231235

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.

DeathKing avatar Mar 22 '23 15:03 DeathKing

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 avatar Jul 29 '23 12:07 vbauerster

@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.

DeathKing avatar Jul 29 '23 22:07 DeathKing

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, "")
}

RikaCelery avatar Aug 29 '23 07:08 RikaCelery