8000 Fixed the positioning of cursor in Textbox: no approximation by Tortar · Pull Request #24065 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Fixed the positioning of cursor in Textbox: no approximation #24065

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

Merged
merged 1 commit into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/matplotlib/tests/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,32 @@ def test_set_position():
assert a + shift_val == b


def test_char_index_at():
fig = plt.figure()
text = fig.text(0.1, 0.9, "")

text.set_text("i")
bbox = text.get_window_extent()
size_i = bbox.x1 - bbox.x0

text.set_text("m")
bbox = text.get_window_extent()
size_m = bbox.x1 - bbox.x0

text.set_text("iiiimmmm")
bbox = text.get_window_extent()
origin = bbox.x0

assert text._char_index_at(origin - size_i) == 0 # left of first char
assert text._char_index_at(origin) == 0
assert text._char_index_at(origin + 0.499*size_i) == 0
assert text._char_index_at(origin + 0.501*size_i) == 1
assert text._char_index_at(origin + size_i*3) == 3
assert text._char_index_at(origin + size_i*4 + size_m*3) == 7
assert text._char_index_at(origin + size_i*4 + size_m*4) == 8
assert text._char_index_at(origin + size_i*4 + size_m*10) == 8


@pytest.mark.parametrize('text', ['', 'O'], ids=['empty', 'non-empty'])
def test_non_default_dpi(text):
fig, ax = plt.subplots()
Expand Down
33 changes: 33 additions & 0 deletions lib/matplotlib/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Text(Artist):
"""Handle storing and drawing of text in window or data coordinates."""

zorder = 3
_charsize_cache = dict()

def __repr__(self):
return "Text(%s, %s, %s)" % (self._x, self._y, repr(self._text))
Expand Down Expand Up @@ -279,6 +280,38 @@ def _get_multialignment(self):
else:
return self._horizontalalignment

def _char_index_at(self, x):
"""
Calculate the index closest to the coordinate x in display space.

The position of text[index] is assumed to be the sum of the widths
of all preceding characters text[:index].

This works only on single line texts.
"""
if not self._text:
return 0

text = self._text

fontproperties = str(self._fontproperties)
if fontproperties not in Text._charsize_cache:
Text._charsize_cache[fontproperties] = dict()

charsize_cache = Text._charsize_cache[fontproperties]
for char in set(text):
if char not in charsize_cache:
self.set_text(char)
bb = self.get_window_extent()
charsize_cache[char] = bb.x1 - bb.x0

self.set_text(text)
bb = self.get_window_extent()

size_accum = np.cumsum([0] + [charsize_cache[x] for x in text])
std_x = x - bb.x0
return (np.abs(size_accum - std_x)).argmin()

def get_rotation(self):
"""Return the text angle in degrees between 0 and 360."""
if self.get_transform_rotates_text():
Expand Down
14 changes: 2 additions & 12 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1315,17 +1315,6 @@ def stop_typing(self):
# call it once we've already done our cleanup.
self._observers.process('submit', self.text)

def position_cursor(self, x):
# now, we have to figure out where the cursor goes.
# approximate it based on assuming all characters the same length
if len(self.text) == 0:
self.cursor_index = 0
else:
bb = self.text_disp.get_window_extent()
ratio = np.clip((x - bb.x0) / bb.width, 0, 1)
self.cursor_index = int(len(self.text) * ratio)
self._rendercursor()

def _click(self, event):
if self.ignore(event):
return
Expand All @@ -1338,7 +1327,8 @@ def _click(self, event):
event.canvas.grab_mouse(self.ax)
if not self.capturekeystrokes:
self.begin_typing(event.x)
self.position_cursor(event.x)
self.cursor_index = self.text_disp._char_in 4A42 dex_at(event.x)
self._rendercursor()

def _resize(self, event):
self.stop_typing()
Expand Down
0