Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 84 additions & 1 deletion src/Myra/Graphics2D/UI/Desktop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using FontStashSharp.RichText;
using Myra.Graphics2D.UI.Styles;
using Myra.Utility;
using Myra.Events;
Expand Down Expand Up @@ -541,7 +543,11 @@ public void RenderVisual()
widget.Render(_renderContext);
}
}


#if DEBUG
RenderDebugInfo(_renderContext);
#endif

_renderContext.End();

_renderContext.DeviceScissor = oldDeviceScissor;
Expand Down Expand Up @@ -585,6 +591,83 @@ public void Render()
// Render run
RenderVisual();
}

/// <summary>
/// Draws debug information on the screen.
/// This function can be called after the normal render procedures conclude
/// and before the context is closed.
///
/// This allows us to render on top of everything else.
/// </summary>
/// <param name="context">The current render context</param>
private void RenderDebugInfo(RenderContext context)
{
if (!MyraEnvironment.DrawMouseHoveredWidgetInfo ||
MyraEnvironment.DefaultDebugFont == null ||
MousePosition == null
)
return;

// Look for the deepest child being hit. That'd be the actual widget we're hovering over as opposed to one of its parents
Widget widget = null;
var children = ChildrenCopy;
for (var i = children.Count - 1; i >= 0; --i)
{
var w = children[i].HitTest(MousePosition);
if (w != null)
{
widget = w;
break;
}
}

// Nothing under the cursor, nothing to render
if (widget == null)
return;

// Get the widget's current transformed rectangle; That should effectively be the actual on-screen position and size
// in the context of the monitor being used (i.e., ignores other monitors)
Rectangle transformedPos = widget.Transform.Apply(widget.Bounds);
var text = new RichTextLayout
{
Text = $"""
{GetDebugTypeName(widget.GetType())}
eH: {widget.Height ?? transformedPos.Height}
eW: {widget.Width ?? transformedPos.Width}
eX: {transformedPos.X}
eY: {transformedPos.Y}
""",
Font = MyraEnvironment.DefaultDebugFont
};

context.DrawRichText(
text,
new Vector2(MousePosition.X - 60, MousePosition.Y - 30),
Color.Yellow
);
}

/// <summary>
/// Gets a type's 'clean' name, replacing generic type arguments with their actual types
/// </summary>
/// <param name="type">The type to get the name of</param>
/// <returns>The type's clean display name</returns>
private static string GetDebugTypeName(Type type)
{
if (!type.IsGenericType)
{
return type.Name;
}

var name = type.Name;
var tickIndex = name.IndexOf('`');
if (tickIndex >= 0)
{
name = name.Substring(0, tickIndex);
}

return $"{name}<{string.Join(", ", type.GetGenericArguments().Select(GetDebugTypeName))}>";
}

private void InvalidateTransform()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Myra/Graphics2D/UI/Misc/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Myra.Graphics2D.UI
public class Window : ContentControl
{
private readonly StackPanelLayout _layout = new StackPanelLayout(Orientation.Vertical);
private readonly Label _titleLabel;
protected readonly Label _titleLabel;
private Widget _content;
private Widget _previousKeyboardFocus;

Expand Down
166 changes: 166 additions & 0 deletions src/Myra/Graphics2D/UI/WrapPanel/WrapPanel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Myra.Graphics2D.UI;
using Container = Myra.Graphics2D.UI.Container;

namespace Myra.Graphics2D.UI.WrapPanel;

/// <summary>
/// A container that arranges its child widgets in a sequence that wraps at the edge of the container.
/// </summary>
public class WrapPanel : Container
{
private readonly WrapPanelLayout _layout = new();

/// <summary>
/// Gets or sets the orientation of the layout (Horizontal or Vertical).
/// </summary>
[Category("Layout")]
[DefaultValue(Orientation.Horizontal)]
public Orientation Orientation
{
get => _layout.Orientation;
set
{
if (value == _layout.Orientation)
return;

_layout.Orientation = value;
InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets the horizontal spacing between child widgets.
/// </summary>
[Category("Layout")]
[DefaultValue(0)]
public int HorizontalSpacing
{
get => _layout.HorizontalSpacing;
set
{
if (value == _layout.HorizontalSpacing)
return;

_layout.HorizontalSpacing = value;
InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets the vertical spacing between child widgets.
/// </summary>
[Category("Layout")]
[DefaultValue(0)]
public int VerticalSpacing
{
get => _layout.VerticalSpacing;
set
{
if (value == _layout.VerticalSpacing)
return;

_layout.VerticalSpacing = value;
InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets a value indicating whether child widgets in a row/column should be aligned to the row height/column width.
/// </summary>
[Category("Layout")]
[DefaultValue(true)]
public bool Aligned
{
get => _layout.Aligned;
set
{
if (value == _layout.Aligned)
return;

_layout.Aligned = value;
InvalidateArrange();
}
}

/// <summary>
/// Gets or sets a value indicating whether all child widgets should have the same size, based on the largest child.
/// </summary>
[Category("Layout")]
[DefaultValue(true)]
public bool UniformSizing
{
get => _layout.UniformSizing;
set
{
if (value == _layout.UniformSizing)
return;

_layout.UniformSizing = value;
InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets the preferred width of the wrap panel.
/// </summary>
[Category("Layout")]
[DefaultValue(null)]
public int? PreferredWidth
{
get => _layout.PreferredWidth;
set
{
if (value == _layout.PreferredWidth)
return;

_layout.PreferredWidth = value;
InvalidateMeasure();
}
}

/// <summary>
/// Gets or sets the preferred height of the wrap panel.
/// </summary>
[Category("Layout")]
[DefaultValue(null)]
public int? PreferredHeight
{
get => _layout.PreferredHeight;
set
{
if (value == _layout.PreferredHeight)
return;

_layout.PreferredHeight = value;
InvalidateMeasure();
}
}

/// <summary>
/// Initializes a new instance of the <see cref="WrapPanel"/> class.
/// </summary>
public WrapPanel()
{
ChildrenLayout = _layout;
}

/// <summary>
/// Copies the properties from another widget into this one.
/// </summary>
/// <param name="w">The widget to copy from.</param>
protected internal override void CopyFrom(Widget w)
{
base.CopyFrom(w);

var wrapPanel = (WrapPanel)w;
Orientation = wrapPanel.Orientation;
HorizontalSpacing = wrapPanel.HorizontalSpacing;
VerticalSpacing = wrapPanel.VerticalSpacing;
Aligned = wrapPanel.Aligned;
UniformSizing = wrapPanel.UniformSizing;
PreferredWidth = wrapPanel.PreferredWidth;
PreferredHeight = wrapPanel.PreferredHeight;
}
}
Loading