Skip to content

Commit

Permalink
Merge pull request #324 from aycabta/fix-some-multi-dialog-bug
Browse files Browse the repository at this point in the history
Fix some multi dialog bug
  • Loading branch information
aycabta authored Aug 31, 2021
2 parents 0ae7d8b + de1ad93 commit ee465e6
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 50 deletions.
2 changes: 1 addition & 1 deletion lib/reline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ConfigEncodingConversionError < StandardError; end

Key = Struct.new('Key', :char, :combined_char, :with_meta)
CursorPos = Struct.new(:x, :y)
DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :height, keyword_init: true)
DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :width, :height, keyword_init: true)

class Core
ATTR_READER_NAMES = %i(
Expand Down
99 changes: 55 additions & 44 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -538,19 +538,31 @@ def call
end

class Dialog
attr_reader :name
attr_accessor :scroll_top, :column, :vertical_offset, :contents, :lines_backup
attr_reader :name, :contents, :width
attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup

def initialize(name, proc_scope)
@name = name
@proc_scope = proc_scope
@width = nil
@scroll_top = 0
end

def set_cursor_pos(col, row)
@proc_scope.set_cursor_pos(col, row)
end

def width=(v)
@width = v
end

def contents=(contents)
@contents = contents
if contents and @width.nil?
@width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max
end
end

def call
@proc_scope.set_dialog(self)
@proc_scope.call
Expand All @@ -577,9 +589,8 @@ def add_dialog_proc(name, p, context = nil)
end
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
dialog_render_info = dialog.call
old_dialog_contents = dialog.contents
old_dialog_column = dialog.column
old_dialog_vertical_offset = dialog.vertical_offset
dialog.width = dialog_render_info.width if dialog_render_info and dialog_render_info.width
old_dialog = dialog.clone
if dialog_render_info and dialog_render_info.contents and not dialog_render_info.contents.empty?
height = dialog_render_info.height || DIALOG_HEIGHT
pointer = dialog_render_info.pointer
Expand Down Expand Up @@ -612,7 +623,7 @@ def add_dialog_proc(name, p, context = nil)
upper_space = @first_line_started_from - @started_from
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
dialog.column = dialog_render_info.pos.x
diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
diff = (dialog.column + dialog.width) - (@screen_size.last - 1)
if diff > 0
dialog.column -= diff
end
Expand All @@ -628,7 +639,7 @@ def add_dialog_proc(name, p, context = nil)
dialog.vertical_offset = dialog_render_info.pos.y + 1
end
Reline::IOGate.hide_cursor
reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
reset_dialog(dialog, old_dialog)
move_cursor_down(dialog.vertical_offset)
Reline::IOGate.move_cursor_column(dialog.column)
dialog.contents.each_with_index do |item, i|
Expand All @@ -641,7 +652,7 @@ def add_dialog_proc(name, p, context = nil)
bg_color = '46'
end
end
@output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
@output.write "\e[#{bg_color}m%-#{dialog.width}s\e[49m" % Reline::Unicode.take_range(item, 0, dialog.width)
Reline::IOGate.move_cursor_column(dialog.column)
move_cursor_down(1) if i < (dialog.contents.size - 1)
end
Expand All @@ -657,8 +668,8 @@ def add_dialog_proc(name, p, context = nil)
}
end

private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset)
return if dialog.lines_backup.nil? or old_dialog_contents.nil?
private def reset_dialog(dialog, old_dialog)
return if dialog.lines_backup.nil? or old_dialog.contents.nil?
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
visual_lines = []
visual_start = nil
Expand All @@ -674,76 +685,76 @@ def add_dialog_proc(name, p, context = nil)
old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
y = @first_line_started_from + @started_from
y_diff = y - old_y
if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
if (old_y + old_dialog.vertical_offset) < (y + dialog.vertical_offset)
# rerender top
move_cursor_down(old_dialog_vertical_offset - y_diff)
start = visual_start + old_dialog_vertical_offset
line_num = dialog.vertical_offset - old_dialog_vertical_offset
move_cursor_down(old_dialog.vertical_offset - y_diff)
start = visual_start + old_dialog.vertical_offset
line_num = dialog.vertical_offset - old_dialog.vertical_offset
line_num.times do |i|
Reline::IOGate.move_cursor_column(old_dialog_column)
Reline::IOGate.move_cursor_column(old_dialog.column)
if visual_lines[start + i].nil?
s = ' ' * DIALOG_WIDTH
s = ' ' * dialog.width
else
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
end
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
@output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s
move_cursor_down(1) if i < (line_num - 1)
end
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
end
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
if (old_y + old_dialog.vertical_offset + old_dialog.contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
# rerender bottom
move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
start = visual_start + dialog.vertical_offset + dialog.contents.size
line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
line_num = (old_dialog.vertical_offset + old_dialog.contents.size) - (dialog.vertical_offset + dialog.contents.size)
line_num.times do |i|
Reline::IOGate.move_cursor_column(old_dialog_column)
Reline::IOGate.move_cursor_column(old_dialog.column)
if visual_lines[start + i].nil?
s = ' ' * DIALOG_WIDTH
s = ' ' * dialog.width
else
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width)
end
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % s
@output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s
move_cursor_down(1) if i < (line_num - 1)
end
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
end
if old_dialog_column < dialog.column
if old_dialog.column < dialog.column
# rerender left
move_cursor_down(old_dialog_vertical_offset - y_diff)
width = dialog.column - old_dialog_column
start = visual_start + old_dialog_vertical_offset
line_num = old_dialog_contents.size
move_cursor_down(old_dialog.vertical_offset - y_diff)
width = dialog.column - old_dialog.column
start = visual_start + old_dialog.vertical_offset
line_num = old_dialog.contents.size
line_num.times do |i|
Reline::IOGate.move_cursor_column(old_dialog_column)
Reline::IOGate.move_cursor_column(old_dialog.column)
if visual_lines[start + i].nil?
s = ' ' * width
else
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width)
end
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
move_cursor_down(1) if i < (line_num - 1)
end
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff)
end
if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width)
# rerender right
move_cursor_down(old_dialog_vertical_offset + y_diff)
width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
start = visual_start + old_dialog_vertical_offset
line_num = old_dialog_contents.size
move_cursor_down(old_dialog.vertical_offset + y_diff)
width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width)
start = visual_start + old_dialog.vertical_offset
line_num = old_dialog.contents.size
line_num.times do |i|
Reline::IOGate.move_cursor_column(old_dialog_column + DIALOG_WIDTH)
Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width)
if visual_lines[start + i].nil?
s = ' ' * width
else
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width)
end
Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
Reline::IOGate.move_cursor_column(dialog.column + dialog.width)
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
move_cursor_down(1) if i < (line_num - 1)
end
move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff)
end
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
end
Expand Down Expand Up @@ -777,10 +788,10 @@ def add_dialog_proc(name, p, context = nil)
dialog_vertical_size.times do |i|
if i < visual_lines_under_dialog.size
Reline::IOGate.move_cursor_column(0)
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
@output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % visual_lines_under_dialog[i]
else
Reline::IOGate.move_cursor_column(dialog.column)
@output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
@output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m"
end
Reline::IOGate.erase_after_cursor
move_cursor_down(1) if i < (dialog_vertical_size - 1)
Expand Down
11 changes: 6 additions & 5 deletions lib/reline/unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ def self.split_by_width(str, max_width, encoding = str.encoding)
end

# Take a chunk of a String with escape sequences.
def self.take_range(str, col, length, encoding = str.encoding)
def self.take_range(str, start_col, max_width, encoding = str.encoding)
chunk = String.new(encoding: encoding)
width = 0
total_width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
rest.scan(WIDTH_SCANNER) do |gc|
Expand All @@ -206,9 +206,10 @@ def self.take_range(str, col, length, encoding = str.encoding)
if in_zero_width
chunk << gc
else
width = get_mbchar_width(gc)
break if (width + length) <= col
chunk << gc if col <= width
mbchar_width = get_mbchar_width(gc)
total_width += mbchar_width
break if (start_col + max_width) < total_width
chunk << gc if start_col < total_width
end
end
end
Expand Down

0 comments on commit ee465e6

Please sign in to comment.