diff --git a/Quik/Controls/CommonEnums.cs b/Quik/Controls/CommonEnums.cs new file mode 100644 index 0000000..157c267 --- /dev/null +++ b/Quik/Controls/CommonEnums.cs @@ -0,0 +1,56 @@ + +using System; + +namespace Quik.Controls +{ + public enum Dock + { + None, + Top, + Left, + Bottom, + Right, + Center + } + + [Flags] + public enum Anchor + { + None = 0, + Top = 1 << 0, + Left = 1 << 1, + Bottom = 1 << 2, + Right = 1 << 3, + All = Top | Left | Bottom | Right + } + + public enum Direction + { + Vertical, + Horizontal + } + + public enum TextAlignment + { + Left, + Center, + Right, + Justify, + } + + public enum VerticalAlignment + { + Top, + Center, + Bottom, + Justify, + } + + public enum HorizontalAlignment + { + Left, + Center, + Right, + Justify + } +} \ No newline at end of file diff --git a/Quik/Controls/ContainerControl.cs b/Quik/Controls/ContainerControl.cs new file mode 100644 index 0000000..0598cad --- /dev/null +++ b/Quik/Controls/ContainerControl.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Quik.Controls +{ + public abstract class ContainerControl : Control, ICollection + { + private readonly List children = new List(); + + public int Count => children.Count; + + public bool IsReadOnly => false; + + public void Add(Control item) + { + children.Add(item); + } + + public void Clear() + { + children.Clear(); + } + + public bool Contains(Control item) + { + return children.Contains(item); + } + + public void CopyTo(Control[] array, int arrayIndex) + { + children.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return children.GetEnumerator(); + } + + public bool Remove(Control item) + { + return children.Remove(item); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return children.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Quik/Controls/Control.cs b/Quik/Controls/Control.cs index cf93de2..3042bc7 100644 --- a/Quik/Controls/Control.cs +++ b/Quik/Controls/Control.cs @@ -14,7 +14,10 @@ namespace Quik.Controls set => Style["padding"] = value; } - public bool IsVisualsValid { get; private set; } + public bool IsVisualsValid { get; private set; } = false; + public bool IsLayoutValid { get; private set; } = false; + + protected bool IsLayoutSuspended { get; private set; } = false; public void InvalidateVisual() { @@ -22,11 +25,39 @@ namespace Quik.Controls OnVisualsInvalidated(this, EventArgs.Empty); } + public void InvalidateLayout() + { + IsLayoutValid = false; + OnLayoutInvalidated(this, EventArgs.Empty); + } + + public void SuspendLayout() + { + IsLayoutSuspended = true; + } + + public void ResumeLayout() + { + IsLayoutSuspended = false; + InvalidateLayout(); + } + protected abstract void ValidateVisual(CommandList cmd); + protected abstract void ValidateLayout(); + protected override void PaintBegin(CommandList cmd) { base.PaintBegin(cmd); + if (!IsLayoutValid && !IsLayoutSuspended) + { + ValidateLayout(); + OnLayoutValidated(this, EventArgs.Empty); + IsLayoutValid = true; + + InvalidateVisual(); + } + if (!IsVisualsValid) { ValidateVisual(drawCommands); @@ -47,11 +78,13 @@ namespace Quik.Controls public event EventHandler StyleChanged; public event EventHandler VisualsInvalidated; public event EventHandler VisualsValidated; + public event EventHandler LayoutInvalidated; + public event EventHandler LayoutValidated; protected virtual void OnStyleChanged(object sender, EventArgs ea) { StyleChanged?.Invoke(sender, ea); - InvalidateVisual(); + InvalidateLayout(); } protected virtual void OnVisualsInvalidated(object sender, EventArgs ea) @@ -63,5 +96,15 @@ namespace Quik.Controls { VisualsValidated?.Invoke(sender, ea); } + + protected virtual void OnLayoutInvalidated(object sender, EventArgs ea) + { + LayoutInvalidated?.Invoke(sender, ea); + } + + protected virtual void OnLayoutValidated(object sender, EventArgs ea) + { + LayoutValidated?.Invoke(sender, ea); + } } } \ No newline at end of file diff --git a/Quik/Controls/FlowBox.cs b/Quik/Controls/FlowBox.cs new file mode 100644 index 0000000..a73f802 --- /dev/null +++ b/Quik/Controls/FlowBox.cs @@ -0,0 +1,23 @@ +using System; +using Quik.CommandMachine; + +namespace Quik.Controls +{ + public class FlowBox : ContainerControl + { + public Direction FlowDirection { get; set; } + public bool AllowWrap { get; set; } + public VerticalAlignment VerticalAlignment { get; set; } + public HorizontalAlignment HorizontalAlignment { get; set; } + + protected override void ValidateLayout() + { + throw new NotImplementedException(); + } + + protected override void ValidateVisual(CommandList cmd) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Quik/Controls/Label.cs b/Quik/Controls/Label.cs index 020f00c..b5a7215 100644 --- a/Quik/Controls/Label.cs +++ b/Quik/Controls/Label.cs @@ -7,15 +7,26 @@ namespace Quik.Controls public class Label : Control { public string Text { get; set; } - public QFont TextFont { get; set; } + public QFont Font { get; set; } public float TextSize { get; set; } + public bool AutoSize { get; set; } = true; + + protected override void ValidateLayout() + { + if (AutoSize) + { + QVec2 size = Typesetter.MeasureHorizontal(Text, TextSize, Font); + Size = size; + } + } + protected override void ValidateVisual(CommandList cmd) { float padding = Padding; QVec2 origin = new QVec2(padding, padding); - cmd.TypesetHorizontalDirect(Text, origin, TextSize, TextFont); + cmd.TypesetHorizontalDirect(Text, origin, TextSize, Font); } } } \ No newline at end of file diff --git a/Quik/Controls/UIBase.cs b/Quik/Controls/UIBase.cs index ffd919d..1da1506 100644 --- a/Quik/Controls/UIBase.cs +++ b/Quik/Controls/UIBase.cs @@ -9,20 +9,32 @@ namespace Quik.Controls /// public abstract class UIBase { + private QVec2 size; + public UIBase Parent { get; protected set; } public string Id { get; set; } - public QRectangle Bounds { get; set; } - - public QVec2 Position + public QRectangle Bounds { - get => Bounds.Min; - set => Bounds = new QRectangle(Bounds.Max - Bounds.Min + value, value); + get => new QRectangle(Position + Size, Position); + set + { + Size = value.Size; + Position = value.Min; + } } + public QVec2 Position { get; set; } + public QVec2 Size { - get => Bounds.Max - Bounds.Min; - set => Bounds = new QRectangle(value + Bounds.Min, Bounds.Min); + get => size; + set + { + QVec2 oldSize = size; + size = value; + + OnResized(this, new ResizedEventArgs(size, oldSize)); + } } public QRectangle AbsoluteBounds @@ -40,7 +52,13 @@ namespace Quik.Controls } } - public void NotifyEvent(object sender, EventArgs args) + public QVec2 MaximumSize { get; set; } = new QVec2(-1, -1); + public QVec2 MinimumSize { get; set; } = new QVec2(-1, -1); + + public bool IsMaximumSizeSet => MaximumSize != new QVec2(-1, -1); + public bool IsMinimumSizeSet => MinimumSize != new QVec2(-1, -1); + + public virtual void NotifyEvent(object sender, EventArgs args) { } @@ -61,5 +79,24 @@ namespace Quik.Controls PaintBegin(cmd); PaintEnd(cmd); } + + public event EventHandler Resized; + + public virtual void OnResized(object sender, ResizedEventArgs ea) + { + Resized?.Invoke(sender, ea); + } + } + + public class ResizedEventArgs : EventArgs + { + public QVec2 NewSize { get; } + public QVec2 OldSize { get; } + + public ResizedEventArgs(QVec2 newSize, QVec2 oldSize) + { + NewSize = newSize; + OldSize = oldSize; + } } } \ No newline at end of file diff --git a/tests/QuikDemo/Program.cs b/tests/QuikDemo/Program.cs index a1d8744..e889011 100644 --- a/tests/QuikDemo/Program.cs +++ b/tests/QuikDemo/Program.cs @@ -21,7 +21,7 @@ namespace QuikDemo public class EmptyView : View { private QFont font; - private readonly Label Label = new Label() { Text = "Hello world!", Size = new QVec2(256, 32), Position = new QVec2(300, 300) }; + private readonly Label Label = new Label() { Text = "Hello world!", Position = new QVec2(300, 300) }; protected override void PaintBegin(CommandList cmd) { @@ -32,8 +32,8 @@ namespace QuikDemo IFontDataBase db = FontDataBaseProvider.Instance; font = new QFontFreeType(db.FontFileInfo(db.Sans).OpenRead()); - Label.TextFont = font; - Label.TextSize = 16; + Label.Font = font; + Label.TextSize = 12; Label.InvalidateVisual(); }