tview
tview copied to clipboard
Keep Indentation when wrapping text
Sometimes it would be very useful to keep the indentation when wrapping text, I have noticed this when trying to indent complete text blocks in order to make them more readable.
For example I got this:
[::b]DESCRPTION
This command allows you to manipulate and retrieve your user information.
This command is split into multiple subcommands. The default subcommand
is [::b]user-get[::-] and will be used if no other command was supplied.
if it linebreaks, it will look like this:
[::b]DESCRPTION
This command allows you to manipulate and retrieve your user information.
This command is split into multiple subcommands. The default
subcommand is [::b]user-get[::-] and will be used if no other command was supplied.
So the rule should be, that when the current line has indentation in the front, the wrapped content will as well.
This should be optional and disabled by default
I'm not sure I will add this. This is also not the wrapping behaviour of common word processors. Also check out man
where you'll commonly see indented text. But it also doesn't break the lines like you suggest.
I just spent many hours rewriting the WordWrap()
function. This is a lot more complicated than one would expect. You suggestion would add a considerable amount of complexity on top, which is what I'm trying to avoid. And I'm not sure it would benefit a lot of people.
You may want to think about turning off line breaking and manually formatting for a fixed width.
Yeah, that's what I have done so far, but it kinda wastes vertical space, so I thought this might be very handy. I am not sure whether many people would use this or not. However, I understand if you don't want to add this because it's not worth the trade-offs.
I will keep this in mind. If other people need this, too, we can revisit this topic.
I ran into a similar, but not quite identical, desire while building a chat application using tview (when prefixing messages with a timestamp/username, I prefer when the message does not wrap around underneath the username/timestamp, but is rather indented to align with the start of the message, the way weechat does it).
Regarding your suggestion to format for fixed-width, is there a convenient way to hook into component size changes to reflow the text? Or perhaps would it be better to just write a custom component instead of using TextView for this?
I recommend a custom component. I had a lot of problems that I had to hack around. Performance was bad too.
EDIT
I am not talking about reindentation, but general use as a chat view. I am assuming you'll have a multitude of features :D
I guess I could add a hook for custom reflows. I would prefer that to implementing some automatic indentation detection that may lead to undesirable side effects in other contexts. Basically, I would add the option to supply your own version of the reindexBuffer(width int)
function. Let me tell you, though, that word wrapping is not simple, especially when you have to deal with Unicode.
Let me know what if that would help you.
(Reopening for now.)
If anyone who is still interested this, here is a small fix to keep indentation. I'm not sending a pull request, because I have changed lots of source code to fit my app. If you are okay with it, I will fork it again and make a clean commit for it. Anyways, here is the small fix: Preserve indent on textview
Edit: Doesnt work with dynamic colors, trying to solve it
dynamic color fix I think this solves it, but there is probably a lot better optimized way to handle this problem
Coming back to this again and thinking about it some more, I don't think this will be added, at least not any time soon. TextView
will see another rewrite in the near future (using the new segmenting algorithms of rivo/uniseg
) and I believe the additional complexity of offering a custom reflow option is just not worth it. I actually believe it would be quite difficult for anyone who wanted to use it, too. (Consider all the Unicode quirks, color tags, and region tags, which would need to be handled.)
But I didn't want to simply close this thread so here's a simple implementation which formats text in a different way. It keeps the original text outside the component (thus, the built-in io.Writer
interface cannot be used) but I suppose this could also be wrapped into its own class if needed.
package main
import (
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/rivo/uniseg"
)
const lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. "
func main() {
t, str := newTextView()
*str = strings.Repeat(lorem, 40)
if err := tview.NewApplication().SetRoot(t, true).Run(); err != nil {
panic(err)
}
}
func newTextView() (*tview.TextView, *string) {
var str string
textView := tview.NewTextView().SetWrap(false)
textView.SetBorder(true)
textView.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) {
// Let's apply our own formatting.
x, y, width, height = textView.GetInnerRect()
words := strings.Split(str, " ")
var (
b strings.Builder
row, column int
)
for _, word := range words {
w := uniseg.StringWidth(word)
if column+w >= width {
row++
if row%4 == 0 {
b.WriteString("\n")
column = 0
} else {
b.WriteString("\n ")
column = 4
}
}
b.WriteString(word)
b.WriteRune(' ')
column += w + 1
}
textView.SetText(b.String())
return x, y, width, height
})
return textView, &str
}