Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Word wrap #5

Open
josephg opened this issue Dec 7, 2022 · 7 comments
Open

Feature request: Word wrap #5

josephg opened this issue Dec 7, 2022 · 7 comments
Labels
enhancement New feature or request

Comments

@josephg
Copy link

josephg commented Dec 7, 2022

This is super cool!

I want to make a little note editor using tui-textarea. But my notes will often use long lines - and ideally rendered with word wrap.

How can I do that? Would it be an easy change to make to tui-textarea? (I'm happy to get my hands dirty)

@rhysd rhysd added the enhancement New feature or request label Dec 12, 2022
@brooksvb
Copy link

I was also hoping this was a feature. I'll take a look to see if I can figure out how to implement it.

@brooksvb
Copy link

brooksvb commented Mar 25, 2023

Here is where a Paragraph widget is created during rendering:

tui-textarea/src/widget.rs

Lines 122 to 125 in d4bbccb

let text = self.text(top_row as usize, height as usize);
let mut inner = Paragraph::new(text)
.style(self.0.style())
.alignment(self.0.alignment());

Paragraph has a wrap() method to control line wrapping:
https://docs.rs/tui/latest/tui/widgets/struct.Wrap.html

So I think the render function could be changed to something like

        let mut inner = Paragraph::new(text)
            .style(self.0.style())
            .alignment(self.0.alignment())
            .wrap(Wrap { trim: false });

Of course a couple things will need to be done like adding a wrap option to the TextArea widget and related methods. I'm not sure if this would interfere with any of the logic for window scrolling and scroll position; perhaps there is an assumption that there is a 1-to-1 relation between a line in the text content and a rendered line in the terminal. I don't fully understand all the scroll position logic in that file.

@rhysd
Copy link
Owner

rhysd commented Nov 16, 2023

I'm slowly starting to think about this as a new feature after 0.4.0. This is the biggest missing feature for this crate to be a simple textarea. Since width of each line is repeatedly required on rendering underlying Paragraph widget, lines would need to cache their width. I would need to start with internal refactoring because currently lines are kept as simplest Vec<String> and there is no place for the cache.

@brooksvb
Copy link

brooksvb commented Nov 20, 2023

Based on my incomplete attempt to implement this before getting too busy to continue (#13), there's a couple considerations I'll mention.

In addition to caching line length, the number of rows (wrapped) that a single line occupies based on the area width might be good to cache.

In working out the logic for counting rows and positioning the cursor and viewport, it became quite annoying to try and distinguish between "rows" and "lines", whichever may be which, where one of them refers to a line of text in the text buffer, and one refers to rows in the terminal viewport. I think I have settled on referring to terminal "rows" and text "lines".

Another consideration is support for multiple-byte/char graphemes. Any special characters that are actually composed of multiple characters will change the way line length needs to be computed. I'm not sure if support for those is intended or not.

Another consideration that affects line length and line wrap calculations is whether or not line numbers are active.

I had a decent start to computing row wraps with https://github.com/rhysd/tui-textarea/pull/13/files#diff-4007187965c1fea4fd252a76f5f1007b2ea59f6d86e2f454edee547c5036be24R11

Another consideration that I hadn't yet figured out how to handle is that since text lines do not 1-1 map to terminal rows, and the viewport is positioned based on a text line, it is not possible to position the top edge of the terminal viewport in the middle of a wrapped row. I avoided this complexity with the requirement that scrolling up or down can only be done by an entire text line at a time; it works, but it's not typical expected behavior from a text field.

Perhaps it is necessary to create a buffer of "virtual" lines, that split the source text lines based on line wraps before the terminal renders them, allowing you to set the position in the middle of a wrapped text line.

Another sticky consideration is handling vertical cursor movement properly for wrapped lines. In my implementation, I stuck with a simplified movement that doesn't really consider line wraps. Moving the cursor up and down would not only require considering line wraps, but considering those graphemes that may be composed of multiple characters and affect the counting logic to place the cursor in the right spot.

If you get to this issue and need help, I will try to lend my efforts again.

@pythops
Copy link

pythops commented Jan 22, 2024

+1 for this one, would be great to have 🙏

@pythops
Copy link

pythops commented May 27, 2024

any updates on this one ?

@pythops
Copy link

pythops commented Aug 12, 2024

@rhysd is there a way to prioritize this one plz 🙏 many users are interested in it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants