8000 Updated the position_cursor function of Textbox · matplotlib/matplotlib@b0ce735 · GitHub
[go: up one dir, main page]

Skip to content

Commit b0ce735

Browse files
committed
Updated the position_cursor function of Textbox
create char_index_at inside text class Update widgets.py Update widgets.py Update text.py solved bug in caching Simpler logic Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> make char_index_at private as suggested Co-authored-by: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> refer to private method change __str__ to str mistake in applying a change fixed added tests style edit make _charsize_cache class attribute
1 parent bc73259 commit b0ce735

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

lib/matplotlib/tests/test_text.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,28 @@ 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) == 0
359+
assert text._char_index_at(origin + size_i*3) == 3
360+
assert text._char_index_at(origin + size_i*4 + size_m*3) == 7
361+
assert text._char_index_at(origin + size_i*4 + size_m*4) == 8
362+
363+
342364
@pytest.mark.parametrize('text', ['', 'O'], ids=['empty', 'non-empty'])
343365
def test_non_default_dpi(text):
344366
fig, ax = plt.subplots()

lib/matplotlib/text.py

Lines changed: 32 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,37 @@ def _get_multialignment(self):
279280
else:
280281
return self._horizontalalignment
281282

283+
def _char_index_at(self, x):
284+
"""
285+
Calculates the index nearer to x, we accumulate
286+
the width of each character contained in the text
287+
and find the index at which the distance between the
288+
click position and the accumulated sum is minimal.
289+
This works only on single line texts.
290+
"""
291+
if not self._text:
292+
return 0
293+
294+
text = self._text
295+
296+
fontproperties = str(self._fontproperties)
297+
if fontproperties not in Text._charsize_cache:
298+
Text._charsize_cache[fontproperties] = dict()
299+
300+
charsize_cache = Text._charsize_cache[fontproperties]
301+
for char in set(text):
302+
if char not in charsize_cache:
303+
self.set_text(char)
304+
bb = self.get_window_extent()
305+
charsize_cache[char] = bb.x1 - bb.x0
306+
307+
self.set_text(text)
308+
bb = self.get_window_extent()
309+
310+
size_accum = np.cumsum([0] + [charsize_cache[x] for x in text])
311+
std_x = x - bb.x0
312+
return (np.abs(size_accum - std_x)).argmin()
313+
282314
def get_rotation(self):
283315
"""Return the text angle in degrees between 0 and 360."""
284316
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)
0