@@ -71,6 +71,7 @@ class Char(namedtuple("Char", [
71
71
"strikethrough" ,
72
72
"reverse" ,
73
73
"blink" ,
74
+ "width" ,
74
75
])):
75
76
"""A single styled on-screen character.
76
77
@@ -89,15 +90,16 @@ class Char(namedtuple("Char", [
89
90
during rendering. Defaults to ``False``.
90
91
:param bool blink: flag for rendering the character blinked. Defaults to
91
92
``False``.
93
+ :param bool width: the width in terms of cells to display this char.
92
94
"""
93
95
__slots__ = ()
94
96
95
- def __new__ (cls , data , fg = "default" , bg = "default" , bold = False ,
97
+ def __new__ (cls , data = " " , fg = "default" , bg = "default" , bold = False ,
96
98
italics = False , underscore = False ,
97
- strikethrough = False , reverse = False , blink = False ):
99
+ strikethrough = False , reverse = False , blink = False , width = wcwidth ( " " ) ):
98
100
return super (Char , cls ).__new__ (cls , data , fg , bg , bold , italics ,
99
101
underscore , strikethrough , reverse ,
100
- blink )
102
+ blink , width )
101
103
102
104
103
105
class Cursor :
@@ -111,7 +113,7 @@ class Cursor:
111
113
"""
112
114
__slots__ = ("x" , "y" , "attrs" , "hidden" )
113
115
114
- def __init__ (self , x , y , attrs = Char (" " )):
116
+ def __init__ (self , x , y , attrs = Char (" " , width = wcwidth ( " " ) )):
115
117
self .x = x
116
118
self .y = y
117
119
self .attrs = attrs
@@ -211,7 +213,7 @@ class Screen:
211
213
def default_char (self ):
212
214
"""An empty character with default foreground and background colors."""
213
215
reverse = mo .DECSCNM in self .mode
214
- return Char (data = " " , fg = "default" , bg = "default" , reverse = reverse )
216
+ return Char (data = " " , fg = "default" , bg = "default" , reverse = reverse , width = wcwidth ( " " ) )
215
217
216
218
def __init__ (self , columns , lines ):
217
219
self .savepoints = []
@@ -256,7 +258,7 @@ def display(self):
256
258
is_wide_char = False
257
259
continue
258
260
char = cell .data
259
- is_wide_char = wcwidth ( char [ 0 ]) == 2
261
+ is_wide_char = cell . width == 2
260
262
display_line .append (char )
261
263
262
264
gap = columns - (prev_x + 1 )
@@ -527,16 +529,18 @@ def draw(self, data):
527
529
528
530
line = self .buffer [self .cursor .y ]
529
531
if char_width == 1 :
530
- line [self .cursor .x ] = self .cursor .attrs ._replace (data = char )
532
+ line [self .cursor .x ] = self .cursor .attrs ._replace (data = char , width = char_width )
531
533
elif char_width == 2 :
532
534
# A two-cell character has a stub slot after it.
533
- line [self .cursor .x ] = self .cursor .attrs ._replace (data = char )
535
+ line [self .cursor .x ] = self .cursor .attrs ._replace (data = char , width = char_width )
534
536
if self .cursor .x + 1 < self .columns :
535
537
line [self .cursor .x + 1 ] = self .cursor .attrs \
536
- ._replace (data = "" )
538
+ ._replace (data = "" , width = 0 )
537
539
elif char_width == 0 and unicodedata .combining (char ):
538
540
# A zero-cell character is combined with the previous
539
541
# character either on this or preceding line.
542
+ # Because char's width is zero, this will not change the width
543
+ # of the previous character.
540
544
if self .cursor .x :
541
545
last = line [self .cursor .x - 1 ]
542
546
normalized = unicodedata .normalize ("NFC" , last .data + char )
0 commit comments