Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [55.3.0]
- [ContextMenu] Added `PreviewView` property to `ContextMenu` for use with `LongPressed` mode. On iOS, the view is shown as the native `UIContextMenuInteraction` preview. On Android, the view is shown in a popup above the context menu.

## [55.2.2]
- [iOS26][Tip] Added more padding.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,35 @@
</dui:ContextMenuEffect.Menu>
</dui:ListItem>

<!-- Long press with PreviewView -->
<dui:ListItem Title="{x:Static localizedStrings:LocalizedStrings.Context_Menu_LongPressWithPreview}"
HasBottomDivider="True"
dui:ContextMenuEffect.Mode="LongPressed">
<dui:ContextMenuEffect.Menu>
<dui:ContextMenu ItemClickedCommand="{Binding ItemClickedCommand}">
<dui:ContextMenu.PreviewView>
<Border StrokeThickness="2" BackgroundColor="Gray">
<dui:VerticalStackLayout Padding="{dui:Sizes content_margin_medium}"
BackgroundColor="{dui:Colors color_surface_default}"
Spacing="{dui:Sizes content_margin_small}">
<dui:Label Style="{dui:Styles Label=UI200}"
Text="This is a very long document title that would normally be truncated in the list" />
<dui:Label Style="{dui:Styles Label=UI100}"
TextColor="{dui:Colors color_text_subtle}"
Text="Additional context about the document can be placed here" />
</dui:VerticalStackLayout></Border>
</dui:ContextMenu.PreviewView>
<dui:ContextMenuItem
Title="{dui:StringFormat {x:Static localizedStrings:LocalizedStrings.Action_Format}, Argument=1}"
Icon="{dui:Icons internalmessage_fill}" />
<dui:ContextMenuItem
IsDestructive="True"
Title="{dui:StringFormat {x:Static localizedStrings:LocalizedStrings.Action_Format}, Argument=2}"
Icon="{dui:Icons internalmessage_fill}" />
</dui:ContextMenu>
</dui:ContextMenuEffect.Menu>
</dui:ListItem>

<dui:ListItem Title="{x:Static localizedStrings:LocalizedStrings.Context_Menu_Divider}"
HasBottomDivider="True">

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@
<data name="Context_Menu_LongPress_OneItemOneGroup" xml:space="preserve">
<value>Single item and single group</value>
</data>
<data name="Context_Menu_LongPressWithPreview" xml:space="preserve">
<value>Long press with preview</value>
</data>
<data name="DefaultStateViews" xml:space="preserve">
<value>Default state views</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@
<data name="Context_Menu_LongPress_OneItemOneGroup" xml:space="preserve">
<value>Én handling og én gruppe</value>
</data>
<data name="Context_Menu_LongPressWithPreview" xml:space="preserve">
<value>Trykk og hold med forhåndsvisning</value>
</data>
<data name="DefaultStateViews" xml:space="preserve">
<value>Standard tilstand views</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
using Android.Graphics.Drawables;
using Android.Views;
using Android.Widget;
using AndroidX.AppCompat.Widget;
using DIPS.Mobile.UI.API.Library;
using DIPS.Mobile.UI.Components.ContextMenus.Android;
using DIPS.Mobile.UI.Effects.Layout;
using DIPS.Mobile.UI.Extensions.Android;
using Google.Android.Material.Shape;
using Microsoft.Maui.Platform;
using Object = Java.Lang.Object;
using PopupMenu = Android.Widget.PopupMenu;
using View = Android.Views.View;
Expand Down Expand Up @@ -45,13 +52,14 @@ protected override partial void OnAttached()
}
}

public class ContextMenuHandler : Object, PopupMenu.IOnMenuItemClickListener
public class ContextMenuHandler : Object, PopupMenu.IOnMenuItemClickListener, PopupMenu.IOnDismissListener
{
private readonly ContextMenu m_contextMenu;
private readonly View m_control;

private Dictionary<IContextMenuItem, IMenuItem> m_menuItems;
private PopupMenu m_popupMenu;
private PopupWindow? m_previewPopupWindow;

public ContextMenuHandler(ContextMenu contextMenu, View view)
{
Expand Down Expand Up @@ -81,14 +89,78 @@ public void OpenContextMenu(object? sender, EventArgs e)
}));

SetListeners();


if (m_contextMenu.PreviewView != null)
{
ShowPreview();
}

m_popupMenu.Show();

}

private void ShowPreview()
{
var activity = Platform.CurrentActivity;
var mauiContext = DUI.GetCurrentMauiContext;
if (activity is null || mauiContext is null || m_contextMenu.PreviewView is null)
return;

var previewNativeView = m_contextMenu.PreviewView.ToPlatform(mauiContext);

// Wrap in a container with rounded corners and shadow elevation
var container = new FrameLayout(activity);
var background = MaterialShapeDrawableHelper.CreateDrawable(new CornerRadius(
Sizes.GetSize(SizeName.size_2)));
background.FillColor = Resources.Colors.Colors.GetColor(ColorName.color_surface_default)
.ToDefaultColorStateList();
container.Background = background;
container.ClipToOutline = true;
container.OutlineProvider = ViewOutlineProvider.Background;
container.Elevation = Sizes.GetSize(SizeName.size_3).ToMauiPixel();
container.AddView(previewNativeView);

container.Measure(
View.MeasureSpec.MakeMeasureSpec(m_control.Width, MeasureSpecMode.AtMost),
View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified));

m_previewPopupWindow = new PopupWindow(
container,
m_control.Width,
ViewGroup.LayoutParams.WrapContent,
false);

m_previewPopupWindow.SetBackgroundDrawable(new ColorDrawable(global::Android.Graphics.Color.Transparent));

var previewHeight = container.MeasuredHeight;
m_previewPopupWindow.ShowAsDropDown(m_control, 0, -(m_control.Height + previewHeight));

// Apply dim behind the preview popup
var rootView = m_previewPopupWindow.ContentView?.RootView;
if (rootView?.LayoutParameters is WindowManagerLayoutParams layoutParams
&& activity.GetSystemService(global::Android.Content.Context.WindowService) is IWindowManager windowManager)
{
layoutParams.Flags |= WindowManagerFlags.DimBehind;
layoutParams.DimAmount = 0.5f;
windowManager.UpdateViewLayout(rootView, layoutParams);
}
}

private void DismissPreview()
{
m_previewPopupWindow?.Dismiss();
m_previewPopupWindow = null;
}

public void OnDismiss(PopupMenu? menu)
{
DismissPreview();
}

private void SetListeners()
{
m_popupMenu.SetOnMenuItemClickListener(this);
m_popupMenu.SetOnDismissListener(this);
}

public bool OnMenuItemClick(IMenuItem? theTappedNativeItem)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,25 @@ public ContextMenuHorizontalOptions ContextMenuHorizontalOptions
set => SetValue(ContextMenuHorizontalOptionsProperty, value);
}

/// <summary>
/// <see cref="PreviewView"/>
/// </summary>
public static readonly BindableProperty PreviewViewProperty = BindableProperty.Create(
nameof(PreviewView),
typeof(View),
typeof(ContextMenu));

/// <summary>
/// An optional view to display as a preview when the context menu is opened in <see cref="ContextMenuEffect.ContextMenuMode.LongPressed"/> mode.
/// On iOS, the view is shown as the native <c>UIContextMenuInteraction</c> preview.
/// On Android, the view is shown in a popup above the context menu.
/// </summary>
public View? PreviewView
{
get => (View?)GetValue(PreviewViewProperty);
set => SetValue(PreviewViewProperty, value);
}

/// <summary>
/// Get the mode of the context menu.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public override void WillEnd(UIContextMenuInteraction interaction, UIContextMenu
contextMenu);
var menu = UIMenu.Create(contextMenu.Title, dict.Select(k => k.Value).ToArray());

return UIContextMenuConfiguration.Create(null, null, actions => menu);
var previewView = contextMenu.PreviewView;

return UIContextMenuConfiguration.Create(null, () => previewView != null ? new ContextMenuPreviewViewController(previewView)
: null, actions => menu);
}

public Element? Element { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using CoreGraphics;
using DIPS.Mobile.UI.API.Library;
using Microsoft.Maui.Platform;
using UIKit;

// ReSharper disable once CheckNamespace
namespace DIPS.Mobile.UI.Components.ContextMenus.iOS;

internal class ContextMenuPreviewViewController : UIViewController
{
private readonly View m_previewView;

internal ContextMenuPreviewViewController(View previewView)
{
m_previewView = previewView;
}

public override void ViewDidLoad()
{
base.ViewDidLoad();

var mauiContext = DUI.GetCurrentMauiContext;
if (mauiContext is null || View is null)
return;

var nativeView = m_previewView.ToPlatform(mauiContext);
View.AddSubview(nativeView);

var maxWidth = UIScreen.MainScreen.Bounds.Width - Sizes.GetSize(SizeName.content_margin_large) * 2;
var measurement = m_previewView.Measure(maxWidth, double.PositiveInfinity);

var width = Math.Min(measurement.Width, maxWidth);
var height = measurement.Height;

PreferredContentSize = new CGSize(width, height);
nativeView.Frame = new CGRect(0, 0, width, height);
}
}