1 module poison.ui.window;
2 
3 import dsfmlWindow = dsfml.window;
4 import dsfml.window : Event;
5 
6 private alias ContextSettings = dsfmlWindow.ContextSettings;
7 private alias VideoMode = dsfmlWindow.VideoMode;
8 private alias WindowStyle = dsfmlWindow.Window.Style;
9 
10 import poison.core : Point, Size, ActionArgs, KeyEventArgs, MouseEventArgs, TextEventArgs;
11 import poison.ui.container;
12 import poison.ui.component;
13 
14 /// A window component.
15 class Window : Container {
16   private:
17   /// The context settings.
18   ContextSettings _context;
19 
20   /// The video mode.
21   VideoMode _videoMode;
22 
23   /// The render window.
24   RenderWindow _window;
25 
26   /// The key event.
27   KeyEventArgs _keyEvent;
28 
29   /// The mouse event.
30   MouseEventArgs _mouseEvent;
31 
32   /// The text event.
33   TextEventArgs _textEvent;
34 
35   /// The fps for the window.
36   uint _fps;
37 
38   public:
39   /**
40   * Creates a new window.
41   * Params:
42   *   name =    The name of the window.
43   *   title =   The title of the window.
44   *   size =    The size of the window.
45   *   layers =  The layers of the window. (Defaults to 1)
46   */
47   this(string name, dstring title, Size size, size_t layers = 1) {
48     super(name, size, layers);
49 
50     _context.antialiasingLevel = 8;
51     _videoMode = VideoMode(cast(int)size.width, cast(int)size.height);
52     _keyEvent = new KeyEventArgs();
53     _mouseEvent = new MouseEventArgs();
54     _textEvent = new TextEventArgs();
55 
56     innerText = title;
57     _fps = 60;
58 
59     addSelector("window");
60   }
61 
62   @property {
63     /// Gets the title of the window.
64     dstring title() { return super.innerText; }
65 
66     /// Sets the title of the window.
67     void title(dstring newTitle) {
68       super.innerText = newTitle;
69 
70       if (_window) {
71         _window.setTitle(super.innerText);
72       }
73     }
74 
75     /// Gets a boolean determining whether the window is open or not.
76     bool isOpen() {
77       return _window && _window.isOpen();
78     }
79 
80     /**
81     * Gets the parent window.
82     * Returns:
83     *   Always returns "this" where "this" is the window itself.
84     */
85     override Window parentWindow() {
86       return this;
87     }
88 
89     /// Gets or sets a boolean determining whether the window is hidden or not.
90     override void hidden(bool isHidden) {
91       if (_window) {
92         _window.setVisible(!isHidden);
93       }
94 
95       super.hidden = isHidden;
96     }
97 
98     /// Gets the fps of the window.
99     uint fps() { return _fps; }
100 
101     /// Sets the fps of the window.
102     void fps(uint newFps) {
103       _fps = newFps;
104 
105       if (_window) {
106         _window.setFramerateLimit(_fps);
107       }
108     }
109   }
110 
111   /// Shows the window.
112   override void show() {
113     if (!_window) {
114       _window = new RenderWindow(_videoMode, title, (WindowStyle.Titlebar | WindowStyle.Resize | WindowStyle.Close), _context);
115 
116       if (_fps) {
117         _window.setFramerateLimit(_fps);
118       }
119 
120       _windowComponents[super.id] = this;
121     }
122 
123     super.show();
124   }
125 
126   /// Closes the window.
127   void close() {
128     assert(_window !is null);
129 
130     _window.close();
131   }
132 
133   package(poison):
134   /// Components for event handling
135   Component[size_t] _windowComponents;
136 
137   /// Processes the window cycle.
138   void process() {
139     Event event;
140     while(_window.pollEvent(event)) {
141       switch (event.type) {
142         case Event.EventType.Closed: {
143           _window.close();
144           return;
145         }
146 
147         case Event.EventType.KeyPressed: {
148           _keyEvent.press(event.key.code);
149 
150           if (_windowComponents) {
151             foreach (component; _windowComponents) {
152               if (component && !component.disabled) {
153                 component.fireEvent("keyDown", _keyEvent);
154               }
155             }
156           }
157 
158           break;
159         }
160 
161         case Event.EventType.KeyReleased: {
162           _keyEvent.release(event.key.code);
163 
164           if (_windowComponents) {
165             foreach (component; _windowComponents) {
166 
167               if (component && !component.disabled) {
168                 component.fireEvent("keyUp", _keyEvent);
169               }
170             }
171           }
172 
173           break;
174         }
175 
176         case Event.EventType.MouseButtonPressed: {
177           _mouseEvent.press(event.mouseButton.button);
178 
179           if (_windowComponents) {
180             foreach (component; _windowComponents) {
181               if (component && !component.disabled && component.intersect(_mouseEvent.position)) {
182                 component.fireEvent("mouseDown", _mouseEvent);
183               }
184             }
185           }
186 
187           break;
188         }
189 
190         case Event.EventType.MouseButtonReleased: {
191           _mouseEvent.release(event.mouseButton.button);
192 
193           if (_windowComponents) {
194             foreach (component; _windowComponents) {
195               if (component && !component.disabled && component.intersect(_mouseEvent.position)) {
196                 component.fireEvent("mouseUp", _mouseEvent);
197               }
198             }
199           }
200 
201           break;
202         }
203 
204         case Event.EventType.MouseMoved: {
205           _mouseEvent.position = new Point(event.mouseMove.x, event.mouseMove.y);
206 
207           if (_windowComponents) {
208             foreach (component; _windowComponents) {
209               if (component) {
210                 component.fireEvent("mouseMoved", _mouseEvent);
211               }
212             }
213           }
214 
215           break;
216         }
217 
218         case Event.EventType.TextEntered: {
219           _textEvent.enter(event.text.unicode);
220 
221           if (_windowComponents) {
222             foreach (component; _windowComponents) {
223               if (component && !component.disabled) {
224                 component.fireEvent("textEntered", _textEvent);
225               }
226             }
227           }
228 
229           break;
230         }
231 
232         default: break;
233       }
234     }
235 
236     _window.clear(super.graphics.backgroundPaint.sfmlColor);
237     super.process(_window);
238     _window.display();
239   }
240 }