diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index 31e61e0f..c82d6c5a 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -2,7 +2,7 @@ net10.0 - 13.3.2.1 + 13.3.2.2 enable diff --git a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs index 22187a2a..7c06a085 100644 --- a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs +++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs @@ -4,11 +4,22 @@ using Avalonia.Input; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace LibationAvalonia.Controls; public class DataGridCellContextMenu where TContext : class { + private static readonly PropertyInfo? DataGridCellOwningColumnProperty = + typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic); + + private static DataGridColumn? GetDataGridColumn(DataGridCell cell) + { + if (cell.Tag is DataGridColumn columnFromTag) + return columnFromTag; + return DataGridCellOwningColumnProperty?.GetValue(cell) as DataGridColumn; + } + public static DataGridCellContextMenu? Create(ContextMenu? contextMenu) { DataGrid? grid = null; @@ -22,7 +33,11 @@ public class DataGridCellContextMenu where TContext : class parent = parent.Parent; } - if (grid is null || cell is null || cell.Tag is not DataGridColumn column || contextMenu!.DataContext is not TContext clickedEntry) + if (grid is null || cell is null || contextMenu!.DataContext is not TContext clickedEntry) + return null; + + var column = GetDataGridColumn(cell); + if (column is null) return null; var allSelected = grid.SelectedItems.OfType().ToArray(); diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index ac740a8a..a80a2097 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -1,6 +1,9 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Input.Platform; +using Avalonia.Interactivity; +using Avalonia.VisualTree; using Avalonia.Platform.Storage; using Avalonia.Styling; using DataLayer; @@ -105,6 +108,46 @@ public partial class ProductsDisplay : UserControl { column.CustomSortComparer = new RowComparer(column); } + + // macOS: control-click may be delivered as left+control or as a secondary click; the default + // ContextMenu route can fail on DataGrid cells. Open explicitly after children handle the event. + if (Configuration.IsMacOs) + { + productsGrid.AddHandler(InputElement.PointerPressedEvent, ProductsGrid_PointerPressedMacContextMenu, RoutingStrategies.Bubble, handledEventsToo: true); + } + } + + private void ProductsGrid_PointerPressedMacContextMenu(object? sender, PointerPressedEventArgs e) + { + if (DisableContextMenu) + return; + + var point = e.GetCurrentPoint(productsGrid); + var props = point.Properties; + bool isContextGesture = props.IsRightButtonPressed + || (props.IsLeftButtonPressed && e.KeyModifiers.HasFlag(KeyModifiers.Control)); + + if (!isContextGesture) + return; + + if (e.Source is not Visual visual) + return; + + DataGridCell? cell = null; + for (Visual? v = visual; v is not null; v = v.GetVisualParent()) + { + if (v is DataGridCell c) + { + cell = c; + break; + } + } + + if (cell?.ContextMenu is not { } contextMenu) + return; + + contextMenu.Open(cell); + e.Handled = true; } protected override void OnApplyTemplate(Avalonia.Controls.Primitives.TemplateAppliedEventArgs e)