feat: add two attached properties to mark an custom element triggering drag move or close.

This commit is contained in:
rabbitism
2024-02-08 23:15:26 +08:00
parent f758ceb760
commit 974afa33c5
5 changed files with 128 additions and 22 deletions

View File

@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ursa.Demo.Dialogs" xmlns:local="clr-namespace:Ursa.Demo.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
d:DesignHeight="450" d:DesignHeight="450"
@@ -30,6 +31,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
Orientation="Horizontal" Orientation="Horizontal"
Spacing="8"> Spacing="8">
<Rectangle Width="10" Height="10" Fill="Red" u:DialogControlBase.CanClose="True"></Rectangle>
<Button Command="{Binding DialogCommand}" Content="Dialog" /> <Button Command="{Binding DialogCommand}" Content="Dialog" />
<Button Command="{Binding OKCommand}" Content="OK" /> <Button Command="{Binding OKCommand}" Content="OK" />
<Button Command="{Binding CancelCommand}" Content="Cancel" /> <Button Command="{Binding CancelCommand}" Content="Cancel" />

View File

@@ -1,8 +1,10 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Helpers;
using Ursa.Common; using Ursa.Common;
using Ursa.Controls.OverlayShared; using Ursa.Controls.OverlayShared;
@@ -29,17 +31,17 @@ public abstract class DialogControlBase: OverlayFeedbackElement
internal double? VerticalOffsetRatio { get; set; } internal double? VerticalOffsetRatio { get; set; }
internal bool CanClickOnMaskToClose { get; set; } internal bool CanClickOnMaskToClose { get; set; }
internal bool CanLightDismiss { get; set; } internal bool CanLightDismiss { get; set; }
internal bool CanDragMove { get; set; }
protected internal Button? _closeButton; protected internal Button? _closeButton;
private Panel? _titleArea; private Panel? _titleArea;
internal void SetAsModal(bool modal) #region Layer Management
{
PseudoClasses.Set(PC_Modal, modal);
}
public static readonly RoutedEvent<DialogLayerChangeEventArgs> LayerChangedEvent = RoutedEvent.Register<CustomDialogControl, DialogLayerChangeEventArgs>( public static readonly RoutedEvent<DialogLayerChangeEventArgs> LayerChangedEvent =
RoutedEvent.Register<CustomDialogControl, DialogLayerChangeEventArgs>(
nameof(LayerChanged), RoutingStrategies.Bubble); nameof(LayerChanged), RoutingStrategies.Bubble);
public event EventHandler<DialogLayerChangeEventArgs> LayerChanged public event EventHandler<DialogLayerChangeEventArgs> LayerChanged
{ {
add => AddHandler(LayerChangedEvent, value); add => AddHandler(LayerChangedEvent, value);
@@ -54,35 +56,133 @@ public abstract class DialogControlBase: OverlayFeedbackElement
} }
} }
#endregion
#region DragMove AttachedPropert
public static readonly AttachedProperty<bool> CanDragMoveProperty =
AvaloniaProperty.RegisterAttached<DialogControlBase, InputElement, bool>("CanDragMove");
public static void SetCanDragMove(InputElement obj, bool value) => obj.SetValue(CanDragMoveProperty, value);
public static bool GetCanDragMove(InputElement obj) => obj.GetValue(CanDragMoveProperty);
private static void OnCanDragMoveChanged(InputElement arg1, AvaloniaPropertyChangedEventArgs<bool> arg2)
{
if (arg2.NewValue.Value)
{
arg1.AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Bubble);
arg1.AddHandler(PointerMovedEvent, OnPointerMoved, RoutingStrategies.Bubble);
arg1.AddHandler(PointerReleasedEvent, OnPointerReleased, RoutingStrategies.Bubble);
}
else
{
arg1.RemoveHandler(PointerPressedEvent, OnPointerPressed);
arg1.RemoveHandler(PointerMovedEvent, OnPointerMoved);
arg1.RemoveHandler(PointerReleasedEvent, OnPointerReleased);
}
void OnPointerPressed(InputElement sender, PointerPressedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
void OnPointerMoved(InputElement sender, PointerEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
void OnPointerReleased(InputElement sender, PointerReleasedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
}
#endregion
#region Close AttachedProperty
public static readonly AttachedProperty<bool> CanCloseProperty =
AvaloniaProperty.RegisterAttached<DialogControlBase, InputElement, bool>("CanClose");
public static void SetCanClose(InputElement obj, bool value) => obj.SetValue(CanCloseProperty, value);
public static bool GetCanClose(InputElement obj) => obj.GetValue(CanCloseProperty);
private static void OnCanCloseChanged(InputElement arg1, AvaloniaPropertyChangedEventArgs<bool> arg2)
{
if (arg2.NewValue.Value)
{
arg1.AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Bubble);
}
void OnPointerPressed(InputElement sender, PointerPressedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
dialog.Close();
}
}
}
#endregion
static DialogControlBase()
{
CanDragMoveProperty.Changed.AddClassHandler<InputElement, bool>(OnCanDragMoveChanged);
CanCloseProperty.Changed.AddClassHandler<InputElement, bool>(OnCanCloseChanged);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
_titleArea = e.NameScope.Find<Panel>(PART_TitleArea);
if (CanDragMove)
{
_titleArea?.RemoveHandler(PointerMovedEvent, OnTitlePointerMove); _titleArea?.RemoveHandler(PointerMovedEvent, OnTitlePointerMove);
_titleArea?.RemoveHandler(PointerPressedEvent, OnTitlePointerPressed); _titleArea?.RemoveHandler(PointerPressedEvent, OnTitlePointerPressed);
_titleArea?.RemoveHandler(PointerReleasedEvent, OnTitlePointerRelease); _titleArea?.RemoveHandler(PointerReleasedEvent, OnTitlePointerRelease);
_titleArea = e.NameScope.Find<Panel>(PART_TitleArea);
_titleArea?.AddHandler(PointerMovedEvent, OnTitlePointerMove, RoutingStrategies.Bubble); _titleArea?.AddHandler(PointerMovedEvent, OnTitlePointerMove, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerPressedEvent, OnTitlePointerPressed, RoutingStrategies.Bubble); _titleArea?.AddHandler(PointerPressedEvent, OnTitlePointerPressed, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerReleasedEvent, OnTitlePointerRelease, RoutingStrategies.Bubble); _titleArea?.AddHandler(PointerReleasedEvent, OnTitlePointerRelease, RoutingStrategies.Bubble);
}
else
{
if (_titleArea is not null) _titleArea.IsHitTestVisible = false;
}
Button.ClickEvent.RemoveHandler(OnCloseButtonClick, _closeButton); Button.ClickEvent.RemoveHandler(OnCloseButtonClick, _closeButton);
_closeButton = e.NameScope.Find<Button>(PART_CloseButton); _closeButton = e.NameScope.Find<Button>(PART_CloseButton);
Button.ClickEvent.AddHandler(OnCloseButtonClick, _closeButton); Button.ClickEvent.AddHandler(OnCloseButtonClick, _closeButton);
} }
private void OnTitlePointerPressed(object sender, PointerPressedEventArgs e) private void OnTitlePointerPressed(InputElement sender, PointerPressedEventArgs e)
{ {
e.Source = this; e.Source = this;
} }
private void OnTitlePointerMove(object sender, PointerEventArgs e) private void OnTitlePointerMove(InputElement sender, PointerEventArgs e)
{ {
e.Source = this; e.Source = this;
} }
private void OnTitlePointerRelease(object sender, PointerReleasedEventArgs e) private void OnTitlePointerRelease(InputElement sender, PointerReleasedEventArgs e)
{ {
e.Source = this; e.Source = this;
} }
private void OnCloseButtonClick(object sender, RoutedEventArgs args) => Close(); private void OnCloseButtonClick(object sender, RoutedEventArgs args) => Close();
internal void SetAsModal(bool modal)
{
PseudoClasses.Set(PC_Modal, modal);
}
} }

View File

@@ -26,4 +26,5 @@ public class DialogOptions
public DialogButton Button { get; set; } = DialogButton.OKCancel; public DialogButton Button { get; set; } = DialogButton.OKCancel;
public bool IsCloseButtonVisible { get; set; } = true; public bool IsCloseButtonVisible { get; set; } = true;
public bool CanDragMove { get; set; } = true;
} }

View File

@@ -28,4 +28,5 @@ public class OverlayDialogOptions
public bool IsCloseButtonVisible { get; set; } = true; public bool IsCloseButtonVisible { get; set; } = true;
public bool CanLightDismiss { get; set; } public bool CanLightDismiss { get; set; }
public bool CanDragMove { get; set; } = true;
} }

View File

@@ -192,6 +192,7 @@ public static class OverlayDialog
control.CanClickOnMaskToClose = options.CanClickOnMaskToClose; control.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
control.IsCloseButtonVisible = options.IsCloseButtonVisible; control.IsCloseButtonVisible = options.IsCloseButtonVisible;
control.CanLightDismiss = options.CanLightDismiss; control.CanLightDismiss = options.CanLightDismiss;
control.CanDragMove = options.CanDragMove;
} }
private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options) private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options)
@@ -210,6 +211,7 @@ public static class OverlayDialog
control.Buttons = options.Buttons; control.Buttons = options.Buttons;
control.Title = options.Title; control.Title = options.Title;
control.CanLightDismiss = options.CanLightDismiss; control.CanLightDismiss = options.CanLightDismiss;
control.CanDragMove = options.CanDragMove;
} }