OpenClonk
C4ScriptGuiWindow.h
Go to the documentation of this file.
1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2014-2016, The OpenClonk Team and contributors
5 *
6 * Distributed under the terms of the ISC license; see accompanying file
7 * "COPYING" for details.
8 *
9 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10 * See accompanying file "TRADEMARK" for details.
11 *
12 * To redistribute this file separately, substitute the full license texts
13 * for the above references.
14 */
15 
16 /* a flexisble ingame menu system that can be composed out of multiple windows */
17 
18 #ifndef INC_C4ScriptGuiWindow
19 #define INC_C4ScriptGuiWindow
20 
21 #include "graphics/C4Surface.h"
22 #include "gui/C4Gui.h"
23 #include "script/C4Value.h"
24 
26  enum type
27  {
28  left = 0,
29  top,
32 
37 
42 
47 
62  _lastProp
63  };
64 }
65 
67  enum type
68  {
69  SetTag = 1,
71  };
72 }
73 
75  enum type
76  {
77  None = 0,
82  TextRight = 16,
83  TextBottom = 32,
85  Multiple = 128,
86  IgnoreMouse = 256,
87  NoCrop = 512,
89  };
90 }
91 
92 class C4ScriptGuiWindow;
93 
95 {
96  friend class C4ScriptGuiWindow;
97 
98  private:
99  // the ID is unique among all actions. It is used later to synchronize callbacks
100  int32_t id{0};
101 
102  int32_t action{0};
103  C4ScriptGuiWindowAction *nextAction{nullptr}; // a linked list of actions
104  // note: depending on the action not all of the following attributes always have values
105  C4PropList *target{nullptr}; // contains a valid C4Object in case of SetTag, a generic proplist in case of Call
106  C4String *text{nullptr}; // can be either a function name to call or a tag to set
107  C4Value value; // arbitrary value used for Call
108  int32_t subwindowID{0};
109 
110  public:
111  C4ScriptGuiWindowAction() : value(0) { }
113  void ClearPointers(C4Object *pObj);
114  bool Init(C4ValueArray *array, int32_t index = 0); // index is the current action in an array of actions
115  // executes non-synced actions and syncs the others
116  // the action type parameters is only used to be able to sync commands
117  void Execute(C4ScriptGuiWindow *parent, int32_t player, int32_t actionType);
118  // used to execute synced commands, explanation see C4ScriptGuiWindow::ExecuteCommand
119  bool ExecuteCommand(int32_t actionID, C4ScriptGuiWindow *parent, int32_t player);
120  // used for serialization. The "first" parameter is used so that chained actions are stored correctly into an array
121  const C4Value ToC4Value(bool first = true);
122 };
123 
125 {
126  friend class C4ScriptGuiWindow;
127 
128  private:
129  typedef union
130  {
131  void *data;
132  float f;
133  int32_t d;
134  C4Object *obj;
135  C4Def *def;
137  StdCopyStrBuf *strBuf;
138  C4ScriptGuiWindowAction *action;
139  } Prop;
140 
141  Prop *current{nullptr};
142  // the last tag is used to be able to call the correct action on re-synchronizing commands
143  C4String* currentTag{nullptr};
144 
145  std::map<C4String*, Prop> taggedProperties;
146  void CleanUp(Prop &prop);
147  void CleanUpAll();
148 
149  int32_t type{-1}; // which property do I stand for?
150 
151  // the following methods directly set values (default Std tag)
152  // note that for int/floats no cleanup is necessary as it would be for the more general Set method
153  void SetInt(int32_t to, C4String *tag = nullptr);
154  void SetFloat(float to, C4String *tag = nullptr);
155  void SetNull(C4String *tag = nullptr);
156 
157  public:
160  void Set(const C4Value &value, C4String *tag);
161 
162  int32_t GetInt() { return current->d; }
163  float GetFloat() { return current->f; }
164  C4Object *GetObject() { return current->obj; }
165  C4Def *GetDef() { return current->def; }
166  C4GUI::FrameDecoration *GetFrameDecoration() { return current->deco; }
167  StdCopyStrBuf *GetStrBuf() { return current->strBuf; }
168  C4ScriptGuiWindowAction *GetAction() { return current->action; }
169  std::list<C4ScriptGuiWindowAction*> GetAllActions(); // used to synchronize actions
170 
171  bool SwitchTag(C4String *tag);
172  C4String *GetCurrentTag() { return currentTag; }
173 
174  const C4Value ToC4Value();
175 
176  void ClearPointers(C4Object *pObj);
177 };
178 
180 {
183 public:
184  // the size of the screen that is covered by a centered "main menu"
185  static const float standardWidth;
186  static const float standardHeight;
187 
188  private:
189  // the "main" menu ID is always unique, however the sub-menu IDs do NOT have to be unique
190  // they can be set from script and in combination with the target should suffice to identify windows
191  int32_t id;
192  // The name of a window is used when updating a window to identify the correct child;
193  // however, it is NOT generally used to identify windows, f.e. to close them.
194  // The reasoning behind that is that EVERY window needs a name (in the defining proplist) but only windows that need to be addressed later need an ID;
195  // so separating the identifying property from the name hopefully reduces clashes - especially since names will often be "left"/"right" etc.
196  // Note that a window does not necessarily have a name and names starting with an underscore are never saved (to be able to f.e. add new windows without knowing the names of the existing windows).
197  C4String *name;
198  // this is not only a window inside a menu but a top-level-window?
199  // this does not mean the ::WindowMenuRoot but rather a player-created submenu
200  bool isMainWindow;
201  // whether this menu is the root of all script-created menus (aka of the isMainWindow windows)
202  bool IsRoot();
203  bool mainWindowNeedsLayoutUpdate;
204 
205  bool wasRemovedFromParent{ false }; // to notify the window that it should not inform its parent on Close() a second time
206  bool wasClosed{ false }; // to prevent a window from cleaning up twice even if e.g. closed in the close-callback..
207  // This is used to catch the situation when a closing callback wants to close the parent or a sibling.
208  int lockRemovalForClosingCallbackCounter{ 0 };
209  void lockRemovalForClosingCallback();
210  void unlockRemovalForClosingCallback();
211  bool isRemovalLockedForClosingCallback() const noexcept { return lockRemovalForClosingCallbackCounter > 0; }
212 
213  C4Object *target;
214  const C4Object *GetTarget() { return target; }
215 
216  // properties are stored extra to make "tags" possible
218  void Init();
219  // ID is set by parent, parent gives unique IDs to children
220  void SetID(int32_t to) { id = to; }
221  // to be used to generate the quick-access children map for main menus
222  void ChildGotID(C4ScriptGuiWindow *child);
223  void ChildWithIDRemoved(C4ScriptGuiWindow *child);
224  std::multimap<int32_t, C4ScriptGuiWindow *> childrenIDMap;
225  // should be called when the Priority property of a child changes
226  // will sort the child correctly into the children list
227  void ChildChangedPriority(C4ScriptGuiWindow *child);
228  // helper function to extract relative and absolute position values from a string
229  void SetPositionStringProperties(const C4Value &property, C4ScriptGuiWindowPropertyName::type relative, C4ScriptGuiWindowPropertyName::type absolute, C4String *tag);
231  // sets all margins either from a string or from an array
232  void SetMarginProperties(const C4Value &property, C4String *tag);
233  C4Value MarginsToC4Value();
234 
235  // this is only supposed to be called at ::Game.GuiWindowRoot since it uses the "ID" property
236  // this is done to make saving easier. Since IDs do not need to be sequential, action&menu IDs can both be derived from "id"
237  int32_t GenerateMenuID() { return ++id; }
238  int32_t GenerateActionID() { return ++id; }
239 
240 
241  // children height should be set when enabling a scroll bar so that, with style FitChildren, the size can simply be changed
242  void EnableScrollBar(bool enable = true, float childrenHeight = 0.0f);
243 
244  public:
245  // used by mouse input, this is in screen coordinates
246  /*struct _lastDrawPosition
247  {
248  float left, right;
249  float top, bottom;
250 
251  float topMostChild, bottomMostChild;
252  int32_t dirty; // indicates wish to update topMostChild and bottomMostChild asap
253  bool needLayoutUpdate;
254  _lastDrawPosition() : left(0.0f), right(0.0f), top(0.0f), bottom(0.0f), topMostChild(0.0f), bottomMostChild(0.0f), dirty(2), needLayoutUpdate(false){}
255  } lastDrawPosition;*/
256 
257  void SetTag(C4String *tag);
258 
260  ~C4ScriptGuiWindow() override;
261 
262  int32_t GetID() { return id; }
263  // finds a child with a certain ID, usually called on ::MainWindowRoot to get submenus
264  C4ScriptGuiWindow *GetChildByID(int32_t child);
265  // finds a child by name, usually called when updating a window with a new proplist
267  // finds any fitting sub menu - not necessarily direct child
268  // has to be called on children of ::MainWindowRoot, uses the childrenIDMap
269  // note: always checks the target to avoid ambiguities, even if 0
270  C4ScriptGuiWindow *GetSubWindow(int32_t childID, C4Object *childTarget);
271 
272 
273 
274  // pass a proplist to create a window + subwindows as specified
275  // you can call this function on a window more than once
276  // if isUpdate is true, all new children will have resetStdTag set
277  bool CreateFromPropList(C4PropList *proplist, bool resetStdTag = false, bool isUpdate = false, bool isLoading = false);
278 
279  // constructs a C4Value (proplist) that contains everything that is needed for saving this window
280  const C4Value ToC4Value();
281  // this MUST only be called when loading
282  void SetEnumeratedID(int enumID) { id = enumID; }
283  void Denumerate(C4ValueNumbers *numbers);
284  // C4ScriptGuiWindow will delete its children on close. Make sure you don't delete anything twice
287 
288  void ClearChildren(bool close = true); // close: whether to properly "Close" them, alias for RemoveChild
289  void RemoveChild(C4ScriptGuiWindow *child, bool close = true, bool all = false); // child = 0 & all = true to clear all. Also deletes the child(ren).
290  void Close();
291  void ClearPointers(C4Object *pObj);
292 
293  // calculate the width/height based on a certain property (f.e. leftMargin and relLeftMargin) and the parent's width/height
294  float CalculateRelativeSize(float parentWidthOrHeight, C4ScriptGuiWindowPropertyName::type absoluteProperty, C4ScriptGuiWindowPropertyName::type relativeProperty);
295 
296  // schedules a layout update for the next drawing step
297  void RequestLayoutUpdate();
298  // this updates the window's layout and also propagates to all children
299  bool UpdateLayout(C4TargetFacet &cgo);
300  bool UpdateLayout(C4TargetFacet &cgo, float parentWidth, float parentHeight);
301  bool UpdateChildLayout(C4TargetFacet &cgo, float parentWidth, float parentHeight);
302  // special layouts that are set by styles
303  void UpdateLayoutGrid();
304  void UpdateLayoutTightGrid();
305  void UpdateLayoutVertical();
306  // the window will be drawn in the context of a viewport BY the viewport
307  // so just do nothing when TheScreen wants to draw the window
308  void Draw(C4TargetFacet &cgo) override {}
309  // Draw without parameters can be used for the root
310  bool DrawAll(C4TargetFacet &cgo, int32_t player);
311  // the clipping rectangle has already been set, but currentClippingRect must be passed to DrawChildren
312  bool Draw(C4TargetFacet &cgo, int32_t player, C4Rect *currentClippingRect);
313  bool GetClippingRect(int32_t &left, int32_t &top, int32_t &right, int32_t &bottom);
314 
315  // withMultipleFlag is there to draw only the non-multiple or the multiple windows
316  // withMultipleFlag == -1: all windows are drawn (standard)
317  // withMultipleFlag == 0: only one non-Multiple window is drawn
318  // withMultipleFlag == 1: only Multiple windows are drawn
319  // returns whether at least one child was drawn
320  bool DrawChildren(C4TargetFacet &cgo, int32_t player, int32_t withMultipleFlag = -1, C4Rect *currentClippingRect = nullptr);
321 
322  // used for commands that have been synchronized and are coming from the command queue
323  // attention: calls to this need to be synchronized!
324  bool ExecuteCommand(int32_t actionID, int32_t player, int32_t subwindowID, int32_t actionType, C4Object *target);
325 
326  // virtual bool MouseInput(int32_t player, int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
327  // this is called only on the root menu
329  virtual bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam);
330  // this is then called on the child windows, note the return value
331  virtual bool ProcessMouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, int32_t parentOffsetX, int32_t parentOffsetY);
332  // called when mouse cursor enters element region
333  void MouseEnter(C4GUI::CMouse &rMouse) override;
334  // called when mouse cursor leaves element region
335  void MouseLeave(C4GUI::CMouse &rMouse) override;
336  // This remembers whether the window currently has mouse focus and whether it has been mouse-down-ed.
337  // All windows with mouse focus set are remembered by their parents and notified when the mouse left.
338  // The state is also used to make sure that button-up events without button-downs are not caught by the UI.
339  enum MouseState // values of this enum will be bit-wise combined
340  {
341  None = 0,
342  Focus = 1,
343  MouseDown = 2
344  };
345  int32_t currentMouseState; // this needs to be saved in savegames!!!
346  // OnMouseOut() called by this window, unsets the mouse focus
347  // must notify children, too!
348  void OnMouseOut(int32_t player);
349  // called by this window, sets the mouse focus; the offset is used to set the correct tooltip rectangle for ::MouseControl
350  void OnMouseIn(int32_t player, int32_t parentOffsetX, int32_t parentOffsetY);
351  bool HasMouseFocus() { return currentMouseState & MouseState::Focus; }
352  // Returns whether the menu can be seen (and interacted with) by a player. This includes checking the target's visibility.
353  bool IsVisibleTo(int32_t player);
354 private:
355  // Use the currently loaded font to determine on-screen size of 1 EM.
356  static float Em2Pix(float em);
357  static float Pix2Em(float pix);
358 };
359 
360 #endif
uint32_t DWORD
Definition: C4Def.h:99
void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override
Definition: C4Rect.h:28
bool Init(C4ValueArray *array, int32_t index=0)
const C4Value ToC4Value(bool first=true)
void Execute(C4ScriptGuiWindow *parent, int32_t player, int32_t actionType)
bool ExecuteCommand(int32_t actionID, C4ScriptGuiWindow *parent, int32_t player)
void ClearPointers(C4Object *pObj)
bool UpdateChildLayout(C4TargetFacet &cgo, float parentWidth, float parentHeight)
C4ScriptGuiWindow * GetChildByName(C4String *childName)
void RemoveChild(C4ScriptGuiWindow *child, bool close=true, bool all=false)
bool DrawChildren(C4TargetFacet &cgo, int32_t player, int32_t withMultipleFlag=-1, C4Rect *currentClippingRect=nullptr)
float CalculateRelativeSize(float parentWidthOrHeight, C4ScriptGuiWindowPropertyName::type absoluteProperty, C4ScriptGuiWindowPropertyName::type relativeProperty)
bool CreateFromPropList(C4PropList *proplist, bool resetStdTag=false, bool isUpdate=false, bool isLoading=false)
C4ScriptGuiWindow * GetChildByID(int32_t child)
C4ScriptGuiWindow * GetSubWindow(int32_t childID, C4Object *childTarget)
bool IsVisibleTo(int32_t player)
bool ExecuteCommand(int32_t actionID, int32_t player, int32_t subwindowID, int32_t actionType, C4Object *target)
void Draw(C4TargetFacet &cgo) override
const C4Value ToC4Value()
virtual bool ProcessMouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, int32_t parentOffsetX, int32_t parentOffsetY)
void SetEnumeratedID(int enumID)
bool DrawAll(C4TargetFacet &cgo, int32_t player)
bool GetClippingRect(int32_t &left, int32_t &top, int32_t &right, int32_t &bottom)
static const float standardHeight
void OnMouseIn(int32_t player, int32_t parentOffsetX, int32_t parentOffsetY)
virtual bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
C4ScriptGuiWindow * AddChild()
static const float standardWidth
void Denumerate(C4ValueNumbers *numbers)
friend class C4ScriptGuiWindowScrollBar
void SetTag(C4String *tag)
void MouseEnter(C4GUI::CMouse &rMouse) override
bool UpdateLayout(C4TargetFacet &cgo)
void MouseLeave(C4GUI::CMouse &rMouse) override
void ClearPointers(C4Object *pObj)
void OnMouseOut(int32_t player)
C4ScriptGuiWindowAction * GetAction()
C4ScriptGuiWindowProperty()=default
void ClearPointers(C4Object *pObj)
std::list< C4ScriptGuiWindowAction * > GetAllActions()
void Set(const C4Value &value, C4String *tag)
bool SwitchTag(C4String *tag)
C4GUI::FrameDecoration * GetFrameDecoration()