diff --git a/Maui.Tabs/BottomTabItem.xaml b/Maui.Tabs/BottomTabItem.xaml index cd656ad..cbe50eb 100644 --- a/Maui.Tabs/BottomTabItem.xaml +++ b/Maui.Tabs/BottomTabItem.xaml @@ -1,12 +1,11 @@  - + @@ -24,32 +23,29 @@ - + - + - diff --git a/Maui.Tabs/Effects/Commands.cs b/Maui.Tabs/Effects/Commands.cs index 135a892..2dac6dc 100644 --- a/Maui.Tabs/Effects/Commands.cs +++ b/Maui.Tabs/Effects/Commands.cs @@ -76,6 +76,17 @@ public static object GetLongTapParameter(BindableObject view) { return view.GetValue(LongTapParameterProperty); } + public static void UnregisterEffect(BindableObject bindable) + { + if (!(bindable is View view)) + return; + + var eff = view.Effects.FirstOrDefault(e => e is CommandsRoutingEffect); + + if (eff == null) return; + view.Effects.Remove(eff); + } + static void PropertyChanged(BindableObject bindable, object oldValue, object newValue) { if (!(bindable is View view)) return; @@ -93,7 +104,7 @@ static void PropertyChanged(BindableObject bindable, object oldValue, object new } } else { - if (eff == null || view.BindingContext == null) return; + if (eff == null) return; view.Effects.Remove(eff); if (EffectsConfig.AutoChildrenInputTransparent && bindable is Layout && EffectsConfig.GetChildrenInputTransparent(view)) { diff --git a/Maui.Tabs/Effects/TouchEffect.cs b/Maui.Tabs/Effects/TouchEffect.cs index 88568bc..8a8d6f1 100644 --- a/Maui.Tabs/Effects/TouchEffect.cs +++ b/Maui.Tabs/Effects/TouchEffect.cs @@ -1,4 +1,6 @@ -namespace Sharpnado.Tabs.Effects { +using Microsoft.Maui.Controls; + +namespace Sharpnado.Tabs.Effects { public static class TouchEffect { public static readonly BindableProperty ColorProperty = @@ -6,7 +8,7 @@ public static class TouchEffect { "Color", typeof(Color), typeof(TouchEffect), - Colors.Transparent, + KnownColor.Accent, propertyChanged: PropertyChanged ); @@ -18,6 +20,17 @@ public static Color GetColor(BindableObject view) { return (Color)view.GetValue(ColorProperty); } + public static void UnregisterEffect(BindableObject bindable) + { + if (!(bindable is View view)) + return; + + var eff = view.Effects.FirstOrDefault(e => e is TouchRoutingEffect); + + if (eff == null) return; + view.Effects.Remove(eff); + } + static void PropertyChanged(BindableObject bindable, object oldValue, object newValue) { if (!(bindable is View view)) return; @@ -34,7 +47,7 @@ static void PropertyChanged(BindableObject bindable, object oldValue, object new } } else { - if (eff == null || view.BindingContext == null) return; + if (eff == null) return; view.Effects.Remove(eff); if (EffectsConfig.AutoChildrenInputTransparent && bindable is Layout && EffectsConfig.GetChildrenInputTransparent(view)) { diff --git a/Maui.Tabs/MaterialUnderlinedTabItem.xaml b/Maui.Tabs/MaterialUnderlinedTabItem.xaml index b70ec55..ed1222b 100644 --- a/Maui.Tabs/MaterialUnderlinedTabItem.xaml +++ b/Maui.Tabs/MaterialUnderlinedTabItem.xaml @@ -1,11 +1,10 @@  - + @@ -44,33 +43,28 @@ - - - - + - + diff --git a/Maui.Tabs/Maui.Tabs.csproj b/Maui.Tabs/Maui.Tabs.csproj index 183d4f9..d034962 100644 --- a/Maui.Tabs/Maui.Tabs.csproj +++ b/Maui.Tabs/Maui.Tabs.csproj @@ -22,9 +22,9 @@ $(AssemblyName) ($(TargetFramework)) - 3.0.1 - 3.0.1 - 3.0.1 + 3.1.0 + 3.1.0 + 3.1.0 true en @@ -41,7 +41,7 @@ docs\ReadMe.md maui dotnetmaui xamarin.forms android ios uwp netstandard tabs segmented control bottombar fixed tabs scrollable tabs badge - First MAUI release \o/ + Fixes for touch effects on tabs "Pure" MAUI Tabs (no renderers) diff --git a/Maui.Tabs/Platforms/Android/CommandsPlatform.cs b/Maui.Tabs/Platforms/Android/CommandsPlatform.cs index e88da21..111e569 100644 --- a/Maui.Tabs/Platforms/Android/CommandsPlatform.cs +++ b/Maui.Tabs/Platforms/Android/CommandsPlatform.cs @@ -20,7 +20,8 @@ namespace Sharpnado.Tabs.Effects.Droid { public class CommandsPlatform : PlatformEffect { public View View => Control ?? Container; - public bool IsDisposed => (Container as IVisualElementRenderer)?.Element == null; + public bool IsDisposed => Container is null + || (Container is Java.Lang.Object javaContainer) && javaContainer.Handle == IntPtr.Zero; DateTime _tapTime; readonly Rect _rect = new Rect(); @@ -33,7 +34,7 @@ protected override void OnAttached() { View.Clickable = true; View.LongClickable = true; View.SoundEffectsEnabled = true; - TouchCollector.Add(View, OnTouch); + TouchCollector.Add(View, OnTouch, ActionType.Tap); } void OnTouch(View.TouchEventArgs args) { @@ -83,7 +84,7 @@ void LongClickHandler() { protected override void OnDetached() { if (IsDisposed) return; - TouchCollector.Delete(View, OnTouch); + TouchCollector.Delete(View, OnTouch, ActionType.Tap); } } } diff --git a/Maui.Tabs/Platforms/Android/TouchCollector.cs b/Maui.Tabs/Platforms/Android/TouchCollector.cs index b9ed4cc..4cfbb44 100644 --- a/Maui.Tabs/Platforms/Android/TouchCollector.cs +++ b/Maui.Tabs/Platforms/Android/TouchCollector.cs @@ -11,37 +11,52 @@ using View = Android.Views.View; namespace Sharpnado.Tabs.Effects.Droid.GestureCollectors { + enum ActionType + { + Ripple = 0, + Tap = 1, + } + + record ActionSource(ActionType ActionType, Action Action); + internal static class TouchCollector { - static Dictionary>> Collection { get; } = - new Dictionary>>(); + static Dictionary> Collection { get; } = + new (); static View _activeView; - public static void Add(View view, Action action) { + public static void Add(View view, Action action, ActionType actionType) { if (Collection.ContainsKey(view)) { - Collection[view].Add(action); + Collection[view].Add(new ActionSource(actionType, action)); } else { view.Touch += ActionActivator; - Collection.Add(view, new List> { action }); + Collection.Add(view, [ new(actionType, action) ] ); } } - public static void Delete(View view, Action action) { + public static void Delete(View view, Action action, ActionType actionType) { if (!Collection.ContainsKey(view)) return; var actions = Collection[view]; - actions.Remove(action); + actions.Remove(new ActionSource(actionType, action)); if (actions.Count != 0) return; view.Touch -= ActionActivator; Collection.Remove(view); } - static void ActionActivator(object sender, View.TouchEventArgs e) { + static async void ActionActivator(object sender, View.TouchEventArgs e) { var view = (View)sender; if (!Collection.ContainsKey(view) || (_activeView != null && _activeView != view)) return; + var actions = Collection[view].ToArray(); + bool rippleRan = false; + foreach (var valueAction in actions.Where(source => source.ActionType == ActionType.Ripple)) { + valueAction?.Action.Invoke(e); + rippleRan = true; + } + switch (e.Event.Action) { case MotionEventActions.Down: _activeView = view; @@ -55,9 +70,13 @@ static void ActionActivator(object sender, View.TouchEventArgs e) { break; } - var actions = Collection[view].ToArray(); - foreach (var valueAction in actions) { - valueAction?.Invoke(e); + if (rippleRan) + { + await Task.Delay(200); + } + + foreach (var valueAction in actions.Where(source => source.ActionType == ActionType.Tap)) { + valueAction?.Action.Invoke(e); } } } diff --git a/Maui.Tabs/Platforms/Android/TouchEffectPlatform.cs b/Maui.Tabs/Platforms/Android/TouchEffectPlatform.cs index e618481..33a5249 100644 --- a/Maui.Tabs/Platforms/Android/TouchEffectPlatform.cs +++ b/Maui.Tabs/Platforms/Android/TouchEffectPlatform.cs @@ -55,7 +55,7 @@ protected override void OnAttached() { _viewOverlay.Background = CreateRipple(_color); SetEffectColor(); - TouchCollector.Add(View, OnTouch); + TouchCollector.Add(View, OnTouch, ActionType.Ripple); group.AddView(_viewOverlay); _viewOverlay.BringToFront(); @@ -78,7 +78,7 @@ protected override void OnDetached() { if (EnableRipple) _ripple?.Dispose(); - TouchCollector.Delete(View, OnTouch); + TouchCollector.Delete(View, OnTouch, ActionType.Ripple); } protected override void OnElementPropertyChanged(PropertyChangedEventArgs e) { diff --git a/Maui.Tabs/Platforms/iOS/TintableImageEffect.cs b/Maui.Tabs/Platforms/iOS/TintableImageEffect.cs index a6e3292..b12abd9 100644 --- a/Maui.Tabs/Platforms/iOS/TintableImageEffect.cs +++ b/Maui.Tabs/Platforms/iOS/TintableImageEffect.cs @@ -88,7 +88,7 @@ private void UpdateColor() private async Task DelayedPost(int milliseconds, Action action) { await Task.Delay(milliseconds); - Device.BeginInvokeOnMainThread(action); + MainThread.BeginInvokeOnMainThread(action); } } } diff --git a/Maui.Tabs/Platforms/iOS/TouchEffectPlatform.cs b/Maui.Tabs/Platforms/iOS/TouchEffectPlatform.cs index 02397ce..e41cf2d 100644 --- a/Maui.Tabs/Platforms/iOS/TouchEffectPlatform.cs +++ b/Maui.Tabs/Platforms/iOS/TouchEffectPlatform.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -using Microsoft.Maui.Controls.Compatibility.Platform.iOS; using Microsoft.Maui.Controls.Platform; +using Microsoft.Maui.Platform; using ObjCRuntime; using Sharpnado.Tabs.Effects.iOS.GestureCollectors; using Sharpnado.Tabs.Effects.iOS.GestureRecognizers; @@ -80,7 +80,7 @@ void UpdateEffectColor() { InternalLogger.Debug($"UpdateEffectColor"); _alpha = color.Alpha < 1.0 ? 1 : (float)0.3; - _layer.BackgroundColor = color.ToUIColor(); + _layer.BackgroundColor = color.ToPlatform(); } void BringLayer() { diff --git a/Maui.Tabs/UnderlinedTabItem.xaml b/Maui.Tabs/UnderlinedTabItem.xaml index 5470815..d4a0a91 100644 --- a/Maui.Tabs/UnderlinedTabItem.xaml +++ b/Maui.Tabs/UnderlinedTabItem.xaml @@ -1,11 +1,10 @@  - + @@ -23,29 +22,26 @@ - + - + - diff --git a/MauiSample/App.xaml.cs b/MauiSample/App.xaml.cs index 9d81503..a0989ba 100644 --- a/MauiSample/App.xaml.cs +++ b/MauiSample/App.xaml.cs @@ -11,5 +11,6 @@ public App(BottomTabsPageViewModel mainViewModel) Current!.UserAppTheme = AppTheme.Dark; MainPage = new MainPage(mainViewModel); + // MainPage = new Playground(); } } diff --git a/MauiSample/MainPage.xaml b/MauiSample/MainPage.xaml index 44e97e6..cfd9e30 100644 --- a/MauiSample/MainPage.xaml +++ b/MauiSample/MainPage.xaml @@ -29,9 +29,9 @@ - + - @@ -77,7 +77,8 @@ Orientation="Horizontal" SegmentedOutlineColor="{StaticResource Gray950}" SelectedIndex="{Binding Source={x:Reference Switcher}, Path=SelectedIndex, Mode=TwoWay}" - TabType="Fixed"> + TabType="Fixed" + UseMauiTapGesture="True"> (BottomTabsPageViewModel)BindingContext; diff --git a/MauiSample/MauiSample.sln b/MauiSample/MauiSample.sln index 8f2b1f7..e94d5a6 100644 --- a/MauiSample/MauiSample.sln +++ b/MauiSample/MauiSample.sln @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "configuration", "configurat ..\Sharpnado.Tabs.nuspec = ..\Sharpnado.Tabs.nuspec EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MauiShellSample", "..\MauiShellSample\MauiShellSample.csproj", "{75F26D59-6560-4B75-94ED-7A1947BEBF9A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MauiShellSample", "..\MauiShellSample\MauiShellSample.csproj", "{75F26D59-6560-4B75-94ED-7A1947BEBF9A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -35,6 +35,7 @@ Global {528FB5B5-CBFA-4820-B915-937F2578807C}.Release|Any CPU.Build.0 = Release|Any CPU {75F26D59-6560-4B75-94ED-7A1947BEBF9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75F26D59-6560-4B75-94ED-7A1947BEBF9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75F26D59-6560-4B75-94ED-7A1947BEBF9A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {75F26D59-6560-4B75-94ED-7A1947BEBF9A}.Release|Any CPU.ActiveCfg = Release|Any CPU {75F26D59-6560-4B75-94ED-7A1947BEBF9A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/MauiSample/Playground.xaml b/MauiSample/Playground.xaml new file mode 100644 index 0000000..76b3f9d --- /dev/null +++ b/MauiSample/Playground.xaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MauiSample/Playground.xaml.cs b/MauiSample/Playground.xaml.cs new file mode 100644 index 0000000..a3d12de --- /dev/null +++ b/MauiSample/Playground.xaml.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MauiSample; + +public partial class Playground : ContentPage +{ + public Playground() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/MauiSample/Presentation/CustomViews/RichLabel.cs b/MauiSample/Presentation/CustomViews/RichLabel.cs index ba9a56b..d345178 100644 --- a/MauiSample/Presentation/CustomViews/RichLabel.cs +++ b/MauiSample/Presentation/CustomViews/RichLabel.cs @@ -53,7 +53,6 @@ private static void OnMarkdownPropertyChanged(BindableObject bindable, object ol private static void OnBoldFontFamilyPropertyChanged(BindableObject bindable, object oldValue, object newValue) { - throw new System.NotImplementedException(); } private void ProcessMarkdown(string markdown) diff --git a/MauiSample/Presentation/ViewModels/TabsLayout/BottomTabsPageViewModel.cs b/MauiSample/Presentation/ViewModels/TabsLayout/BottomTabsPageViewModel.cs index b8ab142..cf7d355 100644 --- a/MauiSample/Presentation/ViewModels/TabsLayout/BottomTabsPageViewModel.cs +++ b/MauiSample/Presentation/ViewModels/TabsLayout/BottomTabsPageViewModel.cs @@ -6,7 +6,7 @@ namespace MauiSample.Presentation.ViewModels { public class BottomTabsPageViewModel : ANavigableViewModel { - private int _selectedViewModelIndex = 0; + private int _selectedViewModelIndex; public BottomTabsPageViewModel( INavigationService navigationService, @@ -16,6 +16,9 @@ public BottomTabsPageViewModel( { HomePageViewModel = new HomePageViewModel(navigationService, sillyDudeService); GridPageViewModel = new GridPageViewModel(navigationService, sillyDudeService); + + // If you want to start at the 3rd page + // SelectedViewModelIndex = 2; } public int SelectedViewModelIndex diff --git a/MauiSample/Presentation/Views/TabM.xaml b/MauiSample/Presentation/Views/TabM.xaml index f6b96ff..b5b2786 100644 --- a/MauiSample/Presentation/Views/TabM.xaml +++ b/MauiSample/Presentation/Views/TabM.xaml @@ -7,7 +7,7 @@ - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MauiSample/VerticalTabItem.xaml.cs b/MauiSample/VerticalTabItem.xaml.cs new file mode 100644 index 0000000..d492058 --- /dev/null +++ b/MauiSample/VerticalTabItem.xaml.cs @@ -0,0 +1,58 @@ +using Sharpnado.Tabs; +using System.Runtime.CompilerServices; + +namespace MauiSample +{ + public partial class VerticalTabItem : TabTextItem + { + public static readonly BindableProperty SelectorColorProperty = BindableProperty.Create( + nameof(SelectorColor), + typeof(Color), + typeof(TabTextItem), + Colors.DodgerBlue); + + public Color SelectorColor + { + get => (Color)GetValue(SelectorColorProperty); + set => SetValue(SelectorColorProperty, value); + } + + public VerticalTabItem() + { + InitializeComponent(); + + } + + protected override void OnBadgeChanged(BadgeView oldBadge) + { + if (oldBadge != null) + { + Grid.Children.Remove(Badge); + return; + } + + Grid.Children.Add(Badge); + } + + protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + base.OnPropertyChanged(propertyName); + + switch (propertyName) + { + case nameof(IsSelectable): + case nameof(UnselectedLabelColor): + case nameof(SelectedTabColor): + case nameof(IsSelected): + UpdateColors(); + break; + } + } + + private void UpdateColors() + { + InnerLabel.TextColor = IsSelectable ? IsSelected ? SelectedTabColor : UnselectedLabelColor : DisabledLabelColor; + Selector.BackgroundColor = SelectorColor; + } + } +} \ No newline at end of file diff --git a/MauiShellSample/App.xaml b/MauiShellSample/App.xaml index 76d14bc..6a00270 100644 --- a/MauiShellSample/App.xaml +++ b/MauiShellSample/App.xaml @@ -1,8 +1,8 @@ - - + + xmlns:local="clr-namespace:MauiShellSample"> diff --git a/MauiShellSample/AppShell.xaml b/MauiShellSample/AppShell.xaml index 701e557..2c333cc 100644 --- a/MauiShellSample/AppShell.xaml +++ b/MauiShellSample/AppShell.xaml @@ -1,24 +1,21 @@  - + - + - + diff --git a/MauiShellSample/MainPage.xaml b/MauiShellSample/MainPage.xaml index d858a21..28ee679 100644 --- a/MauiShellSample/MainPage.xaml +++ b/MauiShellSample/MainPage.xaml @@ -1,35 +1,29 @@  - + - - + + -