FmGuiLayoutEditor.pas 15 KB

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