GXS.GameMenu.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.GameMenu;
  5. (* Manages a basic game menu UI *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.Classes,
  12. System.SysUtils,
  13. Stage.VectorTypes,
  14. GXS.Scene,
  15. GXS.Coordinates,
  16. GXS.Material,
  17. GXS.BitmapFont,
  18. GXS.Color,
  19. GXS.RenderContextInfo,
  20. GXS.Canvas,
  21. GXS.Context;
  22. type
  23. TgxGameMenuScale = (gmsNormal, gms1024x768);
  24. { Classic game menu interface made of several lines. }
  25. TgxGameMenu = class(TgxSceneObject, IgxMaterialLibrarySupported)
  26. private
  27. FItems: TStrings;
  28. FSelected: Integer;
  29. FFont: TgxCustomBitmapFont;
  30. FMarginVert, FMarginHorz, FSpacing: Integer;
  31. FMenuScale: TgxGameMenuScale;
  32. FBackColor: TgxColor;
  33. FInactiveColor, FActiveColor, FDisabledColor: TgxColor;
  34. FMaterialLibrary: TgxMaterialLibrary;
  35. FTitleMaterialName: TgxLibMaterialName;
  36. FTitleWidth, FTitleHeight: Integer;
  37. FOnSelectedChanged: TNotifyEvent;
  38. FBoxTop, FBoxBottom, FBoxLeft, FBoxRight: Integer;
  39. FMenuTop: Integer;
  40. // implementing IGLMaterialLibrarySupported
  41. function GetMaterialLibrary: TgxAbstractMaterialLibrary;
  42. protected
  43. procedure SetMenuScale(AValue: TgxGameMenuScale);
  44. procedure SetMarginHorz(AValue: Integer);
  45. procedure SetMarginVert(AValue: Integer);
  46. procedure SetSpacing(AValue: Integer);
  47. procedure SetFont(AValue: TgxCustomBitmapFont);
  48. procedure SetBackColor(AValue: TgxColor);
  49. procedure SetInactiveColor(AValue: TgxColor);
  50. procedure SetActiveColor(AValue: TgxColor);
  51. procedure SetDisabledColor(AValue: TgxColor);
  52. function GetEnabled(AIndex: Integer): Boolean;
  53. procedure SetEnabled(AIndex: Integer; AValue: Boolean);
  54. procedure SetItems(AValue: TStrings);
  55. procedure SetSelected(AValue: Integer);
  56. function GetSelectedText: string;
  57. procedure SetMaterialLibrary(AValue: TgxMaterialLibrary);
  58. procedure SetTitleMaterialName(const AValue: string);
  59. procedure SetTitleWidth(AValue: Integer);
  60. procedure SetTitleHeight(AValue: Integer);
  61. procedure ItemsChanged(Sender: TObject);
  62. public
  63. constructor Create(AOwner: TComponent); override;
  64. destructor Destroy; override;
  65. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  66. procedure BuildList(var rci: TgxRenderContextInfo); override;
  67. property Enabled[AIndex: Integer]: Boolean read GetEnabled write SetEnabled;
  68. property SelectedText: string read GetSelectedText;
  69. procedure SelectNext;
  70. procedure SelectPrev;
  71. procedure MouseMenuSelect(const X, Y: Integer);
  72. published
  73. property MaterialLibrary: TgxMaterialLibrary read FMaterialLibrary write SetMaterialLibrary;
  74. property MenuScale: TgxGameMenuScale read FMenuScale write SetMenuScale default gmsNormal;
  75. property MarginHorz: Integer read FMarginHorz write SetMarginHorz default 16;
  76. property MarginVert: Integer read FMarginVert write SetMarginVert default 16;
  77. property Spacing: Integer read FSpacing write SetSpacing default 16;
  78. property Font: TgxCustomBitmapFont read FFont write SetFont;
  79. property TitleMaterialName: string read FTitleMaterialName write SetTitleMaterialName;
  80. property TitleWidth: Integer read FTitleWidth write SetTitleWidth default 0;
  81. property TitleHeight: Integer read FTitleHeight write SetTitleHeight default 0;
  82. property BackColor: TgxColor read FBackColor write SetBackColor;
  83. property InactiveColor: TgxColor read FInactiveColor write SetInactiveColor;
  84. property ActiveColor: TgxColor read FActiveColor write SetActiveColor;
  85. property DisabledColor: TgxColor read FDisabledColor write SetDisabledColor;
  86. property Items: TStrings read FItems write SetItems;
  87. property Selected: Integer read FSelected write SetSelected default -1;
  88. property OnSelectedChanged: TNotifyEvent read FOnSelectedChanged write FOnSelectedChanged;
  89. // these are the extents of the menu
  90. property BoxTop: Integer read FBoxTop;
  91. property BoxBottom: Integer read FBoxBottom;
  92. property BoxLeft: Integer read FBoxLeft;
  93. property BoxRight: Integer read FBoxRight;
  94. // this is the top of the first menu item
  95. property MenuTop: Integer read FMenuTop;
  96. // publish other stuff from TgxBaseSceneObject
  97. property ObjectsSorting;
  98. property VisibilityCulling;
  99. property Position;
  100. property Visible;
  101. property OnProgress;
  102. property Behaviours;
  103. property Effects;
  104. end;
  105. // ------------------------------------------------------------------
  106. implementation
  107. // ------------------------------------------------------------------
  108. // ------------------
  109. // ------------------ TgxGameMenu ------------------
  110. // ------------------
  111. constructor TgxGameMenu.Create(AOwner: TComponent);
  112. begin
  113. inherited;
  114. ObjectStyle := ObjectStyle + [osDirectDraw];
  115. FItems := TStringList.Create;
  116. TStringList(FItems).OnChange := ItemsChanged;
  117. FSelected := -1;
  118. FMarginHorz := 16;
  119. FMarginVert := 16;
  120. FSpacing := 16;
  121. FMenuScale := gmsNormal;
  122. FBackColor := TgxColor.CreateInitialized(Self, clrTransparent, NotifyChange);
  123. FInactiveColor := TgxColor.CreateInitialized(Self, clrGray75, NotifyChange);
  124. FActiveColor := TgxColor.CreateInitialized(Self, clrWhite, NotifyChange);
  125. FDisabledColor := TgxColor.CreateInitialized(Self, clrGray60, NotifyChange);
  126. end;
  127. destructor TgxGameMenu.Destroy;
  128. begin
  129. inherited;
  130. FItems.Free;
  131. Font := nil;
  132. FBackColor.Free;
  133. FInactiveColor.Free;
  134. FActiveColor.Free;
  135. FDisabledColor.Free;
  136. end;
  137. procedure TgxGameMenu.Notification(AComponent: TComponent; Operation: TOperation);
  138. begin
  139. inherited;
  140. if Operation = opRemove then
  141. begin
  142. if AComponent = Font then
  143. Font := nil;
  144. if AComponent = MaterialLibrary then
  145. MaterialLibrary := nil;
  146. end;
  147. end;
  148. procedure TgxGameMenu.BuildList(var rci: TgxRenderContextInfo);
  149. var
  150. Canvas: TgxCanvas;
  151. buffer: TgxSceneBuffer;
  152. i, w, h, tw, Y: Integer;
  153. Color: TgxColorVector;
  154. libMat: TgxLibMaterial;
  155. begin
  156. if Font = nil then
  157. Exit;
  158. case MenuScale of
  159. gmsNormal:
  160. begin
  161. buffer := TgxSceneBuffer(rci.buffer);
  162. Canvas := TgxCanvas.Create(buffer.Width, buffer.Height);
  163. end;
  164. gms1024x768:
  165. Canvas := TgxCanvas.Create(1024, 768);
  166. else
  167. Canvas := nil;
  168. Assert(False);
  169. end;
  170. try
  171. // determine extents
  172. h := FItems.Count * (Font.CharHeight + Spacing) - Spacing + MarginVert * 2;
  173. if TitleHeight > 0 then
  174. h := h + TitleHeight + Spacing;
  175. w := TitleWidth;
  176. for i := 0 to FItems.Count - 1 do
  177. begin
  178. tw := Font.TextWidth(FItems[i]);
  179. if tw > w then
  180. w := tw;
  181. end;
  182. w := w + 2 * MarginHorz;
  183. // calculate boundaries for user
  184. FBoxLeft := Round(Position.X - w / 2);
  185. FBoxTop := Round(Position.Y - h / 2);
  186. FBoxRight := Round(Position.X + w / 2);
  187. FBoxBottom := Round(Position.Y + h / 2);
  188. // paint back
  189. if BackColor.Alpha > 0 then
  190. begin
  191. Canvas.PenColor := BackColor.AsWinColor;
  192. Canvas.PenAlpha := BackColor.Alpha;
  193. Canvas.FillRect(FBoxLeft, FBoxTop, FBoxRight, FBoxBottom);
  194. end;
  195. Canvas.StopPrimitive;
  196. // paint items
  197. Y := Round(Position.Y - h / 2 + MarginVert);
  198. if TitleHeight > 0 then
  199. begin
  200. if (TitleMaterialName <> '') and (MaterialLibrary <> nil) and (TitleWidth > 0) then
  201. begin
  202. libMat := MaterialLibrary.LibMaterialByName(TitleMaterialName);
  203. if libMat <> nil then
  204. begin
  205. libMat.Apply(rci);
  206. repeat
  207. glBegin(GL_QUADS);
  208. glTexCoord2f(0, 0);
  209. glVertex2f(Position.X - TitleWidth div 2, Y + TitleHeight);
  210. glTexCoord2f(1, 0);
  211. glVertex2f(Position.X + TitleWidth div 2, Y + TitleHeight);
  212. glTexCoord2f(1, 1);
  213. glVertex2f(Position.X + TitleWidth div 2, Y);
  214. glTexCoord2f(0, 1);
  215. glVertex2f(Position.X - TitleWidth div 2, Y);
  216. glEnd;
  217. until (not libMat.UnApply(rci));
  218. end;
  219. end;
  220. Y := Y + TitleHeight + Spacing;
  221. FMenuTop := Y;
  222. end
  223. else
  224. FMenuTop := Y + Spacing;
  225. for i := 0 to FItems.Count - 1 do
  226. begin
  227. tw := Font.TextWidth(FItems[i]);
  228. if not Enabled[i] then
  229. Color := DisabledColor.Color
  230. else if i = Selected then
  231. Color := ActiveColor.Color
  232. else
  233. Color := InactiveColor.Color;
  234. Font.TextOut(rci, Position.X - tw div 2, Y, FItems[i], Color);
  235. Y := Y + Font.CharHeight + Spacing;
  236. end;
  237. finally
  238. Canvas.Free;
  239. end;
  240. end;
  241. procedure TgxGameMenu.SelectNext;
  242. var
  243. i: Integer;
  244. begin
  245. i := Selected;
  246. repeat
  247. i := i + 1;
  248. until (i >= Items.Count) or Enabled[i];
  249. if (i < Items.Count) and (i <> Selected) then
  250. Selected := i;
  251. end;
  252. procedure TgxGameMenu.SelectPrev;
  253. var
  254. i: Integer;
  255. begin
  256. i := Selected;
  257. repeat
  258. i := i - 1;
  259. until (i < 0) or Enabled[i];
  260. if (i >= 0) and (i <> Selected) then
  261. Selected := i;
  262. end;
  263. procedure TgxGameMenu.SetMenuScale(AValue: TgxGameMenuScale);
  264. begin
  265. if FMenuScale <> AValue then
  266. begin
  267. FMenuScale := AValue;
  268. StructureChanged;
  269. end;
  270. end;
  271. procedure TgxGameMenu.SetMarginHorz(AValue: Integer);
  272. begin
  273. if FMarginHorz <> AValue then
  274. begin
  275. FMarginHorz := AValue;
  276. StructureChanged;
  277. end;
  278. end;
  279. procedure TgxGameMenu.SetMarginVert(AValue: Integer);
  280. begin
  281. if FMarginVert <> AValue then
  282. begin
  283. FMarginVert := AValue;
  284. StructureChanged;
  285. end;
  286. end;
  287. procedure TgxGameMenu.SetSpacing(AValue: Integer);
  288. begin
  289. if FSpacing <> AValue then
  290. begin
  291. FSpacing := AValue;
  292. StructureChanged;
  293. end;
  294. end;
  295. procedure TgxGameMenu.SetFont(AValue: TgxCustomBitmapFont);
  296. begin
  297. if FFont <> nil then
  298. FFont.RemoveFreeNotification(Self);
  299. FFont := AValue;
  300. if FFont <> nil then
  301. FFont.FreeNotification(Self);
  302. end;
  303. procedure TgxGameMenu.SetBackColor(AValue: TgxColor);
  304. begin
  305. FBackColor.Assign(AValue);
  306. end;
  307. procedure TgxGameMenu.SetInactiveColor(AValue: TgxColor);
  308. begin
  309. FInactiveColor.Assign(AValue);
  310. end;
  311. procedure TgxGameMenu.SetActiveColor(AValue: TgxColor);
  312. begin
  313. FActiveColor.Assign(AValue);
  314. end;
  315. procedure TgxGameMenu.SetDisabledColor(AValue: TgxColor);
  316. begin
  317. FDisabledColor.Assign(AValue);
  318. end;
  319. function TgxGameMenu.GetEnabled(AIndex: Integer): Boolean;
  320. begin
  321. Result := not Boolean(Cardinal(FItems.Objects[AIndex]));
  322. end;
  323. procedure TgxGameMenu.SetEnabled(AIndex: Integer; AValue: Boolean);
  324. begin
  325. FItems.Objects[AIndex] := TObject(pointer(Cardinal(ord(not AValue))));
  326. StructureChanged;
  327. end;
  328. procedure TgxGameMenu.SetItems(AValue: TStrings);
  329. begin
  330. FItems.Assign(AValue);
  331. SetSelected(Selected);
  332. end;
  333. procedure TgxGameMenu.SetSelected(AValue: Integer);
  334. begin
  335. if AValue < -1 then
  336. AValue := -1;
  337. if AValue >= FItems.Count then
  338. AValue := FItems.Count - 1;
  339. if AValue <> FSelected then
  340. begin
  341. FSelected := AValue;
  342. StructureChanged;
  343. if Assigned(FOnSelectedChanged) then
  344. FOnSelectedChanged(Self);
  345. end;
  346. end;
  347. function TgxGameMenu.GetSelectedText: string;
  348. begin
  349. if Cardinal(Selected) < Cardinal(FItems.Count) then
  350. Result := FItems[Selected]
  351. else
  352. Result := '';
  353. end;
  354. procedure TgxGameMenu.SetMaterialLibrary(AValue: TgxMaterialLibrary);
  355. begin
  356. if FMaterialLibrary <> nil then
  357. FMaterialLibrary.RemoveFreeNotification(Self);
  358. FMaterialLibrary := AValue;
  359. if FMaterialLibrary <> nil then
  360. FMaterialLibrary.FreeNotification(Self);
  361. end;
  362. procedure TgxGameMenu.SetTitleMaterialName(const AValue: string);
  363. begin
  364. if FTitleMaterialName <> AValue then
  365. begin
  366. FTitleMaterialName := AValue;
  367. StructureChanged;
  368. end;
  369. end;
  370. procedure TgxGameMenu.SetTitleWidth(AValue: Integer);
  371. begin
  372. if AValue < 0 then
  373. AValue := 0;
  374. if FTitleWidth <> AValue then
  375. begin
  376. FTitleWidth := AValue;
  377. StructureChanged;
  378. end;
  379. end;
  380. procedure TgxGameMenu.SetTitleHeight(AValue: Integer);
  381. begin
  382. if AValue < 0 then
  383. AValue := 0;
  384. if FTitleHeight <> AValue then
  385. begin
  386. FTitleHeight := AValue;
  387. StructureChanged;
  388. end;
  389. end;
  390. procedure TgxGameMenu.ItemsChanged(Sender: TObject);
  391. begin
  392. SetSelected(FSelected);
  393. StructureChanged;
  394. end;
  395. procedure TgxGameMenu.MouseMenuSelect(const X, Y: Integer);
  396. begin
  397. if (X >= BoxLeft) and (Y >= MenuTop) and (X <= BoxRight) and (Y <= BoxBottom) then
  398. begin
  399. Selected := (Y - FMenuTop) div (Font.CharHeight + FSpacing);
  400. end
  401. else
  402. Selected := -1;
  403. end;
  404. function TgxGameMenu.GetMaterialLibrary: TgxAbstractMaterialLibrary;
  405. begin
  406. Result := FMaterialLibrary;
  407. end;
  408. // ------------------------------------------------------------------
  409. initialization
  410. // ------------------------------------------------------------------
  411. RegisterClass(TgxGameMenu);
  412. end.