designer.pp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. unit designer;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. Classes, SysUtils, libjquery, webwidget, htmlwidgets, contnrs, js, web, webideclient;
  6. Type
  7. { TRegisteredWidget }
  8. TRegisteredWidget = Class
  9. Private
  10. FClass : TWebwidgetClass;
  11. FImageName : String;
  12. Public
  13. Constructor Create(aClass : TWebwidgetClass; aImageName : String);
  14. Property WidgetClass : TWebwidgetClass Read FClass;
  15. Property ImageName : String Read FimageName;
  16. end;
  17. TWidgetButtonWidget = Class(TButtonWidget)
  18. MyWidget : TRegisteredWidget;
  19. end;
  20. TSortable = Class helper for TJQuery
  21. Procedure sortable(Options : TJSObject); external name 'sortable'; overload;
  22. Procedure sortable(Options : string); external name 'sortable'; overload;
  23. end;
  24. { TDesignDemo }
  25. TDesignDemo = class(TComponent)
  26. private
  27. FIDEClient: TIDEClient;
  28. procedure AddWidgetByName(aID: NativeInt; AName: String);
  29. function CreateNewWidget(aParent: TCustomWebWidget; aClass: TCustomWebWidgetClass): TCustomWebWidget;
  30. function DoActive(Event: TEventListenerEvent): boolean;
  31. procedure DoCommandsReceived(Sender: TObject; aCommands: TJSArray);
  32. procedure DoWidgetAddClick(Sender: TObject; Event: TJSEvent);
  33. procedure SetIDEClient(AValue: TIDEClient);
  34. function SortableOptions: TJSObject;
  35. function StreamWidget(aWidget: TCustomWebWidget): String;
  36. Public
  37. FConfirmAdd : NativeInt;
  38. FAddWidget : TRegisteredWidget;
  39. FPage : TWebPage;
  40. FToolBar : TContainerWidget;
  41. FButtons : Array[1..10] of TButtonWidget;
  42. FWidgetButtons : Array of TWidgetButtonWidget;
  43. FRegisteredWidgets : TObjectList;
  44. Constructor Create(aOwner : TComponent); override;
  45. Destructor Destroy; override;
  46. Procedure RegisterWidgets;
  47. Procedure RegisterWidget(aClass : TWebWidgetClass; aImageName : String);
  48. procedure SetAddMode(aRegisteredWidget: TRegisteredWidget);
  49. Procedure FillToolBar;
  50. Procedure SetupPage;
  51. property IDEClient: TIDEClient Read FIDEClient Write SetIDEClient;
  52. end;
  53. implementation
  54. Const
  55. SSortableSelect = '#designpage, #designpage [data-ww-element-content]';
  56. Type
  57. { TJumboWidget }
  58. TJumboWidget = class(TSimpleTemplateWidget)
  59. Public
  60. Constructor Create(aOwner: TComponent); override;
  61. end;
  62. type
  63. TWidgetHack = Class(TCustomWebWidget)
  64. Property Element;
  65. Property ElementID;
  66. Property TopElement;
  67. Property ContentElement;
  68. end;
  69. TPageHack = Class(TWebPage)
  70. Property Element;
  71. Property ElementID;
  72. end;
  73. { TJumboWidget }
  74. constructor TJumboWidget.Create(aOwner: TComponent);
  75. begin
  76. inherited Create(aOwner);
  77. Template:='<div class="jumbotron">'+sLineBreak+
  78. '<h1 class="display-4">Hello, world!</h1>'+sLineBreak+
  79. '<p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>'+sLineBreak+
  80. '<hr class="my-4">'+sLineBreak+
  81. '<p>It uses utility classes for typography and spacing to space content out within the larger container.</p>'+sLineBreak+
  82. '<p class="lead">'+sLineBreak+
  83. ' <a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>'+sLineBreak+
  84. '</p>'+sLineBreak+
  85. '</div>';
  86. end;
  87. { TRegisteredWidget }
  88. constructor TRegisteredWidget.Create(aClass: TWebwidgetClass; aImageName: String);
  89. begin
  90. FClass:=aClass;
  91. FImageName:=aImageName;
  92. end;
  93. { TDesignDemo }
  94. function TDesignDemo.CreateNewWidget(aParent : TCustomWebWidget; aClass : TCustomWebWidgetClass) : TCustomWebWidget;
  95. begin
  96. Result:=aClass.Create(FPage);
  97. Result.Name:=Result.ClassName+IntToStr(FPage.ChildCount);
  98. Result.Parent:=aParent;
  99. Result.Refresh;
  100. if Assigned(IDEClient) then
  101. IDEClient.SendAction('create',New(['widget',Result.Name,'class',Result.ClassName]));
  102. end;
  103. function TDesignDemo.StreamWidget(aWidget : TCustomWebWidget) : String;
  104. Var
  105. S: TBytesStream;
  106. T : TStringStream;
  107. begin
  108. T:=Nil;
  109. S:=TBytesStream.Create(Nil);
  110. try
  111. S.WriteComponent(aWidget);
  112. S.Position:=0;
  113. T:=TStringStream.Create('');
  114. ObjectBinaryToText(S,T);
  115. Result:=T.DataString;
  116. finally
  117. T.Free;
  118. S.Free;
  119. end;
  120. end;
  121. function TDesignDemo.DoActive(Event: TEventListenerEvent): boolean;
  122. Const
  123. Toolbar = '<div class="designerToolbar">' +
  124. '<div class="designerDragHandle ui-icon ui-icon-arrow-4"></div>' +
  125. '<div class="designerDelete ui-icon ui-icon-trash"></div>' +
  126. '</div>';
  127. var
  128. aParent,aNew : TCustomWebWidget;
  129. aNewActive : TJSHTMLElement;
  130. aNewWidget : TRegisteredWidget;
  131. begin
  132. Result:=True;
  133. JQuery('.designerActive').removeClass('designerActive');
  134. JQuery('.designerToolbar').remove();
  135. aNewActive:=TJSHTMLElement(event.target);
  136. aParent:=FPage.FindWidgetByID(String(aNewActive.dataset[STopElementData]));
  137. if aParent=Nil then
  138. aParent:=FPage;
  139. if (FAddWidget<>Nil) then
  140. begin
  141. aNewWidget:=FAddWidget;
  142. FAddWidget:=Nil;
  143. if aParent<>Nil then
  144. begin
  145. aNew:=CreateNewWidget(aParent,aNewWidget.WidgetClass);
  146. aNewActive:=TWidgetHack(aNew).TopElement;
  147. if Assigned(TWidgetHack(aNew).ContentElement) then
  148. JQuery(TWidgetHack(aNew).ContentElement).Sortable(SortableOptions);
  149. jQuery(aNewActive).on_('click',@DoActive);
  150. aParent:=aNew;
  151. end;
  152. end;
  153. JQuery(aNewActive).AddClass('designerActive').prepend(toolbar);
  154. if assigned(aParent) and Assigned(IDEClient) then
  155. IDEClient.SendAction('select',New(['widget',aParent.Name,'class',aParent.ClassName,'state',StreamWidget(aParent)]));
  156. end;
  157. procedure TDesignDemo.SetAddMode(aRegisteredWidget : TRegisteredWidget);
  158. begin
  159. FAddWidget:=aRegisteredWidget;
  160. end;
  161. procedure TDesignDemo.DoWidgetAddClick(Sender: TObject; Event: TJSEvent);
  162. begin
  163. SetAddMode((Sender as TWidgetButtonWidget).MyWidget);
  164. end;
  165. procedure TDesignDemo.AddWidgetByName(aID : NativeInt; AName : String);
  166. Var
  167. I : integer;
  168. Btn : TWidgetButtonWidget;
  169. begin
  170. I:=FRegisteredWidgets.Count-1;
  171. While (i>=0) and Not SameText(TRegisteredWidget(FRegisteredWidgets[i]).WidgetClass.ClassName,aName) do
  172. Dec(I);
  173. if I<0 then exit;
  174. SetAddMode(TRegisteredWidget(FRegisteredWidgets[i]));
  175. FConfirmAdd:=aID;
  176. end;
  177. procedure TDesignDemo.DoCommandsReceived(Sender: TObject; aCommands: TJSArray);
  178. var
  179. J,P : TJSOBject;
  180. aName : String;
  181. aID : NativeInt;
  182. I : integer;
  183. begin
  184. for I:=0 to aCommands.Length-1 do
  185. begin
  186. J:=TJSObject(aCommands[i]);
  187. aName:=String(J['name']);
  188. aID:=NativeInt(J['id']);
  189. p:=TJSObject(J['payload']);
  190. case aName of
  191. 'addWidget' : AddWidgetByName(aID,String(P['class']));
  192. end;
  193. end;
  194. end;
  195. procedure TDesignDemo.SetIDEClient(AValue: TIDEClient);
  196. begin
  197. if FIDEClient=AValue then Exit;
  198. FIDEClient:=AValue;
  199. if assigned(FIDEClient) then
  200. begin
  201. FIDEClient.OnCommands:=@DoCommandsReceived;
  202. FIDEClient.StartCommandPolling;
  203. FToolBar.Visible:=False;
  204. end;
  205. end;
  206. function TDesignDemo.SortableOptions: TJSObject;
  207. begin
  208. Result:=New([
  209. 'items','> [data-ww-element-top]',
  210. 'connectWith','[data-ww-element-content]',
  211. 'placeholder','designerPlaceholder',
  212. 'tolerance','pointer',
  213. // 'containment',TPageHack(FPage).Element,
  214. // 'handle','[data-ww-element-top]',
  215. 'cancel',''
  216. ]);
  217. end;
  218. constructor TDesignDemo.Create(aOwner: TComponent);
  219. begin
  220. inherited Create(aOwner);
  221. FRegisteredWidgets:=TObjectList.Create;
  222. RegisterWidgets;
  223. FillToolBar;
  224. SetUpPage;
  225. // []
  226. JQuery(SSortableSelect).Sortable(SortableOptions);
  227. jQuery('#designpage').on_('click','[data-ww-element-top]',@DoActive);
  228. jQuery('#designpage').on_('click',@DoActive);
  229. end;
  230. destructor TDesignDemo.Destroy;
  231. begin
  232. FreeAndNil(FRegisteredWidgets);
  233. inherited Destroy;
  234. end;
  235. procedure TDesignDemo.RegisterWidgets;
  236. begin
  237. RegisterWidget(TButtonWidget,'button');
  238. RegisterWidget(TCheckBoxInputWidget,'checkbox');
  239. RegisterWidget(TRadioInputWidget,'radio');
  240. RegisterWidget(TTextInputWidget,'edit');
  241. RegisterWidget(TImageWidget,'image');
  242. RegisterWidget(TTextAreaWidget,'memo');
  243. RegisterWidget(TSelectWidget,'select');
  244. RegisterWidget(TContainerWidget,'container');
  245. RegisterWidget(TJumboWidget,'jumbo');
  246. end;
  247. procedure TDesignDemo.RegisterWidget(aClass: TWebWidgetClass; aImageName: String);
  248. begin
  249. FRegisteredWidgets.Add(TRegisteredWidget.Create(aClass,aImageName));
  250. end;
  251. procedure TDesignDemo.SetupPage;
  252. Const
  253. ButtonClasses : Array[0..8] of string
  254. = ('primary','secondary','success','danger','warning','info','light','dark','link');
  255. var
  256. I : Integer;
  257. begin
  258. FPage:=TWebPage.Create(Self);
  259. FPage.ElementID:='designpage';
  260. FPage.Refresh;
  261. For I:=0 to 9 do
  262. begin
  263. FButtons[I]:=TWidgetButtonWidget.Create(FPage);
  264. FButtons[I].Classes:='btn btn-'+ButtonClasses[I mod 9];
  265. FButtons[I].Text:='Button #'+IntToStr(I+1);
  266. FButtons[I].Parent:=FPage;
  267. FButtons[I].Refresh;
  268. end;
  269. end;
  270. procedure TDesignDemo.FillToolBar;
  271. Var
  272. RW : TRegisteredWidget;
  273. I : Integer;
  274. Btn : TWidgetButtonWidget;
  275. begin
  276. FToolBar:=TContainerWidget.Create(Self);
  277. FToolbar.Styles.EnsureStyle('min-height','34px');
  278. FToolbar.Styles.RemoveStyle('width');
  279. FToolbar.Styles.RemoveStyle('height');
  280. FToolbar.ElementId:='toolbar';
  281. FToolbar.Refresh;
  282. SetLength(FWidgetButtons,FRegisteredWidgets.Count);
  283. For I:=0 to FRegisteredWidgets.Count-1 do
  284. begin
  285. RW:=TRegisteredWidget(FRegisteredWidgets[I]);
  286. Btn:=TWidgetButtonWidget.Create(Self);
  287. FWidgetButtons[I]:=Btn;
  288. Btn.MyWidget:=RW;
  289. Btn.Classes:='btn btn-light';
  290. Btn.Text:='';
  291. Btn.Parent:=FToolbar;
  292. Btn.Styles.Add('background-image','url("widgets/'+RW.ImageName+'.png")');
  293. Btn.Refresh;
  294. Btn.Data['widgetClass']:=RW.WidgetClass.ClassName;
  295. Btn.OnClick:=@DoWidgetAddClick;
  296. end;
  297. end;
  298. end.