FmGuiLayoutEditor.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit FmGuiLayoutEditor;
  5. interface
  6. {$I GLScene.inc}
  7. uses
  8. Winapi.Windows,
  9. Winapi.ShellApi,
  10. System.SysUtils,
  11. System.Variants,
  12. System.Classes,
  13. Vcl.Graphics,
  14. Vcl.Controls,
  15. Vcl.Forms,
  16. Vcl.Dialogs,
  17. Vcl.Buttons,
  18. Vcl.ExtDlgs,
  19. Vcl.StdCtrls,
  20. Vcl.ExtCtrls,
  21. Vcl.Samples.Spin,
  22. Vcl.Grids,
  23. GLS.BaseClasses,
  24. GLS.Gui,
  25. GLS.Strings;
  26. type
  27. TGLLayoutEditorForm = class(TForm)
  28. Panel1: TPanel;
  29. Panel2: TPanel;
  30. items_list: TListBox;
  31. x_label: TLabel;
  32. y_label: TLabel;
  33. open_image_button: TBitBtn;
  34. open_button: TBitBtn;
  35. save_button: TBitBtn;
  36. OpenDialog1: TOpenDialog;
  37. SaveDialog1: TSaveDialog;
  38. delete_item_button: TBitBtn;
  39. add_button: TBitBtn;
  40. Label1: TLabel;
  41. Label2: TLabel;
  42. left_edit: TSpinEdit;
  43. top_edit: TSpinEdit;
  44. Label3: TLabel;
  45. Label4: TLabel;
  46. height_edit: TSpinEdit;
  47. width_edit: TSpinEdit;
  48. Label5: TLabel;
  49. name_edit: TEdit;
  50. elements_grid: TStringGrid;
  51. Panel3: TPanel;
  52. BitBtn4: TBitBtn;
  53. BitBtn5: TBitBtn;
  54. ScrollBox1: TScrollBox;
  55. Image2: TImage;
  56. PaintBox1: TPaintBox;
  57. Image1: TImage;
  58. BitBtn6: TBitBtn;
  59. BitBtn1: TBitBtn;
  60. GLGuiLayout1: TGLGuiLayout;
  61. procedure open_image_buttonClick(Sender: TObject);
  62. procedure open_buttonClick(Sender: TObject);
  63. procedure save_buttonClick(Sender: TObject);
  64. procedure FormCreate(Sender: TObject);
  65. procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  66. Y: Integer);
  67. procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
  68. Shift: TShiftState; X, Y: Integer);
  69. procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
  70. Shift: TShiftState; X, Y: Integer);
  71. procedure add_buttonClick(Sender: TObject);
  72. procedure delete_item_buttonClick(Sender: TObject);
  73. procedure items_listClick(Sender: TObject);
  74. procedure name_editExit(Sender: TObject);
  75. procedure name_editKeyPress(Sender: TObject; var Key: Char);
  76. procedure elements_gridClick(Sender: TObject);
  77. procedure left_editChange(Sender: TObject);
  78. procedure top_editChange(Sender: TObject);
  79. procedure width_editChange(Sender: TObject);
  80. procedure height_editChange(Sender: TObject);
  81. procedure BitBtn4Click(Sender: TObject);
  82. procedure BitBtn6Click(Sender: TObject);
  83. procedure elements_gridDblClick(Sender: TObject);
  84. private
  85. procedure SyncImages;
  86. procedure DrawCurrentElement;
  87. procedure RefreshComponentBox;
  88. function GetEnabledSpins: Boolean;
  89. procedure SetEnabledSpins(Value: Boolean);
  90. public
  91. procedure Execute(AGUILayout: TGLGuiLayout);
  92. property EnabledSpins: Boolean read GetEnabledSpins write SetEnabledSpins;
  93. end;
  94. function GUILayoutEditorForm: TGLLayoutEditorForm;
  95. procedure ReleaseGUILayoutEditorForm;
  96. //-------------------------------------------------
  97. implementation
  98. //-------------------------------------------------
  99. {$R *.dfm}
  100. uses
  101. GLS.Utils;
  102. var
  103. vGUILayoutEditorForm: TGLLayoutEditorForm;
  104. rect_point1, rect_point2: TPoint;
  105. zoom: integer = 1;
  106. sorted_elements: array[0..9] of TGLGuiElement;
  107. function GUILayoutEditorForm: TGLLayoutEditorForm;
  108. begin
  109. if not Assigned(vGUILayoutEditorForm) then
  110. vGUILayoutEditorForm := TGLLayoutEditorForm.Create(nil);
  111. Result := vGUILayoutEditorForm;
  112. end;
  113. procedure ReleaseGUILayoutEditorForm;
  114. begin
  115. if Assigned(vGUILayoutEditorForm) then
  116. begin
  117. vGUILayoutEditorForm.Free;
  118. vGUILayoutEditorForm := nil;
  119. end;
  120. end;
  121. function SnapPoint(X, Y: integer): TPoint;
  122. begin
  123. Result.X := (X div zoom) * zoom;
  124. Result.Y := (Y div zoom) * zoom;
  125. end;
  126. procedure TGLLayoutEditorForm.SetEnabledSpins(Value: Boolean);
  127. begin
  128. left_edit.Enabled := Value;
  129. top_edit.Enabled := Value;
  130. height_edit.Enabled := Value;
  131. width_edit.Enabled := Value;
  132. end;
  133. procedure TGLLayoutEditorForm.SyncImages;
  134. begin
  135. Image2.Width := Image1.Width;
  136. Image2.Height := Image1.Height;
  137. Image2.Picture.Bitmap.Width := Image1.Width;
  138. Image2.Picture.Bitmap.Height := Image1.Height;
  139. PaintBox1.Width := Image1.Width;
  140. PaintBox1.Height := Image1.Height;
  141. ScrollBox1.HorzScrollBar.Range := Image1.Width;
  142. ScrollBox1.VertScrollBar.Range := Image1.Height;
  143. PaintBox1.Canvas.CopyRect(PaintBox1.Canvas.ClipRect,
  144. Image1.Canvas, Image1.Canvas.ClipRect);
  145. end;
  146. procedure TGLLayoutEditorForm.DrawCurrentElement;
  147. begin
  148. with elements_grid do
  149. if (items_list.ItemIndex > -1) and (sorted_elements[Col + 3 * Row] <> nil)
  150. then
  151. with sorted_elements[Col + 3 * Row], Image2.Canvas do
  152. begin
  153. FillRect(ClipRect);
  154. Rectangle(Rect(zoom * Round(TopLeft.X), zoom * Round(TopLeft.Y),
  155. zoom * Round(BottomRight.X), zoom * Round(BottomRight.Y)));
  156. end;
  157. end;
  158. procedure TGLLayoutEditorForm.open_image_buttonClick(Sender: TObject);
  159. var
  160. LFileName: string;
  161. begin
  162. LFileName := '';
  163. if OpenPictureDialog(LFileName) then
  164. try
  165. Image1.Stretch := false;
  166. Image1.AutoSize := true;
  167. Image1.Picture.LoadFromFile(LFileName);
  168. Image1.AutoSize := false;
  169. Image1.Stretch := true;
  170. Image2.Canvas.Pen.Width := 1;
  171. SyncImages;
  172. zoom := 1;
  173. except
  174. Application.MessageBox('Unable to load picture!', 'Error', MB_ICONERROR);
  175. end;
  176. end;
  177. procedure TGLLayoutEditorForm.RefreshComponentBox;
  178. var
  179. i: integer;
  180. begin
  181. items_list.Clear;
  182. for i := 0 to GLGuiLayout1.GuiComponents.Count - 1 do
  183. items_list.Items.Add(GLGuiLayout1.GuiComponents[i].Name);
  184. items_list.ItemIndex := 0;
  185. items_listClick(nil);
  186. end;
  187. procedure TGLLayoutEditorForm.open_buttonClick(Sender: TObject);
  188. begin
  189. case Application.MessageBox('Save layout?',
  190. 'Question', MB_ICONQUESTION + MB_YESNOCANCEL) of
  191. mrYes: save_buttonClick(nil);
  192. mrCancel: Exit;
  193. end;
  194. if OpenDialog1.Execute then
  195. try
  196. GLGuiLayout1.Clear;
  197. GLGuiLayout1.LoadFromFile(OpenDialog1.FileName);
  198. RefreshComponentBox;
  199. except
  200. Application.MessageBox('Unable to load layout!', 'Error', MB_ICONERROR);
  201. end;
  202. end;
  203. procedure TGLLayoutEditorForm.save_buttonClick(Sender: TObject);
  204. begin
  205. if SaveDialog1.FileName = '' then
  206. if SaveDialog1.Execute then
  207. GLGuiLayout1.SaveToFile(SaveDialog1.FileName)
  208. else
  209. else
  210. GLGuiLayout1.SaveToFile(SaveDialog1.FileName);
  211. end;
  212. procedure TGLLayoutEditorForm.FormCreate(Sender: TObject);
  213. begin
  214. rect_point1.X := -1;
  215. Image2.Canvas.FillRect(Image2.Canvas.ClipRect);
  216. Image2.Canvas.Pen.Color := clAqua;
  217. end;
  218. function TGLLayoutEditorForm.GetEnabledSpins: Boolean;
  219. begin
  220. Result := left_edit.Enabled;
  221. end;
  222. procedure TGLLayoutEditorForm.Image1MouseMove(Sender: TObject;
  223. Shift: TShiftState; X, Y: Integer);
  224. begin
  225. x_label.Caption := 'X: ' + IntToStr(X div zoom);
  226. y_label.Caption := 'Y: ' + IntToStr(Y div zoom);
  227. if not (ssRight in Shift) then
  228. Exit;
  229. if (X < 0) or (Y < 0) or (X > Image2.Width) or (Y > Image2.Height) then
  230. Exit;
  231. if rect_point1.X < 0 then
  232. rect_point1 := SnapPoint(X, Y)
  233. else
  234. with Image2.Canvas do
  235. begin
  236. FillRect(ClipRect);
  237. rect_point2 := SnapPoint(X, Y);
  238. Rectangle(rect_point1.x, rect_point1.y, X, Y);
  239. end;
  240. if items_list.ItemIndex = -1 then
  241. Exit;
  242. if rect_point1.X < rect_point2.X then
  243. left_edit.Value := rect_point1.X div zoom
  244. else
  245. left_edit.Value := rect_point2.X div zoom;
  246. if rect_point1.Y > rect_point2.Y then
  247. top_edit.Value := rect_point2.Y div zoom
  248. else
  249. top_edit.Value := rect_point1.Y div zoom;
  250. width_edit.Value := Abs(rect_point2.X - rect_point1.X) div zoom;
  251. height_edit.Value := Abs(rect_point2.Y - rect_point1.Y) div zoom;
  252. end;
  253. procedure TGLLayoutEditorForm.Image1MouseDown(Sender: TObject;
  254. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  255. begin
  256. if not (ssRight in Shift) then
  257. Exit;
  258. with Image2.Canvas do
  259. begin
  260. FillRect(ClipRect);
  261. rect_point1 := SnapPoint(X, Y);
  262. rect_point2 := rect_point1;
  263. end;
  264. end;
  265. procedure TGLLayoutEditorForm.Image1MouseUp(Sender: TObject;
  266. Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  267. begin
  268. if not (ssRight in Shift) then
  269. Exit;
  270. rect_point1.X := -1;
  271. end;
  272. procedure TGLLayoutEditorForm.add_buttonClick(Sender: TObject);
  273. var
  274. i: integer;
  275. begin
  276. with TGLGuiComponent(GLGuiLayout1.GuiComponents.Add) do
  277. begin
  278. Name := InputBox('Question', 'Name of new region:', '');
  279. if Name = '' then
  280. begin
  281. Free;
  282. Exit;
  283. end;
  284. items_list.Items.Add(Name);
  285. items_list.ItemIndex := items_list.Count - 1;
  286. for i := 0 to 9 do
  287. begin
  288. sorted_elements[i] := TGLGuiElement(Elements.Add);
  289. sorted_elements[i].Align := TGUIAlignments(i);
  290. end;
  291. name_edit.Text := Name;
  292. end;
  293. end;
  294. procedure TGLLayoutEditorForm.delete_item_buttonClick(Sender: TObject);
  295. begin
  296. if items_list.ItemIndex = -1 then
  297. Exit;
  298. GLGuiLayout1.GuiComponents.Delete(items_list.ItemIndex);
  299. items_list.DeleteSelected;
  300. if items_list.ItemIndex > -1 then
  301. name_edit.Text := GLGuiLayout1.GuiComponents[items_list.ItemIndex].Name
  302. else
  303. name_edit.Text := '';
  304. end;
  305. procedure TGLLayoutEditorForm.items_listClick(Sender: TObject);
  306. var
  307. i, p: integer;
  308. begin
  309. if items_list.ItemIndex = -1 then
  310. Exit;
  311. name_edit.Text := GLGuiLayout1.GuiComponents[
  312. items_list.ItemIndex].Name;
  313. elements_grid.Row := 0;
  314. elements_grid.Col := 0;
  315. for i := 0 to Length(sorted_elements) - 1 do
  316. begin
  317. sorted_elements[i] := nil;
  318. if I < 9 then
  319. elements_grid.Cells[i mod 3, i div 3] := #32;
  320. end;
  321. with GLGuiLayout1.GuiComponents[items_list.ItemIndex] do
  322. for i := 0 to Elements.Count - 1 do
  323. begin
  324. p := Integer(Elements[i].Align);
  325. sorted_elements[p] := Elements[i];
  326. elements_grid.Cells[p mod 3, p div 3] := '+';
  327. end;
  328. elements_gridClick(nil);
  329. end;
  330. procedure TGLLayoutEditorForm.name_editExit(Sender: TObject);
  331. begin
  332. if items_list.ItemIndex > -1 then
  333. begin
  334. GLGuiLayout1.GuiComponents[items_list.
  335. ItemIndex].Name := name_edit.Text;
  336. items_list.Items[items_list.ItemIndex] := name_edit.Text;
  337. items_listClick(nil);
  338. end;
  339. end;
  340. procedure TGLLayoutEditorForm.name_editKeyPress(Sender: TObject; var Key: Char);
  341. begin
  342. if Key = #13 then
  343. name_editExit(nil);
  344. end;
  345. procedure TGLLayoutEditorForm.elements_gridClick(Sender: TObject);
  346. begin
  347. with elements_grid do
  348. if (items_list.ItemIndex > -1) and (sorted_elements[Col + 3 * Row] <> nil)
  349. then
  350. with sorted_elements[Col + 3 * Row] do
  351. begin
  352. EnabledSpins := True;
  353. left_edit.Value := Round(TopLeft.X);
  354. top_edit.Value := Round(TopLeft.Y);
  355. width_edit.Value := Round(BottomRight.X - TopLeft.X);
  356. height_edit.Value := Round(BottomRight.Y - TopLeft.Y);
  357. DrawCurrentElement;
  358. end
  359. else
  360. begin
  361. EnabledSpins := False;
  362. Image2.Canvas.FillRect(Image2.Canvas.ClipRect);
  363. end;
  364. end;
  365. procedure TGLLayoutEditorForm.elements_gridDblClick(Sender: TObject);
  366. var
  367. I: Integer;
  368. E: TGLGuiElement;
  369. begin
  370. if items_list.ItemIndex > -1 then
  371. with elements_grid do
  372. begin
  373. if Assigned(sorted_elements[Col + 3 * Row]) then
  374. begin
  375. I := GLGuiLayout1.GuiComponents[items_list.ItemIndex].Elements.IndexOf(sorted_elements[Col + 3 * Row]);
  376. GLGuiLayout1.GuiComponents[items_list.ItemIndex].Elements.Delete(I);
  377. sorted_elements[Col + 3 * Row] := nil;
  378. Cells[Col, Row] := #32;
  379. elements_gridClick(nil);
  380. end
  381. else begin
  382. E := TGLGuiElement(GLGuiLayout1.GuiComponents[items_list.ItemIndex].Elements.Add);
  383. E.Align := TGUIAlignments(Col + 3 * Row);
  384. sorted_elements[Col + 3 * Row] := E;
  385. Cells[Col, Row] := '+';
  386. elements_gridClick(nil);
  387. end;
  388. end;
  389. end;
  390. procedure TGLLayoutEditorForm.left_editChange(Sender: TObject);
  391. begin
  392. if (items_list.ItemIndex = -1) or not EnabledSpins then
  393. Exit;
  394. if left_edit.Value + width_edit.Value > Image2.Width div zoom then
  395. left_edit.Value := (Image2.Width div zoom) - width_edit.Value;
  396. if left_edit.Value < 0 then
  397. left_edit.Value := 0;
  398. with elements_grid do
  399. sorted_elements[Col + 3 * Row].TopLeft.X := left_edit.Value;
  400. DrawCurrentElement;
  401. end;
  402. procedure TGLLayoutEditorForm.top_editChange(Sender: TObject);
  403. begin
  404. if (items_list.ItemIndex = -1) or not EnabledSpins then
  405. Exit;
  406. if top_edit.Value + height_edit.Value > Image2.Height div zoom then
  407. top_edit.Value := (Image2.Height div zoom) - height_edit.Value;
  408. if top_edit.Value < 0 then
  409. top_edit.Value := 0;
  410. with elements_grid do
  411. sorted_elements[Col + 3 * Row].TopLeft.Y := top_edit.Value;
  412. DrawCurrentElement;
  413. end;
  414. procedure TGLLayoutEditorForm.width_editChange(Sender: TObject);
  415. begin
  416. if (items_list.ItemIndex = -1) or not EnabledSpins then
  417. Exit;
  418. with elements_grid do
  419. sorted_elements[Col + 3 * Row].BottomRight.X := left_edit.Value +
  420. width_edit.Value;
  421. if left_edit.Value + width_edit.Value > Image2.Width div zoom then
  422. width_edit.Value := (Image2.Width div zoom) - left_edit.Value;
  423. if width_edit.Value < 0 then
  424. width_edit.Value := 0;
  425. DrawCurrentElement;
  426. end;
  427. procedure TGLLayoutEditorForm.height_editChange(Sender: TObject);
  428. begin
  429. if (items_list.ItemIndex = -1) or not EnabledSpins then
  430. Exit;
  431. with elements_grid do
  432. sorted_elements[Col + 3 * Row].BottomRight.Y := top_edit.Value +
  433. height_edit.Value;
  434. if top_edit.Value + height_edit.Value > Image2.Height div zoom then
  435. height_edit.Value := (Image2.Height div zoom) - top_edit.Value;
  436. if height_edit.Value < 0 then
  437. height_edit.Value := 0;
  438. DrawCurrentElement;
  439. end;
  440. procedure TGLLayoutEditorForm.BitBtn4Click(Sender: TObject);
  441. begin
  442. if zoom + TBitBtn(Sender).Tag < 1 then
  443. Exit;
  444. Image1.Width := (Image1.Width div zoom) * (zoom + TBitBtn(Sender).Tag);
  445. Image1.Height := (Image1.Height div zoom) * (zoom + TBitBtn(Sender).Tag);
  446. SyncImages;
  447. zoom := zoom + TBitBtn(Sender).Tag;
  448. Image2.Canvas.Pen.Width := zoom;
  449. elements_gridClick(nil);
  450. end;
  451. procedure TGLLayoutEditorForm.BitBtn6Click(Sender: TObject);
  452. {$IFDEF LINUX}
  453. var
  454. lProcess: TProcess;
  455. {$ENDIF}
  456. begin
  457. {$IFDEF MSWINDOWS}
  458. ShellExecuteW(0, 'open', 'mspaint', '', '', SW_SHOW)
  459. {$ENDIF}
  460. {$IFDEF LINUX}
  461. lProcess := TProcess.Create(nil);
  462. lProcess.CommandLine := 'gimp';
  463. try
  464. lProcess.Execute;
  465. finally
  466. lProcess.Destroy;
  467. end;
  468. {$ENDIF}
  469. end;
  470. procedure TGLLayoutEditorForm.Execute(AGUILayout: TGLGuiLayout);
  471. begin
  472. GLGuiLayout1.Assign(AGUILayout);
  473. Image1.Stretch := false;
  474. Image1.AutoSize := true;
  475. Image1.Picture.Assign(AGUILayout.Material.GetActualPrimaryTexture.Image.GetBitmap32.Create32BitsBitmap);
  476. Image1.AutoSize := false;
  477. Image1.Stretch := true;
  478. Image2.Canvas.Pen.Width := 1;
  479. SyncImages;
  480. zoom := 1;
  481. RefreshComponentBox;
  482. if ShowModal = mrOk then
  483. AGUILayout.Assign(GLGuiLayout1);
  484. end;
  485. //-------------------------------------
  486. initialization
  487. //-------------------------------------
  488. finalization
  489. ReleaseGUILayoutEditorForm;
  490. end.