Change the way container controls work and add Grid layout.
This commit is contained in:
parent
ff3b3ee961
commit
c6a9dd8008
@ -12,13 +12,22 @@ namespace Dashboard.Controls
|
|||||||
|
|
||||||
public bool IsReadOnly => false;
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
public event EventHandler<ChildEventArgs>? ChildAdded;
|
||||||
|
public event EventHandler<ChildEventArgs>? ChildRemoved;
|
||||||
|
|
||||||
public void Add(Control item)
|
public void Add(Control item)
|
||||||
{
|
{
|
||||||
children.Add(item);
|
children.Add(item);
|
||||||
|
OnChildAdded(new ChildEventArgs(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
foreach (Control child in this)
|
||||||
|
{
|
||||||
|
OnChildRemoved(new ChildEventArgs(child));
|
||||||
|
}
|
||||||
|
|
||||||
children.Clear();
|
children.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +48,25 @@ namespace Dashboard.Controls
|
|||||||
|
|
||||||
public bool Remove(Control item)
|
public bool Remove(Control item)
|
||||||
{
|
{
|
||||||
return children.Remove(item);
|
if (children.Remove(item))
|
||||||
|
{
|
||||||
|
OnChildRemoved(new ChildEventArgs(item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnChildAdded(ChildEventArgs ea)
|
||||||
|
{
|
||||||
|
Adopt(ea.Child, this);
|
||||||
|
ChildAdded?.Invoke(this, ea);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnChildRemoved(ChildEventArgs ea)
|
||||||
|
{
|
||||||
|
Adopt(ea.Child, null);
|
||||||
|
ChildRemoved?.Invoke(this, ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
@ -47,4 +74,13 @@ namespace Dashboard.Controls
|
|||||||
return children.GetEnumerator();
|
return children.GetEnumerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public class ChildEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Control Child { get; }
|
||||||
|
|
||||||
|
public ChildEventArgs(Control child)
|
||||||
|
{
|
||||||
|
Child = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Dashboard.ImmediateDraw;
|
using Dashboard.ImmediateDraw;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Dashboard.Controls
|
namespace Dashboard.Controls
|
||||||
{
|
{
|
||||||
@ -8,6 +9,18 @@ namespace Dashboard.Controls
|
|||||||
{
|
{
|
||||||
private readonly DrawList drawCommands = new DrawList();
|
private readonly DrawList drawCommands = new DrawList();
|
||||||
|
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
public override Vector2 Position
|
||||||
|
{
|
||||||
|
get => base.Position;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Position = value;
|
||||||
|
InvalidateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Style Style { get; set; } = new Style();
|
public Style Style { get; set; } = new Style();
|
||||||
public float Padding
|
public float Padding
|
||||||
{
|
{
|
||||||
@ -20,6 +33,8 @@ namespace Dashboard.Controls
|
|||||||
|
|
||||||
protected bool IsLayoutSuspended { get; private set; } = false;
|
protected bool IsLayoutSuspended { get; private set; } = false;
|
||||||
|
|
||||||
|
protected ResizedEventArgs? LastResizeEvent { get; private set; }
|
||||||
|
|
||||||
public void InvalidateVisual()
|
public void InvalidateVisual()
|
||||||
{
|
{
|
||||||
IsVisualsValid = false;
|
IsVisualsValid = false;
|
||||||
@ -72,6 +87,14 @@ namespace Dashboard.Controls
|
|||||||
|
|
||||||
cmd.Splice(drawCommands);
|
cmd.Splice(drawCommands);
|
||||||
|
|
||||||
|
if (this is IEnumerable<Control> children)
|
||||||
|
{
|
||||||
|
foreach (Control child in children)
|
||||||
|
{
|
||||||
|
child.Paint(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd.PopViewport();
|
cmd.PopViewport();
|
||||||
cmd.PopStyle();
|
cmd.PopStyle();
|
||||||
}
|
}
|
||||||
@ -108,18 +131,12 @@ namespace Dashboard.Controls
|
|||||||
LayoutValidated?.Invoke(sender, ea);
|
LayoutValidated?.Invoke(sender, ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ValidateChildrenLayout()
|
public override void OnResized(object sender, ResizedEventArgs ea)
|
||||||
{
|
{
|
||||||
if (this is IEnumerable<Control> enumerable)
|
base.OnResized(sender, ea);
|
||||||
{
|
|
||||||
foreach (Control child in enumerable)
|
LastResizeEvent = ea;
|
||||||
{
|
InvalidateLayout();
|
||||||
if (child.IsLayoutValid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
child.ValidateLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,7 +17,6 @@ namespace Dashboard.Controls
|
|||||||
|
|
||||||
protected override void ValidateLayout()
|
protected override void ValidateLayout()
|
||||||
{
|
{
|
||||||
ValidateChildrenLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ValidateVisual(DrawList cmd)
|
protected override void ValidateVisual(DrawList cmd)
|
||||||
|
31
Dashboard/Controls/GridBox.cs
Normal file
31
Dashboard/Controls/GridBox.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
using Dashboard.ImmediateDraw;
|
||||||
|
using Dashboard.Layout;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
|
namespace Dashboard.Controls
|
||||||
|
{
|
||||||
|
public class GridBox : ContainerControl
|
||||||
|
{
|
||||||
|
protected override void ValidateLayout()
|
||||||
|
{
|
||||||
|
if (LastResizeEvent != null)
|
||||||
|
{
|
||||||
|
foreach (Control child in this)
|
||||||
|
{
|
||||||
|
GridLayoutAttribute attribute = GridLayoutAttribute.GetGridLayout(child);
|
||||||
|
|
||||||
|
attribute.Evaluate(LastResizeEvent, child.Bounds, out Rectangle newBounds);
|
||||||
|
child.Bounds = newBounds;
|
||||||
|
|
||||||
|
child.InvalidateLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ValidateVisual(DrawList cmd)
|
||||||
|
{
|
||||||
|
cmd.Rectangle(new Rectangle(Size, Vector2.Zero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Dashboard.Controls
|
namespace Dashboard.Controls
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bases for all UI elements.
|
/// Bases for all UI elements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -14,7 +15,6 @@ namespace Dashboard.Controls
|
|||||||
private Vector2 size;
|
private Vector2 size;
|
||||||
|
|
||||||
public UIBase? Parent { get; protected set; }
|
public UIBase? Parent { get; protected set; }
|
||||||
public string? Id { get; set; }
|
|
||||||
public Rectangle Bounds
|
public Rectangle Bounds
|
||||||
{
|
{
|
||||||
get => new Rectangle(Position + Size, Position);
|
get => new Rectangle(Position + Size, Position);
|
||||||
@ -25,7 +25,7 @@ namespace Dashboard.Controls
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 Position { get; set; }
|
public virtual Vector2 Position { get; set; }
|
||||||
|
|
||||||
public Vector2 Size
|
public Vector2 Size
|
||||||
{
|
{
|
||||||
@ -85,12 +85,18 @@ namespace Dashboard.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<ResizedEventArgs>? Resized;
|
public event EventHandler<ResizedEventArgs>? Resized;
|
||||||
|
public event EventHandler<AdoptedEventArgs>? Adopted;
|
||||||
|
|
||||||
public virtual void OnResized(object sender, ResizedEventArgs ea)
|
public virtual void OnResized(object sender, ResizedEventArgs ea)
|
||||||
{
|
{
|
||||||
Resized?.Invoke(sender, ea);
|
Resized?.Invoke(sender, ea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void OnAdopted(UIBase? parent)
|
||||||
|
{
|
||||||
|
Adopted?.Invoke(this, new AdoptedEventArgs(parent));
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsDisposed { get; private set; } = false;
|
public bool IsDisposed { get; private set; } = false;
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
@ -113,6 +119,12 @@ namespace Dashboard.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => DisposeInvoker(true);
|
public void Dispose() => DisposeInvoker(true);
|
||||||
|
|
||||||
|
protected static void Adopt(UIBase child, UIBase? parent)
|
||||||
|
{
|
||||||
|
child.Parent = parent;
|
||||||
|
child.OnAdopted(parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResizedEventArgs : EventArgs
|
public class ResizedEventArgs : EventArgs
|
||||||
@ -126,4 +138,14 @@ namespace Dashboard.Controls
|
|||||||
OldSize = oldSize;
|
OldSize = oldSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AdoptedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public UIBase? Parent { get; }
|
||||||
|
|
||||||
|
public AdoptedEventArgs(UIBase? parent = null)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
116
Dashboard/Layout/GridLayoutAttribute.cs
Normal file
116
Dashboard/Layout/GridLayoutAttribute.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using OpenTK.Mathematics;
|
||||||
|
using Dashboard.Controls;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
|
namespace Dashboard.Layout
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Control attributes for grid layout.
|
||||||
|
/// </summary>
|
||||||
|
public class GridLayoutAttribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An anchor will keep the relative distance of a control's edges the same during resizes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><see cref="Dock"/> has higher precedence.</remarks>
|
||||||
|
public Anchor Anchor { get; set; } = Anchor.Left | Anchor.Top;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dock will strongly attach a control to its container or its edges.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Has more precedence than <see cref="Anchor"/></remarks>
|
||||||
|
public Dock Dock { get; set; } = Dock.None;
|
||||||
|
|
||||||
|
public void Evaluate(
|
||||||
|
ResizedEventArgs parentResizeEvent,
|
||||||
|
in Rectangle oldBounds,
|
||||||
|
out Rectangle newBounds
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (Dock)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Dock.None:
|
||||||
|
break;
|
||||||
|
case Dock.Top:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
oldBounds.Size.Y,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Bottom:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
0,
|
||||||
|
oldBounds.Size.Y
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Left:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
oldBounds.Size.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
case Dock.Right:
|
||||||
|
newBounds = new Rectangle(
|
||||||
|
parentResizeEvent.NewSize.X,
|
||||||
|
parentResizeEvent.NewSize.Y,
|
||||||
|
parentResizeEvent.NewSize.Y - oldBounds.Size.Y,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 scale = parentResizeEvent.NewSize / parentResizeEvent.OldSize;
|
||||||
|
|
||||||
|
newBounds = oldBounds;
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Top))
|
||||||
|
{
|
||||||
|
newBounds.Top = scale.Y * oldBounds.Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Left))
|
||||||
|
{
|
||||||
|
newBounds.Left = scale.X * oldBounds.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Bottom))
|
||||||
|
{
|
||||||
|
float margin = scale.Y * (parentResizeEvent.OldSize.Y - newBounds.Bottom);
|
||||||
|
newBounds.Bottom = parentResizeEvent.NewSize.Y - margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Anchor.HasFlag(Anchor.Right))
|
||||||
|
{
|
||||||
|
float margin = scale.X * (parentResizeEvent.OldSize.X - newBounds.Right);
|
||||||
|
newBounds.Right = parentResizeEvent.NewSize.X - margin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the grid layout attribute associated with the given UI-base.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uiBase">The UI-base to query.</param>
|
||||||
|
/// <returns>It's grid layout attribute, if any.</returns>
|
||||||
|
public static GridLayoutAttribute GetGridLayout(UIBase uiBase)
|
||||||
|
{
|
||||||
|
const string GRID_LAYOUT_ATTRIBUTE_KEY = "486ddf8c-b75f-4ad4-a51d-5ba20db9bd0e";
|
||||||
|
|
||||||
|
if (
|
||||||
|
!uiBase.Attributes.TryGetValue(GRID_LAYOUT_ATTRIBUTE_KEY, out object? attribute)
|
||||||
|
|| attribute is not GridLayoutAttribute)
|
||||||
|
{
|
||||||
|
attribute = new GridLayoutAttribute();
|
||||||
|
uiBase.Attributes[GRID_LAYOUT_ATTRIBUTE_KEY] = attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (GridLayoutAttribute)attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user