/
widget.rb
430 lines (365 loc) · 10.5 KB
/
widget.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
require "yast"
require "abstract_method"
require "cwm/abstract_widget"
require "cwm/custom_widget"
require "cwm/tabs"
require "cwm/tree"
# Common Widget Manipulation.
# An object-oriented API for the YCP-era {Yast::CWMClass}.
module CWM
# An empty widget useful mainly as placeholder for replacement
# or for catching global events
#
# @example empty widget usage
# CWM.show(VBox(CWM::Empty.new("replace_point")))
class Empty < AbstractWidget
self.widget_type = :empty
# @param id [String] widget ID
def initialize(id)
self.widget_id = id
end
end
# A mix-in for widgets using the :Value property
module ValueBasedWidget
# Get widget value
# @return [Object] a value according to specific widget type
def value
Yast::UI.QueryWidget(Id(widget_id), :Value)
end
# Set widget value
# @param val [Object] a value according to specific widget type
# @return [void]
def value=(val)
Yast::UI.ChangeWidget(Id(widget_id), :Value, val)
end
end
# A mix-in to define items used by widgets
# that offer a selection from a list of values.
module ItemsSelection
# Items are defined as a list of pairs, where
# the first one is the ID and
# the second one is the user visible value
# @return [Array<Array(String,String)>]
# @example items method in widget
# def items
# [
# [ "Canada", _("Canada")],
# [ "USA", _("United States of America")],
# [ "North Pole", _("Really cold place")],
# ]
# end
def items
[]
end
def cwm_definition
super.merge(
"items" => items
)
end
# Change the list of items offered in widget.
# The format is the same as in {#items}
# @param items_list [Array<Array(String,String)>] new items
# @return [void]
def change_items(items_list)
val = items_list.map { |i| Item(Id(i[0]), i[1]) }
Yast::UI.ChangeWidget(Id(widget_id), :Items, val)
end
end
# An input field widget.
# The {#label} method is mandatory.
#
# @example input field widget child
# class MyWidget < CWM::InputField
# def initialize(myconfig)
# @config = myconfig
# end
#
# def label
# _("The best widget ever is:")
# end
#
# def init
# self.value = @config.value
# end
#
# def store
# @config.value = value
# end
# end
class InputField < AbstractWidget
self.widget_type = :inputfield
include ValueBasedWidget
abstract_method :label
end
# A Password widget.
# The {#label} method is mandatory.
#
# @see InputField for example of child
class Password < AbstractWidget
self.widget_type = :password
include ValueBasedWidget
abstract_method :label
end
# A CheckBox widget.
# The {#label} method is mandatory.
#
# @see InputField for example of child
class CheckBox < AbstractWidget
self.widget_type = :checkbox
include ValueBasedWidget
abstract_method :label
# @return [Boolean] true if the box is checked
def checked?
value == true
end
# @return [Boolean] true if the box is unchecked
def unchecked?
# explicit check as the value can be also nil,
# which is shown as a grayed-out box, with "indeterminate" meaning
value == false
end
# Checks the box
# @return [void]
def check
self.value = true
end
# Unchecks the box
# @return [void]
def uncheck
self.value = false
end
end
# A Combo box to select a value.
# The {#label} method is mandatory.
#
# @example combobox widget child
# class MyWidget < CWM::ComboBox
# def initialize(myconfig)
# @config = myconfig
# end
#
# def label
# _("Choose carefully:")
# end
#
# def init
# self.value = @config.value
# end
#
# def store
# @config.value = value
# end
#
# def items
# [
# [ "Canada", _("Canada")],
# [ "USA", _("United States of America")],
# [ "North Pole", _("Really cold place")],
# ]
# end
# end
class ComboBox < AbstractWidget
self.widget_type = :combobox
include ValueBasedWidget
include ItemsSelection
abstract_method :label
end
# Widget representing selection box to select value.
# The {#label} method is mandatory.
#
# @see ComboBox for child example
class SelectionBox < AbstractWidget
self.widget_type = :selection_box
include ItemsSelection
abstract_method :label
# @return [String] ID of the selected item
def value
Yast::UI.QueryWidget(Id(widget_id), :CurrentItem)
end
# @param val [String] ID of the selected item
def value=(val)
Yast::UI.ChangeWidget(Id(widget_id), :CurrentItem, val)
end
end
# A multi-selection box to select more values.
# The {#label} method is mandatory.
#
# @see {ComboBox} for child example
class MultiSelectionBox < AbstractWidget
self.widget_type = :multi_selection_box
include ItemsSelection
abstract_method :label
# @return [Array<String>] return IDs of selected items
def value
Yast::UI.QueryWidget(Id(widget_id), :SelectedItems)
end
# @param val [Array<String>] IDs of newly selected items
def value=(val)
Yast::UI.ChangeWidget(Id(widget_id), :SelectedItems, val)
end
end
# An integer field widget.
# The {#label} method is mandatory.
# It supports optional {#minimum} and {#maximum} methods
# for limiting the range.
# See {#cwm_definition} method for minimum and maximum example
#
# @see InputField for example of child
class IntField < AbstractWidget
self.widget_type = :intfield
include ValueBasedWidget
abstract_method :label
# @!method minimum
# @return [Fixnum] limited by C signed int range (-2**30 to 2**31-1).
# @!method maximum
# @return [Fixnum] limited by C signed int range (-2**30 to 2**31-1).
# The definition for IntField additionally supports
# `minimum` and `maximum` methods.
# @example minimum and maximum methods
# def minimum
# 50
# end
#
# def maximum
# 200
# end
def cwm_definition
res = {}
res["minimum"] = minimum if respond_to?(:minimum)
res["maximum"] = maximum if respond_to?(:maximum)
super.merge(res)
end
end
# A selection of a value via radio buttons.
# The {#label} method is mandatory.
#
# @see {ComboBox} for child example
class RadioButtons < AbstractWidget
self.widget_type = :radio_buttons
include ItemsSelection
abstract_method :label
# @!method vspacing
# @return [Fixnum] space between the options
# @!method hspacing
# @return [Fixnum] margin at both sides of the options list
def value
Yast::UI.QueryWidget(Id(widget_id), :CurrentButton)
end
def value=(val)
Yast::UI.ChangeWidget(Id(widget_id), :CurrentButton, val)
end
# See AbstractWidget#cwm_definition
# In addition to the base definition, this honors possible
# `vspacing` and `hspacing` methods
#
# @example defining additional space between the options
# def vspacing
# 1
# end
#
# @example defining some margin at both sides of the list of options
# def hspacing
# 3
# end
def cwm_definition
additional = {}
additional["vspacing"] = vspacing if respond_to?(:vspacing)
additional["hspacing"] = hspacing if respond_to?(:hspacing)
super.merge(additional)
end
end
# Widget representing button.
#
# @example push button widget child
# class MyEvilWidget < CWM::PushButton
# def label
# _("Win the lottery by clicking this.")
# end
#
# def handle
# Virus.install
# nil
# end
# end
class PushButton < AbstractWidget
self.widget_type = :push_button
abstract_method :label
end
# Widget representing menu button with its submenu
class MenuButton < AbstractWidget
self.widget_type = :menu_button
include ItemsSelection
abstract_method :label
end
# Multiline text widget
# @note label method is required and used as default value (TODO: incosistent with similar richtext in CWM itself)
class MultiLineEdit < AbstractWidget
self.widget_type = :multi_line_edit
include ValueBasedWidget
abstract_method :label
end
# Rich text widget supporting some highlighting
class RichText < AbstractWidget
self.widget_type = :richtext
include ValueBasedWidget
end
# Placeholder widget that is used to replace content on demand.
# The most important method is {#replace} which allows switching content
class ReplacePoint < CustomWidget
# @param id [Object] id of widget. Needed to redefine only if more than one
# placeholder needed to be in dialog. Parameter type is limited by component
# system
# @param widget [CWM::AbstractWidget] initial widget in placeholder
def initialize(id: "_placeholder", widget: Empty.new("_initial_placeholder"))
self.handle_all_events = true
self.widget_id = id
@widget = widget
end
def contents
ReplacePoint(Id(widget_id), widget_content(@widget))
end
def init
@widget.init if @widget.respond_to?(:init)
end
# Replaces content with different widget. All its events are properly
# handled.
# @param widget [CWM::AbstractWidget] widget to display and process events
def replace(widget)
log.info "replacing with new widget #{widget.inspect}"
Yast::UI.ReplaceWidget(Id(widget_id), widget_content(widget))
@widget = widget
init
end
def help
@widget.respond_to?(:help) ? @widget.help : ""
end
def handle(event)
return unless @widget.respond_to?(:handle)
if !@widget.handle_all_events
return if event["ID"] != @widget.widget_id
end
m = @widget.method(:handle)
if m.arity == 0
m.call
else
m.call(event)
end
end
def validate
@widget.respond_to?(:validate) ? @widget.validate : true
end
def store
@widget.store if @widget.respond_to?(:store)
end
def cleanup
@widget.cleanup if @widget.respond_to?(:cleanup)
end
private
def widget_content(widget)
definition = widget.cwm_definition
definition["_cwm_key"] = widget.widget_id # a bit hacky way to pass widget id
definition = Yast::CWM.prepareWidget(definition)
definition["widget"]
end
end
end