[GH-ISSUE #1104] [Feature Request] ScrollView Widget/Primitive #802

Open
opened 2026-03-04 01:07:51 +03:00 by kerem · 12 comments
Owner

Originally created by @ossenthusiast on GitHub (Jul 20, 2025).
Original GitHub issue: https://github.com/rivo/tview/issues/1104

Add a new tview.Primitive that is a scrollable list of tview.Primitives.

Originally created by @ossenthusiast on GitHub (Jul 20, 2025). Original GitHub issue: https://github.com/rivo/tview/issues/1104 Add a new `tview.Primitive` that is a scrollable list of `tview.Primitive`s.
Author
Owner

@ossenthusiast commented on GitHub (Jul 21, 2025):

Do you still maintain this? @rivo

<!-- gh-comment-id:3097657209 --> @ossenthusiast commented on GitHub (Jul 21, 2025): Do you still maintain this? @rivo
Author
Owner

@rivo commented on GitHub (Jul 21, 2025):

Yes. Please be patient.

<!-- gh-comment-id:3099180346 --> @rivo commented on GitHub (Jul 21, 2025): Yes. Please be patient.
Author
Owner

@rivo commented on GitHub (Jul 21, 2025):

Also, I'm not sure what you mean by "scrollable list of Primitives". You might want to elaborate. With examples, if possible.

<!-- gh-comment-id:3099188358 --> @rivo commented on GitHub (Jul 21, 2025): Also, I'm not sure what you mean by "scrollable list of `Primitive`s". You might want to elaborate. With examples, if possible.
Author
Owner

@ossenthusiast commented on GitHub (Jul 21, 2025):

Also, I'm not sure what you mean by "scrollable list of Primitives". You might want to elaborate. With examples, if possible.

Sorry for the mention. I mean basically a scrollable horizontal layout (list) of widgets.

tview.NewScrollList().
    Append(tview.NewInputField()).
    Append(tview.NewBox()).
    Append(tview.NewFlex())
<!-- gh-comment-id:3099243630 --> @ossenthusiast commented on GitHub (Jul 21, 2025): > Also, I'm not sure what you mean by "scrollable list of `Primitive`s". You might want to elaborate. With examples, if possible. Sorry for the mention. I mean basically a scrollable horizontal layout (list) of widgets. ```go tview.NewScrollList(). Append(tview.NewInputField()). Append(tview.NewBox()). Append(tview.NewFlex()) ```
Author
Owner

@rivo commented on GitHub (Aug 27, 2025):

Flex is basically that. But it's not scrollable. The question with these suggestions is: How would you control the scrolling? Let's say one of the contained elements is a TextArea and the user is currently entering text. Which keys would you assign to scrolling the Flex?

<!-- gh-comment-id:3228701350 --> @rivo commented on GitHub (Aug 27, 2025): `Flex` is basically that. But it's not scrollable. The question with these suggestions is: How would you control the scrolling? Let's say one of the contained elements is a `TextArea` and the user is currently entering text. Which keys would you assign to scrolling the `Flex`?
Author
Owner

@ossenthusiast commented on GitHub (Aug 27, 2025):

Flex is basically that. But it's not scrollable. The question with these suggestions is: How would you control the scrolling? Let's say one of the contained elements is a TextArea and the user is currently entering text. Which keys would you assign to scrolling the Flex?

Thanks for the response! I am aware of Flex, but fundamentally, a flexbox is not meant to be scrollable. This is where ScrollView comes in. This is a very common requirement by a lot of TUI applications: you want to display a list of "messages" that the user can perform "actions" on (delete, edit, etc.). Regarding navigation, I would recommend following traditional vim-like keybindings for autocomplete menu:

Ctrl+P = Focus Previous
Ctrl+N = Focus Next

<!-- gh-comment-id:3229970158 --> @ossenthusiast commented on GitHub (Aug 27, 2025): > `Flex` is basically that. But it's not scrollable. The question with these suggestions is: How would you control the scrolling? Let's say one of the contained elements is a `TextArea` and the user is currently entering text. Which keys would you assign to scrolling the `Flex`? Thanks for the response! I am aware of `Flex`, but fundamentally, a flexbox is not meant to be scrollable. This is where `ScrollView` comes in. This is a very common requirement by a lot of TUI applications: you want to display a list of "messages" that the user can perform "actions" on (delete, edit, etc.). Regarding navigation, I would recommend following traditional vim-like keybindings for autocomplete menu: Ctrl+P = Focus **P**revious Ctrl+N = Focus **N**ext
Author
Owner

@ossenthusiast commented on GitHub (Aug 28, 2025):

Alternatively, you could just expose two methods FocusPrevious and FocusNext, which the user can use to set an input capture callback themselves for navigation instead of handling defaults. Furthermore, if you don't like the above approach, you could also accept extra arguments in the constructor of ScrollView (NewScrollView) primitive: focusNextKey and focusPreviousKey, both are of type tcell.EventKey:

tview.NewScrollNew(tcell.NewEventKey(KeyUp, ...), tcell.NewEventKey(KeyDown, ...))
<!-- gh-comment-id:3230879986 --> @ossenthusiast commented on GitHub (Aug 28, 2025): Alternatively, you could just expose two methods `FocusPrevious` and `FocusNext`, which the user can use to set an input capture callback themselves for navigation instead of handling defaults. Furthermore, if you don't like the above approach, you could also accept extra arguments in the constructor of `ScrollView` (`NewScrollView`) primitive: focusNextKey and focusPreviousKey, both are of type `tcell.EventKey`: ```go tview.NewScrollNew(tcell.NewEventKey(KeyUp, ...), tcell.NewEventKey(KeyDown, ...)) ```
Author
Owner

@rivo commented on GitHub (Aug 28, 2025):

Actually, the main problem is not so much the navigation itself — I'd argue that you can do this today by removing/adding items from a Flex as needed, resulting in a scroll-by-flex-row effect — but that, as mentioned many times in other issues, primitives don't propagate their optimal size upwards. In a chat application, there are some longer messages and some shorter ones. It is currently not possible to size a Flex's child primitive so it fits its contents optimally and there are no plans to introduce this. (This is quite a difficult problem to solve anyway.)

The question for such apps is also, why would you need to add any primitive to such a layout? Flex or Grid are primarily aimed at placing interactive controls on the screen, like input fields, checkboxes, buttons, text areas, etc. It strikes me that a chat application is mostly about formatting a long list of text messages, rather than placing interactive controls on screen. Instead of placing dozens or hundreds of small TextView elements into a Flex, it may make more sense to put them all into one TextView, a primitive which is specifically made to display large amounts of text (and it's scrollable, too). It's also flexible in terms of text size: If there is more text, it will expand to more lines automatically.

I get that at this point, formatting messages in a TextView for a chat/messages app may be difficult. But I would think that this is a problem that can be solved a lot more easily than inventing a new container primitive. (I seem to recall an issue asking for something like this but I cannot find it right now.)

<!-- gh-comment-id:3233574648 --> @rivo commented on GitHub (Aug 28, 2025): Actually, the main problem is not so much the navigation itself — I'd argue that you can do this today by removing/adding items from a `Flex` as needed, resulting in a scroll-by-flex-row effect — but that, as mentioned many times in other issues, primitives don't propagate their optimal size upwards. In a chat application, there are some longer messages and some shorter ones. It is currently not possible to size a `Flex`'s child primitive so it fits its contents optimally and there are no plans to introduce this. (This is quite a difficult problem to solve anyway.) The question for such apps is also, why would you need to add *any* primitive to such a layout? `Flex` or `Grid` are primarily aimed at placing interactive controls on the screen, like input fields, checkboxes, buttons, text areas, etc. It strikes me that a chat application is mostly about formatting a long list of text messages, rather than placing interactive controls on screen. Instead of placing dozens or hundreds of small `TextView` elements into a `Flex`, it may make more sense to put them all into one `TextView`, a primitive which is specifically made to display large amounts of text (and it's scrollable, too). It's also flexible in terms of text size: If there is more text, it will expand to more lines automatically. I get that at this point, formatting messages in a `TextView` for a chat/messages app may be difficult. But I would think that this is a problem that can be solved a lot more easily than inventing a new container primitive. (I seem to recall an issue asking for something like this but I cannot find it right now.)
Author
Owner

@rivo commented on GitHub (Sep 4, 2025):

FYI, I added a very simple chat application which illustrates how you might deal with flexible paragraphs:

https://github.com/rivo/tview/tree/master/demos/textview/chat

<!-- gh-comment-id:3255624811 --> @rivo commented on GitHub (Sep 4, 2025): FYI, I added a very simple chat application which illustrates how you might deal with flexible paragraphs: https://github.com/rivo/tview/tree/master/demos/textview/chat
Author
Owner

@ossenthusiast commented on GitHub (Sep 4, 2025):

FYI, I added a very simple chat application which illustrates how you might deal with flexible paragraphs:

https://github.com/rivo/tview/tree/master/demos/textview/chat

Thanks! A common thing chat apps have is being able to select a "message" and perform certain actions on it (delete, edit, etc, navigate using keys, etc), how would you implement that while being performant? For example, I have used region IDs and highlights to mimic that behavior in my chat TUI for Matrix, but to update a single message, I would have to remove the message and then redraw everything to TextView, which is pretty slow. This has been a pretty major pain-point for me with tview. Looking forward to your thoughts on this!

<!-- gh-comment-id:3255643624 --> @ossenthusiast commented on GitHub (Sep 4, 2025): > FYI, I added a very simple chat application which illustrates how you might deal with flexible paragraphs: > > https://github.com/rivo/tview/tree/master/demos/textview/chat Thanks! A common thing chat apps have is being able to select a "message" and perform certain actions on it (delete, edit, etc, navigate using keys, etc), how would you implement that while being performant? For example, I have used region IDs and highlights to mimic that behavior in my chat TUI for Matrix, but to update a single message, I would have to remove the message and then redraw everything to TextView, which is pretty slow. This has been a pretty major pain-point for me with tview. Looking forward to your thoughts on this!
Author
Owner

@ossenthusiast commented on GitHub (Sep 4, 2025):

BTW, the chat demo uses the (*tview.TextView).GetRegions function, which does not exist.

<!-- gh-comment-id:3255673657 --> @ossenthusiast commented on GitHub (Sep 4, 2025): BTW, the chat demo uses the `(*tview.TextView).GetRegions` function, which does not exist.
Author
Owner

@rivo commented on GitHub (Sep 4, 2025):

BTW, the chat demo uses the (*tview.TextView).GetRegions function, which does not exist.

It does for me: github.com/rivo/tview@4cdaaa9bd6/textview.go (L823)

<!-- gh-comment-id:3255938626 --> @rivo commented on GitHub (Sep 4, 2025): > BTW, the chat demo uses the `(*tview.TextView).GetRegions` function, which does not exist. It does for me: https://github.com/rivo/tview/blob/4cdaaa9bd6f6dcf226f43644d00f6b5703d69a8b/textview.go#L823
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#802
No description provided.