gemba 0.1.0
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 +7 -0
- data/THIRD_PARTY_NOTICES +113 -0
- data/assets/JetBrainsMonoNL-Regular.ttf +0 -0
- data/assets/ark-pixel-12px-monospaced-ja.ttf +0 -0
- data/bin/gemba +14 -0
- data/ext/gemba/extconf.rb +185 -0
- data/ext/gemba/gemba_ext.c +1051 -0
- data/ext/gemba/gemba_ext.h +15 -0
- data/gemba.gemspec +38 -0
- data/lib/gemba/child_window.rb +62 -0
- data/lib/gemba/cli.rb +384 -0
- data/lib/gemba/config.rb +621 -0
- data/lib/gemba/core.rb +121 -0
- data/lib/gemba/headless.rb +12 -0
- data/lib/gemba/headless_player.rb +206 -0
- data/lib/gemba/hotkey_map.rb +202 -0
- data/lib/gemba/input_mappings.rb +214 -0
- data/lib/gemba/locale.rb +92 -0
- data/lib/gemba/locales/en.yml +157 -0
- data/lib/gemba/locales/ja.yml +157 -0
- data/lib/gemba/method_coverage_service.rb +265 -0
- data/lib/gemba/overlay_renderer.rb +109 -0
- data/lib/gemba/player.rb +1515 -0
- data/lib/gemba/recorder.rb +156 -0
- data/lib/gemba/recorder_decoder.rb +325 -0
- data/lib/gemba/rom_info_window.rb +346 -0
- data/lib/gemba/rom_loader.rb +100 -0
- data/lib/gemba/runtime.rb +39 -0
- data/lib/gemba/save_state_manager.rb +155 -0
- data/lib/gemba/save_state_picker.rb +199 -0
- data/lib/gemba/settings_window.rb +1173 -0
- data/lib/gemba/tip_service.rb +133 -0
- data/lib/gemba/toast_overlay.rb +128 -0
- data/lib/gemba/version.rb +5 -0
- data/lib/gemba.rb +17 -0
- data/test/fixtures/test.gba +0 -0
- data/test/fixtures/test.sav +0 -0
- data/test/shared/screenshot_helper.rb +113 -0
- data/test/shared/simplecov_config.rb +59 -0
- data/test/shared/teek_test_worker.rb +388 -0
- data/test/shared/tk_test_helper.rb +354 -0
- data/test/support/input_mocks.rb +61 -0
- data/test/support/player_helpers.rb +77 -0
- data/test/test_cli.rb +281 -0
- data/test/test_config.rb +897 -0
- data/test/test_core.rb +401 -0
- data/test/test_gamepad_map.rb +116 -0
- data/test/test_headless_player.rb +205 -0
- data/test/test_helper.rb +19 -0
- data/test/test_hotkey_map.rb +396 -0
- data/test/test_keyboard_map.rb +108 -0
- data/test/test_locale.rb +159 -0
- data/test/test_mgba.rb +26 -0
- data/test/test_overlay_renderer.rb +199 -0
- data/test/test_player.rb +903 -0
- data/test/test_recorder.rb +180 -0
- data/test/test_rom_loader.rb +149 -0
- data/test/test_save_state_manager.rb +289 -0
- data/test/test_settings_hotkeys.rb +434 -0
- data/test/test_settings_window.rb +1039 -0
- data/test/test_tip_service.rb +138 -0
- data/test/test_toast_overlay.rb +216 -0
- data/test/test_virtual_keyboard.rb +39 -0
- data/test/test_xor_delta.rb +61 -0
- metadata +234 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/autorun"
|
|
4
|
+
require_relative "shared/tk_test_helper"
|
|
5
|
+
|
|
6
|
+
class TestMGBASettingsHotkeys < Minitest::Test
|
|
7
|
+
include TeekTestHelper
|
|
8
|
+
|
|
9
|
+
def test_hotkeys_tab_exists
|
|
10
|
+
assert_tk_app("hotkeys tab exists in notebook") do
|
|
11
|
+
require "gemba/settings_window"
|
|
12
|
+
require "gemba/hotkey_map"
|
|
13
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
14
|
+
sw.show
|
|
15
|
+
app.update
|
|
16
|
+
|
|
17
|
+
tabs = app.command(Gemba::SettingsWindow::NB, 'tabs')
|
|
18
|
+
assert_includes tabs, Gemba::SettingsWindow::HK_TAB
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_hotkey_buttons_show_default_keysyms
|
|
23
|
+
assert_tk_app("hotkey buttons show default keysyms") do
|
|
24
|
+
require "gemba/settings_window"
|
|
25
|
+
require "gemba/hotkey_map"
|
|
26
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
27
|
+
sw.show
|
|
28
|
+
app.update
|
|
29
|
+
|
|
30
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
31
|
+
assert_equal 'q', text
|
|
32
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:pause], 'cget', '-text')
|
|
33
|
+
assert_equal 'p', text
|
|
34
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quick_save], 'cget', '-text')
|
|
35
|
+
assert_equal 'F5', text
|
|
36
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:screenshot], 'cget', '-text')
|
|
37
|
+
assert_equal 'F9', text
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_clicking_hotkey_button_enters_listen_mode
|
|
42
|
+
assert_tk_app("clicking hotkey button enters listen mode") do
|
|
43
|
+
require "gemba/settings_window"
|
|
44
|
+
require "gemba/hotkey_map"
|
|
45
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
46
|
+
sw.show
|
|
47
|
+
app.update
|
|
48
|
+
|
|
49
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
50
|
+
app.update
|
|
51
|
+
|
|
52
|
+
assert_equal :quit, sw.hk_listening_for
|
|
53
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
54
|
+
assert_equal "Press\u2026", text
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_capture_updates_label_and_fires_callback
|
|
59
|
+
assert_tk_app("capturing hotkey updates label and fires callback") do
|
|
60
|
+
require "gemba/settings_window"
|
|
61
|
+
require "gemba/hotkey_map"
|
|
62
|
+
received_action = nil
|
|
63
|
+
received_key = nil
|
|
64
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
65
|
+
on_hotkey_change: proc { |a, k| received_action = a; received_key = k }
|
|
66
|
+
})
|
|
67
|
+
sw.show
|
|
68
|
+
app.update
|
|
69
|
+
|
|
70
|
+
# Click to start listening for quit hotkey
|
|
71
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
72
|
+
app.update
|
|
73
|
+
|
|
74
|
+
# Simulate key capture
|
|
75
|
+
sw.capture_hk_mapping('Escape')
|
|
76
|
+
app.update
|
|
77
|
+
|
|
78
|
+
assert_nil sw.hk_listening_for
|
|
79
|
+
assert_equal :quit, received_action
|
|
80
|
+
assert_equal 'Escape', received_key
|
|
81
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
82
|
+
assert_equal 'Escape', text
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_capture_enables_undo_button
|
|
87
|
+
assert_tk_app("capturing hotkey enables undo button") do
|
|
88
|
+
require "gemba/settings_window"
|
|
89
|
+
require "gemba/hotkey_map"
|
|
90
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
91
|
+
sw.show
|
|
92
|
+
app.update
|
|
93
|
+
|
|
94
|
+
# Initially disabled
|
|
95
|
+
state = app.command(Gemba::SettingsWindow::HK_UNDO_BTN, 'cget', '-state')
|
|
96
|
+
assert_equal 'disabled', state
|
|
97
|
+
|
|
98
|
+
# Rebind
|
|
99
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:pause], 'invoke')
|
|
100
|
+
app.update
|
|
101
|
+
sw.capture_hk_mapping('F12')
|
|
102
|
+
app.update
|
|
103
|
+
|
|
104
|
+
state = app.command(Gemba::SettingsWindow::HK_UNDO_BTN, 'cget', '-state')
|
|
105
|
+
assert_equal 'normal', state
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_undo_fires_callback_and_disables
|
|
110
|
+
assert_tk_app("undo fires on_undo_hotkeys and disables button") do
|
|
111
|
+
require "gemba/settings_window"
|
|
112
|
+
require "gemba/hotkey_map"
|
|
113
|
+
undo_called = false
|
|
114
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
115
|
+
on_undo_hotkeys: proc { undo_called = true }
|
|
116
|
+
})
|
|
117
|
+
sw.show
|
|
118
|
+
app.update
|
|
119
|
+
|
|
120
|
+
# Rebind to enable undo
|
|
121
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
122
|
+
app.update
|
|
123
|
+
sw.capture_hk_mapping('Escape')
|
|
124
|
+
app.update
|
|
125
|
+
|
|
126
|
+
# Click undo
|
|
127
|
+
app.command(Gemba::SettingsWindow::HK_UNDO_BTN, 'invoke')
|
|
128
|
+
app.update
|
|
129
|
+
|
|
130
|
+
assert undo_called
|
|
131
|
+
state = app.command(Gemba::SettingsWindow::HK_UNDO_BTN, 'cget', '-state')
|
|
132
|
+
assert_equal 'disabled', state
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def test_reset_restores_defaults
|
|
137
|
+
assert_tk_app("reset restores default hotkey labels") do
|
|
138
|
+
require "gemba/settings_window"
|
|
139
|
+
require "gemba/hotkey_map"
|
|
140
|
+
reset_called = false
|
|
141
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
142
|
+
on_hotkey_reset: proc { reset_called = true },
|
|
143
|
+
on_confirm_reset_hotkeys: -> { true },
|
|
144
|
+
})
|
|
145
|
+
sw.show
|
|
146
|
+
app.update
|
|
147
|
+
|
|
148
|
+
# Rebind quit
|
|
149
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
150
|
+
app.update
|
|
151
|
+
sw.capture_hk_mapping('Escape')
|
|
152
|
+
app.update
|
|
153
|
+
|
|
154
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
155
|
+
assert_equal 'Escape', text
|
|
156
|
+
|
|
157
|
+
# Click Reset to Defaults
|
|
158
|
+
app.command(Gemba::SettingsWindow::HK_RESET_BTN, 'invoke')
|
|
159
|
+
app.update
|
|
160
|
+
|
|
161
|
+
assert reset_called
|
|
162
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
163
|
+
assert_equal 'q', text
|
|
164
|
+
state = app.command(Gemba::SettingsWindow::HK_UNDO_BTN, 'cget', '-state')
|
|
165
|
+
assert_equal 'disabled', state
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def test_refresh_hotkeys_updates_labels
|
|
170
|
+
assert_tk_app("refresh_hotkeys updates button labels") do
|
|
171
|
+
require "gemba/settings_window"
|
|
172
|
+
require "gemba/hotkey_map"
|
|
173
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
174
|
+
sw.show
|
|
175
|
+
app.update
|
|
176
|
+
|
|
177
|
+
new_labels = Gemba::HotkeyMap::DEFAULTS.merge(quit: 'Escape', pause: 'F12')
|
|
178
|
+
sw.refresh_hotkeys(new_labels)
|
|
179
|
+
app.update
|
|
180
|
+
|
|
181
|
+
assert_equal 'Escape', app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
182
|
+
assert_equal 'F12', app.command(Gemba::SettingsWindow::HK_ACTIONS[:pause], 'cget', '-text')
|
|
183
|
+
# Unchanged bindings stay the same
|
|
184
|
+
assert_equal 'Tab', app.command(Gemba::SettingsWindow::HK_ACTIONS[:fast_forward], 'cget', '-text')
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def test_cancel_listen_restores_label
|
|
189
|
+
assert_tk_app("canceling listen restores original label") do
|
|
190
|
+
require "gemba/settings_window"
|
|
191
|
+
require "gemba/hotkey_map"
|
|
192
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
193
|
+
sw.show
|
|
194
|
+
app.update
|
|
195
|
+
|
|
196
|
+
# Enter listen for quit
|
|
197
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
198
|
+
app.update
|
|
199
|
+
assert_equal :quit, sw.hk_listening_for
|
|
200
|
+
|
|
201
|
+
# Start listening for a different one — cancels the first
|
|
202
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:pause], 'invoke')
|
|
203
|
+
app.update
|
|
204
|
+
|
|
205
|
+
assert_equal :pause, sw.hk_listening_for
|
|
206
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
207
|
+
assert_equal 'q', text, "Original quit label should be restored"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_capture_without_listen_is_noop
|
|
212
|
+
assert_tk_app("capture without listen mode is a no-op") do
|
|
213
|
+
require "gemba/settings_window"
|
|
214
|
+
require "gemba/hotkey_map"
|
|
215
|
+
received = false
|
|
216
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
217
|
+
on_hotkey_change: proc { |*| received = true }
|
|
218
|
+
})
|
|
219
|
+
sw.show
|
|
220
|
+
app.update
|
|
221
|
+
|
|
222
|
+
# Capture without entering listen mode
|
|
223
|
+
sw.capture_hk_mapping('F12')
|
|
224
|
+
app.update
|
|
225
|
+
|
|
226
|
+
refute received
|
|
227
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
228
|
+
assert_equal 'q', text, "Label should be unchanged"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# -- Conflict validation ---------------------------------------------------
|
|
233
|
+
|
|
234
|
+
def test_hotkey_rejected_when_conflicting_with_gamepad_key
|
|
235
|
+
assert_tk_app("hotkey rejected when key conflicts with gamepad mapping") do
|
|
236
|
+
require "gemba/settings_window"
|
|
237
|
+
require "gemba/hotkey_map"
|
|
238
|
+
received = false
|
|
239
|
+
conflict_msg = nil
|
|
240
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
241
|
+
on_hotkey_change: proc { |*| received = true },
|
|
242
|
+
on_validate_hotkey: ->(keysym) {
|
|
243
|
+
# Simulate: 'z' is GBA button A
|
|
244
|
+
keysym == 'z' ? '"z" is mapped to GBA button A' : nil
|
|
245
|
+
},
|
|
246
|
+
on_key_conflict: proc { |msg| conflict_msg = msg },
|
|
247
|
+
})
|
|
248
|
+
sw.show
|
|
249
|
+
app.update
|
|
250
|
+
|
|
251
|
+
# Try to bind quit to 'z' (conflicts with GBA A)
|
|
252
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
253
|
+
app.update
|
|
254
|
+
sw.capture_hk_mapping('z')
|
|
255
|
+
app.update
|
|
256
|
+
|
|
257
|
+
refute received, "on_hotkey_change should not fire for rejected key"
|
|
258
|
+
assert_equal '"z" is mapped to GBA button A', conflict_msg
|
|
259
|
+
# Label should revert to original, not show 'z'
|
|
260
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
261
|
+
assert_equal 'q', text
|
|
262
|
+
assert_nil sw.hk_listening_for
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def test_hotkey_accepted_when_no_conflict
|
|
267
|
+
assert_tk_app("hotkey accepted when no conflict") do
|
|
268
|
+
require "gemba/settings_window"
|
|
269
|
+
require "gemba/hotkey_map"
|
|
270
|
+
received_action = nil
|
|
271
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
272
|
+
on_hotkey_change: proc { |a, _| received_action = a },
|
|
273
|
+
on_validate_hotkey: ->(_) { nil },
|
|
274
|
+
})
|
|
275
|
+
sw.show
|
|
276
|
+
app.update
|
|
277
|
+
|
|
278
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
279
|
+
app.update
|
|
280
|
+
sw.capture_hk_mapping('F12')
|
|
281
|
+
app.update
|
|
282
|
+
|
|
283
|
+
assert_equal :quit, received_action
|
|
284
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
285
|
+
assert_equal 'F12', text
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# -- Modifier combo capture -----------------------------------------------
|
|
290
|
+
|
|
291
|
+
def test_capture_modifier_then_key_produces_combo
|
|
292
|
+
assert_tk_app("modifier + key produces combo hotkey") do
|
|
293
|
+
require "gemba/settings_window"
|
|
294
|
+
require "gemba/hotkey_map"
|
|
295
|
+
received_action = nil
|
|
296
|
+
received_hk = nil
|
|
297
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
298
|
+
on_hotkey_change: proc { |a, hk| received_action = a; received_hk = hk },
|
|
299
|
+
})
|
|
300
|
+
sw.show
|
|
301
|
+
app.update
|
|
302
|
+
|
|
303
|
+
# Enter listen mode for quit
|
|
304
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
305
|
+
app.update
|
|
306
|
+
|
|
307
|
+
# Simulate pressing Control_L (modifier), then 'k' (non-modifier)
|
|
308
|
+
sw.capture_hk_mapping('Control_L')
|
|
309
|
+
sw.capture_hk_mapping('k')
|
|
310
|
+
app.update
|
|
311
|
+
|
|
312
|
+
assert_equal :quit, received_action
|
|
313
|
+
assert_equal ['Control', 'k'], received_hk
|
|
314
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
315
|
+
assert_equal 'Ctrl+K', text
|
|
316
|
+
assert_nil sw.hk_listening_for
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def test_capture_multi_modifier_combo
|
|
321
|
+
assert_tk_app("multi-modifier combo (Ctrl+Shift+S)") do
|
|
322
|
+
require "gemba/settings_window"
|
|
323
|
+
require "gemba/hotkey_map"
|
|
324
|
+
received_hk = nil
|
|
325
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
326
|
+
on_hotkey_change: proc { |_, hk| received_hk = hk },
|
|
327
|
+
})
|
|
328
|
+
sw.show
|
|
329
|
+
app.update
|
|
330
|
+
|
|
331
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:screenshot], 'invoke')
|
|
332
|
+
app.update
|
|
333
|
+
|
|
334
|
+
sw.capture_hk_mapping('Control_L')
|
|
335
|
+
sw.capture_hk_mapping('Shift_L')
|
|
336
|
+
sw.capture_hk_mapping('s')
|
|
337
|
+
app.update
|
|
338
|
+
|
|
339
|
+
assert_equal ['Control', 'Shift', 's'], received_hk
|
|
340
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:screenshot], 'cget', '-text')
|
|
341
|
+
assert_equal 'Ctrl+Shift+S', text
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def test_combo_hotkey_skips_gamepad_conflict_validation
|
|
346
|
+
assert_tk_app("combo hotkey skips gamepad conflict validation") do
|
|
347
|
+
require "gemba/settings_window"
|
|
348
|
+
require "gemba/hotkey_map"
|
|
349
|
+
received_hk = nil
|
|
350
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
351
|
+
on_hotkey_change: proc { |_, hk| received_hk = hk },
|
|
352
|
+
# 'z' conflicts as a plain key, but Ctrl+z should be fine
|
|
353
|
+
on_validate_hotkey: ->(key) {
|
|
354
|
+
key == 'z' ? '"z" is mapped to GBA button A' : nil
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
sw.show
|
|
358
|
+
app.update
|
|
359
|
+
|
|
360
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
361
|
+
app.update
|
|
362
|
+
|
|
363
|
+
sw.capture_hk_mapping('Control_L')
|
|
364
|
+
sw.capture_hk_mapping('z')
|
|
365
|
+
app.update
|
|
366
|
+
|
|
367
|
+
assert_equal ['Control', 'z'], received_hk, "Ctrl+Z combo should bypass plain-key conflict"
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def test_refresh_hotkeys_shows_combo_display_name
|
|
372
|
+
assert_tk_app("refresh_hotkeys shows combo display name") do
|
|
373
|
+
require "gemba/settings_window"
|
|
374
|
+
require "gemba/hotkey_map"
|
|
375
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
376
|
+
sw.show
|
|
377
|
+
app.update
|
|
378
|
+
|
|
379
|
+
new_labels = Gemba::HotkeyMap::DEFAULTS.merge(quit: ['Control', 'q'])
|
|
380
|
+
sw.refresh_hotkeys(new_labels)
|
|
381
|
+
app.update
|
|
382
|
+
|
|
383
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
384
|
+
assert_equal 'Ctrl+Q', text
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def test_bind_script_modifier_combo_roundtrip
|
|
389
|
+
assert_tk_app("Tcl bind script round-trip with modifier+key combo") do
|
|
390
|
+
require "gemba/settings_window"
|
|
391
|
+
require "gemba/hotkey_map"
|
|
392
|
+
received_hk = nil
|
|
393
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {
|
|
394
|
+
on_hotkey_change: proc { |_, hk| received_hk = hk },
|
|
395
|
+
})
|
|
396
|
+
sw.show
|
|
397
|
+
app.update
|
|
398
|
+
|
|
399
|
+
# Enter listen mode
|
|
400
|
+
top = Gemba::SettingsWindow::TOP
|
|
401
|
+
app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'invoke')
|
|
402
|
+
app.update
|
|
403
|
+
|
|
404
|
+
# Verify the <Key> bind script was installed on the toplevel
|
|
405
|
+
bind_script = app.tcl_eval("bind #{top} <Key>")
|
|
406
|
+
refute_empty bind_script, "Key binding should be set during listen mode"
|
|
407
|
+
assert_match(/ruby_callback/, bind_script)
|
|
408
|
+
|
|
409
|
+
# Simulate what Tk does on key events: evaluate the bind script
|
|
410
|
+
# with %K substituted to the keysym value
|
|
411
|
+
app.tcl_eval(bind_script.gsub('%K', 'Control_L'))
|
|
412
|
+
app.update
|
|
413
|
+
app.tcl_eval(bind_script.gsub('%K', 'k'))
|
|
414
|
+
app.update
|
|
415
|
+
|
|
416
|
+
assert_equal ['Control', 'k'], received_hk
|
|
417
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:quit], 'cget', '-text')
|
|
418
|
+
assert_equal 'Ctrl+K', text
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def test_record_hotkey_button_shows_default
|
|
423
|
+
assert_tk_app("record hotkey button shows F10") do
|
|
424
|
+
require "gemba/settings_window"
|
|
425
|
+
require "gemba/hotkey_map"
|
|
426
|
+
sw = Gemba::SettingsWindow.new(app, callbacks: {})
|
|
427
|
+
sw.show
|
|
428
|
+
app.update
|
|
429
|
+
|
|
430
|
+
text = app.command(Gemba::SettingsWindow::HK_ACTIONS[:record], 'cget', '-text')
|
|
431
|
+
assert_equal 'F10', text
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
end
|