designer.pp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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. ElId: String;
  132. begin
  133. Result:=True;
  134. JQuery('.designerActive').removeClass('designerActive');
  135. JQuery('.designerToolbar').remove();
  136. aNewActive:=TJSHTMLElement(event.target);
  137. if (aNewActive.dataset[STopElementData] <> nil) and not isUndefined(aNewActive.dataset[STopElementData]) then
  138. ElId := String(aNewActive.dataset[STopElementData])
  139. else
  140. ElId := aNewActive.id;
  141. aParent:=FPage.FindWidgetByID(ElId);
  142. if aParent=Nil then
  143. aParent:=FPage;
  144. if (FAddWidget<>Nil) then
  145. begin
  146. aNewWidget:=FAddWidget;
  147. FAddWidget:=Nil;
  148. if aParent<>Nil then
  149. begin
  150. aNew:=CreateNewWidget(aParent,aNewWidget.WidgetClass);
  151. aNewActive:=TWidgetHack(aNew).TopElement;
  152. if Assigned(TWidgetHack(aNew).ContentElement) then
  153. JQuery(TWidgetHack(aNew).ContentElement).Sortable(SortableOptions);
  154. jQuery(aNewActive).on_('click',@DoActive);
  155. aParent:=aNew;
  156. end;
  157. end;
  158. JQuery(aNewActive).AddClass('designerActive').prepend(toolbar);
  159. if assigned(aParent) and Assigned(IDEClient) then
  160. IDEClient.SendAction('select',New(['widget',aParent.Name,'class',aParent.ClassName,'state',StreamWidget(aParent)]));
  161. end;
  162. procedure TDesignDemo.SetAddMode(aRegisteredWidget : TRegisteredWidget);
  163. begin
  164. FAddWidget:=aRegisteredWidget;
  165. end;
  166. procedure TDesignDemo.DoWidgetAddClick(Sender: TObject; Event: TJSEvent);
  167. begin
  168. SetAddMode((Sender as TWidgetButtonWidget).MyWidget);
  169. end;
  170. procedure TDesignDemo.AddWidgetByName(aID : NativeInt; AName : String);
  171. Var
  172. I : integer;
  173. Btn : TWidgetButtonWidget;
  174. begin
  175. I:=FRegisteredWidgets.Count-1;
  176. While (i>=0) and Not SameText(TRegisteredWidget(FRegisteredWidgets[i]).WidgetClass.ClassName,aName) do
  177. Dec(I);
  178. if I<0 then exit;
  179. SetAddMode(TRegisteredWidget(FRegisteredWidgets[i]));
  180. FConfirmAdd:=aID;
  181. end;
  182. procedure TDesignDemo.DoCommandsReceived(Sender: TObject; aCommands: TJSArray);
  183. var
  184. J,P : TJSOBject;
  185. aName : String;
  186. aID : NativeInt;
  187. I : integer;
  188. begin
  189. for I:=0 to aCommands.Length-1 do
  190. begin
  191. J:=TJSObject(aCommands[i]);
  192. aName:=String(J['name']);
  193. aID:=NativeInt(J['id']);
  194. p:=TJSObject(J['payload']);
  195. case aName of
  196. 'addWidget' : AddWidgetByName(aID,String(P['class']));
  197. end;
  198. end;
  199. end;
  200. procedure TDesignDemo.SetIDEClient(AValue: TIDEClient);
  201. begin
  202. if FIDEClient=AValue then Exit;
  203. FIDEClient:=AValue;
  204. if assigned(FIDEClient) then
  205. begin
  206. FIDEClient.OnCommands:=@DoCommandsReceived;
  207. FIDEClient.StartCommandPolling;
  208. FToolBar.Visible:=False;
  209. end;
  210. end;
  211. function TDesignDemo.SortableOptions: TJSObject;
  212. begin
  213. Result:=New([
  214. 'items','> [data-ww-element-top]',
  215. 'connectWith','[data-ww-element-content]',
  216. 'placeholder','designerPlaceholder',
  217. 'tolerance','pointer',
  218. // 'containment',TPageHack(FPage).Element,
  219. // 'handle','[data-ww-element-top]',
  220. 'cancel',''
  221. ]);
  222. end;
  223. constructor TDesignDemo.Create(aOwner: TComponent);
  224. begin
  225. inherited Create(aOwner);
  226. FRegisteredWidgets:=TObjectList.Create;
  227. RegisterWidgets;
  228. FillToolBar;
  229. SetUpPage;
  230. // []
  231. JQuery(SSortableSelect).Sortable(SortableOptions);
  232. jQuery('#designpage').on_('click','[data-ww-element-top]',@DoActive);
  233. jQuery('#designpage').on_('click',@DoActive);
  234. end;
  235. destructor TDesignDemo.Destroy;
  236. begin
  237. FreeAndNil(FRegisteredWidgets);
  238. inherited Destroy;
  239. end;
  240. procedure TDesignDemo.RegisterWidgets;
  241. begin
  242. RegisterWidget(TButtonWidget,'button');
  243. RegisterWidget(TCheckBoxInputWidget,'checkbox');
  244. RegisterWidget(TRadioInputWidget,'radio');
  245. RegisterWidget(TTextInputWidget,'edit');
  246. RegisterWidget(TImageWidget,'image');
  247. RegisterWidget(TTextAreaWidget,'memo');
  248. RegisterWidget(TSelectWidget,'select');
  249. RegisterWidget(TContainerWidget,'container');
  250. RegisterWidget(TJumboWidget,'jumbo');
  251. end;
  252. procedure TDesignDemo.RegisterWidget(aClass: TWebWidgetClass; aImageName: String);
  253. begin
  254. FRegisteredWidgets.Add(TRegisteredWidget.Create(aClass,aImageName));
  255. end;
  256. procedure TDesignDemo.SetupPage;
  257. Const
  258. ButtonClasses : Array[0..8] of string
  259. = ('primary','secondary','success','danger','warning','info','light','dark','link');
  260. var
  261. I : Integer;
  262. begin
  263. FPage:=TWebPage.Create(Self);
  264. FPage.ElementID:='designpage';
  265. FPage.Refresh;
  266. For I:=0 to 9 do
  267. begin
  268. FButtons[I]:=TButtonWidget.Create(FPage);
  269. FButtons[I].Classes:='btn btn-'+ButtonClasses[I mod 9];
  270. FButtons[I].Text:='Button #'+IntToStr(I+1);
  271. FButtons[I].Parent:=FPage;
  272. FButtons[I].Refresh;
  273. end;
  274. end;
  275. procedure TDesignDemo.FillToolBar;
  276. Var
  277. RW : TRegisteredWidget;
  278. I : Integer;
  279. Btn : TWidgetButtonWidget;
  280. begin
  281. FToolBar:=TContainerWidget.Create(Self);
  282. FToolbar.Styles.EnsureStyle('min-height','34px');
  283. FToolbar.Styles.RemoveStyle('width');
  284. FToolbar.Styles.RemoveStyle('height');
  285. FToolbar.ElementId:='toolbar';
  286. FToolbar.Refresh;
  287. SetLength(FWidgetButtons,FRegisteredWidgets.Count);
  288. For I:=0 to FRegisteredWidgets.Count-1 do
  289. begin
  290. RW:=TRegisteredWidget(FRegisteredWidgets[I]);
  291. Btn:=TWidgetButtonWidget.Create(Self);
  292. FWidgetButtons[I]:=Btn;
  293. Btn.MyWidget:=RW;
  294. Btn.Classes:='btn btn-light';
  295. Btn.Text:='';
  296. Btn.Parent:=FToolbar;
  297. Btn.Styles.Add('background-image','url("widgets/'+RW.ImageName+'.png")');
  298. Btn.Refresh;
  299. Btn.Data['widgetClass']:=RW.WidgetClass.ClassName;
  300. Btn.OnClick:=@DoWidgetAddClick;
  301. end;
  302. end;
  303. end.