Add `ITextButton` and related Mappers (#2868)
* textbutton_mapper * - cleanup * - fix stubs * - fix stubs
This commit is contained in:
Родитель
d9327d13ed
Коммит
dcc4cf0f14
|
@ -1,7 +1,9 @@
|
|||
namespace Microsoft.Maui.Controls
|
||||
{
|
||||
public partial class Button : IButton, IText
|
||||
public partial class Button : IButton, ITextButton, IImageButton
|
||||
{
|
||||
bool _wasImageLoading;
|
||||
|
||||
void IButton.Clicked()
|
||||
{
|
||||
(this as IButtonController).SendClicked();
|
||||
|
@ -17,13 +19,22 @@ namespace Microsoft.Maui.Controls
|
|||
(this as IButtonController).SendReleased();
|
||||
}
|
||||
|
||||
void IButton.ImageSourceLoaded()
|
||||
void IImageSourcePart.UpdateIsLoading(bool isLoading)
|
||||
{
|
||||
Handler?.UpdateValue(nameof(ContentLayout));
|
||||
if (!isLoading && _wasImageLoading)
|
||||
Handler?.UpdateValue(nameof(ContentLayout));
|
||||
|
||||
_wasImageLoading = isLoading;
|
||||
}
|
||||
|
||||
IImageSource IButton.ImageSource => ImageSource;
|
||||
|
||||
Font ITextStyle.Font => (Font)GetValue(FontElement.FontProperty);
|
||||
|
||||
Aspect IImage.Aspect => Aspect.Fill;
|
||||
|
||||
bool IImage.IsOpaque => true;
|
||||
|
||||
IImageSource IImageSourcePart.Source => ImageSource;
|
||||
|
||||
bool IImageSourcePart.IsAnimationPlaying => false;
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ namespace Microsoft.Maui.Controls
|
|||
[nameof(Padding)] = MapPadding,
|
||||
#endif
|
||||
#if WINDOWS
|
||||
[nameof(IText.Text)] = MapText,
|
||||
[nameof(IButton.ImageSource)] = MapImageSource
|
||||
[nameof(IText.Text)] = MapText,
|
||||
[nameof(ImageSource)] = MapImageSource
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -26,11 +26,5 @@ namespace Microsoft.Maui.Controls
|
|||
{
|
||||
(this as IButtonController).SendReleased();
|
||||
}
|
||||
|
||||
void IButton.ImageSourceLoaded()
|
||||
{
|
||||
}
|
||||
|
||||
IImageSource IButton.ImageSource => Source;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,6 @@ namespace Microsoft.Maui
|
|||
/// </summary>
|
||||
public interface IButton : IView, IPadding
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows you to display a bitmap image on the Button.
|
||||
/// </summary>
|
||||
IImageSource? ImageSource { get; }
|
||||
|
||||
|
||||
void ImageSourceLoaded();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the Button is pressed.
|
||||
/// </summary>
|
||||
|
|
|
@ -3,7 +3,7 @@ namespace Microsoft.Maui
|
|||
/// <summary>
|
||||
/// Provides functionality to be able to customize Text.
|
||||
/// </summary>
|
||||
public interface IText : ITextStyle, IElement
|
||||
public interface IText : ITextStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the text.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Maui
|
||||
{
|
||||
public interface ITextButton : IView, IButton, IText
|
||||
{
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace Microsoft.Maui
|
|||
/// <summary>
|
||||
/// Provides functionality to be able to customize the appearance of text.
|
||||
/// </summary>
|
||||
public interface ITextStyle : IElement
|
||||
public interface ITextStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the text color.
|
||||
|
|
|
@ -105,23 +105,17 @@ namespace Microsoft.Maui.Handlers
|
|||
handler.TypedNativeView?.UpdatePadding(button, DefaultPadding);
|
||||
}
|
||||
|
||||
public static void MapImageSource(IButtonHandler handler, IButton image) =>
|
||||
public static void MapImageSource(IButtonHandler handler, IImageButton image) =>
|
||||
MapImageSourceAsync(handler, image).FireAndForget(handler);
|
||||
|
||||
public static Task MapImageSourceAsync(IButtonHandler handler, IButton image)
|
||||
public static Task MapImageSourceAsync(IButtonHandler handler, IImageButton image)
|
||||
{
|
||||
if (image.ImageSource == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return handler.ImageSourceLoader.UpdateImageSourceAsync();
|
||||
}
|
||||
|
||||
void OnSetImageSource(Drawable? obj)
|
||||
{
|
||||
NativeView.Icon = obj;
|
||||
VirtualView?.ImageSourceLoaded();
|
||||
}
|
||||
|
||||
bool NeedsExactMeasure()
|
||||
|
|
|
@ -14,25 +14,25 @@ namespace Microsoft.Maui.Handlers
|
|||
{
|
||||
ImageSourcePartLoader? _imageSourcePartLoader;
|
||||
public ImageSourcePartLoader ImageSourceLoader =>
|
||||
_imageSourcePartLoader ??= new ImageSourcePartLoader(this, () => VirtualView?.ImageSource, OnSetImageSource);
|
||||
_imageSourcePartLoader ??= new ImageSourcePartLoader(this, () => (VirtualView as IImageButton), OnSetImageSource);
|
||||
|
||||
public static IPropertyMapper<ITextStyle, IButtonHandler> TextStyleMapper = new PropertyMapper<ITextStyle, IButtonHandler>(ViewHandler.ViewMapper)
|
||||
public static IPropertyMapper<IImageButton, IButtonHandler> ImageButtonMapper = new PropertyMapper<IImageButton, IButtonHandler>()
|
||||
{
|
||||
[nameof(IImageButton.Source)] = MapImageSource,
|
||||
};
|
||||
|
||||
public static IPropertyMapper<ITextButton, IButtonHandler> TextButtonMapper = new PropertyMapper<ITextButton, IButtonHandler>()
|
||||
{
|
||||
[nameof(ITextStyle.CharacterSpacing)] = MapCharacterSpacing,
|
||||
[nameof(ITextStyle.Font)] = MapFont,
|
||||
[nameof(ITextStyle.TextColor)] = MapTextColor,
|
||||
};
|
||||
|
||||
public static IPropertyMapper<IText, IButtonHandler> TextMapper = new PropertyMapper<IText, IButtonHandler>(TextStyleMapper)
|
||||
{
|
||||
[nameof(IText.Text)] = MapText,
|
||||
};
|
||||
|
||||
public static IPropertyMapper<IButton, IButtonHandler> Mapper = new PropertyMapper<IButton, IButtonHandler>(TextMapper)
|
||||
public static IPropertyMapper<IButton, IButtonHandler> Mapper = new PropertyMapper<IButton, IButtonHandler>(TextButtonMapper, ImageButtonMapper, ViewHandler.ViewMapper)
|
||||
{
|
||||
[nameof(IButton.Background)] = MapBackground,
|
||||
[nameof(IButton.Padding)] = MapPadding,
|
||||
[nameof(IButton.ImageSource)] = MapImageSource
|
||||
};
|
||||
|
||||
public ButtonHandler() : base(Mapper)
|
||||
|
|
|
@ -92,16 +92,14 @@ namespace Microsoft.Maui.Handlers
|
|||
{
|
||||
NativeView.SetImage(null, UIControlState.Normal);
|
||||
}
|
||||
|
||||
VirtualView.ImageSourceLoaded();
|
||||
}
|
||||
|
||||
public static void MapImageSource(IButtonHandler handler, IButton image) =>
|
||||
public static void MapImageSource(IButtonHandler handler, IImageButton image) =>
|
||||
MapImageSourceAsync(handler, image).FireAndForget(handler);
|
||||
|
||||
public static Task MapImageSourceAsync(IButtonHandler handler, IButton image)
|
||||
public static Task MapImageSourceAsync(IButtonHandler handler, IImageButton image)
|
||||
{
|
||||
if (image.ImageSource == null)
|
||||
if (image.Source == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace Microsoft.Maui.Handlers
|
|||
var map = imv.GetPropertyMapperOverrides();
|
||||
if (map is not null)
|
||||
{
|
||||
map.Chained = _defaultMapper;
|
||||
map.Chained = new[] { _defaultMapper };
|
||||
_mapper = map;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Microsoft.Maui.Handlers
|
|||
#if __IOS__
|
||||
UIKit.UIImageView IImageHandler.TypedNativeView => NativeView.ImageView;
|
||||
#elif WINDOWS
|
||||
UI.Xaml.Controls.Image IImageHandler.TypedNativeView => (UI.Xaml.Controls.Image)NativeView.Content;
|
||||
UI.Xaml.Controls.Image IImageHandler.TypedNativeView => NativeView.GetImage() ?? (UI.Xaml.Controls.Image)NativeView.Content;
|
||||
#elif __ANDROID__
|
||||
Android.Widget.ImageView IImageHandler.TypedNativeView => NativeView;
|
||||
#else
|
||||
|
|
|
@ -42,17 +42,6 @@ namespace Microsoft.Maui
|
|||
SetImage = setImage;
|
||||
}
|
||||
|
||||
internal ImageSourcePartLoader(
|
||||
IElementHandler handler,
|
||||
Func<IImageSource?> imageSource,
|
||||
Action<NativeImage?> setImage)
|
||||
{
|
||||
Handler = handler;
|
||||
var wrapper = new ImageSourcePartWrapper(imageSource);
|
||||
_imageSourcePart = () => wrapper;
|
||||
SetImage = setImage;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
SourceManager.Reset();
|
||||
|
@ -83,26 +72,5 @@ namespace Microsoft.Maui
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO MAUI: This is currently here so that Button can continue to use IImageSource
|
||||
// At a later point once we further define the interface for IButtonHandler we will probably
|
||||
// change IButton to return an IImageSourcePart and we can get rid of this class
|
||||
class ImageSourcePartWrapper : IImageSourcePart
|
||||
{
|
||||
readonly Func<IImageSource?> _imageSource;
|
||||
|
||||
public ImageSourcePartWrapper(Func<IImageSource?> imageSource)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
}
|
||||
|
||||
IImageSource? IImageSourcePart.Source => _imageSource.Invoke();
|
||||
|
||||
bool IImageSourcePart.IsAnimationPlaying => false;
|
||||
|
||||
void IImageSourcePart.UpdateIsLoading(bool isLoading)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace Microsoft.Maui
|
|||
image = contentImage;
|
||||
}
|
||||
// This means the users image hasn't loaded yet but we still want to setup the container for the user
|
||||
else if (button.ImageSource != null)
|
||||
else if (button is IImageButton ib && ib.Source != null)
|
||||
{
|
||||
image = nativeButton.GetImage() ?? new();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Microsoft.Maui
|
|||
{
|
||||
readonly Dictionary<string, Action<IElementHandler, IElement>> _mapper = new();
|
||||
|
||||
IPropertyMapper? _chained;
|
||||
IPropertyMapper[]? _chained;
|
||||
|
||||
// Keep a distinct list of the keys so we don't run any duplicate (overridden) updates more than once
|
||||
// when we call UpdateProperties
|
||||
|
@ -18,7 +18,7 @@ namespace Microsoft.Maui
|
|||
{
|
||||
}
|
||||
|
||||
public PropertyMapper(IPropertyMapper chained)
|
||||
public PropertyMapper(params IPropertyMapper[]? chained)
|
||||
{
|
||||
Chained = chained;
|
||||
}
|
||||
|
@ -40,9 +40,16 @@ namespace Microsoft.Maui
|
|||
if (_mapper.TryGetValue(key, out var action))
|
||||
return action;
|
||||
else if (Chained is not null)
|
||||
return Chained.GetProperty(key);
|
||||
else
|
||||
return null;
|
||||
{
|
||||
foreach(var ch in Chained)
|
||||
{
|
||||
var returnValue = ch.GetProperty(key);
|
||||
if (returnValue != null)
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UpdateProperty(IElementHandler viewHandler, IElement? virtualView, string property)
|
||||
|
@ -64,7 +71,7 @@ namespace Microsoft.Maui
|
|||
}
|
||||
}
|
||||
|
||||
public IPropertyMapper? Chained
|
||||
public IPropertyMapper[]? Chained
|
||||
{
|
||||
get => _chained;
|
||||
set
|
||||
|
@ -101,8 +108,9 @@ namespace Microsoft.Maui
|
|||
|
||||
if (Chained is not null)
|
||||
{
|
||||
foreach (var key in Chained.GetKeys())
|
||||
yield return key;
|
||||
foreach (var chain in Chained)
|
||||
foreach (var key in chain.GetKeys())
|
||||
yield return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +141,7 @@ namespace Microsoft.Maui
|
|||
{
|
||||
}
|
||||
|
||||
public PropertyMapper(IPropertyMapper chained)
|
||||
public PropertyMapper(params IPropertyMapper[] chained)
|
||||
: base(chained)
|
||||
{
|
||||
}
|
||||
|
@ -153,8 +161,17 @@ namespace Microsoft.Maui
|
|||
{
|
||||
if (v is TVirtualView vv)
|
||||
action?.Invoke((TViewHandler)h, vv);
|
||||
else
|
||||
Chained?.UpdateProperty(h, v, key);
|
||||
else if(Chained != null)
|
||||
{
|
||||
foreach(var chain in Chained)
|
||||
{
|
||||
if(chain.GetProperty(key) != null)
|
||||
{
|
||||
chain.UpdateProperty(h, v, key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -165,7 +182,7 @@ namespace Microsoft.Maui
|
|||
{
|
||||
}
|
||||
|
||||
public PropertyMapper(PropertyMapper chained)
|
||||
public PropertyMapper(params PropertyMapper[] chained)
|
||||
: base(chained)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ using Microsoft.Maui.Graphics;
|
|||
|
||||
namespace Microsoft.Maui.DeviceTests.Stubs
|
||||
{
|
||||
public partial class ButtonStub : StubBase, IButton, IText
|
||||
public partial class ButtonStub : StubBase, IButton, ITextButton, IImageButton
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
|
@ -17,6 +17,16 @@ namespace Microsoft.Maui.DeviceTests.Stubs
|
|||
|
||||
public IImageSource ImageSource { get; set; }
|
||||
|
||||
Aspect IImage.Aspect => Aspect.Fill;
|
||||
|
||||
bool IImage.IsOpaque => true;
|
||||
|
||||
IImageSource IImageSourcePart.Source => ImageSource;
|
||||
|
||||
bool IImageSourcePart.IsAnimationPlaying => false;
|
||||
|
||||
void IImageSourcePart.UpdateIsLoading(bool isLoading) { }
|
||||
|
||||
public event EventHandler Pressed;
|
||||
public event EventHandler Released;
|
||||
public event EventHandler Clicked;
|
||||
|
|
|
@ -47,10 +47,5 @@ namespace Microsoft.Maui.DeviceTests.Stubs
|
|||
void IButton.Pressed() => Pressed?.Invoke(this, EventArgs.Empty);
|
||||
void IButton.Released() => Released?.Invoke(this, EventArgs.Empty);
|
||||
void IButton.Clicked() => Clicked?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
void IButton.ImageSourceLoaded()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,9 +40,9 @@ namespace Microsoft.Maui.UnitTests
|
|||
[nameof(IView.Background)] = (r, v) => wasMapper1Called = true
|
||||
};
|
||||
|
||||
var mapper2 = new PropertyMapper<ITextStyle>(mapper1)
|
||||
var mapper2 = new PropertyMapper<ITextButton>(mapper1)
|
||||
{
|
||||
[nameof(ITextStyle.TextColor)] = (r, v) => wasMapper2Called = true
|
||||
[nameof(ITextButton.TextColor)] = (r, v) => wasMapper2Called = true
|
||||
};
|
||||
|
||||
mapper2.UpdateProperties(null, new Button());
|
||||
|
@ -51,6 +51,53 @@ namespace Microsoft.Maui.UnitTests
|
|||
Assert.True(wasMapper2Called);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ConstructorChainingMappersWorks()
|
||||
{
|
||||
bool wasMapper1Called = false;
|
||||
bool wasMapper2Called = false;
|
||||
var mapper1 = new PropertyMapper<IView>
|
||||
{
|
||||
[nameof(IView.Background)] = (r, v) => wasMapper1Called = true
|
||||
};
|
||||
|
||||
var mapper2 = new PropertyMapper<ITextButton>()
|
||||
{
|
||||
[nameof(ITextButton.TextColor)] = (r, v) => wasMapper2Called = true
|
||||
};
|
||||
|
||||
|
||||
new PropertyMapper<ITextButton>(mapper2, mapper1)
|
||||
.UpdateProperties(null, new Button());
|
||||
|
||||
Assert.True(wasMapper1Called);
|
||||
Assert.True(wasMapper2Called);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstructorChainingMappersOverrideBase()
|
||||
{
|
||||
bool wasMapper1Called = false;
|
||||
bool wasMapper2Called = false;
|
||||
var mapper1 = new PropertyMapper<IView>
|
||||
{
|
||||
[nameof(IView.Background)] = (r, v) => wasMapper1Called = true
|
||||
};
|
||||
|
||||
var mapper2 = new PropertyMapper<IButton>()
|
||||
{
|
||||
[nameof(IView.Background)] = (r, v) => wasMapper2Called = true
|
||||
};
|
||||
|
||||
new PropertyMapper<ITextButton>(mapper2, mapper1)
|
||||
.UpdateProperties(null, new Button());
|
||||
|
||||
Assert.False(wasMapper1Called);
|
||||
Assert.True(wasMapper2Called);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ChainingMappersStillAllowReplacingChainedRoot()
|
||||
{
|
||||
|
@ -62,9 +109,9 @@ namespace Microsoft.Maui.UnitTests
|
|||
[nameof(IView.Background)] = (r, v) => wasMapper1Called = true
|
||||
};
|
||||
|
||||
var mapper2 = new PropertyMapper<ITextStyle>(mapper1)
|
||||
var mapper2 = new PropertyMapper<ITextButton>(mapper1)
|
||||
{
|
||||
[nameof(ITextStyle.TextColor)] = (r, v) => wasMapper2Called = true
|
||||
[nameof(ITextButton.TextColor)] = (r, v) => wasMapper2Called = true
|
||||
};
|
||||
|
||||
mapper1[nameof(IView.Background)] = (r, v) => wasMapper3Called = true;
|
||||
|
|
|
@ -3,7 +3,7 @@ using Microsoft.Maui.Graphics;
|
|||
|
||||
namespace Microsoft.Maui.UnitTests
|
||||
{
|
||||
class ButtonStub : View, IButton, IText
|
||||
class ButtonStub : View, IButton, ITextButton, IImageButton
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
|
@ -22,6 +22,15 @@ namespace Microsoft.Maui.UnitTests
|
|||
public Font Font { get; set; }
|
||||
|
||||
public IImageSource ImageSource { get; set; }
|
||||
Aspect IImage.Aspect => Aspect.Fill;
|
||||
|
||||
bool IImage.IsOpaque => true;
|
||||
|
||||
IImageSource IImageSourcePart.Source => ImageSource;
|
||||
|
||||
bool IImageSourcePart.IsAnimationPlaying => false;
|
||||
|
||||
void IImageSourcePart.UpdateIsLoading(bool isLoading) { }
|
||||
|
||||
public void ImageSourceLoaded()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче