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
data/test/test_grid.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minitest/autorun'
|
|
4
|
+
require_relative '../../test/tk_test_helper'
|
|
5
|
+
|
|
6
|
+
class TestGrid < Minitest::Test
|
|
7
|
+
include TeekTestHelper
|
|
8
|
+
|
|
9
|
+
def test_labeled_field_form_realizes_at_the_right_positions
|
|
10
|
+
assert_tk_app("the canonical 2-column labeled-field form should realize with correct grid positions") do
|
|
11
|
+
require 'teek/ui'
|
|
12
|
+
|
|
13
|
+
session = Teek::UI.app(title: 'Grid Test') do |ui|
|
|
14
|
+
ui.grid(:form, gap: 4) do |g|
|
|
15
|
+
g.cell(row: 0, col: 0) { g.label(text: 'Name:') }
|
|
16
|
+
g.cell(row: 0, col: 1) { g.text_box(:name_field) }
|
|
17
|
+
g.cell(row: 1, col: 0) { g.label(text: 'Email:') }
|
|
18
|
+
g.cell(row: 1, col: 1) { g.text_box(:email_field) }
|
|
19
|
+
g.stretch(columns: [1])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
session.run_async
|
|
23
|
+
session.app.update
|
|
24
|
+
|
|
25
|
+
name_info = session.app.command(:grid, :info, session[:name_field].path)
|
|
26
|
+
assert_match(/-row 0/, name_info)
|
|
27
|
+
assert_match(/-column 1/, name_info)
|
|
28
|
+
|
|
29
|
+
email_info = session.app.command(:grid, :info, session[:email_field].path)
|
|
30
|
+
assert_match(/-row 1/, email_info)
|
|
31
|
+
assert_match(/-column 1/, email_info)
|
|
32
|
+
|
|
33
|
+
# the input column (1) is the one that should absorb extra width
|
|
34
|
+
weight = session.app.tcl_eval("grid columnconfigure #{session[:form].path} 1 -weight")
|
|
35
|
+
assert_equal '1', weight
|
|
36
|
+
|
|
37
|
+
session.app.destroy
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_span_produces_a_real_columnspan
|
|
42
|
+
assert_tk_app("span: should produce a real -columnspan in the realized grid") do
|
|
43
|
+
require 'teek/ui'
|
|
44
|
+
|
|
45
|
+
session = Teek::UI.app(title: 'Grid Test') do |ui|
|
|
46
|
+
ui.grid(:form) do |g|
|
|
47
|
+
g.cell(row: 0, col: 0) { g.label(text: 'Name:') }
|
|
48
|
+
g.cell(row: 0, col: 1) { g.text_box(:name_field) }
|
|
49
|
+
g.cell(row: 1, col: 0, span: 2) { g.divider(:sep) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
session.run_async
|
|
53
|
+
session.app.update
|
|
54
|
+
|
|
55
|
+
sep_info = session.app.command(:grid, :info, session[:sep].path)
|
|
56
|
+
assert_match(/-columnspan 2/, sep_info)
|
|
57
|
+
|
|
58
|
+
session.app.destroy
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_a_widget_placed_directly_in_a_grid_without_cell_is_caught_by_validation
|
|
63
|
+
assert_tk_app("a grid child never wrapped in g.cell should be caught by validation, before any Tk call happens") do
|
|
64
|
+
require 'teek/ui'
|
|
65
|
+
|
|
66
|
+
session = Teek::UI.app(title: 'Grid Test') do |ui|
|
|
67
|
+
ui.grid(:form) { |g| g.label(:oops, text: 'no cell') }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
error = assert_raises(Teek::UI::ValidationError) { session.realize }
|
|
71
|
+
assert_match(/cell/i, error.message)
|
|
72
|
+
assert_match(/oops/, error.message)
|
|
73
|
+
|
|
74
|
+
# validation runs before any interpreter is constructed at all - a
|
|
75
|
+
# failed realize never gets far enough to leave anything to clean up.
|
|
76
|
+
assert_raises(Teek::UI::NotRealizedError) { session.app }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_a_widget_added_to_a_grid_without_cell_via_session_add_still_raises_at_realize
|
|
81
|
+
assert_tk_app("session.add skips validation, so the realizer's own backstop check is what catches this instead") do
|
|
82
|
+
require 'teek/ui'
|
|
83
|
+
|
|
84
|
+
session = Teek::UI.app(title: 'Grid Test') do |ui|
|
|
85
|
+
ui.grid(:form) { |g| g.cell(row: 0, col: 0) { g.label(text: 'Name:') } }
|
|
86
|
+
end
|
|
87
|
+
session.run_async
|
|
88
|
+
session.app.update
|
|
89
|
+
|
|
90
|
+
error = assert_raises(ArgumentError) { session.add(:form) { |a| a.label(:oops, text: 'no cell') } }
|
|
91
|
+
assert_match(/cell/i, error.message)
|
|
92
|
+
assert_match(/oops/, error.message)
|
|
93
|
+
|
|
94
|
+
session.app.destroy
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/test/test_handle.rb
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
require 'teek/ui/node'
|
|
5
|
+
require 'teek/ui/handle'
|
|
6
|
+
require 'teek/ui/widget_types'
|
|
7
|
+
|
|
8
|
+
class TestHandle < Minitest::Test
|
|
9
|
+
def test_path_raises_before_realize
|
|
10
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
11
|
+
handle = Teek::UI::Handle.new(node)
|
|
12
|
+
|
|
13
|
+
error = assert_raises(Teek::UI::NotRealizedError) { handle.path }
|
|
14
|
+
assert_match(/not realized/i, error.message)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_configure_raises_before_realize
|
|
18
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
19
|
+
handle = Teek::UI::Handle.new(node)
|
|
20
|
+
|
|
21
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.configure(text: 'Go') }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_app_raises_before_realize
|
|
25
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
26
|
+
handle = Teek::UI::Handle.new(node)
|
|
27
|
+
|
|
28
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.app }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_app_returns_the_realized_app_once_realized
|
|
32
|
+
app = FakeApp.new
|
|
33
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
34
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.win.save')
|
|
35
|
+
handle = Teek::UI::Handle.new(node)
|
|
36
|
+
|
|
37
|
+
assert_same app, handle.app
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_path_returns_the_real_path_once_realized
|
|
41
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
42
|
+
node.realized = Teek::UI::RealizedNode.new(app: FakeApp.new([]), path: '.win.save')
|
|
43
|
+
handle = Teek::UI::Handle.new(node)
|
|
44
|
+
|
|
45
|
+
assert_equal '.win.save', handle.path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_configure_delegates_to_the_realized_apps_command_once_realized
|
|
49
|
+
app = FakeApp.new([])
|
|
50
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
51
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.win.save')
|
|
52
|
+
handle = Teek::UI::Handle.new(node)
|
|
53
|
+
|
|
54
|
+
handle.configure(text: 'Go', width: 10)
|
|
55
|
+
|
|
56
|
+
assert_equal [[['.win.save', :configure], { text: 'Go', width: 10 }]], app.calls
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Handle itself carries zero per-type addressing knowledge - it resolves
|
|
60
|
+
# a type's WidgetType#addressing strategy from the registry and
|
|
61
|
+
# delegates #configure/#path entirely to it. A plugin/custom widget
|
|
62
|
+
# type registering its own addressing: works correctly with NO changes
|
|
63
|
+
# to Handle's own code - this test IS the proof of that, not an
|
|
64
|
+
# incidental check of one more type.
|
|
65
|
+
def test_configure_and_path_delegate_via_a_custom_registered_addressing_strategy
|
|
66
|
+
calls = []
|
|
67
|
+
strategy_class = Class.new do
|
|
68
|
+
define_method(:initialize) { |node| @node = node }
|
|
69
|
+
define_method(:virtual_path) { "FAKE:#{@node.name}" }
|
|
70
|
+
define_method(:configure) { |**opts| calls << [@node, opts] }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Teek::UI::WidgetTypes.register(
|
|
74
|
+
Teek::UI::WidgetType.new(
|
|
75
|
+
type: :__test_handle_custom_addressing__, tk_command: 'ttk::label', addressing: strategy_class
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
node = Teek::UI::Node.new(type: :__test_handle_custom_addressing__, name: :thing)
|
|
80
|
+
handle = Teek::UI::Handle.new(node)
|
|
81
|
+
|
|
82
|
+
handle.configure(text: 'hi')
|
|
83
|
+
|
|
84
|
+
assert_equal [[node, { text: 'hi' }]], calls
|
|
85
|
+
assert_equal 'FAKE:thing', handle.path
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_type_and_name_reflect_the_underlying_node_at_any_phase
|
|
89
|
+
node = Teek::UI::Node.new(type: :button, name: :save)
|
|
90
|
+
handle = Teek::UI::Handle.new(node)
|
|
91
|
+
|
|
92
|
+
assert_equal :button, handle.type
|
|
93
|
+
assert_equal :save, handle.name
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_on_click_queues_an_event_binding_before_realize
|
|
97
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
98
|
+
handle = Teek::UI::Handle.new(node)
|
|
99
|
+
clicked = -> { }
|
|
100
|
+
|
|
101
|
+
result = handle.on_click(&clicked)
|
|
102
|
+
|
|
103
|
+
assert_same handle, result, "on_click should return self for chaining"
|
|
104
|
+
assert_equal 1, node.events.length
|
|
105
|
+
binding = node.events.first
|
|
106
|
+
assert_equal '<Button-1>', binding.event
|
|
107
|
+
assert_equal clicked, binding.handler
|
|
108
|
+
assert_nil binding.target
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def test_on_click_wires_immediately_once_already_realized
|
|
112
|
+
app = FakeApp.new
|
|
113
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
114
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.win.go')
|
|
115
|
+
handle = Teek::UI::Handle.new(node)
|
|
116
|
+
|
|
117
|
+
handle.on_click { }
|
|
118
|
+
|
|
119
|
+
assert_equal 1, app.binds.length
|
|
120
|
+
assert_equal '.win.go', app.binds.first[:path]
|
|
121
|
+
assert_equal '<Button-1>', app.binds.first[:event]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_on_click_wired_after_realize_still_shows_up_in_events
|
|
125
|
+
app = FakeApp.new
|
|
126
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
127
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.win.go')
|
|
128
|
+
handle = Teek::UI::Handle.new(node)
|
|
129
|
+
|
|
130
|
+
handle.on_click { }
|
|
131
|
+
|
|
132
|
+
assert_equal 1, node.events.length, "an event bound after realize should still be visible via #events - " \
|
|
133
|
+
"it wires immediately AND is recorded, not one or the other"
|
|
134
|
+
assert_equal '<Button-1>', node.events.first.event
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_events_returns_every_binding_declared_so_far_before_realize
|
|
138
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
139
|
+
handle = Teek::UI::Handle.new(node)
|
|
140
|
+
|
|
141
|
+
handle.on_click { }
|
|
142
|
+
handle.on_key(:enter) { }
|
|
143
|
+
|
|
144
|
+
assert_equal ['<Button-1>', '<Return>'], handle.events.map(&:event)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def test_events_reflects_bindings_from_both_before_and_after_realize
|
|
148
|
+
app = FakeApp.new
|
|
149
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
150
|
+
handle = Teek::UI::Handle.new(node)
|
|
151
|
+
handle.on_click { }
|
|
152
|
+
|
|
153
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.win.go')
|
|
154
|
+
handle.on_key(:enter) { }
|
|
155
|
+
|
|
156
|
+
assert_equal ['<Button-1>', '<Return>'], handle.events.map(&:event)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_events_is_empty_for_a_handle_with_nothing_bound
|
|
160
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
161
|
+
handle = Teek::UI::Handle.new(node)
|
|
162
|
+
|
|
163
|
+
assert_equal [], handle.events
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_on_key_friendly_symbol_queues_the_resolved_pattern
|
|
167
|
+
node = Teek::UI::Node.new(type: :text_box, name: :query)
|
|
168
|
+
handle = Teek::UI::Handle.new(node)
|
|
169
|
+
|
|
170
|
+
handle.on_key(:enter) { }
|
|
171
|
+
|
|
172
|
+
assert_equal ['<Return>'], node.events.map(&:event)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_on_key_modifier_string_queues_the_resolved_pattern
|
|
176
|
+
node = Teek::UI::Node.new(type: :text_box, name: :query)
|
|
177
|
+
handle = Teek::UI::Handle.new(node)
|
|
178
|
+
|
|
179
|
+
handle.on_key('Ctrl-s') { }
|
|
180
|
+
|
|
181
|
+
assert_equal ['<Control-s>'], node.events.map(&:event)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_on_drag_queues_with_x_y_subs
|
|
185
|
+
node = Teek::UI::Node.new(type: :panel, name: :area)
|
|
186
|
+
handle = Teek::UI::Handle.new(node)
|
|
187
|
+
|
|
188
|
+
handle.on_drag { |_x, _y| }
|
|
189
|
+
|
|
190
|
+
binding = node.events.first
|
|
191
|
+
assert_equal '<B1-Motion>', binding.event
|
|
192
|
+
assert_equal %i[x y], binding.subs
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_on_right_click_queues_the_platform_appropriate_event_patterns
|
|
196
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
197
|
+
handle = Teek::UI::Handle.new(node)
|
|
198
|
+
|
|
199
|
+
handle.on_right_click { }
|
|
200
|
+
|
|
201
|
+
assert_equal Teek::UI::MouseEvents::RIGHT_CLICK_EVENTS, node.events.map(&:event)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def test_on_close_queues_the_block_on_a_window_node_before_realize
|
|
205
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
206
|
+
handle = Teek::UI::Handle.new(node)
|
|
207
|
+
closer = -> { }
|
|
208
|
+
|
|
209
|
+
result = handle.on_close(&closer)
|
|
210
|
+
|
|
211
|
+
assert_same handle, result, "on_close should return self for chaining"
|
|
212
|
+
assert_equal closer, node.opts[:on_close]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def test_on_close_wires_immediately_once_already_realized
|
|
216
|
+
app = FakeApp.new
|
|
217
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
218
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.settings')
|
|
219
|
+
handle = Teek::UI::Handle.new(node)
|
|
220
|
+
|
|
221
|
+
handle.on_close { }
|
|
222
|
+
|
|
223
|
+
assert_nil node.opts[:on_close], "should wire immediately, not queue, once realized"
|
|
224
|
+
assert_equal 1, app.on_closes.length
|
|
225
|
+
assert_equal '.settings', app.on_closes.first[:window]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def test_on_close_raises_on_a_non_window_node
|
|
229
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
230
|
+
handle = Teek::UI::Handle.new(node)
|
|
231
|
+
|
|
232
|
+
error = assert_raises(ArgumentError) { handle.on_close { } }
|
|
233
|
+
assert_match(/window/i, error.message)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def test_on_right_click_with_a_menu_queues_root_coordinate_bindings_before_realize
|
|
237
|
+
node = Teek::UI::Node.new(type: :canvas, name: :board)
|
|
238
|
+
handle = Teek::UI::Handle.new(node)
|
|
239
|
+
menu_node = Teek::UI::Node.new(type: :context_menu, name: :ctx)
|
|
240
|
+
menu_handle = Teek::UI::Handle.new(menu_node)
|
|
241
|
+
|
|
242
|
+
result = handle.on_right_click(menu_handle)
|
|
243
|
+
|
|
244
|
+
assert_same handle, result, "on_right_click should return self for chaining"
|
|
245
|
+
assert_equal Teek::UI::MouseEvents::RIGHT_CLICK_EVENTS.length, node.events.length
|
|
246
|
+
assert_equal Teek::UI::MouseEvents::RIGHT_CLICK_EVENTS, node.events.map(&:event)
|
|
247
|
+
node.events.each { |binding| assert_equal %i[root_x root_y], binding.subs }
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def test_on_right_click_with_a_menu_pops_up_at_the_events_root_coordinates
|
|
251
|
+
app = FakeApp.new
|
|
252
|
+
node = Teek::UI::Node.new(type: :canvas, name: :board)
|
|
253
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.board')
|
|
254
|
+
handle = Teek::UI::Handle.new(node)
|
|
255
|
+
menu_node = Teek::UI::Node.new(type: :context_menu, name: :ctx)
|
|
256
|
+
menu_node.realized = Teek::UI::RealizedNode.new(app: app, path: '.ctx')
|
|
257
|
+
menu_handle = Teek::UI::Handle.new(menu_node)
|
|
258
|
+
|
|
259
|
+
handle.on_right_click(menu_handle)
|
|
260
|
+
app.binds.first[:block].call(50, 60)
|
|
261
|
+
|
|
262
|
+
assert_equal 1, app.popups.length
|
|
263
|
+
assert_equal({ menu: '.ctx', x: 50, y: 60, entry: nil }, app.popups.first)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def test_on_right_click_with_a_menu_handle_of_the_wrong_type_raises
|
|
267
|
+
node = Teek::UI::Node.new(type: :canvas, name: :board)
|
|
268
|
+
handle = Teek::UI::Handle.new(node)
|
|
269
|
+
not_a_menu = Teek::UI::Handle.new(Teek::UI::Node.new(type: :button, name: :go))
|
|
270
|
+
|
|
271
|
+
error = assert_raises(ArgumentError) { handle.on_right_click(not_a_menu) }
|
|
272
|
+
assert_match(/menu/i, error.message)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def test_on_right_click_with_neither_a_menu_nor_a_block_raises
|
|
276
|
+
node = Teek::UI::Node.new(type: :canvas, name: :board)
|
|
277
|
+
handle = Teek::UI::Handle.new(node)
|
|
278
|
+
|
|
279
|
+
assert_raises(ArgumentError) { handle.on_right_click }
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def test_on_right_click_with_both_a_menu_and_a_block_raises
|
|
283
|
+
node = Teek::UI::Node.new(type: :canvas, name: :board)
|
|
284
|
+
handle = Teek::UI::Handle.new(node)
|
|
285
|
+
menu_handle = Teek::UI::Handle.new(Teek::UI::Node.new(type: :menu, name: :m))
|
|
286
|
+
|
|
287
|
+
error = assert_raises(ArgumentError) { handle.on_right_click(menu_handle) { } }
|
|
288
|
+
assert_match(/either/i, error.message)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def test_modal_raises_before_realize
|
|
292
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
293
|
+
handle = Teek::UI::Handle.new(node)
|
|
294
|
+
|
|
295
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.modal }
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_grab_release_raises_before_realize
|
|
299
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
300
|
+
handle = Teek::UI::Handle.new(node)
|
|
301
|
+
|
|
302
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.grab_release }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def test_modal_raises_on_a_non_window_node
|
|
306
|
+
app = FakeApp.new
|
|
307
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
308
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.go')
|
|
309
|
+
handle = Teek::UI::Handle.new(node)
|
|
310
|
+
|
|
311
|
+
error = assert_raises(ArgumentError) { handle.modal }
|
|
312
|
+
assert_match(/window/i, error.message)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_grab_release_raises_on_a_non_window_node
|
|
316
|
+
app = FakeApp.new
|
|
317
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
318
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.go')
|
|
319
|
+
handle = Teek::UI::Handle.new(node)
|
|
320
|
+
|
|
321
|
+
error = assert_raises(ArgumentError) { handle.grab_release }
|
|
322
|
+
assert_match(/window/i, error.message)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def test_modal_delegates_to_the_realized_apps_window_once_realized
|
|
326
|
+
app = FakeApp.new
|
|
327
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
328
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.settings')
|
|
329
|
+
handle = Teek::UI::Handle.new(node)
|
|
330
|
+
ran = false
|
|
331
|
+
|
|
332
|
+
handle.modal(global: true) { ran = true }
|
|
333
|
+
|
|
334
|
+
assert_equal 1, app.windows.length
|
|
335
|
+
assert_equal '.settings', app.windows.first.path
|
|
336
|
+
assert_equal [{ global: true }], app.windows.first.modal_calls
|
|
337
|
+
assert ran, "the setup block should run"
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def test_grab_release_delegates_to_the_realized_apps_window
|
|
341
|
+
app = FakeApp.new
|
|
342
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
343
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.settings')
|
|
344
|
+
handle = Teek::UI::Handle.new(node)
|
|
345
|
+
|
|
346
|
+
handle.grab_release
|
|
347
|
+
|
|
348
|
+
assert_equal 1, app.windows.length
|
|
349
|
+
assert_equal '.settings', app.windows.first.path
|
|
350
|
+
assert_equal [true], app.windows.first.grab_releases
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def test_show_raises_before_realize
|
|
354
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
355
|
+
handle = Teek::UI::Handle.new(node)
|
|
356
|
+
|
|
357
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.show }
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def test_hide_raises_before_realize
|
|
361
|
+
node = Teek::UI::Node.new(type: :window, name: :settings)
|
|
362
|
+
handle = Teek::UI::Handle.new(node)
|
|
363
|
+
|
|
364
|
+
assert_raises(Teek::UI::NotRealizedError) { handle.hide }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def test_show_raises_on_a_non_window_node
|
|
368
|
+
app = FakeApp.new
|
|
369
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
370
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.go')
|
|
371
|
+
handle = Teek::UI::Handle.new(node)
|
|
372
|
+
|
|
373
|
+
error = assert_raises(ArgumentError) { handle.show }
|
|
374
|
+
assert_match(/window/i, error.message)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def test_hide_raises_on_a_non_window_node
|
|
378
|
+
app = FakeApp.new
|
|
379
|
+
node = Teek::UI::Node.new(type: :button, name: :go)
|
|
380
|
+
node.realized = Teek::UI::RealizedNode.new(app: app, path: '.go')
|
|
381
|
+
handle = Teek::UI::Handle.new(node)
|
|
382
|
+
|
|
383
|
+
error = assert_raises(ArgumentError) { handle.hide }
|
|
384
|
+
assert_match(/window/i, error.message)
|
|
385
|
+
end
|
|
386
|
+
end
|