1 module poison.ui.component; 2 3 import std.algorithm : filter; 4 import std.array : array; 5 6 import poison.ui.space; 7 import poison.core : ActionArgs, Point, Size, EventArgs, ChangeEventArgs, executeUI, isUIThread, CrossThreadingExeption; 8 import poison.ui.window; 9 import poison.ui.graphics; 10 import poison.ui.paint; 11 import poison.ui.picture; 12 import poison.ui.container; 13 14 public import dsfml.graphics : RenderWindow; 15 16 /// The next component id. 17 private size_t nextId = 0; 18 19 /// A component. 20 class Component : Space { 21 private: 22 /// The name. 23 string _name; 24 25 /// The inner text. 26 dstring _innerText; 27 28 /// The outer text. 29 dstring _outerText; 30 31 /// Boolean determining whether it's disabled or not. 32 bool _disabled; 33 34 /// Boolean determining whether it's hidden or not. 35 bool _hidden; 36 37 /// The id. 38 size_t _id; 39 40 /// The layer. 41 ptrdiff_t _layer; 42 43 /// The parent window. 44 Window _parentWindow; 45 46 /// The parent container. 47 Container _parentContainer; 48 49 /// The graphics. 50 Graphics _graphics; 51 52 /// Style selectors. 53 string[] _selectors; 54 55 /// Selectors to render with. 56 string[] _renderSelectors; 57 58 /// The selector name. 59 string _selectorName; 60 61 protected: 62 /** 63 * Creates a new component. 64 * Params: 65 * name = The name of the component. 66 * initialSize = The initial size of the component. 67 */ 68 this(string name, Size initialSize) { 69 if (!isUIThread) { 70 throw new CrossThreadingExeption("Cannot create a component outside the UI thread. Consider using exeuteUI();"); 71 } 72 73 super(new Point(0, 0), initialSize); 74 75 _name = name; 76 _layer = -1; 77 _id = nextId++; 78 _graphics = new Graphics(); 79 _selectors = ["component"]; 80 _selectorName = "#" ~ _name; 81 } 82 83 /** 84 * Creates a new component. 85 * Params: 86 * name = The name of the component. 87 */ 88 this(string name) { 89 this(name, new Size(100, 100)); 90 } 91 92 93 /// Renders the component. Override this! 94 void render() { 95 _graphics.size = super.size; 96 _graphics.position = super.position; 97 } 98 99 /** 100 * Draws the component. Override this! 101 * Params: 102 * window = The window to draw the component to. 103 */ 104 void draw(RenderWindow window) { 105 if (_graphics.displayableBackgroundRect) { 106 window.draw(_graphics.backgroundRect); 107 } 108 109 auto picture = _graphics.backgroundPicture; 110 111 if (picture && _graphics.displayableBackgroundPicture) { 112 if (picture.backgroundSprite) { 113 window.draw(picture.backgroundSprite); 114 } 115 116 if (picture.drawingSprite) { 117 window.draw(picture.drawingSprite); 118 } 119 } 120 } 121 122 public: 123 @property { 124 /// Gets the name of the component. 125 string name() { return _name; } 126 127 /// Gets a boolean determining whether the component is enabled or not. 128 bool enabled() { return !_disabled; } 129 130 /// Sets a boolean determining whether the component is enabled or not. 131 void enabled(bool isEnabled) { 132 disabled = !isEnabled; 133 } 134 135 /// Gets a boolean determining whether the component is disabled or not. 136 bool disabled() { return _disabled; } 137 138 /// Sets a boolean determining whether the component is disabled. 139 void disabled(bool isDisabled) { 140 executeUI({ 141 _disabled = isDisabled; 142 143 fireEvent(_disabled ? "beforeDisabled" : "beforeEnabled", EventArgs.empty); 144 145 updateSelectors(); 146 render(); 147 148 fireEvent(_disabled ? "disabled" : "enabled", EventArgs.empty); 149 }); 150 } 151 152 /// Gets a boolean determining whether the component is visible or not. 153 bool visible() { return !_hidden; } 154 155 /// Sets a boolean determining whether the component is visible or not. 156 void visible(bool isVisible) { 157 hidden = !isVisible; 158 } 159 160 /// Gets a boolean determining whether the component is hidden or not. 161 bool hidden() { return _hidden; } 162 163 /// Sets a boolean determining whether the component is hidden or not. 164 void hidden(bool isHidden) { 165 executeUI({ 166 _hidden = isHidden; 167 168 fireEvent(_hidden ? "beforeHide" : "beforeShow", EventArgs.empty); 169 170 if (!_hidden) { 171 render(); 172 } 173 174 fireEvent(_hidden ? "hide" : "show", EventArgs.empty); 175 }); 176 } 177 178 /// Gets the inner text of the component. 179 dstring innerText() { return _innerText; } 180 181 /// Sets the inner text of the component. 182 void innerText(dstring newInnerText) { 183 executeUI({ 184 auto oldInnerText = _innerText; 185 _innerText = newInnerText; 186 187 fireEvent("innerText", new ChangeEventArgs!dstring(oldInnerText, _innerText)); 188 189 render(); 190 renderSub(); 191 }); 192 } 193 194 /// Gets the outer text of the component. 195 dstring outerText() { return _outerText; } 196 197 /// Sets the outer text of the component. 198 void outerText(dstring newOuterText) { 199 executeUI({ 200 auto oldOuterText = _outerText; 201 _outerText = newOuterText; 202 203 fireEvent("outerText", new ChangeEventArgs!dstring(oldOuterText, _outerText)); 204 205 render(); 206 }); 207 } 208 209 /// Ges the position of the component. 210 override Point position() { return super.position; } 211 212 /// Sets the position of the component. 213 override void position(Point newPoint) { 214 executeUI({ 215 super.position = newPoint; 216 217 render(); 218 renderSub(); 219 }); 220 } 221 222 /// Gets the size of the component. 223 override Size size() { return super.size; } 224 225 /// Sets the size of the component. 226 override void size(Size newSize) { 227 executeUI({ 228 super.size = newSize; 229 230 render(); 231 renderSub(); 232 }); 233 } 234 235 /// Gets the layer of the component. 236 ptrdiff_t layer() { return _layer; } 237 238 /// Gets the parent window of the component. 239 Window parentWindow() { 240 return _parentWindow; 241 } 242 243 /// Gets the parent container of the component. 244 Container parentContainer() { 245 return _parentContainer; 246 } 247 } 248 249 /// Shows the component. 250 void show() { 251 visible = true; 252 } 253 254 /// Hides the component. 255 void hide() { 256 hidden = true; 257 } 258 259 /// Enables the component. 260 void enable() { 261 enabled = true; 262 } 263 264 /// Disables the component. 265 void disable() { 266 disabled = true; 267 } 268 269 /** 270 * Adds a style selector. 271 * Params: 272 * selector = The selector to add. 273 */ 274 void addSelector(string selector) { 275 _selectors ~= selector; 276 277 updateSelectors(); 278 } 279 280 /** 281 * Removes a style selector. 282 * Params: 283 * The style selector to remove. 284 */ 285 void removeSelector(string selector) { 286 _selectors = _selectors.filter!((s) { return s != selector; }).array; 287 288 updateSelectors(); 289 } 290 291 /** 292 * Checks whether the space intersects with a point. 293 * Params: 294 * p = The point to check for intersection with. 295 */ 296 override bool intersect(Point p) { 297 auto pIntersects = _parentContainer ? _parentContainer.intersect(p) : true; 298 299 return pIntersects && super.intersect(p); 300 } 301 302 /** 303 * Checks whether the space intersects with another space. 304 * Params: 305 * target = The space to check for intersection with. 306 */ 307 override bool intersect(Space target) { 308 auto pIntersects = _parentContainer ? _parentContainer.intersect(target) : true; 309 310 return pIntersects && super.intersect(target); 311 } 312 313 protected: 314 @property { 315 /// Gets the graphics of the component. 316 Graphics graphics() { return _graphics; } 317 } 318 319 /** 320 * Processes the component during application cycles. 321 * Params: 322 * window = The render window to process. 323 */ 324 void process(RenderWindow window) { 325 if (!_hidden) { 326 draw(window); 327 } 328 } 329 330 private: 331 /// Updates the selectors. 332 void updateSelectors() { 333 auto prefix = (_disabled ? ":disabled" : ":enabled"); 334 335 _renderSelectors ~= _selectors; 336 337 foreach (selector; _selectors) { 338 _renderSelectors ~= selector ~ prefix; 339 } 340 341 _renderSelectors ~= _selectorName; 342 _renderSelectors ~= _selectorName ~ prefix; 343 344 updateStyles(); 345 } 346 347 package(poison): 348 /// Renders the sub rectangles for the graphics of the component. 349 void renderSub() { 350 if (_graphics && _parentContainer) { 351 _graphics.renderSub(_parentContainer.position, _parentContainer.size); 352 } 353 } 354 355 /** 356 * Processes the component during application cycles. 357 * Params: 358 * window = The render window to process. 359 */ 360 void processInternal(RenderWindow window) { 361 process(window); 362 } 363 364 /// Updates the styles of the component. 365 void updateStyles() { 366 import poison.ui.styles; 367 368 if (_selectors) { 369 foreach (selector; _renderSelectors) { 370 auto styleEntry = getStyleEntry(selector); 371 372 if (styleEntry) { 373 if (styleEntry.backgroundPicture) { 374 _graphics.backgroundPicture = new Picture(styleEntry.backgroundPicture); 375 _graphics.position = super.position; 376 _graphics.backgroundPicture.finalize(); 377 } 378 379 _graphics.backgroundPaint = styleEntry.backgroundPaint; 380 _graphics.foregroundPaint = styleEntry.foregroundPaint; 381 382 _graphics.font = styleEntry.font; 383 _graphics.fontSize = styleEntry.fontSize; 384 } 385 } 386 } 387 388 renderSub(); 389 } 390 391 @property { 392 /// Gets the id of the component. 393 size_t id() { return _id; } 394 395 /// Sets the layer of the component. 396 void layer(ptrdiff_t newLayer) { 397 _layer = newLayer; 398 } 399 400 /// Sets the parent window of the component. 401 void parentWindow(Window newParentWindow) { 402 _parentWindow = newParentWindow; 403 } 404 405 /// Sets the parent container of the component. 406 void parentContainer(Container newParentContainer) { 407 _parentContainer = newParentContainer; 408 } 409 } 410 }