I have tested this alternative in a modified Sonntag-EventBus demo with pane and pane-container controls using both the identifier 'clear' and my preferred
ScrnCommand.clear
Code:
{define-macro public
{define-screen-command
?input-obj:expression
of ?screen-spec:expression
?enabled:{optional {enabled? ?:statements}}
do
?execute-body:verbatim
}
let id:#Identifier = null
let command-name:#Literal = null
let str:String = "define-screen-command_unknown_token"
{syntax-switch
input-obj,
must-match? = true
case {pattern ?type:identifier} do
set str = {String {input-obj.get-text}}
case {pattern ?expr:statement} do
{type-switch expr
case bop:BinaryOp do
|| {output "BOP: " & bop}
|| let clss-name:String = "UnknownClass"
{if (bop.operator == OperatorKind.Dot) then
|| set clss-name = {String {bop.left.get-text}} || test if class is kind of Command ?
set str = {String {bop.right.get-text}}
|| {output "parsed Right: " & str}
{if str.empty? then || what else should we test ?
set str = "define-screen-command_parse-error" || TODO set as a constant above
}
else
set str = "define-screen-command_invalid-op" || TODO set as a constant above
}
}
}
|| {if not {str.equal? "define-screen-command_unknown_token"} then {output input-obj}}
set id = {Identifier str} || what validation does this afford ? id is not re-used
set command-name = {Literal id.name}
|| Screen class
let screen-type:#CurlSource = null
let screen-var-decl:#CurlSource = null
{syntax-switch
screen-spec,
must-match? = true
case {pattern ?type:identifier} do
set screen-type = type
case {pattern ?expr:expression} do
let valid?:bool = false
{type-switch expr
case sb:SingleBinding do
{if sb.value == null and sb.type != null then
set screen-type = {non-null sb.type}
set screen-var-decl =
{expand-template
let ?sb = self.screen
}
set valid? = true
}
}
{if not valid? then
{expr.parse-error
"Unexpected expression '%s'",
{expr.get-text}
}
}
}
|| Command class
def command-class =
{Identifier
{screen-type.get-text} & '_' & command-name.value
}
|| default constructor
def constructor-src =
{expand-template
{constructor public {default screen:?screen-type}
{construct-super ?command-name, screen}
}
}
|| enabled? getter
def enabled-src =
{syntax-switch
enabled,
must-match? = true
case {pattern} do
{EmptySource}
case {pattern {enabled? ?enabled-body:statements}} do
{expand-template
{getter public {enabled?}:bool
?screen-var-decl
{return {value ?enabled-body} and super.enabled?}
}
}
}
|| execute method
def execute-src =
{expand-template
{method public {execute}:void
?screen-var-decl
?execute-body
}
}
{return
{expand-template
{define-class public ?=command-class
{inherits {ScreenCommand-of ?screen-type}}
?constructor-src
?enabled-src
?execute-src
}
{do
{ScreenCommandContext.registry.register-command
?command-name, ?screen-type, ?command-class
}
}
}
}
}
The demo alternative for App.A is
Code:
|| Clear command for the content of ScreenA; note use of parameterized type
{define-screen-command
ScrnCommand.clear of screen:AScreen || this version uses a BinaryOp in the macro
do
and other changes are only self-documenting, such as in message-handler
Code:
{def clear-text-entry-field:String = ScrnCommand.clear}
{if message == ScrnMsg.clear then
{self.do-command clear-text-entry-field}
}
Only
define-screen-command.scurl is altered within the Sonntag framework itself.