1 /** 2 * Module for window handling. 3 * 4 * Authors: 5 * Jacob Jensen 6 * License: 7 * https://github.com/PoisonEngine/poison-ui/blob/master/LICENSE 8 */ 9 module poison.ui.window; 10 11 import dsfmlWindow = dsfml.window; 12 import dsfml.window : Event, Keyboard, Mouse; 13 14 private alias ContextSettings = dsfmlWindow.ContextSettings; 15 private alias VideoMode = dsfmlWindow.VideoMode; 16 private alias WindowStyle = dsfmlWindow.Window.Style; 17 18 import poison.core : Point, Size, ActionArgs, KeyEventArgs, MouseEventArgs, TextEventArgs, executeUI; 19 import poison.ui.container; 20 import poison.ui.component; 21 22 /// A window component. 23 class Window : Container { 24 private: 25 /// The context settings. 26 ContextSettings _context; 27 28 /// The video mode. 29 VideoMode _videoMode; 30 31 /// The render window. 32 RenderWindow _window; 33 34 /// The key event. 35 KeyEventArgs _keyEvent; 36 37 /// The mouse event. 38 MouseEventArgs _mouseEvent; 39 40 /// The text event. 41 TextEventArgs _textEvent; 42 43 /// The fps for the window. 44 uint _fps; 45 46 /// Boolean determining whether the cursor is visible or not. 47 bool _cursorVisible; 48 49 public: 50 /** 51 * Creates a new window. 52 * Params: 53 * name = The name of the window. 54 * title = The title of the window. 55 * size = The size of the window. 56 * layers = The layers of the window. (Defaults to 1) 57 */ 58 this(string name, dstring title, Size size, size_t layers = 1) { 59 super(name, size, layers); 60 61 _context.antialiasingLevel = 8; 62 _videoMode = VideoMode(cast(int)size.width, cast(int)size.height); 63 _keyEvent = new KeyEventArgs(); 64 _mouseEvent = new MouseEventArgs(); 65 _textEvent = new TextEventArgs(); 66 67 innerText = title; 68 _fps = 60; 69 70 addSelector("window"); 71 } 72 73 @property { 74 /// Gets the title of the window. 75 dstring title() { return super.innerText; } 76 77 /// Sets the title of the window. 78 void title(dstring newTitle) { 79 super.innerText = newTitle; 80 81 if (_window) { 82 _window.setTitle(super.innerText); 83 } 84 } 85 86 /// Gets a boolean determining whether the window is open or not. 87 bool isOpen() { 88 return _window && _window.isOpen(); 89 } 90 91 /** 92 * Gets the parent window. 93 * Returns: 94 * Always returns "this" where "this" is the window itself. 95 */ 96 override Window parentWindow() { 97 return this; 98 } 99 100 /// Gets or sets a boolean determining whether the window is hidden or not. 101 override void hidden(bool isHidden) { 102 if (_window) { 103 _window.setVisible(!isHidden); 104 } 105 106 super.hidden = isHidden; 107 } 108 109 /// Gets the fps of the window. 110 uint fps() { return _fps; } 111 112 /// Sets the fps of the window. 113 void fps(uint newFps) { 114 _fps = newFps; 115 116 if (_window) { 117 _window.setFramerateLimit(_fps); 118 } 119 } 120 121 /// Gets a boolean determining whether the cursor is visible or not. 122 bool cursorVisible() { return _cursorVisible; } 123 124 /// Sets a boolean determining whether the cursor is visible or not. 125 void cursorVisible(bool isCursorVisible) { 126 _cursorVisible = isCursorVisible; 127 128 _window.setMouseCursorVisible(_cursorVisible); 129 } 130 } 131 132 /// Shows the window. 133 override void show() { 134 if (!_window) { 135 _window = new RenderWindow(_videoMode, title, (WindowStyle.Titlebar | WindowStyle.Resize | WindowStyle.Close), _context); 136 137 if (_fps) { 138 _window.setFramerateLimit(_fps); 139 } 140 141 _windowComponents[super.id] = this; 142 } 143 144 super.show(); 145 } 146 147 /// Closes the window. 148 void close() { 149 assert(_window !is null); 150 151 _window.close(); 152 } 153 154 /** 155 * Sends a key input through memory. 156 * Params: 157 * key = The key to send an input of. 158 */ 159 void sendKeyInput(Keyboard.Key key) { 160 executeUI({ 161 _keyEvent.press(key); 162 163 if (_windowComponents) { 164 foreach (component; _windowComponents) { 165 if (component && !component.disabled) { 166 component.fireEvent("keyDown", _keyEvent); 167 } 168 } 169 } 170 171 _keyEvent.release(key); 172 173 if (_windowComponents) { 174 foreach (component; _windowComponents) { 175 if (component && !component.disabled) { 176 component.fireEvent("keyUp", _keyEvent); 177 } 178 } 179 } 180 }); 181 } 182 183 /** 184 * Sends a mouse input through memory. 185 * Params: 186 * button = The button to send an input of. 187 * position = The position of the mouse input. 188 * Note: 189 * This doesn't change the position of the cursor. Use Window.changeCursorPosition() instead. 190 */ 191 void sendMouseInput(Mouse.Button button, Point position) { 192 executeUI({ 193 auto lastPosition = _mouseEvent.position; 194 195 _mouseEvent.press(button); 196 _mouseEvent.position = position; 197 198 if (_windowComponents) { 199 foreach (component; _windowComponents) { 200 if (component && !component.disabled) { 201 component.fireEvent("mouseDown", _mouseEvent); 202 } 203 } 204 } 205 206 _mouseEvent.release(button); 207 208 if (_windowComponents) { 209 foreach (component; _windowComponents) { 210 if (component && !component.disabled) { 211 component.fireEvent("mouseUp", _mouseEvent); 212 } 213 } 214 } 215 216 _mouseEvent.position = lastPosition; 217 }); 218 } 219 220 /** 221 * Changes the cursor position. 222 * Params: 223 * position = The position to set the cursor at. 224 * Note: 225 * For virtual mouse presses do not use this. Use Window.sendMouseInput() instead. 226 */ 227 void changeCursorPosition(Point position) { 228 executeUI({ 229 _mouseEvent.position = position; 230 231 import dsfml.system : Vector2i; 232 Mouse.setPosition(Vector2i(position.x, position.y)); 233 }); 234 } 235 236 package(poison): 237 /// Components for event handling 238 Component[size_t] _windowComponents; 239 240 /// Processes the window cycle. 241 void process() { 242 Event event; 243 while(_window.pollEvent(event)) { 244 switch (event.type) { 245 case Event.EventType.Closed: { 246 _window.close(); 247 return; 248 } 249 250 case Event.EventType.KeyPressed: { 251 _keyEvent.press(event.key.code); 252 253 if (_windowComponents) { 254 foreach (component; _windowComponents) { 255 if (component && !component.disabled) { 256 component.fireEvent("keyDown", _keyEvent); 257 } 258 } 259 } 260 261 break; 262 } 263 264 case Event.EventType.KeyReleased: { 265 _keyEvent.release(event.key.code); 266 267 if (_windowComponents) { 268 foreach (component; _windowComponents) { 269 270 if (component && !component.disabled) { 271 component.fireEvent("keyUp", _keyEvent); 272 } 273 } 274 } 275 276 break; 277 } 278 279 case Event.EventType.MouseButtonPressed: { 280 _mouseEvent.press(event.mouseButton.button); 281 282 if (_windowComponents) { 283 foreach (component; _windowComponents) { 284 if (component && !component.disabled && component.intersect(_mouseEvent.position)) { 285 component.fireEvent("mouseDown", _mouseEvent); 286 } 287 } 288 } 289 290 break; 291 } 292 293 case Event.EventType.MouseButtonReleased: { 294 _mouseEvent.release(event.mouseButton.button); 295 296 if (_windowComponents) { 297 foreach (component; _windowComponents) { 298 if (component && !component.disabled && component.intersect(_mouseEvent.position)) { 299 component.fireEvent("mouseUp", _mouseEvent); 300 } 301 } 302 } 303 304 break; 305 } 306 307 case Event.EventType.MouseMoved: { 308 _mouseEvent.position = new Point(event.mouseMove.x, event.mouseMove.y); 309 310 if (_windowComponents) { 311 foreach (component; _windowComponents) { 312 if (component) { 313 component.fireEvent("mouseMoved", _mouseEvent); 314 } 315 } 316 } 317 318 break; 319 } 320 321 case Event.EventType.TextEntered: { 322 _textEvent.enter(event.text.unicode); 323 324 if (_windowComponents) { 325 foreach (component; _windowComponents) { 326 if (component && !component.disabled) { 327 component.fireEvent("textEntered", _textEvent); 328 } 329 } 330 } 331 332 break; 333 } 334 335 default: break; 336 } 337 } 338 339 _window.clear(super.graphics.backgroundPaint.sfmlColor); 340 super.process(_window); 341 _window.display(); 342 } 343 }