From bec083326bb26785c47d0bdec856b0718b36690b Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 31 Mar 2015 18:33:06 +0300 Subject: [PATCH] ML parser, continue --- dlanguilib.visualdproj | 1 + examples/example1/src/example1.d | 9 ++++ src/dlangui/core/parser.d | 49 ++++++++++++----- src/dlangui/widgets/controls.d | 2 + src/dlangui/widgets/layouts.d | 3 ++ src/dlangui/widgets/metadata.d | 52 ++++++++++++++++++ src/dlangui/widgets/widget.d | 91 +++++++++++++++++++++++--------- 7 files changed, 170 insertions(+), 37 deletions(-) create mode 100644 src/dlangui/widgets/metadata.d diff --git a/dlanguilib.visualdproj b/dlanguilib.visualdproj index 295152c1..c903befa 100644 --- a/dlanguilib.visualdproj +++ b/dlanguilib.visualdproj @@ -426,6 +426,7 @@ + diff --git a/examples/example1/src/example1.d b/examples/example1/src/example1.d index 92de05bb..7ec39a55 100644 --- a/examples/example1/src/example1.d +++ b/examples/example1/src/example1.d @@ -445,6 +445,15 @@ extern (C) int UIAppMain(string[] args) { layout.addChild((new TextWidget(null, "Text widget3 with very long text"d)).textColor(0x004000)); layout.addChild(new VSpacer()); // vertical spacer to fill extra space + /* + import dlangui.core.parser; + Widget w = parseML(q{ + TextWidget { + } + }); + Log.d("id=", w.id); + */ + layout.childById("BTN1").onClickListener = (delegate (Widget w) { Log.d("onClick ", w.id); return true; }); layout.childById("BTN2").onClickListener = (delegate (Widget w) { Log.d("onClick ", w.id); return true; }); layout.childById("BTN3").onClickListener = (delegate (Widget w) { Log.d("onClick ", w.id); return true; }); diff --git a/src/dlangui/core/parser.d b/src/dlangui/core/parser.d index cabbf00f..1c334214 100644 --- a/src/dlangui/core/parser.d +++ b/src/dlangui/core/parser.d @@ -2,6 +2,7 @@ module dlangui.core.parser; import dlangui.core.linestream; import dlangui.widgets.widget; +import dlangui.widgets.metadata; import std.conv : to; class ParserException : Exception { @@ -313,7 +314,7 @@ class Tokenizer { } protected ref const(Token) parseOp(TokenType op) { - _token.type = TokenType.error; + _token.type = op; skipChar(); return _token; } @@ -380,26 +381,41 @@ class MLParser { protected Token _token; + + protected void nextToken() { + _token = _tokenizer.nextToken(); + Log.d("parsed token: ", _token.type, " ", _token.line, ":", _token.pos, " ", _token.text); + } + protected void skipWhitespaceAndEols() { for (;;) { - _token = _tokenizer.nextToken(); - if (_token.type != TokenType.eol && _token.type != TokenType.eof && _token.type != TokenType.whitespace && _token.type != TokenType.comment) + nextToken(); + if (_token.type != TokenType.eol && _token.type != TokenType.whitespace && _token.type != TokenType.comment) break; } if (_token.type == TokenType.error) _tokenizer.emitError("error while parsing ML code"); } + protected void skipWhitespace() { + for (;;) { + nextToken(); + if (_token.type != TokenType.whitespace && _token.type != TokenType.comment) + break; + } + if (_token.type == TokenType.error) + _tokenizer.emitError("error while parsing ML code"); + } protected void error(string msg) { _tokenizer.emitError(msg); } Widget createWidget(string name) { - WidgetFactory factory = getWidgetFactory(name); - if (!factory) - error("Cannot create widget " ~ name ~ " : unknown class"); - return factory(); + auto metadata = findWidgetMetadata(name); + if (!metadata) + error("Cannot create widget " ~ name ~ " : unregistered widget class"); + return metadata.create(); } protected void createContext(string name) { @@ -419,6 +435,11 @@ class MLParser { if (!_context) _tokenizer.emitError("No context widget is specified!"); skipWhitespaceAndEols(); + if (_token.type != TokenType.curlyClose) // { + _tokenizer.emitError("} is expected"); + skipWhitespaceAndEols(); + if (_token.type != TokenType.eof) // { + _tokenizer.emitError("end of file expected"); return _context; } @@ -427,10 +448,12 @@ class MLParser { _tokenizer = null; } - /// Parse DlangUI ML code - static Widget parse(string code, string filename = "", Widget context = null) { - MLParser parser = new MLParser(code, filename); - scope(exit) destroy(parser); - return parser.parse(); - } +} + + +/// Parse DlangUI ML code +public Widget parseML(string code, string filename = "", Widget context = null) { + MLParser parser = new MLParser(code, filename); + scope(exit) destroy(parser); + return parser.parse(); } diff --git a/src/dlangui/widgets/controls.d b/src/dlangui/widgets/controls.d index 8fe66470..49de599c 100644 --- a/src/dlangui/widgets/controls.d +++ b/src/dlangui/widgets/controls.d @@ -1029,3 +1029,5 @@ class CanvasWidget : Widget { } } +import dlangui.widgets.metadata; +mixin(registerWidgets!(Widget, TextWidget, Button, ImageWidget, ImageButton, ImageTextButton, RadioButton, CheckBox, ScrollBar)()); diff --git a/src/dlangui/widgets/layouts.d b/src/dlangui/widgets/layouts.d index f0628c40..9640882d 100644 --- a/src/dlangui/widgets/layouts.d +++ b/src/dlangui/widgets/layouts.d @@ -840,3 +840,6 @@ class TableLayout : WidgetGroupDefaultDrawing { } } + +import dlangui.widgets.metadata; +mixin(registerWidgets!(VerticalLayout, HorizontalLayout, TableLayout, FrameLayout)()); diff --git a/src/dlangui/widgets/metadata.d b/src/dlangui/widgets/metadata.d new file mode 100644 index 00000000..7f07ffbf --- /dev/null +++ b/src/dlangui/widgets/metadata.d @@ -0,0 +1,52 @@ +module dlangui.widgets.metadata; + +import dlangui.widgets.widget; + +interface WidgetMetadataDef { + Widget create(); +} + +private __gshared WidgetMetadataDef[string] _registeredWidgets; + +WidgetMetadataDef findWidgetMetadata(string name) { + if (auto p = name in _registeredWidgets) + return *p; + return null; +} + +void registerWidgetMetadata(string name, WidgetMetadataDef metadata) { + _registeredWidgets[name] = metadata; +} + +string generateMetadataClass(alias t)() { + import std.traits; + immutable string metadataClassName = t.stringof ~ "Metadata"; + return "class " ~ metadataClassName ~ " : WidgetMetadataDef { \n" ~ + " override Widget create() {\n" ~ + " return new " ~ moduleName!t ~ "." ~ t.stringof ~ "();\n" ~ + " }\n" ~ + "}\n"; +} + +string generateRegisterMetadataClass(alias t)() { + immutable string metadataClassName = t.stringof ~ "Metadata"; + return "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ metadataClassName ~ "());\n"; +} + +string registerWidgets(T...)() { + string classDefs; + string registerDefs; + foreach(t; T) { + //pragma(msg, t.stringof); + //pragma(msg, moduleName!t); + // + immutable string classdef = generateMetadataClass!t; + //pragma(msg, classdef); + immutable string registerdef = generateRegisterMetadataClass!t; + //pragma(msg, registerdef); + classDefs ~= classdef; + registerDefs ~= registerdef; + //registerWidgetMetadata(T.stringof, new Metadata()); + } + return classDefs ~ "\n__gshared static this() {\n" ~ registerDefs ~ "}"; +} diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 88ccdff9..90ddc6c8 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -1439,6 +1439,73 @@ class Widget { // override } + /// set string property value, for ML loaders + bool setProperty(string name, string value) { + if (name.equal("text")) { + text = UIString(value); + return true; + } + return false; + } + + /// set string property value, for ML loaders + bool setProperty(string name, UIString value) { + if (name.equal("text")) { + text = value; + return true; + } + return false; + } + + /// set int property value, for ML loaders + bool setProperty(string name, int value) { + if (name.equal("minWidth")) { + minWidth = value; + return true; + } + if (name.equal("maxWidth")) { + maxWidth = value; + return true; + } + if (name.equal("minHeight")) { + minHeight = value; + return true; + } + if (name.equal("maxHeight")) { + maxHeight = value; + return true; + } + if (name.equal("layoutWidth")) { + layoutWidth = value; + return true; + } + if (name.equal("layoutHeight")) { + layoutHeight = value; + return true; + } + if (name.equal("textColor")) { + textColor = cast(uint)value; + return true; + } + if (name.equal("backgroundColor")) { + backgroundColor = cast(uint)value; + return true; + } + return false; + } + + /// set int property value, for ML loaders + bool setProperty(string name, Rect value) { + if (name.equal("margins")) { + margins = value; + return true; + } + if (name.equal("padding")) { + padding = value; + return true; + } + return false; + } } /** Widget list holder. */ @@ -1596,28 +1663,4 @@ mixin template ActionTooltipSupport() { } } -alias WidgetFactory = Widget function(); -private __gshared WidgetFactory[string] _widgetFactories; - -WidgetFactory getWidgetFactory(string name) { - return _widgetFactories[name]; -} - -void registerWidgetFactory(string name, WidgetFactory factory) { - _widgetFactories[name] = factory; -} - -void registerWidgetFactories(T...)() { - foreach(t; T) { - //pragma(msg, t.stringof); - immutable string code = "WidgetFactory f = function() { return new " ~ t.stringof ~ "(); };"; - //pragma(msg, code); - mixin(code); - registerWidgetFactory(T.stringof, f); - } -} - -__gshared static this() { - registerWidgetFactories!Widget; -}