Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e7f52ea

Browse files
authored
Merge pull request #24065 from Tortar/patch-2
Fixed the positioning of cursor in Textbox: no approximation
2 parents 84dec9c + e0f0e0e commit e7f52ea

File tree

3 files changed

+61
-12
lines changed

3 files changed

+61
-12
lines changed

lib/matplotlib/tests/test_text.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,32 @@ def test_set_position():
339339
assert a + shift_val == b
340340

341341

342+
def test_char_index_at():
343+
fig = plt.figure()
344+
text = fig.text(0.1, 0.9, "")
345+
346+
text.set_text("i")
347+
bbox = text.get_window_extent()
348+
size_i = bbox.x1 - bbox.x0
349+
350+
text.set_text("m")
351+
bbox = text.get_window_extent()
352+
size_m = bbox.x1 - bbox.x0
353+
354+
text.set_text("iiiimmmm")
355+
bbox = text.get_window_extent()
356+
origin = bbox.x0
357+
358+
assert text._char_index_at(origin - size_i) == 0 # left of first char
359+
assert text._char_index_at(origin) == 0
360+
assert text._char_index_at(origin + 0.499*size_i) == 0
361+
assert text._char_index_at(origin + 0.501*size_i) == 1
362+
assert text._char_index_at(origin + size_i*3) == 3
363+
assert text._char_index_at(origin + size_i*4 + size_m*3) == 7
364+
assert text._char_index_at(origin + size_i*4 + size_m*4) == 8
365+
assert text._char_index_at(origin + size_i*4 + size_m*10) == 8
366+
367+
342368
@pytest.mark.parametrize('text', ['', 'O'], ids=['empty', 'non-empty'])
343369
def test_non_default_dpi(text):
344370
fig, ax = plt.subplots()

lib/matplotlib/text.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class Text(Artist):
124124
"""Handle storing and drawing of text in window or data coordinates."""
125125

126126
zorder = 3
127+
_charsize_cache = dict()
127128

128129
def __repr__(self):
129130
return "Text(%s, %s, %s)" % (self._x, self._y, repr(self._text))
@@ -279,6 +280,38 @@ def _get_multialignment(self):
279280
else:
280281
return self._horizontalalignment
281282

283+
def _char_index_at(self, x):
284+
"""
285+
Calculate the index closest to the coordinate x in display space.
286+
287+
The position of text[index] is assumed to be the sum of the widths
288+
of all preceding characters text[:index].
289+
290+
This works only on single line texts.
291+
"""
292+
if not self._text:
293+
return 0
294+
295+
text = self._text
296+
297+
fontproperties = str(self._fontproperties)
298+
if fontproperties not in Text._charsize_cache:
299+
Text._charsize_cache[fontproperties] = dict()
300+
301+
charsize_cache = Text._charsize_cache[fontproperties]
302+
for char in set(text):
303+
if char not in charsize_cache:
304+
self.set_text(char)
305+
bb = self.get_window_extent()
306+
charsize_cache[char] = bb.x1 - bb.x0
307+
308+
self.set_text(text)
309+
bb = self.get_window_extent()
310+
311+
size_accum = np.cumsum([0] + [charsize_cache[x] for x in text])
312+
std_x = x - bb.x0
313+
return (np.abs(size_accum - std_x)).argmin()
314+
282315
def get_rotation(self):
283316
"""Return the text angle in degrees between 0 and 360."""
284317
if self.get_transform_rotates_text():

lib/matplotlib/widgets.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,17 +1315,6 @@ def stop_typing(self):
13151315
# call it once we've already done our cleanup.
13161316
self._observers.process('submit', self.text)
13171317

1318-
def position_cursor(self, x):
1319-
# now, we have to figure out where the cursor goes.
1320-
# approximate it based on assuming all characters the same length
1321-
if len(self.text) == 0:
1322-
self.cursor_index = 0
1323-
else:
1324-
bb = self.text_disp.get_window_extent()
1325-
ratio = np.clip((x - bb.x0) / bb.width, 0, 1)
1326-
self.cursor_index = int(len(self.text) * ratio)
1327-
self._rendercursor()
1328-
13291318
def _click(self, event):
13301319
if self.ignore(event):
13311320
return
@@ -1338,7 +1327,8 @@ def _click(self, event):
13381327
event.canvas.grab_mouse(self.ax)
13391328
if not self.capturekeystrokes:
13401329
self.begin_typing(event.x)
1341-
self.position_cursor(event.x)
1330+
self.cursor_index = self.text_disp._char_index_at(event.x)
1331+
self._rendercursor()
13421332

13431333
def _resize(self, event):
13441334
self.stop_typing()

0 commit comments

Comments
 (0)