diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1feb41b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[*.cs] +indent_style = space +indent_size = 3 \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs index 47961c0..d0dce00 100644 --- a/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs +++ b/Qrakhen.Qamp.Core/Compilation/ExpressionParser.cs @@ -77,9 +77,11 @@ public static class ExpressionParser digester.Emit(OpCode.BitwiseRight); break; case TokenType.Increment: + digester.EmitConstant(new Value(1L)); digester.Emit(OpCode.Increment); break; case TokenType.Decrement: + digester.EmitConstant(new Value(1L)); digester.Emit(OpCode.Decrement); break; default: @@ -148,17 +150,23 @@ public static class ExpressionParser digester.EmitDynamic(OpCode.Array, length); // digester.MakeConstant(new Value((long)length))); } - static void AddItem(Digester digester, bool canAssign) + static void Increment(Digester digester, bool canAssign) { digester.ParseExpression(); - digester.Emit(OpCode.AddItem); + digester.Emit(OpCode.Increment); + } + + static void Decrement(Digester digester, bool canAssign) + { + digester.ParseExpression(); + digester.Emit(OpCode.Decrement); } static void Accessor(Digester digester, bool canAssign) { digester.ParseExpression(); digester.Consume(TokenType.ArrayClose, "expected ] after array accessor"); - if (canAssign && digester.Match(TokenType.AddItem)) { + if (canAssign && digester.Match(TokenType.PlusEqual)) { digester.ParseExpression(); digester.Emit(OpCode.AddItem); } else if (canAssign && digester.Match(TokenType.Equal)) { @@ -295,8 +303,8 @@ public static class ExpressionParser _rules[TokenType.ContextClose] = new Rule(null, null, Weight.None); _rules[TokenType.ArrayOpen] = new Rule(Array, Accessor, Weight.Call); _rules[TokenType.ArrayClose] = new Rule(null, null, Weight.None); - _rules[TokenType.AddItem] = new Rule(null, AddItem, Weight.None); - _rules[TokenType.RemoveItem] = new Rule(null, null, Weight.None); + _rules[TokenType.PlusEqual] = new Rule(null, Increment, Weight.None); // this is its own compilation operation as x++ calls binary and makes up the atomic change (1) on the spot. + _rules[TokenType.MinusEqual] = new Rule(null, Decrement, Weight.None); // same as above _rules[TokenType.Colon] = new Rule(null, Dot, Weight.Call); _rules[TokenType.Comma] = new Rule(null, null, Weight.None); _rules[TokenType.Dot] = new Rule(null, Dot, Weight.Call); diff --git a/Qrakhen.Qamp.Core/Execution/OpCode.cs b/Qrakhen.Qamp.Core/Execution/OpCode.cs index 4852756..142ffb6 100644 --- a/Qrakhen.Qamp.Core/Execution/OpCode.cs +++ b/Qrakhen.Qamp.Core/Execution/OpCode.cs @@ -52,8 +52,8 @@ public enum OpCode Greater = 0x51, Less = 0x52, - Increment = 0x70, - Decrement = 0x71, + Increment = 0x70, // basically increment by, ++ simply 'makes up' the 1. + Decrement = 0x71, // same here broren min Array = 0xc0, List = 0xc1, diff --git a/Qrakhen.Qamp.Core/Execution/Runner.cs b/Qrakhen.Qamp.Core/Execution/Runner.cs index a30f378..488eb4a 100644 --- a/Qrakhen.Qamp.Core/Execution/Runner.cs +++ b/Qrakhen.Qamp.Core/Execution/Runner.cs @@ -456,7 +456,7 @@ public class Runner : IDisposable } } - private bool InvokeNative(Value target, NativeExtension extension, int argumentCount) + private bool InvokeExtension(Value target, ExtensionMethod extension, int argumentCount) { #if LOG _logger.Method(extension); @@ -472,7 +472,7 @@ public class Runner : IDisposable } Stack.Decimate(Stack.Position - argumentCount - 1); - Value result = extension.Callback(target, values); + Value result = extension.Call(target, values); Push(result); return true; } @@ -500,13 +500,14 @@ public class Runner : IDisposable private bool Invoke(string methodName, int argumentCount) { Value value = Peek(-argumentCount - 1); + TypeInfo typeInfo = value.TypeInfo; if (!value.Is(T.Instance)) { - NativeExtension? extension = NativeExtension.Get(value.Type, methodName); + ExtensionMethod? extension = ExtensionMethod.Get(typeInfo, methodName, argumentCount); if (extension == null) { - Error($"Could not invoke {methodName} on {value}, no method or extension by that name found for this type."); + Error($"Could not invoke {methodName} on {value}, no method or extension by that name found for {typeInfo}."); return false; } - return InvokeNative(value, extension, argumentCount); + return InvokeExtension(value, extension, argumentCount); } Instance instance = value.Ptr.As()!; diff --git a/Qrakhen.Qamp.Core/Extensions.cs b/Qrakhen.Qamp.Core/Extensions.cs index 4d78c8a..59190bf 100644 --- a/Qrakhen.Qamp.Core/Extensions.cs +++ b/Qrakhen.Qamp.Core/Extensions.cs @@ -27,8 +27,9 @@ public static class Extensions return match.Success; } - public static T[] Subset(this T[] array, long from, long length) + public static T[] Subset(this T[] array, long from, long length = -1) { + length = length < 0 ? array.Length - from : length; var result = new T[length]; Array.Copy(array, from, result, 0, length); return result; diff --git a/Qrakhen.Qamp.Core/Tokenization/Reader.cs b/Qrakhen.Qamp.Core/Tokenization/Reader.cs index f72edc4..ba96578 100644 --- a/Qrakhen.Qamp.Core/Tokenization/Reader.cs +++ b/Qrakhen.Qamp.Core/Tokenization/Reader.cs @@ -273,77 +273,79 @@ public class Reader : IReader, IDisposable '(' => MakeToken(GroupOpen, buffer), ')' => MakeToken(GroupClose, buffer), '.' => Check('~') ? - MakeToken(This, buffer + Next()) : - MakeToken(Dot, buffer), + MakeToken(This, buffer + Next()) : + MakeToken(Dot, buffer), ',' => MakeToken(Comma, buffer), ';' => MakeToken(Semicolon, buffer), ':' => Check(':') ? - MakeToken(Print, buffer + Next()) : - Check('[') ? - MakeToken(ListOpen, buffer + Next()) : - MakeToken(Colon, buffer), + MakeToken(Print, buffer + Next()) : + Check('[') ? + MakeToken(ListOpen, buffer + Next()) : + MakeToken(Colon, buffer), '&' => Check('&') ? - MakeToken(And, buffer + Next()) : - MakeToken(BitwiseAnd, buffer), + MakeToken(And, buffer + Next()) : + MakeToken(BitwiseAnd, buffer), '^' => Check('~') ? - MakeToken(Base, buffer + Next()) : - MakeToken(BitwiseXor, buffer), + MakeToken(Base, buffer + Next()) : + MakeToken(BitwiseXor, buffer), '%' => Check('=') ? - MakeToken(ModuloEqual, buffer + Next()) : - MakeToken(Modulo, buffer), + MakeToken(ModuloEqual, buffer + Next()) : + MakeToken(Modulo, buffer), '|' => Check('|') ? - MakeToken(Or, buffer + Next()) : - MakeToken(BitwiseOr, buffer), + MakeToken(Or, buffer + Next()) : + MakeToken(BitwiseOr, buffer), '!' => Check('=') ? - MakeToken(BangEqual, buffer + Next()) : - MakeToken(Bang, buffer), + MakeToken(BangEqual, buffer + Next()) : + MakeToken(Bang, buffer), '+' => Check('+') ? - MakeToken(Increment, buffer + Next()) : - Check('=') ? - MakeToken(PlusEqual, buffer + Next()) : - MakeToken(Plus, buffer), + MakeToken(Increment, buffer + Next()) : + Check('=') ? + MakeToken(PlusEqual, buffer + Next()) : + MakeToken(Plus, buffer), '-' => Check('-') ? - MakeToken(Decrement, buffer + Next()) : - Check('=') ? - MakeToken(MinusEqual, buffer + Next()) : - MakeToken(Minus, buffer), + MakeToken(Decrement, buffer + Next()) : + Check('=') ? + MakeToken(MinusEqual, buffer + Next()) : + MakeToken(Minus, buffer), '/' => Check('=') ? - MakeToken(SlashEqual, buffer + Next()) : - MakeToken(Slash, buffer), + MakeToken(SlashEqual, buffer + Next()) : + MakeToken(Slash, buffer), '*' => Check('=') ? - MakeToken(StarEqual, buffer + Next()) : - Check('~') ? - MakeToken(Var, buffer + Next()) : - MakeToken(Star, buffer), + MakeToken(StarEqual, buffer + Next()) : + Check('~') ? + MakeToken(Var, buffer + Next()) : + MakeToken(Star, buffer), '=' => Check('=') ? - MakeToken(EqualEqual, buffer + Next()) : - MakeToken(Equal, buffer), + MakeToken(EqualEqual, buffer + Next()) : + MakeToken(Equal, buffer), '<' => Check('~') ? - MakeToken(Equal, buffer + Next()) : - Check(':') ? + MakeToken(Equal, buffer + Next()) : + Check(':') ? MakeToken(Return, buffer + Next()) : Check('+') ? - MakeToken(AddItem, buffer + Next()) : - Check('<') ? - MakeToken(BitwiseLeft, buffer + Next()) : - Check('=') ? + MakeToken(PlusEqual, buffer + Next()) : + Check('-') ? + MakeToken(MinusEqual, buffer + Next()) : + Check('<') ? + MakeToken(BitwiseLeft, buffer + Next()) : + Check('=') ? MakeToken(LessEqual, buffer + Next()) : Check('>') ? - MakeToken(TernaryElse, buffer + Next()) : - MakeToken(Less, buffer), + MakeToken(TernaryElse, buffer + Next()) : + MakeToken(Less, buffer), '>' => Check('>') ? - MakeToken(BitwiseRight, buffer + Next()) : - Check('=') ? - MakeToken(GreaterEqual, buffer + Next()) : - MakeToken(Greater, buffer), + MakeToken(BitwiseRight, buffer + Next()) : + Check('=') ? + MakeToken(GreaterEqual, buffer + Next()) : + MakeToken(Greater, buffer), '~' => Check('(') ? - MakeToken(Lambda, buffer) : - MakeToken(BitwiseNot, buffer), + MakeToken(Lambda, buffer) : + MakeToken(BitwiseNot, buffer), '?' => Check('?') ? - MakeToken(DoubleQuestion, buffer + Next()) : - Check(':') ? - MakeToken(TypeOf, buffer + Next()) : - MakeToken(Question, buffer), + MakeToken(DoubleQuestion, buffer + Next()) : + Check(':') ? + MakeToken(TypeOf, buffer + Next()) : + MakeToken(Question, buffer), _ => MakeToken(Error, buffer) //throw new ReaderException($"Could not identify operator <{buffer}>", this) }; } @@ -351,23 +353,26 @@ public class Reader : IReader, IDisposable private Token MakeToken(TokenType type, string buffer) { return new Token( - type, - buffer, - _startPosition, - new StreamSpan(_startIndex, _current - _startIndex)); + type, + buffer, + _startPosition, + new StreamSpan(_startIndex, _current - _startIndex)); } private void Continue() { if (Done) return; + long size = _stream.Length - _stream.Position; if (size > ChunkSize) size = ChunkSize; + byte[] buffer = new byte[size]; int read = _stream.Read(buffer, 0, (int)size); if (read < 0) return; + _buffer.AddRange(Encoding.UTF8.GetChars(buffer)); } diff --git a/Qrakhen.Qamp.Core/Tokenization/TokenType.cs b/Qrakhen.Qamp.Core/Tokenization/TokenType.cs index 85ccccc..576e591 100644 --- a/Qrakhen.Qamp.Core/Tokenization/TokenType.cs +++ b/Qrakhen.Qamp.Core/Tokenization/TokenType.cs @@ -57,8 +57,6 @@ public enum TokenType SlashEqual = Assignment | 4, StarEqual = Assignment | 5, ModuloEqual = Assignment | 6, - AddItem = Assignment | 7, - RemoveItem = Assignment | 8, Condition = 1 << 10, Bang = Condition | 1, diff --git a/Qrakhen.Qamp.Core/Values/ExtensionMethod.cs b/Qrakhen.Qamp.Core/Values/ExtensionMethod.cs new file mode 100644 index 0000000..cf5082e --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/ExtensionMethod.cs @@ -0,0 +1,249 @@ +using Qrakhen.Qamp.Core.Collections; +using Qrakhen.Qamp.Core.Logging; +using Qrakhen.Qamp.Core.Values.Objects; +using System.Linq.Expressions; +using System.Reflection; + +namespace Qrakhen.Qamp.Core.Values; + +public delegate Value ExtensionDelegate(Value target, Value[] parameters); + +public class ExtensionException(string message, object? context = null) : QampException(message, context); + +public abstract class ExtensionAttribute : Attribute; + +public abstract class TypedExtensionAttribute : ExtensionAttribute +{ + public ValueType ValueType { get; set; } = ValueType.Undefined; + public Type? ValueObjectType { get; set; } = null; + public string? ValueObjectName { get; set; } + public bool Nullable { get; set; } = false; + public TypeInfo TypeInfo => new TypeInfo(ValueType, ValueObjectType, ValueObjectName, Nullable); +} + +[AttributeUsage(AttributeTargets.Method)] +public class ExtensionMethodAttribute : ExtensionAttribute +{ + public string? Name { get; set; } = null; +} + +[AttributeUsage(AttributeTargets.Method)] +public class ReturnsAttribute : TypedExtensionAttribute; + +[AttributeUsage(AttributeTargets.Parameter)] +public class SelfAttribute : TypedExtensionAttribute; + +[AttributeUsage(AttributeTargets.Class)] +public class ExtensionClassAttribute : SelfAttribute +{ + public string? Name { get; set; } = null; +} + +[AttributeUsage(AttributeTargets.Parameter)] +public class ParameterAttribute : TypedExtensionAttribute +{ + public string? Name { get; set; } = null; + public bool Optional { get; set; } = false; +} + + +public class ExtensionMethod( + TypeInfo targetType, + TypeInfo returnType, + string name, + ExtensionDelegate callback, + string[] parameters) +{ + private static readonly Register> _register = []; + + private static readonly ILogger _logger = LoggerService.Get(); + + public readonly string Name = name; + public readonly TypeInfo TargetType = targetType; + public readonly TypeInfo ReturnType = returnType; + public readonly ExtensionDelegate Callback = callback; + public readonly string[] Parameters = parameters.ToArray(); + + public readonly string Key = parameters.Length > 0 ? $"{name}_{parameters.Length}" : name; + + public static ExtensionMethod? Get(TypeInfo targetType, string name, int parameters = 0) + { + if (parameters > 0) + name = $"{name}_{parameters}"; + + if (!_register.Has(targetType)) + return null; + + if (!_register[targetType].Has(name)) + return null; + + return _register[targetType][name]; + } + + public static void Register(IEnumerable extensions) + { + foreach (var extension in extensions) + Register(extension); + } + + public static void Register(ExtensionMethod extension) + { + if (!_register.Has(extension.TargetType)) + _register.Add(extension.TargetType, []); + + if (_register[extension.TargetType].Has(extension.Key)) + throw new ExtensionException($"Extension {extension.Key} already exists for type {extension.TargetType}."); + + _register[extension.TargetType].Add(extension.Key, extension); + } + + public static void Register(TypeInfo targetType, + TypeInfo returnType, + string name, + ExtensionDelegate callback, + params string[] parameters) + => Register(new ExtensionMethod(targetType, returnType, name, callback, parameters)); + + private static ExtensionMethod RenderExtension(MethodInfo method) + { + _logger.Verbose($"Rendering extension from {method}..."); + var attr = method.GetCustomAttribute(); + if (attr is null) + throw new ExtensionException($"Expected an [ExtensionMethod] attribute on method {method} for extension compilation.", method); + + var parameters = method.GetParameters(); + if (parameters.Length < 1) + throw new ExtensionException($"Tried to define method {method} as extension, but extensions require their first parameter to be , the target value of the extension.", method); + + var selfAttr = parameters[0].GetCustomAttribute() ?? method.DeclaringType?.GetCustomAttribute(); + if (selfAttr is null) + throw new ExtensionException($"Expected first parameter of {method} to have a [Self], or its declaring type to have a [ExtensionClass] attribute in order to define the extension's target type."); + + TypeInfo anyType = TypeInfo.Any; + + string name = attr.Name ?? method.Name; + TypeInfo selfType = selfAttr.TypeInfo; + TypeInfo returnType = method.GetCustomAttribute()?.TypeInfo ?? anyType; // todo: assume returned type from actual return value + string[] parameterNames = parameters.Subset(1).Select(p => p.Name!).ToArray() ?? []; // todo: use the actual parameter attribute for this. + + ExtensionDelegate callback = RenderDelegate(method); + + return new ExtensionMethod(selfType, returnType, name, callback, parameterNames); + } + + private static ExtensionDelegate RenderDelegate(MethodInfo method) + { + _logger.Verbose($"Rendering delegate expression..."); + var selfParameter = Expression.Parameter(typeof(Value), "self"); + var argsParameter = Expression.Parameter(typeof(Value[]), "args"); + ParameterInfo[] parameterInfos = method.GetParameters(); + + var parameterExpressions = new Expression[parameterInfos.Length - 1]; + + for (int i = 0; i < parameterInfos.Length; i++) { + ConstantExpression index = Expression.Constant(i); + BinaryExpression indexExpression = Expression.ArrayIndex(argsParameter, index); + + /* todo: unsure + Type requiredType = parameterInfos[i].ParameterType; + if (indexExpression.Type != requiredType) { + parameterExpressions[i] = Expression.Convert(indexExpression, requiredType); + }*/ + + parameterExpressions[i] = indexExpression; + } + + Expression[] arguments = [selfParameter, .. parameterExpressions]; + MethodCallExpression callExpression = Expression.Call(method, arguments); + Expression final; + + if (method.ReturnType != typeof(Value)) { + ConstructorInfo? ctor = typeof(Value).GetConstructor([ method.ReturnType ]); + if (ctor is null) + throw new ExtensionException($"Can not compile extension from method {method}, its return type '{method.ReturnType}' is not accepted by any Value constructors.", method); + final = Expression.New(ctor, callExpression); + } else { + final = callExpression; + } + + LambdaExpression lambda = Expression.Lambda(final, selfParameter, argsParameter); + + _logger.Verbose($"Done: ", lambda); + return (ExtensionDelegate)lambda.Compile(); + } + + public static IEnumerable CompileExtensionsFromType(Type type) + { + _logger.Debug($"Compiling static extension methods from {type}..."); + List extensions = []; + + var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(method => method.GetCustomAttribute() is not null) + .ToArray(); + + if (methods.Length == 0) { + _logger.Warn($"Tried to compile static extension methods from {type}, but no public static methods were found declared inside it."); + return extensions; + } + + foreach (var method in methods) { + extensions.Add(RenderExtension(method)); + } + + return extensions; + } + + static ExtensionMethod() + { + Register(CompileExtensionsFromType(typeof(StringExtensions))); + } + + public Value Call(Value self, Value[] parameters) + { + return Callback(self, parameters); + } + + public override string ToString() + { + return $"{TargetType}.{Name}({string.Join(", ", Parameters)}) (NativeExtension)"; + } +} + +[ExtensionClass(ValueType = ValueType.String)] +public static class StringExtensions +{ + [ExtensionMethod] + public static long Length(Objects.String self) + { + return self.Value?.Length ?? 0; + } + + [ExtensionMethod] + public static long IndexOf(Objects.String self, Objects.String needle) + { + if (needle.Value is null) + return -1; + + return self.Value?.IndexOf(needle.Value) ?? -1; + } + + [ExtensionMethod] + public static Objects.Array Split(Objects.String self, Objects.String splitter) + { + string? _splitter = splitter.Value; + string? _string = self.Value; + + if (_string is null) + return new Objects.Array([]); + + if (_splitter is null) + return new Objects.Array([ Obj.Create(self) ]); + + string[] parts = _string.Split(_splitter); + Value[] values = new Value[parts.Length]; + for (int i = 0; i < parts.Length; i++) + values[i] = Objects.String.Make(parts[i]); + + return new Objects.Array(values); + } +} diff --git a/Qrakhen.Qamp.Core/Values/NativeExtension.cs b/Qrakhen.Qamp.Core/Values/NativeExtension.cs deleted file mode 100644 index 0aafb2c..0000000 --- a/Qrakhen.Qamp.Core/Values/NativeExtension.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Qrakhen.Qamp.Core.Collections; -using Qrakhen.Qamp.Core.Values.Objects; - -namespace Qrakhen.Qamp.Core.Values; - -public delegate Value ExtensionDelegate(Value target, Value[] parameters); - -public class NativeExtension( - ValueType targetType, - string name, - ExtensionDelegate callback, - string[] parameters) -{ - private static readonly Register> _register = new(); - - public readonly string Name = name; - public readonly ValueType TargetType = targetType; - public readonly ExtensionDelegate Callback = callback; - public readonly string[] Parameters = parameters.ToArray(); - - public static NativeExtension? Get(ValueType targetType, string name) - { - if (!_register.Has(targetType)) - return null; - if (!_register[targetType].Has(name)) - return null; - return _register[targetType][name]; - } - - public static void Register(ValueType targetType, - string name, - ExtensionDelegate callback, - params string[] parameters) - { - if (!_register.Has(targetType)) - _register.Add(targetType, new()); - if (_register[targetType].Has(name)) - throw new QampException($"Extension {name} already exists for type {targetType}."); - _register[targetType].Add(name, new NativeExtension(targetType, name, callback, parameters)); - } - - static NativeExtension() - { - Register(ValueType.String, "Length", (v, p) => new Value((long)v.Ptr.As()!.Value!.Length), []); - Register(ValueType.String, "IndexOf", (v, p) => new Value((long)v.Ptr.As()!.Value!.IndexOf(p[0].Ptr.As()!.Value!)), ["needle"]); - } - - public override string ToString() - { - return $"{TargetType}.{Name}({string.Join(", ", Parameters)}) (NativeExtension)"; - } -} diff --git a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs index 47cefa3..7a3265a 100644 --- a/Qrakhen.Qamp.Core/Values/Objects/Obj.cs +++ b/Qrakhen.Qamp.Core/Values/Objects/Obj.cs @@ -2,8 +2,8 @@ public class Obj(ValueType type) : IValue { - public bool __gcMarked { get; private set; } - public int __gcCount { get; private set; } = 1; + public bool __gcMarked = false; + public int __gcCount = 1; public readonly ValueType Type = type; diff --git a/Qrakhen.Qamp.Core/Values/Ptr.cs b/Qrakhen.Qamp.Core/Values/Ptr.cs index 18ee0b0..13ad90c 100644 --- a/Qrakhen.Qamp.Core/Values/Ptr.cs +++ b/Qrakhen.Qamp.Core/Values/Ptr.cs @@ -44,8 +44,25 @@ public readonly struct Ptr return value; } + public Obj? As(Type type, bool throwWhenNull = true) + { + Obj? value = Convert.ChangeType(Value, type) as Obj; + if (value == null && throwWhenNull) + throw new RuntimeException($"Could not convert Object {Value} to target type {type}.", Value); + return value; + } + public override string ToString() => $"0x{Value}"; + public static async Task __GC_Prepare() + { + await Task.Run(() => { + foreach (var obj in _register) { + obj.__gcCount = 0; + } + }); + } + public static byte[] SerializeFunctions() { List result = new(); diff --git a/Qrakhen.Qamp.Core/Values/TypeInfo.cs b/Qrakhen.Qamp.Core/Values/TypeInfo.cs new file mode 100644 index 0000000..ff5c43f --- /dev/null +++ b/Qrakhen.Qamp.Core/Values/TypeInfo.cs @@ -0,0 +1,26 @@ +namespace Qrakhen.Qamp.Core.Values; + +public readonly record struct TypeInfo(ValueType Type, Type? ObjectType = null, string? TypeName = null, bool Nullable = false) +{ + public static readonly TypeInfo Any = new(ValueType.Undefined, null, null, true); + public static readonly TypeInfo Void = new(ValueType.Void, null, null, false); + + public bool Match(TypeInfo other) + { + if (Type != ValueType.Undefined && other.Type != ValueType.Undefined && Type != other.Type) + return false; + + if (ObjectType != null && other.ObjectType != null && ObjectType != other.ObjectType) + return false; + + if (TypeName != null && other.TypeName != null && TypeName != other.TypeName) + return false; + + return true; + } + + public static TypeInfo FromValue(Value value) + { + return new TypeInfo(value.Type, value.IsObj ? value.Ptr.Value?.GetType() : null, null); + } +} \ No newline at end of file diff --git a/Qrakhen.Qamp.Core/Values/Value.cs b/Qrakhen.Qamp.Core/Values/Value.cs index d04ca2e..1838846 100644 --- a/Qrakhen.Qamp.Core/Values/Value.cs +++ b/Qrakhen.Qamp.Core/Values/Value.cs @@ -43,10 +43,12 @@ public readonly struct Value : IValue, ISerialize, IDebug public Value(Address address) : this(T.Address) => Address = address; public Value(Ptr ptr) : this(ptr.Value?.Type ?? T.Pointer) => Ptr = ptr; public Value(Ref @ref) : this(T.Reference) => Ref = @ref; + public Value(Obj obj) : this(Ptr.Create(obj)) { } // todo: very spicy, let's see if this doesn't fuck up the GC all too bad. public dynamic? Dynamic => AsDynamic(); public T ValueType => Type; + public TypeInfo TypeInfo => TypeInfo.FromValue(this); public bool IsNumber => Type <= T.Decimal; public bool IsSigned => Is(T.Signed); diff --git a/Qrakhen.Qamp.Core/Values/ValueType.cs b/Qrakhen.Qamp.Core/Values/ValueType.cs index 3e503d3..00e6d8e 100644 --- a/Qrakhen.Qamp.Core/Values/ValueType.cs +++ b/Qrakhen.Qamp.Core/Values/ValueType.cs @@ -42,4 +42,4 @@ public enum ValueType Structure = ItemProvider | 0x0004, Module = 0xFFFF -} \ No newline at end of file +} diff --git a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs index 81c5795..a622c08 100644 --- a/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs +++ b/Qrakhen.Qamp.Editor/ViewModel/EditorFrameViewModel.cs @@ -140,17 +140,6 @@ public class EditorFrameViewModel : ObservableObject CurrentSelection = BufferRegion.Void; } - /* - - asdasdy - klöklöklökl - köäläälööx - cyxc - - asdasd - - * */ - public void Insert(string data, bool follow = true) { data = data.Replace("\t", " "); diff --git a/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropControl.cs b/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropControl.cs deleted file mode 100644 index ae04508..0000000 --- a/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropControl.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; -using System.Xml.Linq; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DragAndDrop; - -/// -/// Used to handle drag and drop callbacks via the abstract methods and . -/// Can be used as a parent to any amount of s that refer to the their handler via . -/// If no Handler is provided, the element itself will be considered its own handler, as long as it is a . -/// In order to make an element draggable, set to true within the element's XAML attributes. -/// To mark a FrameworkElement as a drop zone, set to true. -/// If a draggable element is dropped on a drop zone, the handler's is called. -/// -public abstract class DragAndDropControl : Control -{ - /// - /// Called when a DragDrop operation starts. - /// - protected virtual void OnElementDragStart(UIElement draggedElement) { } - - /// - /// Called during a DragDrop operation in order to update the cursor, if needed. - /// - protected virtual void OnElementDragGiveCursorFeedback(object sender, GiveFeedbackEventArgs args) { } - - /// - /// Called during a DragDrop operation. - /// - protected virtual void OnElementDrag(UIElement draggedElement, MouseEventArgs args) { } - - /// - /// Called when an element was dropped without being on an element marked as a DropZone. - /// - /// - protected virtual void OnElementDragStop(UIElement draggedElement) { } - - /// - /// Called when this element is the potential target of a DragDrop operation. - /// - protected virtual void OnElementDragOver(DragAndDropData data, UIElement targetElement) { } - - /// - /// Called when this element stops being a potential target of a DragDrop operation. - /// - protected virtual void OnElementDragLeave(UIElement targetElement) { } - - /// - /// Required callback to handle , - /// provided with containing both the source element being dragged and it's original data context. - /// - protected abstract void OnElementDropped(DragAndDropData data, UIElement targetElement); - - #region Attached Properties - - /* === IsDraggable === */ - - public static readonly DependencyProperty IsDraggableProperty = DependencyProperty - .RegisterAttached( - "IsDraggable", - typeof(bool), - typeof(DragAndDropControl), - new PropertyMetadata(false, OnIsDraggableChanged)); - - public static bool GetIsDraggable(DependencyObject obj) => (bool)obj.GetValue(IsDraggableProperty); - public static void SetIsDraggable(DependencyObject obj, bool value) => obj.SetValue(IsDraggableProperty, value); - - /// - /// Adds event listeners to the element's GiveFeedback, MouseLeftButtonUp and MouseMove events. - /// - private static void OnIsDraggableChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) - { - if (sender is not FrameworkElement element) - { - throw new InvalidOperationException($"IsDraggable may only be attached to FrameworkElements, got {sender.GetType()} instead."); - } - - if ((bool)e.NewValue) - { - element.GiveFeedback += HandleGiveFeedback; - element.MouseLeftButtonUp += HandleLeftMouseButtonUp; - element.MouseMove += HandleMouseMove; - } - else - { - element.GiveFeedback -= HandleGiveFeedback; - element.MouseLeftButtonUp -= HandleLeftMouseButtonUp; - element.MouseMove -= HandleMouseMove; - } - } - - /* === IsDropZone === */ - - public static readonly DependencyProperty IsDropZoneProperty = DependencyProperty - .RegisterAttached( - "IsDropZone", - typeof(bool), - typeof(DragAndDropControl), - new PropertyMetadata(false, OnIsDropZoneChanged)); - - public static bool GetIsDropZone(DependencyObject obj) => (bool)obj.GetValue(IsDropZoneProperty); - public static void SetIsDropZone(DependencyObject obj, bool value) => obj.SetValue(IsDropZoneProperty, value); - - /// - /// Adds an event listener to this element's event. - /// - private static void OnIsDropZoneChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) - { - if (sender is not FrameworkElement element) - { - throw new InvalidOperationException($"IsDraggable may only be attached to FrameworkElements, got {sender.GetType()} instead."); - } - - if ((bool)e.NewValue) - { - element.AllowDrop = true; - element.DragOver += HandleDragOver; - element.DragLeave += HandleDragLeave; - element.Drop += HandleDrop; - } - else - { - element.DragOver -= HandleDragOver; - element.DragLeave -= HandleDragLeave; - element.Drop -= HandleDrop; - } - } - - /* === Handler === */ - - public static readonly DependencyProperty HandlerProperty = DependencyProperty - .RegisterAttached( - "Handler", - typeof(DependencyObject), - typeof(DragAndDropControl), - new PropertyMetadata(null, OnHandlerChanged)); - - public static DependencyObject GetHandler(DependencyObject obj) => (DependencyObject)obj.GetValue(HandlerProperty); - public static void SetHandler(DependencyObject obj, bool value) => obj.SetValue(HandlerProperty, value); - - private static void OnHandlerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) - { - // Simple validation - if (sender is not UIElement element || e.NewValue is not DragAndDropControl dndControl) - { - throw new InvalidOperationException($"Can not set {e.NewValue} as handler. Handler needs to be of type DragAndDropControl."); - } - } - - #endregion - - /// - /// Attempts to retrieve the correct handler for the provided element.
- /// This may either be via the defined Handler property, or the element itself if no handler property was attached. - ///
- private static DragAndDropControl RetrieveHandler(DependencyObject obj) - { - if (obj.GetValue(HandlerProperty) is not DragAndDropControl handler) - { - if (obj is DragAndDropControl) - { - handler = (DragAndDropControl)obj; - } - else // Todo: last resort if ((handler = FindVisualParent(obj)!) == null) - { - throw new InvalidOperationException($"Could not find a DragAndDropControl for element {obj}. You may explicitly provide one using the DragAndDropControl.Handler attached property."); - } - } - - return handler; - } - - /// - /// Dynamic handler for the event. - /// Called when the mouse cursor moves while on an element. - /// This also calls the method in order to handle any specific behaviour during the operation. - /// - private static void HandleMouseMove(object sender, MouseEventArgs args) - { - if (args.LeftButton == MouseButtonState.Pressed) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - DragAndDropData data = new(element, element.DataContext); - DragDrop.DoDragDrop(element, data, DragDropEffects.All); - handler.OnElementDrag(element, args); - } - } - - /// - /// Dynamic handler for the event. - /// - private static void HandleGiveFeedback(object sender, GiveFeedbackEventArgs args) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - handler.OnElementDragGiveCursorFeedback(element, args); - } - - /// - /// Dynamic handler for the event. - /// - private static void HandleLeftMouseButtonUp(object sender, MouseButtonEventArgs args) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - handler.OnElementDragStop(element); - } - - /// - /// Dynamic handler for the event. - /// - private static void HandleDragOver(object sender, DragEventArgs args) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - handler.OnElementDragOver((DragAndDropData)args.Data.GetData(typeof(DragAndDropData)), element); - } - - /// - /// Dynamic handler for the event. - /// - private static void HandleDragLeave(object sender, DragEventArgs args) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - handler.OnElementDragLeave(element); - } - - /// - /// Dynamic handler for the event. - /// - private static void HandleDrop(object sender, DragEventArgs args) - { - FrameworkElement element = (FrameworkElement)sender; - DragAndDropControl handler = RetrieveHandler(element); - handler.OnElementDropped((DragAndDropData)args.Data.GetData(typeof(DragAndDropData)), element); - } - - private static T? FindVisualParent(DependencyObject child) where T : DependencyObject - { - // Todo: find a better solution to this, if there even is one. - // There are, in fact, many controls even in the WPF standard that do similar lookups, - // so I assume it can't be _that_ bad, but every clean solution kind of seems to need some ugly spot in it. :( - - DependencyObject parent = VisualTreeHelper.GetParent(child); - while (parent != null) - { - if (parent is T expected) return expected; - parent = VisualTreeHelper.GetParent(parent); - } - return null; - } -} diff --git a/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropData.cs b/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropData.cs deleted file mode 100644 index 6717f42..0000000 --- a/Qrakhen.TilingFrames/Controls/DragAndDrop/DragAndDropData.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DragAndDrop; - -public class DragAndDropData -{ - public FrameworkElement DraggedElement { get; } - public object DataModel { get; } - - public DragAndDropData(FrameworkElement draggedElement, object dataModel) - { - DraggedElement = draggedElement; - DataModel = dataModel; - } -} diff --git a/Qrakhen.TilingFrames/Controls/DropArea/DropArea.cs b/Qrakhen.TilingFrames/Controls/DropArea/DropArea.cs deleted file mode 100644 index 7f3dcbd..0000000 --- a/Qrakhen.TilingFrames/Controls/DropArea/DropArea.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea; - -public class DropArea : Control -{ - public static readonly DependencyProperty DropDecisionProperty = DependencyProperty - .RegisterAttached( - "Decision", - typeof(DropDecision), - typeof(DropArea)); - - public static DropDecision GetDropDecision(DependencyObject obj) => (DropDecision)obj.GetValue(DropDecisionProperty); - public static void SetDropDecision(DependencyObject obj, bool value) => obj.SetValue(DropDecisionProperty, value); -} - diff --git a/Qrakhen.TilingFrames/Controls/DropArea/DropAreaDecisionEvent.cs b/Qrakhen.TilingFrames/Controls/DropArea/DropAreaDecisionEvent.cs deleted file mode 100644 index dc0686e..0000000 --- a/Qrakhen.TilingFrames/Controls/DropArea/DropAreaDecisionEvent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea; - -public delegate void DropAreaDecisionEvent(DropArea sender, TilingHostControl host, DropDecision decision); diff --git a/Qrakhen.TilingFrames/Controls/DropArea/DropDecision.cs b/Qrakhen.TilingFrames/Controls/DropArea/DropDecision.cs deleted file mode 100644 index 99b9b2d..0000000 --- a/Qrakhen.TilingFrames/Controls/DropArea/DropDecision.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea; - -public enum DropDecision -{ - Center = 0, - Left = 1, - Top = 2, - Right = 3, - Bottom = 4, - Cancel = 5 -} \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Controls/HostControl.cs b/Qrakhen.TilingFrames/Controls/HostControl.cs deleted file mode 100644 index c3fded0..0000000 --- a/Qrakhen.TilingFrames/Controls/HostControl.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using Qrakhen.TilingFrames.Controls.DragAndDrop; -using Qrakhen.TilingFrames.Models; - -namespace Qrakhen.TilingFrames.Controls; - -/// -/// Control that represents the state of a , -/// with all the necessary interaction logic tied to it. -/// -public class HostControl : DragAndDropControl -{ - public TilingHost? Host => DataContext as TilingHost; - - static HostControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(HostControl), - new FrameworkPropertyMetadata(typeof(HostControl))); - } - - public HostControl() - { - - } - - #region DependencyProperties - - /*public Host Host - { - get => (Host)GetValue(HostProperty); - set => SetValue(HostProperty, value); - } - - public static DependencyProperty HostProperty = DependencyProperty - .Register(nameof(Host), - typeof(Host), - typeof(HostControl), - new PropertyMetadata(null));*/ - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/HostFrameControl.cs b/Qrakhen.TilingFrames/Controls/HostFrameControl.cs deleted file mode 100644 index 8f87b09..0000000 --- a/Qrakhen.TilingFrames/Controls/HostFrameControl.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using Qrakhen.TilingFrames.Models; - -namespace Qrakhen.TilingFrames.Controls; - -/// -/// Control that represents the state of a , -/// with all the necessary interaction logic tied to it. -/// -public class HostFrameControl : ContentControl -{ - static HostFrameControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(HostFrameControl), - new FrameworkPropertyMetadata(typeof(HostFrameControl))); - } - - public HostFrameControl() - { - - } - - #region DependencyProperties - - public HostFrame Frame { - get => (HostFrame)GetValue(FrameProperty); - set => SetValue(FrameProperty, value); - } - - public static DependencyProperty FrameProperty = DependencyProperty - .Register(nameof(Frame), - typeof(HostFrame), - typeof(HostFrameControl), - new PropertyMetadata(null)); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/PanelControl.cs b/Qrakhen.TilingFrames/Controls/PanelControl.cs deleted file mode 100644 index 2d34df1..0000000 --- a/Qrakhen.TilingFrames/Controls/PanelControl.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Qrakhen.TilingFrames.Models; -using System.Windows; -using System.Windows.Controls; - -namespace Qrakhen.TilingFrames.Controls; - -public class PanelControl : Control -{ - static PanelControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(PanelControl), - new FrameworkPropertyMetadata(typeof(PanelControl))); - } - - public PanelControl() - { - - } - - #region DependencyProperties - - public Orientation SplitOrientation => Panel.SplitOrientation; - - public TilingNode Alpha => Panel.Alpha; - public TilingNode? Beta => Panel.Beta; - - public Models.TilingPanel Panel { - get => (Models.TilingPanel)GetValue(PanelProperty); - set => SetValue(PanelProperty, value); - } - - public static DependencyProperty PanelProperty = DependencyProperty - .Register(nameof(Panel), - typeof(Models.TilingPanel), - typeof(PanelControl), - new PropertyMetadata(null)); - - public double SplitterWidth { - get => (double)GetValue(SplitterWidthProperty); - set => SetValue(SplitterWidthProperty, value); - } - - public static DependencyProperty SplitterWidthProperty = DependencyProperty - .Register( - nameof(SplitterWidth), - typeof(double), - typeof(PanelControl), - new PropertyMetadata(8.0)); - - #endregion -} - diff --git a/Qrakhen.TilingFrames/Controls/TilingGridAttachment.cs b/Qrakhen.TilingFrames/Controls/TilingGridAttachment.cs deleted file mode 100644 index d86b1c1..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingGridAttachment.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls -{ - /// - /// Used to force-update columns into rows when orientation is vertical - /// - public static class TilingGridAttachment - { - #region AlphaRow - public static readonly DependencyProperty AlphaRowProperty = - DependencyProperty.RegisterAttached( - "AlphaRow", - typeof(int), - typeof(TilingGridAttachment), - new PropertyMetadata(0, OnAlphaRowChanged)); - - public static int GetAlphaRow(DependencyObject obj) - => (int)obj.GetValue(AlphaRowProperty); - public static void SetAlphaRow(DependencyObject obj, int value) - => obj.SetValue(AlphaRowProperty, value); - - private static void OnAlphaRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is ContentPresenter presenter) - { - Grid.SetRow(presenter, (int)e.NewValue); - Grid.SetColumn(presenter, 0); // Always Column 0 for vertical split - } - } - #endregion - - #region BetaRow - public static readonly DependencyProperty BetaRowProperty = - DependencyProperty.RegisterAttached( - "BetaRow", - typeof(int), - typeof(TilingGridAttachment), - new PropertyMetadata(0, OnBetaRowChanged)); - - public static int GetBetaRow(DependencyObject obj) - => (int)obj.GetValue(BetaRowProperty); - public static void SetBetaRow(DependencyObject obj, int value) - => obj.SetValue(BetaRowProperty, value); - - private static void OnBetaRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is ContentPresenter presenter) - { - Grid.SetRow(presenter, (int)e.NewValue); - Grid.SetColumn(presenter, 0); // Always Column 0 for vertical split - } - } - #endregion - } -} diff --git a/Qrakhen.TilingFrames/Controls/TilingHostControl.cs b/Qrakhen.TilingFrames/Controls/TilingHostControl.cs deleted file mode 100644 index 21b71f9..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingHostControl.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using CopaData.FileInspector.GUI.TilingPanels.Controls.DragAndDrop; -using CopaData.FileInspector.GUI.TilingPanels.Controls.DropArea; -using CopaData.FileInspector.GUI.TilingPanels.Models; -using CopaData.FileInspector.GUI.ViewModels; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls; - -/// -/// Control that represents the state of a , -/// with all the necessary interaction logic tied to it. -/// -public class TilingHostControl : DragAndDropControl -{ - public TilingHost? Host => DataContext as TilingHost; - - static TilingHostControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingHostControl), - new FrameworkPropertyMetadata(typeof(TilingHostControl))); - } - - protected override void OnElementDragOver(DragAndDropData data, UIElement targetElement) - { - SetValue(IsDragTargetPropertyKey, true); - } - - protected override void OnElementDragLeave(UIElement targetElement) - { - SetValue(IsDragTargetPropertyKey, false); - } - - protected override void OnElementDropped(DragAndDropData data, UIElement targetElement) - { - if (data.DataModel is not TilingFrame frame) - { - // not for us - return; - } - - DropDecision decision = (DropDecision)targetElement.GetValue(DropArea.DropArea.DropDecisionProperty); - if (decision == DropDecision.Cancel) - return; - - TilingDirection direction = (TilingDirection)decision; // simple cast suffices here - if (direction == TilingDirection.None) { - TilingHost.Insert(TilingManagerViewModel.GlobalRoot, Host!, frame); - } else { - TilingPanel.Attach(TilingManagerViewModel.GlobalRoot, Host!, frame, direction); - } - } - - #region Dependency Properties - - /// - private static readonly DependencyPropertyKey IsDragTargetPropertyKey = - DependencyProperty.RegisterReadOnly( - nameof(IsDragTarget), - typeof(bool), - typeof(TilingHostControl), - new PropertyMetadata(false)); - - /// - public static readonly DependencyProperty IsDragTargetProperty = IsDragTargetPropertyKey.DependencyProperty; - - /// - /// Whether this is being a potential drop target at the moment. - /// - public bool IsDragTarget => (bool)GetValue(IsDragTargetProperty); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/TilingHostFrameControl.cs b/Qrakhen.TilingFrames/Controls/TilingHostFrameControl.cs deleted file mode 100644 index 969b149..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingHostFrameControl.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using CopaData.FileInspector.GUI.TilingPanels.Models; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls; - -/// -/// Control that represents the state of a , -/// with all the necessary interaction logic tied to it. -/// -public class TilingHostFrameControl : ContentControl -{ - static TilingHostFrameControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingHostFrameControl), - new FrameworkPropertyMetadata(typeof(TilingHostFrameControl))); - } - - public TilingHostFrameControl() - { - - } - - #region DependencyProperties - - public TilingFrame Frame - { - get => (TilingFrame)GetValue(FrameProperty); - set => SetValue(FrameProperty, value); - } - - public static DependencyProperty FrameProperty = DependencyProperty - .Register(nameof(Frame), - typeof(TilingFrame), - typeof(TilingHostFrameControl), - new PropertyMetadata(null)); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/TilingPanelControl.cs b/Qrakhen.TilingFrames/Controls/TilingPanelControl.cs deleted file mode 100644 index 7c44903..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingPanelControl.cs +++ /dev/null @@ -1,55 +0,0 @@ -using CopaData.FileInspector.GUI.TilingPanels.Models; -using System.Windows; -using System.Windows.Controls; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls; - -public class TilingPanelControl : Control -{ - static TilingPanelControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingPanelControl), - new FrameworkPropertyMetadata(typeof(TilingPanelControl))); - } - - public TilingPanelControl() - { - - } - - #region DependencyProperties - - public Orientation SplitOrientation => Panel.SplitOrientation; - - public TilingNode Alpha => Panel.Alpha; - public TilingNode? Beta => Panel.Beta; - - public TilingPanel Panel - { - get => (TilingPanel)GetValue(PanelProperty); - set => SetValue(PanelProperty, value); - } - - public static DependencyProperty PanelProperty = DependencyProperty - .Register(nameof(Panel), - typeof(TilingPanel), - typeof(TilingPanelControl), - new PropertyMetadata(null)); - - public double SplitterWidth - { - get => (double)GetValue(SplitterWidthProperty); - set => SetValue(SplitterWidthProperty, value); - } - - public static DependencyProperty SplitterWidthProperty = DependencyProperty - .Register( - nameof(SplitterWidth), - typeof(double), - typeof(TilingPanelControl), - new PropertyMetadata(6.0)); - - #endregion -} - diff --git a/Qrakhen.TilingFrames/Controls/TilingRoot.cs b/Qrakhen.TilingFrames/Controls/TilingRoot.cs deleted file mode 100644 index 4024e27..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingRoot.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Windows; -using System.Windows.Controls; - -namespace Qrakhen.TilingFrames.Controls; - -public class TilingRoot : Control -{ - static TilingRoot() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingRoot), - new FrameworkPropertyMetadata(typeof(TilingRoot))); - } - - #region Dependency Properties - - public double SplitterWidth { - get => (double)GetValue(SplitterWidthProperty); - set => SetValue(SplitterWidthProperty, value); - } - - public static DependencyProperty SplitterWidthProperty = DependencyProperty - .Register( - nameof(SplitterWidth), - typeof(double), - typeof(TilingRoot), - new PropertyMetadata(8.0)); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/TilingRootControl.cs b/Qrakhen.TilingFrames/Controls/TilingRootControl.cs deleted file mode 100644 index 7d3fa8e..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingRootControl.cs +++ /dev/null @@ -1,45 +0,0 @@ -using CopaData.FileInspector.GUI.TilingPanels.Models; -using System.Windows; -using System.Windows.Controls; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls; - -public class TilingRootControl : Control -{ - static TilingRootControl() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingRootControl), - new FrameworkPropertyMetadata(typeof(TilingRootControl))); - } - - #region Dependency Properties - - public double SplitterWidth - { - get => (double)GetValue(SplitterWidthProperty); - set => SetValue(SplitterWidthProperty, value); - } - - public static DependencyProperty SplitterWidthProperty = DependencyProperty - .Register( - nameof(SplitterWidth), - typeof(double), - typeof(TilingRootControl), - new PropertyMetadata(6.0)); - - public bool IsPrimaryRoot - { - get => (bool)GetValue(IsPrimaryRootProperty); - set => SetValue(IsPrimaryRootProperty, value); - } - - public static DependencyProperty IsPrimaryRootProperty = DependencyProperty - .Register( - nameof(IsPrimaryRoot), - typeof(bool), - typeof(TilingRootControl), - new PropertyMetadata(true)); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Controls/TilingWindow.cs b/Qrakhen.TilingFrames/Controls/TilingWindow.cs deleted file mode 100644 index e6e5e48..0000000 --- a/Qrakhen.TilingFrames/Controls/TilingWindow.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Windows; - -namespace CopaData.FileInspector.GUI.TilingPanels.Controls; - -/// -/// A dedicated window for hosting its own , to be used for pop-out interactions. -/// -public class TilingWindow : Window -{ - static TilingWindow() - { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(TilingWindow), - new FrameworkPropertyMetadata(typeof(TilingWindow))); - } - - #region Dependency Properties - - public double SplitterWidth - { - get => (double)GetValue(SplitterWidthProperty); - set => SetValue(SplitterWidthProperty, value); - } - - public static DependencyProperty SplitterWidthProperty = DependencyProperty - .Register( - nameof(SplitterWidth), - typeof(double), - typeof(TilingWindow), - new PropertyMetadata(6.0)); - - public TilingRootControl Root - { - get => (TilingRootControl)GetValue(RootProperty); - set => SetValue(RootProperty, value); - } - - public static DependencyProperty RootProperty = DependencyProperty - .Register( - nameof(Root), - typeof(TilingRootControl), - typeof(TilingWindow)); - - #endregion -} diff --git a/Qrakhen.TilingFrames/Converters/AlphaRatioConverter.cs b/Qrakhen.TilingFrames/Converters/AlphaRatioConverter.cs deleted file mode 100644 index f89feea..0000000 --- a/Qrakhen.TilingFrames/Converters/AlphaRatioConverter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; - -namespace CopaData.FileInspector.GUI.TilingPanels.Converters; - -/// -/// Converts a double between 0 and 1 to a star-length representation, -/// to be used in conjunction with . -/// -public class AlphaRatioConverter : MarkupExtension, IValueConverter -{ - public override object ProvideValue(IServiceProvider serviceProvider) => this; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is double ratio) - { - if (ratio <= double.Epsilon) - { - // If a ratio near zero, alpha is essentially hidden. - return new GridLength(0, GridUnitType.Star); - } - - if (ratio <= .5) - { - // With a ratio less than or equal to .5, alpha will be calculated as the minor component - return new GridLength(1, GridUnitType.Star); - } - - if (ratio >= 1) - { - // With a ratio near 1, alpha will be 1. - return new GridLength(1, GridUnitType.Star); - } - - // Returns the parts-per-ratio that alpha represents - return new GridLength((1 - ratio) / ratio, GridUnitType.Star); - } - - throw new ArgumentException($"Expected value to be ratio (double), god {value} instead."); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - => throw new NotImplementedException(); -} diff --git a/Qrakhen.TilingFrames/Converters/BetaRatioConverter.cs b/Qrakhen.TilingFrames/Converters/BetaRatioConverter.cs deleted file mode 100644 index be1dc40..0000000 --- a/Qrakhen.TilingFrames/Converters/BetaRatioConverter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; - -namespace CopaData.FileInspector.GUI.TilingPanels.Converters; - -/// -/// Converts a double between 0 and 1 to a star-length representation, -/// to be used in conjunction with . -/// -public class BetaRatioConverter : MarkupExtension, IValueConverter -{ - public override object ProvideValue(IServiceProvider serviceProvider) => this; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is double ratio) - { - if (ratio >= 1) - { - // If near 1, beta is essentially hidden. - return new GridLength(0, GridUnitType.Star); - } - - if (ratio >= .5) - { - // With a ratio larger than or equal to 0.5, beta is the lesser component of the two. - return new GridLength(1, GridUnitType.Star); - } - - if (ratio <= double.Epsilon) - { - // If near zero, beta covers the entire panel. - return new GridLength(1, GridUnitType.Star); - } - - // Return the parts-per-ratio that beta represents. - return new GridLength((1 - ratio) / ratio, GridUnitType.Star); - } - - throw new ArgumentException($"Expected value to be ratio (double), god {value} instead."); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - => throw new NotImplementedException(); -} diff --git a/Qrakhen.TilingFrames/Models/EmptyFrame.cs b/Qrakhen.TilingFrames/Models/EmptyFrame.cs deleted file mode 100644 index f13ff84..0000000 --- a/Qrakhen.TilingFrames/Models/EmptyFrame.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Initial empty frame, can be turned of by setting to false. -/// -public class EmptyFrame : TilingFrame -{ - public EmptyFrame() - { - HeaderText = "Empty"; - } -} - -/// -/// Todo: remove this later, only used to test the non-overriden case of a data template -/// -public class TestOverrideFrame : TilingFrame { public TestOverrideFrame() { HeaderText = "Testerinho"; } } diff --git a/Qrakhen.TilingFrames/Models/EmptyHost.cs b/Qrakhen.TilingFrames/Models/EmptyHost.cs deleted file mode 100644 index 0f0e716..0000000 --- a/Qrakhen.TilingFrames/Models/EmptyHost.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -public class EmptyHost : TilingHost -{ - public EmptyHost() : base(new EmptyFrame(), new TestOverrideFrame()) - { - } -} diff --git a/Qrakhen.TilingFrames/Models/FrameButtons.cs b/Qrakhen.TilingFrames/Models/FrameButtons.cs deleted file mode 100644 index 0773fef..0000000 --- a/Qrakhen.TilingFrames/Models/FrameButtons.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Flags for declaring the buttons on a -/// -[Flags] -public enum FrameButtons -{ - None = 0, - Close = 1 << 0, - Minimize = 1 << 1, - Pin = 1 << 2, - Split = 1 << 3, - PopOut = 1 << 4, - Options = 1 << 5, - Help = 1 << 6, - - Default = Close | Pin | PopOut -} diff --git a/Qrakhen.TilingFrames/Models/HostFrame.cs b/Qrakhen.TilingFrames/Models/HostFrame.cs deleted file mode 100644 index a79e742..0000000 --- a/Qrakhen.TilingFrames/Models/HostFrame.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Qrakhen.TilingFrames.Models; - -/// -/// Base data class for content that is displayed for a 's tabs or stand-alone pop-out windows.
-/// Anything that you can physically see from the root is a .
-/// Serving a header text, pin state and button configuration properties. -///
-public abstract class HostFrame : ObservableObject -{ - /// - private string _headerText = string.Empty; - /// - /// The title of the tab that will be displayed inside the tab button. - /// - public string HeaderText { - get => _headerText; - set => SetField(ref _headerText, value); - } - - /// - private FrameButtons _frameButtons = FrameButtons.Default; - /// - /// The buttons to be displayed next to the header. - /// - public FrameButtons FrameButtons { - get => _frameButtons; - set => SetField(ref _frameButtons, value); - } - - /// - private bool _isPinned = false; - /// - /// The title of the tab that will be displayed inside the tab button. - /// - public bool IsPinned { - get => _isPinned; - set => SetField(ref _isPinned, value); - } - - public virtual object? GetOptions() => null; - public virtual void SetOptions(object? options) { } - - public virtual object? GetHelp() => null; -} diff --git a/Qrakhen.TilingFrames/Models/ObservableObject.cs b/Qrakhen.TilingFrames/Models/ObservableObject.cs deleted file mode 100644 index f26bbaa..0000000 --- a/Qrakhen.TilingFrames/Models/ObservableObject.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace Qrakhen.TilingFrames.Models; - -public abstract class ObservableObject : INotifyPropertyChanged -{ - public event PropertyChangedEventHandler? PropertyChanged; - - protected bool SetField([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null) - { - if (EqualityComparer.Default.Equals(field, newValue)) - return false; - - field = newValue; - OnPropertyChanged(propertyName); - return true; - } - - protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } -} \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Models/TilingDirection.cs b/Qrakhen.TilingFrames/Models/TilingDirection.cs deleted file mode 100644 index 1983b54..0000000 --- a/Qrakhen.TilingFrames/Models/TilingDirection.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Used to declare where a new shall be placed when tiling. -/// -public enum TilingDirection -{ - None = 0, - /// - /// Left side of the target host, the new child becomes alpha and the target host moves to beta. - /// - Left = 1, - /// - /// The top of the target host, the new child becomes alpha and the target host moves to beta. - /// - Top = 2, - /// - /// Right side of the target host, the new child becomes beta. - /// - Right = 3, - /// - /// The bottom of the target host, the new child becomes beta. - /// - Bottom = 4 -} \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Models/TilingFrame.cs b/Qrakhen.TilingFrames/Models/TilingFrame.cs deleted file mode 100644 index 13bb199..0000000 --- a/Qrakhen.TilingFrames/Models/TilingFrame.cs +++ /dev/null @@ -1,60 +0,0 @@ -using CopaData.FileInspector.GUI.Models; - -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Base data class for content that is displayed for a 's tabs or stand-alone pop-out windows.
-/// Anything that you can physically see from the root is a .
-/// Serving a header text, pin state and button configuration properties. -///
-public abstract class TilingFrame : Observable -{ - /// - private string _headerText = string.Empty; - /// - /// The title of the tab that will be displayed inside the tab button. - /// - public string HeaderText - { - get => _headerText; - set => SetField(ref _headerText, value); - } - - /// - private FrameButtons _frameButtons = FrameButtons.Default; - /// - /// The buttons to be displayed next to the header. - /// - public FrameButtons FrameButtons - { - get => _frameButtons; - set => SetField(ref _frameButtons, value); - } - - /// - private bool _isPinned = false; - /// - /// The title of the tab that will be displayed inside the tab button. - /// - public bool IsPinned - { - get => _isPinned; - set => SetField(ref _isPinned, value); - } - - /// - private TilingHost? _parent; - /// - /// The parent hosting this frame. - /// - public TilingHost? Parent - { - get => _parent; - set => SetField(ref _parent, value); - } - - public virtual object? GetOptions() => null; - public virtual void SetOptions(object? options) { } - - public virtual object? GetHelp() => null; -} diff --git a/Qrakhen.TilingFrames/Models/TilingHost.cs b/Qrakhen.TilingFrames/Models/TilingHost.cs deleted file mode 100644 index 72b1341..0000000 --- a/Qrakhen.TilingFrames/Models/TilingHost.cs +++ /dev/null @@ -1,94 +0,0 @@ -using CopaData.FileInspector.GUI.Models; -using System.Collections.ObjectModel; -using System.Collections.Specialized; - -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Host node that by paradigm is located only at the very end of the tree's branches.
-/// Hosts may contain at least one central control, or multiple tabs of controls.
-/// Note that tab contents must _not_ be any tiling controls, as that would break hierarchy and branching logic.
-/// Everything above a will be a by definition. -///
-public class TilingHost : TilingNode -{ - public ObservableCollection Frames { get; } = []; - - private TilingFrame? _activeFrame; - public TilingFrame? ActiveFrame - { - get => _activeFrame; - set => SetField(ref _activeFrame, value); - } - - public bool ShowTabs => Frames.Count > 1; - - public bool IsEmpty => Frames.Count == 0; - - public TilingHost(params TilingFrame[] frames) - { - if (frames != null && frames.Length > 0) - { - Frames = new ObservableCollection(frames); - ActiveFrame = Frames[^1]; - foreach (var frame in Frames) - { - frame.Parent = this; - } - } - - Frames.CollectionChanged += OnFramesItemsChanged; - } - - private void OnFramesItemsChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - if (e.NewItems != null && e.NewItems.Count > 0) - { - // Make the last item active - ActiveFrame = e.NewItems[^1] as TilingFrame; - foreach (var item in e.NewItems) - { - if (item is TilingFrame frame) - { - // We're using a reference here as this is the only place that always listens to updates. - frame.Parent = this; - } - } - } - else if (e.OldItems != null && e.OldItems.Count > 0) - { - if (ActiveFrame != null && !Frames.Contains(ActiveFrame)) - { - // Previous active tab got removed, revert back to the most recent tab or set ActiveTab to null if tabs are empty. - if (Frames.Count > 0) - { - ActiveFrame = Frames[^1]; - } - else - { - ActiveFrame = null; - } - } - } - } - - /// - /// Inserts into as a new tab - /// after being dropped in the center region by the user.
- /// If the previous host only had one frame inside it, it will be detached from its parent. - /// Otherwise, only the frame will be removed and inserted into 's frames. - ///
- public static void Insert(TilingPanel rootPanel, TilingHost targetHost, TilingFrame frame) - { - if (frame.Parent?.Frames.Count < 2) - { - TilingPanel.Detach(rootPanel, frame.Parent); - } - else - { - frame.Parent?.Frames.Remove(frame); - } - - targetHost.Frames.Add(frame); - } -} diff --git a/Qrakhen.TilingFrames/Models/TilingMethod.cs b/Qrakhen.TilingFrames/Models/TilingMethod.cs deleted file mode 100644 index 22eba42..0000000 --- a/Qrakhen.TilingFrames/Models/TilingMethod.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Declares the intent of dropping a host into another. -/// -public enum TilingMethod -{ - /// - /// In the case of the host being placed as a split next to the target host. - /// - Split = 0, - /// - /// In case of the new host becoming a tab of the target host, rather than splitting. - /// - Drop = 1 -} diff --git a/Qrakhen.TilingFrames/Models/TilingNode.cs b/Qrakhen.TilingFrames/Models/TilingNode.cs deleted file mode 100644 index 0efb773..0000000 --- a/Qrakhen.TilingFrames/Models/TilingNode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using CopaData.FileInspector.GUI.Models; - -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// Base node for the binary tree structure to work.
-/// A tiling node is either a , which has a reference to two s.
-/// Those nodes may then be additional s, or, when at the end of the branch, a . -///
-public abstract class TilingNode : Observable; diff --git a/Qrakhen.TilingFrames/Models/TilingPanel.cs b/Qrakhen.TilingFrames/Models/TilingPanel.cs deleted file mode 100644 index 7e63153..0000000 --- a/Qrakhen.TilingFrames/Models/TilingPanel.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System.Windows.Controls; - -namespace CopaData.FileInspector.GUI.TilingPanels.Models; - -/// -/// This class represents a branch in the tree structure, having two children,
-/// which can be either another - in the case of the tree branching onwards -
-/// or a , if we're at the end of a branch (a so-called leaf node).
-/// A panel contains information about how its children and -/// are separated by exposing an and split ratio. -///
-/// -/// The entire attaching and detaching logic is handled from static methods in this class for brevity.
-/// Those methods are and . -///
-public class TilingPanel : TilingNode -{ - /// - private Orientation _splitOrientation = Orientation.Horizontal; - /// - /// The orientation of the split, so either vertical or horizontal. - /// - public Orientation SplitOrientation - { - get => _splitOrientation; - set => SetField(ref _splitOrientation, value); - } - - /// - private double _splitRatio = 0.5; - /// - /// The width or height ratio of the split, with 0.5 being the exact middle. - /// - public double SplitRatio - { - get => _splitRatio; - set => SetField(ref _splitRatio, Math.Min(1, Math.Max(0, value))); - } - - /// - private double _cutoffLength = 256; - /// - /// TODO: Move this into the view/control, it really got nothing in common with the model i think. - /// The width or height of alpha in pixels, will be initialized with 50% of the panel's available width. - /// - public double CutoffLength - { - get => _cutoffLength; - set => SetField(ref _cutoffLength, Math.Min(1, Math.Max(0, value))); - } - - /// - private TilingNode _alpha; - /// - /// 'Alpha is always set' is an enforced paradigm, so it will always point to an existing node. - /// - public TilingNode Alpha - { - get => _alpha; - private set => SetField(ref _alpha, value); - } - - /// - private TilingNode? _beta; - /// - /// The beta node may be null, for example if a panel is not yet split. - /// - public TilingNode? Beta - { - get => _beta; - private set => SetField(ref _beta, value); - } - - /// - /// Whether this panel is split in two, so whether beta is not null. - /// - public bool IsSplit => Beta != null; - - public TilingPanel(TilingNode alpha, TilingNode? beta = null) - : base() - { - _alpha = alpha; - _beta = beta; - } - - /// - /// Traverses down the entire tree from until - /// is encountered, returning its direct parent panel. - /// - /// - /// This may not be the quickest approach, but we're looking at UI tiling panels with perhaps 20 children at most. - /// - public static TilingPanel? GetParentPanel(TilingPanel rootPanel, TilingNode node) - { - if (rootPanel.Alpha == node || rootPanel.Beta == node) - { - return rootPanel; - } - - TilingPanel? parent = null; - if (rootPanel.Alpha is TilingPanel alphaPanel) - { - parent = GetParentPanel(alphaPanel, node); - } - - if (parent == null && rootPanel.Beta is TilingPanel betaPanel) - { - parent = GetParentPanel(betaPanel, node); - } - - return parent; - } - - /// - /// - /// Attaches to 's parent as a new .
- /// The parent will be looked up by traversing the tree from .
- /// The following ordered rules apply when attaching: - ///
- /// - /// 1. If 's parent has an open beta slot, will become the beta node, wrappped in a new host.
- /// 2. If one of the parent's child nodes is an , will be inserted
- /// 3. If none of the above apply, the parent's beta node will be split into a new tiling panel. - ///
- ///
- /// - /// All of these actions will automatically split the parent panel.
- /// If had a parent assigned previously, that parent will be detached from the branch first.
- /// If more frames are present in 's parent, only that frame will be removed from it,
- /// and a new is created, containing only that frame.
- /// Note that all passed elements have to be children to the same tree. - /// In order to attach a to a foreign tree, it first and insert it to the foreign host. - ///
- /// The root to start branch traversal from. - /// The host to be split. - /// - /// The frame to be attached inside a new host to 's parent. - /// If set to null, an will be created in its place. - /// - /// The direction at which to drop relative to . - /// The that now contains and . - public static TilingPanel Attach(TilingPanel rootPanel, - TilingHost targetHost, - TilingFrame? newFrame, - TilingDirection direction) - { - // This method looks way more complicated than it actually is, - // all I wanted to achieve is one centralized place where branching and child assignments happen, - // as tree structures are notoriously hard to navigate and handle when putting references everywhere. - // It really boils down to a simple three-step process: - // 1. Get or create correct host to attach, - // 2. Find out where to place the new host (Alpha or Beta, depending on tiling direction), - // 3. Create branch to be attached, or attach to free slot of parent. - // Todo: This note is in place for the reviewer to have a bit more context. Remove when done. - - TilingPanel? parent = GetParentPanel(rootPanel, targetHost); - if (parent == null) - { - throw new NullReferenceException($"Detached target: Could not find a parent for {targetHost} in any of {rootPanel}'s child nodes."); - } - - TilingHost? newHost; - if (newFrame == null) - { - // Substitute for the split. - newHost = new EmptyHost(); - } - else - { - if (newFrame.Parent?.Frames.Count < 2) - { - // Detach newFrame's host from its parent. - Detach(rootPanel, newFrame.Parent); - } - - newHost = new TilingHost([ newFrame ]); - } - - // I tried formulating an explanation for this step but I simply can't. - // "Left or Top go alpha, Bottom or Right go beta." - bool newHostIsAlpha = direction is TilingDirection.Left or TilingDirection.Top; - - // Check whether there's a free beta slot in the parent to use - if (parent.Beta == null || parent.Beta is EmptyHost emptyHost) - { - if (newHostIsAlpha) - { - // Open beta slot but newHost wants to be in alpha slot, simply swap and attach. - parent.Beta = parent.Alpha; - parent.Alpha = newHost; - } - else - { - // Open beta slot means we just put newHost there, no branching needed. - parent.Beta = newHost; - } - return parent; - } - - // Create a new branch to hold both target- and newHost. - TilingPanel branch; - if (direction is TilingDirection.Left or TilingDirection.Top) - { - branch = new TilingPanel(newHost, targetHost); - } - else - { - branch = new TilingPanel(targetHost, newHost); - } - - // Assign new branch to correct chíld node. - if (parent.Alpha == targetHost) - { - parent.Alpha = branch; - } - else if (parent.Beta == targetHost) - { - parent.Beta = branch; - } - - return branch; - } - - /// - /// Detaches from its parent node, which will be looked up by traversing the .
- /// If the child happened to be the alpha value, the beta value will be moved into the alpha slot to ensure alpha always having a value.
- /// If the child's parent node ends up having no children left, which would violate the 'alpha is always set' rule,
- /// that node itself will also be detached in order to prevent trailing zombie nodes. - ///
- public static void Detach(TilingPanel rootPanel, TilingNode child) - { - TilingPanel? parent = GetParentPanel(rootPanel, child); - if (parent == null) - { - return; // Already an orphan. - } - - if (parent.Alpha == child) - { - if (parent.Beta != null) - { - // Move beta over to alpha if beta has a value. - parent.Alpha = parent.Beta; - parent.Beta = null; - } - else - { - // Both branches have detached, which means we'll detach the parent itself - // in order to prevent empty, trailing panels with no children. - Detach(rootPanel, child); - } - } - else - { - // Child was the beta node, so we simply null it. - parent.Beta = null; - } - } -} diff --git a/Qrakhen.TilingFrames/README.md b/Qrakhen.TilingFrames/README.md index 815dc7e..dbd3d32 100644 --- a/Qrakhen.TilingFrames/README.md +++ b/Qrakhen.TilingFrames/README.md @@ -1,6 +1,6 @@ -# TilingPanels -## Tiling window manager control library for WPF -### CopaData.Ui.Wpf.TilingPanels +# TilingFrames +## Tiling (Docking) window manager library for WPF +### Qrakhen.TilingFramess Cool Library to have tiling panels. Is that not cool? diff --git a/Qrakhen.TilingFrames/Themes/Colors.xaml b/Qrakhen.TilingFrames/Themes/Colors.xaml deleted file mode 100644 index 6b9ec96..0000000 --- a/Qrakhen.TilingFrames/Themes/Colors.xaml +++ /dev/null @@ -1,36 +0,0 @@ - - - #242527 - #28292c - #161718 - #1c1d1f - #323336 - #37393c - #727478 - #84868b - #fcfeff - #97999c - #32ce96 - #48faaf - #249672 - #249672 - #ef4232 - - - - - - - - - - - - - - - - - - diff --git a/Qrakhen.TilingFrames/Themes/Controls/DropArea.xaml b/Qrakhen.TilingFrames/Themes/Controls/DropArea.xaml deleted file mode 100644 index 43fb407..0000000 --- a/Qrakhen.TilingFrames/Themes/Controls/DropArea.xaml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Themes/Controls/Host.xaml b/Qrakhen.TilingFrames/Themes/Controls/Host.xaml deleted file mode 100644 index 4f54c8c..0000000 --- a/Qrakhen.TilingFrames/Themes/Controls/Host.xaml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Themes/Controls/HostFrame.xaml b/Qrakhen.TilingFrames/Themes/Controls/HostFrame.xaml deleted file mode 100644 index d81d10e..0000000 --- a/Qrakhen.TilingFrames/Themes/Controls/HostFrame.xaml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - No Template override for the provided HostFrame model found. - You need to override the data template of your host frame like so: - - -<DataTemplate DataType="{x:Type YourFrameModel}"> - <TextBlock Text="This is my model view! :)" /> -</DataTemplate> - - - - - - - - - - Drag or Drop something here :) - - - - - \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Themes/Controls/Panel.xaml b/Qrakhen.TilingFrames/Themes/Controls/Panel.xaml deleted file mode 100644 index c97fdaf..0000000 --- a/Qrakhen.TilingFrames/Themes/Controls/Panel.xaml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Themes/Controls/TilingRoot.xaml b/Qrakhen.TilingFrames/Themes/Controls/TilingRoot.xaml deleted file mode 100644 index a9e3c13..0000000 --- a/Qrakhen.TilingFrames/Themes/Controls/TilingRoot.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Qrakhen.TilingFrames/Themes/Templates/Generic.xaml b/Qrakhen.TilingFrames/Themes/Templates/Generic.xaml deleted file mode 100644 index fe0e561..0000000 --- a/Qrakhen.TilingFrames/Themes/Templates/Generic.xaml +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/Qrakhen.TilingFrames/Themes/Themes.xaml b/Qrakhen.TilingFrames/Themes/Themes.xaml deleted file mode 100644 index ac43302..0000000 --- a/Qrakhen.TilingFrames/Themes/Themes.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file