guiAPI.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. 
  2. // zlib open source license
  3. //
  4. // Copyright (c) 2019 David Forsgren Piuva
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would be
  17. // appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not be
  20. // misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. #ifndef DFPSR_API_GUI
  25. #define DFPSR_API_GUI
  26. #include "../base/Handle.h"
  27. #include "../api/stringAPI.h"
  28. #include "../implementation/image/Image.h"
  29. #include "../implementation/gui/InputEvent.h"
  30. #include "../implementation/gui/VisualTheme.h"
  31. // createBackendWindow should be implemented outside of the core framework
  32. // Choose one of the window backends in SDK/native to compile and link with your application.
  33. // Handle<dsr::BackendWindow> createBackendWindow(const dsr::String& title, int width, int height);
  34. // Constness on handles doesn't propagate to any inner types
  35. // "const Comopnent&" only means that the writable Component handle can be created from a sub-expression
  36. // because the location where the handle is stored cannot be overwritten.
  37. // This allow getting a component by name and using it as an argument without being stored in a variable.
  38. namespace dsr {
  39. enum class ReturnCode {
  40. Good,
  41. KeyNotFound,
  42. ParsingFailure
  43. };
  44. // A handle to a window.
  45. // The Window wraps itself around native window backends to abstract away platform specific details.
  46. // It also makes it easy to load and use a graphical interface using the optional component system.
  47. class DsrWindow;
  48. using Window = Handle<DsrWindow>;
  49. // A handle to a GUI component.
  50. // Components are an abstraction for graphical user interfaces, which might not always be powerful enough.
  51. // * If you're making something advanced that components cannot do,
  52. // you can also use draw calls and input events directly against the window without using Component.
  53. class VisualComponent;
  54. using Component = Handle<VisualComponent>;
  55. // Window Construction
  56. // A portable window will be wrapped around a native window backend supplied from a call to createBackendWindow.
  57. Window window_create(const dsr::String& title, int32_t width, int32_t height);
  58. // If the game starts in full screen, this constructor should be used instead.
  59. // Otherwise the canvas may ask for the window's dimensions while the system still keeps the old dimensions due to delays.
  60. Window window_create_fullscreen(const dsr::String& title);
  61. // Returns true iff the window exists.
  62. bool window_exists(const Window& window);
  63. // Set window title
  64. // Assigns the window's title.
  65. void window_setTitle(Window& window, const dsr::String& title);
  66. // Returns the window's title.
  67. String window_getTitle(Window& window);
  68. // Clipboard
  69. // Most operating systems require that a window is associated with each request to access the clipboard.
  70. // This allow anti-virus software to see the user consent for accessing the clipboard,
  71. // because a key combination commonly associated with pasting text was pressed before the request was made.
  72. // Returns content from an internal or extenal clipboard.
  73. // The internal clipboard within the application is used when the system specific backend has not implemented clipboard access.
  74. String window_loadFromClipboard(Window& window, double timeoutInSeconds);
  75. // Stores the text argument to an internal or extenal clipboard.
  76. // The internal clipboard within the application is used when the system specific backend has not implemented clipboard access.
  77. void window_saveToClipboard(Window& window, const ReadableString &text, double timeoutInSeconds);
  78. // Layout files
  79. // Loading an interface by parsing a layout file's content, with any external resources loaded relative to fromPath.
  80. // Embedded images do not count as external resources, but file paths need fromPath in order to know from where they will be loaded.
  81. // Raises an exception if window doesn't exist.
  82. void window_loadInterfaceFromString(const Window& window, const dsr::String& content, const ReadableString &fromPath);
  83. // Loading an interface by parsing a layout file's content, with any external resources loaded relative to the current directory.
  84. // Useful when your application has already assigned the current directory, or when all resources are embedded into the layout.
  85. // Raises an exception if window doesn't exist.
  86. void window_loadInterfaceFromString(const Window& window, const dsr::String& content);
  87. // Loading an interface by parsing a layout file loaded by filename.
  88. // Raises an exception if window doesn't exist.
  89. void window_loadInterfaceFromFile(const Window& window, const dsr::ReadableString& filename);
  90. // Store the interface back into a layout file.
  91. // Raises an exception if window doesn't exist.
  92. String window_saveInterfaceToString(const Window& window);
  93. // Find a component
  94. // Get the component being stored directly in the window
  95. // Raises an exception if window doesn't exist.
  96. // There should always exist a root component where more components can be added recursively
  97. Component window_getRoot(const Window& window);
  98. // Returns a handle to the first matching component of the name in window.
  99. // For consistent behavior, make sure that only one component has the name, or use window_findComponentByNameAndIndex after giving each a unique index.
  100. // If mustExist is true, not finding any component with the name throws an exception.
  101. // Raises an exception if window doesn't exist.
  102. // Component names are case sensitive to reduce the risk of accidental naming conflicts among many components.
  103. Component window_findComponentByName(const Window& window, const ReadableString& name, bool mustExist = true);
  104. // Returns a handle to the first matching component of the name and index in window.
  105. // If mustExist is true, not finding any component with the name throws an exception.
  106. // Raises an exception if window doesn't exist.
  107. // Component names are case sensitive to reduce the risk of accidental naming conflicts among many components.
  108. Component window_findComponentByNameAndIndex(const Window& window, const ReadableString& name, int index, bool mustExist = true);
  109. // Calls back with the component handle and index for each match of the name.
  110. // Can be used to count the number of components by incrementing a counter for each match.
  111. // Can be used to detach all the matching components and have them automatically garbage collected by reference counting.
  112. // The index can be used as a filter or to look up information.
  113. // To allow detaching components while iterating over the list of children, order is reversed for child components.
  114. // Raises an exception if window doesn't exist.
  115. // Component names are case sensitive to reduce the risk of accidental naming conflicts among many components.
  116. void window_findAllComponentsByName(const Window& window, const ReadableString& name, std::function<void(Component component, int index)> callback);
  117. // The three main events to run in a loop at the end of the main function
  118. // If the window's event queue contained any resize of the window, the canvas and the depth buffer will be replaced during this call.
  119. // New calls to window_getCanvas and window_getDepthBuffer are required after this call, because the window could be given a new size.
  120. // Returns true iff any event were processed.
  121. // By calling window_executeEvents in a loop while returning false, one can wait for input.
  122. // Sleeping for 10 milliseconds is quite responsive while saving lots of battery.
  123. // Only redrawing the regions that has changed (dirty rectangles et cetera) can further save power.
  124. // Example:
  125. // while (!window_executeEvents(window)) {
  126. // time_sleepSeconds(0.01);
  127. // }
  128. // window_drawComponents(window);
  129. // window_showCanvas(window);
  130. bool window_executeEvents(const Window& window);
  131. // Draw the root component and its children to the canvas.
  132. // Raises an exception if window doesn't exist.
  133. void window_drawComponents(const Window& window);
  134. // Executes deferred actions without having to call window_drawComponents.
  135. // So if you just called component_detachFromParent to remove a child component, this will trigger it instantly
  136. // so that calls to component_getChildCount will show the new count without having to wait for window_drawComponents.
  137. // Make sure not to call window_flushDeferredActions from inside of a comonent's event, because that would remove the whole point of deferring actions.
  138. // If an event is triggered from within another event, it can easily cause bugs in the program by not executing transactions one after another.
  139. // Raises an exception if window doesn't exist.
  140. void window_flushDeferredActions(const Window& window);
  141. // Show the canvas.
  142. // Raises an exception if window doesn't exist.
  143. void window_showCanvas(const Window& window);
  144. // Pixel upscaling
  145. // The pixel-scale is the width and height of each canvas pixel when displayed on the window.
  146. // The color and depth buffers from window_getCanvas and window_getDepthBuffer will shrink to fit each pixel within the window.
  147. // Partial pixels at right and bottom sides are replaced with black padding,
  148. // so that mouse coordinates can be divided and multiplied evenly during scale conversion.
  149. // If using a higher value than the default 1, upscaling will be done during the call to window_showCanvas.
  150. // The backend window will receive the upscaled image to display over the whole window.
  151. // Gets the current pixel scale.
  152. // Raises an exception if window doesn't exist.
  153. int window_getPixelScale(const Window& window);
  154. // Assigns a new pixel scale.
  155. // Raises an exception if window doesn't exist.
  156. // Just like when handling a window resize, this will replace the canvas and depth buffer.
  157. // Any old handles to canvas and depth buffer will become useless, so fetch new image handles from the window to avoid black flickering.
  158. void window_setPixelScale(const Window& window, int scale);
  159. // Cursor
  160. // Sets the cursor visibility for window, hiding if visible is false and showing if visible is true.
  161. // Returns true on success and false on failure.
  162. bool window_setCursorVisibility(const Window& window, bool visible);
  163. // Returns true iff the cursor is allowed to be displayed over window.
  164. bool window_getCursorVisibility(const Window& window);
  165. // Tries move the cursor so that its active point is at (x, y) within window.
  166. // To get back the new cursor location and see if it worked, you must wait for the triggered mouse move event.
  167. // It is impossible to know if the operation succeeded while returning, due to absolute position input devices overwriting the side-effect.
  168. // It is recommended to avoid this method if you can, but some games require capturing relative mouse movements.
  169. // If you use this then make sure to test it carefully on all targeted operating systems and both relative and absolute input devices.
  170. // Pre-conditions:
  171. // 0 <= x < width
  172. // 0 <= y < height
  173. // Moving the mouse outside of the window's region may cause undefined behavior between platforms.
  174. // Post-condition:
  175. // Returns true on success and false on failure.
  176. // WARNING!
  177. // Only set the cursor location if you know for sure that the cursor is controlled using relative input devices, such as a mouse or track-pad.
  178. // If an absolute input method is used, such as a stylus-pen, touch-screen or eye-tracker, the cursor will shake quickly.
  179. // The solution is to let the user select between modes for relative and absolute inputs.
  180. // You can for example let absolute input mode move the cursor freely within a dead zone and hover over edges
  181. // to rotate the camera itself, when pattern detection noticed a stylus pen being used instead of a mouse.
  182. // WARNING!
  183. // On MS-Windows the cursor will always move, but on X11 the cursor must be within the window's region.
  184. // Only use this in full-screen mode to prevent getting stuck outside of the window.
  185. // WARNING!
  186. // Due to potential race-conditions, you can not assume that the next mouse move event is generated by this call.
  187. bool window_setCursorPosition(const Window& window, int x, int y);
  188. // Full screen
  189. void window_setFullScreen(const Window& window, bool enabled);
  190. bool window_isFullScreen(const Window& window);
  191. // Fetch the window's surfaces
  192. // Always get the canvas (and any depth buffer) after calls to window_executeEvents or window_setPixelScale,
  193. // because these may replace the canvas with a new size.
  194. // TODO: Prevent the backend window from freeing the memory while the canvas is still being used.
  195. // Get the canvas/color-buffer.
  196. // Raises an exception if window doesn't exist.
  197. // The canvas size will be smaller when pixelScale is larger, because the canvas has to fit inside the window.
  198. AlignedImageRgbaU8 window_getCanvas(const Window& window);
  199. // Get the depth buffer allocated on demand.
  200. // Raises an exception if window doesn't exist.
  201. // If you never call this method, no depth buffer will be allocated.
  202. // If you call it at the same time as window_getCanvas, it will have the same size as the canvas.
  203. AlignedImageF32 window_getDepthBuffer(const Window& window);
  204. // The low-resolution canvas and depth buffer dimensions are relative to mouse events given to components.
  205. // Because component are drawn to the canvas and affected by upscaling.
  206. // Returns the width of the canvas.
  207. // Raises an exception if window doesn't exist.
  208. int window_getCanvasWidth(const Window& window);
  209. // Returns the height of the canvas.
  210. // Raises an exception if window doesn't exist.
  211. int window_getCanvasHeight(const Window& window);
  212. // The window's inner dimensions are relative to mouse events received directly from the window at full pixel resolution.
  213. // Returns the inner width of the window.
  214. // Raises an exception if window doesn't exist.
  215. int window_getInnerWidth(const Window& window);
  216. // Returns the inner height of the window.
  217. // Raises an exception if window doesn't exist.
  218. int window_getInnerHeight(const Window& window);
  219. // Direct window events
  220. // Listen to window mouse events.
  221. // Raises an exception if window doesn't exist.
  222. // event.mouseEventType gives the type of mouse event.
  223. // event.key gives the key being used.
  224. void window_setMouseEvent(const Window& window, const MouseCallback& mouseEvent);
  225. // Listen to window keyboard events.
  226. // Raises an exception if window doesn't exist.
  227. // event.keyboardEventType gives the type of keyboard event.
  228. // event.dsrKey gives the key being used.
  229. void window_setKeyboardEvent(const Window& window, const KeyboardCallback& keyboardEvent);
  230. // Listen to the window close event.
  231. // Raises an exception if window doesn't exist.
  232. void window_setCloseEvent(const Window& window, const EmptyCallback& closeEvent);
  233. // Components
  234. // Create a new component belonging to parent.
  235. // Returns a handle to the component or an empty handle if className have not been registered.
  236. // Can use component_exists on the result to check if the operation was a success.
  237. // className must be the name of a registered persistent class inheriting from VisualComponent.
  238. // identifierName is used to find the component.
  239. // index can be used to identify multiple components with the same name without having to generate numbered names.
  240. Component component_create(const Component& parent, const ReadableString& className, const ReadableString& identifierName, int index = 0);
  241. // An alternative to window_loadInterfaceFromString when you want the interface loaded into a panel instead of the whole window.
  242. // Useful for dynamically creating and destroying interfaces to save memory and load the application faster.
  243. // Loading an interface by parsing a layout file's content, with any external resources loaded relative to fromPath.
  244. // Embedded images do not count as external resources, but file paths need fromPath in order to know from where they will be loaded.
  245. // Raises an exception if parent doesn't exist.
  246. Component component_createWithInterfaceFromString(Component& parent, const String& content, const ReadableString &fromPath);
  247. // With fromPath implicitly being the current path.
  248. Component component_createWithInterfaceFromString(Component& parent, const String& content);
  249. // Loading the interface from a file and loading resources relative to filename's parent folder.
  250. Component component_createWithInterfaceFromFile(Component& parent, const String& filename);
  251. // Returns true iff the component exists.
  252. bool component_exists(const Component& component);
  253. // Marks component as detached, so that it will be safely removed from the parent's list next time window_drawComponents or window_flushDeferredActions is called.
  254. // Deferring the removal makes sure that no event's callback is called from inside of another event.
  255. void component_detachFromParent(const Component& component);
  256. // Returns the number of direct (non-recursive) child components attached to parent, or -1 if parent is a null handle.
  257. int component_getChildCount(const Component& parent);
  258. // Returns the child at childIndex from parent, or an empty handle if parent does not exist or childIndex is out of bound.
  259. // Child indices go from 0 to count - 1.
  260. // The child index refers to the parent's list of children, not the child's index attribute.
  261. Component component_getChild(const Component& parent, int childIndex);
  262. // Returns a handle to the first matching component of the name in parent recursively.
  263. // Can be used instead of window_findComponentByName to further reduce the risk of name collisions, by only looking for recursive child components of a specific parent component.
  264. // If mustExist is true, not finding any component with the name within parent throws an exception.
  265. // Raises an exception if parent doesn't exist.
  266. // Component names are case sensitive to reduce the risk of accidental naming conflicts among many components.
  267. Component component_findChildByName(const Component& parent, const ReadableString& name, bool mustExist = true);
  268. // Returns a handle to the first matching component of the name and index in parent recursively.
  269. // If mustExist is true, not finding any component with the name within parent throws an exception.
  270. // Raises an exception if parent doesn't exist.
  271. // Component names are case sensitive to reduce the risk of accidental naming conflicts among many components.
  272. Component component_findChildByNameAndIndex(const Component& parent, const ReadableString& name, int index, bool mustExist = true);
  273. // Returns true iff propertyName exists in component.
  274. // Property names are case insensitive, to give more flexibility for the few property names.
  275. bool component_hasProperty(const Component& component, const ReadableString& propertyName);
  276. // Sets a property found using propertyName in component to the value serialized in value.
  277. // Raises an exception if component doesn't exist.
  278. // Matching of propertyName is case insensitive, to give more flexibility for the few property names.
  279. // Returns ReturnCode::Good if assigned.
  280. // Unless mustAssign forces an exception.
  281. // Returns ReturnCode::KeyNotFound if propertyName wasn't found in component.
  282. // Unless mustAssign forces an exception.
  283. // Returns ReturnCode::ParsingFailure if propertyName was found but value couldn't be converted to its type.
  284. ReturnCode component_setProperty(const Component& component, const ReadableString& propertyName, const ReadableString& value, bool mustAssign = true);
  285. // An advanced overload allowing loading of resources from a specific path using the fromPath argument.
  286. ReturnCode component_setProperty(const Component& component, const ReadableString& propertyName, const ReadableString& value, const ReadableString& fromPath, bool mustAssign = true);
  287. // A version for setting integers and booleans.
  288. // For integers:
  289. // Just set value to whatever you want assigned directly.
  290. // This is faster than using component_setProperty by not parsing the value from any string.
  291. // For booleans:
  292. // Use 1 (or another non-zero value) for true and 0 for false.
  293. // Boolean properties will convery any non-zero values into ones.
  294. ReturnCode component_setProperty_integer(const Component& component, const ReadableString& propertyName, int64_t value, bool mustAssign = true);
  295. // A version for setting images.
  296. ReturnCode component_setProperty_image(const Component& component, const ReadableString& propertyName, const OrderedImageRgbaU8& value, bool mustAssign = true);
  297. // A version optimized for basic strings to bypass quote mangling
  298. // The new value is given as it is without unmangling.
  299. ReturnCode component_setProperty_string(const Component& component, const ReadableString& propertyName, const ReadableString& value, bool mustAssign = true);
  300. // Returns a property found using propertyName in component.
  301. // Raises an exception if component doesn't exist.
  302. // Matching of propertyName is case insensitive, to give more flexibility for the few property names.
  303. // If mustExist is true
  304. // Raises an exception when propertyName isn't found.
  305. // If mustExist is false
  306. // Returns an empty string when propertyName isn't found.
  307. String component_getProperty(const Component& component, const ReadableString& propertyName, bool mustExist = true);
  308. // A version for getting integers and booleans.
  309. // Returns defaultValue on failure, which should use a value that is never actually used.
  310. int64_t component_getProperty_integer(const Component& component, const ReadableString& propertyName, bool mustExist = true, int64_t defaultValue = 0);
  311. // A version for getting images
  312. OrderedImageRgbaU8 component_getProperty_image(const Component& component, const ReadableString& propertyName, bool mustExist = true);
  313. // A version optimized for basic strings to bypass quote mangling.
  314. // Returns the result without adding any quote signs or escape characters.
  315. String component_getProperty_string(const Component& component, const ReadableString& propertyName, bool mustExist = true);
  316. // Call a named method in the component using optional text arguments.
  317. // Matching of methodName is case insensitive, to give more flexibility for the few method names.
  318. String component_call(const Component& component, const ReadableString& methodName);
  319. String component_call(const Component& component, const ReadableString& methodName, const ReadableString& arguments);
  320. // Component events
  321. // The main activation of clickable components.
  322. // The pressed callback doesn't take any arguments, because it should be possible to generate from multiple input methods.
  323. void component_setPressedEvent(const Component& component, const EmptyCallback& event);
  324. // Called before the component and it's child components are destructed.
  325. // The event is called when there are no more handles to component.
  326. // Be careful not to forget any extra handles to the component if a memory leak would significantly change the behavior.
  327. void component_setDestroyEvent(const Component& component, const EmptyCallback& event);
  328. // Mouse-down activates when any mouse button is pressed down within the component
  329. // Raises an exception if component doesn't exist.
  330. // The component itself decides if the mouse is inside, which allow rounded components to act as their true shape.
  331. void component_setMouseDownEvent(const Component& component, const MouseCallback& mouseEvent);
  332. // Mouse-up should eventually follow after a mouse-down event, to ensure basic transaction safety.
  333. // Raises an exception if component doesn't exist.
  334. // * Even if the mouse is dragged outside of the component or window before being lifted.
  335. // * Even if the component is removed from the window while the button is pressed,
  336. // the button press will keep it alive long enough to receive the mouse-up event before being freed.
  337. void component_setMouseUpEvent(const Component& component, const MouseCallback& mouseEvent);
  338. // Mouse-move is triggered when the mouse moves over the component.
  339. // Raises an exception if component doesn't exist.
  340. // * When pressed down inside of the component, dragging outside the component or even window will
  341. // continue to give mouse-move events to the callback.
  342. // * If dragging left of or above the window, event.position may contain negative coordinates.
  343. void component_setMouseMoveEvent(const Component& component, const MouseCallback& mouseEvent);
  344. // Mouse-scroll is triggered by scrolling in any direction.
  345. // Raises an exception if component doesn't exist.
  346. // Currently only supporting MouseKeyEnum::ScrollUp and MouseKeyEnum::ScrollDown as values in event.key.
  347. void component_setMouseScrollEvent(const Component& component, const MouseCallback& mouseEvent);
  348. // Key-down only comes when a button is pressed down. (No repeat)
  349. // Raises an exception if component doesn't exist.
  350. // The backend window is responsible to filter away any false positives for down events caused by repetition.
  351. void component_setKeyDownEvent(const Component& component, const KeyboardCallback& keyboardEvent);
  352. // Key-up only comes when a button is lifted after being pressed. (No repeat)
  353. // Raises an exception if component doesn't exist.
  354. void component_setKeyUpEvent(const Component& component, const KeyboardCallback& keyboardEvent);
  355. // Key-type comes both when a key is pressed, and then repeatedly without having to lift the key.
  356. // Raises an exception if component doesn't exist.
  357. // There's usually a second's delay before quickly repeating.
  358. void component_setKeyTypeEvent(const Component& component, const KeyboardCallback& keyboardEvent);
  359. // Select events are sent when the selected index of something has changed.
  360. // Used in Listbox to cover updates from all different ways the selection may change.
  361. void component_setSelectEvent(const Component& component, const IndexCallback& selectEvent);
  362. // Theme
  363. // Apply the given theme recursively to all components in the window's interface.
  364. // Raises an exception if window or component doesn't exist.
  365. // Components will gather what they can from the theme and save it for later.
  366. // Changing a theme while being used by an interface or adding new components,
  367. // should apply the theme again to ensure that all changes are applied.
  368. // TODO: Automate this process by storing a reference to the theme in each component and checking for updates before drawing.
  369. void window_applyTheme(const Window& window, const VisualTheme& theme);
  370. }
  371. #endif