From c99b3976f244eea30cb3bb2ef69648172e30b8c2 Mon Sep 17 00:00:00 2001 From: "V. Khmelevskiy" Date: Wed, 16 Aug 2017 19:20:29 +0700 Subject: [PATCH] Added basic property metadata generation, and extended dmledit example to demonstrate it. --- examples/dmledit/src/dmledit.d | 69 ++++++++++++++++++++++++++++++---- src/dlangui/widgets/metadata.d | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/examples/dmledit/src/dmledit.d b/examples/dmledit/src/dmledit.d index 9d71dda7..a6dd34db 100644 --- a/examples/dmledit/src/dmledit.d +++ b/examples/dmledit/src/dmledit.d @@ -4,6 +4,7 @@ import dlangui; import dlangui.dialogs.filedlg; import dlangui.dialogs.dialog; import dlangui.dml.dmlhighlight; +import dlangui.widgets.metadata; import std.array : replaceFirst; mixin APP_ENTRY_POINT; @@ -91,6 +92,8 @@ q{VerticalLayout { } }; +// used to generate property lists once, then simply swap +StringListAdapter[string] propListsAdapters; class EditFrame : AppFrame { @@ -181,15 +184,15 @@ class EditFrame : AppFrame { window.close(); return true; case IDEActions.HelpAbout: - window.showMessageBox(UIString("About DlangUI ML Editor"d), - UIString("DLangIDE\n(C) Vadim Lopatin, 2015\nhttp://github.com/buggins/dlangui\nSimple editor for DML code"d)); + window.showMessageBox(UIString.fromRaw("About DlangUI ML Editor"d), + UIString.fromRaw("DLangIDE\n(C) Vadim Lopatin, 2015\nhttp://github.com/buggins/dlangui\nSimple editor for DML code"d)); return true; case IDEActions.FileNew: UIString caption; caption = "Create new DML file"d; FileDialog dlg = createFileDialog(caption, false); - dlg.addFilter(FileFilterEntry(UIString("DML files"d), "*.dml")); - dlg.addFilter(FileFilterEntry(UIString("All files"d), "*.*")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("DML files"d), "*.dml")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("All files"d), "*.*")); dlg.dialogResult = delegate(Dialog dlg, const Action result) { if (result.id == ACTION_OPEN.id) { string filename = result.stringParam; @@ -207,8 +210,8 @@ class EditFrame : AppFrame { UIString caption; caption = "Save DML File as"d; FileDialog dlg = createFileDialog(caption, false); - dlg.addFilter(FileFilterEntry(UIString("DML files"d), "*.dml")); - dlg.addFilter(FileFilterEntry(UIString("All files"d), "*.*")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("DML files"d), "*.dml")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("All files"d), "*.*")); dlg.dialogResult = delegate(Dialog dlg, const Action result) { if (result.id == ACTION_OPEN.id) { string filename = result.stringParam; @@ -221,8 +224,8 @@ class EditFrame : AppFrame { UIString caption; caption = "Open DML File"d; FileDialog dlg = createFileDialog(caption); - dlg.addFilter(FileFilterEntry(UIString("DML files"d), "*.dml")); - dlg.addFilter(FileFilterEntry(UIString("All files"d), "*.*")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("DML files"d), "*.dml")); + dlg.addFilter(FileFilterEntry(UIString.fromRaw("All files"d), "*.*")); dlg.dialogResult = delegate(Dialog dlg, const Action result) { if (result.id == ACTION_OPEN.id) { string filename = result.stringParam; @@ -302,6 +305,29 @@ class EditFrame : AppFrame { HorizontalLayout hlayout = new HorizontalLayout(); hlayout.layoutWidth = FILL_PARENT; hlayout.layoutHeight = FILL_PARENT; + WidgetsList widgetsList = new WidgetsList(); + StringListWidget propList = new StringListWidget(); + auto sla = new StringListAdapter(); + foreach(const ref widget; getRegisteredWidgetsList()) + { + auto propertyListAdapter = new StringListAdapter(); + if ( auto meta = findWidgetMetadata(widget) ) + { + foreach(prop; meta.properties) + { + propertyListAdapter.add(UIString.fromRaw(prop.name ~ " [" ~ to!string(prop.type) ~ "]" )); + propListsAdapters[widget] = propertyListAdapter; + } + } + sla.add(UIString.fromRaw(widget)); + } + widgetsList.adapter = sla; + auto leftPanel = new VerticalLayout(); + leftPanel.addChild(new TextWidget().text("Widgets").backgroundColor(0x7F7F7F) ); + leftPanel.addChild(widgetsList); + leftPanel.addChild(new TextWidget().text("Widget properties").backgroundColor(0x7F7F7F)); + leftPanel.addChild(propList); + hlayout.addChild(leftPanel); _editor = new DMLSourceEdit(); hlayout.addChild(_editor); _editor.text = SAMPLE_SOURCE_CODE; @@ -327,6 +353,17 @@ class EditFrame : AppFrame { updatePreview(); return true; }; + widgetsList.itemClick = delegate (Widget source, int itemIndex){ + propList.adapter = propListsAdapters[to!string(widgetsList.selectedItem)]; + return true; + }; + widgetsList.onItemDoubleClick = delegate (Widget source, int itemIndex) { + auto caret = _editor.caretPos; + auto widgetClassName = widgetsList.selectedItem; + EditOperation op = new EditOperation(EditAction.Replace, caret, widgetClassName); + _editor.content.performOperation(op, this); + }; + previewControls.addChild(cbFillHorizontal); previewControls.addChild(cbFillVertical); previewControls.addChild(cbHighlightBackground); @@ -344,6 +381,22 @@ class EditFrame : AppFrame { } +alias onItemDoubleClickHandler = void delegate (Widget source, int itemIndex); + +class WidgetsList : StringListWidget +{ + onItemDoubleClickHandler onItemDoubleClick; + + override bool onMouseEvent(MouseEvent event) { + bool result = super.onMouseEvent(event); + if (event.doubleClick) { + if (onItemDoubleClick !is null) + onItemDoubleClick(this, selectedItemIndex); + } + return result; + } +} + /// entry point for dlangui based application extern (C) int UIAppMain(string[] args) { diff --git a/src/dlangui/widgets/metadata.d b/src/dlangui/widgets/metadata.d index 601045c3..90e7c6a6 100644 --- a/src/dlangui/widgets/metadata.d +++ b/src/dlangui/widgets/metadata.d @@ -10,6 +10,8 @@ interface WidgetMetadataDef { string moduleName(); /// full class name, e.g. "dlangui.widgets.editors.EditLine" string fullName(); + /// property list, e.g. "backgroundColor" + WidgetPropertyMetadata[] properties(); } struct WidgetSignalMetadata { @@ -20,8 +22,22 @@ struct WidgetSignalMetadata { TypeInfo paramsType; } +/** +* Stores information about property +* +*/ +struct WidgetPropertyMetadata { + TypeInfo type; + string name; +} + private __gshared WidgetMetadataDef[string] _registeredWidgets; +string[] getRegisteredWidgetsList() +{ + return _registeredWidgets.keys; +} + WidgetMetadataDef findWidgetMetadata(string name) { if (auto p = name in _registeredWidgets) return *p; @@ -51,10 +67,60 @@ WidgetSignalMetadata[] getSignalList(alias T)() { return res; } +template isMarkupType(T) +{ + enum isMarkupType = is(T==int) || + is(T==float) || + is(T==double) || + is(T==bool) || + is(T==Rect) || + is(T==string) || + is(T==dstring) || + is(T==UIString) || + is(T==UIString[]) || + is(T==StringListValue[]); +} + +string generatePropertiesMetadata(alias T)() { + import std.algorithm.searching; + import std.traits; + import std.meta; + string str = "["; + WidgetPropertyMetadata[] res; + foreach(m; __traits(allMembers, T)) { + static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){ + // skip non-public members, only functions that takes 0 or 1 arguments, add only types that parseable in markup + static if (__traits(getProtection, __traits(getMember, T, m)) == "public") { + static if (isFunction!(__traits(getMember, T, m))) { + immutable int fnArity = arity!(__traits(getMember, T, m)); + static if (fnArity == 0 || fnArity == 1) { + // TODO: filter out templates, signals and such + static if ([__traits(getFunctionAttributes, __traits(getMember, T, m))[]].canFind("@property")) { + alias ret = ReturnType!(__traits(getMember, T, m)); + alias params = Parameters!(__traits(getMember, T, m)); + string typestring; + static if (fnArity == 0 && !__traits(isTemplate,ret) && isMarkupType!ret) + typestring = ret.stringof; + else static if (fnArity == 1 && !__traits(isTemplate,params[0]) && isMarkupType!(params[0])) + typestring = params[0].stringof; + if (typestring is null) + continue; + str ~= "WidgetPropertyMetadata( typeid(" ~ typestring ~ "), " ~ m.stringof ~ " ), "; + } + } + } + } + } + } + str ~= "]"; + return str; +} + string generateMetadataClass(alias t)() { //pragma(msg, moduleName!t); import std.traits; //pragma(msg, getSignalList!t); + //pragma(msg, generatePropertiesMetadata!t); immutable string metadataClassName = t.stringof ~ "Metadata"; return "class " ~ metadataClassName ~ " : WidgetMetadataDef { \n" ~ " override Widget create() {\n" ~ @@ -69,6 +135,9 @@ string generateMetadataClass(alias t)() { " override string fullName() {\n" ~ " return \"" ~ moduleName!t ~ "." ~ t.stringof ~ "\";\n" ~ " }\n" ~ + " override WidgetPropertyMetadata[] properties() {" ~ + " return " ~ generatePropertiesMetadata!t ~ ";\n" ~ + " }\n" ~ "}\n"; }