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,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'widget_addressing'
|
|
4
|
+
|
|
5
|
+
module Teek
|
|
6
|
+
module UI
|
|
7
|
+
# @api private
|
|
8
|
+
#
|
|
9
|
+
# A single widget/node type's own metadata: what it draws, how it's
|
|
10
|
+
# built and arranged, what it validates - self-contained enough that
|
|
11
|
+
# {WidgetDSL} (the builder), {Realizer}, and {Validator} can each treat
|
|
12
|
+
# a registered type as the sole source of truth for it, dispatched by
|
|
13
|
+
# node type via {WidgetTypes}.
|
|
14
|
+
#
|
|
15
|
+
# Leaf defaults cover the common case, so a real widget is a ~5-line
|
|
16
|
+
# descriptor: +WidgetType.new(type: :divider, tk_command:
|
|
17
|
+
# 'ttk::separator')+ is a complete, working leaf widget. A container,
|
|
18
|
+
# or a widget needing bespoke DSL methods/realize setup, overrides
|
|
19
|
+
# +dsl:+/+post_create:+.
|
|
20
|
+
class WidgetType
|
|
21
|
+
attr_reader :type, :tk_command, :bind_option, :validator, :flow, :addressing
|
|
22
|
+
|
|
23
|
+
# @param type [Symbol] the node type this describes, e.g. +:divider+
|
|
24
|
+
# @param tk_command [String] the Tk widget-creation command, e.g.
|
|
25
|
+
# +'ttk::separator'+ - documentary only for a type that also sets
|
|
26
|
+
# +custom_create:+, since that hook bypasses the generic
|
|
27
|
+
# widget-creation call this would otherwise drive
|
|
28
|
+
# @param leaf [Boolean] true for a childless widget (the default);
|
|
29
|
+
# false for a container that holds a DSL subtree
|
|
30
|
+
# @param natively_scrollable [Boolean] whether this widget already
|
|
31
|
+
# speaks Tk's -yscrollcommand/-xscrollcommand protocol - see
|
|
32
|
+
# {Realizer#auto_scrollable?}, which consults this for a registered type
|
|
33
|
+
# @param scroll_default [Symbol] which {Teek::UI} global default
|
|
34
|
+
# reader this type's own auto-scrollable wrapping falls back to
|
|
35
|
+
# when neither the widget's own +scroll:+ nor the session's
|
|
36
|
+
# app-wide override says otherwise - +:auto_scroll+ (the default)
|
|
37
|
+
# for most natively-scrollable types, +:auto_scroll_canvas+ for
|
|
38
|
+
# canvas specifically. Only meaningful alongside
|
|
39
|
+
# +natively_scrollable: true+ - see {Realizer#resolve_scroll}.
|
|
40
|
+
# @param arranged [Boolean] whether a geometry manager (pack/grid)
|
|
41
|
+
# should place this node inside its parent - true (the default) for
|
|
42
|
+
# almost everything; false for a type placed some other way
|
|
43
|
+
# entirely, e.g. a toplevel window (placed by the window manager,
|
|
44
|
+
# not its nominal parent) or a tab/pane (placed by its own
|
|
45
|
+
# container's `add` command) - see {Realizer#unarranged?}, which
|
|
46
|
+
# consults this for a registered type
|
|
47
|
+
# @param bind_option [Symbol, nil] the Tk option `bind:` plugs a
|
|
48
|
+
# {Var} into for this widget (+:textvariable+/+:variable+/...) -
|
|
49
|
+
# +nil+ (the default) means this widget doesn't support `bind:`
|
|
50
|
+
# @param flow [Hash, nil] flow-packing config for a `column`/`row`-
|
|
51
|
+
# style container (side/main_pad/cross_pad/main_fill/cross_fill/
|
|
52
|
+
# anchor) - +nil+ (the default) means this type isn't flow-arranged.
|
|
53
|
+
# A convenience over +arrange:+ for exactly this shape: giving
|
|
54
|
+
# +flow:+ computes an +arrange:+ that delegates to
|
|
55
|
+
# {Realizer#arrange_flow} with this data, so most flow containers
|
|
56
|
+
# never need to touch +arrange:+ directly.
|
|
57
|
+
# @param arrange [#call, nil] +->(realizer, node, children) { ... }+,
|
|
58
|
+
# replaces the realizer's generic "pack every child plainly"
|
|
59
|
+
# default arrangement for this type entirely - e.g. real Tk grid
|
|
60
|
+
# placement (row/col/span/stretch) for a `:grid`-shaped type. Since
|
|
61
|
+
# the logic usually needs realizer-private helpers (gap/align
|
|
62
|
+
# computation, error formatting, ...), the callable is typically a
|
|
63
|
+
# thin adapter delegating back via +realizer.send(:some_private_method, ...)+
|
|
64
|
+
# rather than reimplementing arrangement from scratch - see
|
|
65
|
+
# {Realizer#arrange_grid} for an example. +nil+ (the default,
|
|
66
|
+
# unless +flow:+ computed one) means the generic pack default applies.
|
|
67
|
+
# @param custom_children [#call, nil] +->(realizer, node, path) { ... }+,
|
|
68
|
+
# replaces the realizer's generic "create every child normally"
|
|
69
|
+
# step for this type, once its OWN widget has already been created
|
|
70
|
+
# the normal way - e.g. `:scrollable`'s children live inside an
|
|
71
|
+
# embedded canvas+viewport it builds itself, not directly under its
|
|
72
|
+
# own path. +nil+ (the default) means children realize normally.
|
|
73
|
+
# @param custom_create [#call, nil] +->(realizer, node, parent_path) { ... }+,
|
|
74
|
+
# replaces the realizer's ENTIRE per-node create/link handling for
|
|
75
|
+
# this type - no generic widget-creation call, no {#post_create},
|
|
76
|
+
# no {#arrange}/`custom_children`, no normal `link` processing
|
|
77
|
+
# (events/close-handler/child recursion) either. For a type whose
|
|
78
|
+
# realize model doesn't share ANY of that machinery at all - e.g.
|
|
79
|
+
# `:menu_bar`/`:context_menu`, realized via
|
|
80
|
+
# {Realizer#create_menu_tree}'s own bespoke traversal, using
|
|
81
|
+
# `Teek::App#menu` rather than a generic widget-creation command.
|
|
82
|
+
# +nil+ (the default) means this type goes through the normal
|
|
83
|
+
# create/link two-pass path.
|
|
84
|
+
# @param validator [#call, nil] a +(node, parent, document, errors)+
|
|
85
|
+
# callable checking this type's own contract - a REFERENCE to an
|
|
86
|
+
# already-written validator (e.g. an existing WidgetValidators-style
|
|
87
|
+
# module), not duplicated logic. Composed into {WidgetValidators}
|
|
88
|
+
# automatically at {WidgetTypes.register} time, so {Validator}
|
|
89
|
+
# needs no separate awareness of descriptors at all.
|
|
90
|
+
# @param dsl [Proc, nil] +->(mod) { ... }+, called once at
|
|
91
|
+
# {WidgetTypes.register} time (and replayed for any subscriber that
|
|
92
|
+
# joins later - see {WidgetTypes.on_register}) to define this
|
|
93
|
+
# type's `ui.<type>` method(s) on the builder module. Defaults to
|
|
94
|
+
# the leaf/container-appropriate +append_leaf+/+append_container+
|
|
95
|
+
# call - pass +->(mod) { }+ (a genuine no-op) for a type reachable
|
|
96
|
+
# only via a bespoke, hand-written top-level method with a
|
|
97
|
+
# different signature (e.g. `#tab`/`#pane`/`#split`, or
|
|
98
|
+
# `#menu_bar`/`#context_menu`), so the registry doesn't shadow it
|
|
99
|
+
# with a same-named generic method.
|
|
100
|
+
# @param post_create [#call, nil] +->(app, node, path, parent_path) { ... }+,
|
|
101
|
+
# run right after the generic widget-creation command at realize -
|
|
102
|
+
# see {WindowRealize}/{TabRealize}/{PaneRealize} for real examples.
|
|
103
|
+
# Defaults to a no-op. Not called at all for a type that sets
|
|
104
|
+
# +custom_create:+, since that hook bypasses this entire step.
|
|
105
|
+
# @param addressing [#new, nil] a class - +->(node) { ... }+-shaped
|
|
106
|
+
# via +.new+ - knowing how to read/write THIS type's live state
|
|
107
|
+
# (+#virtual_path+, +#configure+). {Handle} resolves this per node
|
|
108
|
+
# from the registry, with no type-specific knowledge of its own -
|
|
109
|
+
# a plugin/custom widget type gets correct addressing purely by
|
|
110
|
+
# registering its own +addressing:+ here. Defaults to
|
|
111
|
+
# {WidgetAddressing} (an ordinary Tk widget with its own path) -
|
|
112
|
+
# see {MenuEntryAddressing} for a type with none of its own.
|
|
113
|
+
def initialize(type:, tk_command:, leaf: true, natively_scrollable: false, arranged: true,
|
|
114
|
+
scroll_default: :auto_scroll, bind_option: nil, flow: nil, arrange: nil,
|
|
115
|
+
custom_children: nil, custom_create: nil, validator: nil, dsl: nil, post_create: nil,
|
|
116
|
+
addressing: WidgetAddressing)
|
|
117
|
+
@type = type.to_sym
|
|
118
|
+
@tk_command = tk_command
|
|
119
|
+
@leaf = leaf
|
|
120
|
+
@natively_scrollable = natively_scrollable
|
|
121
|
+
@arranged = arranged
|
|
122
|
+
@scroll_default = scroll_default
|
|
123
|
+
@bind_option = bind_option
|
|
124
|
+
@flow = flow
|
|
125
|
+
@arrange = arrange || (flow && ->(realizer, node, children) { realizer.send(:arrange_flow, node, children, flow) })
|
|
126
|
+
@custom_children = custom_children
|
|
127
|
+
@custom_create = custom_create
|
|
128
|
+
@validator = validator
|
|
129
|
+
@dsl = dsl || default_dsl
|
|
130
|
+
@addressing = addressing
|
|
131
|
+
@post_create = post_create
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @return [Boolean]
|
|
135
|
+
def leaf?
|
|
136
|
+
@leaf
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# @return [Boolean]
|
|
140
|
+
def container?
|
|
141
|
+
!@leaf
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# @return [Boolean]
|
|
145
|
+
def natively_scrollable?
|
|
146
|
+
@natively_scrollable
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @return [Boolean] the current value of this type's own
|
|
150
|
+
# {Teek::UI} global scroll-default reader (+scroll_default:+)
|
|
151
|
+
def global_scroll_default
|
|
152
|
+
Teek::UI.public_send(@scroll_default)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @return [Boolean]
|
|
156
|
+
def arranged?
|
|
157
|
+
@arranged
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @return [Boolean] whether this type replaces the generic arrangement
|
|
161
|
+
def arrange?
|
|
162
|
+
!@arrange.nil?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Runs this type's custom arrangement strategy.
|
|
166
|
+
# @param realizer [Realizer]
|
|
167
|
+
# @param node [Node]
|
|
168
|
+
# @param children [Array<Node>] this node's arrangeable children
|
|
169
|
+
# @return [void]
|
|
170
|
+
def arrange(realizer, node, children)
|
|
171
|
+
@arrange.call(realizer, node, children)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @return [Boolean] whether this type replaces generic child creation
|
|
175
|
+
def custom_children?
|
|
176
|
+
!@custom_children.nil?
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Runs this type's custom child-creation strategy, once its own
|
|
180
|
+
# widget already exists at +path+.
|
|
181
|
+
# @param realizer [Realizer]
|
|
182
|
+
# @param node [Node]
|
|
183
|
+
# @param path [String]
|
|
184
|
+
# @return [void]
|
|
185
|
+
def custom_children(realizer, node, path)
|
|
186
|
+
@custom_children.call(realizer, node, path)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# @return [Boolean] whether this type replaces the entire create/link handling
|
|
190
|
+
def custom_create?
|
|
191
|
+
!@custom_create.nil?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Runs this type's entire create/link replacement.
|
|
195
|
+
# @param realizer [Realizer]
|
|
196
|
+
# @param node [Node]
|
|
197
|
+
# @param parent_path [String]
|
|
198
|
+
# @return [void]
|
|
199
|
+
def custom_create(realizer, node, parent_path)
|
|
200
|
+
@custom_create.call(realizer, node, parent_path)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Defines this type's `ui.<type>` method(s) on +mod+ (the {WidgetDSL}
|
|
204
|
+
# module) - see {WidgetTypes.on_register}, which drives this.
|
|
205
|
+
# @param mod [Module]
|
|
206
|
+
# @return [void]
|
|
207
|
+
def define_dsl_method!(mod)
|
|
208
|
+
@dsl.call(mod)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Runs this type's post-creation realize setup, if any - a no-op
|
|
212
|
+
# unless +post_create:+ was given.
|
|
213
|
+
# @param app [Teek::App]
|
|
214
|
+
# @param node [Node]
|
|
215
|
+
# @param path [String]
|
|
216
|
+
# @param parent_path [String]
|
|
217
|
+
# @return [void]
|
|
218
|
+
def post_create(app, node, path, parent_path)
|
|
219
|
+
@post_create&.call(app, node, path, parent_path)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
private
|
|
223
|
+
|
|
224
|
+
def default_dsl
|
|
225
|
+
widget_type = self
|
|
226
|
+
if leaf?
|
|
227
|
+
->(mod) {
|
|
228
|
+
mod.send(:define_method, widget_type.type) { |name = nil, **opts|
|
|
229
|
+
append_leaf(widget_type.type, name, opts)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else
|
|
233
|
+
->(mod) {
|
|
234
|
+
mod.send(:define_method, widget_type.type) { |name = nil, **opts, &block|
|
|
235
|
+
append_container(widget_type.type, name, opts, &block)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# A canvas is as often fixed drawing as scrollable content, unlike the
|
|
6
|
+
# other natively-scrollable types - scroll_default: points its own
|
|
7
|
+
# auto-scrollable wrapping at Teek::UI.auto_scroll_canvas (false by
|
|
8
|
+
# default) instead of the shared Teek::UI.auto_scroll (true by default)
|
|
9
|
+
# every other natively-scrollable type falls back to.
|
|
10
|
+
Teek::UI::WidgetTypes.register(
|
|
11
|
+
Teek::UI::WidgetType.new(
|
|
12
|
+
type: :canvas, tk_command: 'canvas', leaf: false,
|
|
13
|
+
natively_scrollable: true, scroll_default: :auto_scroll_canvas
|
|
14
|
+
)
|
|
15
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
Teek::UI::WidgetTypes.register(
|
|
6
|
+
Teek::UI::WidgetType.new(
|
|
7
|
+
type: :column, tk_command: 'ttk::frame', leaf: false,
|
|
8
|
+
flow: {
|
|
9
|
+
side: 'top', main_pad: :pady, cross_pad: :padx,
|
|
10
|
+
main_fill: 'y', cross_fill: 'x',
|
|
11
|
+
anchor: { start: 'w', center: 'center', end: 'e' },
|
|
12
|
+
}
|
|
13
|
+
)
|
|
14
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# See widget_types/menu_bar.rb for the shared reasoning - context_menu is
|
|
6
|
+
# the other entry point into the same Realizer#create_menu_tree traversal,
|
|
7
|
+
# just never attached to a -menu option automatically (popped up via
|
|
8
|
+
# Handle#on_right_click instead).
|
|
9
|
+
Teek::UI::WidgetTypes.register(
|
|
10
|
+
Teek::UI::WidgetType.new(
|
|
11
|
+
type: :context_menu, tk_command: 'menu', leaf: false, arranged: false,
|
|
12
|
+
custom_create: ->(realizer, node, parent_path) { realizer.send(:create_menu_tree, node, parent_path) },
|
|
13
|
+
dsl: ->(mod) { }
|
|
14
|
+
)
|
|
15
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# A plain leaf with no bind option, no scrolling, and no realize setup
|
|
6
|
+
# beyond the generic widget-creation command - every field but
|
|
7
|
+
# type/tk_command is a leaf default.
|
|
8
|
+
Teek::UI::WidgetTypes.register(
|
|
9
|
+
Teek::UI::WidgetType.new(type: :divider, tk_command: 'ttk::separator')
|
|
10
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../grid_validator'
|
|
5
|
+
|
|
6
|
+
Teek::UI::WidgetTypes.register(
|
|
7
|
+
Teek::UI::WidgetType.new(
|
|
8
|
+
type: :grid, tk_command: 'ttk::frame', leaf: false,
|
|
9
|
+
arrange: ->(realizer, node, children) { realizer.send(:arrange_grid, node, children) },
|
|
10
|
+
validator: Teek::UI::GridValidator.method(:call)
|
|
11
|
+
)
|
|
12
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# menu_bar/context_menu are the two entry points into a menu subtree -
|
|
6
|
+
# everything under them (nested :menu cascades, :menu_item/:menu_separator/
|
|
7
|
+
# :menu_checkbox/:menu_radio entries) is built in one shot by
|
|
8
|
+
# Realizer#create_menu_tree, in menu-add order, rather than through the
|
|
9
|
+
# generic per-node create/link passes every other node type uses - menu
|
|
10
|
+
# entries have no Tk path or geometry-managed arrangement of their own to
|
|
11
|
+
# visit separately, so custom_create: hands the whole thing off (see
|
|
12
|
+
# create_menu_tree's own doc comment for why it also skips #link entirely).
|
|
13
|
+
#
|
|
14
|
+
# tk_command: is documentary only here - create_menu_tree calls
|
|
15
|
+
# Teek::App#menu directly, never the generic tk_command_for path
|
|
16
|
+
# custom_create: bypasses. No auto-generated `ui.menu_bar` method either -
|
|
17
|
+
# it's only ever reachable via the hand-written WidgetDSL#menu_bar, which
|
|
18
|
+
# validates its parent (see WidgetDSL::MENU_BAR_HOSTS); dsl: is a genuine
|
|
19
|
+
# no-op so the registry doesn't shadow that with a same-named generic method.
|
|
20
|
+
Teek::UI::WidgetTypes.register(
|
|
21
|
+
Teek::UI::WidgetType.new(
|
|
22
|
+
type: :menu_bar, tk_command: 'menu', leaf: false, arranged: false,
|
|
23
|
+
custom_create: ->(realizer, node, parent_path) { realizer.send(:create_menu_tree, node, parent_path) },
|
|
24
|
+
dsl: ->(mod) { }
|
|
25
|
+
)
|
|
26
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../menu_entry_addressing'
|
|
5
|
+
|
|
6
|
+
# See widget_types/menu_item.rb for the shared reasoning - :menu_checkbox
|
|
7
|
+
# is a menu entry kind, addressed the same way (MenuEntryAddressing),
|
|
8
|
+
# reachable only via the hand-written MenuBuilder#checkbox.
|
|
9
|
+
Teek::UI::WidgetTypes.register(
|
|
10
|
+
Teek::UI::WidgetType.new(
|
|
11
|
+
type: :menu_checkbox, tk_command: 'menu', addressing: Teek::UI::MenuEntryAddressing,
|
|
12
|
+
dsl: ->(mod) { }
|
|
13
|
+
)
|
|
14
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../menu_entry_addressing'
|
|
5
|
+
|
|
6
|
+
# :menu_item never flows through the generic Realizer#create path -
|
|
7
|
+
# Realizer#create_menu_tree issues its own `menu add command` call for
|
|
8
|
+
# every :menu_item child directly, so leaf:/arranged:/etc are inert here.
|
|
9
|
+
# Registered so its addressing: (how a named item's Handle reads/writes
|
|
10
|
+
# its live -state/-label/...) is discoverable from the registry, the same
|
|
11
|
+
# way every other type's is - see menu_entry_addressing.rb. No
|
|
12
|
+
# auto-generated `ui.menu_item` method either - it's only ever reachable
|
|
13
|
+
# via the hand-written MenuBuilder#item; dsl: is a genuine no-op so the
|
|
14
|
+
# registry doesn't shadow that with a same-named generic method.
|
|
15
|
+
Teek::UI::WidgetTypes.register(
|
|
16
|
+
Teek::UI::WidgetType.new(
|
|
17
|
+
type: :menu_item, tk_command: 'menu', addressing: Teek::UI::MenuEntryAddressing,
|
|
18
|
+
dsl: ->(mod) { }
|
|
19
|
+
)
|
|
20
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../menu_entry_addressing'
|
|
5
|
+
|
|
6
|
+
# See widget_types/menu_item.rb for the shared reasoning - :menu_radio is
|
|
7
|
+
# a menu entry kind, addressed the same way (MenuEntryAddressing),
|
|
8
|
+
# reachable only via the hand-written MenuBuilder#radio.
|
|
9
|
+
Teek::UI::WidgetTypes.register(
|
|
10
|
+
Teek::UI::WidgetType.new(
|
|
11
|
+
type: :menu_radio, tk_command: 'menu', addressing: Teek::UI::MenuEntryAddressing,
|
|
12
|
+
dsl: ->(mod) { }
|
|
13
|
+
)
|
|
14
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../pane_validator'
|
|
5
|
+
|
|
6
|
+
module Teek
|
|
7
|
+
module UI
|
|
8
|
+
# @api private
|
|
9
|
+
#
|
|
10
|
+
# Adds a freshly created :pane's own frame to the enclosing
|
|
11
|
+
# panedwindow, with whatever `#pane` stashed as pane_weight: (if any).
|
|
12
|
+
# `ttk::panedwindow add` is the pane's whole placement - unlike every
|
|
13
|
+
# other container, a pane's frame is never pack/grid-managed on its
|
|
14
|
+
# own (see :pane's own +arranged: false+ below). Registered as :pane's
|
|
15
|
+
# own `post_create:`.
|
|
16
|
+
module PaneRealize
|
|
17
|
+
def self.post_create(app, node, path, parent_path)
|
|
18
|
+
weight = node.opts[:pane_weight]
|
|
19
|
+
opts = weight.nil? ? {} : { weight: weight }
|
|
20
|
+
app.command(parent_path, :add, path, **opts)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# :pane has no auto-generated `ui.pane` method - it's only ever reachable
|
|
27
|
+
# via the hand-written WidgetDSL#pane, which validates it's declared
|
|
28
|
+
# directly inside ui.split. dsl: is a genuine no-op so the registry
|
|
29
|
+
# doesn't shadow that with a same-named generic method.
|
|
30
|
+
Teek::UI::WidgetTypes.register(
|
|
31
|
+
Teek::UI::WidgetType.new(
|
|
32
|
+
type: :pane, tk_command: 'ttk::frame', leaf: false, arranged: false,
|
|
33
|
+
post_create: Teek::UI::PaneRealize.method(:post_create),
|
|
34
|
+
validator: Teek::UI::PaneValidator.method(:call),
|
|
35
|
+
dsl: ->(mod) { }
|
|
36
|
+
)
|
|
37
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
Teek::UI::WidgetTypes.register(
|
|
6
|
+
Teek::UI::WidgetType.new(
|
|
7
|
+
type: :row, tk_command: 'ttk::frame', leaf: false,
|
|
8
|
+
flow: {
|
|
9
|
+
side: 'left', main_pad: :padx, cross_pad: :pady,
|
|
10
|
+
main_fill: 'x', cross_fill: 'y',
|
|
11
|
+
anchor: { start: 'n', center: 'center', end: 's' },
|
|
12
|
+
}
|
|
13
|
+
)
|
|
14
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# There's no Tk protocol to hook a scrollbar into arbitrary widgets (unlike
|
|
6
|
+
# a natively-scrollable widget's own wrapping - see list.rb/table.rb/etc),
|
|
7
|
+
# so :scrollable's children are created inside an embedded canvas+viewport
|
|
8
|
+
# it builds itself (Realizer#create_scrollable) rather than directly under
|
|
9
|
+
# its own path - custom_children: takes over from the generic
|
|
10
|
+
# "create every child normally" step once its own frame already exists.
|
|
11
|
+
Teek::UI::WidgetTypes.register(
|
|
12
|
+
Teek::UI::WidgetType.new(
|
|
13
|
+
type: :scrollable, tk_command: 'ttk::frame', leaf: false,
|
|
14
|
+
custom_children: ->(realizer, node, path) { realizer.send(:create_scrollable, node, path) },
|
|
15
|
+
arrange: ->(realizer, node, children) { realizer.send(:arrange_scrollable_frame, node, children) }
|
|
16
|
+
)
|
|
17
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# A flexible gap - the named replacement for the "invisible spring row"
|
|
6
|
+
# trick (an empty row/column given all the leftover weight). A leaf with
|
|
7
|
+
# `grow: true` baked in and no arguments at all, so it needs its own `dsl:`
|
|
8
|
+
# rather than the generic leaf default (`name = nil, **opts`).
|
|
9
|
+
Teek::UI::WidgetTypes.register(
|
|
10
|
+
Teek::UI::WidgetType.new(
|
|
11
|
+
type: :spacer, tk_command: 'ttk::frame',
|
|
12
|
+
dsl: ->(mod) { mod.send(:define_method, :spacer) { append_leaf(:spacer, nil, grow: true) } }
|
|
13
|
+
)
|
|
14
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# :split has no auto-generated `ui.split` method - it's only ever reachable
|
|
6
|
+
# via the hand-written WidgetDSL#split, which validates orientation: and
|
|
7
|
+
# translates it to the real -orient option before creating the node.
|
|
8
|
+
# dsl: is a genuine no-op so the registry doesn't shadow that with a
|
|
9
|
+
# same-named generic method. `orient:` itself is a real ttk::panedwindow
|
|
10
|
+
# option, so it needs no reserved-option handling here - it passes through
|
|
11
|
+
# the generic widget-creation call untouched.
|
|
12
|
+
Teek::UI::WidgetTypes.register(
|
|
13
|
+
Teek::UI::WidgetType.new(
|
|
14
|
+
type: :split, tk_command: 'ttk::panedwindow', leaf: false,
|
|
15
|
+
dsl: ->(mod) { }
|
|
16
|
+
)
|
|
17
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
require_relative '../tab_validator'
|
|
5
|
+
|
|
6
|
+
module Teek
|
|
7
|
+
module UI
|
|
8
|
+
# @api private
|
|
9
|
+
#
|
|
10
|
+
# Adds a freshly created :tab's own frame to the enclosing notebook as
|
|
11
|
+
# a page, labeled with whatever `#tab` stashed as tab_label:.
|
|
12
|
+
# `ttk::notebook add` is the page's whole placement - unlike every
|
|
13
|
+
# other container, a tab's frame is never pack/grid-managed on its own
|
|
14
|
+
# (see :tab's own +arranged: false+ below). Registered as :tab's own
|
|
15
|
+
# `post_create:`.
|
|
16
|
+
module TabRealize
|
|
17
|
+
def self.post_create(app, node, path, parent_path)
|
|
18
|
+
app.command(parent_path, :add, path, text: node.opts[:tab_label])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# :tab has no auto-generated `ui.tab` method - it's only ever reachable via
|
|
25
|
+
# the hand-written WidgetDSL#tab, which validates it's declared directly
|
|
26
|
+
# inside ui.tabs. dsl: is a genuine no-op so the registry doesn't shadow
|
|
27
|
+
# that with a same-named generic method.
|
|
28
|
+
Teek::UI::WidgetTypes.register(
|
|
29
|
+
Teek::UI::WidgetType.new(
|
|
30
|
+
type: :tab, tk_command: 'ttk::frame', leaf: false, arranged: false,
|
|
31
|
+
post_create: Teek::UI::TabRealize.method(:post_create),
|
|
32
|
+
validator: Teek::UI::TabValidator.method(:call),
|
|
33
|
+
dsl: ->(mod) { }
|
|
34
|
+
)
|
|
35
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# `table` and `tree` are two DSL names over the same Tk widget
|
|
6
|
+
# (ttk::treeview, used with/without -show tree) - see tree.rb.
|
|
7
|
+
Teek::UI::WidgetTypes.register(
|
|
8
|
+
Teek::UI::WidgetType.new(type: :table, tk_command: 'ttk::treeview', natively_scrollable: true)
|
|
9
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../widget_type'
|
|
4
|
+
|
|
5
|
+
# A plain container holding #tab-declared pages - each one placed entirely
|
|
6
|
+
# by `ttk::notebook add` (see tab.rb's own arranged: false), so :tabs
|
|
7
|
+
# itself never has any arrangeable children and needs no custom arrange:.
|
|
8
|
+
Teek::UI::WidgetTypes.register(
|
|
9
|
+
Teek::UI::WidgetType.new(type: :tabs, tk_command: 'ttk::notebook', leaf: false)
|
|
10
|
+
)
|