Browse Source

* Simple version of OI

michael 4 years ago
parent
commit
26d68a1ffa
1 changed files with 1197 additions and 0 deletions
  1. 1197 0
      packages/webwidget/propertygridwidget.pp

+ 1197 - 0
packages/webwidget/propertygridwidget.pp

@@ -0,0 +1,1197 @@
+unit propertygridwidget;
+
+{$mode objfpc}
+
+interface
+
+uses
+  Classes, SysUtils, types, web, webwidget, htmlwidgets, typinfo, contnrs;
+
+Type
+
+  { TPropertyEditor }
+  TSimplePropertyGridWidget = Class;
+
+  TPropertyEditor = Class (TObject)
+  private
+    FGrid : TSimplePropertyGridWidget;
+    FInfo : TTypeMemberProperty;
+    FInstance : TObject;
+    Level : Integer;
+    SubList: TFPObjectList;
+    ElRow : TJSHTMLElement;
+    ElName : TJSHTMLElement;
+    ElValue : TJSHTMLElement;
+    OnValueChanged : TNotifyEvent;
+    procedure SetInstance(AValue: TObject);
+  public
+    function CreateEditorElement (aTag : String; aID : String) : TJSHTMLElement; virtual;
+    Constructor Create(aGrid : TSimplePropertyGridWidget;aInstance : TObject; aInfo: TTypeMemberProperty);
+    Destructor destroy; override;
+    Procedure ValueChanged(Event: TJSEvent);
+    // Initialize editor
+    Function InitEditor : TJSHTMLElement; virtual; abstract;
+    // Apply editor value to property
+    Procedure EditorToProperty; virtual; abstract;
+    // Apply property value to editor
+    Procedure PropertyToEditor; virtual; abstract;
+    Property Info : TTypeMemberProperty Read FInfo;
+    Property Instance : TObject Read FInstance Write SetInstance;
+    Property Grid : TSimplePropertyGridWidget Read FGrid;
+  end;
+  TPropertyEditorClass = Class of TPropertyEditor;
+
+  { TBooleanPropertyEditor }
+
+  TBooleanPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TIntegerPropertyEditor }
+
+  TIntegerPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TFloatPropertyEditor }
+
+  TFloatPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TStringPropertyEditor }
+
+  TStringPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TLabelPropertyEditor }
+
+  TLabelPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TStringsPropertyEditor }
+
+  TStringsPropertyEditor = Class(TPropertyEditor)
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TStringListPropertyEditor }
+
+  TStringListPropertyEditor = Class(TPropertyEditor)
+    // Values and texts to display.
+    Procedure GetStrings(aTexts,aValues : TStrings); virtual; abstract;
+    // Selected Option value
+    Function GetSelectedValue : string;
+    // Selected Option text
+    Function GetSelectedText : string;
+    Procedure SetSelectedValue(aValue : string);
+    Function InitEditor: TJSHTMLElement; override;
+  end;
+
+  { TEnumerationPropertyEditor }
+
+  TEnumerationPropertyEditor = Class(TStringListPropertyEditor)
+    Procedure GetStrings(aTexts,aValues : TStrings); override;
+    procedure EditorToProperty; override;
+    procedure PropertyToEditor; override;
+  end;
+  
+  { TComponentPropertyEditor }
+
+  TComponentPropertyEditor = Class(TStringListPropertyEditor)
+    Procedure GetStrings(aTexts,aValues : TStrings); override;
+    procedure EditorToProperty; override;
+    procedure PropertyToEditor; override;
+  end;
+
+
+  { TSetPropertyEditor }
+
+  TSetPropertyEditor = Class(TPropertyEditor)
+    Function InitEditor: TJSHTMLElement; override;
+    procedure EditorToProperty; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TPersistentPropertyEditor }
+
+  TPersistentPropertyEditor = Class(TPropertyEditor)
+    labelEL,ButtoneL: TJSHTMLelement;
+    procedure EditorToProperty; override;
+    Function InitEditor: TJSHTMLElement; override;
+    procedure PropertyToEditor; override;
+  end;
+
+  { TSimplePropertyGridWidget }
+
+  // ObjectStack : 
+  // Collection: |PropName| -0 ClassName (N) |
+  // Persistent: | > PropName| -0 ClassName (N) |
+  // Top: 
+  // ObjectPath: < Instance1.PropName1[3].PropName2 |
+  // + (N) [ XXX V] - ]  
+  TSimplePropertyGridWidget = class(TWebWidget)
+  private
+    FLookupRoot: TComponent;
+    FMySubject: TObject;
+    FProperties : TFPObjectList;
+    procedure DoPropertyChanged(Sender: TObject);
+    procedure SetSubject(AValue: TObject);
+    procedure SetLookupRoot(AValue: TComponent);
+    procedure UpdatePropertyFromEditor(aRow: TPropertyEditor);
+  Protected
+    function CreateEditorElement (aTag : String; aID : String) : TJSHTMLElement; virtual;
+    function AllowProperty(M: TTypeMemberProperty): boolean; virtual;
+    procedure CreatePropertyList(aInstance: TObject; aList: TFPObjectList); virtual;
+    function CreatePropertyEditor(aInstance: TObject; aInfo: TTypeMemberProperty): TpropertyEditor; virtual;
+    procedure CreatePropertyGrid(aTableElement, aRowElement: TJSHTMLElement; FMySubject: TObject; aProperties: TFPObjectList); virtual;
+    function DoBooleanEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoComponentEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoEnumerationEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoFloatEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoIntegerEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoLabelEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
+    function DoPersistentEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
+    function DoSetEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
+    function DoStringEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
+    function DoStringsEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
+  Public
+    Constructor Create(aOwner : TComponent); override;
+    Destructor Destroy; override;
+    Function DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement; override;
+    Function HTMLTag : String; override;
+  Published
+    Property Subject : TObject Read FMySubject Write SetSubject;
+    Property LookupRoot : TComponent Read FLookupRoot Write SetLookupRoot;
+  end;
+
+implementation
+
+uses js;
+
+{ TSetPropertyEditor }
+
+function TSetPropertyEditor.InitEditor: TJSHTMLElement;
+
+Var
+  inp : TJSHTMLTableElement;
+  cell : TJSHTMLTableDataCellElement;
+  r : TJSHTMLTableRowElement;
+  vals : TIntegerDynArray;
+  eInfo : TTypeInfoEnum;
+  I : Integer;
+  selcb,CB : TJSHTMLInputElement;
+  lbl : TJSHTMLLabelElement;
+
+begin
+  Result:=CreateEditorElement('table','');
+  eInfo:=TTypeInfoEnum(TTypeInfoSet(Info.TypeInfo).CompType);
+  // Row with the property as a string.
+  R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
+  Result.Append(R);
+  cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
+  cell.InnerText:=GetSetProp(instance,Info);
+  R.AppendChild(Cell);
+  // For every element in the set, a checkbox row
+  for I:=TTypeInfoEnum(Info.TypeInfo).MinValue to TTypeInfoEnum(Info.TypeInfo).MaxValue do
+    begin
+    // Row
+    R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
+    Result.Append(R);
+    // Cell
+    cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
+    R.AppendChild(Cell);
+    // Input
+    CB:=TJSHTMLInputElement(CreateEditorElement('input',''));
+    cb._type:='checkbox';
+    cb.id:=Grid.GenerateID;
+    cb.dataset['enumval']:=IntTostr(i);
+    cell.AppendChild(cb);
+    // label for input
+    lbl:=TJSHTMLLabelElement(CreateEditorElement('label',''));
+    lbl.For_:=cb.Id;
+    lbl.InnerText:=TTypeInfoEnum(Info.TypeInfo).EnumType.IntToName[I];
+    cell.AppendChild(lbl);
+    end;
+end;
+
+procedure TSetPropertyEditor.EditorToProperty;
+Var
+  vals : TIntegerDynArray;
+  els: TJSNodeList;
+  cb : TJSHTMLInputElement;
+  I,V : Integer;
+
+begin
+  vals:=[];
+  els:=elValue.querySelectorAll('[data-enumval]');
+  for I:=0 to els.length-1 do
+    begin
+    CB:=TJSHTMLInputElement(els[i]);
+    if CB.checked then
+      begin
+      V:=StrToIntDef(String(CB.Dataset['enumval']),-1);
+      if V>=0 then
+        TJSArray(vals).push(V);
+      end;
+    end;
+  SetSetPropArray(instance,Info,Vals)
+end;
+
+procedure TSetPropertyEditor.PropertyToEditor;
+
+Var
+  vals : TIntegerDynArray;
+  els: TJSNodeList;
+  cb : TJSHTMLInputElement;
+  I : Integer;
+
+begin
+  vals:=GetSetPropArray(instance,Info);
+  els:=elValue.querySelectorAll('[data-enumval]');
+  for I:=0 to els.length-1 do
+    begin
+    CB:=TJSHTMLInputElement(els[i]);
+    CB.checked:=TJSArray(vals).indexOf(StrToIntDef(String(CB.Dataset['enumval']),-1))>=0;
+    end;
+end;
+
+{ TFloatPropertyEditor }
+
+procedure TFloatPropertyEditor.EditorToProperty;
+Var
+  S : String;
+  F : Double;
+
+begin
+  S:=TJSHTMLInputElement(ElValue).value;
+  if TryStrToFloat(S,F) then
+    SetFloatProp(Instance,Info,F)
+end;
+
+function TFloatPropertyEditor.InitEditor: TJSHTMLElement;
+
+begin
+  Result:=CreateEditorElement('input','');
+  TJSHTMLInputElement(Result)._type:='number';
+end;
+
+procedure TFloatPropertyEditor.PropertyToEditor;
+begin
+  TJSHTMLInputElement(elValue).Value:=FloatToStr(GetFloatProp(Instance,Info));
+end;
+
+{ TStringListPropertyEditor }
+
+function TStringListPropertyEditor.GetSelectedValue: string;
+
+Var
+  Sel : TJSHTMLSelectElement;
+
+begin
+  Sel:=TJSHTMLSelectElement(ElValue);
+  if Sel.selectedIndex>=0 then
+    Result:=TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).Value
+  else
+    Result:='';
+end;
+
+function TStringListPropertyEditor.GetSelectedText: string;
+Var
+  Sel : TJSHTMLSelectElement;
+
+begin
+  Sel:=TJSHTMLSelectElement(ElValue);
+  if Sel.selectedIndex>=0 then
+    Result:=TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).InnerText
+  else
+    Result:='';
+end;
+
+procedure TStringListPropertyEditor.SetSelectedValue(aValue: string);
+Var
+  Inp:TJSHTMLSelectElement;
+  I : integer;
+  o : TJSHTMLOptionElement;
+begin
+  Inp:=TJSHTMLSelectElement(elValue);
+  O:=Nil;
+  I:=0;
+  While (O=Nil) and (I<inp.options.length) do
+    begin
+    if TJSHTMLOptionElement(inp.options.Items[i]).value=aValue then
+      o:=TJSHTMLOptionElement(inp.options.Items[i]);
+    Inc(I);
+    end;
+  if assigned(o) then
+    o.selected:=True;
+end;
+
+function TStringListPropertyEditor.InitEditor: TJSHTMLElement;
+
+Var
+  inp : TJSHTMLSelectElement;
+  O,osel : TJSHTMLOptionElement;
+  avalS : String;
+  aVal,I : Integer;
+  aTexts,aValues : TStrings;
+
+begin
+  Result:=nil;
+  aTexts:=Nil;
+  aValues:=TstringList.Create;
+  try
+    aTexts:=TstringList.Create;
+    GetStrings(aTexts,aValues);
+    Result:=CreateEditorElement('select','');
+    Inp:=TJSHTMLSelectElement(Result);
+    inp.Multiple:=False;
+    For I:=0 to aTexts.Count-1 do
+      begin
+      O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
+      O.InnerText:=aTexts[i];
+      if (I<aValues.Count) then
+        O.value:=aValues[i];
+      Inp.appendChild(O)
+      end
+  finally
+    aTexts.Free;
+    aValues.Free;
+  end;
+end;
+
+{ TEnumerationPropertyEditor }
+
+procedure TEnumerationPropertyEditor.GetStrings(aTexts,aValues: TStrings);
+
+Var
+  I : Integer;
+
+begin
+  for I:=TTypeInfoEnum(Info.TypeInfo).MinValue to TTypeInfoEnum(Info.TypeInfo).MaxValue do
+    begin
+    aTexts.Add(TTypeInfoEnum(Info.TypeInfo).EnumType.IntToName[I]);
+    aValues.Add(IntToStr(I));
+    end;
+
+end;
+
+procedure TEnumerationPropertyEditor.EditorToProperty;
+
+Var
+  Sel : TJSHTMLSelectElement;
+  S : String;
+
+begin
+  S:=GetSelectedValue;
+  if S<>'' then
+    SetOrdProp(Instance,Info,StrToInt(S));
+end;
+
+procedure TEnumerationPropertyEditor.PropertyToEditor;
+
+var
+  aVal : Integer;
+
+begin
+  AVal:=GetOrdProp(Instance,Info);
+  SetSelectedValue(IntToStr(aVal));
+end;
+
+{ TComponentPropertyEditor }
+
+procedure TComponentPropertyEditor.GetStrings(aTexts,aValues: TStrings);
+
+Var
+  I : Integer;
+  N : String;
+
+begin
+  for I:=0 to Grid.LookupRoot.ComponentCount-1 do
+    begin
+    N:=Grid.LookupRoot.Components[i].Name;
+    if N<>'' then
+      aTexts.Add(N);
+    end;
+end;
+
+procedure TComponentPropertyEditor.EditorToProperty;
+
+Var
+  Sel : TJSHTMLSelectElement;
+  S : String;
+  C : TComponent;
+  
+begin
+  S:=GetSelectedValue;
+  if S<>'' then
+    begin
+    C:=Grid.LookupRoot.FindComponent(S);
+    SetObjectProp(Instance,Info,C);
+    end
+  else
+    C:=Nil;  
+  SetObjectProp(Instance,Info,C);
+end;
+
+procedure TComponentPropertyEditor.PropertyToEditor;
+
+var
+  aVal : TObject;
+
+begin
+  AVal:=GetObjectProp(Instance,Info);
+  if (AVal is TComponent) then
+    SetSelectedValue(TComponent(aVal).Name)
+  else
+    SetSelectedValue('');  
+end;
+
+{ TLabelPropertyEditor }
+
+procedure TLabelPropertyEditor.EditorToProperty;
+
+Var
+  S : String;
+
+begin
+  S:=TJSHTMLLabelElement(ElValue).InnerText;
+  SetStrProp(Instance,Info,S);
+end;
+
+function TLabelPropertyEditor.InitEditor: TJSHTMLElement;
+
+
+begin
+  Result:=CreateEditorElement('label','');
+end;
+
+procedure TLabelPropertyEditor.PropertyToEditor;
+begin
+  TJSHTMLLabelElement(elValue).InnerText:=GetStrProp(Instance,Info);
+end;
+
+{ TPersistentPropertyEditor }
+
+procedure TPersistentPropertyEditor.EditorToProperty;
+
+var
+  i : Integer;
+
+begin
+  For I:=0 to SubList.Count-1 do
+    TPersistentPropertyEditor(SubList[i]).EditorToProperty;
+end;
+
+function TPersistentPropertyEditor.InitEditor: TJSHTMLElement;
+
+Var
+  O : TObject;
+  I : Integer;
+  SPE : TPropertyEditor;
+
+begin
+  Result:=CreateEditorElement('div','');
+  ButtonEl:=CreateEditorElement('i','');
+  ButtonEl.ClassName:='fi fi-plus';
+  Result.AppendChild(ButtonEl);
+  Labelel:=CreateEditorElement('span','');
+  Labelel.className:='persistentname';
+  labelel.InnerText:='?';
+  O:=GetObjectProp(Instance,Info);
+  Writeln('Getting sublist for ',O.ClassName);
+  SubList:=TFPObjectList.Create(True);
+  Grid.CreatePropertyList(O,SubList);
+  For I:=0 to SubList.Count-1 do
+    begin
+    SPE:=TPropertyEditor(SubList[i]);
+    SPE.InitEditor;
+    end;
+end;
+
+procedure TPersistentPropertyEditor.PropertyToEditor;
+
+Var
+  I : Integer;
+  SPE : TPropertyEditor;
+  O : TObject;
+
+begin
+  LabelEl.innertext:=GetObjectProp(Instance,Info).ClassName;
+  O:=GetObjectProp(Self.Instance,Info);
+  For I:=0 to SubList.Count-1 do
+    begin
+    SPE:=TPropertyEditor(SubList[i]);
+    SPE.Instance:=O;
+    if Assigned(SPE.Instance) then
+      SPE.PropertyToEditor;
+    end;
+end;
+
+
+{ TStringPropertyEditor }
+
+procedure TStringPropertyEditor.EditorToProperty;
+Var
+  S : String;
+begin
+  S:=TJSHTMLInputElement(ElValue).value;
+  SetStrProp(Instance,Info,S);
+end;
+
+function TStringPropertyEditor.InitEditor: TJSHTMLElement;
+
+
+begin
+  Result:=CreateEditorElement('input','');
+  TJSHTMLInputElement(Result)._type:='text';
+end;
+
+procedure TStringPropertyEditor.PropertyToEditor;
+begin
+  TJSHTMLInputElement(elValue).Value:=GetStrProp(Instance,Info);
+end;
+
+{ TStringsPropertyEditor }
+
+procedure TStringsPropertyEditor.EditorToProperty;
+
+Var
+  L : TStrings;  
+begin
+  L:=TStringList.Create;
+  try
+    L.Text:=TJSHTMLTextAreaElement(ElValue).value;
+    SetObjectProp(Instance,Info,L);
+  finally
+    l.free;
+  end;  
+end;
+
+function TStringsPropertyEditor.InitEditor: TJSHTMLElement;
+
+Var
+  AVal : TObject;
+
+begin
+  Result:=TJSHTMLTextAreaElement(CreateEditorElement('textarea',''));
+  Result['rows']:='5';
+  Result['columns']:='40';
+end;
+
+procedure TStringsPropertyEditor.PropertyToEditor;
+
+Var
+  AVal : TObject;
+
+begin
+  AVal:=GetObjectProp(Instance,Info);
+  if Assigned(AVal) then
+    TJSHTMLTextAreaElement(elValue).Value:=TStrings(aVal).Text
+  else  
+    TJSHTMLTextAreaElement(elValue).Value:='';
+end;
+
+{ TIntegerPropertyEditor }
+
+procedure TIntegerPropertyEditor.EditorToProperty;
+
+Var
+  S : String;
+  I : NativeInt;
+
+begin
+  S:=TJSHTMLInputElement(ElValue).value;
+  if TryStrToInt64(S,I) then
+    SetOrdProp(Instance,Info,I)
+end;
+
+function TIntegerPropertyEditor.InitEditor: TJSHTMLElement;
+begin
+  Result:=CreateEditorElement('input','');
+  Result.Dataset['propname']:=Info.Name;
+  TJSHTMLInputElement(Result)._type:='number';
+end;
+
+procedure TIntegerPropertyEditor.PropertyToEditor;
+begin
+  TJSHTMLInputElement(ElValue).Value:=IntToStr(GetOrdProp(Instance,Info));
+end;
+
+{ TBooleanPropertyEditor }
+
+procedure TBooleanPropertyEditor.EditorToProperty;
+Var
+  B : Boolean;
+begin
+  B:=TJSHTMLInputElement(ElValue).checked;
+  SetBoolProp(Instance,Info,B);
+end;
+
+function TBooleanPropertyEditor.InitEditor: TJSHTMLElement;
+begin
+  Result:=CreateEditorElement('input','');
+  TJSHTMLInputElement(Result)._type:='checkbox';
+end;
+
+procedure TBooleanPropertyEditor.PropertyToEditor;
+begin
+  TJSHTMLInputElement(elValue).Checked:=GetBoolProp(Instance,Info);
+end;
+
+{ TPropertyEditor }
+
+procedure TPropertyEditor.ValueChanged(Event: TJSEvent);
+begin
+  If Assigned(OnValueChanged) then
+    OnValueChanged(Self);
+  EditorToProperty;
+end;
+
+destructor TPropertyEditor.destroy;
+begin
+  FreeAndNil(Sublist);
+  inherited destroy;
+end;
+
+{ TSimplePropertyGridWidget }
+
+procedure TSimplePropertyGridWidget.SetSubject(AValue: TObject);
+begin
+  Writeln('Setting subject ', Assigned(aValue));
+  if FMySubject=AValue then Exit;
+  FMySubject:=AValue;
+  if IsRendered then
+    Refresh;
+end;
+
+function TSimplePropertyGridWidget.AllowProperty(M : TTypeMemberProperty) : boolean;
+
+begin
+  Result:=M.TypeInfo.Kind in [tkInteger,tkChar,tkString,tkEnumeration,tkSet,tkDouble,tkBool,tkClass];
+end;
+
+procedure TSimplePropertyGridWidget.CreatePropertyList(aInstance : TObject; aList: TFPObjectList);
+
+Var
+  R : TPropertyEditor;
+  M: TTypeMemberProperty;
+
+begin
+  aList.Clear;
+  if Not Assigned(aInstance) then
+    exit;
+  for M in GetPropList(aInstance) do
+    if AllowProperty(M) then
+      begin
+      // Writeln('Row for property ',M.Name);
+      R:=CreatePropertyEditor(aInstance,M);
+      if R<>Nil then
+        aList.Add(R);
+      end;
+end;
+
+constructor TSimplePropertyGridWidget.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FProperties:=TFPObjectList.Create(True);
+end;
+
+destructor TSimplePropertyGridWidget.Destroy;
+begin
+  FreeAndNil(FProperties);
+  inherited Destroy;
+end;
+
+function TSimplePropertyGridWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement) : TJSHTMLElement;
+
+
+begin
+  // Writeln('Grid Render HTML',assigned(FMySubject));
+  Result:=Inherited DoRenderHTML(aParent,aElement);
+  CreatePropertyList(FMySubject,FProperties);
+  aElement.className:='propGridTable table-bordered table-sm table-dark';
+  CreatePropertyGrid(aElement,Nil,FMySubject,FProperties);
+end;
+
+procedure TSimplePropertyGridWidget.CreatePropertyGrid(aTableElement, aRowElement: TJSHTMLElement; FMySubject: TObject;
+  aProperties: TFPObjectList);
+
+Var
+  R : TPropertyEditor;
+  TR,TD : TJSHTMLElement;
+  I : integer;
+  O : TObject;
+
+begin
+  For I:=0 to aProperties.Count-1 do
+    begin
+    R:=TPropertyEditor(aProperties[i]);
+    TR:=CreateEditorElement('tr','');
+    TR.dataset['propertyName']:=R.Info.Name;
+    TR.ClassName:='propGridRow';
+    R.ElRow:=TR;
+    aTableElement.AppendChild(TR);
+    TD:=CreateEditorElement('td','');
+    TD.className:='propGridNameCell';
+    TR.AppendChild(TD);
+    TD.InnerText:=R.Info.Name;
+    R.ElName:=TD;
+    TD:=CreateEditorElement('td','');
+    TD.className:='propGridDataCell';
+    TR.AppendChild(TD);
+    R.ElValue:=R.InitEditor;
+    if Assigned(R.ElValue) then
+      begin
+      R.PropertyToEditor;
+      R.ElValue.Dataset['propname']:=R.Info.Name;
+      R.ElValue.addEventListener('change',@R.ValueChanged);
+      end;
+    TD.AppendChild(R.ElValue);
+    if Assigned(R.SubList) then
+      begin
+      O:=GetObjectProp(R.Instance,R.Info);
+      if Assigned(O) then
+        CreatePropertyGrid(aTableElement,TR,O,R.Sublist);
+      end;
+    end;
+end;
+
+function TSimplePropertyGridWidget.DoIntegerEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+
+begin
+  Result:=aEditor;
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('input','');
+    Result.Dataset['propname']:=aInfo.Info.Name;
+    TJSHTMLInputElement(Result)._type:='number';
+    end;
+  TJSHTMLInputElement(Result).Value:=IntToStr(GetOrdProp(aInfo.Instance,aInfo.Info));
+end;
+
+function TSimplePropertyGridWidget.DoFloatEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+
+begin
+  Result:=aEditor;
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('input','');
+    Result.Dataset['propname']:=aInfo.Info.Name;
+    TJSHTMLInputElement(Result)._type:='number';
+    end;
+  TJSHTMLInputElement(Result).Value:=FloatToStr(GetFloatProp(aInfo.Instance,aInfo.Info));
+end;
+
+function TSimplePropertyGridWidget.DoStringEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+begin
+  Result:=aEditor;
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('input','');
+    TJSHTMLInputElement(Result)._type:='text';
+    end;
+  TJSHTMLInputElement(Result).Value:=GetStrProp(aInfo.Instance,aInfo.Info);
+end;
+
+function TSimplePropertyGridWidget.DoBooleanEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+begin
+end;
+
+function TSimplePropertyGridWidget.DoEnumerationEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+
+Var
+  inp : TJSHTMLSelectElement;
+  O,osel : TJSHTMLOptionElement;
+  avalS : String;
+  aVal,I : Integer;
+
+
+begin
+  oSel:=nil;
+  Result:=aEditor;
+  AVal:=GetOrdProp(aInfo.Instance,aInfo.Info);
+  aValS:=IntTostr(aVal);
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('select','');
+    Inp:=TJSHTMLSelectElement(Result);
+    inp.Multiple:=False;
+    for I:=TTypeInfoEnum(aInfo.Info.TypeInfo).MinValue to TTypeInfoEnum(aInfo.Info.TypeInfo).MaxValue do
+      begin
+      O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
+      O.InnerText:=TTypeInfoEnum(aInfo.Info.TypeInfo).EnumType.IntToName[I];
+      O.value:=IntToStr(I);
+      if I=aVal then
+        oSel:=o;
+      Inp.appendChild(O)
+      end;
+    end
+  else
+    begin
+    Inp:=TJSHTMLSelectElement(Result);
+    for I:=0 to inp.options.length-1 do
+      if TJSHTMLOptionElement(inp.options.Items[i]).value=aVals then
+        oSel:=TJSHTMLOptionElement(inp.options.Items[i]);
+    end;
+  if assigned(oSel) then
+    oSel.selected:=True;
+  Inp.Value:=IntToStr(GetOrdProp(aInfo.Instance,aInfo.Info));
+end;
+
+function TSimplePropertyGridWidget.DoSetEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+Var
+  inp : TJSHTMLTableElement;
+  cell : TJSHTMLTableDataCellElement;
+  r : TJSHTMLTableRowElement;
+  vals : TIntegerDynArray;
+  eInfo : TTypeInfoEnum;
+  I : Integer;
+  selcb,CB : TJSHTMLInputElement;
+  lbl : TJSHTMLLabelElement;
+
+begin
+  result:=aEditor;
+  eInfo:=TTypeInfoEnum(TTypeInfoSet(aInfo.Info.TypeInfo).CompType);
+  vals:=GetSetPropArray(aInfo.instance,aInfo.Info);
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('table','');
+    // Row with the property as a string.
+    R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
+    Result.Append(R);
+    cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
+    cell.InnerText:=GetSetProp(aInfo.instance,aInfo.Info);
+    R.AppendChild(Cell);
+    // For every element in the set, a checkbox row
+    for I:=TTypeInfoEnum(aInfo.Info.TypeInfo).MinValue to TTypeInfoEnum(aInfo.Info.TypeInfo).MaxValue do
+      begin
+      // Row
+      R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
+      Result.Append(R);
+      // Cell
+      cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
+      R.AppendChild(Cell);
+      // Input
+      CB:=TJSHTMLInputElement(CreateEditorElement('input',''));
+      cb._type:='checkbox';
+      cb.id:=GenerateID;
+      cb.dataset['enumval']:=IntTostr(i);
+      cell.AppendChild(cb);
+      // label for input
+      lbl:=TJSHTMLLabelElement(CreateEditorElement('label',''));
+      lbl.For_:=cb.Id;
+      lbl.InnerText:=TTypeInfoEnum(aInfo.Info.TypeInfo).EnumType.IntToName[I];
+      cell.AppendChild(lbl);
+      end;
+    end;
+end;
+
+function TSimplePropertyGridWidget.DoComponentEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+
+Var
+  inp : TJSHTMLSelectElement;
+  O,osel : TJSHTMLOptionElement;
+  I : Integer;
+  L,aVal : TObject;
+
+begin
+  oSel:=nil;
+  Result:=aEditor;
+  AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
+  if Result=Nil then
+    begin
+    Result:=CreateEditorElement('select','');
+    Inp:=TJSHTMLSelectElement(Result);
+    inp.Multiple:=False;
+    if Assigned(LookupRoot) then
+       begin
+       For I:=0 to LookupRoot.ComponentCount-1 do
+         begin
+         O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
+         O.InnerText:=LookupRoot.Components[i].Name;
+         O.value:=LookupRoot.Components[i].Name;
+         if aVal=LookupRoot.Components[i] then
+           oSel:=o;
+         Inp.appendChild(O);
+         end;
+      end;
+    end
+  else
+    begin
+    Inp:=TJSHTMLSelectElement(Result);
+    if Assigned(LookupRoot) then
+      begin
+      I:=0;
+      While (oSeL=Nil) and (I<inp.options.length) do
+        begin
+        O:=TJSHTMLOptionElement(inp.options.Items[i]);
+        L:=LookupRoot.FindComponent(O.Value);
+        if L=aval then
+          oSel:=O;
+        inc(I);
+        end;
+      end;
+    end;
+  if assigned(oSel) then
+    oSel.selected:=True;
+end;
+
+function TSimplePropertyGridWidget.DoLabelEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+Var
+  AVal : TObject;
+
+begin
+  AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
+  Result:=aEditor;
+  if Result=Nil then
+    Result:=TJSHTMLLabelElement(CreateEditorElement('label',''));
+  Result.InnerText:=aVal.ClassName;
+end;
+
+function TSimplePropertyGridWidget.DoPersistentEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+Var
+  AVal : TObject;
+
+begin
+  AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
+  Result:=aEditor;
+  if Result=Nil then
+    Result:=TJSHTMLLabelElement(CreateEditorElement('label',''));
+  Result.InnerText:=aVal.ClassName;
+end;
+
+function TSimplePropertyGridWidget.DoStringsEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
+
+Var
+  AVal : TObject;
+
+begin
+  AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
+  Result:=aEditor;
+  if Result=Nil then
+    begin
+    Result:=TJSHTMLTextAreaElement(CreateEditorElement('textarea',''));
+    Result['rows']:='5';
+    end;
+  Result.InnerText:=aVal.ClassName;
+end;
+
+
+function TSimplePropertyGridWidget.CreatePropertyEditor (aInstance : TObject; aInfo: TTypeMemberProperty) : TpropertyEditor;
+
+Var
+  EditorClass : TPropertyEditorClass;
+  aEditor : TPropertyEditor;
+
+begin
+  EditorClass:=nil;
+  Result:=Nil;
+  // Writeln('Creating property grid editor for ',aInfo.Info.Name);
+  case aInfo.TypeInfo.Kind of
+    tkInteger : EditorClass:=TIntegerPropertyEditor;
+    tkChar    : EditorClass:=TStringPropertyEditor;
+    tkString  : EditorClass:=TStringPropertyEditor;
+    tkEnumeration : EditorClass:=TEnumerationPropertyEditor;
+    tkSet : EditorClass:=TSetPropertyEditor;
+    tkDouble : EditorClass:=TFloatPropertyEditor;
+    tkBool : EditorClass:=TBooleanPropertyEditor;
+    tkClass :
+       begin
+       If TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TComponent) then
+         EditorClass:=TComponentPropertyEditor
+       else if TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TStrings) then
+         EditorClass:=TStringsPropertyEditor
+       else if TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TPersistent) then
+         EditorClass:=TPersistentPropertyEditor
+       else
+         EditorClass:=TLabelPropertyEditor;
+       end;
+  end;
+  if Assigned(EditorClass) then
+    Result:= EditorClass.Create(self,aInstance,aInfo);
+end;
+
+procedure TSimplePropertyGridWidget.DoPropertyChanged(Sender: TObject);
+
+Var
+  aRow : TPropertyEditor absolute sender;
+
+begin
+  Writeln('Property ',aRow.Info.Name,' changed');
+end;
+
+(*
+procedure TSimplePropertyGridWidget.ApplyIntegerEditor(aRow : TPropertyEditor);
+
+Var
+  S : String;
+  I : NativeInt;
+
+begin
+  S:=TJSHTMLInputElement(aRow.ElValue).value;
+  if TryStrToInt64(S,I) then
+    SetOrdProp(aRow.Instance,aRow.Info,I)
+end;
+
+procedure TSimplePropertyGridWidget.ApplyStringEditor(aRow : TPropertyEditor);
+
+Var
+  S : String;
+
+begin
+  S:=TJSHTMLInputElement(aRow.ElValue).value;
+  SetStrProp(aRow.Instance,aRow.Info,S);
+end;
+
+procedure TSimplePropertyGridWidget.ApplyEnumEditor(aRow : TPropertyEditor);
+
+Var
+  Sel : TJSHTMLSelectElement;
+
+begin
+  Sel:=TJSHTMLSelectElement(aRow.ElValue);
+  SetOrdProp(aRow.Instance,aRow.Info,StrToInt(TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).value));
+end;
+
+
+procedure TSimplePropertyGridWidget.ApplySetEditor(aRow : TPropertyEditor);
+
+begin
+
+end;
+
+procedure TSimplePropertyGridWidget.ApplyFloatEditor(aRow : TPropertyEditor);
+
+Var
+  S : String;
+  D : Double;
+  Err : Integer;
+
+begin
+  S:=TJSHTMLInputElement(aRow.ElValue).value;
+  val(S,D,Err);
+  if Err<>0 then
+    if TryStrToFloat(S,D) then
+      Err:=0;
+  if Err=0 then
+    SetFloatProp(aRow.Instance,aRow.Info,D);
+end;
+
+procedure TSimplePropertyGridWidget.ApplyBooleanEditor(aRow : TPropertyEditor);
+
+
+begin
+end;
+
+procedure TSimplePropertyGridWidget.ApplyComponentEditor(aRow : TPropertyEditor);
+
+begin
+end;
+
+procedure TSimplePropertyGridWidget.ApplyStringsEditor(aRow : TPropertyEditor);
+
+begin
+end;
+
+procedure TSimplePropertyGridWidget.ApplyPersistentEditor(aRow : TPropertyEditor);
+
+begin
+end;
+*)
+
+procedure TSimplePropertyGridWidget.UpdatePropertyFromEditor(aRow : TPropertyEditor);
+
+begin
+// Writeln('Creating property grid editor for ',aInfo.Info.Name);
+
+(*
+  case Arow.Info.TypeInfo.Kind of
+    tkInteger : ApplyIntegerEditor(aRow);
+    tkChar    : ApplyStringEditor(aRow);
+    tkString  : ApplyStringEditor(aRow);
+    tkEnumeration : ApplyEnumEditor(aRow);
+    tkSet : ApplySetEditor(aRow);
+    tkDouble : ApplyFloatEditor(aRow);
+    tkBool : ApplyBooleanEditor(aRow);
+    tkClass :
+       begin
+       If TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TComponent) then
+         ApplyComponentEditor(aRow)
+       else if TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TStrings) then
+         ApplyStringsEditor(aRow)
+       else if TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TPersistent) then
+         ApplyPersistentEditor(aRow)
+       end;
+  end;
+*)
+//  RefreshProperties(aRow);
+end;
+
+function TSimplePropertyGridWidget.CreateEditorElement(aTag: String; aID: String): TJSHTMLElement;
+
+begin
+  Result:=CreateElement(aTag,aID);
+  Case lowercase(aTag) of
+    'input',
+    'select' :
+       Result.className:='form-control';
+  end;
+end;
+
+procedure TSimplePropertyGridWidget.SetLookupRoot(AValue: TComponent);
+begin
+  if FLookupRoot=AValue then Exit;
+  FLookupRoot:=AValue;
+  if IsRendered then
+    Refresh;
+end;
+
+procedure TPropertyEditor.SetInstance(AValue: TObject);
+begin
+  if FInstance=AValue then Exit;
+  FInstance:=AValue;
+end;
+
+function TPropertyEditor.CreateEditorElement(aTag: String; aID: String): TJSHTMLElement;
+begin
+  Result:=Grid.CreateEditorElement(aTag,aID);
+end;
+
+constructor TPropertyEditor.Create(aGrid: TSimplePropertyGridWidget; aInstance: TObject; aInfo: TTypeMemberProperty);
+begin
+  FGrid:=aGrid;
+  FInstance:=aInstance;
+  FInfo:=aInfo;
+end;
+
+function TSimplePropertyGridWidget.HTMLTag: String;
+begin
+  Result:='TABLE';
+end;
+
+end.
+