23 public record DrawEvent(
CUIComponent Target,
bool front =
false);
30 public Action<CUIInput> OnMouseDown;
public void InvokeOnMouseDown(
CUIInput e) => OnMouseDown?.Invoke(e);
31 public Action<CUIInput> OnMouseUp;
public void InvokeOnMouseUp(
CUIInput e) => OnMouseUp?.Invoke(e);
32 public Action<CUIInput> OnMouseMoved;
public void InvokeOnMouseMoved(
CUIInput e) => OnMouseMoved?.Invoke(e);
33 public Action<CUIInput> OnClick;
public void InvokeOnClick(
CUIInput e) => OnClick?.Invoke(e);
34 public Action<CUIInput> OnKeyDown;
public void InvokeOnKeyDown(
CUIInput e) => OnKeyDown?.Invoke(e);
35 public Action<CUIInput> OnKeyUp;
public void InvokeOnKeyUp(
CUIInput e) => OnKeyUp?.Invoke(e);
42 public double UpdateInterval = 1.0 / 300.0;
52 public event Action OnTreeChanged;
53 public Action AddOnTreeChanged {
set { OnTreeChanged += value; } }
55 public CUIDragHandle GrabbedDragHandle;
56 public CUIResizeHandle GrabbedResizeHandle;
57 public CUISwipeHandle GrabbedSwipeHandle;
61 get =>
CUI.FocusedComponent;
62 set => CUI.FocusedComponent = value;
71 private Stopwatch sw =
new Stopwatch();
73 internal List<CUIComponent> Flat =
new();
74 internal List<CUIComponent> Leaves =
new();
75 internal List<DrawEvent> DrawEvents =
new();
76 internal SortedList<int, List<CUIComponent>> Layers =
new();
77 private List<CUIComponent> MouseOnList =
new();
78 private Vector2 GrabbedOffset;
80 private void RunStraigth(Action<CUIComponent> a) {
for (
int i = 0; i < Flat.Count; i++) a(Flat[i]); }
81 private void RunReverse(Action<CUIComponent> a) {
for (
int i = Flat.Count - 1; i >= 0; i--) a(Flat[i]); }
86 private void FlattenTree()
93 if (retries > 10)
break;
101 void CalcZIndexRec(CUIComponent component,
int added = 0)
103 component.positionalZIndex = globalIndex;
105 component.addedZIndex = added;
106 if (component.ZIndex.HasValue) component.addedZIndex += component.ZIndex.Value;
108 foreach (CUIComponent child
in component.Children)
110 CalcZIndexRec(child, component.addedZIndex);
114 CalcZIndexRec(
this, 0);
117 int i = c.positionalZIndex + c.addedZIndex;
118 if (!Layers.ContainsKey(i)) Layers[i] =
new List<CUIComponent>();
122 foreach (var layer
in Layers)
124 Flat.AddRange(layer.Value);
131 CUI.Warning($
"Couldn't Flatten component tree: {e.Message}");
138 internal bool GlobalLayoutChanged;
139 internal void LayoutChanged() => GlobalLayoutChanged =
true;
140 private double LastUpdateTime;
141 private int UpdateLoopCount = 0;
147 Update(LastUpdateTime + UpdateInterval,
true,
true);
149 public void Update(
double totalTime,
bool force =
false,
bool noInput =
false)
154 if (totalTime - LastUpdateTime <= UpdateInterval)
return;
161 OnTreeChanged?.Invoke();
167 if (!noInput) HandleInput(totalTime);
169 RunStraigth(c => c.InvokeOnUpdate(totalTime));
177 GlobalLayoutChanged =
false;
181 OnTreeChanged?.Invoke();
189 c.Layout.ResizeToContent();
195 c.Layout.UpdateDecor();
201 PrintRecalLimitWarning();
205 while (GlobalLayoutChanged);
212 c.Layout.ResizeToContent();
218 c.Layout.UpdateDecor();
225 LastUpdateTime = totalTime;
231 private void StopStart(SpriteBatch spriteBatch, Rectangle SRect,
SamplerState? samplerState =
null)
233 samplerState ??= GUI.SamplerState;
235 spriteBatch.GraphicsDevice.ScissorRectangle = SRect;
236 spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: samplerState, rasterizerState: GameMain.ScissorTestEnable);
239 public new void Draw(SpriteBatch spriteBatch)
243 Rectangle OriginalSRect = spriteBatch.GraphicsDevice.ScissorRectangle;
244 Rectangle SRect = OriginalSRect;
250 if (!c.Visible || c.CulledOut)
return;
251 if (c.Parent !=
null && c.Parent.ScissorRect.HasValue && SRect != c.Parent.ScissorRect.Value)
253 SRect = c.Parent.ScissorRect.Value;
254 StopStart(spriteBatch, SRect, c.SamplerState);
261 if (spriteBatch.GraphicsDevice.ScissorRectangle != OriginalSRect) StopStart(spriteBatch, OriginalSRect);
266 if (!c.Visible || c.CulledOut)
return;
267 c.DrawFront(spriteBatch);
276 #region HandleInput Start
278 public void OnDragEnd(CUIDragHandle h) {
if (h == GrabbedDragHandle) GrabbedDragHandle =
null; }
279 public void OnResizeEnd(CUIResizeHandle h) {
if (h == GrabbedResizeHandle) GrabbedResizeHandle =
null; }
280 public void OnSwipeEnd(CUISwipeHandle h) {
if (h == GrabbedSwipeHandle) GrabbedSwipeHandle =
null; }
283 private void HandleInput(
double totalTime)
285 HandleGlobal(totalTime);
286 HandleMouse(totalTime);
287 HandleKeyboard(totalTime);
290 private void HandleGlobal(
double totalTime)
292 if (CUI.Input.MouseDown)
Global.InvokeOnMouseDown(CUI.Input);
293 if (CUI.Input.MouseUp)
295 Global.InvokeOnMouseUp(CUI.Input);
296 Global.InvokeOnClick(CUI.Input);
298 if (CUI.Input.MouseMoved)
Global.InvokeOnMouseMoved(CUI.Input);
299 if (CUI.Input.SomeKeyPressed)
Global.InvokeOnKeyDown(CUI.Input);
300 if (CUI.Input.SomeKeyUnpressed)
Global.InvokeOnKeyUp(CUI.Input);
303 private void HandleKeyboard(
double totalTime)
305 if (FocusedComponent ==
null) FocusedComponent =
this;
306 if (CUI.Input.PressedKeys.Contains(Keys.Escape)) FocusedComponent =
this;
307 if (CUI.Input.SomeKeyPressed) FocusedComponent.InvokeOnKeyDown(CUI.Input);
308 if (CUI.Input.SomeKeyUnpressed) FocusedComponent.InvokeOnKeyUp(CUI.Input);
309 if (CUI.Input.SomeWindowEvents) FocusedComponent.InvokeOnTextInput(CUI.Input);
312 private void HandleMouse(
double totalTime)
314 if (!CUI.Input.SomethingHappened)
return;
316 if (!CUI.Input.MouseHeld)
318 GrabbedDragHandle?.EndDrag();
319 GrabbedResizeHandle?.EndResize();
320 GrabbedSwipeHandle?.EndSwipe();
323 if (CUI.Input.MouseMoved)
325 GrabbedDragHandle?.DragTo(CUI.Input.MousePosition);
326 GrabbedResizeHandle?.Resize(CUI.Input.MousePosition);
327 GrabbedSwipeHandle?.Swipe(CUI.Input);
330 if (CUI.Input.MouseInputHandled)
return;
336 if (GrabbedResizeHandle !=
null || GrabbedDragHandle !=
null || GrabbedSwipeHandle !=
null)
return;
338 List<CUIComponent> prevMouseOnList =
new List<CUIComponent>(MouseOnList);
340 CUIComponent CurrentMouseOn =
null;
346 GUI.MouseOn ==
null || (GUI.MouseOn is GUIButton btn && btn.Text ==
"DUMMY")
347 || (
this == CUI.TopMain)
352 bool ok = !c.IgnoreEvents && c.Real.Contains(CUI.Input.MousePosition) && c.ShouldInvoke(CUI.Input);
354 if (c.Parent !=
null && c.Parent.ScissorRect.HasValue &&
355 !c.Parent.ScissorRect.Value.Contains(CUI.Input.Mouse.Position))
360 if (ok) MouseOnList.Add(c);
364 MouseOn = MouseOnList.LastOrDefault();
369 CUI.Input.MouseInputHandled =
true;
370 CUIMultiModResolver.MarkOtherInputsAsHandled();
376 foreach (CUIComponent c
in prevMouseOnList)
378 c.MousePressed =
false;
380 c.InvokeOnMouseOff(CUI.Input);
384 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
386 MouseOnList[i].MousePressed = CUI.Input.MouseHeld;
387 MouseOnList[i].MouseOver =
true;
388 MouseOnList[i].InvokeOnMouseOn(CUI.Input);
390 if (MouseOnList[i].ConsumeMouseClicks)
break;
394 foreach (CUIComponent c
in prevMouseOnList.Except(MouseOnList)) c.InvokeOnMouseLeave(CUI.Input);
395 foreach (CUIComponent c
in MouseOnList.Except(prevMouseOnList)) c.InvokeOnMouseEnter(CUI.Input);
399 if (CUI.Input.MouseDown)
401 CUIComponent newFocused =
this;
402 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
404 if (MouseOnList[i].FocusHandle.ShouldStart(CUI.Input))
406 newFocused = MouseOnList[i];
410 FocusedComponent = newFocused;
414 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
416 if (MouseOnList[i].RightResizeHandle.ShouldStart(CUI.Input))
418 GrabbedResizeHandle = MouseOnList[i].RightResizeHandle;
419 GrabbedResizeHandle.BeginResize(CUI.Input.MousePosition);
423 if (MouseOnList[i].LeftResizeHandle.ShouldStart(CUI.Input))
425 GrabbedResizeHandle = MouseOnList[i].LeftResizeHandle;
426 GrabbedResizeHandle.BeginResize(CUI.Input.MousePosition);
430 if (GrabbedResizeHandle !=
null)
return;
433 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
435 if (CUI.Input.Scrolled) MouseOnList[i].InvokeOnScroll(CUI.Input);
437 if (MouseOnList[i].ConsumeMouseScroll)
break;
441 if (CUI.Input.MouseMoved)
443 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
445 MouseOnList[i].InvokeOnMouseMove(CUI.Input);
452 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
454 if (CUI.Input.MouseDown) MouseOnList[i].InvokeOnMouseDown(CUI.Input);
455 if (CUI.Input.MouseUp)
457 MouseOnList[i].InvokeOnMouseUp(CUI.Input);
458 MouseOnList[i].InvokeOnClick(CUI.Input);
460 if (CUI.Input.DoubleClick) MouseOnList[i].InvokeOnDClick(CUI.Input);
462 if (MouseOnList[i].ConsumeMouseClicks || CUI.Input.ClickConsumed)
break;
464 if (CUI.Input.ClickConsumed)
return;
467 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
469 if (MouseOnList[i].SwipeHandle.ShouldStart(CUI.Input))
471 GrabbedSwipeHandle = MouseOnList[i].SwipeHandle;
472 GrabbedSwipeHandle.BeginSwipe(CUI.Input.MousePosition);
476 if (MouseOnList[i].ConsumeSwipe)
break;
478 if (GrabbedSwipeHandle !=
null)
return;
481 for (
int i = MouseOnList.Count - 1; i >= 0; i--)
483 if (MouseOnList[i].DragHandle.ShouldStart(CUI.Input))
485 GrabbedDragHandle = MouseOnList[i].DragHandle;
486 GrabbedDragHandle.BeginDrag(CUI.Input.MousePosition);
490 if (MouseOnList[i].ConsumeDragAndDrop)
break;
492 if (GrabbedDragHandle !=
null)
return;
495 #region HandleInput End
503 public void Load(Action<CUIMainComponent> initFunc)
512 Real =
new CUIRect(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
522 public void PrintRecalLimitWarning()
524 CUI.Log($
"Warning: Your GUI code requires {MaxLayoutRecalcLoopsPerUpdate} layout update loops to fully resolve (which is cringe). Optimize it!", Color.Orange);