From: Jean-Philippe Bruyère Date: Thu, 27 Feb 2025 00:16:26 +0000 (+0100) Subject: wip X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=9076776696efe1300efd339c999dfe49a62eb7ee;p=jp%2Fcrow.git wip --- diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..adb261b8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,225 @@ +--- +Language: CSharp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: Never +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/Backends/CairoBackend/Crow.CairoBackend.csproj b/Backends/CairoBackend/Crow.CairoBackend.csproj index 50563bd7..6b15a45e 100644 --- a/Backends/CairoBackend/Crow.CairoBackend.csproj +++ b/Backends/CairoBackend/Crow.CairoBackend.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 - 1.2.0 + 1.2.1 $(AssemblyVersion)-beta C.R.O.W Cairo Backend diff --git a/Backends/CairoBackend/src/Context.cs b/Backends/CairoBackend/src/Context.cs index f9af1bf1..4472f650 100644 --- a/Backends/CairoBackend/src/Context.cs +++ b/Backends/CairoBackend/src/Context.cs @@ -266,8 +266,12 @@ namespace Crow.CairoBackend => Rectangle (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); public void Rectangle (PointD p, double width, double height) => Rectangle (p.X, p.Y, width, height); - public void Rectangle (double x, double y, double width, double height) - => NativeMethods.cairo_rectangle (handle, x, y, width, height); + public void Rectangle (double x, double y, double width, double height) { + if (width > 0 && height > 0) + NativeMethods.cairo_rectangle (handle, x, y, width, height); + else + System.Diagnostics.Debug.WriteLine("cairo: invalid rectangle"); + } public void ClosePath () => NativeMethods.cairo_close_path (handle); public Path CopyPath () => new Path (NativeMethods.cairo_copy_path (handle)); public Path CopyPathFlat () => new Path (NativeMethods.cairo_copy_path_flat (handle)); diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index f73c69e8..9fdc56e0 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -67,7 +67,7 @@ Crow.Cursors.%(Filename) - + @@ -77,4 +77,10 @@ + + + + <_Parameter1>1000 + + diff --git a/Crow/Default.style b/Crow/Default.style index 98c5276b..473f4070 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -265,7 +265,7 @@ HSVSpinner { } TxtInFileDialog { Margin = "1"; - Font = "droid, 12"; + Font = "mono, 12"; } CheckBoxAlt { Template= "#Crow.CheckBox2.template"; @@ -305,6 +305,9 @@ HScrollBar { Width = "Inherit"; Orientation = "Horizontal"; } +ScrollingListBox { + Template= "#Crow.ScrollingListBox.template"; +} EnumSelector { Width = "Fit"; Height = "Fit"; diff --git a/Crow/Properties/AssemblyInfo.cs b/Crow/Properties/AssemblyInfo.cs deleted file mode 100644 index d8646524..00000000 --- a/Crow/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -[assembly: Crow.Crow ] - diff --git a/Crow/src/CrowAssemblyAttribute.cs b/Crow/src/CrowAssemblyAttribute.cs index 647a84fe..d09e348b 100644 --- a/Crow/src/CrowAssemblyAttribute.cs +++ b/Crow/src/CrowAssemblyAttribute.cs @@ -2,16 +2,22 @@ // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; -namespace Crow +namespace Crow { +/// +/// Add this attribute to an assembly to have it search for Crow ressources (.style, images, templates,...) +/// +/// +/// By default, only the entry assembly and the crow assembly will be searched for resources. +/// +[AttributeUsage (AttributeTargets.Assembly)] + +public class CrowAssemblyPriority : Attribute { - /// - /// Add this attribute to an assembly to have it search for Crow ressources (.style, images, templates,...) - /// - /// - /// By default, only the entry assembly and the crow assembly will be searched for resources. - /// - [AttributeUsage (AttributeTargets.Assembly)] - public class CrowAttribute : Attribute - { + public int Priority = 1000; + public CrowAssemblyPriority(string priority) { + if (int.TryParse(priority, out int pri)) { + Priority = pri; + } } } +} diff --git a/Crow/src/DebugUtils/DebugLogger.cs b/Crow/src/DebugUtils/DebugLogger.cs index c859fef9..f7078ed8 100644 --- a/Crow/src/DebugUtils/DebugLogger.cs +++ b/Crow/src/DebugUtils/DebugLogger.cs @@ -171,12 +171,23 @@ lock (logMutex) { if (!logevt (evtType)) return; - lock (logMutex) { - chrono.Stop (); - DbgEvent evt = addEventInternal (evtType, data); - evt.Message = message; - chrono.Start (); - } + Monitor.Enter (logMutex); + chrono.Stop (); + DbgEvent evt = addEventInternal (evtType, data); + evt.Message = message; + chrono.Start (); + Monitor.Exit(logMutex); + + if (ConsoleOutput) { + if (evt.type.HasFlag (DbgEvtType.Error)) { + Console.ForegroundColor = ConsoleColor.Red; + } + if (evt is DbgWidgetEvent we) { + Console.WriteLine ($"{evt.Print()} {Widget.GraphicObjects[we.InstanceIndex]}"); + } else + Console.WriteLine ($"{evt.Print()}"); + Console.ResetColor (); + } #endif } @@ -202,16 +213,7 @@ lock (logMutex) { else evt = new DbgEvent (chrono.ElapsedTicks, evtType); - if (ConsoleOutput) { - if (evt.type.HasFlag (DbgEvtType.Error)) { - Console.ForegroundColor = ConsoleColor.Red; - } - if (evt is DbgWidgetEvent we) - Console.WriteLine ($"{evt.Print()} {Widget.GraphicObjects[we.InstanceIndex]}"); - else - Console.WriteLine ($"{evt.Print()}"); - Console.ResetColor (); - } else + if (!ConsoleOutput) curEventList.Add (evt); return evt; } @@ -362,7 +364,6 @@ lock (logMutex) { }*/ } startedEvents.Pop(); - } } } diff --git a/Crow/src/ExtensionsMethods.cs b/Crow/src/ExtensionsMethods.cs index 41c31a64..ed226b8a 100644 --- a/Crow/src/ExtensionsMethods.cs +++ b/Crow/src/ExtensionsMethods.cs @@ -15,6 +15,17 @@ namespace Crow { public static class ExtensionsMethods { + #region CrowAssemblyPriority + internal static bool TryGetCrowAssemblyPriority(this Assembly a, out int priority) { + priority = -1; + if (a.GetCustomAttributes(typeof(CrowAssemblyPriority)).FirstOrDefault() is CrowAssemblyPriority cap) { + priority = cap.Priority; + return true; + } + return false; + } + #endregion + #region Cairo extensions public static void Rectangle(this IContext ctx, Rectangle r, double stroke = 0.0) diff --git a/Crow/src/Fill/BmpPicture.cs b/Crow/src/Fill/BmpPicture.cs index 7cf8983c..b4ad651e 100644 --- a/Crow/src/Fill/BmpPicture.cs +++ b/Crow/src/Fill/BmpPicture.cs @@ -33,6 +33,7 @@ namespace Crow /// load the image for rendering from the path given as argument /// public override void load (Interface iFace) { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"BMPPicture.load:{Path}"); if (iFace.sharedPictures.ContainsKey (Path)) { sharedPicture sp = iFace.sharedPictures[Path]; image = (byte[])sp.Data; @@ -45,106 +46,24 @@ namespace Crow iFace.sharedPictures[Path] = new sharedPicture (image, Dimensions); } } - - //load image via System.Drawing.Bitmap, cairo load png only - /*void loadBitmap (System.Drawing.Bitmap bitmap) - { - if (bitmap == null) - return; - - System.Drawing.Imaging.BitmapData data = bitmap.LockBits - (new System.Drawing.Rectangle (0, 0, bitmap.Width, bitmap.Height), - System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - Dimensions = new Size (bitmap.Width, bitmap.Height); - - int stride = data.Stride; - int bitmapSize = Math.Abs (data.Stride) * bitmap.Height; - - image = new byte[bitmapSize]; - System.Runtime.InteropServices.Marshal.Copy (data.Scan0, image, 0, bitmapSize); - - bitmap.UnlockBits (data); - }*/ + public override void LoadFromStream (Interface iFace, Stream stream) { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"BMPPicture.LoadFromStream:{Path}"); + image = iFace.Backend.LoadBitmap (stream, out Size dimensions); + Dimensions = dimensions; + iFace.sharedPictures[Path] = new sharedPicture (image, Dimensions); + } #region implemented abstract members of Fill public override bool IsLoaded => image != null; - public override void SetAsSource (Interface iFace, IContext ctx, Rectangle bounds = default(Rectangle)) - { - if (image == null) - load (iFace); - - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled){ - widthRatio = (float)bounds.Width / Dimensions.Width; - heightRatio = (float)bounds.Height / Dimensions.Height; - } - - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { - using (IContext gr = iFace.Backend.CreateContext (tmp)) { - gr.Translate (bounds.Left, bounds.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); - - using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { - gr.SetSource (imgSurf, 0,0); - gr.Paint (); - } - } - ctx.SetSource (tmp); - } - } #endregion - /// - /// paint the image in the rectangle given in arguments according - /// to the Scale and keepProportion parameters. - /// - /// drawing Backend context - /// bounds of the target surface to paint - /// used for svg only - public override void Paint (Interface iFace, IContext gr, Rectangle rect, string subPart = "") - { - if (image == null) - load (iFace); - - double widthRatio = 1; - double heightRatio = 1; - - if (Scaled){ - widthRatio = (double)rect.Width / Dimensions.Width; - heightRatio = (double)rect.Height / Dimensions.Height; - } - - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - gr.SaveTransformations (); - - gr.Translate (rect.Left,rect.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate ((rect.Width/widthRatio - Dimensions.Width)/2, (rect.Height/heightRatio - Dimensions.Height)/2); - + protected override void Render(Interface iFace, IContext gr, string subPart = "") + { using (ISurface imgSurf = iFace.Backend.CreateSurface (image, Dimensions.Width, Dimensions.Height)) { gr.SetSource (imgSurf, 0,0); gr.Paint (); } - - gr.RestoreTransformations (); - } + } } } diff --git a/Crow/src/Fill/Picture.cs b/Crow/src/Fill/Picture.cs index b5655c1a..a2c67613 100644 --- a/Crow/src/Fill/Picture.cs +++ b/Crow/src/Fill/Picture.cs @@ -8,6 +8,8 @@ using System.IO; using System.Collections.Generic; using Drawing2D; +using System.Globalization; +using Crow.DebugLogger; namespace Crow { @@ -66,16 +68,70 @@ namespace Crow } #endregion + void init(Interface iFace, ref Rectangle rect, out float widthRatio, out float heightRatio) { + if (!IsLoaded) + load (iFace); + + widthRatio = 1f; + heightRatio = 1f; + + if (Scaled) { + widthRatio = (float)rect.Width / Dimensions.Width; + heightRatio = (float)rect.Height / Dimensions.Height; + if (KeepProportions) { + if (widthRatio < heightRatio) + heightRatio = widthRatio; + else + widthRatio = heightRatio; + } + } + } + /// - /// abstract method to paint the image in the rectangle given in arguments according + /// paint the picture in the rectangle given in arguments according /// to the Scale and keepProportion parameters. /// /// drawing Backend context - /// bounds of the target surface to paint - /// used for svg only - public abstract void Paint(Interface iFace, IContext ctx, Rectangle rect, string subPart = ""); + /// bounds of the target surface to paint + /// limit rendering to this coma separated list of svg part identified with their svg 'id' attribute. + public void Paint (Interface iFace, IContext gr, Rectangle bounds, string subPart = "") + { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"{Path}[Picture.Paint:]"); + + init(iFace, ref bounds, out float widthRatio, out float heightRatio); + + gr.SaveTransformations (); + + gr.Translate (bounds.Left,bounds.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate (((float)bounds.Width/widthRatio - Dimensions.Width)/2f, ((float)bounds.Height/heightRatio - Dimensions.Height)/2f); + + Render(iFace, gr, subPart); + + gr.RestoreTransformations (); + } + public override void SetAsSource (Interface iFace, IContext ctx, Rectangle bounds = default(Rectangle)) + { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"{Path} [Picture.SetAsSource]"); + + init(iFace, ref bounds, out float widthRatio, out float heightRatio); + + using (ISurface tmp = iFace.Backend.CreateSurface (bounds.Width, bounds.Height)) { + using (IContext gr = iFace.Backend.CreateContext (tmp)) { + gr.Translate (bounds.Left, bounds.Top); + gr.Scale (widthRatio, heightRatio); + gr.Translate ((bounds.Width/widthRatio - Dimensions.Width)/2, (bounds.Height/heightRatio - Dimensions.Height)/2); + + Render(iFace, gr); + } + ctx.SetSource (tmp); + } + } + //final rendering on a scaled and configured context + protected abstract void Render(Interface iFace, IContext gr, string subPart = ""); public abstract bool IsLoaded { get; } public abstract void load (Interface iface); + public abstract void LoadFromStream (Interface iface, Stream stream); #region Operators public static implicit operator Picture(string path) => Parse (path) as Picture; public static implicit operator string(Picture _pic) => _pic == null ? null : _pic.Path; @@ -88,6 +144,8 @@ namespace Crow Picture _pic = null; + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"Picture.parse:{path}"); + if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) _pic = new SvgPicture (path); else diff --git a/Crow/src/Fill/SvgPicture.cs b/Crow/src/Fill/SvgPicture.cs index e2c73bbd..0fe954af 100644 --- a/Crow/src/Fill/SvgPicture.cs +++ b/Crow/src/Fill/SvgPicture.cs @@ -31,6 +31,7 @@ namespace Crow public override void load (Interface iFace) { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"SVGPicture.load:{Path}"); if (iFace.sharedPictures.ContainsKey (Path)) { sharedPicture sp = iFace.sharedPictures [Path]; hSVG = (ISvgHandle)sp.Data; @@ -42,8 +43,14 @@ namespace Crow Dimensions = hSVG.Dimensions; iFace.sharedPictures [Path] = new sharedPicture (hSVG, Dimensions); } - + public override void LoadFromStream (Interface iFace, Stream stream) { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"SVGPicture.LoadFromStream:{Path}"); + hSVG = iFace.Backend.LoadSvg (stream); + Dimensions = hSVG.Dimensions; + iFace.sharedPictures [Path] = new sharedPicture (hSVG, Dimensions); + } public void LoadSvgFragment (Interface iface, string fragment) { + DbgLogger.AddEventWithMsg(DbgEvtType.Ressources, $"SVGPicture.LoadSvgFragment:{fragment}"); hSVG = iface.Backend.LoadSvg (fragment); Dimensions = hSVG.Dimensions; } @@ -52,7 +59,7 @@ namespace Crow public override bool IsLoaded => hSVG != null; public override void SetAsSource (Interface iFace, IContext ctx, Rectangle bounds = default(Rectangle)) { - if (hSVG == null) + if (!IsLoaded) load (iFace); float widthRatio = 1f; @@ -82,38 +89,9 @@ namespace Crow } #endregion - /// - /// paint the image in the rectangle given in arguments according - /// to the Scale and keepProportion parameters. - /// - /// drawing Backend context - /// bounds of the target surface to paint - /// limit rendering to this coma separated list of svg part identified with their svg 'id' attribute. - public override void Paint (Interface iFace, IContext gr, Rectangle rect, string subPart = "") - { - if (hSVG == null) - load (iFace); - - float widthRatio = 1f; - float heightRatio = 1f; - - if (Scaled) { - widthRatio = (float)rect.Width / Dimensions.Width; - heightRatio = (float)rect.Height / Dimensions.Height; - } - if (KeepProportions) { - if (widthRatio < heightRatio) - heightRatio = widthRatio; - else - widthRatio = heightRatio; - } - - gr.Save (); - - gr.Translate (rect.Left,rect.Top); - gr.Scale (widthRatio, heightRatio); - gr.Translate (((float)rect.Width/widthRatio - Dimensions.Width)/2f, ((float)rect.Height/heightRatio - Dimensions.Height)/2f); + protected override void Render(Interface iFace, IContext gr, string subPart = "") + { if (string.IsNullOrEmpty (subPart)) hSVG.Render (gr); else { @@ -121,9 +99,8 @@ namespace Crow foreach (string p in parts) hSVG.Render (gr, "#" + subPart); } - - gr.Restore (); - } - } + + } + } } diff --git a/Crow/src/IML/CompilerServices.cs b/Crow/src/IML/CompilerServices.cs index ba6e2935..9ddc43be 100644 --- a/Crow/src/IML/CompilerServices.cs +++ b/Crow/src/IML/CompilerServices.cs @@ -392,7 +392,7 @@ namespace Crow.IML if (knownExtMethods.ContainsKey (key)) return knownExtMethods [key]; - //System.Diagnostics.Console.WriteLine ($"*** search extension method: {t};{methodName} => key={key}"); + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.SearchExtMethod] search extension method: {t};{methodName} => key={key}"); MethodInfo mi = null; if (!TryGetExtensionMethods (Assembly.GetEntryAssembly (), t, methodName, out mi)) { @@ -411,7 +411,7 @@ namespace Crow.IML return mi; } } catch (Exception e) {//added this catch for CrowEdit, ext method should be search with appropriate LoadContext. - Debug.WriteLine ($"[CompilerServices.SearchExtMethod]{e}"); + DbgLogger.AddEvent (DbgEvtType.Binding | DbgEvtType.Error, $"[CompilerServices.SearchExtMethod]{e}"); } return null; } @@ -421,18 +421,32 @@ namespace Crow.IML foundMI = null; if (assembly == null) return false; + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.TryGetExtensionMethods] search in {assembly.FullName}"); foreach (Type t in assembly.GetExportedTypes().Where (ty => ty.IsDefined (typeof (ExtensionAttribute), false))) { + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.TryGetExtensionMethods] search in type: {t.FullName}"); foreach (MethodInfo mi in t.GetMethods (BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where (m=> m.Name == methodName && m.IsDefined (typeof (ExtensionAttribute), false) && m.GetParameters ().Length > 0)) { + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.TryGetExtensionMethods] testing method: {mi.Name} extendedType:{extendedType}"); Type curType = extendedType; while (curType != null) { - if (mi.GetParameters () [0].ParameterType == curType) { + Type firstParamType = mi.GetParameters () [0].ParameterType; + if (firstParamType == curType) { + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.TryGetExtensionMethods] Found for {curType}"); foundMI = mi; return true; } + if (firstParamType.IsInterface) { + foreach (Type ifaceType in curType.GetInterfaces()) { + if (firstParamType == ifaceType) { + DbgLogger.AddEvent (DbgEvtType.Binding, $"[CompilerServices.TryGetExtensionMethods] Found interface {ifaceType} in {curType}"); + foundMI = mi; + return true; + } + } + } curType = curType.BaseType; } } diff --git a/Crow/src/IML/Instantiator.cs b/Crow/src/IML/Instantiator.cs index e338e714..69159830 100644 --- a/Crow/src/IML/Instantiator.cs +++ b/Crow/src/IML/Instantiator.cs @@ -828,11 +828,11 @@ namespace Crow.IML { il.MarkLabel (cancel); //#if DEBUG_BINDING //TODO: try to print datasource type in the error message - il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' NOT FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); + il.EmitWriteLine ($"[{dm.Name}] Handler method '{bindingDef.TargetMember}' for '{sourceEvent.Name}' NOT FOUND in new dataSource"); //#endif il.MarkLabel (finish); #if DEBUG_BINDING - il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' FOUND in new dataSource", bindingDef.TargetMember, sourceEvent.Name)); + il.EmitWriteLine ($"[{dm.Name}] Handler method '{bindingDef.TargetMember}' for '{sourceEvent.Name}' FOUND in new dataSource"); #endif il.Emit (OpCodes.Ret); @@ -1525,7 +1525,9 @@ namespace Crow.IML { foreach (string m in destMember.Split('.')) { MemberInfo miDest = curType.GetMember (m).FirstOrDefault (); if (miDest == null) { - Console.WriteLine ($"Member '{destMember}' not found in new DataSource '{dest}' of '{orig}'"); +#if DEBUG_BINDING + Console.WriteLine ($"[ITOR][dataSourceReverseBinding] {delName} Member '{destMember}' not found in new DataSource '{dest}' of '{orig}'"); +#endif return; } miDests.Add (miDest); diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 09407be4..e865739e 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -20,6 +20,9 @@ using Glfw; using Path = System.IO.Path; using Drawing2D; +using System.Runtime.Loader; +using System.Net.Http; +using System.Threading.Tasks; namespace Crow { @@ -60,6 +63,8 @@ namespace Crow /// the 'UpdateFrame' method in the 'Run' cycle and may be overriden. /// public static int POLLING_INTERVAL = 1; + /// Maximum total layoutings + public static long MAX_LAYOUTINGS_COUNT = 200000; /// Crow configuration root path public static string CROW_CONFIG_ROOT; /// If true, mouse focus is given when mouse is over control @@ -97,8 +102,6 @@ namespace Crow /// //protected static Interface CurrentInterface; #endregion - - internal static List crowAssemblies = new List (); /// /// Add Assembly that may contains CROW ui ressources like custom widget classes, IML, images, ... /// Styling fond in those assemblies are automatically loaded on addition; @@ -126,6 +129,7 @@ namespace Crow init_internal (); } } + internal static List crowAssemblies; static IntPtr resolveUnmanaged(Assembly assembly, String libraryName) { try { @@ -160,28 +164,35 @@ namespace Crow /// backends are search where the main crow assembly is. /// public static string BackendsDirectory = null; - protected static Type getBackendType (IEnumerable backendTypes) { + protected static Type getBackend (IEnumerable backendTypes) { if (backendTypes == null) return null; + Type backend = null; switch (PreferedBackendType) { case BackendType.Default: - return backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend"); + backend = backendTypes.FirstOrDefault(be => be.Name == "DefaultBackend"); + break; case BackendType.Egl: - return backendTypes.FirstOrDefault(be => be.Name == "EglBackend"); + backend = backendTypes.FirstOrDefault(be => be.Name == "EglBackend"); + break; case BackendType.Vulkan: - return backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend"); + backend = backendTypes.FirstOrDefault(be => be.Name == "VulkanBackend"); + break; case BackendType.Gl: - return backendTypes.FirstOrDefault(be => be.Name == "GlBackend"); + backend = backendTypes.FirstOrDefault(be => be.Name == "GlBackend"); + break; default: - return backendTypes.FirstOrDefault(); + backend = backendTypes.FirstOrDefault(); + break; } + return backend ?? backendTypes.FirstOrDefault(); } - protected static bool tryFindBackendType (out Type backendType) { + protected static bool tryFindBackend (out Type backendType) { backendType = default; //search loaded assemblies System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); foreach (Assembly a in ldCtx.Assemblies.Where (asb => backends.Contains (asb.GetName ().Name))) { - backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); + backendType = getBackend (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); if (backendType != null) return true; } @@ -194,7 +205,7 @@ namespace Crow string bPath = Path.Combine (bp,$"Crow.{b}.dll"); if (File.Exists (bPath)) { Assembly a = ldCtx.LoadFromAssemblyPath (bPath); - backendType = getBackendType (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); + backendType = getBackend (a.ExportedTypes?.Where (e=>e.IsSubclassOf(typeof(CrowBackend)) && !e.IsAbstract)); return backendType != null; } } @@ -205,7 +216,8 @@ namespace Crow #region CTOR static Interface () { - System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + Assembly crowAssembly = Assembly.GetExecutingAssembly(); + System.Runtime.Loader.AssemblyLoadContext ldCtx = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(crowAssembly); ldCtx.ResolvingUnmanagedDll += resolveUnmanaged; CROW_CONFIG_ROOT = @@ -216,44 +228,14 @@ namespace Crow if (!Directory.Exists (CROW_CONFIG_ROOT)) Directory.CreateDirectory (CROW_CONFIG_ROOT); - } - /// - /// Each time this array is set, the resolved Assemblies will be - /// added to the CrowAssemblies list, see 'AddCrowAssembly' for details. - /// - /// - public static string [] CrowAssemblyNames { - set { - if (value == null) - return; - preloadCrowAssemblies (value); - } - } - static void preloadCrowAssemblies (string [] crowAssemblyNames) { - //ensure all assemblies are loaded, because IML could contains classes not instanciated in source - /*Assembly ea = Assembly.GetEntryAssembly (); - System.IO.FileStream[] files = ea.GetFiles (); - foreach (AssemblyName an in ea.GetReferencedAssemblies()) { - try { - Assembly a = Assembly.ReflectionOnlyLoad (an.Name); - if (a == Assembly.GetExecutingAssembly ()) - continue; - if (a.GetCustomAttribute (typeof (CrowAttribute)) != null) - crowAssemblies.Add (a); - } catch { - - } - }*/ - foreach (string assemblyName in crowAssemblyNames) { - try { - crowAssemblies.Add (Assembly.Load (assemblyName)); - } catch (Exception ex) { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine ($"Unable to preload CrowAssembly: {assemblyName}: {ex}"); - Console.ResetColor(); + SortedDictionary assemblies = new SortedDictionary(); + AssemblyLoadContext ctx = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()); + foreach (Assembly assembly in ctx.Assemblies) { + if (assembly != crowAssembly && assembly.TryGetCrowAssemblyPriority(out int priority)) { + assemblies.Add(priority, assembly); } } - + crowAssemblies = assemblies.Values.Reverse().ToList(); } /// /// Create a new Crow Interface by providing an existing valid GLFW window handle. @@ -440,7 +422,7 @@ namespace Crow } protected virtual void initBackend () { - if (!tryFindBackendType (out Type backendType)) + if (!tryFindBackend (out Type backendType)) throw new Exception ("No backend found."); backend = (CrowBackend)Activator.CreateInstance (backendType, new object[] {clientRectangle.Width, clientRectangle.Height, hWin}); hWin = backend.hWin; @@ -464,14 +446,12 @@ namespace Crow knownCrowWidgetTypes.Add (typeName, t); return t; } - //TODO:LoadContext may now be used there!!! foreach (Type expT in Assembly.GetEntryAssembly ().GetExportedTypes ()) { if (expT.Name != typeName) continue; knownCrowWidgetTypes.Add (typeName, expT); return expT; } - foreach (Assembly a in Interface.crowAssemblies) { foreach (Type expT in a.GetExportedTypes ()) { if (expT.Name != typeName) @@ -489,7 +469,7 @@ namespace Crow if (knownExtMethods.ContainsKey (key)) return knownExtMethods [key]; - //System.Diagnostics.Debug.WriteLine ($"*** search extension method: {t};{methodName} => key={key}"); + Debug.WriteLine ($"[iface] search extension method: {t};{methodName} => key={key}"); MethodInfo mi = null; if (!CompilerServices.TryGetExtensionMethods (Assembly.GetEntryAssembly (), t, methodName, out mi)) { @@ -707,13 +687,6 @@ namespace Crow } #endif - - - - - - - #region DragAndDrop public bool DragAndDropInProgress => DragAndDropOperation != null; public Widget DropTarget => DragAndDropOperation?.DropTarget; @@ -725,7 +698,7 @@ namespace Crow public void ClearDragImage () { lock (UpdateMutex) { if (DragImage == null) - return; + return; clipping.UnionRectangle (lastDragImageBounds); DragImage.Dispose(); DragImage = null; @@ -925,16 +898,19 @@ namespace Crow string resId = path.Substring (1); if (tryFindResource (Assembly.GetEntryAssembly (), resId, out stream)) return stream; - string[] assemblyNames = resId.Split ('.'); + /*string[] assemblyNames = resId.Split ('.'); if (AppDomain.CurrentDomain.GetAssemblies ().FirstOrDefault (aa => aa.GetName ().Name == assemblyNames[0]).TryGetResource (resId, out stream)) return stream; if (assemblyNames.Length > 3) if (tryFindResource (AppDomain.CurrentDomain.GetAssemblies () .FirstOrDefault (aa => aa.GetName ().Name == $"{assemblyNames[0]}.{assemblyNames[1]}"), resId, out stream)) - return stream; + return stream;*/ foreach (Assembly ca in crowAssemblies) if (tryFindResource (ca, resId, out stream)) return stream; + if (tryFindResource (Assembly.GetExecutingAssembly (), resId, out stream)) + return stream; + throw new Exception ("Resource not found: " + path); } if (!File.Exists (path)) @@ -1151,6 +1127,13 @@ namespace Crow } else if (lastMouseDown.ElapsedMilliseconds > DEVICE_REPEAT_DELAY) mouseRepeatTimer.Start (); } + if (FocusedWidget != null && typeof(IEditableTextWidget).IsAssignableFrom(FocusedWidget.GetType())) { + if (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) { + blinkingCursor.Restart(); + drawTextCursor = !drawTextCursor; + FocusedWidget?.RegisterForRepaint(); + } + } if (!Monitor.TryEnter (UpdateMutex)) return; @@ -1171,7 +1154,7 @@ namespace Crow clipping.UnionRectangle(lastDragImageBounds); } - if (!clipping.IsEmpty || shouldDrawTextCursor) { + if (!clipping.IsEmpty) { ctx = Backend.PrepareUIFrame (ctx, clipping); processDrawing (ctx); Backend.FlushUIFrame (ctx); @@ -1198,10 +1181,14 @@ namespace Crow PerformanceMeasure.Begin (PerformanceMeasure.Kind.Layouting); try { DiscardQueue = new Queue (LayoutingQueue.Count); - while (LayoutingQueue.Count > 0) { + long totLayoutings = 0; + while (LayoutingQueue.Count > 0 && totLayoutings++ < MAX_LAYOUTINGS_COUNT) { LayoutingQueueItem lqi = LayoutingQueue.Dequeue (); lqi.ProcessLayouting (); } + if (totLayoutings == MAX_LAYOUTINGS_COUNT) { + DbgLogger.AddEvent(DbgEvtType.LayoutingLoopError); + } LayoutingQueue = DiscardQueue; } finally { PerformanceMeasure.End (PerformanceMeasure.Kind.Layouting); @@ -1259,6 +1246,16 @@ namespace Crow } if (lastDragImageBounds != DragImageBounds) { + /*ctx.LineWidth = 1; + ctx.SetSource(1,0,0,0.6); + ctx.Rectangle(DragImageBounds); + ctx.Stroke (); + ctx.SetSource(0,1,0,0.6); + ctx.Rectangle(lastDragImageBounds); + ctx.Stroke (); + ctx.Arc(lastDragImageBounds.X, lastDragImageBounds.Y, 5,0,Math.PI*2.0); + ctx.Fill ();*/ + DirtyRect += lastDragImageBounds; ctx.Save (); ctx.ResetClip (); @@ -1272,11 +1269,10 @@ namespace Crow #if DEBUG_CLIP_RECTANGLE ctx.LineWidth = 1; - ctx.SetSource(1,1,0,0.5); - for (int i = 0; i < clipping.NumRectangles; i++) - ctx.Rectangle(clipping.GetRectangle(i)); + ctx.SetSource(1,1,0,0.6); + for (int i = 0; i < clipping.NumRectangles; i++) + ctx.Rectangle(clipping.GetRectangle(i).Inflated(-1,-1)); ctx.Stroke (); - #endif clipping.Reset (); @@ -1285,8 +1281,6 @@ namespace Crow IsDirty = true; } - drawTextCursor (ctx); - debugHighlightFocus (ctx); DbgLogger.EndEvent (DbgEvtType.ProcessDrawing, true); @@ -1322,39 +1316,24 @@ namespace Crow /// Text cursor blinking frequency. /// public static long TEXT_CURSOR_BLINK_FREQUENCY = 400; - internal Rectangle? textCursor = null;//last printed cursor, used to clear it. - public bool forceTextCursor = true;//when true, cursor is printed even if blinkingCursor.elapsed is not reached. - Stopwatch blinkingCursor = Stopwatch.StartNew (); - void drawTextCursor (IContext ctx) { - if (forceTextCursor) { - if (FocusedWidget is IEditableTextWidget lab) { - if (lab.DrawCursor (ctx, out Rectangle c)) { - if (textCursor != null && c != textCursor.Value) - RegisterChildClip (textCursor.Value); - textCursor = c; - //MainSurface.Flush (); - } else if (textCursor != null) - RegisterChildClip (textCursor.Value); - } - blinkingCursor.Restart (); - forceTextCursor = false; - } else if (textCursor != null && blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) { - RegisterChildClip (textCursor.Value); - textCursor = null; - blinkingCursor.Restart (); - } else if (FocusedWidget is IEditableTextWidget lab) { - if (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY) { - if (lab.DrawCursor (ctx, out Rectangle c)) { - textCursor = c; - //MainSurface.Flush (); - blinkingCursor.Restart (); - } - } + + bool _drawTextCursor = true; + public bool drawTextCursor { + get => _drawTextCursor; + set { + if (_drawTextCursor == value) + return; + _drawTextCursor = value; + //Console.WriteLine($"draw cursor: {_drawTextCursor}"); } } + + public void forceTextCursor () { + drawTextCursor = true; + blinkingCursor.Restart(); + } + Stopwatch blinkingCursor = Stopwatch.StartNew (); #endregion - bool shouldDrawTextCursor => forceTextCursor || (blinkingCursor.ElapsedMilliseconds > TEXT_CURSOR_BLINK_FREQUENCY && - (FocusedWidget is IEditableTextWidget || textCursor != null)); #region GraphicTree handling /// Add widget to the Graphic tree of this interface and register it for layouting @@ -1813,6 +1792,10 @@ namespace Crow // ProcessMouseMove (Mouse.X, Mouse.Y); // } + return true; + } catch (Exception e) { + Console.WriteLine(e.Message); + Console.WriteLine(e.StackTrace); return true; } finally { Monitor.Exit (UpdateMutex); @@ -1853,8 +1836,12 @@ namespace Crow } public virtual bool OnKeyDown (KeyEventArgs e) { + if (e.Key == Key.F5) { + registerRefreshClientRectangle(); + e.Handled = true; + } #if DEBUG_STATS - if (Shift && key == Key.F1) { + if (e.Modifiers == Modifier.Shift && e.Key == Key.F1) { LoadIMLFragment (@" diff --git a/Crow/src/ItemTemplate.cs b/Crow/src/ItemTemplate.cs index 9c67d57e..cf5bcab3 100644 --- a/Crow/src/ItemTemplate.cs +++ b/Crow/src/ItemTemplate.cs @@ -210,7 +210,7 @@ namespace Crow il.Emit (OpCodes.Br, gotoEnd); il.MarkLabel(gotoItemsContainerNotFound); - il.EmitWriteLine("ItemsContainer not found in ItemTemplate for " + host.ToString()); + il.EmitWriteLine($"[{dm.Name}] ItemsContainer not found in ItemTemplate for {host.ToString()}"); il.MarkLabel(gotoEnd); diff --git a/Crow/src/ObservableList.cs b/Crow/src/ObservableList.cs index 3cfe101a..a62e7e3a 100644 --- a/Crow/src/ObservableList.cs +++ b/Crow/src/ObservableList.cs @@ -139,14 +139,20 @@ namespace Crow public static ObservableList Parse (string str) { ObservableList tmp = new ObservableList(); - Type t = typeof(T); - MethodInfo miParse = t.GetMethod ("Parse", BindingFlags.Static | BindingFlags.Public, - Type.DefaultBinder, new Type [] {typeof (string)}, null); - if (miParse == null) - throw new Exception ("no Parse method found for: " + t.FullName); if (!string.IsNullOrEmpty (str)) { - foreach (string s in str.Split(';')) - tmp.Add((T)miParse.Invoke (null, new object[] {s})); + Type t = typeof(T); + if (t.IsEnum) { + foreach (string s in str.Split(';')) + tmp.Add((T)Enum.Parse(t, s)); + } else { + MethodInfo miParse = t.GetMethod ("Parse", + BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, new Type [] {typeof (string)}, null); + if (miParse == null) + throw new Exception ("no Parse method found for: " + t.FullName); + + foreach (string s in str.Split(';')) + tmp.Add((T)miParse.Invoke (null, new object[] {s})); + } } return tmp; } diff --git a/Crow/src/Text/CharLocation.cs b/Crow/src/Text/CharLocation.cs index 0d07b302..345f8fa3 100644 --- a/Crow/src/Text/CharLocation.cs +++ b/Crow/src/Text/CharLocation.cs @@ -11,14 +11,20 @@ namespace Crow.Text { public readonly int Line; /// - /// Character position in current line. If equals '-1', the visualX must contains the on screen position. - /// + /// Character position in current line. + /// If Column value is '-1', the visualX must contains the on screen position, and column will be computed from it. /// public int Column; + /// + /// The column as presented to the user, counting spaces in tabulations. + /// Its value is set during computations. + /// + public int TabulatedColumn; public double VisualCharXPosition; public CharLocation (int line, int column, double visualX = -1) { Line = line; Column = column; + TabulatedColumn = -1; VisualCharXPosition = visualX; } public bool HasVisualX => Column >= 0 && VisualCharXPosition >= 0; diff --git a/Crow/src/Text/Extensions.cs b/Crow/src/Text/Extensions.cs index b0b87180..77360ddd 100644 --- a/Crow/src/Text/Extensions.cs +++ b/Crow/src/Text/Extensions.cs @@ -7,27 +7,26 @@ namespace Crow.Text { public static class Extensions { - public static ReadOnlySpan GetLine (this string str, TextLine ls) { + public static ReadOnlySpan GetLine (this ReadOnlySpan str, TextLine ls) { if (ls.Start >= str.Length) return "".AsSpan (); - return str.AsSpan ().Slice (ls.Start, ls.Length); + return str.Slice (ls.Start, ls.Length); } - public static ReadOnlySpan GetLine (this string str, TextLine ls, int offset) { + public static ReadOnlySpan GetLine (this ReadOnlySpan str, TextLine ls, int offset) { int start = ls.Start + offset; if (start >= str.Length) return "".AsSpan (); - return str.AsSpan ().Slice (start, ls.Length); - + return str.Slice (start, ls.Length); } public static ReadOnlySpan GetLineIncludingLineBreak (this string str, TextLine ls) { if (ls.Start >= str.Length) return "".AsSpan (); return str.AsSpan ().Slice (ls.Start, ls.LengthIncludingLineBreak); } - public static ReadOnlySpan GetLineBreak (this string str, TextLine ls) { + public static ReadOnlySpan GetLineBreak (this ReadOnlySpan str, TextLine ls) { if (ls.LineBreakLength == 0) return "".AsSpan (); - return str.AsSpan ().Slice (ls.End, ls.LineBreakLength); + return str.Slice (ls.End, ls.LineBreakLength); } public static ReadOnlySpan GetLineIncludingLineBreak (this string str, TextLine ls, int offset) { int start = ls.Start + offset; diff --git a/Crow/src/Text/SpanCharReader.cs b/Crow/src/Text/SpanCharReader.cs index ae55d64f..60fb432b 100644 --- a/Crow/src/Text/SpanCharReader.cs +++ b/Crow/src/Text/SpanCharReader.cs @@ -12,17 +12,26 @@ namespace Crow.Text int curPos; ReadOnlySpan buffer; + [Obsolete] public SpanCharReader (string text) { buffer = text.AsSpan (); curPos = 0; } + public SpanCharReader (ReadOnlySpan text) { + buffer = text; + curPos = 0; + } public int CurrentPosition => curPos; + /// + /// Current reader position is further the end of the buffer. + /// + public bool EndOfSpan => curPos >= buffer.Length; public void Seek (int position) => curPos = position; - public Char Peak => buffer[curPos]; - public Char Read () => buffer[curPos++]; + public char Peek => buffer[curPos]; + public char Read () => buffer[curPos++]; public bool TryRead (out char c) { if (EndOfSpan) { c = default; @@ -39,6 +48,24 @@ namespace Crow.Text curPos += increment; return curPos < buffer.Length; } + /// + /// Retrieve a span of that buffer from provided starting position to the current reader position. + /// + /// + /// + public ReadOnlySpan Get (int fromPosition) => buffer.Slice (fromPosition, curPos - fromPosition); + public bool TryPeek (char c) => !EndOfSpan && Peek == c; + /// + /// Try peak one char, return false if end of span, true otherwise. + /// + /// + /// + public bool TryPeek (ref char c) { + if (EndOfSpan) + return false; + c = buffer[curPos]; + return true; + } public bool TryReadUntil (ReadOnlySpan str, StringComparison comparison = StringComparison.Ordinal) { int startPos = curPos; @@ -79,32 +106,10 @@ namespace Crow.Text curPos += expectedString.Length; return res; } - public bool TryPeak (ReadOnlySpan expectedString, StringComparison comparison = StringComparison.Ordinal) => + public bool TryPeek (ReadOnlySpan expectedString, StringComparison comparison = StringComparison.Ordinal) => (buffer.Length < curPos + expectedString.Length)? false : buffer.Slice(curPos, expectedString.Length).Equals (expectedString, comparison); - /// - /// Retrieve a span of that buffer from provided starting position to the current reader position. - /// - /// - /// - public ReadOnlySpan Get (int fromPosition) => buffer.Slice (fromPosition, curPos - fromPosition); - /// - /// Current reader position is further the end of the buffer. - /// - public bool EndOfSpan => curPos >= buffer.Length; - public bool TryPeak (char c) => !EndOfSpan && Peak == c; - /// - /// Try peak one char, return false if end of span, true otherwise. - /// - /// - /// - public bool TryPeak (ref char c) { - if (EndOfSpan) - return false; - c = buffer[curPos]; - return true; - } /// /// test if next char is one of the provided one as parameter /// @@ -121,7 +126,7 @@ namespace Crow.Text /// public void AdvanceUntilEol () { while(!EndOfSpan) { - switch (Peak) { + switch (Peek) { case '\x85': case '\x2028': case '\xA': @@ -140,8 +145,8 @@ namespace Crow.Text /// /// public bool Eol () { - return Peak == '\x85' || Peak == '\x2028' || Peak == '\xA' || curPos + 1 == buffer.Length || - (Peak == '\xD' && (buffer [curPos + 1] == '\xA' || buffer [curPos + 1] == '\x85')); + return Peek == '\x85' || Peek == '\x2028' || Peek == '\xA' || curPos + 1 == buffer.Length || + (Peek == '\xD' && (buffer [curPos + 1] == '\xA' || buffer [curPos + 1] == '\x85')); } /// diff --git a/Crow/src/Text/TextChange.cs b/Crow/src/Text/TextChange.cs index 6aae62b1..ae1362a4 100644 --- a/Crow/src/Text/TextChange.cs +++ b/Crow/src/Text/TextChange.cs @@ -14,7 +14,7 @@ namespace Crow.Text public readonly string ChangedText; public int End => Start + Length; - public int End2 => Start + (string.IsNullOrEmpty (ChangedText) ? 0 : CharDiff); + public int End2 => End + CharDiff; public int CharDiff => string.IsNullOrEmpty (ChangedText) ? - Length : ChangedText.Length - Length; public TextChange (int position, int length, string changedText) { @@ -22,8 +22,14 @@ namespace Crow.Text Length = length; ChangedText = changedText; } - public TextChange Inverse (string src) + public TextChange (int position, int length, ReadOnlySpan changedText) { + Start = position; + Length = length; + ChangedText = changedText.ToString(); + } + public TextChange Inverse (ReadOnlySpan src) => new TextChange (Start, string.IsNullOrEmpty (ChangedText) ? 0 : ChangedText.Length, - Length == 0 ? "" : src.AsSpan (Start, Length).ToString ()); + Length == 0 ? "" : src.Slice (Start, Length).ToString ()); + public override string ToString() => $"{Start},{ChangedText}"; } } diff --git a/Crow/src/Text/TextLineCollection.cs b/Crow/src/Text/TextLineCollection.cs index ba40e084..5031496b 100644 --- a/Crow/src/Text/TextLineCollection.cs +++ b/Crow/src/Text/TextLineCollection.cs @@ -118,9 +118,7 @@ namespace Crow.Text int result = lines.AsSpan (0, length).BinarySearch (tl); if (result < 0) { result = ~result; - return result == 0 ? - new CharLocation (0, absolutePosition) : - new CharLocation (result - 1, absolutePosition - lines[result - 1].Start); + return new CharLocation (result - 1, absolutePosition - lines[result - 1].Start); } return new CharLocation (result, absolutePosition - lines[result].Start); } diff --git a/Crow/src/Widgets/DockStack.cs b/Crow/src/Widgets/DockStack.cs index 6d1a835d..60644c15 100644 --- a/Crow/src/Widgets/DockStack.cs +++ b/Crow/src/Widgets/DockStack.cs @@ -200,7 +200,6 @@ namespace Crow int vTreshold = (int)(r.Height * dockThresh); int hTreshold = (int)(r.Width * dockThresh); - System.Diagnostics.Debug.WriteLine ("Docking {0} as {2} in {1}", dw.Name, activeStack.Name, dw.DockingPosition); switch (dw.DockingPosition) { case Alignment.Top: dw.Height = vTreshold; @@ -237,8 +236,6 @@ namespace Crow public void Undock (DockWindow dw){ int idx = Children.IndexOf(dw); - System.Diagnostics.Debug.WriteLine ("undocking child index: {0} ; name={1}; pos:{2} ; childcount:{3}",idx, dw.Name, dw.DockingPosition, Children.Count); - RemoveChild(dw); if (Children.Count == 0)//TODO:empty Stack should be removed if not root stack I guess diff --git a/Crow/src/Widgets/DockWindow.cs b/Crow/src/Widgets/DockWindow.cs index 0fc8a825..43fd8a9a 100644 --- a/Crow/src/Widgets/DockWindow.cs +++ b/Crow/src/Widgets/DockWindow.cs @@ -131,8 +131,6 @@ namespace Crow dockParentParent = false; Rectangle r = default; - Console.WriteLine ($"onDrag target={IFace.DragAndDropOperation.DropTarget}"); - if (IFace.DragAndDropOperation.DropTarget is DockStack ds) { ds.onDragMouseMove (this, e); r = ds.ScreenCoordinates (ds.LastPaintedSlot); @@ -149,7 +147,6 @@ namespace Crow dwCb.Inflate (-4,-4); if (tryGetTargetDockStack (dw, out DockStack targetStack)) { - Console.WriteLine ($"exterior: {!dwCb.ContainsOrIsEqual (m)} targetStack.Parent: {targetStack.Parent.GetType()}"); if (dwCb.ContainsOrIsEqual (m)) { r = dw.ScreenCoordinates (dw.LastPaintedSlot); } else if (targetStack.Parent is DockStack) { @@ -197,9 +194,11 @@ namespace Crow r.Width /= 4; break; case Alignment.Center: - r.Inflate (r.Width / -3, r.Height / -3); + r.Inflate (r.Width / -4, r.Height / -4); + Console.WriteLine(r); break; } + r.Inflate(-2,-2); ISurface dragImg = IFace.Backend.CreateSurface (r.Width, r.Height); using (IContext gr = IFace.Backend.CreateContext (dragImg)) { gr.LineWidth = 1; @@ -293,9 +292,6 @@ namespace Crow } } bool checkUndock (Point mousePos) { - //if (DockingPosition == Alignment.Center) - // return false; - System.Diagnostics.Debug.WriteLine ($"{mousePos.X},{mousePos.Y}"); if (Math.Abs (mousePos.X - undockingMousePosOrig.X) < undockThreshold || Math.Abs (mousePos.X - undockingMousePosOrig.X) < undockThreshold) return false; diff --git a/Crow/src/Widgets/GroupBase.cs b/Crow/src/Widgets/GroupBase.cs index e99b176e..3a3e72c9 100644 --- a/Crow/src/Widgets/GroupBase.cs +++ b/Crow/src/Widgets/GroupBase.cs @@ -238,6 +238,9 @@ namespace Crow { for (int i = 0; i < Children.Count; i++) Children[i].Paint (gr); + } catch (Exception e) { + Console.WriteLine($"Erreur group {this} paint: {e.Message}"); + Console.WriteLine(e.StackTrace); } finally { childrenRWLock.ExitReadLock (); } diff --git a/Crow/src/Widgets/Image.cs b/Crow/src/Widgets/Image.cs index 2c102c22..b1a3a94f 100644 --- a/Crow/src/Widgets/Image.cs +++ b/Crow/src/Widgets/Image.cs @@ -8,6 +8,13 @@ using System.Xml.Serialization; using System.ComponentModel; using System.Diagnostics; using Drawing2D; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using System.IO; +using System.Linq; +using System.Collections; +using System.Collections.Generic; namespace Crow { @@ -69,16 +76,14 @@ namespace Crow try { if (string.IsNullOrEmpty(value)) Picture = null; - else { - //lock(IFace.LayoutMutex){ - LoadImage (value); - //} - } + else + LoadImage (value); } catch (Exception ex) { Debug.WriteLine (ex.Message); _pic = null; } NotifyValueChangedAuto (Path); + RegisterForGraphicUpdate(); } } /// @@ -140,15 +145,64 @@ namespace Crow #endregion #region Image Loading + static HttpClient http = new HttpClient(); + static int HTTP_DOWNLOAD_TIMEOUT_MS = 5000; + void downloadThread(Uri uri) { + Picture pic = null; + var getcontenttype = http.GetAsync(uri); + var task = http.GetStreamAsync(uri); + if (getcontenttype.Wait(HTTP_DOWNLOAD_TIMEOUT_MS) && getcontenttype.IsCompletedSuccessfully) { + if (!getcontenttype.Result.Content.Headers.TryGetValues("content-Type", out IEnumerable contents)) + return; + string[] type = contents.FirstOrDefault().Split('/'); + if (!string.Equals(type[0], "image", StringComparison.Ordinal)) + return; + if (string.Equals(type[1], "svg", StringComparison.Ordinal)) + pic = new SvgPicture(uri.AbsoluteUri); + else if (string.Equals(type[1], "png", StringComparison.Ordinal)) + pic = new BmpPicture(uri.AbsoluteUri); + else { + Debug.WriteLine($"Unsupported image format for download: {type[1]}"); + return; + } + + if (task.Wait(HTTP_DOWNLOAD_TIMEOUT_MS) && task.IsCompletedSuccessfully) { + MemoryStream dataCopy = new MemoryStream(); + task.Result.CopyTo(dataCopy); + dataCopy.Position = 0; + pic.LoadFromStream(IFace, dataCopy); + pic.Scaled = scaled; + pic.KeepProportions = keepProps; + lock(IFace.UpdateMutex) + Picture = pic; + } + } + + } + public void LoadImage (string path) { Picture pic; + if (path.StartsWith("url:", StringComparison.OrdinalIgnoreCase)) { + /*if (IFace.sharedPictures.ContainsKey (Path)) { + sharedPicture sp = IFace.sharedPictures [Path]; + if (sp.Data is ISvgHandle svgHandle) { + pic = new SvgPicture() + } + }*/ + Uri uri = new Uri(path.Substring (4)); + if (string.IsNullOrEmpty(uri.AbsolutePath)) + return; + Thread download = new Thread(()=>downloadThread(uri)); + download.Start(); + return; + } + if (path.EndsWith (".svg", true, System.Globalization.CultureInfo.InvariantCulture)) pic = new SvgPicture (path); else pic = new BmpPicture (path); - pic.Scaled = scaled; pic.KeepProportions = keepProps; diff --git a/Crow/src/Widgets/Label.cs b/Crow/src/Widgets/Label.cs index f5b4fb1c..a35151ea 100644 --- a/Crow/src/Widgets/Label.cs +++ b/Crow/src/Widgets/Label.cs @@ -289,10 +289,10 @@ namespace Crow LineBreak = Environment.NewLine; return; } - LineBreak = _text.GetLineBreak (lines[0]).ToString (); + LineBreak = _text.AsSpan().GetLineBreak (lines[0]).ToString (); for (int i = 1; i < lines.Count; i++) { - ReadOnlySpan lb = _text.GetLineBreak (lines[i]); + ReadOnlySpan lb = _text.AsSpan().GetLineBreak (lines[i]); if (!lb.SequenceEqual (LineBreak)) { mixedLineBreak = true; break; @@ -377,7 +377,7 @@ namespace Crow if (lines[i].Length == 0) lines.UpdateLineLengthInPixel (i, 0);// (int)Math.Ceiling (fe.MaxXAdvance); else { - gr.TextExtents (_text.GetLine (lines[i]), Interface.TAB_SIZE, out tmp); + gr.TextExtents (_text.AsSpan().GetLine (lines[i]), Interface.TAB_SIZE, out tmp); lines.UpdateLineLengthInPixel (i, (int)Math.Ceiling (tmp.XAdvance)); } } @@ -420,8 +420,7 @@ namespace Crow selStart = SelectionStart.Value; selEnd = CurrentLoc.Value; } - } else - IFace.forceTextCursor = true; + } } if (!string.IsNullOrEmpty (_text)) { @@ -439,7 +438,7 @@ namespace Crow if (bytes.Length < size) bytes = size > 512 ? new byte[size] : stackalloc byte[size]; - encodedBytes = _text.GetLine (lines[i]).ToUtf8 (bytes); + encodedBytes = _text.AsSpan().GetLine (lines[i]).ToUtf8 (bytes); bytes[encodedBytes++] = 0; if (lines[i].LengthInPixel < 0) { @@ -555,8 +554,8 @@ namespace Crow return false; } - Rectangle c = ScreenCoordinates (textCursor.Value + Slot.Position + ClientRectangle.Position); - ctx.ResetClip(); + Rectangle c = ContextCoordinates(textCursor.Value + Slot.Position + ClientRectangle.Position); + Foreground.SetAsSource (IFace, ctx, c); ctx.LineWidth = 1.0; ctx.MoveTo (0.5 + c.X, c.Y); @@ -574,7 +573,7 @@ namespace Crow if (loc.HasVisualX) return; TextLine ls = lines[loc.Line]; - ReadOnlySpan curLine = _text.GetLine (ls); + ReadOnlySpan curLine = _text.AsSpan().GetLine (ls); double cPos = getX (clientWidth, ls); if (loc.Column >= 0) { @@ -733,7 +732,7 @@ namespace Crow else if (!SelectionStart.HasValue) SelectionStart = CurrentLoc; CurrentLoc = hoverLoc; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); RegisterForRedraw (); e.Handled = true; } @@ -820,7 +819,7 @@ namespace Crow base.onKeyDown (sender, e); return; } - IFace.forceTextCursor = true; + IFace.forceTextCursor(); e.Handled = true; } #endregion diff --git a/Crow/src/Widgets/TextBox.cs b/Crow/src/Widgets/TextBox.cs index ff9bec25..5d58c606 100644 --- a/Crow/src/Widgets/TextBox.cs +++ b/Crow/src/Widgets/TextBox.cs @@ -220,8 +220,15 @@ namespace Crow return cursor; } - - void updateMaxScrolls (LayoutingType layout) { + public override bool Paint(IContext ctx) + { + bool painted = base.Paint(ctx); + if (HasFocus && painted && IFace.drawTextCursor) { + DrawCursor(ctx, out Rectangle r); + } + return painted; + } + void updateMaxScrolls (LayoutingType layout) { Rectangle cb = ClientRectangle; if (layout == LayoutingType.Width) { MaxScrollX = cachedTextSize.Width - cb.Width; @@ -339,7 +346,7 @@ namespace Crow } #endregion - protected void update (TextChange change) { + protected void update (TextChange change, int charLocOffset = 0) { lock (linesMutex) { ReadOnlySpan src = Text.AsSpan (); Span tmp = stackalloc char[src.Length + (change.ChangedText.Length - change.Length)]; @@ -353,9 +360,9 @@ namespace Crow //lines.Update (_text); SelectionStart = null; - CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length); + CurrentLoc = lines.GetLocation (change.Start + change.ChangedText.Length + charLocOffset); textMeasureIsUpToDate = false; - IFace.forceTextCursor = true; + IFace.forceTextCursor(); } NotifyValueChanged ("Text", Text); diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index e3703119..bebb57a3 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -409,7 +409,8 @@ namespace Crow [XmlIgnore]public virtual Rectangle ClientRectangle { get { Rectangle cb = Slot.Size; - cb.Inflate ( - margin); + cb.Inflate (-margin); + //return cb.IsValid ? cb : Slot.Size; return cb; } } @@ -1986,10 +1987,10 @@ namespace Crow } /// Chained painting routine on the parent context of the actual cached version /// of the widget - public virtual void Paint (IContext ctx) + public virtual bool Paint (IContext ctx) { if (!IsVisible) - return; + return false; DbgLogger.StartEvent (DbgEvtType.GOPaint, this); @@ -2006,7 +2007,7 @@ namespace Crow #endif DbgLogger.AddEvent (DbgEvtType.Warning); DbgLogger.EndEvent (DbgEvtType.GOPaint); - return; + return false; } //lock (this) { if (cacheEnabled) { @@ -2044,6 +2045,7 @@ namespace Crow Painted.Raise (this, null); DbgLogger.EndEvent (DbgEvtType.GOPaint); + return true; } void paintDisabled(IContext gr, Rectangle rb){ //gr.Operator = Operator.Xor; diff --git a/Crow/src/Widgets/Window.cs b/Crow/src/Widgets/Window.cs index cc7763c9..389b1668 100644 --- a/Crow/src/Widgets/Window.cs +++ b/Crow/src/Widgets/Window.cs @@ -9,6 +9,9 @@ using Drawing2D; namespace Crow { + /// + /// + /// public class Window : TemplatedContainer { public enum Direction diff --git a/Directory.Build.props b/Directory.Build.props index eb7ad1da..e1bd3e46 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,7 +6,7 @@ Jean-Philippe Bruyère 7.3 - 1.3.1 + 1.3.2 $(CrowVersion)-beta - false + true false diff --git a/Drawing2D/Drawing2D.csproj b/Drawing2D/Drawing2D.csproj index b126ddff..72123f26 100644 --- a/Drawing2D/Drawing2D.csproj +++ b/Drawing2D/Drawing2D.csproj @@ -4,7 +4,7 @@ netstandard2.1 false - 1.2.0 + 1.2.1 $(AssemblyVersion)-beta Drawing 2D Library diff --git a/Drawing2D/src/DbgEvtType.cs b/Drawing2D/src/DbgEvtType.cs index ff214ec8..e54c5603 100644 --- a/Drawing2D/src/DbgEvtType.cs +++ b/Drawing2D/src/DbgEvtType.cs @@ -27,7 +27,8 @@ namespace Crow Dispose = 0x00100000, Mouse = 0x00200000, DragNDrop = 0x00400000, - + Ressources = 0x00800000,//image loading... + Update = IFace | 0x10000000, ProcessLayouting = IFace | Update | Lock | Layouting, ClippingRegistration = IFace | Update | Lock | Clipping, @@ -107,6 +108,7 @@ namespace Crow EndDrag = Widget | DragNDrop | 0x05, Drop = Widget | DragNDrop | 0x06, + LayoutingLoopError = Layouting | Error | 0x01, All = 0x7FFFFF00 } } \ No newline at end of file diff --git a/Drawing2D/src/Encoding.cs b/Drawing2D/src/Encoding.cs index 8999623d..58cdf601 100644 --- a/Drawing2D/src/Encoding.cs +++ b/Drawing2D/src/Encoding.cs @@ -7,10 +7,10 @@ namespace Drawing2D { public static class Extensions { - public static int ToUtf8 (this ReadOnlySpan source, Span buff, int tabWidth = 4) { + public static int ToUtf8 (this ReadOnlySpan source, Span buff, ref int encodedChar, int tabWidth = 4) { int c = 0; int encodedBytes = 0; - int encodedChar = 0; + while (c < source.Length) { if (source[c] < 0xD800) { if (source[c] == '\t') { @@ -44,6 +44,10 @@ namespace Drawing2D } buff[encodedBytes] = 0; return encodedBytes; + } + public static int ToUtf8 (this ReadOnlySpan source, Span buff, int tabWidth = 4) { + int encodedChar = 0; + return ToUtf8(source,buff,ref encodedChar, tabWidth); } diff --git a/Drawing2D/src/Rectangle.cs b/Drawing2D/src/Rectangle.cs index ac0d83c7..d120de69 100644 --- a/Drawing2D/src/Rectangle.cs +++ b/Drawing2D/src/Rectangle.cs @@ -61,7 +61,7 @@ namespace Drawing2D { public Point BottomRight => new Point (Right, Bottom); public Point Center => new Point (Left + Width / 2, Top + Height / 2); public Point CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); - + public bool IsValid => Width > 0 && Height > 0; #endregion #region FUNCTIONS diff --git a/Drawing2D/src/RectangleD.cs b/Drawing2D/src/RectangleD.cs index 2d3f3931..c5d12313 100644 --- a/Drawing2D/src/RectangleD.cs +++ b/Drawing2D/src/RectangleD.cs @@ -60,7 +60,7 @@ namespace Drawing2D { public PointD BottomRight => new PointD (Right, Bottom); public PointD Center => new PointD (Left + Width / 2, Top + Height / 2); public PointD CenterD => new PointD (Left + Width / 2.0, Top + Height / 2.0); - + public bool IsValid => Width > 0 && Height > 0; #endregion #region FUNCTIONS diff --git a/Drawing2D/src/Size.cs b/Drawing2D/src/Size.cs index acd5c06c..dd3a6c1b 100644 --- a/Drawing2D/src/Size.cs +++ b/Drawing2D/src/Size.cs @@ -50,7 +50,7 @@ namespace Drawing2D public static Size operator * (Size s, double i) => new Size ((int)(s.Width * i), (int)(s.Height * i)); #endregion - + public bool IsValid => Width > 0 && Height > 0; public bool Equals (Size other) => Width == other.Width && Height == other.Height; public bool Equals (int other) => Width == other && Height == other; diff --git a/Drawing2D/src/SizeD.cs b/Drawing2D/src/SizeD.cs index dd466491..93f7698c 100644 --- a/Drawing2D/src/SizeD.cs +++ b/Drawing2D/src/SizeD.cs @@ -48,6 +48,7 @@ namespace Drawing2D public static SizeD operator / (SizeD s, double i) => new SizeD (s.Width / i, s.Height / i); #endregion + public bool IsValid => Width > 0 && Height > 0; public override int GetHashCode () => HashCode.Combine (Width, Height); public override bool Equals (object obj) => obj is SizeD s ? Equals (s) : false; public bool Equals(SizeD other) => Width == other.Width && Height == other.Height; diff --git a/Samples/AnimTest/AnimTest.csproj b/Samples/AnimTest/AnimTest.csproj index 178d4537..5ab677ea 100644 --- a/Samples/AnimTest/AnimTest.csproj +++ b/Samples/AnimTest/AnimTest.csproj @@ -1,11 +1,4 @@  - - netcoreapp3.1 - Exe - - - - \ No newline at end of file diff --git a/Samples/HelloWorld/HelloWorld.csproj b/Samples/HelloWorld/HelloWorld.csproj index 6c6cdaf7..4c64f057 100644 --- a/Samples/HelloWorld/HelloWorld.csproj +++ b/Samples/HelloWorld/HelloWorld.csproj @@ -1,6 +1,8 @@  - - + + ui.%(Filename)%(Extension) + + \ No newline at end of file diff --git a/Samples/HelloWorld/main.cs b/Samples/HelloWorld/main.cs index 54429171..50255ac9 100644 --- a/Samples/HelloWorld/main.cs +++ b/Samples/HelloWorld/main.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Crow; using Glfw; using Samples; @@ -11,8 +12,11 @@ namespace HelloWorld //Interface.PreferedBackendType = Drawing2D.BackendType.Egl; using (Program app = new Program ()) { //app.Initialized += (sender, e) => app.LoadIMLFragment (@"