ruby_rich 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50bb2ebd116fd55abbe618cbd351ad1b73dab3a040d5df2f43011aff77a608bf
4
- data.tar.gz: be41ae4ba9c0d4754c3eb9de3070d914e6dba23dd383f5fc81ee72c848f8e4a6
3
+ metadata.gz: fa1b6881cd40609ff69b7e2b4c766c5fa3551bf49cd5558ad79681d89abc4e1a
4
+ data.tar.gz: e596c29715037aca70508b3dc564b009bc20bfa8f56a6286fb173819f9b2750a
5
5
  SHA512:
6
- metadata.gz: c71f39b536218b86f9ce58e15ab476d7927bc086be78ec46f36629e9d3007972f5b4c727a924fad3729666362ae3fb600445ff0acd265e58bac9d266097b4edf
7
- data.tar.gz: 2424396618c67fdad4568d77917424377c70e0284572ceb8e2735a53ccf9d10d217f1273cbc337ffd9063b70c9cfe60b310deb34278b619232ef8302140d8dc4
6
+ metadata.gz: 425b8e8584af060a2e9eb1ffe7666e0f2ddcec84c4a6a8005ffaf0f44c976b3e9c4c121a839a7c2b617f26ccabac20db8ef93ec1b3bc163761bd538a689e8f93
7
+ data.tar.gz: f17b46fb04ef923a0803163b06be590f985187ac50a81e9d76a47329ce2503c353c98f7d90295a94a534ecefe201907d0e6aa2585764c8a76df99f2ce436bf62
@@ -42,7 +42,7 @@ module RubyRich
42
42
  self
43
43
  end
44
44
 
45
- def start(refresh_rate: 24, mouse: true, alt_screen: false)
45
+ def start(refresh_rate: 24, mouse: true, alt_screen: true)
46
46
  @state_mutex.synchronize { @state = :starting }
47
47
  Live.start(@layout, refresh_rate: refresh_rate, mouse: mouse, alt_screen: alt_screen, autowrap: false) do |live|
48
48
  @state_mutex.synchronize do
@@ -147,7 +147,7 @@ module RubyRich
147
147
  false
148
148
  end
149
149
 
150
- def start(refresh_rate: 24, mouse: true, alt_screen: false)
150
+ def start(refresh_rate: 24, mouse: true, alt_screen: true)
151
151
  Live.start(@layout, refresh_rate: refresh_rate, mouse: mouse, alt_screen: alt_screen, autowrap: false) do |live|
152
152
  @live = live
153
153
  live.listening = true
@@ -73,6 +73,27 @@ module RubyRich
73
73
  @menu_open
74
74
  end
75
75
 
76
+ def native_cursor_position
77
+ return nil unless focused?
78
+
79
+ input_width = [inner_width - 2, 1].max
80
+ editor_row, editor_col = @editor.cursor_visual_position(width: input_width)
81
+ raw_row = [@attachments.length, 3].min + editor_row
82
+ raw_col = 2 + editor_col
83
+
84
+ raw_height = [@height.to_i, 1].max
85
+ raw_lines = []
86
+ raw_lines.concat(render_attachments)
87
+ raw_lines.concat(render_input_lines)
88
+ raw_lines.concat(render_menu_lines) if menu_open?
89
+
90
+ clipped_rows = [raw_lines.length - raw_height, 0].max
91
+ visible_row = raw_row - clipped_rows
92
+ return nil if visible_row.negative? || visible_row >= raw_height
93
+
94
+ [visible_row, raw_col]
95
+ end
96
+
76
97
  def wants_tab?
77
98
  focused? && (menu_open? || @editor.value.include?("/"))
78
99
  end
@@ -229,7 +250,7 @@ module RubyRich
229
250
  elsif !@editor.empty?
230
251
  @editor.clear
231
252
  else
232
- blur
253
+ focus
233
254
  end
234
255
  end
235
256
 
@@ -29,8 +29,8 @@ module RubyRich
29
29
  end
30
30
  end
31
31
 
32
- root_layout.key(:mouse_down, priority) do |event_data, _live|
33
- entry = entry_at(event_data[:x], event_data[:y])
32
+ root_layout.key(:mouse_target, priority) do |event_data, _live|
33
+ entry = entry_for_layout(event_data[:target_layout]) || entry_at(event_data[:x], event_data[:y])
34
34
  if entry
35
35
  focus(entry[:name])
36
36
  false
@@ -73,5 +73,16 @@ module RubyRich
73
73
  def entry_at(x, y)
74
74
  @entries.reverse.find { |entry| entry[:layout].contains?(x, y) }
75
75
  end
76
+
77
+ def entry_for_layout(layout)
78
+ current = layout
79
+ while current
80
+ entry = @entries.reverse.find { |item| item[:layout] == current }
81
+ return entry if entry
82
+
83
+ current = current.parent
84
+ end
85
+ nil
86
+ end
76
87
  end
77
88
  end
@@ -334,6 +334,7 @@ module RubyRich
334
334
 
335
335
  capture = root.instance_variable_get(:@mouse_capture)
336
336
  target = capture && [:mouse_drag, :mouse_up].include?(event_data[:name]) ? capture : (hit_test(event_data[:x], event_data[:y]) || self)
337
+ dispatch_mouse_target_event(event_data, target) if event_data[:name] == :mouse_down
337
338
  handled = target.bubble_mouse_event(event_data)
338
339
 
339
340
  root.instance_variable_set(:@mouse_capture, target) if event_data[:name] == :mouse_down && handled
@@ -341,6 +342,16 @@ module RubyRich
341
342
  handled
342
343
  end
343
344
 
345
+ def dispatch_mouse_target_event(event_data, target)
346
+ target.bubble_mouse_event(
347
+ event_data.merge(
348
+ name: :mouse_target,
349
+ target_layout: target,
350
+ original_name: event_data[:name]
351
+ )
352
+ )
353
+ end
354
+
344
355
  protected
345
356
 
346
357
  def bubble_mouse_event(event_data)
@@ -193,18 +193,33 @@ module RubyRich
193
193
  end
194
194
 
195
195
  def render_lines(width:, placeholder: nil, focused: true)
196
+ _ = focused
196
197
  content = value
197
198
  return [placeholder.to_s] if content.empty? && placeholder
198
199
 
199
200
  rendered = []
200
- line_index, col = cursor_line_col
201
- lines.each_with_index do |line, index|
202
- marker_col = focused && index == line_index ? col : nil
203
- rendered.concat(wrap_line_with_cursor(line, width, marker_col))
201
+ lines.each do |line|
202
+ rendered.concat(wrap_line_without_cursor(line, width))
204
203
  end
205
204
  rendered.empty? ? [""] : rendered
206
205
  end
207
206
 
207
+ def cursor_visual_position(width:)
208
+ line_index, cursor_col = cursor_line_col
209
+ row = 0
210
+
211
+ lines.each_with_index do |line, index|
212
+ if index == line_index
213
+ cursor_row, display_col = cursor_position_in_wrapped_line(line, width, cursor_col)
214
+ return [row + cursor_row, display_col]
215
+ end
216
+
217
+ row += wrap_line_without_cursor(line, width).length
218
+ end
219
+
220
+ [row, 0]
221
+ end
222
+
208
223
  private
209
224
 
210
225
  def invalidate_content_cache
@@ -332,30 +347,46 @@ module RubyRich
332
347
  nil
333
348
  end
334
349
 
335
- def wrap_line_with_cursor(line, width, marker_col)
350
+ def wrap_line_without_cursor(line, width)
336
351
  width = [width, 1].max
337
352
  segments = []
338
353
  current = +""
339
354
  current_width = 0
340
- chars = line.chars
341
- chars.each_with_index do |char, index|
355
+
356
+ line.chars.each do |char|
342
357
  char_width = display_width(char)
343
- if current_width + char_width > width
358
+ if current_width + char_width > width && !current.empty?
344
359
  segments << current
345
360
  current = +""
346
361
  current_width = 0
347
362
  end
348
- current << (marker_col == index ? cursor_marker(char) : char)
363
+
364
+ current << char
349
365
  current_width += char_width
350
366
  end
351
367
 
352
- current << cursor_marker(" ") if marker_col == chars.length
353
368
  segments << current
354
369
  segments
355
370
  end
356
371
 
357
- def cursor_marker(char)
358
- "#{AnsiCode.inverse}#{char}#{AnsiCode.reset}"
372
+ def cursor_position_in_wrapped_line(line, width, cursor_col)
373
+ width = [width, 1].max
374
+ row = 0
375
+ display_col = 0
376
+
377
+ line.chars.each_with_index do |char, index|
378
+ char_width = display_width(char)
379
+ if display_col + char_width > width && display_col.positive?
380
+ row += 1
381
+ display_col = 0
382
+ end
383
+
384
+ return [row, display_col] if index == cursor_col
385
+
386
+ display_col += char_width
387
+ end
388
+
389
+ [row, display_col]
359
390
  end
360
391
 
361
392
  def display_width(char)
@@ -236,6 +236,25 @@ module RubyRich
236
236
  @last_terminal_size = [width, height]
237
237
  @layout.calculate_dimensions(width, height)
238
238
  @render.draw(@layout.render_to_buffer)
239
+ position_native_cursor
240
+ end
241
+
242
+ def position_native_cursor
243
+ composer_layout = @layout[:composer]
244
+ return unless composer_layout
245
+
246
+ frame = composer_layout.content
247
+ component = frame.instance_variable_get(:@component) if frame
248
+ return unless component&.respond_to?(:native_cursor_position)
249
+
250
+ cursor = component.native_cursor_position
251
+ return unless cursor
252
+
253
+ row, col = cursor
254
+ terminal_row = composer_layout.y_offset.to_i + 1 + row.to_i
255
+ terminal_col = composer_layout.x_offset.to_i + 1 + col.to_i
256
+ print "\e[#{terminal_row + 1};#{terminal_col + 1}H"
257
+ $stdout.flush
239
258
  end
240
259
 
241
260
  def wait_for_activity
@@ -42,7 +42,7 @@ module RubyRich
42
42
  class << self
43
43
  attr_accessor :debug_input
44
44
 
45
- def setup(mouse: false, hide_cursor: true, autowrap: true, alt_screen: false)
45
+ def setup(mouse: false, hide_cursor: false, autowrap: true, alt_screen: false)
46
46
  capture_state
47
47
  enable_virtual_terminal_on_windows
48
48
  system('stty -echo') unless windows?
@@ -1,3 +1,3 @@
1
1
  module RubyRich
2
- VERSION = "0.4.4"
2
+ VERSION = "0.4.5"
3
3
  end
@@ -14,6 +14,7 @@ module RubyRich
14
14
  @height = 0
15
15
  @scroll_top = 0
16
16
  @dragging_scrollbar = false
17
+ @dragging_viewport = false
17
18
  @drag_start_y = nil
18
19
  @drag_start_scroll_top = nil
19
20
  @selecting = false
@@ -62,7 +63,6 @@ module RubyRich
62
63
 
63
64
  def handle_event(event_data, layout = nil)
64
65
  return false if keyboard_event?(event_data) && !@focused
65
- return false if mouse_event?(event_data) && !@focused
66
66
 
67
67
  case event_data[:name]
68
68
  when :page_up
@@ -89,11 +89,11 @@ module RubyRich
89
89
  when :mouse_down
90
90
  return copy_selection if event_data[:button] == :right
91
91
 
92
- start_scrollbar_drag(event_data, layout) || start_selection(event_data, layout)
92
+ start_scrollbar_drag(event_data, layout) || start_viewport_drag(event_data, layout)
93
93
  when :mouse_drag
94
- drag_scrollbar(event_data, layout) || drag_selection(event_data, layout)
94
+ drag_scrollbar(event_data, layout) || drag_viewport(event_data, layout) || drag_selection(event_data, layout)
95
95
  when :mouse_up
96
- stop_scrollbar_drag || stop_selection
96
+ stop_scrollbar_drag || stop_viewport_drag || stop_selection
97
97
  else
98
98
  false
99
99
  end
@@ -352,6 +352,18 @@ module RubyRich
352
352
  true
353
353
  end
354
354
 
355
+ def start_viewport_drag(event_data, layout)
356
+ return false unless layout
357
+ return false unless event_data[:button] == :left
358
+ return false if event_data[:x] >= layout.x_offset + content_width
359
+ return false unless event_data[:y].between?(layout.y_offset, layout.y_offset + @height - 1)
360
+
361
+ @dragging_viewport = true
362
+ @drag_start_y = event_data[:y]
363
+ @drag_start_scroll_top = @scroll_top
364
+ true
365
+ end
366
+
355
367
  def drag_scrollbar(event_data, layout)
356
368
  return false unless @dragging_scrollbar && layout
357
369
 
@@ -367,6 +379,14 @@ module RubyRich
367
379
  true
368
380
  end
369
381
 
382
+ def drag_viewport(event_data, layout)
383
+ return false unless @dragging_viewport && layout
384
+
385
+ delta_y = event_data[:y] - @drag_start_y
386
+ scroll_to(@drag_start_scroll_top - delta_y)
387
+ true
388
+ end
389
+
370
390
  def drag_selection(event_data, layout)
371
391
  return false unless @selecting && layout
372
392
 
@@ -382,6 +402,14 @@ module RubyRich
382
402
  was_dragging
383
403
  end
384
404
 
405
+ def stop_viewport_drag
406
+ was_dragging = @dragging_viewport
407
+ @dragging_viewport = false
408
+ @drag_start_y = nil
409
+ @drag_start_scroll_top = nil
410
+ was_dragging
411
+ end
412
+
385
413
  def stop_selection
386
414
  return false unless @selecting
387
415
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_rich
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - zhuang biaowei