DsrWindow.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2018 to 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #include "DsrWindow.h"
  24. #include "components/Panel.h"
  25. #include "components/Button.h"
  26. #include "../math/scalar.h"
  27. #include "../math/IVector.h"
  28. #include "../api/imageAPI.h"
  29. #include "../api/drawAPI.h"
  30. using namespace dsr;
  31. static bool initialized = false;
  32. static void initializeGui() {
  33. if (!initialized) {
  34. // Register built-in components by name
  35. REGISTER_PERSISTENT_CLASS(Panel)
  36. REGISTER_PERSISTENT_CLASS(Button)
  37. initialized = true;
  38. }
  39. }
  40. DsrWindow::DsrWindow(std::shared_ptr<BackendWindow> backend)
  41. : backend(backend), innerWidth(backend->getWidth()), innerHeight(backend->getHeight()) {
  42. // Initialize the GUI system if needed
  43. initializeGui();
  44. // Listen to mouse and keyboard events from the backend window
  45. this->backend->mouseEvent() = [this](const MouseEvent& event) {
  46. this->sendMouseEvent(event);
  47. };
  48. this->backend->keyboardEvent() = [this](const KeyboardEvent& event) {
  49. this->sendKeyboardEvent(event);
  50. };
  51. this->backend->closeEvent() = [this]() {
  52. this->sendCloseEvent();
  53. };
  54. // Receiving notifications about resizing should be done in the main panel
  55. this->backend->resizeEvent() = [this](int width, int height) {
  56. BackendWindow *backend = this->backend.get();
  57. ImageRgbaU8 canvas = backend->getCanvas();
  58. this->innerWidth = width;
  59. this->innerHeight = height;
  60. if (image_getWidth(canvas) != width || image_getHeight(canvas) != height) {
  61. // Resize the image that holds everything drawn on the window
  62. backend->resizeCanvas(width, height);
  63. // Remove the old depth buffer, so that it will resize when being requested again
  64. this->removeDepthBuffer();
  65. }
  66. this->applyLayout();
  67. };
  68. this->resetInterface();
  69. }
  70. DsrWindow::~DsrWindow() {}
  71. void DsrWindow::applyLayout() {
  72. this->mainPanel->applyLayout(IVector2D(this->getCanvasWidth(), this->getCanvasHeight()));
  73. }
  74. std::shared_ptr<VisualComponent> DsrWindow::findComponentByName(ReadableString name, bool mustExist) const {
  75. if (string_match(this->mainPanel->getName(), name)) {
  76. return this->mainPanel;
  77. } else {
  78. return this->mainPanel->findChildByName(name, mustExist);
  79. }
  80. }
  81. std::shared_ptr<VisualComponent> DsrWindow::findComponentByNameAndIndex(ReadableString name, int index, bool mustExist) const {
  82. if (string_match(this->mainPanel->getName(), name) && this->mainPanel->getIndex() == index) {
  83. return this->mainPanel;
  84. } else {
  85. return this->mainPanel->findChildByNameAndIndex(name, index, mustExist);
  86. }
  87. }
  88. std::shared_ptr<VisualComponent> DsrWindow::getRootComponent() const {
  89. return this->mainPanel;
  90. }
  91. void DsrWindow::resetInterface() {
  92. // Create an empty main panel
  93. this->mainPanel = std::dynamic_pointer_cast<VisualComponent>(createPersistentClass("Panel"));
  94. if (this->mainPanel.get() == nullptr) {
  95. throwError(U"DsrWindow::resetInterface: The window's Panel could not be created!");
  96. }
  97. this->mainPanel->setName("mainPanel");
  98. this->applyLayout();
  99. }
  100. void DsrWindow::loadInterfaceFromString(String layout) {
  101. // Load a tree structure of visual components from text
  102. this->mainPanel = std::dynamic_pointer_cast<VisualComponent>(createPersistentClassFromText(layout));
  103. if (this->mainPanel.get() == nullptr) {
  104. throwError(U"DsrWindow::loadInterfaceFromString: The window's root component could not be created!\n\nLayout:\n", layout, "\n");
  105. }
  106. this->applyLayout();
  107. }
  108. String DsrWindow::saveInterfaceToString() {
  109. return this->mainPanel->toString();
  110. }
  111. bool DsrWindow::executeEvents() {
  112. return this->backend->executeEvents();
  113. }
  114. void DsrWindow::sendMouseEvent(const MouseEvent& event) {
  115. this->lastMousePosition = event.position;
  116. // Components will receive scaled mouse coordinates by being drawn to the low-resolution canvas
  117. MouseEvent scaledEvent = event / this->pixelScale;
  118. // Send the global event
  119. this->callback_windowMouseEvent(scaledEvent);
  120. // Send to the main panel and its components
  121. this->mainPanel->sendMouseEvent(scaledEvent);
  122. }
  123. void DsrWindow::sendKeyboardEvent(const KeyboardEvent& event) {
  124. // Send the global event
  125. this->callback_windowKeyboardEvent(event);
  126. // Send to the main panel and its components
  127. this->mainPanel->sendKeyboardEvent(event);
  128. }
  129. void DsrWindow::sendCloseEvent() {
  130. this->callback_windowCloseEvent();
  131. }
  132. int DsrWindow::getInnerWidth() {
  133. return this->innerWidth;
  134. }
  135. int DsrWindow::getInnerHeight() {
  136. return this->innerHeight;
  137. }
  138. int DsrWindow::getCanvasWidth() {
  139. return std::max(1, this->innerWidth / this->pixelScale);
  140. }
  141. int DsrWindow::getCanvasHeight() {
  142. return std::max(1, this->innerHeight / this->pixelScale);
  143. }
  144. AlignedImageF32 DsrWindow::getDepthBuffer() {
  145. auto fullResolutionCanvas = this->backend->getCanvas();
  146. int smallWidth = getCanvasWidth();
  147. int smallHeight = getCanvasHeight();
  148. if (!image_exists(this->depthBuffer)
  149. || image_getWidth(this->depthBuffer) != smallWidth
  150. || image_getHeight(this->depthBuffer) != smallHeight) {
  151. this->depthBuffer = image_create_F32(smallWidth, smallHeight);
  152. }
  153. return this->depthBuffer;
  154. }
  155. void DsrWindow::removeDepthBuffer() {
  156. this->depthBuffer = AlignedImageF32();
  157. }
  158. int DsrWindow::getPixelScale() const {
  159. return this->pixelScale;
  160. }
  161. void DsrWindow::setPixelScale(int scale) {
  162. if (this->pixelScale != scale) {
  163. this->pixelScale = scale;
  164. // Update layout
  165. this->applyLayout();
  166. // The mouse moves relative to the canvas when scale changes
  167. this->sendMouseEvent(MouseEvent(MouseEventType::MouseMove, MouseKeyEnum::NoKey, this->lastMousePosition));
  168. }
  169. }
  170. void DsrWindow::setFullScreen(bool enabled) {
  171. if (this->backend->isFullScreen() != enabled) {
  172. this->backend->setFullScreen(enabled);
  173. // TODO: The mouse moves relative to the canvas when the window moves, but the new mouse location was never given.
  174. // How can mouse-move events be made consistent in applications when toggling full-screen without resorting to hacks?
  175. // Return the moved pixel offset from backend's setFullScreen?
  176. }
  177. }
  178. bool DsrWindow::isFullScreen() {
  179. return this->backend->isFullScreen();
  180. }
  181. void DsrWindow::drawComponents() {
  182. auto canvas = this->getCanvas();
  183. this->mainPanel->draw(canvas, IVector2D(0, 0));
  184. }
  185. AlignedImageRgbaU8 DsrWindow::getCanvas() {
  186. // TODO: Query if the backend has an optimized upload for a smaller canvas
  187. // Useful if a window backend has GPU accelerated or native upscaling
  188. auto fullResolutionCanvas = this->backend->getCanvas();
  189. if (this->pixelScale > 1) {
  190. // Get low resolution canvas in deterministic RGBA pack order
  191. int smallWidth = getCanvasWidth();
  192. int smallHeight = getCanvasHeight();
  193. if (!image_exists(this->lowResolutionCanvas)
  194. || image_getWidth(this->lowResolutionCanvas) != smallWidth
  195. || image_getHeight(this->lowResolutionCanvas) != smallHeight) {
  196. this->lowResolutionCanvas = image_create_RgbaU8_native(smallWidth, smallHeight, image_getPackOrderIndex(fullResolutionCanvas));
  197. }
  198. return this->lowResolutionCanvas;
  199. } else {
  200. // Get full resolution canvas in arbitrary pack order
  201. return fullResolutionCanvas;
  202. }
  203. }
  204. void DsrWindow::showCanvas() {
  205. if (this->pixelScale > 1 && image_exists(this->lowResolutionCanvas)) {
  206. // Use an exact pixel size, by cutting into the last row and column when not even
  207. // This makes it easy to convert mouse coordinates using multiplication and division with pixelScale
  208. auto target = this->backend->getCanvas();
  209. auto source = this->getCanvas();
  210. filter_blockMagnify(target, source, this->pixelScale, this->pixelScale);
  211. }
  212. this->backend->showCanvas();
  213. }
  214. String DsrWindow::getTitle() {
  215. return this->backend->getTitle();
  216. }
  217. void DsrWindow::setTitle(const String &newTitle) {
  218. }
  219. void DsrWindow::applyTheme(VisualTheme theme) {
  220. this->mainPanel->applyTheme(theme);
  221. }
  222. VisualTheme DsrWindow::getTheme() {
  223. return this->mainPanel->getTheme();
  224. }