* limitations under the License.
*
*/
+using System;
using System.ComponentModel;
using Tizen.NUI.Binding;
using Tizen.NUI.Components;
/// <summary>
/// A class that helps binding a non-selector property in View to selector property in ViewStyle.
/// </summary>
- internal class ViewSelector<T> where T : class, Tizen.NUI.Internal.ICloneable
+ internal class ViewSelector<T>
{
- private Selector<T> selector;
- private View view;
- private View.ControlStateChangesDelegate controlStateChanged;
+ protected Selector<T> selector;
+ protected View view;
+ protected View.ControlStateChangesDelegate controlStateChanged;
internal ViewSelector(View view, View.ControlStateChangesDelegate controlStateChanged)
{
internal T GetValue()
{
- return selector == null ? null : selector.GetValue(view.ControlState);
+ return selector == null ? default(T) : selector.GetValue(view.ControlState);
}
- internal void Clone(object value)
+ internal void Set(object value)
{
bool hadMultiValue = HasMultiValue();
var type = value?.GetType();
if (type == typeof(T))
{
- selector = new Selector<T>();
- selector.All = (T)((T)value).Clone();
+ CopyValueToSelector((T)value);
}
else if (type == typeof(Selector<T>))
{
- selector = new Selector<T>();
- selector.Clone<T>((Selector<T>)value);
+ CopySelectorToSelector((Selector<T>)value);
+ }
+ else if (type == Nullable.GetUnderlyingType(typeof(T)))
+ {
+ CopyValueToSelector((T)value);
}
else
{
}
}
+ protected virtual void CopyValueToSelector(T value)
+ {
+ selector = new Selector<T>();
+ selector.All = value;
+ }
+
+ protected virtual void CopySelectorToSelector(Selector<T> value)
+ {
+ selector = new Selector<T>();
+ selector.Clone(value);
+ }
+
internal void Clear()
{
if (HasMultiValue())
return selector == null;
}
- private bool HasMultiValue()
+ protected bool HasMultiValue()
{
return (selector != null && selector.All == null);
}
}
- internal static class SelectorHelper<T> where T : class, Tizen.NUI.Internal.ICloneable
+ /// <summary>
+ /// ViewSelector class for ICloneable type
+ /// </summary>
+ internal class CloneableViewSelector<T> : ViewSelector<T> where T : Tizen.NUI.Internal.ICloneable
+ {
+ internal CloneableViewSelector(View view, View.ControlStateChangesDelegate controlStateChanged) : base(view, controlStateChanged)
+ {
+ }
+
+ protected override void CopyValueToSelector(T value)
+ {
+ selector = new Selector<T>();
+ selector.All = (T)((T)value).Clone();
+ }
+
+ protected override void CopySelectorToSelector(Selector<T> value)
+ {
+ selector = new Selector<T>();
+ selector.Clone<T>((Selector<T>)value);
+ }
+ }
+
+ internal static class SelectorHelper
{
/// <summary>
/// For the object type of T or Selector T, convert it to Selector T and return the cloned one.
/// Otherwise, return null. <br/>
/// </summary>
- static internal Selector<T> Clone(object value)
+ static internal Selector<T> CopyCloneable<T>(object value) where T : class, Tizen.NUI.Internal.ICloneable
{
var type = value?.GetType();
}
/// <summary>
- /// For the object type of T or Selector T, convert it to T and return the cloned one.
+ /// For the value type of T or Selector T, convert it to Selector T and return the cloned one.
/// Otherwise, return null. <br/>
/// </summary>
- static internal T Clone(object value, View view)
+ static internal Selector<T> CopyValue<T>(object value)
{
var type = value?.GetType();
- if (type == typeof(T))
+ if (type == typeof(Selector<T>))
{
- return (T)((T)value).Clone();
+ var result = new Selector<T>();
+ result.Clone((Selector<T>)value);
+ return result;
}
- if (type == typeof(Selector<T>) && view != null && value != null)
+ if (type == typeof(T))
{
- Selector<T> selector = (Selector<T>)value;
- T valueInState = selector.GetValue(view.ControlState);
-
- return (T)valueInState?.Clone();
+ return new Selector<T>((T)value);
}
return null;
public static readonly BindableProperty ImageShadowProperty = BindableProperty.Create(nameof(ImageShadow), typeof(Selector<ImageShadow>), typeof(ViewStyle), null, propertyChanged: (bindable, oldValue, newValue) =>
{
var viewStyle = (ViewStyle)bindable;
- viewStyle.imageShadow = SelectorHelper<ImageShadow>.Clone(newValue);
+ viewStyle.imageShadow = SelectorHelper.CopyCloneable<ImageShadow>(newValue);
if (viewStyle.imageShadow != null) viewStyle.boxShadow = null;
},
public static readonly BindableProperty BoxShadowProperty = BindableProperty.Create(nameof(BoxShadow), typeof(Selector<ImageShadow>), typeof(ViewStyle), null, propertyChanged: (bindable, oldValue, newValue) =>
{
var viewStyle = (ViewStyle)bindable;
- viewStyle.boxShadow = SelectorHelper<Shadow>.Clone(newValue);
+ viewStyle.boxShadow = SelectorHelper.CopyCloneable<Shadow>(newValue);
if (viewStyle.boxShadow != null) viewStyle.imageShadow = null;
},
return viewStyle.boxShadow;
});
+ /// A BindableProperty for CornerRadius
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(Selector<float?>), typeof(ViewStyle), null, propertyChanged: (bindable, oldValue, newValue) =>
+ {
+ var viewStyle = (ViewStyle)bindable;
+ viewStyle.cornerRadius = SelectorHelper.CopyValue<float?>(newValue);
+ },
+ defaultValueCreator: (bindable) =>
+ {
+ var viewStyle = (ViewStyle)bindable;
+ return viewStyle.cornerRadius;
+ });
+
private string styleName;
private string backgroundImage;
private View.States? state;
private Selector<ImageShadow> imageShadow;
private Selector<Shadow> boxShadow;
private Selector<string> backgroundImageSelector;
+ private Selector<float?> cornerRadius;
private Selector<float?> opacitySelector;
private Selector<Color> backgroundColorSelector;
private Selector<Rectangle> backgroundImageBorderSelector;
set => SetValue(BoxShadowProperty, value);
}
+ /// <summary>
+ /// The radius for the rounded corners of the View
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Selector<float?> CornerRadius
+ {
+ get => (Selector<float?>)GetValue(CornerRadiusProperty);
+ set => SetValue(CornerRadiusProperty, value);
+ }
+
private void OnPaddingChanged(ushort start, ushort end, ushort top, ushort bottom)
{
Padding = new Extents(start, end, top, bottom);
private string[] transitionNames;
private Rectangle backgroundImageBorder;
- private ViewSelector<ImageShadow> imageShadow;
+ private CloneableViewSelector<ImageShadow> imageShadow;
- private ViewSelector<Shadow> boxShadow;
+ private CloneableViewSelector<Shadow> boxShadow;
+
+ private ViewSelector<float?> cornerRadius;
internal Size2D sizeSetExplicitly = new Size2D(); // Store size set by API, will be used in place of NaturalSize if not set.
}
/// <summary>
+ /// The radius for the rounded corners of the View.
+ /// This will rounds background and shadow edges.
+ /// Note that, an image background (or shadow) may not have rounded corners if it uses a Border property.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public float CornerRadius
+ {
+ get
+ {
+ float? value = (float?)GetValue(CornerRadiusProperty);
+ return value ?? 0;
+ }
+ set
+ {
+ SetValue(CornerRadiusProperty, value);
+ NotifyPropertyChanged();
+ }
+ }
+
+ /// <summary>
/// The current state of the view.
/// </summary>
/// <since_tizen> 3 </since_tizen>
if (newValue != null)
{
Tizen.NUI.Object.SetProperty(view.swigCPtr, View.Property.BACKGROUND, new Tizen.NUI.PropertyValue((Color)newValue));
+
+ // Apply CornerRadius if needs
+ if (view.cornerRadius != null && view.cornerRadius.GetValue() != 0)
+ {
+ view.ApplyCornerRadius();
+ }
}
},
defaultValueCreator: (bindable) =>
if (Rectangle.IsNullOrZero(view.backgroundImageBorder))
{
Tizen.NUI.Object.SetProperty(view.swigCPtr, View.Property.BACKGROUND, string.IsNullOrEmpty(url) ? new PropertyValue() : new PropertyValue(url));
+
+ // Apply CornerRadius if needs
+ if (view.cornerRadius != null && view.cornerRadius.GetValue() != 0)
+ {
+ view.ApplyCornerRadius();
+ }
}
else
{
if (Rectangle.IsNullOrZero(view.backgroundImageBorder))
{
Tizen.NUI.Object.SetProperty(view.swigCPtr, View.Property.BACKGROUND, new PropertyValue(url));
+
+ // Apply CornerRadius if needs
+ if (view.cornerRadius != null && view.cornerRadius.GetValue() != 0)
+ {
+ view.ApplyCornerRadius();
+ }
}
else
{
var view = (View)bindable;
bool hadShadowExtents = view.HasShadowExtents();
- (view.imageShadow ?? (view.imageShadow = new ViewSelector<ImageShadow>(view, view.OnControlStateChangedForShadow))).Clone(newValue);
+ (view.imageShadow ?? (view.imageShadow = new CloneableViewSelector<ImageShadow>(view, view.OnControlStateChangedForShadow))).Set(newValue);
Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), ImageShadow.ToPropertyValue(view.imageShadow.GetValue(), view));
view.boxShadow?.Clear();
var view = (View)bindable;
bool hadShadowExtents = view.HasShadowExtents();
- (view.boxShadow ?? (view.boxShadow = new ViewSelector<Shadow>(view, view.OnControlStateChangedForShadow))).Clone(newValue);
+ (view.boxShadow ?? (view.boxShadow = new CloneableViewSelector<Shadow>(view, view.OnControlStateChangedForShadow))).Set(newValue);
Tizen.NUI.Object.SetProperty(view.swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), Shadow.ToPropertyValue(view.boxShadow.GetValue(), view));
view.imageShadow?.Clear();
});
/// <summary>
+ /// CornerRadius Property
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(View), default(float), propertyChanged: (bindable, oldValue, newValue) =>
+ {
+ var view = (View)bindable;
+
+ (view.cornerRadius ?? (view.cornerRadius = new ViewSelector<float?>(view, view.OnControlStateChangedForCornerRadius))).Set(newValue);
+
+ view.ApplyCornerRadius();
+
+ // Update shadow visual
+ view.ApplyShadow();
+ },
+ defaultValueCreator: (bindable) =>
+ {
+ var view = (View)bindable;
+ return view.cornerRadius?.GetValue();
+ });
+
+ /// <summary>
/// XamlStyleProperty
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
private void OnRelayoutForShadow(object sender, global::System.EventArgs e)
{
- UpdateShadowVisual();
+ ApplyShadow();
}
private void OnControlStateChangedForShadow(View obj, NUI.Components.ControlStates state)
{
- UpdateShadowVisual();
+ ApplyShadow();
}
- private void UpdateShadowVisual()
+ private void ApplyShadow()
{
ShadowBase shadow = (boxShadow != null && !boxShadow.IsEmpty()) ? (ShadowBase)boxShadow.GetValue() : (ShadowBase)imageShadow?.GetValue();
Tizen.NUI.Object.SetProperty(swigCPtr, Interop.ViewProperty.View_Property_SHADOW_get(), ShadowBase.ToPropertyValue(shadow, this));
}
+
+ private void OnControlStateChangedForCornerRadius(View obj, NUI.Components.ControlStates state)
+ {
+ ApplyCornerRadius();
+ ApplyShadow();
+ }
+
+ private void ApplyCornerRadius()
+ {
+ Tizen.NUI.PropertyMap backgroundMap = new Tizen.NUI.PropertyMap();
+ Tizen.NUI.Object.GetProperty(swigCPtr, View.Property.BACKGROUND).Get(backgroundMap);
+
+ if (!backgroundMap.Empty())
+ {
+ // TODO (NDalic.VISUAL_PROPERTY_MIX_COLOR + 3) to CornerRadius
+ backgroundMap[Visual.Property.CornerRadius] = new PropertyValue(cornerRadius.GetValue() ?? 0);
+ Tizen.NUI.Object.SetProperty(swigCPtr, View.Property.BACKGROUND, new Tizen.NUI.PropertyValue(backgroundMap));
+ }
+ }
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public ImageShadow() : base()
{
- propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.NPatch);
+ propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.Image);
}
internal ImageShadow(ImageShadow other, PropertyChangedCallback callback = null) : base(other)
{
- propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.NPatch);
+ propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.Image);
Url = other.Url;
Border = other.Border;
private void UpdateBorder()
{
+ if (Rectangle.IsNullOrZero(border))
+ {
+ propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.Image);
+ }
+ else
+ {
+ propertyMap[Visual.Property.Type] = new PropertyValue((int)Visual.Type.NPatch);
+ }
+
propertyMap[ImageVisualProperty.Border] = PropertyValue.CreateWithGuard(border);
OnPropertyChanged?.Invoke(this);
}
return new PropertyValue();
}
+ if (attachedView.CornerRadius > 0)
+ {
+ instance.propertyMap[Visual.Property.CornerRadius] = new PropertyValue(attachedView.CornerRadius);
+ }
+
instance.propertyMap[Visual.Property.Transform] = instance.GetTransformMap(attachedView);
return new PropertyValue(instance.propertyMap);
/// </summary>
/// <since_tizen> 5 </since_tizen>
public static readonly int VisualFittingMode = NDalic.VISUAL_PROPERTY_MIX_COLOR + 2;
+ /// <summary>
+ /// The fitting mode of the visual.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly int CornerRadius = NDalic.VISUAL_PROPERTY_MIX_COLOR + 3;
}
/// <summary>
--- /dev/null
+using Tizen.NUI;
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI.Components;
+
+namespace Tizen.NUI.Samples
+{
+ public class ControlSample : IExample
+ {
+ private View root;
+ private Control control;
+
+ public void Activate()
+ {
+ Window window = Window.Instance;
+
+ root = new View()
+ {
+ Size = window.Size,
+ BackgroundColor = new Color(0.8f, 0.8f, 0.8f, 0.6f),
+ ParentOrigin = ParentOrigin.Center,
+ PivotPoint = PivotPoint.Center,
+ PositionUsesPivotPoint = true,
+ };
+ window.Add(root);
+
+ control = new Control()
+ {
+ Size = new Size(100, 100),
+ BackgroundColor = Color.Blue,
+ ParentOrigin = ParentOrigin.Center,
+ PivotPoint = PivotPoint.Center,
+ PositionUsesPivotPoint = true,
+ BoxShadow = new Shadow()
+ {
+ Color = new Color(0.2f, 0.2f, 0.2f, 0.3f),
+ Offset = new Vector2(5, 5),
+ },
+ CornerRadius = 26f,
+ };
+
+ root.Add(control);
+ }
+
+ public void Deactivate()
+ {
+ if (root != null)
+ {
+ Window.Instance.Remove(root);
+ root.Dispose();
+ }
+ }
+ }
+}