godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
86.53k stars 19.27k forks source link

`TextEdit.get_caret_draw_pos()` is unreliable altogether #88722

Open MewPurPur opened 4 months ago

MewPurPur commented 4 months ago

Tested versions

4.2.1

System information

Godot v4.2.1.stable - Pop!_OS 22.04 LTS - X11 - Vulkan (Forward+) - integrated Intel(R) Graphics (ADL GT2) () - 12th Gen Intel(R) Core(TM) i5-1235U (12 Threads)

Issue description

My expectation is that TextEdit.get_caret_draw_pos() is a function I can use to consistently predict where a caret should be drawn, and to draw my own things there. But it's entirely unreliable and fails when the draw position is clipped, when the window is resized, and for empty lines, and probably other things I've yet to discover.

https://github.com/godotengine/godot/assets/85438892/17ef3714-5680-4334-8bc7-50c8ada3119d

Related: #65981

Steps to reproduce

There's a MRP. But basically, add a TextEdit, then add a script to make it draw a circle at the caret pos, then try all the stuff I showed in the video above.

Minimal reproduction project (MRP)

TextEdit bug.zip

MewPurPur commented 4 months ago

I believe that to solve this, we need to make it so the caret drawing logic and the logic for this method are 100% unified. In fact, once we do this, we can get make it so caret blinking doesn't redraw the whole TextEdit, which would be a huge win for battery life.

kitbdev commented 4 months ago

It seems that TextEdit.get_caret_draw_pos() is only updated when it is drawn. Try using get_rect_at_line_column() or get_pos_at_line_column() with the caret position.

Mickeon commented 4 months ago

If that's intentional, it needs to be documented. If that's not intentional, welp.

kitbdev commented 4 months ago

Looks like it is intentional that it stays at the last visible position. It is used for code hint, IME, and some context menus, which need a valid position even if the caret gets clipped. We could probably change the behavior for empty lines and resizing, but the last visible position part should be documented.

MewPurPur commented 4 months ago

What's IME or code hint? I don't follow. Anyway, I find this behavior very weird and unlikely to make sense for anything.

If you disable smooth scrolling, you can scroll 3 lines at a time by default, making the drawing position land several rows under. In fact, if you use the scrollbar to move to a completely different spot instantaneously, this doesn't update either, so it can be some random spot on the screen.

If you have a visible caret, then scroll to the left, clipping it, then scroll above it, then scroll to the right, the draw position reported will still be at the left edge, even though the caret is now below.

Whatever this function is supposed to achieve, it's very weird and messed up. Wouldn't it make more sense to fix this function like I suggested and clamp the draw position instead - perhaps even doing so in a dedicated method?

Zireael07 commented 4 months ago

IME is Input Method Editor, something that's REQUIRED for people to input Chinese, Korean, Japanese and some other more complex writing systems.

Code hint I'm not certain what they refer to

MewPurPur commented 4 months ago

It seems that TextEdit.get_caret_draw_pos() is only updated when it is drawn. Try using get_rect_at_line_column() or get_pos_at_line_column() with the caret position.

By the way I tried this and it's good for my use case. Needs a bit of adjustment and it messes up with ligatures, but that's not so hard to workaround.

MewPurPur commented 3 months ago

image

These methods have been much more reliable, but even they fail in some situations, see the caret in this screenshot. I have grievances especially with scrolling, which seems to update too late. I'll see if I can report this separately.

MewPurPur commented 3 weeks ago

when the window is resized

I realized that it has to do with the TextEdit resizing while staying focused - and the workaround also has the issue