From e30e50e8607d6f9644a790f7623ab84fbe64aacf Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Mon, 5 Jan 2026 22:26:34 +0300 Subject: [PATCH] Unfinished work I want to document in the Git history. --- .../Drawing/Brush.cs | 0 .../Drawing/IDeviceContextBase.cs | 3 + Dashboard.Common/Drawing/IImmediateMode.cs | 4 + Dashboard.Common/Layout/ILayoutItem.cs | 18 + Dashboard.Common/Layout/LayoutBox.cs | 435 ++++++++++++++++++ Dashboard.Common/Layout/LayoutInfo.cs | 171 +++++++ Dashboard.Common/Layout/LayoutSolution.cs | 364 +++++++++++++++ Dashboard.Common/LayoutUnit.cs | 30 ++ Dashboard.Common/Measure.cs | 138 ++++-- Dashboard.Common/MeasurementExtensions.cs | 30 ++ Dashboard.OpenGL/Drawing/DeviceContextBase.cs | 11 + Dashboard.OpenGL/Drawing/ImmediateMode.cs | 15 + Dashboard/Controls/Button.cs | 10 +- Dashboard/Controls/Container.cs | 34 +- Dashboard/Controls/Control.cs | 79 ++-- Dashboard/Controls/Form.cs | 1 + Dashboard/Controls/ImageBox.cs | 4 +- Dashboard/Controls/Label.cs | 12 +- Dashboard/Controls/MessageBox.cs | 50 +- tests/Dashboard.TestApplication/Program.cs | 13 +- 20 files changed, 1285 insertions(+), 137 deletions(-) rename {Dashboard => Dashboard.Common}/Drawing/Brush.cs (100%) create mode 100644 Dashboard.Common/Layout/ILayoutItem.cs create mode 100644 Dashboard.Common/Layout/LayoutBox.cs create mode 100644 Dashboard.Common/Layout/LayoutInfo.cs create mode 100644 Dashboard.Common/Layout/LayoutSolution.cs create mode 100644 Dashboard.Common/LayoutUnit.cs create mode 100644 Dashboard.Common/MeasurementExtensions.cs diff --git a/Dashboard/Drawing/Brush.cs b/Dashboard.Common/Drawing/Brush.cs similarity index 100% rename from Dashboard/Drawing/Brush.cs rename to Dashboard.Common/Drawing/Brush.cs diff --git a/Dashboard.Common/Drawing/IDeviceContextBase.cs b/Dashboard.Common/Drawing/IDeviceContextBase.cs index 6d98d24..eaa6b0c 100644 --- a/Dashboard.Common/Drawing/IDeviceContextBase.cs +++ b/Dashboard.Common/Drawing/IDeviceContextBase.cs @@ -26,5 +26,8 @@ namespace Dashboard.Drawing void ClearColor(Color color); void ClearDepth(); + + int IncrementZ(); + int DecrementZ(); } } diff --git a/Dashboard.Common/Drawing/IImmediateMode.cs b/Dashboard.Common/Drawing/IImmediateMode.cs index 31a3ad5..9e3089a 100644 --- a/Dashboard.Common/Drawing/IImmediateMode.cs +++ b/Dashboard.Common/Drawing/IImmediateMode.cs @@ -1,13 +1,17 @@ using System.Drawing; using System.Numerics; +using Dashboard.Layout; using Dashboard.Pal; namespace Dashboard.Drawing { + public record struct RectangleDrawInfo(Vector2 Position, ComputedBox Box, Brush Fill, Brush? Border = null); + public interface IImmediateMode : IDeviceContextExtension { void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color); void Rectangle(Box2d rectangle, float depth, Vector4 color); + void Rectangle(in RectangleDrawInfo rectangle); void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture); } } diff --git a/Dashboard.Common/Layout/ILayoutItem.cs b/Dashboard.Common/Layout/ILayoutItem.cs new file mode 100644 index 0000000..253fa7c --- /dev/null +++ b/Dashboard.Common/Layout/ILayoutItem.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using System.Numerics; + +namespace Dashboard.Layout +{ + public interface ILayoutItem : INotifyPropertyChanged + { + public LayoutInfo Layout { get; } + + public Vector2 CalculateIntrinsicSize(); + public Vector2 CalculateSize(Vector2 limits); + } + + public interface ILayoutContainer : ILayoutItem, IEnumerable + { + public ContainerLayoutInfo ContainerLayout { get; } + } +} diff --git a/Dashboard.Common/Layout/LayoutBox.cs b/Dashboard.Common/Layout/LayoutBox.cs new file mode 100644 index 0000000..2a5699b --- /dev/null +++ b/Dashboard.Common/Layout/LayoutBox.cs @@ -0,0 +1,435 @@ +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Dashboard.Layout +{ + public readonly record struct ComputedBox(Vector4 Margin, Vector4 Padding, Vector4 Border, Vector2 Size) + { + public float MarginLeft => Margin.X; + public float MarginTop => Margin.Y; + public float MarginRight => Margin.Z; + public float MarginBottom => Margin.W; + + public float PaddingLeft => Padding.X; + public float PaddingTop => Padding.Y; + public float PaddingRight => Padding.Z; + public float PaddingBottom => Padding.W; + + public float BorderLeft => Border.X; + public float BorderTop => Border.Y; + public float BorderRight => Border.Z; + public float BorderBottom => Border.W; + + public float Width => Size.X; + public float Height => Size.Y; + + public Vector2 BoundingSize => new Vector2( + MarginLeft + BorderLeft + Width + BorderRight + MarginRight, + MarginTop + BorderTop + Height + BorderBottom + MarginBottom); + + public Vector2 ContentSize => new Vector2( + Width - PaddingLeft - PaddingRight, + Height - PaddingTop - PaddingBottom); + + public Vector4 ContentExtents => new Vector4( + PaddingLeft, + PaddingTop, + PaddingLeft + PaddingRight + Width, + PaddingTop + PaddingBottom + Height); + + public Vector4 CornerRadii { get; init; } = Vector4.Zero; + } + + public class LayoutBox : INotifyPropertyChanged + { + private Vector4 _margin = Vector4.Zero; + private Vector4 _padding = Vector4.Zero; + private Vector4 _border = Vector4.Zero; + private Vector2 _size = Vector2.Zero; + private Vector2 _minimumSize = -Vector2.One; + private Vector2 _maximumSize = -Vector2.One; + private Vector4 _cornerRadii = Vector4.Zero; + + private LayoutUnit _marginLeftUnit = LayoutUnit.Pixel; + private LayoutUnit _marginTopUnit = LayoutUnit.Pixel; + private LayoutUnit _marginRightUnit = LayoutUnit.Pixel; + private LayoutUnit _marginBottomUnit = LayoutUnit.Pixel; + + private LayoutUnit _paddingLeftUnit = LayoutUnit.Pixel; + private LayoutUnit _paddingTopUnit = LayoutUnit.Pixel; + private LayoutUnit _paddingRightUnit = LayoutUnit.Pixel; + private LayoutUnit _paddingBottomUnit = LayoutUnit.Pixel; + + private LayoutUnit _borderLeftUnit = LayoutUnit.Pixel; + private LayoutUnit _borderTopUnit = LayoutUnit.Pixel; + private LayoutUnit _borderRightUnit = LayoutUnit.Pixel; + private LayoutUnit _borderBottomUnit = LayoutUnit.Pixel; + + private LayoutUnit _widthUnit = LayoutUnit.Pixel; + private LayoutUnit _heightUnit = LayoutUnit.Pixel; + + private LayoutUnit _minimumWidthUnit = LayoutUnit.Pixel; + private LayoutUnit _minimumHeightUnit = LayoutUnit.Pixel; + + private LayoutUnit _maximumWidthUnit = LayoutUnit.Pixel; + private LayoutUnit _maximumHeightUnit = LayoutUnit.Pixel; + + private LayoutUnit _cornerRadiusTopLeftUnit = LayoutUnit.Pixel; + private LayoutUnit _cornerRadiusBottomLeftUnit = LayoutUnit.Pixel; + private LayoutUnit _cornerRadiusBottomRightUnit = LayoutUnit.Pixel; + private LayoutUnit _cornerRadiusTopRightUnit = LayoutUnit.Pixel; + private ComputedBox _computedBox = new ComputedBox(); + + public bool UpdateRequired { get; private set; } = true; + + public Vector4 Margin + { + get => _margin; + set => SetField(ref _margin, value); + } + + public Vector4 Padding + { + get => _padding; + set => SetField(ref _padding, value); + } + + public Vector4 Border + { + get => _border; + set => SetField(ref _border, value); + } + + public Vector2 Size + { + get => _size; + set => SetField(ref _size, value); + } + + public Vector2 MinimumSize + { + get => _minimumSize; + set => SetField(ref _minimumSize, value); + } + + public Vector2 MaximumSize + { + get => _maximumSize; + set => SetField(ref _maximumSize, value); + } + + public Vector4 CornerRadii + { + get => _cornerRadii; + set => SetField(ref _cornerRadii, value); + } + + public float MarginLeft + { + get => Margin.X; + set => Margin = Margin with { X = value }; + } + + public float MarginTop + { + get => Margin.Y; + set => Margin = Margin with { Y = value }; + } + + public float MarginRight + { + get => Margin.Z; + set => Margin = Margin with { Z = value }; + } + + public float MarginBottom + { + get => Margin.W; + set => Margin = Margin with { W = value }; + } + + public float PaddingLeft + { + get => Padding.X; + set => Padding = Padding with { X = value }; + } + + public float PaddingTop + { + get => Padding.Y; + set => Padding = Padding with { Y = value }; + } + + public float PaddingRight + { + get => Padding.Z; + set => Padding = Padding with { Z = value }; + } + + public float PaddingBottom + { + get => Padding.W; + set => Padding = Padding with { W = value }; + } + + public float BorderLeft + { + get => Border.X; + set => Border = Border with { X = value }; + } + + public float BorderTop + { + get => Border.Y; + set => Border = Border with { Y = value }; + } + + public float BorderRight + { + get => Border.Z; + set => Border = Border with { Z = value }; + } + + public float BorderBottom + { + get => Border.W; + set => Border = Border with { W = value }; + } + + public float CornerRadiusTopLeft + { + get => CornerRadii.X; + set => CornerRadii = CornerRadii with { X = value }; + } + + public float CornerRadiusBottomLeft + { + get => CornerRadii.Y; + set => CornerRadii = CornerRadii with { Y = value }; + } + + public float CornerRadiusBottomRight + { + get => CornerRadii.Z; + set => CornerRadii = CornerRadii with { Z = value }; + } + + public float CornerRadiusTopRight + { + get => CornerRadii.W; + set => CornerRadii = CornerRadii with { W = value }; + } + + public LayoutUnit MarginLeftUnit + { + get => _marginLeftUnit; + set => SetField(ref _marginLeftUnit, value); + } + + public LayoutUnit MarginTopUnit + { + get => _marginTopUnit; + set => SetField(ref _marginTopUnit, value); + } + + public LayoutUnit MarginRightUnit + { + get => _marginRightUnit; + set => SetField(ref _marginRightUnit, value); + } + + public LayoutUnit MarginBottomUnit + { + get => _marginBottomUnit; + set => SetField(ref _marginBottomUnit, value); + } + + public LayoutUnit PaddingLeftUnit + { + get => _paddingLeftUnit; + set => SetField(ref _paddingLeftUnit, value); + } + + public LayoutUnit PaddingTopUnit + { + get => _paddingTopUnit; + set => SetField(ref _paddingTopUnit, value); + } + + public LayoutUnit PaddingRightUnit + { + get => _paddingRightUnit; + set => SetField(ref _paddingRightUnit, value); + } + + public LayoutUnit PaddingBottomUnit + { + get => _paddingBottomUnit; + set => SetField(ref _paddingBottomUnit, value); + } + + public LayoutUnit BorderLeftUnit + { + get => _borderLeftUnit; + set => SetField(ref _borderLeftUnit, value); + } + + public LayoutUnit BorderTopUnit + { + get => _borderTopUnit; + set => SetField(ref _borderTopUnit, value); + } + + public LayoutUnit BorderRightUnit + { + get => _borderRightUnit; + set => SetField(ref _borderRightUnit, value); + } + + public LayoutUnit BorderBottomUnit + { + get => _borderBottomUnit; + set => SetField(ref _borderBottomUnit, value); + } + + public LayoutUnit WidthUnit + { + get => _widthUnit; + set => SetField(ref _widthUnit, value); + } + + public LayoutUnit HeightUnit + { + get => _heightUnit; + set => SetField(ref _heightUnit, value); + } + + public LayoutUnit MinimumWidthUnit + { + get => _minimumWidthUnit; + set => SetField(ref _minimumWidthUnit, value); + } + + public LayoutUnit MinimumHeightUnit + { + get => _minimumHeightUnit; + set => SetField(ref _minimumHeightUnit, value); + } + + public LayoutUnit MaximumWidthUnit + { + get => _maximumWidthUnit; + set => SetField(ref _maximumWidthUnit, value); + } + + public LayoutUnit MaximumHeightUnit + { + get => _maximumHeightUnit; + set => SetField(ref _maximumHeightUnit, value); + } + + public LayoutUnit CornerRadiusTopLeftUnit + { + get => _cornerRadiusTopLeftUnit; + set => SetField(ref _cornerRadiusTopLeftUnit, value); + } + + public LayoutUnit CornerRadiusBottomLeftUnit + { + get => _cornerRadiusBottomLeftUnit; + set => SetField(ref _cornerRadiusBottomLeftUnit, value); + } + + public LayoutUnit CornerRadiusBottomRightUnit + { + get => _cornerRadiusBottomRightUnit; + set => SetField(ref _cornerRadiusBottomRightUnit, value); + } + + public LayoutUnit CornerRadiusTopRightUnit + { + get => _cornerRadiusTopRightUnit; + set => SetField(ref _cornerRadiusTopRightUnit, value); + } + + public ComputedBox ComputedBox + { + get => _computedBox; + private set => SetField(ref _computedBox, value, false); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + public ComputedBox ComputeLayout(Vector2 intrinsic, Vector2 dpi, Vector2 area, Vector2 star) + { + // TODO: take intrinsic into account. + Vector4 margin = Compute(_margin, dpi, area, star, _marginLeftUnit, _marginTopUnit, _marginRightUnit, _marginBottomUnit); + Vector4 padding = Compute(_padding, dpi, area, star, _paddingLeftUnit, _paddingTopUnit, _paddingRightUnit, _paddingBottomUnit); + Vector4 border = Compute(_border, dpi, area, star, _borderLeftUnit, _borderTopUnit, _borderRightUnit, _borderBottomUnit); + + Vector2 size = Compute(_size, dpi, area, star, _widthUnit, _heightUnit); + Vector2 minimumSize = Compute(_minimumSize, dpi, area, star, _minimumWidthUnit, _minimumHeightUnit); + Vector2 maximumSize = Compute(_maximumSize, dpi, area, star, _maximumWidthUnit, _maximumHeightUnit); + Vector4 cornerRadii = Compute(_cornerRadii, dpi, area, star, _cornerRadiusTopLeftUnit, + _cornerRadiusBottomLeftUnit, _cornerRadiusBottomRightUnit, _cornerRadiusTopRightUnit); + + size = Vector2.Clamp(size, minimumSize, maximumSize); + + ComputedBox = new ComputedBox(margin, padding, border, size) + { + CornerRadii = cornerRadii, + }; + + UpdateRequired = false; + return ComputedBox; + } + + private static float Compute(float value, float dpi, float length, float star, LayoutUnit unit) + { + const float dpiToMm = 0f; + const float dpiToPt = 0f; + + return unit switch + { + LayoutUnit.Pixel => value, + LayoutUnit.Millimeter => value * dpi * dpiToMm, + LayoutUnit.Percent => value * length, + LayoutUnit.Point => value * dpi * dpiToPt, + LayoutUnit.Star => value * star, + _ => throw new ArgumentException(nameof(unit)), + }; + } + + private static Vector2 Compute(Vector2 value, Vector2 dpi, Vector2 size, Vector2 star, LayoutUnit xUnit, LayoutUnit yUnit) + { + return new Vector2( + Compute(value.X, dpi.X, size.X, star.X, xUnit), + Compute(value.Y, dpi.Y, size.Y, star.Y, yUnit)); + } + + private static Vector4 Compute(Vector4 value, Vector2 dpi, Vector2 size, Vector2 star, LayoutUnit xUnit, LayoutUnit yUnit, LayoutUnit zUnit, LayoutUnit wUnit) + { + return new Vector4( + Compute(value.X, dpi.X, size.X, star.X, xUnit), + Compute(value.Y, dpi.Y, size.Y, star.Y, yUnit), + Compute(value.Z, dpi.X, size.X, star.X, zUnit), + Compute(value.W, dpi.Y, size.Y, star.Y, wUnit)); + } + + + private void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private bool SetField(ref T field, T value, bool updateRequired = true, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + UpdateRequired |= updateRequired; + OnPropertyChanged(propertyName); + return true; + } + } +} diff --git a/Dashboard.Common/Layout/LayoutInfo.cs b/Dashboard.Common/Layout/LayoutInfo.cs new file mode 100644 index 0000000..8b03efc --- /dev/null +++ b/Dashboard.Common/Layout/LayoutInfo.cs @@ -0,0 +1,171 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Dashboard.Layout +{ + public enum DisplayMode + { + None, + Inline, + Block, + } + + public enum ContainerMode + { + Basic, + Flex, + Grid, + } + + public enum FlowDirection + { + Row, + Column, + RowReverse, + ColumnReverse, + } + + public enum PositionMode + { + Absolute, + Relative, + } + + public enum OverflowMode + { + Hidden, + Overflow, + ScrollHorizontal, + ScrollVertical, + ScrollBoth, + } + + public record struct TrackInfo(float Width, LayoutUnit Unit) + { + public static readonly TrackInfo Default = new TrackInfo(0, LayoutUnit.Auto); + } + + public class ContainerLayoutInfo : INotifyPropertyChanged + { + private ContainerMode _containerMode; + private FlowDirection _flowDirection = FlowDirection.Row; + + public ObservableCollection Rows { get; } = new ObservableCollection() { TrackInfo.Default }; + + public ObservableCollection Columns { get; } = + new ObservableCollection() { TrackInfo.Default }; + + public ContainerMode ContainerMode + { + get => _containerMode; + set => SetField(ref _containerMode, value); + } + + public FlowDirection FlowDirection + { + get => _flowDirection; + set => SetField(ref _flowDirection, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + } + + public class LayoutInfo : INotifyPropertyChanged + { + private DisplayMode _displayMode = DisplayMode.Inline; + private PositionMode _positionMode = PositionMode.Relative; + private OverflowMode _overflowMode = OverflowMode.Overflow; + private int _row = 0; + private int _column = 0; + + /// + /// Changes the control display. + /// + public DisplayMode DisplayMode + { + get => _displayMode; + set => SetField(ref _displayMode, value); + } + + /// + /// Changes how the control is positioned. + /// + public PositionMode PositionMode + { + get => _positionMode; + set => SetField(ref _positionMode, value); + } + + /// + /// Changes how overflows are handled. + /// + public OverflowMode OverflowMode + { + get => _overflowMode; + set => SetField(ref _overflowMode, value); + } + + public LayoutBox Box { get; } = new LayoutBox(); + + /// + /// The row of the control in a grid container. + /// + public int Row + { + get => _row; + set => SetField(ref _row, value); + } + + /// + /// The column of the control in a grid container. + /// + public int Column + { + get => _column; + set => SetField(ref _column, value); + } + + public event PropertyChangedEventHandler? PropertyChanged; + + public LayoutInfo() + { + Box.PropertyChanged += BoxOnPropertyChanged; + // Rows.CollectionChanged += RowsChanged; + // Columns.CollectionChanged += ColumnsChanged; + } + + private void BoxOnPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + OnPropertyChanged(nameof(Box)); + } + + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + } +} diff --git a/Dashboard.Common/Layout/LayoutSolution.cs b/Dashboard.Common/Layout/LayoutSolution.cs new file mode 100644 index 0000000..19fe327 --- /dev/null +++ b/Dashboard.Common/Layout/LayoutSolution.cs @@ -0,0 +1,364 @@ +using System.Numerics; + +namespace Dashboard.Layout +{ + public record struct LayoutItemSolution(ILayoutItem Item, ComputedBox Solution); + + public class LayoutSolution + { + public ILayoutContainer Container { get; } + public IReadOnlyList Items { get; } + + private LayoutSolution(ILayoutContainer container, IEnumerable itemSolutions) + { + Container = container; + Items = itemSolutions.ToList().AsReadOnly(); + } + + public static LayoutSolution CalculateLayout(T1 container, Vector2 limits, int iterations = 3, float absTol = 0.001f, float relTol = 0.01f) + where T1 : ILayoutContainer + { + switch (container.ContainerLayout.ContainerMode) + { + default: + case ContainerMode.Basic: + return SolveForBasicLayout(container, limits, iterations, absTol, relTol); + case ContainerMode.Flex: + return SolveForFlex(container, limits, iterations, absTol, relTol); + case ContainerMode.Grid: + return SolveForGrid(container, limits, iterations, absTol, relTol); + } + } + + private static LayoutSolution SolveForGrid(T1 container, Vector2 limits, int iterations, float absTol, float relTol) where T1 : ILayoutContainer + { + throw new NotImplementedException(); + } + + private static LayoutSolution SolveForFlex(T1 container, Vector2 limits,int iterations, float absTol, float relTol) where T1 : ILayoutContainer + { + throw new NotImplementedException(); + } + + private static LayoutSolution SolveForBasicLayout(T1 container, Vector2 limits, int iterations, float absTol, float relTol) where T1 : ILayoutContainer + { + int count = container.Count(); + LayoutItemSolution[] items = new LayoutItemSolution[count]; + + int i = 0; + foreach (ILayoutItem item in container) + { + items[i++] = new LayoutItemSolution(item, default); + } + + bool limitX = limits.X > 0; + bool limitY = limits.Y > 0; + + while (iterations-- > 0) + { + Vector2 pen = Vector2.Zero; + + i = 0; + foreach (ILayoutItem item in container) + { + Vector2 size = item.CalculateIntrinsicSize(); + + } + } + + return new LayoutSolution(container, items); + } + + private record TrackSolution(TrackInfo Track) + { + public bool Auto => Track.Unit == LayoutUnit.Auto; + + public bool Absolute => Track.Unit.IsAbsolute(); + + public float Requested { get; private set; } + public float Value { get; private set; } + + public float Result { get; set; } = 0.0f; + public bool IsFrozen { get; set; } = false; + + public void CalculateRequested(float dpi, float rel, float star) + { + Requested = new Metric(Track.Unit, Track.Width).Compute(dpi, rel, star); + } + + public void Freeze() + { + if (IsFrozen) + return; + + IsFrozen = true; + Result = Value; + } + } + + private delegate float GetItemLength(float dpi, float rel, float star, T1 item) + where T1 : ILayoutItem; + + private TrackSolution[] SolveForGridTracks( + float limit, + T1 tracks, + T2 container, + int iterations, + float absTol, + float relTol, + Func getItemTrack, + GetItemLength getItemLength) + where T1 : IList + where T2 : ILayoutContainer + where T3 : ILayoutItem + { + int itemCount = container.Count(); + bool auto = limit < 0; + TrackSolution[] solution = new TrackSolution[tracks.Count]; + + foreach (TrackSolution track in solution) + { + if (track.Absolute) { + // FIXME: pass DPI here. + track.CalculateRequested(96f, limit, 0); + track.Freeze(); + } + } + + while (iterations-- > 0) + { + } + + for (int i = 0; i < tracks.Count; i++) + { + solution[i].Freeze(); + } + + return solution; + } + + // private static void GetIntrinsicGridSizes(Span cols, Span rows, T1 parent, T2 items) + // where T1 : ILayoutItem + // where T2 : IEnumerable + // { + // CopyToSpan(rows, parent.Layout.Rows); + // CopyToSpan(cols, parent.Layout.Columns); + // + // foreach (ILayoutItem item in items) + // { + // int col = Math.Clamp(item.Layout.Column, 0, cols.Length - 1); + // int row = Math.Clamp(item.Layout.Row, 0, rows.Length - 1); + // + // bool autoCols = parent.Layout.Columns[col] < 0; + // bool autoRows = parent.Layout.Rows[row] < 0; + // + // if (!autoRows && !autoCols) + // continue; + // + // Vector2 size = item.CalculateIntrinsicSize(); + // cols[col] = autoCols ? Math.Max(size.X, cols[col]) : cols[col]; + // rows[row] = autoRows ? Math.Max(size.Y, rows[row]) : rows[row]; + // } + // } + // + // public static Vector2 CalculateIntrinsicSize(T1 parent, T2 items) + // where T1 : ILayoutItem + // where T2 : IEnumerable + // { + // // Copy layout details. + // Span cols = stackalloc float[parent.Layout.Columns.Count]; + // Span rows = stackalloc float[parent.Layout.Rows.Count]; + // + // GetIntrinsicGridSizes(cols, rows, parent, items); + // + // float width = parent.Layout.Margin.X + parent.Layout.Margin.Z + parent.Layout.Padding.X + parent.Layout.Padding.Z + SumSpan(cols); + // float height = parent.Layout.Margin.Y + parent.Layout.Margin.W + parent.Layout.Padding.Y + parent.Layout.Padding.W + SumSpan(rows); + // + // return new Vector2(width, height); + // } + // + // public static GridResult Layout(T1 parent, T2 items, Vector2 limits, int iterations = 3, float abstol = 0.0001f, float reltol = 0.01f) + // where T1 : ILayoutItem + // where T2 : IEnumerable + // { + // Vector4 contentSpace = parent.Layout.Margin + parent.Layout.Padding; + // Vector2 contentLimits = new Vector2( + // limits.X > 0 ? limits.X - contentSpace.X - contentSpace.Z : -1, + // limits.Y > 0 ? limits.Y - contentSpace.Y - contentSpace.W : -1); + // + // // Get rows and columns for now. + // Span cols = stackalloc Track[parent.Layout.Columns.Count]; + // Span rows = stackalloc Track[parent.Layout.Rows.Count]; + // + // for (int i = 0; i < cols.Length; i++) + // { + // cols[i] = new Track(parent.Layout.Columns[i]); + // + // if (!cols[i].Auto) + // { + // cols[i].Freeze(); + // } + // } + // + // for (int i = 0; i < rows.Length; i++) + // { + // rows[i] = new Track(parent.Layout.Rows[i]); + // + // if (!rows[i].Auto) + // { + // rows[i].Freeze(); + // } + // } + // + // int freeRows = 0; + // int freeCols = 0; + // while (iterations-- > 0 && ((freeRows = CountFree(rows)) > 0 || (freeCols = CountFree(cols)) > 0)) + // { + // // Calculate the remaining size. + // Vector2 remaining = contentLimits; + // + // for (int i = 0; contentLimits.X > 0 && i < cols.Length; i++) + // { + // if (cols[i].IsFrozen) + // remaining.X -= cols[i].Value; + // } + // + // for (int i = 0; contentLimits.Y > 0 && i < rows.Length; i++) + // { + // if (rows[i].IsFrozen) + // remaining.Y -= rows[i].Value; + // } + // + // Vector2 childLimits = remaining / new Vector2(Math.Max(freeCols, 1), Math.Max(freeRows, 1)); + // + // + // // Calculate the size of each free track. + // foreach (ILayoutItem child in items) + // { + // int c = Math.Clamp(child.Layout.Column, 0, cols.Length - 1); + // int r = Math.Clamp(child.Layout.Row, 0, rows.Length - 1); + // + // bool autoRow = rows[r].Auto; + // bool autoCol = cols[c].Auto; + // + // if (!autoRow && !autoCol) + // continue; + // + // Vector2 childSize = child.CalculateSize(childLimits); + // + // if (autoCol) + // cols[c].Value = Math.Max(childLimits.X, childSize.X); + // + // if (autoRow) + // rows[r].Value = Math.Max(childLimits.Y, childSize.Y); + // } + // + // // Calculate for errors and decide to freeze them. + // + // for (int i = 0; limits.X > 0 && i < cols.Length; i++) + // { + // if (WithinTolerance(cols[i].Value, childLimits.X, abstol, reltol)) + // { + // cols[i].Freeze(); + // } + // } + // + // for (int i = 0; limits.Y > 0 && i < rows.Length; i++) + // { + // if (WithinTolerance(rows[i].Value, childLimits.Y, abstol, reltol)) + // { + // rows[i].Freeze(); + // } + // } + // } + // + // Vector2 size = new Vector2( + // parent.Layout.Margin.X + parent.Layout.Margin.Z + parent.Layout.Padding.X + parent.Layout.Padding.Z, + // parent.Layout.Margin.Y + parent.Layout.Margin.W + parent.Layout.Padding.Y + parent.Layout.Padding.W); + // + // foreach (ref Track col in cols) + // { + // col.Freeze(); + // size.X += col.Result; + // } + // + // foreach (ref Track row in rows) + // { + // row.Freeze(); + // size.Y += row.Result; + // } + // + // if (limits.X > 0) size.X = Math.Max(size.X, limits.X); + // if (limits.Y > 0) size.Y = Math.Max(size.Y, limits.Y); + // + // // Temporary solution + // return new GridResult(size, cols.ToArray().Select(x => x.Result).ToArray(), rows.ToArray().Select(x => x.Result).ToArray()); + // + // static int CountFree(Span tracks) + // { + // int i = 0; + // foreach (Track track in tracks) + // { + // if (!track.IsFrozen) + // i++; + // } + // + // return i; + // } + // } + // + // private static void CopyToSpan(Span span, T2 items) + // where T2 : IEnumerable + // { + // using IEnumerator iterator = items.GetEnumerator(); + // for (int i = 0; i < span.Length; i++) + // { + // if (!iterator.MoveNext()) + // break; + // + // span[i] = iterator.Current; + // } + // } + // + // private static T1 SumSpan(ReadOnlySpan span) + // where T1 : struct, INumber + // { + // T1 value = default; + // + // foreach (T1 item in span) + // { + // value += item; + // } + // + // return value; + // } + // + // private static bool WithinTolerance(T value, T limit, T abstol, T reltol) + // where T : INumber + // { + // T tol = T.Max(abstol, value * reltol); + // return T.Abs(value - limit) < tol; + // } + // + // public record GridResult(Vector2 Size, float[] Columns, float[] Rows) + // { + // + // } + // + // private record struct Track(float Request) + // { + // public bool Auto => Request < 0; + // + // public bool IsFrozen { get; private set; } = false; + // public float Value { get; set; } = Request; + // + // public float Result { get; private set; } + // + // public void Freeze() + // { + // Result = Value; + // IsFrozen = true; + // } + // } + } +} diff --git a/Dashboard.Common/LayoutUnit.cs b/Dashboard.Common/LayoutUnit.cs new file mode 100644 index 0000000..ce47c23 --- /dev/null +++ b/Dashboard.Common/LayoutUnit.cs @@ -0,0 +1,30 @@ +namespace Dashboard +{ + public enum LayoutUnit : short + { + /// + /// Does not specify a unit. + /// + Auto, + /// + /// The default unit. A size of a single picture element. + /// + Pixel = 1, + /// + /// 1/72th of an inch traditional in graphics design. + /// + Point = 2, + /// + /// The universal length unit for small distances. + /// + Millimeter = 3, + /// + /// An inverse proportional unit with respect to the container size. + /// + Star = 4, + /// + /// A directly proportional unit with respect to the container size. + /// + Percent = 5, + } +} diff --git a/Dashboard.Common/Measure.cs b/Dashboard.Common/Measure.cs index 61fbf8d..c8ccf60 100644 --- a/Dashboard.Common/Measure.cs +++ b/Dashboard.Common/Measure.cs @@ -1,48 +1,108 @@ +using System.Numerics; + namespace Dashboard { - public enum MeasurementUnit + public record struct LayoutUnits(LayoutUnit All) { - /// - /// The default unit. A size of a single picture element. - /// - Pixel, - /// - /// 1/72th of an inch traditional in graphics design. - /// - Point, - /// - /// The universal length unit for small distances. - /// - Millimeter, - /// - /// An inverse proportional unit with respect to the container size. - /// - Star, - /// - /// A directly proportional unit with respect to the container size. - /// - Percent, + public LayoutUnit XUnit + { + get => (LayoutUnit)(((int)All & 0xF) >> 0); + set => All = (LayoutUnit)(((int)All & ~(0xF << 0)) | ((int)value << 0)); + } + + public LayoutUnit YUnit + { + get => (LayoutUnit)(((int)All & 0xF) >> 4); + set => All = (LayoutUnit)(((int)All & ~(0xF << 4)) | ((int)value << 4)); + } + + public LayoutUnit ZUnit + { + get => (LayoutUnit)(((int)All & 0xF) >> 8); + set => All = (LayoutUnit)(((int)All & ~(0xF << 8)) | ((int)value << 8)); + } + + public LayoutUnit WUnit + { + get => (LayoutUnit)(((int)All & 0xF) >> 12); + set => All = (LayoutUnit)(((int)All & ~(0xF << 12)) | ((int)value << 12)); + } } - public record struct AdvancedMetric(MeasurementUnit Unit, float Value) + public record struct Metric(LayoutUnit Units, float Value) { - public AdvancedMetric Convert(MeasurementUnit target, float dpi, float rel, int stars) + public float Compute(float dpi, float rel, float star) + { + switch (Units) + { + case LayoutUnit.Auto: + return -1; + case LayoutUnit.Millimeter: + float mm2Px = dpi / 25.4f; + return Value * mm2Px; + case LayoutUnit.Pixel: + return Value; + case LayoutUnit.Point: + float pt2Px = 72 / dpi; + return Value * pt2Px; + case LayoutUnit.Percent: + return rel * Value; + case LayoutUnit.Star: + return star * Value; + default: + throw new Exception("Unrecognized unit."); + } + } + } + + public record struct Metric2D(LayoutUnits Units, Vector2 Value) + { + public float X + { + get => Value.X; + set => Value = new Vector2(value, Value.Y); + } + + public LayoutUnit XUnits + { + get => Units.XUnit; + set => Units = Units with { XUnit = value }; + } + + public float Y + { + get => Value.Y; + set => Value = new Vector2(Value.X, value); + } + + public LayoutUnit YUnits + { + get => Units.YUnit; + set => Units = Units with { YUnit = value }; + } + } + + public record struct BoxMetric(LayoutUnit Units, Box2d Value); + + public record struct AdvancedMetric(LayoutUnit Unit, float Value) + { + public AdvancedMetric Convert(LayoutUnit target, float dpi, float rel, int stars) { if (Unit == target) return this; float pixels = Unit switch { - MeasurementUnit.Pixel => Value, - MeasurementUnit.Point => Value * (72f / dpi), - MeasurementUnit.Millimeter => Value * (28.3464566929f / dpi), - MeasurementUnit.Star => Value * rel / stars, - MeasurementUnit.Percent => Value * rel / 100, + LayoutUnit.Pixel => Value, + LayoutUnit.Point => Value * (72f / dpi), + LayoutUnit.Millimeter => Value * (28.3464566929f / dpi), + LayoutUnit.Star => Value * rel / stars, + LayoutUnit.Percent => Value * rel / 100, _ => throw new Exception(), }; float value = target switch { - MeasurementUnit.Pixel => pixels, - MeasurementUnit.Point => Value * (dpi / 72f), + LayoutUnit.Pixel => pixels, + LayoutUnit.Point => Value * (dpi / 72f), // MeasurementUnit.Millimeter => }; @@ -65,22 +125,4 @@ namespace Dashboard ? metric : throw new Exception($"Could not parse the value '{str}'."); } - - public static class MeasurementExtensions - { - public static bool IsRelative(this MeasurementUnit unit) => unit switch { - MeasurementUnit.Star or MeasurementUnit.Percent => true, - _ => false, - }; - public static bool IsAbsolute(this MeasurementUnit unit) => !IsRelative(unit); - - public static string ToShortString(this MeasurementUnit unit) => unit switch { - MeasurementUnit.Pixel => "px", - MeasurementUnit.Point => "pt", - MeasurementUnit.Millimeter => "mm", - MeasurementUnit.Star => "*", - MeasurementUnit.Percent => "%", - _ => throw new Exception("Unknown unit."), - }; - } } diff --git a/Dashboard.Common/MeasurementExtensions.cs b/Dashboard.Common/MeasurementExtensions.cs new file mode 100644 index 0000000..cbeb8cc --- /dev/null +++ b/Dashboard.Common/MeasurementExtensions.cs @@ -0,0 +1,30 @@ +namespace Dashboard +{ + public static class MeasurementExtensions + { + public static bool IsRelative(this LayoutUnit unit) => unit switch { + LayoutUnit.Star or LayoutUnit.Percent => true, + _ => false, + }; + public static bool IsAbsolute(this LayoutUnit unit) => !IsRelative(unit); + + public static string ToShortString(this LayoutUnit unit) => unit switch { + LayoutUnit.Pixel => "px", + LayoutUnit.Point => "pt", + LayoutUnit.Millimeter => "mm", + LayoutUnit.Star => "*", + LayoutUnit.Percent => "%", + _ => throw new Exception("Unknown unit."), + }; + + public static bool WithinTolerance(this float value, float reference, float absTol, float relTol) + => value.CompareTolerance(reference, absTol, relTol) == 0; + + public static int CompareTolerance(this float value, float reference, float absTol, float relTol) + { + float tolerance = Math.Max(absTol, Math.Abs(reference) * relTol); + float difference = value - reference; + return difference < -tolerance ? -1 : difference > tolerance ? 1 : 0; + } + } +} diff --git a/Dashboard.OpenGL/Drawing/DeviceContextBase.cs b/Dashboard.OpenGL/Drawing/DeviceContextBase.cs index ea58e0c..e213858 100644 --- a/Dashboard.OpenGL/Drawing/DeviceContextBase.cs +++ b/Dashboard.OpenGL/Drawing/DeviceContextBase.cs @@ -16,6 +16,7 @@ namespace Dashboard.OpenGL.Drawing private readonly Stack _transforms = new Stack(); private readonly Stack _clipRegions = new Stack(); private readonly Stack _scissorRegions = new Stack(); + private int _z = 0; public DeviceContext Context { get; private set; } = null!; IContextBase IContextExtensionBase.Context => Context; @@ -152,5 +153,15 @@ namespace Dashboard.OpenGL.Drawing { GL.Clear(ClearBufferMask.DepthBufferBit); } + + public int IncrementZ() + { + return ++_z; + } + + public int DecrementZ() + { + return --_z; + } } } diff --git a/Dashboard.OpenGL/Drawing/ImmediateMode.cs b/Dashboard.OpenGL/Drawing/ImmediateMode.cs index 4c7f181..cd3516a 100644 --- a/Dashboard.OpenGL/Drawing/ImmediateMode.cs +++ b/Dashboard.OpenGL/Drawing/ImmediateMode.cs @@ -1,5 +1,6 @@ using System.Drawing; using System.Numerics; +using System.Runtime; using System.Runtime.InteropServices; using Dashboard.Drawing; using Dashboard.Pal; @@ -164,6 +165,20 @@ namespace Dashboard.OpenGL.Drawing GL.DeleteBuffer(buffer); } + public void Rectangle(in RectangleDrawInfo rectangle) + { + // TODO: implement this better. + int z = Context.ExtensionRequire().IncrementZ(); + Color color = (rectangle.Fill as SolidColorBrush)?.Color ?? Color.LightGray; + Vector4 colorV = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); + Vector4 margin = rectangle.Box.Margin; + Vector4 border = rectangle.Box.Border; + Vector2 size = rectangle.Box.Size + new Vector2(border.X + border.Z, border.Y = border.W); + Box2d box = Box2d.FromPositionAndSize(rectangle.Position + new Vector2(margin.X + border.X, margin.Y + border.Y), size); + + Rectangle(box, z, colorV); + } + public void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture) { Span vertices = diff --git a/Dashboard/Controls/Button.cs b/Dashboard/Controls/Button.cs index 1a0db9d..6929187 100644 --- a/Dashboard/Controls/Button.cs +++ b/Dashboard/Controls/Button.cs @@ -2,6 +2,7 @@ using System; using System.Drawing; using System.Numerics; using Dashboard.Drawing; +using Dashboard.Layout; using Dashboard.Pal; namespace Dashboard.Controls @@ -31,8 +32,8 @@ namespace Dashboard.Controls { Box2d box = dc.ExtensionRequire().MeasureText(Font.Base, TextSize, Text); _intrinsicSize = box.Size; - Size = box.Size; - ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Size); + // Layout.Size = box.Size; + // ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Layout.Size); } public override void OnPaint(DeviceContext dc) @@ -42,8 +43,9 @@ namespace Dashboard.Controls if (AutoSize) CalculateSize(dc); + bool hidden = Layout.OverflowMode == OverflowMode.Hidden; var dcb = dc.ExtensionRequire(); - if (HideOverflow) + if (hidden) dcb.PushScissor(ClientArea); dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Top, 0)); @@ -58,7 +60,7 @@ namespace Dashboard.Controls colorVector = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); text.DrawText(Vector2.Zero, colorVector, TextSize, Font.Base, Text); - if (HideOverflow) + if (hidden) dcb.PopScissor(); dcb.PopTransforms(); diff --git a/Dashboard/Controls/Container.cs b/Dashboard/Controls/Container.cs index cd9cc0d..01cbe5e 100644 --- a/Dashboard/Controls/Container.cs +++ b/Dashboard/Controls/Container.cs @@ -1,16 +1,14 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Data.Common; -using System.Drawing; -using System.Numerics; using Dashboard.Drawing; using Dashboard.Events; +using Dashboard.Layout; using Dashboard.Pal; namespace Dashboard.Controls { - public class Container : Control, IList + public class Container : Control, IList, ILayoutContainer { private readonly List _controls = new List(); @@ -18,22 +16,26 @@ namespace Dashboard.Controls public bool IsReadOnly => false; + public ContainerLayoutInfo ContainerLayout { get; } = new ContainerLayoutInfo(); + public event EventHandler? ChildAdded; public event EventHandler? ChildRemoved; + public Control this[int index] { get => _controls[index]; set => _controls[index] = value; } - protected virtual void LayoutChildren() + protected override void ValidateLayout() { - // TODO: not position everything using absolute coordinates. - foreach (Control child in _controls) - { - child.ClientArea = new Box2d(child.Position, child.Position + child.Size); - } + if (!IsLayoutEnabled || IsLayoutValid) + return; + + // LayoutSolution solution = LayoutSolution.CalculateLayout(this, ClientArea.Size); + + base.ValidateLayout(); } public override void OnPaint(DeviceContext dc) @@ -42,12 +44,11 @@ namespace Dashboard.Controls var dcb = dc.ExtensionRequire(); dcb.PushClip(ClientArea); - // dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Bottom, 0)); - LayoutChildren(); + ValidateLayout(); foreach (Control child in _controls) { - if (child.DisplayMode == DisplayMode.None) + if (child.Layout.DisplayMode == DisplayMode.None) continue; child.SendEvent(this, new PaintEventArgs(dc)); @@ -56,6 +57,11 @@ namespace Dashboard.Controls dcb.PopClip(); } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + public IEnumerator GetEnumerator() { return _controls.GetEnumerator(); @@ -121,6 +127,8 @@ namespace Dashboard.Controls _controls.RemoveAt(index); ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, child)); } + + } public class ContainerChildAddedEventArgs(Container parent, Control child) : EventArgs diff --git a/Dashboard/Controls/Control.cs b/Dashboard/Controls/Control.cs index ea91625..4f6db8f 100644 --- a/Dashboard/Controls/Control.cs +++ b/Dashboard/Controls/Control.cs @@ -1,40 +1,22 @@ using System; +using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.Numerics; +using Dashboard.Drawing; using Dashboard.Events; +using Dashboard.Layout; using Dashboard.Pal; using Dashboard.Windowing; namespace Dashboard.Controls { - public enum DisplayMode - { - None, - Inline, - Block, - Flex, - Grid, - } - - public enum FlowDirection - { - Row, - Column, - RowReverse, - ColumnReverse, - } - - public enum PositionMode - { - Absolute, - Relative, - } - - public class Control : IEventListener, IDisposable + public class Control : IEventListener, ILayoutItem, IDisposable { private Form? _owner = null; public string? Id { get; set; } + public Form Owner { get => _owner ?? throw NoOwnerException; @@ -50,18 +32,12 @@ namespace Dashboard.Controls public virtual Box2d ClientArea { get; set; } public bool IsFocused => _owner?.FocusedControl == this; - // Layout properties for this control. - public Vector2 Size { get; set; } = new Vector2(50, 30); - public Vector2 MinimumSize { get; set; } = new Vector2(-1, -1); - public Vector2 MaximumSize { get; set; } = new Vector2(-1, -1); - public int ZIndex { get; set; } = -1; - public Vector2 Position { get; set; } - public PositionMode PositionMode { get; set; } = PositionMode.Relative; - public DisplayMode DisplayMode { get; set; } = DisplayMode.Inline; - public FlowDirection FlowDirection { get; set; } = FlowDirection.Row; - public int Row { get; set; } - public int Column { get; set; } - public bool HideOverflow { get; set; } = false; + public Brush Background { get; set; } = new SolidColorBrush(Color.Transparent); + public Brush BorderBrush { get; set; } = new SolidColorBrush(Color.Black); + + public LayoutInfo Layout { get; } = new LayoutInfo(); + public bool IsLayoutEnabled { get; private set; } = true; + protected bool IsLayoutValid { get; set; } = false; public event EventHandler? Painting; public event EventHandler? AnimationTick; @@ -74,7 +50,13 @@ namespace Dashboard.Controls public virtual Vector2 CalculateIntrinsicSize() { - return Vector2.Max(Vector2.Zero, Vector2.Max(Size, MinimumSize)); + return Vector2.Zero; + // return Vector2.Max(Vector2.Zero, Vector2.Max(Layout.Size, Layout.MinimumSize)); + } + + public Vector2 CalculateSize(Vector2 limits) + { + return CalculateIntrinsicSize(); } public virtual void OnPaint(DeviceContext dc) @@ -164,6 +146,7 @@ namespace Dashboard.Controls protected virtual void OnResize() { Resized?.Invoke(this, EventArgs.Empty); + InvalidateLayout(); } private void OnOwnerChanged(Form value) @@ -171,6 +154,28 @@ namespace Dashboard.Controls OwnerChanged?.Invoke(this, EventArgs.Empty); } + public void InvalidateLayout() + { + IsLayoutValid = false; + } + + protected virtual void ValidateLayout() + { + IsLayoutValid = true; + } + + public void ResumeLayout() + { + IsLayoutEnabled = true; + } + + public void SuspendLayout() + { + IsLayoutEnabled = false; + IsLayoutValid = false; + } + protected static Exception NoOwnerException => new Exception("No form owns this control"); + public event PropertyChangedEventHandler? PropertyChanged; } } diff --git a/Dashboard/Controls/Form.cs b/Dashboard/Controls/Form.cs index 8a80b91..1da38c3 100644 --- a/Dashboard/Controls/Form.cs +++ b/Dashboard/Controls/Form.cs @@ -56,6 +56,7 @@ namespace Dashboard.Controls dc.Begin(); var dcb = dc.ExtensionRequire(); + dcb.ResetClip(); dcb.ResetScissor(); dcb.ResetTransforms(); diff --git a/Dashboard/Controls/ImageBox.cs b/Dashboard/Controls/ImageBox.cs index d27914d..9ff2e39 100644 --- a/Dashboard/Controls/ImageBox.cs +++ b/Dashboard/Controls/ImageBox.cs @@ -18,9 +18,9 @@ namespace Dashboard.Controls if (Image == null) return; - Size = CalculateIntrinsicSize(); + // Layout.Size = CalculateIntrinsicSize(); - dc.ExtensionRequire().Image(new Box2d(ClientArea.Min, ClientArea.Min + Size), new Box2d(0, 0, 1, 1), 0, Image.InternTexture(dc)); + // dc.ExtensionRequire().Image(new Box2d(ClientArea.Min, ClientArea.Min + Layout.Size), new Box2d(0, 0, 1, 1), 0, Image.InternTexture(dc)); base.OnPaint(dc); } } diff --git a/Dashboard/Controls/Label.cs b/Dashboard/Controls/Label.cs index 2e6c3c5..d57d191 100644 --- a/Dashboard/Controls/Label.cs +++ b/Dashboard/Controls/Label.cs @@ -2,6 +2,7 @@ using System; using System.Drawing; using System.Numerics; using Dashboard.Drawing; +using Dashboard.Layout; using Dashboard.Pal; namespace Dashboard.Controls @@ -28,9 +29,9 @@ namespace Dashboard.Controls protected void CalculateSize(DeviceContext dc) { Box2d box = dc.ExtensionRequire().MeasureText(Font.Base, TextSize, Text); - _intrinsicSize = box.Size; - Size = box.Size; - ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Size); + // _intrinsicSize = box.Size; + // Layout.Size = box.Size; + // ClientArea = new Box2d(ClientArea.Min, ClientArea.Min + Layout.Size); } public override void OnPaint(DeviceContext dc) @@ -40,8 +41,9 @@ namespace Dashboard.Controls if (AutoSize) CalculateSize(dc); + bool hidden = Layout.OverflowMode == OverflowMode.Hidden; var dcb = dc.ExtensionRequire(); - if (HideOverflow) + if (hidden) dcb.PushScissor(ClientArea); dcb.PushTransforms(Matrix4x4.CreateTranslation(ClientArea.Left, ClientArea.Top, 0)); @@ -51,7 +53,7 @@ namespace Dashboard.Controls Vector4 colorVector = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); text.DrawText(Vector2.Zero, colorVector, TextSize, Font.Base, Text); - if (HideOverflow) + if (hidden) dcb.PopScissor(); dcb.PopTransforms(); diff --git a/Dashboard/Controls/MessageBox.cs b/Dashboard/Controls/MessageBox.cs index b37e989..10f1e40 100644 --- a/Dashboard/Controls/MessageBox.cs +++ b/Dashboard/Controls/MessageBox.cs @@ -40,7 +40,8 @@ namespace Dashboard.Controls private MessageBoxButtons _buttons; private ImageBox _iconBox = new ImageBox(); private Label _label = new Label(); - private readonly List