rcurses 3.1 → 3.3

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rcurses/pane.rb +78 -29
  3. data/lib/rcurses.rb +1 -1
  4. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07bf042dcaa49e65d87441e77e32cb04f6d8738abe081979bc8589966b2ec65f
4
- data.tar.gz: 234ad19f3e79216d0dd0561e2cfbb43318d7f308c596ab2a847eeda6fefe9563
3
+ metadata.gz: c115cb970dc679cf90286b3881c0543de420c76930fe25ae63adf224433f6c62
4
+ data.tar.gz: d837ebda76384152815d687fd614ad41b48e8a7af4c131f125e1b4ad293b02bf
5
5
  SHA512:
6
- metadata.gz: d8d764879351431d6057dd1fa573ca2bba3134c207c8c4f1ad4f511d1d12ca93c392e163401a9592c18b6af48f590d7509e510592f4191bf156c89c1c89d644a
7
- data.tar.gz: 8ffe0551b49238aa84ef6811d140c398a96736c92f77afd10afd0116431d6856d6472f619345b96827391a9b83d152965704f586119bcd56410b626100da71e1
6
+ metadata.gz: e9733a2dc05ac13ec49cd8bbd21681da9b0436f845d0a66256e30dc23c5038fb11f25b225174a0b9b008ebb1f0ea5f01a4a8e6571496e4119553ed8f895d2966
7
+ data.tar.gz: 0fde756777022b0606a041ddecaf3f1e44ef00b2e7f099f09ef25ca1c3fb3b18eaf8f067cccc2debf6c216efcee2c01d875cc2f865a59d36f19845deaec134c6
data/lib/rcurses/pane.rb CHANGED
@@ -1,4 +1,33 @@
1
1
  module Rcurses
2
+ # A simple display_width function that approximates how many columns a string occupies.
3
+ # This is a simplified version that may need adjustments for full Unicode support.
4
+ def self.display_width(str)
5
+ width = 0
6
+ str.each_char do |char|
7
+ cp = char.ord
8
+ if cp == 0
9
+ # NUL – no width
10
+ elsif cp < 32 || (cp >= 0x7F && cp < 0xA0)
11
+ # Control characters: no width
12
+ width += 0
13
+ # Approximate common wide ranges:
14
+ elsif (cp >= 0x1100 && cp <= 0x115F) ||
15
+ cp == 0x2329 || cp == 0x232A ||
16
+ (cp >= 0x2E80 && cp <= 0xA4CF) ||
17
+ (cp >= 0xAC00 && cp <= 0xD7A3) ||
18
+ (cp >= 0xF900 && cp <= 0xFAFF) ||
19
+ (cp >= 0xFE10 && cp <= 0xFE19) ||
20
+ (cp >= 0xFE30 && cp <= 0xFE6F) ||
21
+ (cp >= 0xFF00 && cp <= 0xFF60) ||
22
+ (cp >= 0xFFE0 && cp <= 0xFFE6)
23
+ width += 2
24
+ else
25
+ width += 1
26
+ end
27
+ end
28
+ width
29
+ end
30
+
2
31
  class Pane
3
32
  require 'clipboard' # Ensure the 'clipboard' gem is installed
4
33
  include Cursor
@@ -78,12 +107,10 @@ module Rcurses
78
107
  end
79
108
 
80
109
  # Diff-based refresh that minimizes flicker.
81
- # Building a frame (an array of lines) that includes borders (if enabled).
82
- # Content lines are wrapped in vertical border characters when @border is true.
110
+ # In this updated version we lazily process only the raw lines required to fill the pane.
83
111
  def refresh(cont = @text)
84
112
  @max_h, @max_w = IO.console.winsize
85
113
 
86
- # Adjust pane dimensions and positions.
87
114
  if @border
88
115
  @w = @max_w - 2 if @w > @max_w - 2
89
116
  @h = @max_h - 2 if @h > @max_h - 2
@@ -96,27 +123,45 @@ module Rcurses
96
123
  @y = 1 if @y < 1; @y = @max_h - @h + 1 if @y + @h > @max_h + 1
97
124
  end
98
125
 
99
- # Save current cursor position.
100
126
  o_row, o_col = pos
101
-
102
127
  STDOUT.print "\e[?25l" # Hide cursor
103
128
 
104
129
  fmt = [@fg, @bg].compact.join(',')
105
- @txt = cont.split("\n")
106
- @txt = textformat(cont) if @txt.any? { |line| line.pure.length >= @w }
130
+
131
+ # Lazy evaluation: If the content or pane width has changed, reinitialize the lazy cache.
132
+ if !defined?(@cached_text) || @cached_text != cont || @cached_w != @w
133
+ @raw_txt = cont.split("\n")
134
+ @lazy_txt = [] # This will hold the processed (wrapped) lines as needed.
135
+ @lazy_index = 0 # Pointer to the next raw line to process.
136
+ @cached_text = cont
137
+ @cached_w = @w
138
+ end
139
+
140
+ content_rows = @h
141
+ # Ensure we have processed enough lines for the current scroll position + visible area.
142
+ required_lines = @ix + content_rows
143
+ while @lazy_txt.size < required_lines && @lazy_index < @raw_txt.size
144
+ raw_line = @raw_txt[@lazy_index]
145
+ # If the raw line is short, no wrapping is needed.
146
+ if raw_line.respond_to?(:pure) && raw_line.pure.length < @w
147
+ processed = [raw_line]
148
+ else
149
+ processed = split_line_with_ansi(raw_line, @w)
150
+ end
151
+ @lazy_txt.concat(processed)
152
+ @lazy_index += 1
153
+ end
154
+ @txt = @lazy_txt
155
+
107
156
  @ix = @txt.length - 1 if @ix > @txt.length - 1
108
157
  @ix = 0 if @ix < 0
109
158
 
110
- # Build the new frame as an array of strings.
111
159
  new_frame = []
112
160
  if @border
113
- # Top border spans (@w + 2) characters.
114
161
  top_border = ("┌" + "─" * @w + "┐").c(fmt)
115
162
  new_frame << top_border
116
163
  end
117
164
 
118
- # Build content lines.
119
- content_rows = @h
120
165
  content_rows.times do |i|
121
166
  line_str = ""
122
167
  l = @ix + i
@@ -136,37 +181,21 @@ module Rcurses
136
181
  line_str = " ".c(fmt) * @w
137
182
  end
138
183
 
139
- # If border is enabled, add vertical border characters.
140
184
  if @border
141
185
  line_str = "│" + line_str + "│"
142
186
  end
143
187
 
144
- # Add scroll markers (overwrite the last character) if needed.
145
- if i == 0 and @ix > 0 and @scroll
146
- line_str[-1] = "∆".c(fmt)
147
- @moreup = true
148
- elsif i == content_rows - 1 and @txt.length - @ix > @h and @scroll
149
- line_str[-1] = "∇".c(fmt)
150
- @moredown = true
151
- else
152
- @moreup = false
153
- @moredown = false
154
- end
155
188
  new_frame << line_str
156
189
  end
157
190
 
158
191
  if @border
159
- # Bottom border.
160
192
  bottom_border = ("└" + "─" * @w + "┘").c(fmt)
161
193
  new_frame << bottom_border
162
194
  end
163
195
 
164
- # Diff-based update: update only lines that changed.
165
196
  diff_buf = ""
166
197
  new_frame.each_with_index do |line, i|
167
- # Determine row number:
168
198
  row_num = @border ? (@y - 1 + i) : (@y + i)
169
- # When border is enabled, all lines (including content) start at column (@x - 1)
170
199
  col_num = @border ? (@x - 1) : @x
171
200
  if @prev_frame.nil? || @prev_frame[i] != line ||
172
201
  (@border && (i == 0 || i == new_frame.size - 1))
@@ -176,13 +205,33 @@ module Rcurses
176
205
 
177
206
  diff_buf << "\e[#{o_row};#{o_col}H"
178
207
  print diff_buf
179
- #STDOUT.print "\e[?25h" # Show cursor - but use Cursor.show instead if needed
180
-
181
208
  @prev_frame = new_frame
209
+
210
+ # Draw scroll markers after printing the frame.
211
+ if @scroll
212
+ marker_col = @border ? (@x + @w - 1) : (@x + @w - 1)
213
+ if @ix > 0
214
+ print "\e[#{@y};#{marker_col}H" + "∆".c(fmt)
215
+ end
216
+ # If there are more processed lines than fit in the pane
217
+ # OR there remain raw lines to process, show the down marker.
218
+ if (@txt.length - @ix) > @h || (@lazy_index < @raw_txt.size)
219
+ print "\e[#{@y + @h - 1};#{marker_col}H" + "∇".c(fmt)
220
+ end
221
+ end
222
+
223
+ if @border
224
+ (0...@h).each do |i|
225
+ print "\e[#{@y + i};#{@x + @w}H" + "│".c(fmt)
226
+ end
227
+ end
228
+
182
229
  new_frame.join("\n")
183
230
  end
184
231
 
185
232
  def textformat(cont)
233
+ # This method is no longer used in refresh since we process lazily,
234
+ # but is kept here if needed elsewhere.
186
235
  lines = cont.split("\n")
187
236
  result = []
188
237
  lines.each do |line|
data/lib/rcurses.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Web_site: http://isene.com/
6
6
  # Github: https://github.com/isene/rcurses
7
7
  # License: Public domain
8
- # Version: 3.1: Flicker reduction by diff rendering
8
+ # Version: 3.3: Faster rendering of pane content
9
9
 
10
10
  require 'io/console' # Basic gem for rcurses
11
11
  require 'io/wait' # stdin handling
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcurses
3
3
  version: !ruby/object:Gem::Version
4
- version: '3.1'
4
+ version: '3.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-01 00:00:00.000000000 Z
11
+ date: 2025-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clipboard
@@ -29,8 +29,8 @@ description: 'Create curses applications for the terminal easier than ever. Crea
29
29
  up text (in panes or anywhere in the terminal) in bold, italic, underline, reverse
30
30
  color, blink and in any 256 terminal colors for foreground and background. Use a
31
31
  simple editor to let users edit text in panes. Left, right or center align text
32
- in panes. Cursor movement around the terminal. New in 3.1: Flicker reduction by
33
- diff rendering.'
32
+ in panes. Cursor movement around the terminal. New in 3.3: Faster rendering of pane
33
+ content.'
34
34
  email: g@isene.com
35
35
  executables: []
36
36
  extensions: []