Added basic property metadata generation, and extended dmledit example to demonstrate it.

This commit is contained in:
V. Khmelevskiy 2017-08-16 19:20:29 +07:00
parent 075708fae3
commit c99b3976f2
2 changed files with 130 additions and 8 deletions

View File

@ -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) {

View File

@ -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";
}