So, a little late to jump in, but figured I'd add my $0.02 to this
conversation.
I've been hard at work building a future-oriented declarative UI DSL.
Specifically, I extended the Java 8 JavaCC parser to include xml, json and
css expression syntax.
This allows creation of UI's like the following toy code:
<app
ref = "root"
title = "Hi"
data =
{
// The one button uses postIncrement++ so we start its count at 1
one: 1,
// The two button uses ++preIncrement, so we start its count at 0
two: 0,
text: ""
}
class = .{
.cls{
background-color: #011151;
}
.cls .btn {
background-color: #010931;
color: white;
}
}
>
<box
size = ( 300 * (()->150 + 10 * $root.data.one) )
align = center
>
<button ref="one" text="Button 1" class="btn button1" onClick=e-> {
$one.setText("You clicked one " + $root.data.one++ + " times");
} />
<button ref="two" text="Button 2" class="btn button2" onClick=e-> {
$two.setText("You clicked two " + ++$root.data.two + " times");
} />
</box>
<box align = center>
<button text="Close" onClick=System.exit(0) />
</box>
</app>
It allows you to freely mix and match any form of AST you care about,
then a code generation subsystem allows you to define handled for any tag
or attribute,
where you can turn any valid ast structure into whatever generated java
syntax you want.
The really fun part? It creates an interface, a base class and any number
of implementations, with both GWT and JavaFX currently supported (Android
support next; maybe iOS via J2ObjC some day).
For GWT, I am currently using Elemental 1 for low-level implementations,
however, the abstractions are all designed to be completely orthogonal to
low-level wiring.
Only the implementation classes know about GWT at all, so you can focus on
business logic and correct abstractions, instead of making business logic
dependent on UI implementation details.
The final bit I am working on before doing an official public release is
the tag generator.
That is, a generator for the tag "define-tag", which will create not just
the api, base and impl classes, but generate a code generator to then
handle that tag in other UIs.
Best of all, the GWT implementation actually exports web components, so
your defined "XApi declarative UI tag" actually becomes a web component
bound to your java classes,
which can use DOM attributes as accessors to primitives, and javascript
for any kind of interop you want to do with the outside world.
Here is an example component I am using in my prototype:
<define-tag
name = "Point"
tagName = "wti-point"
model =
@Extend("HasAuthors")
@Import("de.mocra.cy.shared.model.*")
{
lines: @Many // Ensures treatment of field as List-Type.
IntTo.class.$generic(ModelText.class),
title: String.class,
hasLinks: public boolean hasLinks(){
return
getLines().hasMatch(t->t.allText().hasMatch(TextNode::isLink));
}
}
api = [
public String getTitle() {
return $model.getTitle();
}
,
public $Self setTitle(String text) {
$model.setTitle(text);
return this;
}
]
impl =
@Import({
"de.mocra.cy.shared.ast.WtiParser",
"de.mocra.cy.shared.css.HasWtiStyle",
"de.mocra.cy.shared.css.WtiStyle"
})
[
private HasWtiStyle resources;, // ugh;, semicolon comma on purpose. ,
is for json array parser
// Either make it a statement w/
semi-colon, or use (parens)
public HasWtiStyle getResources() {
return resources;
},
public WtiStyle getStyle() {
return resources.css();
},
public $Self ui() {
return this;
}
]
ui =
<box>
<if notNull=$model.title>
<wti-title ref="title">$model::title</wti-title>
</if>
<for allOf=$model.lines as=text>
// setting the model directly is the cue for the generator
// that this is going to be an ElementWithModel that has a
.fromModel method.
<wti-text model=text style=getResources() />
</for>
</box>
/define-tag>
Finally, this framework is good for more than just UIs... I also use it to
generate all sorts of other repetitive tasks;
for example, TriFunction or QuadFunction, etc... The java sdk functional
interfaces are pretty sparse,
so I've taken to generating my own extrapolations of N-ary functional
interfaces (by default scaling up to 5-in, 5-out, with potential to
dynamically generate higher arities if needed for method references.
package xapi.fu.out;
@Generate(
in = {
<var name = "max"
default = 5 />,
<var name = "types"
default = ["Type", "Int", "Long", "Double", "Bool"] />
},
out = {
<loop
from = 1
to = $max
var = "size"
>
<generateInterface
var = {
typeParams :
$range(1, $size, $i->`O$i`),
wrappedParams :
$range(1, $size, $i->`Out1<O$i>`)
}
template =
@Import({
"xapi.fu.HasOutput",
"xapi.fu.Rethrowable",
"xapi.fu.Lambda"
})
public interface Out$size <$typeParams>
extends HasOutput, Rethrowable, Lambda {
Immutable$size <$wrappedParams> outAll();
@unfold(from = 1, to = $size, var = "n")
default O$n out$n(){
return out$nProvider().out1();
}
@unfold(from = 1, to = $size, var = "n")
default Out1<O$n> out$nProvider(){
return outAll().out$n();
}
@unfold(from = 1, to = $size, var = "n")
default Out$size<$typeParams> read$n(In1<O$n> callback){
callback.in(out$n());
return this;
}
@unfold(from = 1, to = $size, var = "n")
@var(
name = "outParams",
value = $range(1, $size, $p ->
$p == $n ? "To" : `O$p`)
)
default <To> Out$size<$outParams> mapped$n(In1Out1<O$n,
To> mapper){
return Out.out3(
$range(1, $size, $p ->
$p == $n ?
mapper.supplyDeferred(out$pProvider()) : out$pProvider()
)
);
}
@unfold(from = 1, to = $size, var = "n")
default <To> Out$size<$typeParams> spy$n(In1<O$n>
callback){
return
$if($size == 1, $print(()->{
O1 out = out1();
callback.in(out);
return out;
}))
.$else(
Out.out$size(
$range(1, $size, $p ->
$p == $n ? out$pProvider().spy1(callback) :
out$pProvider()
)
)
);
}
}
/generateInterface>
</loop>,
<loop
from = 1
to = 3
var = "size">
<var name = "asPrimitive"
value = {
Type:"O$n",
Int:"int",
Long:"long",
Double:"double",
Bool:"boolean"
} />,
<var name = "asObject"
value = {
Type:"O$n",
Int: "Integer",
Long: "Long",
Double: "Double",
Bool: "Boolean"
} />
</loop>
})
public class Out {
}
So, it's definitely a work in progress, but I am hoping that the community
finds value in it, and it gains more than one maintainer going forward. :-)
--
You received this message because you are subscribed to the Google Groups "GWT
Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-web-toolkit-contributors/d7da6526-caba-4f28-a43e-33676257a3bf%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.