Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split off some CWM widgets to separate files.
- Loading branch information
Showing
4 changed files
with
478 additions
and
472 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
module CWM | ||
# Represent base for any widget used in CWM. It can be passed as "widget" | ||
# argument. For more details about usage | ||
# see {Yast::CWMClass#show Yast::CWM.show} | ||
# | ||
# Underneath there is a widget library with a procedural API, using symbol parameters as widget IDs. | ||
# | ||
# The call sequence is: | ||
# | ||
# - {#initialize} is called by the Ruby constructor {.new} | ||
# - CWM#show builds a widget tree, using | ||
# - the AbstractWidget concrete class | ||
# - {#opt} widget options: `[:notify]` is needed if {#handle} is defined | ||
# - {#label} | ||
# - {#help} | ||
# | ||
# - {#init} may initialize the widget state from persistent storage | ||
# - loop: | ||
# - {#handle} may update other widgets if this one tells them to | ||
# - {#validate} may decide whether the user input is valid | ||
# - {#store} may store the widget state to persistent storage | ||
class AbstractWidget | ||
include Yast::UIShortcuts | ||
include Yast::I18n | ||
include Yast::Logger | ||
|
||
# By default, {#handle} has no argument and it is called | ||
# only for events of its own widget. | ||
# If true, {#handle}(event) is called for events of any widget. | ||
# @return [Boolean] | ||
def handle_all_events | ||
@handle_all_events.nil? ? false : @handle_all_events | ||
end | ||
attr_writer :handle_all_events | ||
|
||
# @return [String] An ID, unique within a dialog, used for the widget. | ||
# By default, the class name is used. | ||
def widget_id | ||
@widget_id || self.class.to_s | ||
end | ||
attr_writer :widget_id | ||
|
||
# Declare widget type for {Yast::CWMClass}. | ||
# Your derived widgets will not need to do this. | ||
# @param type [Symbol] | ||
# @return [void] | ||
def self.widget_type=(type) | ||
define_method(:widget_type) { type } | ||
end | ||
|
||
# The following methods are only documented but not defined | ||
# because we do not want to accidentally override the subtle defaults | ||
# used by Yast::CWMClass. | ||
|
||
# @!method help | ||
# @return [String] translated help text for the widget | ||
|
||
# @!method label | ||
# Derived classes must override this method to specify a label. | ||
# @return [String] translated label text for the widget | ||
|
||
# @!method opt | ||
# Specifies options passed to widget. When {handle} method is defined, it | ||
# is often expected to be immediatelly notified from widget and for that | ||
# purpose opt method with `[:notify]` as return value is needed. | ||
# @return [Array<Symbol>] options passed to widget | ||
# like `[:hstretch, :vstretch]` | ||
|
||
# @!method init | ||
# Initialize the widget: set initial value | ||
# @return [void] | ||
|
||
# @!method handle(*args) | ||
# @overload handle | ||
# Process an event generated by this widget. This method is invoked | ||
# if {#handle_all_events} is `false`. | ||
# @note defining {handle} itself does not pass `:notify` to widget. It has | ||
# to be done with {opt} method. | ||
# @return [nil,Symbol] what to return from the dialog, | ||
# or `nil` to continue processing | ||
# @overload handle(event) | ||
# Process an event generated a widget. This method is invoked | ||
# if {#handle_all_events} is `true`. | ||
# @note defining {handle} itself does not pass `:notify` to widget. It has | ||
# to be done with {opt} method. | ||
# @param event [Hash] see CWMClass | ||
# @return [nil,Symbol] what to return from the dialog, | ||
# or `nil` to continue processing | ||
|
||
# @!method validate | ||
# Validate widgets before ending the loop and storing. | ||
# @return [Boolean] validate widget value. | ||
# If it fails (`false`) the dialog will not return yet. | ||
|
||
# @!method store | ||
# Store the widget value for further processing | ||
# @return [void] | ||
|
||
# @!method cleanup | ||
# Clean up after the widget is destroyed | ||
# @return [void] | ||
|
||
# Generate widget definition for {Yast::CWMClass}. | ||
# It refers to | ||
# {#help}, {#label}, {#opt} | ||
# {#validate}, {#init}, {#handle}, {#store}, {#cleanup}. | ||
# @return [Hash{String => Object}] | ||
# @raise [RuntimeError] if a required method is not implemented | ||
# or widget_type is not set. | ||
def cwm_definition | ||
if !respond_to?(:widget_type) | ||
raise "Widget '#{self.class}' does set its widget type" | ||
end | ||
|
||
res = {} | ||
|
||
if respond_to?(:help) | ||
res["help"] = help | ||
else | ||
res["no_help"] = "" | ||
end | ||
res["label"] = label if respond_to?(:label) | ||
res["opt"] = opt if respond_to?(:opt) | ||
if respond_to?(:validate) | ||
res["validate_function"] = validate_method | ||
res["validate_type"] = :function | ||
end | ||
res["handle_events"] = [widget_id] unless handle_all_events | ||
res["init"] = init_method if respond_to?(:init) | ||
res["handle"] = handle_method if respond_to?(:handle) | ||
res["store"] = store_method if respond_to?(:store) | ||
res["cleanup"] = cleanup_method if respond_to?(:cleanup) | ||
res["widget"] = widget_type | ||
|
||
res | ||
end | ||
|
||
# @return [Boolean] Is widget open for interaction? | ||
def enabled? | ||
Yast::UI.QueryWidget(Id(widget_id), :Enabled) | ||
end | ||
|
||
# Opens widget for interaction | ||
# @return [void] | ||
def enable | ||
Yast::UI.ChangeWidget(Id(widget_id), :Enabled, true) | ||
end | ||
|
||
# Closes widget for interaction | ||
# @return [void] | ||
def disable | ||
Yast::UI.ChangeWidget(Id(widget_id), :Enabled, false) | ||
end | ||
|
||
protected | ||
|
||
# A helper to check if an event is invoked by this widget | ||
# @param event [Hash] a UI event | ||
def my_event?(event) | ||
widget_id == event["ID"] | ||
end | ||
|
||
# shortcut from Yast namespace to avoid including whole namespace | ||
# @todo kill converts in CWM module, to avoid this workaround for funrefs | ||
# @return [Yast::FunRef] | ||
def fun_ref(*args) | ||
Yast::FunRef.new(*args) | ||
end | ||
|
||
private | ||
|
||
# @note all methods here use wrappers to modify required parameters as CWM | ||
# have not so nice callbacks API | ||
def init_method | ||
fun_ref(method(:init_wrapper), "void (string)") | ||
end | ||
|
||
def init_wrapper(_widget_id) | ||
init | ||
end | ||
|
||
def handle_method | ||
fun_ref(method(:handle_wrapper), "symbol (string, map)") | ||
end | ||
|
||
# allows both variant of handle. with event map and without. | ||
# with map it make sense when handle_all_events is true or in custom widgets | ||
# with multiple elements, that generate events, otherwise map is not needed | ||
def handle_wrapper(_widget_id, event) | ||
m = method(:handle) | ||
if m.arity == 0 | ||
m.call | ||
else | ||
m.call(event) | ||
end | ||
end | ||
|
||
def store_method | ||
fun_ref(method(:store_wrapper), "void (string, map)") | ||
end | ||
|
||
def store_wrapper(_widget_id, _event) | ||
store | ||
end | ||
|
||
def cleanup_method | ||
fun_ref(method(:cleanup_wrapper), "void (string)") | ||
end | ||
|
||
def cleanup_wrapper(_widget_id) | ||
cleanup | ||
end | ||
|
||
def validate_method | ||
fun_ref(method(:validate_wrapper), "boolean (string, map)") | ||
end | ||
|
||
def validate_wrapper(_widget_id, _event) | ||
validate | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
module CWM | ||
# A custom widget that has its UI content defined in the method {#contents}. | ||
# Useful mainly when a specialized widget including more subwidgets should be | ||
# reusable at more places. | ||
# | ||
# @example custom widget child | ||
# class MyWidget < CWM::CustomWidget | ||
# def initialize | ||
# self.handle_all_events = true | ||
# end | ||
# | ||
# def contents | ||
# HBox( | ||
# PushButton(Id(:reset), _("Reset")), | ||
# PushButton(Id(:undo), _("Undo")) | ||
# ) | ||
# end | ||
# | ||
# def handle(event) | ||
# case event["ID"] | ||
# when :reset then ... | ||
# when :undo then ... | ||
# else ... | ||
# end | ||
# nil | ||
# end | ||
# end | ||
class CustomWidget < AbstractWidget | ||
self.widget_type = :custom | ||
|
||
# @!method contents | ||
# Must be defined by subclasses | ||
# @return [Yast::Term] a UI term; {AbstractWidget} are not allowed inside | ||
abstract_method :contents | ||
|
||
def cwm_definition | ||
res = { "custom_widget" => cwm_contents } | ||
|
||
res["handle_events"] = ids_in_contents unless handle_all_events | ||
|
||
super.merge(res) | ||
end | ||
|
||
# Returns all nested widgets used in contents | ||
def nested_widgets | ||
Yast.import "CWM" | ||
|
||
Yast::CWM.widgets_in_contents(contents) | ||
end | ||
|
||
protected | ||
|
||
# return contents converted to format understandable by CWM module | ||
# Basically it replace instance of AbstractWidget by its widget_id | ||
def cwm_contents | ||
Yast.import "CWM" | ||
|
||
Yast::CWM.widgets_contents(contents) | ||
end | ||
|
||
def ids_in_contents | ||
find_ids(contents) << widget_id | ||
end | ||
|
||
def find_ids(term) | ||
term.each_with_object([]) do |arg, res| | ||
next unless arg.is_a? Yast::Term | ||
|
||
if arg.value == :id | ||
res << arg.params[0] | ||
else | ||
res.concat(find_ids(arg)) | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.