Skip to content

Commit

Permalink
Fix #385 - Use wcswidth for DropdownList sizing
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbrittain committed Apr 24, 2024
1 parent 336ad4f commit 82674a9
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 55 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGE HISTORY
LATEST
------
- Added border option to PopupMenu
- Fixed double-width alignment issues on DropdownList

1.15.0
------
Expand Down
12 changes: 9 additions & 3 deletions asciimatics/widgets/dropdownlist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""This module defines a dropdown list widget"""
from wcwidth import wcswidth
from asciimatics.event import KeyboardEvent, MouseEvent
from asciimatics.screen import Screen
from asciimatics.widgets.divider import Divider
Expand Down Expand Up @@ -31,7 +32,7 @@ def __init__(self, parent):
reverse = False

if parent.fit:
width = min(max(map(lambda x: len(x[0]), parent.options)) + 4, parent.width)
width = min(max(map(lambda x: wcswidth(x[0]), parent.options)) + 4, parent.width)
else:
width = parent.width
# Construct the Frame
Expand Down Expand Up @@ -127,11 +128,16 @@ def update(self, frame_no):
text = "" if self._line is None else self._options[self._line][0]
(colour, attr, background) = self._pick_colours("field", selected=self._has_focus)
if self._fit:
width = min(max(map(lambda x: len(x[0]), self._options)) + 1, self.width - 3)
width = min(max(map(lambda x: wcswidth(x[0]), self._options)) + 1, self.width - 3)
else:
width = self.width - 3

# For unicode output, we need to adjust for any double width characters.
output = _enforce_width(text, width, self._frame.canvas.unicode_aware)
output_tweak = wcswidth(output) - len(output)

self._frame.canvas.print_at(
f"[ {_enforce_width(text, width, self._frame.canvas.unicode_aware):{width}}]",
f"[ {output:{width - output_tweak}}]",
self._x + self._offset,
self._y,
colour, attr, background)
Expand Down
104 changes: 52 additions & 52 deletions tests/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2553,13 +2553,13 @@ def _click():
self._clicked = True

# Now set up the Frame ready for testing
screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
screen = MagicMock(spec=Screen, colours=8, unicode_aware=True)
scene = Scene([], duration=-1)
canvas = Canvas(screen, 10, 40, 0, 0)
form = Frame(canvas, canvas.height, canvas.width)
layout = Layout([100], fill_frame=True)
form.add_layout(layout)
layout.add_widget(DropdownList([("Item 1", 1), ("Item 2", 3), ("Item 3", 5)], on_change=_click))
layout.add_widget(DropdownList([("这是第一个选项", 1), ("Item 2", 3), ("Item 3", 5)], on_change=_click))
form.fix()
scene.add_effect(form)
scene.reset()
Expand All @@ -2570,50 +2570,50 @@ def _click():
effect.update(0)
self.assert_canvas_equals(
canvas,
"+--------------------------------------+\n" +
"|[ Item 1 ]|\n" +
"| O\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"+--------------------------------------+\n")
"┌──────────────────────────────────────┐\n" +
"│[ 这这是是第第一一个个选选项项 ]│\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"└──────────────────────────────────────┘\n")

# Check it opens as expected
self.process_mouse(scene, [(7, 1, MouseEvent.LEFT_CLICK)])
for effect in scene.effects:
effect.update(1)
self.assert_canvas_equals(
canvas,
"++------------------------------------++\n" +
"|| Item 1 ||\n" +
"||------------------------------------|O\n" +
"|| Item 1 ||\n" +
"|| Item 2 ||\n" +
"|| Item 3 ||\n" +
"|+------------------------------------+|\n" +
"| |\n" +
"| |\n" +
"+--------------------------------------+\n")
"┌┌────────────────────────────────────┐┐\n" +
"││ 这这是是第第一一个个选选项项 ││\n" +
"││────────────────────────────────────│█\n" +
"││ 这这是是第第一一个个选选项项 │░\n" +
"││ Item 2 │░\n" +
"││ Item 3 │░\n" +
"│└────────────────────────────────────┘░\n" +
" \n" +
" \n" +
"└──────────────────────────────────────┘\n")

# Check ESC works as expected
self.process_keys(scene, [Screen.KEY_DOWN, Screen.KEY_ESCAPE])
for effect in scene.effects:
effect.update(2)
self.assert_canvas_equals(
canvas,
"+--------------------------------------+\n" +
"|[ Item 1 ]|\n" +
"| O\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"+--------------------------------------+\n")
"┌──────────────────────────────────────┐\n" +
"│[ 这这是是第第一一个个选选项项 ]│\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"└──────────────────────────────────────┘\n")
self.assertFalse(self._clicked)

# Check key selection works as expected
Expand All @@ -2622,33 +2622,33 @@ def _click():
effect.update(3)
self.assert_canvas_equals(
canvas,
"++------------------------------------++\n" +
"|| Item 2 ||\n" +
"||------------------------------------|O\n" +
"|| Item 1 ||\n" +
"|| Item 2 ||\n" +
"|| Item 3 ||\n" +
"|+------------------------------------+|\n" +
"| |\n" +
"| |\n" +
"+--------------------------------------+\n")
"┌┌────────────────────────────────────┐┐\n" +
"││ Item 2 ││\n" +
"││────────────────────────────────────│█\n" +
"││ 这这是是第第一一个个选选项项 │░\n" +
"││ Item 2 │░\n" +
"││ Item 3 │░\n" +
"│└────────────────────────────────────┘░\n" +
" \n" +
" \n" +
"└──────────────────────────────────────┘\n")

# Check Enter works as expected
self.process_keys(scene, [Screen.ctrl("m")])
for effect in scene.effects:
effect.update(4)
self.assert_canvas_equals(
canvas,
"+--------------------------------------+\n" +
"|[ Item 2 ]|\n" +
"| O\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"| |\n" +
"+--------------------------------------+\n")
"┌──────────────────────────────────────┐\n" +
"[ Item 2 ]\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"└──────────────────────────────────────┘\n")
self.assertTrue(self._clicked)

def test_dropdown_list_options(self):
Expand Down

0 comments on commit 82674a9

Please sign in to comment.