I would like a pgentry to behave like a normal textbox. I.e: when it’s height is one row and you fill it with text and continue writing, the text to the left starts disappearing and everything scrolls to the right. In contrast, I tried a PGEntry and when you fill it with text you can’t write anymore, which is very unpractical (for a chatbox for example). So I’m thinking there must be a way to fix this but I can’t find it.
We have never implemented the scrolling chatbox at the C++ level. (Some developers have added this feature at the Python level, however.)
Part of the reluctance to implement this is a fundamental disagreement with the philosophy of a scrolling text entry box: its design seems to violate several principles of human-computer interaction, and it never ceases to confuse naive users who can’t tell the difference between text that has scrolled off to the left, and text that is no longer in the form. By now, of course, most computer users have been trained to understand how to use a scrolling text box, so perhaps this objection no longer applies.
If you’d like to extend PGEntry to add support for scrolling text, we’d be happy to incorporate those changes.
I’m adding support for this. I’ve added set_overflow_mode() and get_overflow_mode() to PGEntry, it defaults to false so that it doesnt break existing code.
// Function: PGEntry::set_overflow_mode // Access: Published // Description: Specifies whether overflow mode should be enabled. // In overflow mode, text can overflow the boundaries // of the Entry element horizontally. // // Overflow mode only works when the number of lines // is 1.
I just wanted to add horizontal overflow since vertical overflow involves scrollbars and it’s a more drastic thing and I don’t even know if this is already supported somehow.
Anyway, this disables wrapping in the pgentry and the text assembler. So I now have unbounded text that overflows the PGEntry box, all fine so far.
Now the only thing that’s remaining is doing set_scissor on the Node with the overflowing assembled text, and repositioning it. And here’s my problem. I obtain the coordinates at which to clip the assembled text as a text size unit, and I have no idea what this unit is (it roughly equals 2 average characters) and how it correlates to the coordinates that set_scissor expects. So, how do you convert from text width to the set_scissor coords?
EDIT: Just to clarify, I obtain the cursor position with _text.get_xpos(0, _cursor_position) and the size with _max_width, at PGEntry::update_text
Those units are simply relative to the PGEntry node itself. To convert it to global coordinates, which are what is required by set_scissor(), you have to multiply it by the node’s net transform: entry.get_net_transform(). This will include whatever (presumably tiny) scale you have applied to your PGEntry.
Ideally, you should compute the four corners of the entry, transform these four corners, and rederive the four corners from the new transformed values, because the PGEntry might be rotated. (Though this is probably unlikely in practice, since who wants to type upside-down?)
Doesn’t set_scissor allow you to specify coordinates relative to a different node? So, can’t you just use set_scissor(yourEntry, 0, 1, 0, 1); ?
Oh, yeah! I was thinking that was an interface I’d put on the TODO list, instead of actually adding it; but look, there it is! So, yeah, that would be the much more sensible way to do it.
I didn’t use that method because there’s no such overload, the one that let’s you use a second nodepath uses a 3d space.
But anyway, I’m able to clip the pgentry text now, I use PGEntry::get_transform()->get_pos() for positioning and PGEntry::get_transform()->get_scale() for scale. Then I add 1 and divide by 2, to convert from -1/1 to 0/1 space.
Obviously, this only works fine when the screen is a square, because the first coord space is aspect ratio dependant. Now, I could just lookup the dimensions but I figured there has to be a better way of converting spaces and I didn’t want to add a silly overhead. Is there?
Right, the better way of converting spaces is to use 3-D space, which correctly applies the transforms from each axis independently. So, I’d recommend you use the overload of NodePath that accepts another NodePath, and pass the four corners of the PGEntry, as 3-D coordinates, e.g. LPoint3f(0, 0, 0), LPoint3f(max_width, 0, 0), LPoint3f(max_width, 0, 1), LPoint3f(0, 0, 1).
Ok, done. I’ve done a really simple implementation, where the caret is placed as much to the right as possible, also overflow with rotated text isn’t supported, yet.
So if that’s ok with you, I’ll send a patch tomorrow to pro-rsoft after I clean it up a little. I plan to improve the caret placement and add text selection support in the future, and also support rotated text, but for now this is enough for my needs.
Also, I haven’t tested it with IME because I don’t have it installed, that requires testing.
For the record, usage is:
PT(PGEntry) input = new PGEntry("ChatBox"); input->setup(15, 1); //num_lines must be 1 input->set_overflow_mode(true);
Very nice, thanks!