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.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +452 -0
  4. data/lib/teek/ui/canvas_item.rb +270 -0
  5. data/lib/teek/ui/component_handle.rb +32 -0
  6. data/lib/teek/ui/document.rb +185 -0
  7. data/lib/teek/ui/errors.rb +41 -0
  8. data/lib/teek/ui/event_binding.rb +22 -0
  9. data/lib/teek/ui/event_bus.rb +53 -0
  10. data/lib/teek/ui/grid_validator.rb +89 -0
  11. data/lib/teek/ui/handle.rb +585 -0
  12. data/lib/teek/ui/image.rb +58 -0
  13. data/lib/teek/ui/keysyms.rb +72 -0
  14. data/lib/teek/ui/menu_builder.rb +130 -0
  15. data/lib/teek/ui/menu_entry_addressing.rb +62 -0
  16. data/lib/teek/ui/modal_stack.rb +99 -0
  17. data/lib/teek/ui/mouse_events.rb +31 -0
  18. data/lib/teek/ui/node.rb +141 -0
  19. data/lib/teek/ui/option_dump_parsing.rb +31 -0
  20. data/lib/teek/ui/overlay_anchors.rb +29 -0
  21. data/lib/teek/ui/overlay_validator.rb +33 -0
  22. data/lib/teek/ui/pane_validator.rb +30 -0
  23. data/lib/teek/ui/realized_node.rb +25 -0
  24. data/lib/teek/ui/realizer.rb +577 -0
  25. data/lib/teek/ui/scope.rb +40 -0
  26. data/lib/teek/ui/screens.rb +141 -0
  27. data/lib/teek/ui/session.rb +417 -0
  28. data/lib/teek/ui/tab_validator.rb +30 -0
  29. data/lib/teek/ui/text_content.rb +321 -0
  30. data/lib/teek/ui/tree_inspector.rb +97 -0
  31. data/lib/teek/ui/validator.rb +131 -0
  32. data/lib/teek/ui/var.rb +98 -0
  33. data/lib/teek/ui/version.rb +7 -0
  34. data/lib/teek/ui/widget_addressing.rb +47 -0
  35. data/lib/teek/ui/widget_dsl.rb +512 -0
  36. data/lib/teek/ui/widget_type.rb +242 -0
  37. data/lib/teek/ui/widget_types/button.rb +7 -0
  38. data/lib/teek/ui/widget_types/canvas.rb +15 -0
  39. data/lib/teek/ui/widget_types/checkbox.rb +7 -0
  40. data/lib/teek/ui/widget_types/column.rb +14 -0
  41. data/lib/teek/ui/widget_types/context_menu.rb +15 -0
  42. data/lib/teek/ui/widget_types/divider.rb +10 -0
  43. data/lib/teek/ui/widget_types/dropdown.rb +7 -0
  44. data/lib/teek/ui/widget_types/grid.rb +12 -0
  45. data/lib/teek/ui/widget_types/group.rb +7 -0
  46. data/lib/teek/ui/widget_types/label.rb +7 -0
  47. data/lib/teek/ui/widget_types/list.rb +7 -0
  48. data/lib/teek/ui/widget_types/menu_bar.rb +26 -0
  49. data/lib/teek/ui/widget_types/menu_checkbox.rb +14 -0
  50. data/lib/teek/ui/widget_types/menu_item.rb +20 -0
  51. data/lib/teek/ui/widget_types/menu_radio.rb +14 -0
  52. data/lib/teek/ui/widget_types/number_box.rb +7 -0
  53. data/lib/teek/ui/widget_types/pane.rb +37 -0
  54. data/lib/teek/ui/widget_types/panel.rb +7 -0
  55. data/lib/teek/ui/widget_types/progress.rb +7 -0
  56. data/lib/teek/ui/widget_types/radio.rb +7 -0
  57. data/lib/teek/ui/widget_types/row.rb +14 -0
  58. data/lib/teek/ui/widget_types/scrollable.rb +17 -0
  59. data/lib/teek/ui/widget_types/slider.rb +7 -0
  60. data/lib/teek/ui/widget_types/spacer.rb +14 -0
  61. data/lib/teek/ui/widget_types/split.rb +17 -0
  62. data/lib/teek/ui/widget_types/tab.rb +35 -0
  63. data/lib/teek/ui/widget_types/table.rb +9 -0
  64. data/lib/teek/ui/widget_types/tabs.rb +10 -0
  65. data/lib/teek/ui/widget_types/text_area.rb +7 -0
  66. data/lib/teek/ui/widget_types/text_box.rb +7 -0
  67. data/lib/teek/ui/widget_types/tree.rb +9 -0
  68. data/lib/teek/ui/widget_types/window.rb +50 -0
  69. data/lib/teek/ui/widget_types.rb +121 -0
  70. data/lib/teek/ui/widget_validators.rb +62 -0
  71. data/lib/teek/ui.rb +54 -0
  72. data/teek-ui.gemspec +24 -0
  73. data/test/support/fake_app.rb +57 -0
  74. data/test/test_busy.rb +82 -0
  75. data/test/test_canvas_items.rb +551 -0
  76. data/test/test_clipboard.rb +87 -0
  77. data/test/test_close_handling.rb +118 -0
  78. data/test/test_component.rb +151 -0
  79. data/test/test_component_realtk.rb +120 -0
  80. data/test/test_debug_info.rb +158 -0
  81. data/test/test_document.rb +317 -0
  82. data/test/test_event_bus.rb +163 -0
  83. data/test/test_events.rb +164 -0
  84. data/test/test_fake_app_contract.rb +62 -0
  85. data/test/test_grid.rb +97 -0
  86. data/test/test_handle.rb +386 -0
  87. data/test/test_handle_destroy_realtk.rb +344 -0
  88. data/test/test_helper.rb +20 -0
  89. data/test/test_image.rb +24 -0
  90. data/test/test_image_realtk.rb +146 -0
  91. data/test/test_incremental_realize.rb +203 -0
  92. data/test/test_keysyms.rb +76 -0
  93. data/test/test_layout.rb +185 -0
  94. data/test/test_lazy_realize.rb +128 -0
  95. data/test/test_managed_window.rb +304 -0
  96. data/test/test_menu_dsl.rb +230 -0
  97. data/test/test_menu_entry_addressing.rb +73 -0
  98. data/test/test_menu_realize.rb +296 -0
  99. data/test/test_modal_handle.rb +147 -0
  100. data/test/test_modal_stack.rb +219 -0
  101. data/test/test_modal_stack_realtk.rb +131 -0
  102. data/test/test_native_scrollable.rb +250 -0
  103. data/test/test_node.rb +193 -0
  104. data/test/test_overlay.rb +139 -0
  105. data/test/test_raw.rb +58 -0
  106. data/test/test_reactive_vars.rb +118 -0
  107. data/test/test_realizer.rb +264 -0
  108. data/test/test_scope.rb +38 -0
  109. data/test/test_screens.rb +260 -0
  110. data/test/test_screens_realtk.rb +314 -0
  111. data/test/test_scrollable.rb +185 -0
  112. data/test/test_scrollable_wheel.rb +172 -0
  113. data/test/test_scrollbar_auto_hide.rb +127 -0
  114. data/test/test_split.rb +145 -0
  115. data/test/test_tabs.rb +201 -0
  116. data/test/test_text_content.rb +664 -0
  117. data/test/test_toast.rb +126 -0
  118. data/test/test_tree_inspector.rb +183 -0
  119. data/test/test_ui.rb +428 -0
  120. data/test/test_validator.rb +209 -0
  121. data/test/test_var.rb +32 -0
  122. data/test/test_widget_dsl.rb +621 -0
  123. data/test/test_widget_introspection.rb +126 -0
  124. data/test/test_widget_types.rb +429 -0
  125. data/test/test_widget_types_realtk.rb +82 -0
  126. data/test/test_widget_validators.rb +82 -0
  127. metadata +207 -0
data/test/test_tabs.rb ADDED
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../../test/tk_test_helper'
5
+
6
+ # ui.tabs/t.tab realize as a working ttk::notebook - each tab a real Tk
7
+ # frame added as a notebook page (TabRealize.post_create), never pack/grid
8
+ # managed on its own (:tab's own arranged: false). Tab selection is
9
+ # observable via Handle#on_tab_changed, which surfaces Tk's own
10
+ # <<NotebookTabChanged>>.
11
+ class TestTabs < Minitest::Test
12
+ include TeekTestHelper
13
+
14
+ def test_tabs_realizes_as_a_working_notebook_with_labeled_pages
15
+ assert_tk_app("ui.tabs should realize as a ttk::notebook with each tab added as a labeled page") do
16
+ require 'teek/ui'
17
+
18
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
19
+ ui.tabs(:t) do |t|
20
+ t.tab('General') { |g| g.button(:go, text: 'Go') }
21
+ t.tab('Advanced') { |a| a.label(:info, text: 'Advanced stuff') }
22
+ end
23
+ end
24
+ session.run_async
25
+ session.app.update
26
+
27
+ notebook_path = session[:t].path
28
+ tab_paths = session.app.split_list(session.app.command(notebook_path, :tabs))
29
+ assert_equal 2, tab_paths.length
30
+
31
+ assert_equal 'General', session.app.command(notebook_path, :tab, tab_paths[0], '-text')
32
+ assert_equal 'Advanced', session.app.command(notebook_path, :tab, tab_paths[1], '-text')
33
+
34
+ session.app.destroy
35
+ end
36
+ end
37
+
38
+ def test_tab_content_is_addressable_and_configurable_like_any_other_widget
39
+ assert_tk_app("a widget declared inside a tab should be a normal, addressable, configurable widget") do
40
+ require 'teek/ui'
41
+
42
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
43
+ ui.tabs { |t| t.tab('General') { |g| g.button(:go, text: 'Go') } }
44
+ end
45
+ session.run_async
46
+ session.app.update
47
+
48
+ session[:go].configure(text: 'Changed')
49
+ session.app.update
50
+
51
+ assert_equal 'Changed', session.app.command(session[:go].path, :cget, '-text')
52
+
53
+ session.app.destroy
54
+ end
55
+ end
56
+
57
+ def test_tab_frame_is_not_independently_pack_or_grid_managed
58
+ assert_tk_app("a tab's own frame should be placed only by notebook add, not pack/grid") do
59
+ require 'teek/ui'
60
+
61
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
62
+ ui.tabs(:t) { |t| t.tab('General', :general) { |g| g.button(text: 'Go') } }
63
+ end
64
+ session.run_async
65
+ session.app.update
66
+
67
+ assert_equal 'notebook', session.app.tcl_eval("winfo manager #{session[:general].path}"),
68
+ "the notebook itself should be the only geometry manager - proves no pack/grid call also ran"
69
+
70
+ session.app.destroy
71
+ end
72
+ end
73
+
74
+ def test_on_tab_changed_fires_with_the_tabs_name_when_it_has_one
75
+ assert_tk_app("switching to a named tab should fire on_tab_changed with that name") do
76
+ require 'teek/ui'
77
+
78
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
79
+ ui.tabs(:t) do |t|
80
+ t.tab('General', :general) { }
81
+ t.tab('Advanced', :advanced) { }
82
+ end
83
+ end
84
+ session.run_async
85
+ session.app.update
86
+
87
+ received = []
88
+ session[:t].on_tab_changed { |id| received << id }
89
+
90
+ session.app.command(session[:t].path, :select, session[:advanced].path)
91
+ session.app.update
92
+
93
+ assert_equal [:advanced], received
94
+
95
+ session.app.destroy
96
+ end
97
+ end
98
+
99
+ def test_on_tab_changed_fires_with_the_index_when_the_tab_is_unnamed
100
+ assert_tk_app("switching to an unnamed tab should fire on_tab_changed with its zero-based index") do
101
+ require 'teek/ui'
102
+
103
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
104
+ ui.tabs(:t) do |t|
105
+ t.tab('General') { }
106
+ t.tab('Advanced') { }
107
+ end
108
+ end
109
+ session.run_async
110
+ session.app.update
111
+
112
+ received = []
113
+ session[:t].on_tab_changed { |id| received << id }
114
+
115
+ notebook_path = session[:t].path
116
+ second_tab = session.app.split_list(session.app.command(notebook_path, :tabs))[1]
117
+ session.app.command(notebook_path, :select, second_tab)
118
+ session.app.update
119
+
120
+ assert_equal [1], received
121
+
122
+ session.app.destroy
123
+ end
124
+ end
125
+
126
+ def test_on_tab_changed_queued_before_realize_fires_once_wired
127
+ assert_tk_app("on_tab_changed declared before realize should still fire correctly once realized") do
128
+ require 'teek/ui'
129
+
130
+ tabs_handle = nil
131
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
132
+ tabs_handle = ui.tabs(:t) do |t|
133
+ t.tab('General', :general) { }
134
+ t.tab('Advanced', :advanced) { }
135
+ end
136
+ end
137
+
138
+ received = []
139
+ tabs_handle.on_tab_changed { |id| received << id }
140
+
141
+ session.run_async
142
+ session.app.update
143
+ # realizing the notebook auto-selects its first tab, which is a real
144
+ # <<NotebookTabChanged>> in its own right and (since this binding
145
+ # was queued before realize, unlike the other on_tab_changed tests)
146
+ # gets caught by it too - clear that out to isolate the transition
147
+ # this test actually cares about.
148
+ received.clear
149
+
150
+ session.app.command(session[:t].path, :select, session[:advanced].path)
151
+ session.app.update
152
+
153
+ assert_equal [:advanced], received
154
+
155
+ session.app.destroy
156
+ end
157
+ end
158
+
159
+ def test_on_tab_changed_raises_on_a_non_tabs_handle
160
+ assert_tk_app("on_tab_changed should raise a clear error on a non-tabs handle") do
161
+ require 'teek/ui'
162
+
163
+ session = Teek::UI.app(title: 'Tabs Test') { |ui| ui.button(:go, text: 'Go') }
164
+ session.run_async
165
+ session.app.update
166
+
167
+ error = assert_raises(ArgumentError) { session[:go].on_tab_changed { } }
168
+ assert_match(/tabs/i, error.message)
169
+
170
+ session.app.destroy
171
+ end
172
+ end
173
+
174
+ def test_a_new_tab_can_be_added_at_runtime_via_incremental_realize
175
+ assert_tk_app("session.add should be able to add a whole new tab to an already-realized ui.tabs") do
176
+ require 'teek/ui'
177
+
178
+ session = Teek::UI.app(title: 'Tabs Test') do |ui|
179
+ ui.tabs(:t) { |t| t.tab('General') { |g| g.button(text: 'Go') } }
180
+ end
181
+ session.run_async
182
+ session.app.update
183
+
184
+ notebook_path = session[:t].path
185
+ assert_equal 1, session.app.split_list(session.app.command(notebook_path, :tabs)).length
186
+
187
+ session.add(:t) { |a| a.tab('New Tab', :new_tab) { |n| n.button(:new_button, text: 'New') } }
188
+ session.app.update
189
+
190
+ tab_paths = session.app.split_list(session.app.command(notebook_path, :tabs))
191
+ assert_equal 2, tab_paths.length
192
+ assert_equal 'New Tab', session.app.command(notebook_path, :tab, tab_paths[1], '-text')
193
+
194
+ session.app.command(notebook_path, :select, session[:new_tab].path)
195
+ session.app.update
196
+ assert session.app.winfo.ismapped?(session[:new_button].path)
197
+
198
+ session.app.destroy
199
+ end
200
+ end
201
+ end