propertygridwidget.pp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  1. unit propertygridwidget;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. Classes, SysUtils, types, web, webwidget, htmlwidgets, typinfo, contnrs;
  6. Type
  7. { TPropertyEditor }
  8. TSimplePropertyGridWidget = Class;
  9. TPropertyEditor = Class (TObject)
  10. private
  11. FGrid : TSimplePropertyGridWidget;
  12. FInfo : TTypeMemberProperty;
  13. FInstance : TObject;
  14. Level : Integer;
  15. SubList: TFPObjectList;
  16. ElRow : TJSHTMLElement;
  17. ElName : TJSHTMLElement;
  18. ElValue : TJSHTMLElement;
  19. OnValueChanged : TNotifyEvent;
  20. procedure SetInstance(AValue: TObject);
  21. public
  22. function CreateEditorElement (aTag : String; aID : String) : TJSHTMLElement; virtual;
  23. Constructor Create(aGrid : TSimplePropertyGridWidget;aInstance : TObject; aInfo: TTypeMemberProperty);
  24. Destructor destroy; override;
  25. Procedure ValueChanged(Event: TJSEvent);
  26. // Initialize editor
  27. Function InitEditor : TJSHTMLElement; virtual; abstract;
  28. // Apply editor value to property
  29. Procedure EditorToProperty; virtual; abstract;
  30. // Apply property value to editor
  31. Procedure PropertyToEditor; virtual; abstract;
  32. Property Info : TTypeMemberProperty Read FInfo;
  33. Property Instance : TObject Read FInstance Write SetInstance;
  34. Property Grid : TSimplePropertyGridWidget Read FGrid;
  35. end;
  36. TPropertyEditorClass = Class of TPropertyEditor;
  37. { TBooleanPropertyEditor }
  38. TBooleanPropertyEditor = Class(TPropertyEditor)
  39. procedure EditorToProperty; override;
  40. Function InitEditor: TJSHTMLElement; override;
  41. procedure PropertyToEditor; override;
  42. end;
  43. { TIntegerPropertyEditor }
  44. TIntegerPropertyEditor = Class(TPropertyEditor)
  45. procedure EditorToProperty; override;
  46. Function InitEditor: TJSHTMLElement; override;
  47. procedure PropertyToEditor; override;
  48. end;
  49. { TFloatPropertyEditor }
  50. TFloatPropertyEditor = Class(TPropertyEditor)
  51. procedure EditorToProperty; override;
  52. Function InitEditor: TJSHTMLElement; override;
  53. procedure PropertyToEditor; override;
  54. end;
  55. { TStringPropertyEditor }
  56. TStringPropertyEditor = Class(TPropertyEditor)
  57. procedure EditorToProperty; override;
  58. Function InitEditor: TJSHTMLElement; override;
  59. procedure PropertyToEditor; override;
  60. end;
  61. { TLabelPropertyEditor }
  62. TLabelPropertyEditor = Class(TPropertyEditor)
  63. procedure EditorToProperty; override;
  64. Function InitEditor: TJSHTMLElement; override;
  65. procedure PropertyToEditor; override;
  66. end;
  67. { TStringsPropertyEditor }
  68. TStringsPropertyEditor = Class(TPropertyEditor)
  69. procedure EditorToProperty; override;
  70. Function InitEditor: TJSHTMLElement; override;
  71. procedure PropertyToEditor; override;
  72. end;
  73. { TStringListPropertyEditor }
  74. TStringListPropertyEditor = Class(TPropertyEditor)
  75. // Values and texts to display.
  76. Procedure GetStrings(aTexts,aValues : TStrings); virtual; abstract;
  77. // Selected Option value
  78. Function GetSelectedValue : string;
  79. // Selected Option text
  80. Function GetSelectedText : string;
  81. Procedure SetSelectedValue(aValue : string);
  82. Function InitEditor: TJSHTMLElement; override;
  83. end;
  84. { TEnumerationPropertyEditor }
  85. TEnumerationPropertyEditor = Class(TStringListPropertyEditor)
  86. Procedure GetStrings(aTexts,aValues : TStrings); override;
  87. procedure EditorToProperty; override;
  88. procedure PropertyToEditor; override;
  89. end;
  90. { TComponentPropertyEditor }
  91. TComponentPropertyEditor = Class(TStringListPropertyEditor)
  92. Procedure GetStrings(aTexts,aValues : TStrings); override;
  93. procedure EditorToProperty; override;
  94. procedure PropertyToEditor; override;
  95. end;
  96. { TSetPropertyEditor }
  97. TSetPropertyEditor = Class(TPropertyEditor)
  98. Function InitEditor: TJSHTMLElement; override;
  99. procedure EditorToProperty; override;
  100. procedure PropertyToEditor; override;
  101. end;
  102. { TPersistentPropertyEditor }
  103. TPersistentPropertyEditor = Class(TPropertyEditor)
  104. labelEL,ButtoneL: TJSHTMLelement;
  105. procedure EditorToProperty; override;
  106. Function InitEditor: TJSHTMLElement; override;
  107. procedure PropertyToEditor; override;
  108. end;
  109. { TSimplePropertyGridWidget }
  110. // ObjectStack :
  111. // Collection: |PropName| -0 ClassName (N) |
  112. // Persistent: | > PropName| -0 ClassName (N) |
  113. // Top:
  114. // ObjectPath: < Instance1.PropName1[3].PropName2 |
  115. // + (N) [ XXX V] - ]
  116. TSimplePropertyGridWidget = class(TWebWidget)
  117. private
  118. FLookupRoot: TComponent;
  119. FMySubject: TObject;
  120. FProperties : TFPObjectList;
  121. procedure DoPropertyChanged(Sender: TObject);
  122. procedure SetSubject(AValue: TObject);
  123. procedure SetLookupRoot(AValue: TComponent);
  124. procedure UpdatePropertyFromEditor(aRow: TPropertyEditor);
  125. Protected
  126. function CreateEditorElement (aTag : String; aID : String) : TJSHTMLElement; virtual;
  127. function AllowProperty(M: TTypeMemberProperty): boolean; virtual;
  128. procedure CreatePropertyList(aInstance: TObject; aList: TFPObjectList); virtual;
  129. function CreatePropertyEditor(aInstance: TObject; aInfo: TTypeMemberProperty): TpropertyEditor; virtual;
  130. procedure CreatePropertyGrid(aTableElement, aRowElement: TJSHTMLElement; FMySubject: TObject; aProperties: TFPObjectList); virtual;
  131. function DoBooleanEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  132. function DoComponentEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  133. function DoEnumerationEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  134. function DoFloatEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  135. function DoIntegerEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  136. function DoLabelEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement; virtual;
  137. function DoPersistentEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
  138. function DoSetEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
  139. function DoStringEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
  140. function DoStringsEditor(aEditor: TJSHTMLElement; aInfo: TPropertyEditor): TJSHTMLElement;virtual;
  141. Public
  142. Constructor Create(aOwner : TComponent); override;
  143. Destructor Destroy; override;
  144. Function DoRenderHTML(aParent, aElement: TJSHTMLElement): TJSHTMLElement; override;
  145. Function HTMLTag : String; override;
  146. Published
  147. Property Subject : TObject Read FMySubject Write SetSubject;
  148. Property LookupRoot : TComponent Read FLookupRoot Write SetLookupRoot;
  149. end;
  150. implementation
  151. uses js;
  152. { TSetPropertyEditor }
  153. function TSetPropertyEditor.InitEditor: TJSHTMLElement;
  154. Var
  155. inp : TJSHTMLTableElement;
  156. cell : TJSHTMLTableDataCellElement;
  157. r : TJSHTMLTableRowElement;
  158. vals : TIntegerDynArray;
  159. eInfo : TTypeInfoEnum;
  160. I : Integer;
  161. selcb,CB : TJSHTMLInputElement;
  162. lbl : TJSHTMLLabelElement;
  163. begin
  164. Result:=CreateEditorElement('table','');
  165. eInfo:=TTypeInfoEnum(TTypeInfoSet(Info.TypeInfo).CompType);
  166. // Row with the property as a string.
  167. R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
  168. Result.Append(R);
  169. cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
  170. cell.InnerText:=GetSetProp(instance,Info);
  171. R.AppendChild(Cell);
  172. // For every element in the set, a checkbox row
  173. for I:=TTypeInfoEnum(Info.TypeInfo).MinValue to TTypeInfoEnum(Info.TypeInfo).MaxValue do
  174. begin
  175. // Row
  176. R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
  177. Result.Append(R);
  178. // Cell
  179. cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
  180. R.AppendChild(Cell);
  181. // Input
  182. CB:=TJSHTMLInputElement(CreateEditorElement('input',''));
  183. cb._type:='checkbox';
  184. cb.id:=Grid.GenerateID;
  185. cb.dataset['enumval']:=IntTostr(i);
  186. cell.AppendChild(cb);
  187. // label for input
  188. lbl:=TJSHTMLLabelElement(CreateEditorElement('label',''));
  189. lbl.For_:=cb.Id;
  190. lbl.InnerText:=TTypeInfoEnum(Info.TypeInfo).EnumType.IntToName[I];
  191. cell.AppendChild(lbl);
  192. end;
  193. end;
  194. procedure TSetPropertyEditor.EditorToProperty;
  195. Var
  196. vals : TIntegerDynArray;
  197. els: TJSNodeList;
  198. cb : TJSHTMLInputElement;
  199. I,V : Integer;
  200. begin
  201. vals:=[];
  202. els:=elValue.querySelectorAll('[data-enumval]');
  203. for I:=0 to els.length-1 do
  204. begin
  205. CB:=TJSHTMLInputElement(els[i]);
  206. if CB.checked then
  207. begin
  208. V:=StrToIntDef(String(CB.Dataset['enumval']),-1);
  209. if V>=0 then
  210. TJSArray(vals).push(V);
  211. end;
  212. end;
  213. SetSetPropArray(instance,Info,Vals)
  214. end;
  215. procedure TSetPropertyEditor.PropertyToEditor;
  216. Var
  217. vals : TIntegerDynArray;
  218. els: TJSNodeList;
  219. cb : TJSHTMLInputElement;
  220. I : Integer;
  221. begin
  222. vals:=GetSetPropArray(instance,Info);
  223. els:=elValue.querySelectorAll('[data-enumval]');
  224. for I:=0 to els.length-1 do
  225. begin
  226. CB:=TJSHTMLInputElement(els[i]);
  227. CB.checked:=TJSArray(vals).indexOf(StrToIntDef(String(CB.Dataset['enumval']),-1))>=0;
  228. end;
  229. end;
  230. { TFloatPropertyEditor }
  231. procedure TFloatPropertyEditor.EditorToProperty;
  232. Var
  233. S : String;
  234. F : Double;
  235. begin
  236. S:=TJSHTMLInputElement(ElValue).value;
  237. if TryStrToFloat(S,F) then
  238. SetFloatProp(Instance,Info,F)
  239. end;
  240. function TFloatPropertyEditor.InitEditor: TJSHTMLElement;
  241. begin
  242. Result:=CreateEditorElement('input','');
  243. TJSHTMLInputElement(Result)._type:='number';
  244. end;
  245. procedure TFloatPropertyEditor.PropertyToEditor;
  246. begin
  247. TJSHTMLInputElement(elValue).Value:=FloatToStr(GetFloatProp(Instance,Info));
  248. end;
  249. { TStringListPropertyEditor }
  250. function TStringListPropertyEditor.GetSelectedValue: string;
  251. Var
  252. Sel : TJSHTMLSelectElement;
  253. begin
  254. Sel:=TJSHTMLSelectElement(ElValue);
  255. if Sel.selectedIndex>=0 then
  256. Result:=TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).Value
  257. else
  258. Result:='';
  259. end;
  260. function TStringListPropertyEditor.GetSelectedText: string;
  261. Var
  262. Sel : TJSHTMLSelectElement;
  263. begin
  264. Sel:=TJSHTMLSelectElement(ElValue);
  265. if Sel.selectedIndex>=0 then
  266. Result:=TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).InnerText
  267. else
  268. Result:='';
  269. end;
  270. procedure TStringListPropertyEditor.SetSelectedValue(aValue: string);
  271. Var
  272. Inp:TJSHTMLSelectElement;
  273. I : integer;
  274. o : TJSHTMLOptionElement;
  275. begin
  276. Inp:=TJSHTMLSelectElement(elValue);
  277. O:=Nil;
  278. I:=0;
  279. While (O=Nil) and (I<inp.options.length) do
  280. begin
  281. if TJSHTMLOptionElement(inp.options.Items[i]).value=aValue then
  282. o:=TJSHTMLOptionElement(inp.options.Items[i]);
  283. Inc(I);
  284. end;
  285. if assigned(o) then
  286. o.selected:=True;
  287. end;
  288. function TStringListPropertyEditor.InitEditor: TJSHTMLElement;
  289. Var
  290. inp : TJSHTMLSelectElement;
  291. O,osel : TJSHTMLOptionElement;
  292. avalS : String;
  293. aVal,I : Integer;
  294. aTexts,aValues : TStrings;
  295. begin
  296. Result:=nil;
  297. aTexts:=Nil;
  298. aValues:=TstringList.Create;
  299. try
  300. aTexts:=TstringList.Create;
  301. GetStrings(aTexts,aValues);
  302. Result:=CreateEditorElement('select','');
  303. Inp:=TJSHTMLSelectElement(Result);
  304. inp.Multiple:=False;
  305. For I:=0 to aTexts.Count-1 do
  306. begin
  307. O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
  308. O.InnerText:=aTexts[i];
  309. if (I<aValues.Count) then
  310. O.value:=aValues[i];
  311. Inp.appendChild(O)
  312. end
  313. finally
  314. aTexts.Free;
  315. aValues.Free;
  316. end;
  317. end;
  318. { TEnumerationPropertyEditor }
  319. procedure TEnumerationPropertyEditor.GetStrings(aTexts,aValues: TStrings);
  320. Var
  321. I : Integer;
  322. begin
  323. for I:=TTypeInfoEnum(Info.TypeInfo).MinValue to TTypeInfoEnum(Info.TypeInfo).MaxValue do
  324. begin
  325. aTexts.Add(TTypeInfoEnum(Info.TypeInfo).EnumType.IntToName[I]);
  326. aValues.Add(IntToStr(I));
  327. end;
  328. end;
  329. procedure TEnumerationPropertyEditor.EditorToProperty;
  330. Var
  331. Sel : TJSHTMLSelectElement;
  332. S : String;
  333. begin
  334. S:=GetSelectedValue;
  335. if S<>'' then
  336. SetOrdProp(Instance,Info,StrToInt(S));
  337. end;
  338. procedure TEnumerationPropertyEditor.PropertyToEditor;
  339. var
  340. aVal : Integer;
  341. begin
  342. AVal:=GetOrdProp(Instance,Info);
  343. SetSelectedValue(IntToStr(aVal));
  344. end;
  345. { TComponentPropertyEditor }
  346. procedure TComponentPropertyEditor.GetStrings(aTexts,aValues: TStrings);
  347. Var
  348. I : Integer;
  349. N : String;
  350. begin
  351. for I:=0 to Grid.LookupRoot.ComponentCount-1 do
  352. begin
  353. N:=Grid.LookupRoot.Components[i].Name;
  354. if N<>'' then
  355. aTexts.Add(N);
  356. end;
  357. end;
  358. procedure TComponentPropertyEditor.EditorToProperty;
  359. Var
  360. Sel : TJSHTMLSelectElement;
  361. S : String;
  362. C : TComponent;
  363. begin
  364. S:=GetSelectedValue;
  365. if S<>'' then
  366. begin
  367. C:=Grid.LookupRoot.FindComponent(S);
  368. SetObjectProp(Instance,Info,C);
  369. end
  370. else
  371. C:=Nil;
  372. SetObjectProp(Instance,Info,C);
  373. end;
  374. procedure TComponentPropertyEditor.PropertyToEditor;
  375. var
  376. aVal : TObject;
  377. begin
  378. AVal:=GetObjectProp(Instance,Info);
  379. if (AVal is TComponent) then
  380. SetSelectedValue(TComponent(aVal).Name)
  381. else
  382. SetSelectedValue('');
  383. end;
  384. { TLabelPropertyEditor }
  385. procedure TLabelPropertyEditor.EditorToProperty;
  386. Var
  387. S : String;
  388. begin
  389. S:=TJSHTMLLabelElement(ElValue).InnerText;
  390. SetStrProp(Instance,Info,S);
  391. end;
  392. function TLabelPropertyEditor.InitEditor: TJSHTMLElement;
  393. begin
  394. Result:=CreateEditorElement('label','');
  395. end;
  396. procedure TLabelPropertyEditor.PropertyToEditor;
  397. begin
  398. TJSHTMLLabelElement(elValue).InnerText:=GetStrProp(Instance,Info);
  399. end;
  400. { TPersistentPropertyEditor }
  401. procedure TPersistentPropertyEditor.EditorToProperty;
  402. var
  403. i : Integer;
  404. begin
  405. For I:=0 to SubList.Count-1 do
  406. TPersistentPropertyEditor(SubList[i]).EditorToProperty;
  407. end;
  408. function TPersistentPropertyEditor.InitEditor: TJSHTMLElement;
  409. Var
  410. O : TObject;
  411. I : Integer;
  412. SPE : TPropertyEditor;
  413. begin
  414. Result:=CreateEditorElement('div','');
  415. ButtonEl:=CreateEditorElement('i','');
  416. ButtonEl.ClassName:='fi fi-plus';
  417. Result.AppendChild(ButtonEl);
  418. Labelel:=CreateEditorElement('span','');
  419. Labelel.className:='persistentname';
  420. labelel.InnerText:='?';
  421. O:=GetObjectProp(Instance,Info);
  422. Writeln('Getting sublist for ',O.ClassName);
  423. SubList:=TFPObjectList.Create(True);
  424. Grid.CreatePropertyList(O,SubList);
  425. For I:=0 to SubList.Count-1 do
  426. begin
  427. SPE:=TPropertyEditor(SubList[i]);
  428. SPE.InitEditor;
  429. end;
  430. end;
  431. procedure TPersistentPropertyEditor.PropertyToEditor;
  432. Var
  433. I : Integer;
  434. SPE : TPropertyEditor;
  435. O : TObject;
  436. begin
  437. LabelEl.innertext:=GetObjectProp(Instance,Info).ClassName;
  438. O:=GetObjectProp(Self.Instance,Info);
  439. For I:=0 to SubList.Count-1 do
  440. begin
  441. SPE:=TPropertyEditor(SubList[i]);
  442. SPE.Instance:=O;
  443. if Assigned(SPE.Instance) then
  444. SPE.PropertyToEditor;
  445. end;
  446. end;
  447. { TStringPropertyEditor }
  448. procedure TStringPropertyEditor.EditorToProperty;
  449. Var
  450. S : String;
  451. begin
  452. S:=TJSHTMLInputElement(ElValue).value;
  453. SetStrProp(Instance,Info,S);
  454. end;
  455. function TStringPropertyEditor.InitEditor: TJSHTMLElement;
  456. begin
  457. Result:=CreateEditorElement('input','');
  458. TJSHTMLInputElement(Result)._type:='text';
  459. end;
  460. procedure TStringPropertyEditor.PropertyToEditor;
  461. begin
  462. TJSHTMLInputElement(elValue).Value:=GetStrProp(Instance,Info);
  463. end;
  464. { TStringsPropertyEditor }
  465. procedure TStringsPropertyEditor.EditorToProperty;
  466. Var
  467. L : TStrings;
  468. begin
  469. L:=TStringList.Create;
  470. try
  471. L.Text:=TJSHTMLTextAreaElement(ElValue).value;
  472. SetObjectProp(Instance,Info,L);
  473. finally
  474. l.free;
  475. end;
  476. end;
  477. function TStringsPropertyEditor.InitEditor: TJSHTMLElement;
  478. Var
  479. AVal : TObject;
  480. begin
  481. Result:=TJSHTMLTextAreaElement(CreateEditorElement('textarea',''));
  482. Result['rows']:='5';
  483. Result['columns']:='40';
  484. end;
  485. procedure TStringsPropertyEditor.PropertyToEditor;
  486. Var
  487. AVal : TObject;
  488. begin
  489. AVal:=GetObjectProp(Instance,Info);
  490. if Assigned(AVal) then
  491. TJSHTMLTextAreaElement(elValue).Value:=TStrings(aVal).Text
  492. else
  493. TJSHTMLTextAreaElement(elValue).Value:='';
  494. end;
  495. { TIntegerPropertyEditor }
  496. procedure TIntegerPropertyEditor.EditorToProperty;
  497. Var
  498. S : String;
  499. I : NativeInt;
  500. begin
  501. S:=TJSHTMLInputElement(ElValue).value;
  502. if TryStrToInt64(S,I) then
  503. SetOrdProp(Instance,Info,I)
  504. end;
  505. function TIntegerPropertyEditor.InitEditor: TJSHTMLElement;
  506. begin
  507. Result:=CreateEditorElement('input','');
  508. Result.Dataset['propname']:=Info.Name;
  509. TJSHTMLInputElement(Result)._type:='number';
  510. end;
  511. procedure TIntegerPropertyEditor.PropertyToEditor;
  512. begin
  513. TJSHTMLInputElement(ElValue).Value:=IntToStr(GetOrdProp(Instance,Info));
  514. end;
  515. { TBooleanPropertyEditor }
  516. procedure TBooleanPropertyEditor.EditorToProperty;
  517. Var
  518. B : Boolean;
  519. begin
  520. B:=TJSHTMLInputElement(ElValue).checked;
  521. SetBoolProp(Instance,Info,B);
  522. end;
  523. function TBooleanPropertyEditor.InitEditor: TJSHTMLElement;
  524. begin
  525. Result:=CreateEditorElement('input','');
  526. TJSHTMLInputElement(Result)._type:='checkbox';
  527. end;
  528. procedure TBooleanPropertyEditor.PropertyToEditor;
  529. begin
  530. TJSHTMLInputElement(elValue).Checked:=GetBoolProp(Instance,Info);
  531. end;
  532. { TPropertyEditor }
  533. procedure TPropertyEditor.ValueChanged(Event: TJSEvent);
  534. begin
  535. If Assigned(OnValueChanged) then
  536. OnValueChanged(Self);
  537. EditorToProperty;
  538. end;
  539. destructor TPropertyEditor.destroy;
  540. begin
  541. FreeAndNil(Sublist);
  542. inherited destroy;
  543. end;
  544. { TSimplePropertyGridWidget }
  545. procedure TSimplePropertyGridWidget.SetSubject(AValue: TObject);
  546. begin
  547. Writeln('Setting subject ', Assigned(aValue));
  548. if FMySubject=AValue then Exit;
  549. FMySubject:=AValue;
  550. if IsRendered then
  551. Refresh;
  552. end;
  553. function TSimplePropertyGridWidget.AllowProperty(M : TTypeMemberProperty) : boolean;
  554. begin
  555. Result:=M.TypeInfo.Kind in [tkInteger,tkChar,tkString,tkEnumeration,tkSet,tkDouble,tkBool,tkClass];
  556. end;
  557. procedure TSimplePropertyGridWidget.CreatePropertyList(aInstance : TObject; aList: TFPObjectList);
  558. Var
  559. R : TPropertyEditor;
  560. M: TTypeMemberProperty;
  561. begin
  562. aList.Clear;
  563. if Not Assigned(aInstance) then
  564. exit;
  565. for M in GetPropList(aInstance) do
  566. if AllowProperty(M) then
  567. begin
  568. // Writeln('Row for property ',M.Name);
  569. R:=CreatePropertyEditor(aInstance,M);
  570. if R<>Nil then
  571. aList.Add(R);
  572. end;
  573. end;
  574. constructor TSimplePropertyGridWidget.Create(aOwner: TComponent);
  575. begin
  576. inherited Create(aOwner);
  577. FProperties:=TFPObjectList.Create(True);
  578. end;
  579. destructor TSimplePropertyGridWidget.Destroy;
  580. begin
  581. FreeAndNil(FProperties);
  582. inherited Destroy;
  583. end;
  584. function TSimplePropertyGridWidget.DoRenderHTML(aParent, aElement: TJSHTMLElement) : TJSHTMLElement;
  585. begin
  586. // Writeln('Grid Render HTML',assigned(FMySubject));
  587. Result:=Inherited DoRenderHTML(aParent,aElement);
  588. CreatePropertyList(FMySubject,FProperties);
  589. aElement.className:='propGridTable table-bordered table-sm table-dark';
  590. CreatePropertyGrid(aElement,Nil,FMySubject,FProperties);
  591. end;
  592. procedure TSimplePropertyGridWidget.CreatePropertyGrid(aTableElement, aRowElement: TJSHTMLElement; FMySubject: TObject;
  593. aProperties: TFPObjectList);
  594. Var
  595. R : TPropertyEditor;
  596. TR,TD : TJSHTMLElement;
  597. I : integer;
  598. O : TObject;
  599. begin
  600. For I:=0 to aProperties.Count-1 do
  601. begin
  602. R:=TPropertyEditor(aProperties[i]);
  603. TR:=CreateEditorElement('tr','');
  604. TR.dataset['propertyName']:=R.Info.Name;
  605. TR.ClassName:='propGridRow';
  606. R.ElRow:=TR;
  607. aTableElement.AppendChild(TR);
  608. TD:=CreateEditorElement('td','');
  609. TD.className:='propGridNameCell';
  610. TR.AppendChild(TD);
  611. TD.InnerText:=R.Info.Name;
  612. R.ElName:=TD;
  613. TD:=CreateEditorElement('td','');
  614. TD.className:='propGridDataCell';
  615. TR.AppendChild(TD);
  616. R.ElValue:=R.InitEditor;
  617. if Assigned(R.ElValue) then
  618. begin
  619. R.PropertyToEditor;
  620. R.ElValue.Dataset['propname']:=R.Info.Name;
  621. R.ElValue.addEventListener('change',@R.ValueChanged);
  622. end;
  623. TD.AppendChild(R.ElValue);
  624. if Assigned(R.SubList) then
  625. begin
  626. O:=GetObjectProp(R.Instance,R.Info);
  627. if Assigned(O) then
  628. CreatePropertyGrid(aTableElement,TR,O,R.Sublist);
  629. end;
  630. end;
  631. end;
  632. function TSimplePropertyGridWidget.DoIntegerEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  633. begin
  634. Result:=aEditor;
  635. if Result=Nil then
  636. begin
  637. Result:=CreateEditorElement('input','');
  638. Result.Dataset['propname']:=aInfo.Info.Name;
  639. TJSHTMLInputElement(Result)._type:='number';
  640. end;
  641. TJSHTMLInputElement(Result).Value:=IntToStr(GetOrdProp(aInfo.Instance,aInfo.Info));
  642. end;
  643. function TSimplePropertyGridWidget.DoFloatEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  644. begin
  645. Result:=aEditor;
  646. if Result=Nil then
  647. begin
  648. Result:=CreateEditorElement('input','');
  649. Result.Dataset['propname']:=aInfo.Info.Name;
  650. TJSHTMLInputElement(Result)._type:='number';
  651. end;
  652. TJSHTMLInputElement(Result).Value:=FloatToStr(GetFloatProp(aInfo.Instance,aInfo.Info));
  653. end;
  654. function TSimplePropertyGridWidget.DoStringEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  655. begin
  656. Result:=aEditor;
  657. if Result=Nil then
  658. begin
  659. Result:=CreateEditorElement('input','');
  660. TJSHTMLInputElement(Result)._type:='text';
  661. end;
  662. TJSHTMLInputElement(Result).Value:=GetStrProp(aInfo.Instance,aInfo.Info);
  663. end;
  664. function TSimplePropertyGridWidget.DoBooleanEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  665. begin
  666. end;
  667. function TSimplePropertyGridWidget.DoEnumerationEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  668. Var
  669. inp : TJSHTMLSelectElement;
  670. O,osel : TJSHTMLOptionElement;
  671. avalS : String;
  672. aVal,I : Integer;
  673. begin
  674. oSel:=nil;
  675. Result:=aEditor;
  676. AVal:=GetOrdProp(aInfo.Instance,aInfo.Info);
  677. aValS:=IntTostr(aVal);
  678. if Result=Nil then
  679. begin
  680. Result:=CreateEditorElement('select','');
  681. Inp:=TJSHTMLSelectElement(Result);
  682. inp.Multiple:=False;
  683. for I:=TTypeInfoEnum(aInfo.Info.TypeInfo).MinValue to TTypeInfoEnum(aInfo.Info.TypeInfo).MaxValue do
  684. begin
  685. O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
  686. O.InnerText:=TTypeInfoEnum(aInfo.Info.TypeInfo).EnumType.IntToName[I];
  687. O.value:=IntToStr(I);
  688. if I=aVal then
  689. oSel:=o;
  690. Inp.appendChild(O)
  691. end;
  692. end
  693. else
  694. begin
  695. Inp:=TJSHTMLSelectElement(Result);
  696. for I:=0 to inp.options.length-1 do
  697. if TJSHTMLOptionElement(inp.options.Items[i]).value=aVals then
  698. oSel:=TJSHTMLOptionElement(inp.options.Items[i]);
  699. end;
  700. if assigned(oSel) then
  701. oSel.selected:=True;
  702. Inp.Value:=IntToStr(GetOrdProp(aInfo.Instance,aInfo.Info));
  703. end;
  704. function TSimplePropertyGridWidget.DoSetEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  705. Var
  706. inp : TJSHTMLTableElement;
  707. cell : TJSHTMLTableDataCellElement;
  708. r : TJSHTMLTableRowElement;
  709. vals : TIntegerDynArray;
  710. eInfo : TTypeInfoEnum;
  711. I : Integer;
  712. selcb,CB : TJSHTMLInputElement;
  713. lbl : TJSHTMLLabelElement;
  714. begin
  715. result:=aEditor;
  716. eInfo:=TTypeInfoEnum(TTypeInfoSet(aInfo.Info.TypeInfo).CompType);
  717. vals:=GetSetPropArray(aInfo.instance,aInfo.Info);
  718. if Result=Nil then
  719. begin
  720. Result:=CreateEditorElement('table','');
  721. // Row with the property as a string.
  722. R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
  723. Result.Append(R);
  724. cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
  725. cell.InnerText:=GetSetProp(aInfo.instance,aInfo.Info);
  726. R.AppendChild(Cell);
  727. // For every element in the set, a checkbox row
  728. for I:=TTypeInfoEnum(aInfo.Info.TypeInfo).MinValue to TTypeInfoEnum(aInfo.Info.TypeInfo).MaxValue do
  729. begin
  730. // Row
  731. R:=TJSHTMLTableRowElement(CreateEditorElement('tr',''));
  732. Result.Append(R);
  733. // Cell
  734. cell:=TJSHTMLTableDatacellElement(CreateEditorElement('td',''));
  735. R.AppendChild(Cell);
  736. // Input
  737. CB:=TJSHTMLInputElement(CreateEditorElement('input',''));
  738. cb._type:='checkbox';
  739. cb.id:=GenerateID;
  740. cb.dataset['enumval']:=IntTostr(i);
  741. cell.AppendChild(cb);
  742. // label for input
  743. lbl:=TJSHTMLLabelElement(CreateEditorElement('label',''));
  744. lbl.For_:=cb.Id;
  745. lbl.InnerText:=TTypeInfoEnum(aInfo.Info.TypeInfo).EnumType.IntToName[I];
  746. cell.AppendChild(lbl);
  747. end;
  748. end;
  749. end;
  750. function TSimplePropertyGridWidget.DoComponentEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  751. Var
  752. inp : TJSHTMLSelectElement;
  753. O,osel : TJSHTMLOptionElement;
  754. I : Integer;
  755. L,aVal : TObject;
  756. begin
  757. oSel:=nil;
  758. Result:=aEditor;
  759. AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
  760. if Result=Nil then
  761. begin
  762. Result:=CreateEditorElement('select','');
  763. Inp:=TJSHTMLSelectElement(Result);
  764. inp.Multiple:=False;
  765. if Assigned(LookupRoot) then
  766. begin
  767. For I:=0 to LookupRoot.ComponentCount-1 do
  768. begin
  769. O:=TJSHTMLOptionElement(CreateEditorElement('option',''));
  770. O.InnerText:=LookupRoot.Components[i].Name;
  771. O.value:=LookupRoot.Components[i].Name;
  772. if aVal=LookupRoot.Components[i] then
  773. oSel:=o;
  774. Inp.appendChild(O);
  775. end;
  776. end;
  777. end
  778. else
  779. begin
  780. Inp:=TJSHTMLSelectElement(Result);
  781. if Assigned(LookupRoot) then
  782. begin
  783. I:=0;
  784. While (oSeL=Nil) and (I<inp.options.length) do
  785. begin
  786. O:=TJSHTMLOptionElement(inp.options.Items[i]);
  787. L:=LookupRoot.FindComponent(O.Value);
  788. if L=aval then
  789. oSel:=O;
  790. inc(I);
  791. end;
  792. end;
  793. end;
  794. if assigned(oSel) then
  795. oSel.selected:=True;
  796. end;
  797. function TSimplePropertyGridWidget.DoLabelEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  798. Var
  799. AVal : TObject;
  800. begin
  801. AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
  802. Result:=aEditor;
  803. if Result=Nil then
  804. Result:=TJSHTMLLabelElement(CreateEditorElement('label',''));
  805. Result.InnerText:=aVal.ClassName;
  806. end;
  807. function TSimplePropertyGridWidget.DoPersistentEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  808. Var
  809. AVal : TObject;
  810. begin
  811. AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
  812. Result:=aEditor;
  813. if Result=Nil then
  814. Result:=TJSHTMLLabelElement(CreateEditorElement('label',''));
  815. Result.InnerText:=aVal.ClassName;
  816. end;
  817. function TSimplePropertyGridWidget.DoStringsEditor(aEditor : TJSHTMLElement; aInfo: TPropertyEditor) : TJSHTMLElement;
  818. Var
  819. AVal : TObject;
  820. begin
  821. AVal:=GetObjectProp(aInfo.Instance,aInfo.Info);
  822. Result:=aEditor;
  823. if Result=Nil then
  824. begin
  825. Result:=TJSHTMLTextAreaElement(CreateEditorElement('textarea',''));
  826. Result['rows']:='5';
  827. end;
  828. Result.InnerText:=aVal.ClassName;
  829. end;
  830. function TSimplePropertyGridWidget.CreatePropertyEditor (aInstance : TObject; aInfo: TTypeMemberProperty) : TpropertyEditor;
  831. Var
  832. EditorClass : TPropertyEditorClass;
  833. aEditor : TPropertyEditor;
  834. begin
  835. EditorClass:=nil;
  836. Result:=Nil;
  837. // Writeln('Creating property grid editor for ',aInfo.Info.Name);
  838. case aInfo.TypeInfo.Kind of
  839. tkInteger : EditorClass:=TIntegerPropertyEditor;
  840. tkChar : EditorClass:=TStringPropertyEditor;
  841. tkString : EditorClass:=TStringPropertyEditor;
  842. tkEnumeration : EditorClass:=TEnumerationPropertyEditor;
  843. tkSet : EditorClass:=TSetPropertyEditor;
  844. tkDouble : EditorClass:=TFloatPropertyEditor;
  845. tkBool : EditorClass:=TBooleanPropertyEditor;
  846. tkClass :
  847. begin
  848. If TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TComponent) then
  849. EditorClass:=TComponentPropertyEditor
  850. else if TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TStrings) then
  851. EditorClass:=TStringsPropertyEditor
  852. else if TTypeInfoClass(aInfo.TypeInfo).ClassType.InheritsFrom(TPersistent) then
  853. EditorClass:=TPersistentPropertyEditor
  854. else
  855. EditorClass:=TLabelPropertyEditor;
  856. end;
  857. end;
  858. if Assigned(EditorClass) then
  859. Result:= EditorClass.Create(self,aInstance,aInfo);
  860. end;
  861. procedure TSimplePropertyGridWidget.DoPropertyChanged(Sender: TObject);
  862. Var
  863. aRow : TPropertyEditor absolute sender;
  864. begin
  865. Writeln('Property ',aRow.Info.Name,' changed');
  866. end;
  867. (*
  868. procedure TSimplePropertyGridWidget.ApplyIntegerEditor(aRow : TPropertyEditor);
  869. Var
  870. S : String;
  871. I : NativeInt;
  872. begin
  873. S:=TJSHTMLInputElement(aRow.ElValue).value;
  874. if TryStrToInt64(S,I) then
  875. SetOrdProp(aRow.Instance,aRow.Info,I)
  876. end;
  877. procedure TSimplePropertyGridWidget.ApplyStringEditor(aRow : TPropertyEditor);
  878. Var
  879. S : String;
  880. begin
  881. S:=TJSHTMLInputElement(aRow.ElValue).value;
  882. SetStrProp(aRow.Instance,aRow.Info,S);
  883. end;
  884. procedure TSimplePropertyGridWidget.ApplyEnumEditor(aRow : TPropertyEditor);
  885. Var
  886. Sel : TJSHTMLSelectElement;
  887. begin
  888. Sel:=TJSHTMLSelectElement(aRow.ElValue);
  889. SetOrdProp(aRow.Instance,aRow.Info,StrToInt(TJSHTMLOptionElement(Sel.Options.Items[Sel.selectedIndex]).value));
  890. end;
  891. procedure TSimplePropertyGridWidget.ApplySetEditor(aRow : TPropertyEditor);
  892. begin
  893. end;
  894. procedure TSimplePropertyGridWidget.ApplyFloatEditor(aRow : TPropertyEditor);
  895. Var
  896. S : String;
  897. D : Double;
  898. Err : Integer;
  899. begin
  900. S:=TJSHTMLInputElement(aRow.ElValue).value;
  901. val(S,D,Err);
  902. if Err<>0 then
  903. if TryStrToFloat(S,D) then
  904. Err:=0;
  905. if Err=0 then
  906. SetFloatProp(aRow.Instance,aRow.Info,D);
  907. end;
  908. procedure TSimplePropertyGridWidget.ApplyBooleanEditor(aRow : TPropertyEditor);
  909. begin
  910. end;
  911. procedure TSimplePropertyGridWidget.ApplyComponentEditor(aRow : TPropertyEditor);
  912. begin
  913. end;
  914. procedure TSimplePropertyGridWidget.ApplyStringsEditor(aRow : TPropertyEditor);
  915. begin
  916. end;
  917. procedure TSimplePropertyGridWidget.ApplyPersistentEditor(aRow : TPropertyEditor);
  918. begin
  919. end;
  920. *)
  921. procedure TSimplePropertyGridWidget.UpdatePropertyFromEditor(aRow : TPropertyEditor);
  922. begin
  923. // Writeln('Creating property grid editor for ',aInfo.Info.Name);
  924. (*
  925. case Arow.Info.TypeInfo.Kind of
  926. tkInteger : ApplyIntegerEditor(aRow);
  927. tkChar : ApplyStringEditor(aRow);
  928. tkString : ApplyStringEditor(aRow);
  929. tkEnumeration : ApplyEnumEditor(aRow);
  930. tkSet : ApplySetEditor(aRow);
  931. tkDouble : ApplyFloatEditor(aRow);
  932. tkBool : ApplyBooleanEditor(aRow);
  933. tkClass :
  934. begin
  935. If TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TComponent) then
  936. ApplyComponentEditor(aRow)
  937. else if TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TStrings) then
  938. ApplyStringsEditor(aRow)
  939. else if TTypeInfoClass(aRow.Info.TypeInfo).ClassType.InheritsFrom(TPersistent) then
  940. ApplyPersistentEditor(aRow)
  941. end;
  942. end;
  943. *)
  944. // RefreshProperties(aRow);
  945. end;
  946. function TSimplePropertyGridWidget.CreateEditorElement(aTag: String; aID: String): TJSHTMLElement;
  947. begin
  948. Result:=CreateElement(aTag,aID);
  949. Case lowercase(aTag) of
  950. 'input',
  951. 'select' :
  952. Result.className:='form-control';
  953. end;
  954. end;
  955. procedure TSimplePropertyGridWidget.SetLookupRoot(AValue: TComponent);
  956. begin
  957. if FLookupRoot=AValue then Exit;
  958. FLookupRoot:=AValue;
  959. if IsRendered then
  960. Refresh;
  961. end;
  962. procedure TPropertyEditor.SetInstance(AValue: TObject);
  963. begin
  964. if FInstance=AValue then Exit;
  965. FInstance:=AValue;
  966. end;
  967. function TPropertyEditor.CreateEditorElement(aTag: String; aID: String): TJSHTMLElement;
  968. begin
  969. Result:=Grid.CreateEditorElement(aTag,aID);
  970. end;
  971. constructor TPropertyEditor.Create(aGrid: TSimplePropertyGridWidget; aInstance: TObject; aInfo: TTypeMemberProperty);
  972. begin
  973. FGrid:=aGrid;
  974. FInstance:=aInstance;
  975. FInfo:=aInfo;
  976. end;
  977. function TSimplePropertyGridWidget.HTMLTag: String;
  978. begin
  979. Result:='TABLE';
  980. end;
  981. end.