-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathControl.cs
281 lines (248 loc) · 8.15 KB
/
Control.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GUI
{
/// <summary>
/// Base Control class. Handles rendering and mouse events.
/// </summary>
public abstract class Control
{
List<Control> _controls;
WindowManager _wm;
public delegate void ClickEventHandler(object sender, ClickEventArgs eventArgs);
public class ClickEventArgs
{
public int X;
public int Y;
public ClickEventArgs(int X, int Y)
{
this.X = X;
this.Y = Y;
}
}
public event ClickEventHandler OnClick;
/// <summary>
/// If control is not visible, it loses collision and is not rendered.
/// </summary>
public bool Visible { get; set; }
/// <summary>
/// The window manager this control is running on.
/// Used to notify the WM about focus and events.
/// </summary>
public WindowManager WM
{
get
{
return _wm;
}
set
{
_wm = value;
//assigns all the child controls. Recursvie.
if (Controls.Count != 0)
{
foreach (Control c in this.Controls)
{
c.WM = value;
}
}
}
}
/// <summary>
/// Child controls of this control.
/// </summary>
public List<Control> Controls
{
get
{
//lazy initialization, ho!
if (_controls == null)
{
_controls = new List<Control>();
}
return _controls;
}
set
{
_controls = value;
}
}
/// <summary>
/// Wrapper method for adding controls. Handles WM parenting.
/// </summary>
/// <param name="c">The control to add.</param>
public void AddControl(Control c)
{
if (this.Controls == null)
this.Controls = new List<Control>();
c.Parent = (Control)this;
c.WM = this.WM;
this.Controls.Add(c);
}
/// <summary>
/// Generic Constructor.
/// </summary>
public Control()
{
this.Controls = new List<Control>();
this.Visible = true;
}
/// <summary>
/// Parent control - used to propagate events.
/// Can be null for top level controls (mostly windows).
/// </summary>
public Control Parent { get; set; }
/// <summary>
/// Position from left in pixels.
/// </summary>
public virtual int X { get; set; }
/// <summary>
/// Position from top in pixels.
/// </summary>
public virtual int Y { get; set; }
/// <summary>
/// Width in pixels.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Height in pixels.
/// </summary>
public int Height { get; set; }
/// <summary>
/// The title of the control.
/// </summary>
public string Title { get; set; }
/// <summary>
/// Dummy method to override for click events.
/// </summary>
/// <param name="X">X</param>
/// <param name="Y">Y</param>
public virtual void Click(float X, float Y)
{
//Console.Write(this.GetType().ToString() + ": " + (int)X + "," + (int)Y);
}
/// <summary>
/// TODO: Right clicking behaviour.
/// </summary>
public virtual void RightClick()
{
}
/// <summary>
/// Override to define mouse down behaviour. Fires once and won't fire until mouse button has been released and pressed again.
/// </summary>
/// <param name="X">X</param>
/// <param name="Y">Y</param>
public virtual void MouseDown(float X, float Y)
{
// Volatile.Console.Write("mousedown event - " + X.ToString() + "," + Y.ToString() + "@" + this.Title);
this.WM.LastClickedControl = this;
foreach (Control c in this.Controls)
{
if (c.CheckCollision(X, Y))
{
c.MouseDown(X - c.X, Y - c.Y);
break;
}
}
}
/// <summary>
/// Override to define mouse up behaviour. Fires once per mouse release.
/// </summary>
/// <param name="X">X</param>
/// <param name="Y">Y</param>
public virtual void MouseUp(float X, float Y)
{
//this more or less emulates Windows behaviour
//you can mousedown, move out and back in again and releasing will click the control
//otherwise mouseup wastes the click
if (this.WM.LastClickedControl != null && this.WM.LastClickedControl == this)
{
this.Click(X, Y);
if(this is ITextInput input)
{
this.WM.FocusedText = input;
}
else
{
this.WM.FocusedText?.LoseFocus();
this.WM.FocusedText = null;
}
OnClick?.Invoke(this, new ClickEventArgs((int)X, (int)Y));
}
foreach (Control c in this.Controls)
{
if (c.CheckCollision(X, Y))
{
c.MouseUp(X - c.X, Y - c.Y);
break;
}
}
this.WM.LastClickedControl = null;
}
/// <summary>
/// Override to define mouse move behaviour. Fires as long as mouse collides with the control.
/// </summary>
/// <param name="X">X</param>
/// <param name="Y">Y</param>
public virtual void MouseMove(float X, float Y)
{
foreach (Control c in this.Controls)
{
if (c.CheckCollision(X, Y))
{
c.MouseMove(X - c.X, Y - c.Y);
break;
}
}
}
/// <summary>
/// Draws the control. Base behaviour propagates the coordinates
/// so that the controls are positioned relative to parent.
/// </summary>
/// <param name="device">GraphicsDevice that the control is rendered to.</param>
/// <param name="X">X in pixels.</param>
/// <param name="Y">Y in pixels.</param>
public virtual void Render(Microsoft.Xna.Framework.Graphics.GraphicsDevice device, Renderer Renderer, int X, int Y)
{
foreach (Control c in this.Controls)
{
if(c.Visible)
c.Render(device, Renderer, this.X + X, this.Y + Y);
}
}
/// <summary>
/// Checks whether a set of coordinates is within this control's area.
/// </summary>
/// <param name="X">X in pixels.</param>
/// <param name="Y">Y in pixels.</param>
/// <returns></returns>
public bool CheckCollision(float X, float Y)
{
bool r = false;
if (this.Visible && X < this.X + Width && X > this.X && Y < this.Y + this.Height && Y > this.Y)
r = true;
return r;
}
/// <summary>
/// Gets the window this control belongs to
/// </summary>
/// <returns>true on success, false on failure (orphaned control)</returns>
public Window GetParentWindow()
{
Control p = this.Parent;
//traverse up a parent tree - windows have no parent so it will exit once it hits one, closing it.
while (p != null)
{
if (p is Window w)
{
return w;
}
p = p.Parent;
}
return null;
}
}
}