teek-ui 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/CHANGELOG.md +41 -0
- data/README.md +452 -0
- data/lib/teek/ui/canvas_item.rb +270 -0
- data/lib/teek/ui/component_handle.rb +32 -0
- data/lib/teek/ui/document.rb +185 -0
- data/lib/teek/ui/errors.rb +41 -0
- data/lib/teek/ui/event_binding.rb +22 -0
- data/lib/teek/ui/event_bus.rb +53 -0
- data/lib/teek/ui/grid_validator.rb +89 -0
- data/lib/teek/ui/handle.rb +585 -0
- data/lib/teek/ui/image.rb +58 -0
- data/lib/teek/ui/keysyms.rb +72 -0
- data/lib/teek/ui/menu_builder.rb +130 -0
- data/lib/teek/ui/menu_entry_addressing.rb +62 -0
- data/lib/teek/ui/modal_stack.rb +99 -0
- data/lib/teek/ui/mouse_events.rb +31 -0
- data/lib/teek/ui/node.rb +141 -0
- data/lib/teek/ui/option_dump_parsing.rb +31 -0
- data/lib/teek/ui/overlay_anchors.rb +29 -0
- data/lib/teek/ui/overlay_validator.rb +33 -0
- data/lib/teek/ui/pane_validator.rb +30 -0
- data/lib/teek/ui/realized_node.rb +25 -0
- data/lib/teek/ui/realizer.rb +577 -0
- data/lib/teek/ui/scope.rb +40 -0
- data/lib/teek/ui/screens.rb +141 -0
- data/lib/teek/ui/session.rb +417 -0
- data/lib/teek/ui/tab_validator.rb +30 -0
- data/lib/teek/ui/text_content.rb +321 -0
- data/lib/teek/ui/tree_inspector.rb +97 -0
- data/lib/teek/ui/validator.rb +131 -0
- data/lib/teek/ui/var.rb +98 -0
- data/lib/teek/ui/version.rb +7 -0
- data/lib/teek/ui/widget_addressing.rb +47 -0
- data/lib/teek/ui/widget_dsl.rb +512 -0
- data/lib/teek/ui/widget_type.rb +242 -0
- data/lib/teek/ui/widget_types/button.rb +7 -0
- data/lib/teek/ui/widget_types/canvas.rb +15 -0
- data/lib/teek/ui/widget_types/checkbox.rb +7 -0
- data/lib/teek/ui/widget_types/column.rb +14 -0
- data/lib/teek/ui/widget_types/context_menu.rb +15 -0
- data/lib/teek/ui/widget_types/divider.rb +10 -0
- data/lib/teek/ui/widget_types/dropdown.rb +7 -0
- data/lib/teek/ui/widget_types/grid.rb +12 -0
- data/lib/teek/ui/widget_types/group.rb +7 -0
- data/lib/teek/ui/widget_types/label.rb +7 -0
- data/lib/teek/ui/widget_types/list.rb +7 -0
- data/lib/teek/ui/widget_types/menu_bar.rb +26 -0
- data/lib/teek/ui/widget_types/menu_checkbox.rb +14 -0
- data/lib/teek/ui/widget_types/menu_item.rb +20 -0
- data/lib/teek/ui/widget_types/menu_radio.rb +14 -0
- data/lib/teek/ui/widget_types/number_box.rb +7 -0
- data/lib/teek/ui/widget_types/pane.rb +37 -0
- data/lib/teek/ui/widget_types/panel.rb +7 -0
- data/lib/teek/ui/widget_types/progress.rb +7 -0
- data/lib/teek/ui/widget_types/radio.rb +7 -0
- data/lib/teek/ui/widget_types/row.rb +14 -0
- data/lib/teek/ui/widget_types/scrollable.rb +17 -0
- data/lib/teek/ui/widget_types/slider.rb +7 -0
- data/lib/teek/ui/widget_types/spacer.rb +14 -0
- data/lib/teek/ui/widget_types/split.rb +17 -0
- data/lib/teek/ui/widget_types/tab.rb +35 -0
- data/lib/teek/ui/widget_types/table.rb +9 -0
- data/lib/teek/ui/widget_types/tabs.rb +10 -0
- data/lib/teek/ui/widget_types/text_area.rb +7 -0
- data/lib/teek/ui/widget_types/text_box.rb +7 -0
- data/lib/teek/ui/widget_types/tree.rb +9 -0
- data/lib/teek/ui/widget_types/window.rb +50 -0
- data/lib/teek/ui/widget_types.rb +121 -0
- data/lib/teek/ui/widget_validators.rb +62 -0
- data/lib/teek/ui.rb +54 -0
- data/teek-ui.gemspec +24 -0
- data/test/support/fake_app.rb +57 -0
- data/test/test_busy.rb +82 -0
- data/test/test_canvas_items.rb +551 -0
- data/test/test_clipboard.rb +87 -0
- data/test/test_close_handling.rb +118 -0
- data/test/test_component.rb +151 -0
- data/test/test_component_realtk.rb +120 -0
- data/test/test_debug_info.rb +158 -0
- data/test/test_document.rb +317 -0
- data/test/test_event_bus.rb +163 -0
- data/test/test_events.rb +164 -0
- data/test/test_fake_app_contract.rb +62 -0
- data/test/test_grid.rb +97 -0
- data/test/test_handle.rb +386 -0
- data/test/test_handle_destroy_realtk.rb +344 -0
- data/test/test_helper.rb +20 -0
- data/test/test_image.rb +24 -0
- data/test/test_image_realtk.rb +146 -0
- data/test/test_incremental_realize.rb +203 -0
- data/test/test_keysyms.rb +76 -0
- data/test/test_layout.rb +185 -0
- data/test/test_lazy_realize.rb +128 -0
- data/test/test_managed_window.rb +304 -0
- data/test/test_menu_dsl.rb +230 -0
- data/test/test_menu_entry_addressing.rb +73 -0
- data/test/test_menu_realize.rb +296 -0
- data/test/test_modal_handle.rb +147 -0
- data/test/test_modal_stack.rb +219 -0
- data/test/test_modal_stack_realtk.rb +131 -0
- data/test/test_native_scrollable.rb +250 -0
- data/test/test_node.rb +193 -0
- data/test/test_overlay.rb +139 -0
- data/test/test_raw.rb +58 -0
- data/test/test_reactive_vars.rb +118 -0
- data/test/test_realizer.rb +264 -0
- data/test/test_scope.rb +38 -0
- data/test/test_screens.rb +260 -0
- data/test/test_screens_realtk.rb +314 -0
- data/test/test_scrollable.rb +185 -0
- data/test/test_scrollable_wheel.rb +172 -0
- data/test/test_scrollbar_auto_hide.rb +127 -0
- data/test/test_split.rb +145 -0
- data/test/test_tabs.rb +201 -0
- data/test/test_text_content.rb +664 -0
- data/test/test_toast.rb +126 -0
- data/test/test_tree_inspector.rb +183 -0
- data/test/test_ui.rb +428 -0
- data/test/test_validator.rb +209 -0
- data/test/test_var.rb +32 -0
- data/test/test_widget_dsl.rb +621 -0
- data/test/test_widget_introspection.rb +126 -0
- data/test/test_widget_types.rb +429 -0
- data/test/test_widget_types_realtk.rb +82 -0
- data/test/test_widget_validators.rb +82 -0
- metadata +207 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
require 'teek/ui/screens'
|
|
5
|
+
|
|
6
|
+
# Pure stack-bookkeeping tests, no Tk involved - Screens' reveal/conceal
|
|
7
|
+
# logic (pack/pack-forget for a panel, show/hide for a window) is exercised
|
|
8
|
+
# against real Tk widgets in test_screens_realtk.rb instead.
|
|
9
|
+
class TestScreens < Minitest::Test
|
|
10
|
+
FakePanelScreen = Struct.new(:app, :path) do
|
|
11
|
+
def type
|
|
12
|
+
:panel
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
FakeWindowScreen = Struct.new(:shows, :hides) do
|
|
17
|
+
def initialize(shows = [], hides = [])
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def type
|
|
22
|
+
:window
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def show
|
|
26
|
+
shows << true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def hide
|
|
30
|
+
hides << true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
FakeLazyScreen = Struct.new(:app, :path, :realized_flag, :realize_calls) do
|
|
35
|
+
def initialize(app, path, realized_flag = false)
|
|
36
|
+
super(app, path, realized_flag, [])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def type
|
|
40
|
+
:panel
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def realized?
|
|
44
|
+
realized_flag
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def realize!(document)
|
|
48
|
+
realize_calls << document
|
|
49
|
+
self.realized_flag = true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_starts_empty
|
|
54
|
+
screens = Teek::UI::Screens.new
|
|
55
|
+
|
|
56
|
+
refute screens.active?
|
|
57
|
+
assert_nil screens.current
|
|
58
|
+
assert_nil screens.current_screen
|
|
59
|
+
assert_equal 0, screens.size
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_push_activates_and_tracks_current
|
|
63
|
+
screens = Teek::UI::Screens.new
|
|
64
|
+
picker = FakePanelScreen.new(FakeApp.new, '.picker')
|
|
65
|
+
|
|
66
|
+
screens.push(:picker, picker)
|
|
67
|
+
|
|
68
|
+
assert screens.active?
|
|
69
|
+
assert_equal :picker, screens.current
|
|
70
|
+
assert_same picker, screens.current_screen
|
|
71
|
+
assert_equal 1, screens.size
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_push_reveals_a_panel_screen_by_packing_it_to_fill_its_parent
|
|
75
|
+
screens = Teek::UI::Screens.new
|
|
76
|
+
app = FakeApp.new
|
|
77
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
78
|
+
|
|
79
|
+
screens.push(:picker, picker)
|
|
80
|
+
|
|
81
|
+
assert_equal [[[:pack, '.picker'], { fill: :both, expand: 1 }]], app.calls
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_push_reveals_a_window_screen_via_show
|
|
85
|
+
screens = Teek::UI::Screens.new
|
|
86
|
+
settings = FakeWindowScreen.new
|
|
87
|
+
|
|
88
|
+
screens.push(:settings, settings)
|
|
89
|
+
|
|
90
|
+
assert_equal [true], settings.shows
|
|
91
|
+
assert_equal [], settings.hides
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_pushing_a_second_screen_conceals_the_first
|
|
95
|
+
screens = Teek::UI::Screens.new
|
|
96
|
+
app = FakeApp.new
|
|
97
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
98
|
+
emulator = FakePanelScreen.new(app, '.emulator')
|
|
99
|
+
|
|
100
|
+
screens.push(:picker, picker)
|
|
101
|
+
screens.push(:emulator, emulator)
|
|
102
|
+
|
|
103
|
+
assert_equal :emulator, screens.current
|
|
104
|
+
assert_equal 2, screens.size
|
|
105
|
+
assert_equal [
|
|
106
|
+
[[:pack, '.picker'], { fill: :both, expand: 1 }],
|
|
107
|
+
[[:pack, :forget, '.picker'], {}],
|
|
108
|
+
[[:pack, '.emulator'], { fill: :both, expand: 1 }],
|
|
109
|
+
], app.calls
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_pop_conceals_the_current_and_reveals_the_one_underneath
|
|
113
|
+
screens = Teek::UI::Screens.new
|
|
114
|
+
app = FakeApp.new
|
|
115
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
116
|
+
emulator = FakePanelScreen.new(app, '.emulator')
|
|
117
|
+
screens.push(:picker, picker)
|
|
118
|
+
screens.push(:emulator, emulator)
|
|
119
|
+
app.calls.clear
|
|
120
|
+
|
|
121
|
+
screens.pop
|
|
122
|
+
|
|
123
|
+
assert_equal :picker, screens.current
|
|
124
|
+
assert_equal 1, screens.size
|
|
125
|
+
assert_equal [
|
|
126
|
+
[[:pack, :forget, '.emulator'], {}],
|
|
127
|
+
[[:pack, '.picker'], { fill: :both, expand: 1 }],
|
|
128
|
+
], app.calls
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_pop_on_the_last_screen_leaves_the_stack_inactive
|
|
132
|
+
screens = Teek::UI::Screens.new
|
|
133
|
+
picker = FakePanelScreen.new(FakeApp.new, '.picker')
|
|
134
|
+
screens.push(:picker, picker)
|
|
135
|
+
|
|
136
|
+
screens.pop
|
|
137
|
+
|
|
138
|
+
refute screens.active?
|
|
139
|
+
assert_nil screens.current
|
|
140
|
+
assert_equal 0, screens.size
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_pop_on_an_empty_stack_is_a_safe_no_op
|
|
144
|
+
screens = Teek::UI::Screens.new
|
|
145
|
+
|
|
146
|
+
screens.pop
|
|
147
|
+
|
|
148
|
+
refute screens.active?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_replace_current_swaps_the_screen_but_keeps_the_name_and_depth
|
|
152
|
+
screens = Teek::UI::Screens.new
|
|
153
|
+
app = FakeApp.new
|
|
154
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
155
|
+
other_picker = FakePanelScreen.new(app, '.other_picker')
|
|
156
|
+
screens.push(:picker, picker)
|
|
157
|
+
app.calls.clear
|
|
158
|
+
|
|
159
|
+
screens.replace_current(other_picker)
|
|
160
|
+
|
|
161
|
+
assert_equal :picker, screens.current
|
|
162
|
+
assert_same other_picker, screens.current_screen
|
|
163
|
+
assert_equal 1, screens.size
|
|
164
|
+
assert_equal [
|
|
165
|
+
[[:pack, :forget, '.picker'], {}],
|
|
166
|
+
[[:pack, '.other_picker'], { fill: :both, expand: 1 }],
|
|
167
|
+
], app.calls
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_replace_current_on_an_empty_stack_is_a_safe_no_op
|
|
171
|
+
screens = Teek::UI::Screens.new
|
|
172
|
+
app = FakeApp.new
|
|
173
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
174
|
+
|
|
175
|
+
screens.replace_current(picker)
|
|
176
|
+
|
|
177
|
+
refute screens.active?
|
|
178
|
+
assert_equal [], app.calls
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_a_three_deep_push_pop_push_sequence_restores_the_correct_screen_at_each_step
|
|
182
|
+
screens = Teek::UI::Screens.new
|
|
183
|
+
app = FakeApp.new
|
|
184
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
185
|
+
emulator = FakePanelScreen.new(app, '.emulator')
|
|
186
|
+
settings = FakePanelScreen.new(app, '.settings')
|
|
187
|
+
|
|
188
|
+
screens.push(:picker, picker)
|
|
189
|
+
assert_equal :picker, screens.current
|
|
190
|
+
assert_equal 1, screens.size
|
|
191
|
+
|
|
192
|
+
screens.push(:emulator, emulator)
|
|
193
|
+
assert_equal :emulator, screens.current
|
|
194
|
+
assert_equal 2, screens.size
|
|
195
|
+
|
|
196
|
+
screens.push(:settings, settings)
|
|
197
|
+
assert_equal :settings, screens.current
|
|
198
|
+
assert_equal 3, screens.size
|
|
199
|
+
|
|
200
|
+
screens.pop
|
|
201
|
+
assert_equal :emulator, screens.current
|
|
202
|
+
assert_equal 2, screens.size
|
|
203
|
+
|
|
204
|
+
screens.pop
|
|
205
|
+
assert_equal :picker, screens.current
|
|
206
|
+
assert_equal 1, screens.size
|
|
207
|
+
|
|
208
|
+
screens.push(:settings, settings)
|
|
209
|
+
assert_equal :settings, screens.current
|
|
210
|
+
assert_equal 2, screens.size
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def test_push_realizes_a_not_yet_realized_screen_before_revealing
|
|
214
|
+
screens = Teek::UI::Screens.new(document: :the_document)
|
|
215
|
+
app = FakeApp.new
|
|
216
|
+
picker = FakeLazyScreen.new(app, '.picker', false)
|
|
217
|
+
|
|
218
|
+
screens.push(:picker, picker)
|
|
219
|
+
|
|
220
|
+
assert_equal [:the_document], picker.realize_calls
|
|
221
|
+
assert_equal [[[:pack, '.picker'], { fill: :both, expand: 1 }]], app.calls
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def test_push_does_not_realize_an_already_realized_screen
|
|
225
|
+
screens = Teek::UI::Screens.new(document: :the_document)
|
|
226
|
+
app = FakeApp.new
|
|
227
|
+
picker = FakeLazyScreen.new(app, '.picker', true)
|
|
228
|
+
|
|
229
|
+
screens.push(:picker, picker)
|
|
230
|
+
|
|
231
|
+
assert_equal [], picker.realize_calls
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def test_push_skips_the_lazy_check_for_a_screen_that_has_no_opinion_on_it
|
|
235
|
+
# matches every FakePanelScreen/FakeWindowScreen test above - a screen
|
|
236
|
+
# with no realized?/realize! at all (the plain "already-built Handle"
|
|
237
|
+
# usage) behaves exactly as it always has, no document: needed either.
|
|
238
|
+
screens = Teek::UI::Screens.new
|
|
239
|
+
app = FakeApp.new
|
|
240
|
+
picker = FakePanelScreen.new(app, '.picker')
|
|
241
|
+
|
|
242
|
+
screens.push(:picker, picker)
|
|
243
|
+
|
|
244
|
+
assert screens.active?
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def test_pop_returns_the_popped_screen
|
|
248
|
+
screens = Teek::UI::Screens.new
|
|
249
|
+
picker = FakePanelScreen.new(FakeApp.new, '.picker')
|
|
250
|
+
screens.push(:picker, picker)
|
|
251
|
+
|
|
252
|
+
assert_same picker, screens.pop
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def test_pop_on_an_empty_stack_returns_nil
|
|
256
|
+
screens = Teek::UI::Screens.new
|
|
257
|
+
|
|
258
|
+
assert_nil screens.pop
|
|
259
|
+
end
|
|
260
|
+
end
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minitest/autorun'
|
|
4
|
+
require_relative '../../test/tk_test_helper'
|
|
5
|
+
|
|
6
|
+
# Real-Tk half of Screens' coverage - pure stack bookkeeping (current/size/
|
|
7
|
+
# active?, a push-pop-push sequence) is covered headlessly in
|
|
8
|
+
# test_screens.rb; these exercise the actual reveal/conceal side effects
|
|
9
|
+
# (pack/pack-forget for a panel, show/hide - including modal grab/release -
|
|
10
|
+
# for a window) against real widgets.
|
|
11
|
+
class TestScreensRealTk < Minitest::Test
|
|
12
|
+
include TeekTestHelper
|
|
13
|
+
|
|
14
|
+
def test_push_reveals_a_panel_screen
|
|
15
|
+
assert_tk_app("pushing a panel screen should pack it, mapping it") do
|
|
16
|
+
require 'teek/ui'
|
|
17
|
+
|
|
18
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
19
|
+
ui.panel(:picker)
|
|
20
|
+
ui.panel(:emulator)
|
|
21
|
+
end
|
|
22
|
+
session.run_async
|
|
23
|
+
session.app.update
|
|
24
|
+
|
|
25
|
+
session.screens.push(:picker, session[:picker])
|
|
26
|
+
session.app.update
|
|
27
|
+
|
|
28
|
+
assert session.app.winfo.ismapped?(session[:picker].path)
|
|
29
|
+
|
|
30
|
+
session.app.destroy
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_pushing_a_second_panel_screen_conceals_the_first
|
|
35
|
+
assert_tk_app("pushing a second screen should pack-forget the previous one") do
|
|
36
|
+
require 'teek/ui'
|
|
37
|
+
|
|
38
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
39
|
+
ui.panel(:picker)
|
|
40
|
+
ui.panel(:emulator)
|
|
41
|
+
end
|
|
42
|
+
session.run_async
|
|
43
|
+
session.app.update
|
|
44
|
+
|
|
45
|
+
session.screens.push(:picker, session[:picker])
|
|
46
|
+
session.app.update
|
|
47
|
+
session.screens.push(:emulator, session[:emulator])
|
|
48
|
+
session.app.update
|
|
49
|
+
|
|
50
|
+
refute session.app.winfo.ismapped?(session[:picker].path)
|
|
51
|
+
assert session.app.winfo.ismapped?(session[:emulator].path)
|
|
52
|
+
|
|
53
|
+
session.app.destroy
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_pop_conceals_the_current_panel_and_reveals_the_one_underneath
|
|
58
|
+
assert_tk_app("pop should hide the topmost screen and re-show whatever's underneath") do
|
|
59
|
+
require 'teek/ui'
|
|
60
|
+
|
|
61
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
62
|
+
ui.panel(:picker)
|
|
63
|
+
ui.panel(:emulator)
|
|
64
|
+
end
|
|
65
|
+
session.run_async
|
|
66
|
+
session.app.update
|
|
67
|
+
|
|
68
|
+
session.screens.push(:picker, session[:picker])
|
|
69
|
+
session.screens.push(:emulator, session[:emulator])
|
|
70
|
+
session.app.update
|
|
71
|
+
|
|
72
|
+
session.screens.pop
|
|
73
|
+
session.app.update
|
|
74
|
+
|
|
75
|
+
assert session.app.winfo.ismapped?(session[:picker].path)
|
|
76
|
+
refute session.app.winfo.ismapped?(session[:emulator].path)
|
|
77
|
+
assert_equal :picker, session.screens.current
|
|
78
|
+
|
|
79
|
+
session.app.destroy
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_replace_current_swaps_a_panel_screen_in_place
|
|
84
|
+
assert_tk_app("replace_current should hide the old panel, show the new one, keep the same name/depth") do
|
|
85
|
+
require 'teek/ui'
|
|
86
|
+
|
|
87
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
88
|
+
ui.panel(:picker)
|
|
89
|
+
ui.panel(:other_picker)
|
|
90
|
+
end
|
|
91
|
+
session.run_async
|
|
92
|
+
session.app.update
|
|
93
|
+
|
|
94
|
+
session.screens.push(:picker, session[:picker])
|
|
95
|
+
session.app.update
|
|
96
|
+
|
|
97
|
+
session.screens.replace_current(session[:other_picker])
|
|
98
|
+
session.app.update
|
|
99
|
+
|
|
100
|
+
refute session.app.winfo.ismapped?(session[:picker].path)
|
|
101
|
+
assert session.app.winfo.ismapped?(session[:other_picker].path)
|
|
102
|
+
assert_equal :picker, session.screens.current
|
|
103
|
+
assert_equal 1, session.screens.size
|
|
104
|
+
|
|
105
|
+
session.app.destroy
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_push_reveals_a_window_screen_via_show
|
|
110
|
+
assert_tk_app("pushing a window screen should show it (deiconified/mapped)") do
|
|
111
|
+
require 'teek/ui'
|
|
112
|
+
|
|
113
|
+
session = Teek::UI.app(title: 'Screens Test') { |ui| ui.window(:settings) }
|
|
114
|
+
session.run_async
|
|
115
|
+
session.app.update
|
|
116
|
+
|
|
117
|
+
refute session.app.winfo.ismapped?(session[:settings].path), "should start withdrawn"
|
|
118
|
+
|
|
119
|
+
session.screens.push(:settings, session[:settings])
|
|
120
|
+
session.app.update
|
|
121
|
+
|
|
122
|
+
assert session.app.winfo.ismapped?(session[:settings].path)
|
|
123
|
+
|
|
124
|
+
session.app.destroy
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_pop_conceals_a_window_screen_via_hide
|
|
129
|
+
assert_tk_app("popping a window screen should withdraw it") do
|
|
130
|
+
require 'teek/ui'
|
|
131
|
+
|
|
132
|
+
session = Teek::UI.app(title: 'Screens Test') { |ui| ui.window(:settings) }
|
|
133
|
+
session.run_async
|
|
134
|
+
session.app.update
|
|
135
|
+
|
|
136
|
+
session.screens.push(:settings, session[:settings])
|
|
137
|
+
session.app.update
|
|
138
|
+
|
|
139
|
+
session.screens.pop
|
|
140
|
+
session.app.update
|
|
141
|
+
|
|
142
|
+
refute session.app.winfo.ismapped?(session[:settings].path)
|
|
143
|
+
|
|
144
|
+
session.app.destroy
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def test_modal_window_screen_grabs_when_pushed_and_releases_when_popped
|
|
149
|
+
assert_tk_app("a modal: true window screen should grab on push, release on pop") do
|
|
150
|
+
require 'teek/ui'
|
|
151
|
+
|
|
152
|
+
session = Teek::UI.app(title: 'Screens Test') { |ui| ui.window(:settings, modal: true) }
|
|
153
|
+
session.run_async
|
|
154
|
+
session.app.update
|
|
155
|
+
|
|
156
|
+
session.screens.push(:settings, session[:settings])
|
|
157
|
+
session.app.update
|
|
158
|
+
|
|
159
|
+
path = session[:settings].path
|
|
160
|
+
assert_equal path, session.app.tcl_eval("grab current #{path}")
|
|
161
|
+
|
|
162
|
+
session.screens.pop
|
|
163
|
+
session.app.update
|
|
164
|
+
|
|
165
|
+
assert_equal '', session.app.tcl_eval("grab current #{path}")
|
|
166
|
+
|
|
167
|
+
session.app.destroy
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def test_pushing_a_not_yet_realized_lazy_panel_screen_realizes_it_then_reveals_it
|
|
172
|
+
assert_tk_app("pushing a lazy: true panel screen should realize it on demand, then pack it") do
|
|
173
|
+
require 'teek/ui'
|
|
174
|
+
|
|
175
|
+
handle = nil
|
|
176
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
177
|
+
ui.panel(:host) { |p| handle = p.panel(:picker, lazy: true) { |pk| pk.button(:load, text: 'Load') } }
|
|
178
|
+
end
|
|
179
|
+
session.run_async
|
|
180
|
+
session.app.update
|
|
181
|
+
|
|
182
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.path }
|
|
183
|
+
|
|
184
|
+
session.screens.push(:picker, handle)
|
|
185
|
+
session.app.update
|
|
186
|
+
|
|
187
|
+
assert session.app.winfo.ismapped?(handle.path)
|
|
188
|
+
assert session.app.winfo.exists?("#{handle.path}.load")
|
|
189
|
+
|
|
190
|
+
session.app.destroy
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def test_pushing_the_same_lazy_screen_again_after_a_pop_does_not_re_realize_it
|
|
195
|
+
assert_tk_app("popping a lazy screen (which only conceals, doesn't destroy) and pushing it again should just re-reveal the same live widget, not rebuild it") do
|
|
196
|
+
require 'teek/ui'
|
|
197
|
+
|
|
198
|
+
handle = nil
|
|
199
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
200
|
+
ui.panel(:host) { |p| handle = p.panel(:picker, lazy: true) { |pk| pk.button(:load, text: 'Load') } }
|
|
201
|
+
end
|
|
202
|
+
session.run_async
|
|
203
|
+
session.app.update
|
|
204
|
+
|
|
205
|
+
session.screens.push(:picker, handle)
|
|
206
|
+
session.app.update
|
|
207
|
+
first_path = handle.path
|
|
208
|
+
|
|
209
|
+
session.screens.pop
|
|
210
|
+
session.app.update
|
|
211
|
+
refute session.app.winfo.ismapped?(first_path)
|
|
212
|
+
|
|
213
|
+
session.screens.push(:picker, handle)
|
|
214
|
+
session.app.update
|
|
215
|
+
|
|
216
|
+
assert_equal first_path, handle.path, "re-pushing the same still-realized handle should reveal the SAME widget, not build a new one at a disambiguated path"
|
|
217
|
+
assert session.app.winfo.ismapped?(first_path)
|
|
218
|
+
|
|
219
|
+
session.app.destroy
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def test_popping_and_destroying_a_lazy_screen_releases_it_and_a_fresh_mount_can_be_pushed_again
|
|
224
|
+
assert_tk_app("popping a lazy screen and destroying it should tear it down; a freshly-mounted replacement should push and realize fine") do
|
|
225
|
+
require 'teek/ui'
|
|
226
|
+
|
|
227
|
+
build_picker = ->(ui, label) {
|
|
228
|
+
ui.component { |c| c.panel(:picker, lazy: true) { |p| p.button(:load, text: label).on_click { } } }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
first_facade = nil
|
|
232
|
+
session = Teek::UI.app(title: 'Screens Test') do |ui|
|
|
233
|
+
ui.panel(:host) { |p| first_facade = build_picker.call(p, 'First') }
|
|
234
|
+
end
|
|
235
|
+
session.run_async
|
|
236
|
+
session.app.update
|
|
237
|
+
baseline = session.app.interp.callback_ids.length
|
|
238
|
+
|
|
239
|
+
first_handle = first_facade[:picker]
|
|
240
|
+
|
|
241
|
+
session.screens.push(:picker, first_handle) # realizes :picker on demand, since it isn't yet
|
|
242
|
+
session.app.update
|
|
243
|
+
assert session.app.winfo.ismapped?(first_handle.path)
|
|
244
|
+
|
|
245
|
+
popped = session.screens.pop
|
|
246
|
+
assert_same first_handle, popped
|
|
247
|
+
session.app.update
|
|
248
|
+
first_path = first_handle.path
|
|
249
|
+
refute session.app.winfo.ismapped?(first_path), "pop alone should conceal, not destroy"
|
|
250
|
+
|
|
251
|
+
popped.destroy!
|
|
252
|
+
session.app.update
|
|
253
|
+
refute session.app.winfo.exists?(first_path)
|
|
254
|
+
assert_equal baseline, session.app.interp.callback_ids.length
|
|
255
|
+
|
|
256
|
+
second_facade = nil
|
|
257
|
+
session.add(:host) { |p| second_facade = build_picker.call(p, 'Second') }
|
|
258
|
+
second_handle = second_facade[:picker]
|
|
259
|
+
|
|
260
|
+
session.screens.push(:picker, second_handle)
|
|
261
|
+
session.app.update
|
|
262
|
+
|
|
263
|
+
assert session.app.winfo.ismapped?(second_handle.path)
|
|
264
|
+
refute_equal first_path, second_handle.path
|
|
265
|
+
|
|
266
|
+
session.app.destroy
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def test_a_child_window_style_lazy_modal_can_be_opened_and_closed_repeatedly
|
|
271
|
+
assert_tk_app("a lazy: true window pushed through ModalStack should realize fresh each open, cleaning up on close") do
|
|
272
|
+
require 'teek/ui'
|
|
273
|
+
|
|
274
|
+
build_dialog = ->(ui, label) {
|
|
275
|
+
ui.component { |c| c.window(:settings, lazy: true, modal: true) { |w| w.button(:ok, text: label) } }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
session = Teek::UI.app(title: 'Screens Test') { |ui| ui.panel(:host) }
|
|
279
|
+
session.run_async
|
|
280
|
+
session.app.update
|
|
281
|
+
session.modal = Teek::UI::ModalStack.new(on_enter: ->(_) { }, on_exit: -> { }, document: session.document)
|
|
282
|
+
|
|
283
|
+
paths = []
|
|
284
|
+
3.times do |i|
|
|
285
|
+
facade = nil
|
|
286
|
+
# each "open" builds a genuinely FRESH component (a new mount,
|
|
287
|
+
# own Scope - see WidgetDSL#component) via the same incremental-
|
|
288
|
+
# realize entry point any post-run dynamic UI uses - the session
|
|
289
|
+
# is already realized by this point, so ordinary DSL calls
|
|
290
|
+
# outside a session.add block would raise ClosedBuilderError.
|
|
291
|
+
session.add(:host) { |ui2| facade = build_dialog.call(ui2, "Open #{i + 1}") }
|
|
292
|
+
handle = facade[:settings]
|
|
293
|
+
|
|
294
|
+
session.modal.push(:settings, handle)
|
|
295
|
+
session.app.update
|
|
296
|
+
assert session.app.winfo.ismapped?(handle.path)
|
|
297
|
+
paths << handle.path
|
|
298
|
+
|
|
299
|
+
session.modal.pop.destroy!
|
|
300
|
+
session.app.update
|
|
301
|
+
|
|
302
|
+
# destroy! unlinks the dialog's own node from :host's children,
|
|
303
|
+
# not just its Tk-realized state - :host should never accumulate
|
|
304
|
+
# a permanently-dead child across repeated opens.
|
|
305
|
+
assert_equal 0, session.document.find(:host).children.length,
|
|
306
|
+
"each mount's dialog should be fully unlinked from :host after destroy!, not just Tk-destroyed"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
assert_equal paths.uniq.length, paths.length, "each open should have gotten its own distinct Tk path"
|
|
310
|
+
|
|
311
|
+
session.app.destroy
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|