teek 0.1.0 → 0.1.2

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +99 -15
  3. data/Rakefile +201 -2
  4. data/ext/teek/extconf.rb +1 -1
  5. data/ext/teek/tcltkbridge.c +3 -110
  6. data/ext/teek/tcltkbridge.h +3 -0
  7. data/ext/teek/tkeventsource.c +195 -0
  8. data/ext/teek/tkphoto.c +169 -5
  9. data/ext/teek/tkwin.c +84 -0
  10. data/lib/teek/background_ractor4x.rb +35 -6
  11. data/lib/teek/debugger.rb +37 -32
  12. data/lib/teek/method_coverage_service.rb +265 -0
  13. data/lib/teek/photo.rb +232 -0
  14. data/lib/teek/ractor_support.rb +1 -1
  15. data/lib/teek/version.rb +1 -1
  16. data/lib/teek/widget.rb +104 -0
  17. data/lib/teek.rb +144 -1
  18. data/sample/calculator.rb +16 -21
  19. data/sample/debug_demo.rb +20 -22
  20. data/sample/optcarrot/vendor/optcarrot/apu.rb +856 -0
  21. data/sample/optcarrot/vendor/optcarrot/config.rb +257 -0
  22. data/sample/optcarrot/vendor/optcarrot/cpu.rb +1162 -0
  23. data/sample/optcarrot/vendor/optcarrot/driver.rb +144 -0
  24. data/sample/optcarrot/vendor/optcarrot/mapper/cnrom.rb +14 -0
  25. data/sample/optcarrot/vendor/optcarrot/mapper/mmc1.rb +105 -0
  26. data/sample/optcarrot/vendor/optcarrot/mapper/mmc3.rb +153 -0
  27. data/sample/optcarrot/vendor/optcarrot/mapper/uxrom.rb +14 -0
  28. data/sample/optcarrot/vendor/optcarrot/nes.rb +105 -0
  29. data/sample/optcarrot/vendor/optcarrot/opt.rb +168 -0
  30. data/sample/optcarrot/vendor/optcarrot/pad.rb +92 -0
  31. data/sample/optcarrot/vendor/optcarrot/palette.rb +65 -0
  32. data/sample/optcarrot/vendor/optcarrot/ppu.rb +1468 -0
  33. data/sample/optcarrot/vendor/optcarrot/rom.rb +143 -0
  34. data/sample/optcarrot/vendor/optcarrot.rb +14 -0
  35. data/sample/optcarrot.rb +354 -0
  36. data/sample/paint/assets/bucket.png +0 -0
  37. data/sample/paint/assets/cursor.png +0 -0
  38. data/sample/paint/assets/eraser.png +0 -0
  39. data/sample/paint/assets/pencil.png +0 -0
  40. data/sample/paint/assets/spray.png +0 -0
  41. data/sample/paint/layer.rb +255 -0
  42. data/sample/paint/layer_manager.rb +179 -0
  43. data/sample/paint/paint_demo.rb +837 -0
  44. data/sample/paint/sparse_pixel_buffer.rb +202 -0
  45. data/sample/sdl2_demo.rb +318 -0
  46. data/sample/threading_demo.rb +127 -132
  47. metadata +31 -1
@@ -44,35 +44,34 @@ class ThreadingDemo
44
44
  @app.update
45
45
  w = @app.command(:winfo, 'width', '.')
46
46
  h = @app.command(:winfo, 'height', '.')
47
- @app.command(:wm, 'geometry', '.', "#{w}x#{h}+0+0")
48
- @app.command(:wm, 'resizable', '.', 1, 1)
47
+ @app.set_window_geometry("#{w}x#{h}+0+0")
48
+ @app.set_window_resizable(true, true)
49
49
 
50
50
  close_proc = proc { |*|
51
51
  @background_task&.close
52
- @app.command(:destroy, '.')
52
+ @app.destroy('.')
53
53
  }
54
54
  @app.command(:wm, 'protocol', '.', 'WM_DELETE_WINDOW', close_proc)
55
55
  end
56
56
 
57
57
  def build_ui
58
58
  @app.show
59
- @app.command(:wm, 'title', '.', 'Concurrency Demo - File Hasher')
59
+ @app.set_window_title('Concurrency Demo - File Hasher')
60
60
  @app.command(:wm, 'minsize', '.', 600, 400)
61
61
 
62
62
  # Tcl variables for widget bindings
63
- @app.command(:set, '::chunk_size', 3)
64
- @app.command(:set, '::algorithm', 'SHA256')
65
- @app.command(:set, '::mode', 'Thread')
66
- @app.command(:set, '::allow_pause', 0)
67
- @app.command(:set, '::progress', 0)
63
+ @app.set_variable('::chunk_size', 3)
64
+ @app.set_variable('::algorithm', 'SHA256')
65
+ @app.set_variable('::mode', 'Thread')
66
+ @app.set_variable('::allow_pause', 0)
67
+ @app.set_variable('::progress', 0)
68
68
 
69
69
  ractor_note = RACTOR_AVAILABLE ? "Ractor: true parallel." : "(Ractor available on Ruby 4.x+)"
70
- @app.command('ttk::label', '.desc',
70
+ @app.create_widget('ttk::label',
71
71
  text: "File hasher demo - compares concurrency modes.\n" \
72
72
  "None: UI frozen. None+update: progress visible, pause works. " \
73
73
  "Thread: responsive, GVL shared. #{ractor_note}",
74
- justify: :left)
75
- @app.command(:pack, '.desc', fill: :x, padx: 10, pady: 10)
74
+ justify: :left).pack(fill: :x, padx: 10, pady: 10)
76
75
 
77
76
  build_controls
78
77
  build_statusbar
@@ -80,112 +79,116 @@ class ThreadingDemo
80
79
  end
81
80
 
82
81
  def build_controls
83
- @app.command('ttk::frame', '.ctrl')
84
- @app.command(:pack, '.ctrl', fill: :x, padx: 10, pady: 5)
82
+ ctrl = @app.create_widget('ttk::frame')
83
+ ctrl.pack(fill: :x, padx: 10, pady: 5)
85
84
 
86
- @app.command('ttk::button', '.ctrl.start',
85
+ @start_btn = @app.create_widget('ttk::button', parent: ctrl,
87
86
  text: 'Start', command: proc { |*| start_hashing })
88
- @app.command(:pack, '.ctrl.start', side: :left)
87
+ @start_btn.pack(side: :left)
89
88
 
90
- @app.command('ttk::button', '.ctrl.pause',
89
+ @pause_btn = @app.create_widget('ttk::button', parent: ctrl,
91
90
  text: 'Pause', state: :disabled, command: proc { |*| toggle_pause })
92
- @app.command(:pack, '.ctrl.pause', side: :left, padx: 5)
91
+ @pause_btn.pack(side: :left, padx: 5)
93
92
 
94
- @app.command('ttk::button', '.ctrl.reset',
95
- text: 'Reset', command: proc { |*| reset })
96
- @app.command(:pack, '.ctrl.reset', side: :left)
93
+ @app.create_widget('ttk::button', parent: ctrl,
94
+ text: 'Reset', command: proc { |*| reset }).pack(side: :left)
97
95
 
98
- @app.command('ttk::label', '.ctrl.algo_lbl', text: 'Algorithm:')
99
- @app.command(:pack, '.ctrl.algo_lbl', side: :left, padx: 10)
96
+ @app.create_widget('ttk::label', parent: ctrl,
97
+ text: 'Algorithm:').pack(side: :left, padx: 10)
100
98
 
101
- @app.command('ttk::combobox', '.ctrl.algo',
99
+ @algo_combo = @app.create_widget('ttk::combobox', parent: ctrl,
102
100
  textvariable: '::algorithm',
103
101
  values: Teek.make_list(*ALGORITHMS),
104
102
  width: 8,
105
103
  state: :readonly)
106
- @app.command(:pack, '.ctrl.algo', side: :left)
104
+ @algo_combo.pack(side: :left)
107
105
 
108
- @app.command('ttk::label', '.ctrl.batch_lbl', text: 'Batch:')
109
- @app.command(:pack, '.ctrl.batch_lbl', side: :left, padx: 10)
106
+ @app.create_widget('ttk::label', parent: ctrl,
107
+ text: 'Batch:').pack(side: :left, padx: 10)
110
108
 
111
- @app.command('ttk::label', '.ctrl.batch_val', text: '3', width: 3)
112
- @app.command(:pack, '.ctrl.batch_val', side: :left)
109
+ @batch_val = @app.create_widget('ttk::label', parent: ctrl, text: '3', width: 3)
110
+ @batch_val.pack(side: :left)
113
111
 
114
- @app.command('ttk::scale', '.ctrl.scale',
112
+ @app.create_widget('ttk::scale', parent: ctrl,
115
113
  orient: :horizontal,
116
114
  from: 1,
117
115
  to: 100,
118
116
  length: 100,
119
117
  variable: '::chunk_size',
120
- command: proc { |v, *| @app.command('.ctrl.batch_val', 'configure', text: v.to_f.round.to_s) })
121
- @app.command(:pack, '.ctrl.scale', side: :left, padx: 5)
118
+ command: proc { |v, *| @batch_val.command(:configure, text: v.to_f.round.to_s) })
119
+ .pack(side: :left, padx: 5)
122
120
 
123
- @app.command('ttk::label', '.ctrl.mode_lbl', text: 'Mode:')
124
- @app.command(:pack, '.ctrl.mode_lbl', side: :left, padx: 10)
121
+ @app.create_widget('ttk::label', parent: ctrl,
122
+ text: 'Mode:').pack(side: :left, padx: 10)
125
123
 
126
- @app.command('ttk::combobox', '.ctrl.mode',
124
+ @mode_combo = @app.create_widget('ttk::combobox', parent: ctrl,
127
125
  textvariable: '::mode',
128
126
  values: Teek.make_list(*MODES),
129
127
  width: 10,
130
128
  state: :readonly)
131
- @app.command(:pack, '.ctrl.mode', side: :left)
129
+ @mode_combo.pack(side: :left)
132
130
 
133
- @app.command('ttk::checkbutton', '.ctrl.pause_chk',
131
+ @app.create_widget('ttk::checkbutton', parent: ctrl,
134
132
  text: 'Allow Pause',
135
- variable: '::allow_pause')
136
- @app.command(:pack, '.ctrl.pause_chk', side: :left, padx: 10)
133
+ variable: '::allow_pause').pack(side: :left, padx: 10)
137
134
  end
138
135
 
139
136
  def build_statusbar
140
- @app.command('ttk::frame', '.status')
141
- @app.command(:pack, '.status', side: :bottom, fill: :x, padx: 5, pady: 5)
137
+ status = @app.create_widget('ttk::frame')
138
+ status.pack(side: :bottom, fill: :x, padx: 5, pady: 5)
142
139
 
143
140
  # Progress section (left)
144
- @app.command('ttk::frame', '.status.progress', relief: :sunken, borderwidth: 2)
145
- @app.command(:pack, '.status.progress', side: :left, fill: :x, expand: 1, padx: 2)
141
+ progress_frame = @app.create_widget('ttk::frame', parent: status,
142
+ relief: :sunken, borderwidth: 2)
143
+ progress_frame.pack(side: :left, fill: :x, expand: 1, padx: 2)
146
144
 
147
- @app.command('ttk::progressbar', '.status.progress.bar',
145
+ @app.create_widget('ttk::progressbar', parent: progress_frame,
148
146
  orient: :horizontal,
149
147
  length: 200,
150
148
  mode: :determinate,
151
149
  variable: '::progress',
152
- maximum: 100)
153
- @app.command(:pack, '.status.progress.bar', side: :left, padx: 5, pady: 4)
150
+ maximum: 100).pack(side: :left, padx: 5, pady: 4)
154
151
 
155
- @app.command('ttk::label', '.status.progress.status', text: 'Ready', width: 20, anchor: :w)
156
- @app.command(:pack, '.status.progress.status', side: :left, padx: 10)
152
+ @status_label = @app.create_widget('ttk::label', parent: progress_frame,
153
+ text: 'Ready', width: 20, anchor: :w)
154
+ @status_label.pack(side: :left, padx: 10)
157
155
 
158
- @app.command('ttk::label', '.status.progress.file', text: '', width: 28, anchor: :w)
159
- @app.command(:pack, '.status.progress.file', side: :left, padx: 5)
156
+ @file_label = @app.create_widget('ttk::label', parent: progress_frame,
157
+ text: '', width: 28, anchor: :w)
158
+ @file_label.pack(side: :left, padx: 5)
160
159
 
161
160
  # Info section (right)
162
- @app.command('ttk::frame', '.status.info', relief: :sunken, borderwidth: 2)
163
- @app.command(:pack, '.status.info', side: :right, padx: 2)
161
+ info_frame = @app.create_widget('ttk::frame', parent: status,
162
+ relief: :sunken, borderwidth: 2)
163
+ info_frame.pack(side: :right, padx: 2)
164
164
 
165
- @app.command('ttk::label', '.status.info.files', text: '', width: 12, anchor: :e)
166
- @app.command(:pack, '.status.info.files', side: :left, padx: 8, pady: 4)
165
+ @files_label = @app.create_widget('ttk::label', parent: info_frame,
166
+ text: '', width: 12, anchor: :e)
167
+ @files_label.pack(side: :left, padx: 8, pady: 4)
167
168
 
168
- @app.command('ttk::separator', '.status.info.sep', orient: :vertical)
169
- @app.command(:pack, '.status.info.sep', side: :left, fill: :y, pady: 4)
169
+ @app.create_widget('ttk::separator', parent: info_frame,
170
+ orient: :vertical).pack(side: :left, fill: :y, pady: 4)
170
171
 
171
- @app.command('ttk::label', '.status.info.ruby', text: "Ruby #{RUBY_VERSION}", anchor: :e)
172
- @app.command(:pack, '.status.info.ruby', side: :left, padx: 8, pady: 4)
172
+ @app.create_widget('ttk::label', parent: info_frame,
173
+ text: "Ruby #{RUBY_VERSION}", anchor: :e).pack(side: :left, padx: 8, pady: 4)
173
174
  end
174
175
 
175
176
  def build_log
176
- @app.command('ttk::labelframe', '.log', text: 'Output')
177
- @app.command(:pack, '.log', fill: :both, expand: 1, padx: 10, pady: 5)
177
+ log = @app.create_widget('ttk::labelframe', text: 'Output')
178
+ log.pack(fill: :both, expand: 1, padx: 10, pady: 5)
178
179
 
179
- @app.command('ttk::frame', '.log.f')
180
- @app.command(:pack, '.log.f', fill: :both, expand: 1, padx: 5, pady: 5)
181
- @app.command(:pack, 'propagate', '.log.f', 0)
180
+ log_frame = @app.create_widget('ttk::frame', parent: log)
181
+ log_frame.pack(fill: :both, expand: 1, padx: 5, pady: 5)
182
+ @app.command(:pack, 'propagate', log_frame, 0)
182
183
 
183
- @app.command(:text, '.log.f.text', width: 80, height: 15, wrap: :none)
184
- @app.command(:pack, '.log.f.text', side: :left, fill: :both, expand: 1)
184
+ @log_text = @app.create_widget(:text, parent: log_frame,
185
+ width: 80, height: 15, wrap: :none)
186
+ @log_text.pack(side: :left, fill: :both, expand: 1)
185
187
 
186
- @app.command('ttk::scrollbar', '.log.f.vsb', orient: :vertical, command: '.log.f.text yview')
187
- @app.command('.log.f.text', 'configure', yscrollcommand: '.log.f.vsb set')
188
- @app.command(:pack, '.log.f.vsb', side: :right, fill: :y)
188
+ vsb = @app.create_widget('ttk::scrollbar', parent: log_frame,
189
+ orient: :vertical, command: "#{@log_text} yview")
190
+ @log_text.command(:configure, yscrollcommand: "#{vsb} set")
191
+ vsb.pack(side: :right, fill: :y)
189
192
  end
190
193
 
191
194
  def collect_files
@@ -199,24 +202,16 @@ class ThreadingDemo
199
202
  max_files ||= 5 if ENV['TK_READY_PORT'] # test mode -- don't hash 200+ files
200
203
  @files = @files.first(max_files) if max_files && max_files > 0
201
204
 
202
- @app.command('.status.info.files', 'configure', text: "#{@files.size} files")
205
+ @files_label.command(:configure, text: "#{@files.size} files")
203
206
  end
204
207
 
205
208
  def current_mode
206
- @app.command(:set, '::mode')
209
+ @app.get_variable('::mode')
207
210
  end
208
211
 
209
- def get_var(name)
210
- @app.command(:set, name)
211
- end
212
-
213
- def set_var(name, value)
214
- @app.command(:set, name, value)
215
- end
216
-
217
- def set_combo_enabled(path)
212
+ def set_combo_enabled(widget)
218
213
  # ttk state: must clear disabled AND set readonly in one call
219
- @app.tcl_eval("#{path} state {!disabled readonly}")
214
+ @app.tcl_eval("#{widget} state {!disabled readonly}")
220
215
  end
221
216
 
222
217
  def start_hashing
@@ -224,20 +219,20 @@ class ThreadingDemo
224
219
  @paused = false
225
220
  @stop_requested = false
226
221
 
227
- @app.command('.ctrl.start', 'state', 'disabled')
228
- @app.command('.ctrl.algo', 'state', 'disabled')
229
- @app.command('.ctrl.mode', 'state', 'disabled')
230
- @app.command('.log.f.text', 'delete', '1.0', 'end')
231
- set_var('::progress', 0)
232
- @app.command('.status.progress.status', 'configure', text: 'Hashing...')
222
+ @start_btn.command(:state, 'disabled')
223
+ @algo_combo.command(:state, 'disabled')
224
+ @mode_combo.command(:state, 'disabled')
225
+ @log_text.command(:delete, '1.0', 'end')
226
+ @app.set_variable('::progress', 0)
227
+ @status_label.command(:configure, text: 'Hashing...')
233
228
 
234
- if get_var('::allow_pause').to_i == 1
235
- @app.command('.ctrl.pause', 'state', '!disabled')
229
+ if @app.get_variable('::allow_pause').to_i == 1
230
+ @pause_btn.command(:state, '!disabled')
236
231
  else
237
- @app.command('.ctrl.pause', 'state', 'disabled')
232
+ @pause_btn.command(:state, 'disabled')
238
233
  end
239
234
 
240
- @app.command(:wm, 'resizable', '.', 0, 0) unless current_mode == 'Ractor'
235
+ @app.set_window_resizable(false, false) unless current_mode == 'Ractor'
241
236
 
242
237
  @metrics = {
243
238
  start_time: Process.clock_gettime(Process::CLOCK_MONOTONIC),
@@ -257,13 +252,13 @@ class ThreadingDemo
257
252
 
258
253
  def toggle_pause
259
254
  @paused = !@paused
260
- @app.command('.ctrl.pause', 'configure', text: @paused ? 'Resume' : 'Pause')
261
- @app.command('.status.progress.status', 'configure', text: @paused ? 'Paused' : 'Hashing...')
262
- @app.command(:wm, 'resizable', '.', @paused ? 1 : 0, @paused ? 1 : 0)
255
+ @pause_btn.command(:configure, text: @paused ? 'Resume' : 'Pause')
256
+ @status_label.command(:configure, text: @paused ? 'Paused' : 'Hashing...')
257
+ @app.set_window_resizable(@paused, @paused)
263
258
  if @paused
264
- set_combo_enabled('.ctrl.mode')
259
+ set_combo_enabled(@mode_combo)
265
260
  else
266
- @app.command('.ctrl.mode', 'state', 'disabled')
261
+ @mode_combo.command(:state, 'disabled')
267
262
  end
268
263
 
269
264
  if @background_task
@@ -281,22 +276,22 @@ class ThreadingDemo
281
276
  @background_task&.stop
282
277
  @background_task = nil
283
278
 
284
- @app.command('.ctrl.start', 'state', '!disabled')
285
- @app.command('.ctrl.pause', 'state', 'disabled')
286
- @app.command('.ctrl.pause', 'configure', text: 'Pause')
287
- set_combo_enabled('.ctrl.algo')
288
- set_combo_enabled('.ctrl.mode')
289
- @app.command(:wm, 'resizable', '.', 1, 1)
290
- @app.command('.log.f.text', 'delete', '1.0', 'end')
291
- set_var('::progress', 0)
292
- @app.command('.status.progress.status', 'configure', text: 'Ready')
293
- @app.command('.status.progress.file', 'configure', text: '')
294
-
295
- set_var('::mode', 'Thread')
296
- set_var('::algorithm', 'SHA256')
297
- set_var('::chunk_size', 3)
298
- @app.command('.ctrl.batch_val', 'configure', text: '3')
299
- set_var('::allow_pause', 0)
279
+ @start_btn.command(:state, '!disabled')
280
+ @pause_btn.command(:state, 'disabled')
281
+ @pause_btn.command(:configure, text: 'Pause')
282
+ set_combo_enabled(@algo_combo)
283
+ set_combo_enabled(@mode_combo)
284
+ @app.set_window_resizable(true, true)
285
+ @log_text.command(:delete, '1.0', 'end')
286
+ @app.set_variable('::progress', 0)
287
+ @status_label.command(:configure, text: 'Ready')
288
+ @file_label.command(:configure, text: '')
289
+
290
+ @app.set_variable('::mode', 'Thread')
291
+ @app.set_variable('::algorithm', 'SHA256')
292
+ @app.set_variable('::chunk_size', 3)
293
+ @batch_val.command(:configure, text: '3')
294
+ @app.set_variable('::allow_pause', 0)
300
295
  end
301
296
 
302
297
  def write_metrics(status = "DONE")
@@ -308,9 +303,9 @@ class ThreadingDemo
308
303
  f.puts "=" * 60
309
304
  f.puts "Status: #{status} at #{Time.now}"
310
305
  f.puts "Mode: #{m[:mode]}"
311
- f.puts "Algorithm: #{get_var('::algorithm')}"
306
+ f.puts "Algorithm: #{@app.get_variable('::algorithm')}"
312
307
  f.puts "Files processed: #{m[:files_done]}/#{m[:total]}"
313
- chunk = [get_var('::chunk_size').to_f.round, 1].max
308
+ chunk = [@app.get_variable('::chunk_size').to_f.round, 1].max
314
309
  f.puts "Batch size: #{chunk}"
315
310
  f.puts "-" * 40
316
311
  f.puts "Elapsed: #{elapsed.round(3)}s"
@@ -328,14 +323,14 @@ class ThreadingDemo
328
323
 
329
324
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @metrics[:start_time]
330
325
  files_per_sec = (@metrics[:files_done] / elapsed).round(1)
331
- @app.command('.status.progress.status', 'configure',
326
+ @status_label.command(:configure,
332
327
  text: "Done #{elapsed.round(2)}s (#{files_per_sec}/s)")
333
- @app.command('.status.progress.file', 'configure', text: '')
334
- @app.command('.ctrl.start', 'state', '!disabled')
335
- @app.command('.ctrl.pause', 'state', 'disabled')
336
- set_combo_enabled('.ctrl.algo')
337
- set_combo_enabled('.ctrl.mode')
338
- @app.command(:wm, 'resizable', '.', 1, 1)
328
+ @file_label.command(:configure, text: '')
329
+ @start_btn.command(:state, '!disabled')
330
+ @pause_btn.command(:state, 'disabled')
331
+ set_combo_enabled(@algo_combo)
332
+ set_combo_enabled(@mode_combo)
333
+ @app.set_window_resizable(true, true)
339
334
  @running = false
340
335
  end
341
336
 
@@ -347,10 +342,10 @@ class ThreadingDemo
347
342
  ui_mode = current_mode
348
343
 
349
344
  files = @files.dup
350
- algo_name = get_var('::algorithm')
351
- chunk_size = [get_var('::chunk_size').to_f.round, 1].max
345
+ algo_name = @app.get_variable('::algorithm')
346
+ chunk_size = [@app.get_variable('::chunk_size').to_f.round, 1].max
352
347
  base_dir = Dir.pwd
353
- allow_pause = get_var('::allow_pause').to_i == 1
348
+ allow_pause = @app.get_variable('::allow_pause').to_i == 1
354
349
 
355
350
  work_data = {
356
351
  files: files,
@@ -409,11 +404,11 @@ class ThreadingDemo
409
404
  @background_task.on_progress do |msg|
410
405
  ui_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
411
406
 
412
- @app.command('.log.f.text', 'insert', 'end', msg[:updates])
413
- @app.command('.log.f.text', 'see', 'end')
407
+ @log_text.command(:insert, 'end', msg[:updates])
408
+ @log_text.command(:see, 'end')
414
409
  pct = ((msg[:index] + 1).to_f / msg[:total] * 100).round
415
- set_var('::progress', pct)
416
- @app.command('.status.progress.status', 'configure',
410
+ @app.set_variable('::progress', pct)
411
+ @status_label.command(:configure,
417
412
  text: "Hashing... #{msg[:index] + 1}/#{msg[:total]}")
418
413
 
419
414
  @metrics[:ui_update_count] += 1
@@ -450,7 +445,7 @@ if TeekDemo.active?
450
445
  app = demo.app
451
446
 
452
447
  # Set batch size high for fast processing
453
- app.command(:set, '::chunk_size', 100)
448
+ app.set_variable('::chunk_size', 100)
454
449
 
455
450
  # Test matrix: [mode, pause_enabled]
456
451
  tests = [['None', false], ['None+update', false], ['Thread', false]]
@@ -465,11 +460,11 @@ if TeekDemo.active?
465
460
  mode, pause = tests[test_index]
466
461
 
467
462
  # Configure mode and pause
468
- app.command(:set, '::mode', mode)
469
- app.command(:set, '::allow_pause', pause ? 1 : 0)
463
+ app.set_variable('::mode', mode)
464
+ app.set_variable('::allow_pause', pause ? 1 : 0)
470
465
 
471
466
  # Start hashing
472
- app.after(100) { app.command('.ctrl.start', 'invoke') }
467
+ app.after(100) { demo.start_hashing }
473
468
 
474
469
  # Wait for completion
475
470
  check_done = proc do
@@ -479,7 +474,7 @@ if TeekDemo.active?
479
474
  test_index += 1
480
475
  if test_index < tests.size
481
476
  app.after(200) {
482
- app.command('.ctrl.reset', 'invoke')
477
+ demo.reset
483
478
  app.after(200, &run_next_test)
484
479
  }
485
480
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Cook
@@ -124,6 +124,7 @@ files:
124
124
  - ext/teek/tcl9compat.h
125
125
  - ext/teek/tcltkbridge.c
126
126
  - ext/teek/tcltkbridge.h
127
+ - ext/teek/tkeventsource.c
127
128
  - ext/teek/tkfont.c
128
129
  - ext/teek/tkphoto.c
129
130
  - ext/teek/tkwin.c
@@ -133,8 +134,11 @@ files:
133
134
  - lib/teek/background_thread.rb
134
135
  - lib/teek/debugger.rb
135
136
  - lib/teek/demo_support.rb
137
+ - lib/teek/method_coverage_service.rb
138
+ - lib/teek/photo.rb
136
139
  - lib/teek/ractor_support.rb
137
140
  - lib/teek/version.rb
141
+ - lib/teek/widget.rb
138
142
  - sample/calculator.rb
139
143
  - sample/debug_demo.rb
140
144
  - sample/goldberg.rb
@@ -152,6 +156,32 @@ files:
152
156
  - sample/minesweeper/assets/MINESWEEPER_M.png
153
157
  - sample/minesweeper/assets/MINESWEEPER_X.png
154
158
  - sample/minesweeper/minesweeper.rb
159
+ - sample/optcarrot.rb
160
+ - sample/optcarrot/vendor/optcarrot.rb
161
+ - sample/optcarrot/vendor/optcarrot/apu.rb
162
+ - sample/optcarrot/vendor/optcarrot/config.rb
163
+ - sample/optcarrot/vendor/optcarrot/cpu.rb
164
+ - sample/optcarrot/vendor/optcarrot/driver.rb
165
+ - sample/optcarrot/vendor/optcarrot/mapper/cnrom.rb
166
+ - sample/optcarrot/vendor/optcarrot/mapper/mmc1.rb
167
+ - sample/optcarrot/vendor/optcarrot/mapper/mmc3.rb
168
+ - sample/optcarrot/vendor/optcarrot/mapper/uxrom.rb
169
+ - sample/optcarrot/vendor/optcarrot/nes.rb
170
+ - sample/optcarrot/vendor/optcarrot/opt.rb
171
+ - sample/optcarrot/vendor/optcarrot/pad.rb
172
+ - sample/optcarrot/vendor/optcarrot/palette.rb
173
+ - sample/optcarrot/vendor/optcarrot/ppu.rb
174
+ - sample/optcarrot/vendor/optcarrot/rom.rb
175
+ - sample/paint/assets/bucket.png
176
+ - sample/paint/assets/cursor.png
177
+ - sample/paint/assets/eraser.png
178
+ - sample/paint/assets/pencil.png
179
+ - sample/paint/assets/spray.png
180
+ - sample/paint/layer.rb
181
+ - sample/paint/layer_manager.rb
182
+ - sample/paint/paint_demo.rb
183
+ - sample/paint/sparse_pixel_buffer.rb
184
+ - sample/sdl2_demo.rb
155
185
  - sample/threading_demo.rb
156
186
  - teek.gemspec
157
187
  homepage: https://github.com/jamescook/teek