diff --git a/README.md b/README.md index 7344ad53..33b13100 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,9 @@ ___Please star ⭐ if you like it, helps very much!___ ## What's New -### Nuget 1.2.9.6 +### Nuget 1.2.9.7 for SkiaSharp 2.88.9-preview.2.2 +* Added gestures delegate `OnGestures` to SkiaLayout. * HotFix for SkiaCarousel always setting index at 0 upon initialization. * HotFix for random crash accessing disposed LoadedImageSource. * [HotFix](https://github.com/taublast/DrawnUi.Maui/issues/136) for loading images from StreamImageSource @@ -127,6 +128,7 @@ V3 preview: subclassed `SkiaShaderEffect`, implementing `ISkiaGestureProcessor`, ## Development Notes +* To compile the v3 version that supports NEW SHADERS you must set `true` inside `Directory.Build.props` file. * All files to be consumed (images etc) must be placed inside the MAUI app Resources/Raw folder, subfolders allowed. If you need to load from the native app folder use prefix "file://". * Accessibility support is compatible and is on the roadmap. @@ -720,12 +722,6 @@ It will render a mask over its children when hovered, think of it as an inverted ## Published Apps powered by DrawnUI For .Net MAUI -### Bug ID: Insect Identifier AI - -_Totally drawn with just one root view `Canvas` and `SkiaShell` for navigation. First ever drawn MAUI app!_ - -GooglePlay: https://play.google.com/store/apps/details?id=com.niroapps.insects - ### Racebox _MAUI pages with canvases, custom navigation. All scrolls, cells collections, maps, buttons, labels and custom controls are drawn._ @@ -733,6 +729,13 @@ _MAUI pages with canvases, custom navigation. All scrolls, cells collections, ma iOS: https://apps.apple.com/us/app/racebox-vehicle-dynamics/id6444165250 GooglePlay: https://play.google.com/store/apps/details?id=com.raceboxcompanion.app +### Bug ID: Insect Identifier AI + +_Totally drawn with just one root view `Canvas` and `SkiaShell` for navigation. First ever drawn MAUI app!_ + +__Ooops looks like the app API went dead, client maybe abandoned the project due to AI detecting stuff better than dedicated neural networks!__ + +GooglePlay: https://play.google.com/store/apps/details?id=com.niroapps.insects + - diff --git a/dev/github_uploadnugets.bat b/dev/github_uploadnugets.bat index 11226b74..2252670b 100644 --- a/dev/github_uploadnugets.bat +++ b/dev/github_uploadnugets.bat @@ -13,8 +13,8 @@ REM Define the source directory for the packages set "source_dir=E:\Nugets" REM Define the list of file masks for the packages -set "mask[1]=DrawnUi.Maui*.1.2.9.6*.nupkg" -set "mask[2]=AppoMobi.Maui.DrawnUi.1.2.9.6*.*nupkg" +set "mask[1]=DrawnUi.Maui*.1.2.9.7*.nupkg" +set "mask[2]=AppoMobi.Maui.DrawnUi.1.2.9.7*.*nupkg" set "mask_count=2" REM Loop through each file mask diff --git a/dev/nuget_uploadnugets.bat b/dev/nuget_uploadnugets.bat index ba35ed1a..d1d37227 100644 --- a/dev/nuget_uploadnugets.bat +++ b/dev/nuget_uploadnugets.bat @@ -13,8 +13,8 @@ REM Define the source directory for the packages set "source_dir=E:\Nugets" REM Define the list of file masks for the packages -set "mask[1]=DrawnUi.Maui*.1.2.9.6*.nupkg" -set "mask[2]=AppoMobi.Maui.DrawnUi.1.2.9.6*.*nupkg" +set "mask[1]=DrawnUi.Maui*.1.2.9.7*.nupkg" +set "mask[2]=AppoMobi.Maui.DrawnUi.1.2.9.7*.*nupkg" set "mask_count=2" REM Loop through each file mask @@ -24,7 +24,7 @@ for /L %%i in (1,1,%mask_count%) do ( REM Loop through each package file matching the mask in the source directory for %%f in ("!source_dir!\!file_mask!") do ( echo Uploading "%%f" to NUGET.ORG with API key. - nuget push "%%f" -Source https://api.nuget.org/v3/index.json -ApiKey %APIKEY% + nuget push "%%f" -Source https://api.nuget.org/v3/index.json -ApiKey %APIKEY% -SkipDuplicate if errorlevel 1 ( echo An error occurred while uploading "%%f". ) else ( diff --git a/src/Addons/DrawnUi.Maui.Camera/DrawnUi.Maui.Camera.csproj b/src/Addons/DrawnUi.Maui.Camera/DrawnUi.Maui.Camera.csproj index bd332139..6dbbf4a2 100644 --- a/src/Addons/DrawnUi.Maui.Camera/DrawnUi.Maui.Camera.csproj +++ b/src/Addons/DrawnUi.Maui.Camera/DrawnUi.Maui.Camera.csproj @@ -44,7 +44,7 @@ - + \ No newline at end of file diff --git a/src/Addons/DrawnUi.Maui.Game/DrawnUi.Maui.Game.csproj b/src/Addons/DrawnUi.Maui.Game/DrawnUi.Maui.Game.csproj index 6dcf808b..8232e474 100644 --- a/src/Addons/DrawnUi.Maui.Game/DrawnUi.Maui.Game.csproj +++ b/src/Addons/DrawnUi.Maui.Game/DrawnUi.Maui.Game.csproj @@ -42,7 +42,7 @@ - + diff --git a/src/Addons/DrawnUi.Maui.MapsUi/src/DrawnUi.Maui.MapsUi.csproj b/src/Addons/DrawnUi.Maui.MapsUi/src/DrawnUi.Maui.MapsUi.csproj index b9b06758..d06c087e 100644 --- a/src/Addons/DrawnUi.Maui.MapsUi/src/DrawnUi.Maui.MapsUi.csproj +++ b/src/Addons/DrawnUi.Maui.MapsUi/src/DrawnUi.Maui.MapsUi.csproj @@ -54,7 +54,7 @@ - + diff --git a/src/Addons/DrawnUi.Maui.Rive/DrawnUi.Maui.Rive.csproj b/src/Addons/DrawnUi.Maui.Rive/DrawnUi.Maui.Rive.csproj index 57b26e5a..694847f8 100644 --- a/src/Addons/DrawnUi.Maui.Rive/DrawnUi.Maui.Rive.csproj +++ b/src/Addons/DrawnUi.Maui.Rive/DrawnUi.Maui.Rive.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/Addons/DrawnUi.MauiGraphics/DrawnUi.MauiGraphics.csproj b/src/Addons/DrawnUi.MauiGraphics/DrawnUi.MauiGraphics.csproj index 58c28344..9d1c496c 100644 --- a/src/Addons/DrawnUi.MauiGraphics/DrawnUi.MauiGraphics.csproj +++ b/src/Addons/DrawnUi.MauiGraphics/DrawnUi.MauiGraphics.csproj @@ -41,7 +41,7 @@ - + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 109d7734..ba4d863c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -5,13 +5,13 @@ Using SkiaSharp 2.xx. Checkout the DrawnUi Sandbox project for usage example. - 1.2.9.6 + 1.2.9.7 $(DefineConstants);SKIA3 Using SkiaSharp 3-preview. New handlers, SKSL, WinUI hardware acceleration etc.. - 1.3.55.1-pre + 1.3.56.1-pre \ No newline at end of file diff --git a/src/Engine/Draw/Base/SkiaControl.Shared.cs b/src/Engine/Draw/Base/SkiaControl.Shared.cs index 34242037..66a268c1 100644 --- a/src/Engine/Draw/Base/SkiaControl.Shared.cs +++ b/src/Engine/Draw/Base/SkiaControl.Shared.cs @@ -5858,19 +5858,14 @@ public bool SetupShadow(SKPaint paint, SkiaShadow shadow, float scale) { if (shadow != null && paint != null) { - - if (LastShadow == null || LastShadow.Shadow != shadow || - LastShadow.Scale != scale) + var kill = LastShadow; + LastShadow = new() { - var kill = LastShadow; - LastShadow = new() - { - Filter = CreateShadow(shadow, scale), - Scale = scale, - Shadow = shadow - }; - DisposeObject(kill); - } + Filter = CreateShadow(shadow, scale), + Scale = scale, + Shadow = shadow + }; + DisposeObject(kill); var old = paint.ImageFilter; paint.ImageFilter = LastShadow.Filter; diff --git a/src/Engine/Draw/Layout/SkiaLayout.Grid.Structure.cs b/src/Engine/Draw/Layout/SkiaLayout.Grid.Structure.cs index df99eaec..610558e7 100644 --- a/src/Engine/Draw/Layout/SkiaLayout.Grid.Structure.cs +++ b/src/Engine/Draw/Layout/SkiaLayout.Grid.Structure.cs @@ -5,92 +5,6 @@ namespace DrawnUi.Maui.Draw; -public static class FluentExtensions -{ - public static T With(this T view, Action action) where T : SkiaControl - { - action?.Invoke(view); - return view; - } - - public static T WithParent(this T view, IDrawnBase parent) where T : SkiaControl - { - parent.AddSubView(view); - return view; - } - - public static T CenterX(this T view) where T : SkiaControl - { - view.HorizontalOptions = LayoutOptions.Center; - return view; - } - - public static T CenterY(this T view) where T : SkiaControl - { - view.VerticalOptions = LayoutOptions.Center; - return view; - } - -} - -public static class GridExtensions -{ - public static T WithRow(this T view, int row) where T : SkiaControl - { - Grid.SetRow(view, row); - return view; - } - - public static T WithColumn(this T view, int column) where T : SkiaControl - { - Grid.SetColumn(view, column); - return view; - } - - public static T WithRowSpan(this T view, int rowSpan) where T : SkiaControl - { - Grid.SetRowSpan(view, rowSpan); - return view; - } - - public static T WithColumnSpan(this T view, int columnSpan) where T : SkiaControl - { - Grid.SetColumnSpan(view, columnSpan); - return view; - } - - public static SkiaLayout WithColumnDefinitions(this SkiaLayout grid, string columnDefinitions) - { - var converter = new ColumnDefinitionCollectionTypeConverter(); - - if (converter.CanConvertFrom(typeof(string))) - { - var columns = (ColumnDefinitionCollection)converter.ConvertFromInvariantString(columnDefinitions); - grid.ColumnDefinitions = columns; - } - else - { - throw new InvalidOperationException("ColumnDefinitionCollectionTypeConverter cannot convert from string."); - } - return grid; - } - public static SkiaLayout WithRowDefinitions(this SkiaLayout grid, string definitions) - { - var converter = new ColumnDefinitionCollectionTypeConverter(); - - if (converter.CanConvertFrom(typeof(string))) - { - var defs = (RowDefinitionCollection)converter.ConvertFromInvariantString(definitions); - grid.RowDefinitions = defs; - } - else - { - throw new InvalidOperationException("RowDefinitionCollectionTypeConverter cannot convert from string."); - } - return grid; - } -} - public partial class SkiaLayout { public class SkiaGridStructure diff --git a/src/Engine/Draw/Layout/SkiaLayout.cs b/src/Engine/Draw/Layout/SkiaLayout.cs index ec35b81a..6e78b40e 100644 --- a/src/Engine/Draw/Layout/SkiaLayout.cs +++ b/src/Engine/Draw/Layout/SkiaLayout.cs @@ -8,6 +8,30 @@ namespace DrawnUi.Maui.Draw public partial class SkiaLayout : SkiaControl, ISkiaGestureListener, ISkiaGridLayout { + /// + /// For easier code-behind use + /// + /// + /// + /// + public override ISkiaGestureListener OnSkiaGestureEvent(SkiaGesturesParameters args, GestureEventProcessingInfo apply) + { + if (!CanDraw) + return null; + + if (OnGestures != null) + { + return OnGestures(args, apply); + } + + return base.OnSkiaGestureEvent(args, apply); + } + + /// + /// Delegate for use instead of calling base.OnSkiaGestureEvent + /// + public Func OnGestures; + public override bool IsGestureForChild(SkiaControlWithRect child, SKPoint point) { if (this.IsStack) @@ -978,6 +1002,8 @@ public override void OnDisposing() StackStructure?.Clear(); StackStructureMeasured?.Clear(); + OnGestures = null; + base.OnDisposing(); } diff --git a/src/Engine/Internals/Extensions/FluentExtensions.cs b/src/Engine/Internals/Extensions/FluentExtensions.cs new file mode 100644 index 00000000..a6787010 --- /dev/null +++ b/src/Engine/Internals/Extensions/FluentExtensions.cs @@ -0,0 +1,89 @@ +namespace DrawnUi.Maui.Draw +{ + public static class FluentExtensions + { + public static T With(this T view, Action action) where T : SkiaControl + { + action?.Invoke(view); + return view; + } + + public static T WithParent(this T view, IDrawnBase parent) where T : SkiaControl + { + parent.AddSubView(view); + return view; + } + + public static T CenterX(this T view) where T : SkiaControl + { + view.HorizontalOptions = LayoutOptions.Center; + return view; + } + + public static T CenterY(this T view) where T : SkiaControl + { + view.VerticalOptions = LayoutOptions.Center; + return view; + } + + + #region GRID + + public static T WithRow(this T view, int row) where T : SkiaControl + { + Grid.SetRow(view, row); + return view; + } + + public static T WithColumn(this T view, int column) where T : SkiaControl + { + Grid.SetColumn(view, column); + return view; + } + + public static T WithRowSpan(this T view, int rowSpan) where T : SkiaControl + { + Grid.SetRowSpan(view, rowSpan); + return view; + } + + public static T WithColumnSpan(this T view, int columnSpan) where T : SkiaControl + { + Grid.SetColumnSpan(view, columnSpan); + return view; + } + + public static SkiaLayout WithColumnDefinitions(this SkiaLayout grid, string columnDefinitions) + { + var converter = new ColumnDefinitionCollectionTypeConverter(); + + if (converter.CanConvertFrom(typeof(string))) + { + var columns = (ColumnDefinitionCollection)converter.ConvertFromInvariantString(columnDefinitions); + grid.ColumnDefinitions = columns; + } + else + { + throw new InvalidOperationException("ColumnDefinitionCollectionTypeConverter cannot convert from string."); + } + return grid; + } + public static SkiaLayout WithRowDefinitions(this SkiaLayout grid, string definitions) + { + var converter = new ColumnDefinitionCollectionTypeConverter(); + + if (converter.CanConvertFrom(typeof(string))) + { + var defs = (RowDefinitionCollection)converter.ConvertFromInvariantString(definitions); + grid.RowDefinitions = defs; + } + else + { + throw new InvalidOperationException("RowDefinitionCollectionTypeConverter cannot convert from string."); + } + return grid; + } + + #endregion + } +} diff --git a/src/samples/Sandbox/App.xaml b/src/samples/Sandbox/App.xaml index 3638ba9f..e31c9cc4 100644 --- a/src/samples/Sandbox/App.xaml +++ b/src/samples/Sandbox/App.xaml @@ -3,8 +3,8 @@ x:Class="Sandbox.App" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:xaml="clr-namespace:DrawnUi.Maui.Infrastructure.Xaml;assembly=DrawnUi.Maui" - xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"> + xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" + xmlns:xaml="clr-namespace:DrawnUi.Maui.Infrastructure.Xaml;assembly=DrawnUi.Maui"> diff --git a/src/samples/Sandbox/MainPageCodeBehindEmpty.cs b/src/samples/Sandbox/MainPageCodeBehindEmpty.cs new file mode 100644 index 00000000..4d63374c --- /dev/null +++ b/src/samples/Sandbox/MainPageCodeBehindEmpty.cs @@ -0,0 +1,51 @@ +using Sandbox.Views; +using Canvas = DrawnUi.Maui.Views.Canvas; + +namespace Sandbox +{ + public class MainPageCodeBehindEmpty : BasePage, IDisposable + { + Canvas Canvas; + + public void Dispose() + { + this.Content = null; + Canvas?.Dispose(); + } + + public MainPageCodeBehindEmpty() + { +#if DEBUG + HotReloadService.UpdateApplicationEvent += ReloadUI; +#endif + Build(); + } + + private int _reloads; + + void Build() + { + Canvas?.Dispose(); + + Canvas = new Canvas() + { + VerticalOptions = LayoutOptions.Fill, + HorizontalOptions = LayoutOptions.Fill, + BackgroundColor = Colors.LightGray + }; + + _reloads++; + + this.Content = Canvas; + } + + private void ReloadUI(Type[] obj) + { + MainThread.BeginInvokeOnMainThread(() => + { + Build(); + }); + } + + } +} diff --git a/src/samples/Sandbox/MainPageCodeForVideo.cs b/src/samples/Sandbox/MainPageCodeForVideo.cs new file mode 100644 index 00000000..affa8bf5 --- /dev/null +++ b/src/samples/Sandbox/MainPageCodeForVideo.cs @@ -0,0 +1,143 @@ +using AppoMobi.Maui.Gestures; +using Sandbox.Views; +using Canvas = DrawnUi.Maui.Views.Canvas; + +namespace Sandbox +{ + public class MainPageCodeForVideo : BasePage, IDisposable + { + Canvas Canvas; + + public void Dispose() + { + this.Content = null; + Canvas?.Dispose(); + } + + public MainPageCodeForVideo() + { +#if DEBUG + HotReloadService.UpdateApplicationEvent += ReloadUI; +#endif + Build(); + } + + private int _reloads; + + private int _counter; + private SkiaLabel _label; + + void Build() + { + Canvas?.Dispose(); + + Canvas = new Canvas() + { + Gestures = GesturesMode.Enabled, + BackgroundColor = Colors.LightGray, + Content = new SkiaLayout() + { + UseCache = SkiaCacheType.Image, + Children = new List() + { + new SkiaShape() + { + MinimumWidthRequest = 200, + Margin = 10, + StrokeColor = Colors.Black, + StrokeWidth = 1, + CornerRadius = 16, + BackgroundColor = Colors.CadetBlue, + Padding = new Thickness(16,8), + Content = new SkiaLayout() + { + Spacing = 12, + HorizontalOptions = LayoutOptions.Center, + Type = LayoutType.Row, + Children = new List() + { + new SkiaSvg() + { + WidthRequest = 18, + LockRatio = 1, + SvgString = "\r\n\r\n\t\r\n\t\r\n\r\n", + TintColor = Colors.White + }, + new SkiaLabel() + { + TranslationY = -1, + TextColor = Colors.White, + VerticalOptions = LayoutOptions.Center, + Text="Placeholder" + }.With((c) => + { + _label = c; + }) + } + } + }.With((c) => + { + c.Shadows.Add(new () + { + Color = Colors.Black, + Blur = 2, + X = 2, Y=2 + }); + c.OnGestures = (args, info) => + { + if (args.Type == TouchActionResult.Down) + { + c.TranslationX = 1; + c.TranslationY = 1; + + var shadow = c.Shadows[0]; + shadow.X = 0; + shadow.X = 0; + + c.BackgroundColor = Colors.Blue; + + return c; + } + + if (args.Type == TouchActionResult.Up) + { + c.BackgroundColor = Colors.CadetBlue; + + + c.TranslationX = 0; + c.TranslationY = 0; + + var shadow = c.Shadows[0]; + shadow.X = 2; + shadow.X = 2; + } + + if (args.Type == TouchActionResult.Tapped) + { + _label.Text = $"Was tapped {_counter++} time(s)"; + + return c; + } + + return null; + }; + }) + } + } + }; + + _reloads++; + + this.Content = Canvas; + } + + private void ReloadUI(Type[] obj) + { + MainThread.BeginInvokeOnMainThread(() => + { + Build(); + }); + } + + } +} diff --git a/src/samples/Sandbox/Views/MainPageShader.xaml b/src/samples/Sandbox/Views/MainPageShader.xaml index 8b97feec..aa0a1879 100644 --- a/src/samples/Sandbox/Views/MainPageShader.xaml +++ b/src/samples/Sandbox/Views/MainPageShader.xaml @@ -102,7 +102,7 @@ SecondarySource="Images/leather.jpg" VerticalMargin="{Binding Source={x:Reference Texture}, Path=Margins.Top}"> - + - +