rubyterm 0.2.7 → 0.2.8

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: 3ddbf4d21fc1c56952d43421b3fdb23b68a7bf987769c3e01ee1e736ed597872
4
- data.tar.gz: 5e385e0a98361a06069f5937655e037a438ae0064fdf845e4f384883a53e6117
3
+ metadata.gz: 1e558fb7c4f76d082d2b12a187b2896da650c41d78c68a7091e087b44da9f05a
4
+ data.tar.gz: 2218f739b992337ebef7a4aa23d4674beba0d98f8aec7b923308b8add330ef8c
5
5
  SHA512:
6
- metadata.gz: 1d13beb2c8169a361ef35f49334e50cba685b7d7f9fab84b62d3a32f56b2d0a534ec3696d495639d9c1aaedb38cff5c9cd3b9850d0fafd9fdff5d21ccbc93634
7
- data.tar.gz: 6337a2248a5c62f7d7a74bf4b69dbc0c5354b9dd9ec336b9300dfb7fdc0be158da68f275e01f6f14aff607bc509333e98fc858b3e74c6d155d04f605dee754ad
6
+ metadata.gz: bb871759cd9df280d3ea58f6567901e21e8bc6ece1c0338e9c5aa7c39e80dbfded97d2be82029f716b48b3de1b268afb732071497cd0fb7f51a4c8e6d7b59398
7
+ data.tar.gz: 1d2f2a46619c84ac22cfe8242cc635119a57550b57ea24d37f12dd612c9f7aebebeef157b5eda973950010898e6f55ea4b63678702f29a2feed4b791bcef9276
data/lib/rubyterm/app.rb CHANGED
@@ -286,30 +286,10 @@ class RubyTerm
286
286
  when :"ctrl_+" then adjust_fontsize(1.0)
287
287
  when :"ctrl_-" then adjust_fontsize(-1.0)
288
288
  when :shift_page_up
289
- @window.scrollback_page_up
290
- # Full redraw to show scrollback buffer
291
- redraw
289
+ scrollback_view_up
292
290
  return
293
291
  when :shift_page_down
294
- # Don't do anything if not in scrollback mode
295
- return if !@window.scrollback_mode
296
-
297
- # If we're exiting scrollback mode or still in it
298
- changed = @window.scrollback_page_down
299
-
300
- # Redraw everything from scratch
301
- redraw
302
-
303
- # Explicitly force cursor to be redrawn if exiting scrollback mode
304
- if changed
305
- # Clear cursor if it exists
306
- @term.clear_cursor
307
- # Force draw cursor again
308
- @term.draw_cursor
309
- # Ensure changes are flushed
310
- @buffer.draw_flush
311
- @window.flush
312
- end
292
+ scrollback_view_down
313
293
  return
314
294
  when :XK_Insert # Paste primary selection
315
295
  # FIXME: Giant hack
@@ -380,6 +360,82 @@ class RubyTerm
380
360
  redraw if @window.scrollback_reset
381
361
  end
382
362
 
363
+ # Lines the view moves per mouse wheel tick (Shift+PageUp/Down move a
364
+ # full page of 10).
365
+ WHEEL_SCROLL_LINES = 3
366
+
367
+ # Move the scrollback view up/down by +lines+ and repaint incrementally:
368
+ # blit the rows that stay on screen (the same CopyArea mechanism live
369
+ # scrolling uses) and repaint only the newly exposed ones. The previous
370
+ # full clear+redraw per step made wheel scrolling visibly jerky. Shared
371
+ # by Shift+PageUp/Down and the wheel.
372
+ #
373
+ # The blit carries the selection-highlight pixels along with their text
374
+ # (the selection is pinned to buffer content), so only the freshly
375
+ # repainted rows need the overlay re-stamped - reapply_selection does
376
+ # that at the end.
377
+ def scrollback_view_up(lines = 10)
378
+ # Nothing above the view: don't touch the cursor or repaint at all
379
+ # (wheel ticks arrive far more often than Shift+PageUp ever did).
380
+ return if @window.scrollback_count >= @window.scrollback_buffer_size
381
+ if !@window.scrollback_mode
382
+ # Leaving the live screen: repaint the cursor cell first - cursor
383
+ # redraws are suppressed once scrolled back, and the blit below would
384
+ # otherwise drag the cursor overlay along as if it were content.
385
+ @term.clear_cursor
386
+ @buffer.draw_flush
387
+ end
388
+ moved = @window.scrollback_scroll(lines)
389
+ rows = @term.height
390
+ # A jump of a screenful or more retains nothing worth blitting.
391
+ return redraw if moved >= rows
392
+ # Shift the surviving rows down by +moved+ (this also clears the
393
+ # vacated top band and re-draws the scrollback indicator over row 0).
394
+ @window.scroll_down(0, @window.width, (rows - moved) * char_h, moved * char_h)
395
+ # Repaint the newly exposed history rows. Row 0 stays the indicator
396
+ # line, so start at 1; include row +moved+, where the old top row's
397
+ # indicator-overlay pixels landed.
398
+ (1..moved).each { |sy| repaint_view_row(sy) }
399
+ @buffer.draw_flush
400
+ @window.flush
401
+ reapply_selection
402
+ end
403
+
404
+ def scrollback_view_down(lines = 10)
405
+ # Don't do anything if not in scrollback mode
406
+ return if !@window.scrollback_mode
407
+ moved = @window.scrollback_scroll(-lines)
408
+ return if moved == 0
409
+ exited = !@window.scrollback_mode # landed back on the live screen
410
+ rows = @term.height
411
+ if moved >= rows
412
+ redraw
413
+ else
414
+ # Shift the surviving rows up (clears the vacated bottom band; the
415
+ # indicator is re-drawn only while still scrolled back).
416
+ @window.scroll_up(moved * char_h, @window.width, (rows - moved) * char_h, moved * char_h)
417
+ # Repaint the rows that scrolled into view at the bottom.
418
+ (rows - moved...rows).each { |sy| repaint_view_row(sy) }
419
+ @buffer.draw_flush
420
+ @window.flush
421
+ reapply_selection
422
+ end
423
+ # Explicitly force cursor to be redrawn if exiting scrollback mode
424
+ if exited
425
+ @term.clear_cursor
426
+ @term.draw_cursor
427
+ @buffer.draw_flush
428
+ @window.flush
429
+ end
430
+ end
431
+
432
+ # Repaint one on-screen row from the buffer at the current view offset
433
+ # (blank cells paint as spaces, so this also erases stale pixels).
434
+ def repaint_view_row(sy)
435
+ sb = @window.scrollback_count
436
+ (0...@term.width).each { |x| @buffer.redraw_display(x, sy, sb) }
437
+ end
438
+
383
439
  def redraw_positions(positions) = positions.each { |pos| @buffer.redraw(*pos) }
384
440
 
385
441
  # The selection's [start, end] boundary pairs, expanded so a double-width
@@ -500,6 +556,19 @@ class RubyTerm
500
556
  shift = pkt.state.anybits?(0x01) # ShiftMask
501
557
  case shift ? nil : @term.mouse_mode
502
558
  when nil
559
+ # Wheel ticks (buttons 4/5) scroll the local scrollback view, the way
560
+ # every other terminal scrolls natively - this is what an app that
561
+ # does not grab the mouse (e.g. a shell, or claude's inline mode)
562
+ # relies on. Handled before the selection logic below so a wheel tick
563
+ # never starts or clears a selection. With Shift held this branch is
564
+ # forced even when an app owns the mouse, so Shift+wheel always
565
+ # scrolls history - the same override as Shift+select.
566
+ if button >= 4
567
+ return if release # each tick is a press+release pair; act on press
568
+ scrollback_view_up(WHEEL_SCROLL_LINES) if button == 4
569
+ scrollback_view_down(WHEEL_SCROLL_LINES) if button == 5
570
+ return
571
+ end
503
572
  # Selection coordinates are cell *boundaries* (round to the nearest
504
573
  # column edge, not the floor cell), so dragging across a single cell
505
574
  # selects exactly that cell and a click (no boundary crossed) selects
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class RubyTerm
4
- VERSION = "0.2.7"
4
+ VERSION = "0.2.8"
5
5
  end
data/lib/window.rb CHANGED
@@ -41,31 +41,19 @@ class Window
41
41
  @buffer.scrollback_size
42
42
  end
43
43
 
44
- # Increase scrollback counter
45
- def scrollback_page_up
46
- # Get current scrollback buffer size
47
- max_scrollback = scrollback_buffer_size
48
- return if max_scrollback == 0
49
-
50
- # Store previous count for calculating how many new lines to clear
51
- previous_count = @scrollback_count
52
-
53
- # Limit scrollback count to available lines
54
- @scrollback_count += 10
55
- if @scrollback_count > max_scrollback
56
- @scrollback_count = max_scrollback
57
- end
58
-
59
- # Calculate how many new lines we've scrolled and clear them
60
- new_lines = @scrollback_count - previous_count
61
- if new_lines > 0
62
- # Clear the area where new scrollback lines will appear
63
- clear(0, 0, @width, new_lines * char_h)
64
- end
65
-
66
- draw_scrollback_indicator if @scrollback_count <= 10
44
+ # Move the scrollback view by +lines+ (positive = further back into
45
+ # history, negative = towards the live screen), clamped to
46
+ # [0, history size]. This is a pure model update - repainting (a blit of
47
+ # the surviving rows plus a repaint of the exposed ones, or a full
48
+ # redraw) is the caller's job. Returns the number of lines actually
49
+ # moved, 0 when already at the corresponding end.
50
+ def scrollback_scroll(lines)
51
+ was = @scrollback_count
52
+ @scrollback_count = (was + lines).clamp(0, scrollback_buffer_size)
53
+ (@scrollback_count - was).abs
67
54
  end
68
-
55
+
56
+
69
57
  # Snap straight back to the live screen (bottom of scrollback). Returns
70
58
  # true if we were scrolled back, so the caller knows a redraw is needed.
71
59
  def scrollback_reset
@@ -84,32 +72,6 @@ class Window
84
72
  @scrollback_count += 1
85
73
  end
86
74
 
87
- # Decrease scrollback counter
88
- def scrollback_page_down
89
- return false if @scrollback_count <= 0
90
-
91
- # Remember previous count
92
- previous_count = @scrollback_count
93
-
94
- @scrollback_count -= 10
95
- if @scrollback_count <= 0
96
- # Exiting scrollback mode
97
- @scrollback_count = 0
98
- @dirty = true
99
-
100
- # Clear entire scrollback area that was showing
101
- clear(0, 0, @width, previous_count * char_h)
102
- return true
103
- else
104
- # Clear area that was occupied by lines no longer in scrollback
105
- lines_removed = previous_count - @scrollback_count
106
- if lines_removed > 0
107
- clear(0, 0, @width, lines_removed * char_h)
108
- end
109
- end
110
- false
111
- end
112
-
113
75
  def initialize(**opts)
114
76
  @scrollback_count = 0
115
77
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyterm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vidar Hokstad