Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
107 changes: 55 additions & 52 deletions src/Notifications/View/NotificationUI.xaml
Original file line number Diff line number Diff line change
@@ -1,79 +1,82 @@
<Popup x:Class="Dynamo.Notifications.View.NotificationUI"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf"
xmlns:controls="clr-namespace:Dynamo.Controls;assembly=DynamoCoreWpf"
xmlns:p="clr-namespace:Dynamo.Wpf.Properties;assembly=DynamoCoreWpf"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:wv2="clr-namespace:Dynamo.Wpf.Utilities;assembly=DynamoCoreWpf"
mc:Ignorable="d"
AllowsTransparency="True"
StaysOpen="False"
Opacity="0.5"
Width="340"
Height="598">
<Popup
x:Class="Dynamo.Notifications.View.NotificationUI"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Dynamo.Controls;assembly=DynamoCoreWpf"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:p="clr-namespace:Dynamo.Wpf.Properties;assembly=DynamoCoreWpf"
xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf"
xmlns:wv2="clr-namespace:Dynamo.Wpf.Utilities;assembly=DynamoCoreWpf"
Width="340"
Height="598"
AllowsTransparency="True"
Opacity="0.5"
Opened="NotificationUI_Opened"
StaysOpen="False"
mc:Ignorable="d">
<Popup.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:SharedResourceDictionary Source="{x:Static ui:SharedDictionaryManager.DynamoConvertersDictionaryUri}" />
<ui:SharedResourceDictionary Source="{x:Static ui:SharedDictionaryManager.DynamoModernDictionaryUri}" />
</ResourceDictionary.MergedDictionaries>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<controls:PointsToPathConverter x:Key="PointsToPathConverter"/>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<controls:PointsToPathConverter x:Key="PointsToPathConverter" />
</ResourceDictionary>
</Popup.Resources>
<Canvas Background="Transparent"
Name="RootLayout" >
<Path x:Name="PopupPathRectangle"
Style="{StaticResource PoupPathRectangleStyle}">
<Canvas Name="RootLayout" Background="Transparent">
<Path x:Name="PopupPathRectangle" Style="{StaticResource PoupPathRectangleStyle}">
<Path.Data>
<RectangleGeometry x:Name="BackgroundRectangle">
</RectangleGeometry>
<RectangleGeometry x:Name="BackgroundRectangle" />
</Path.Data>
<!--This effect will show a 4px shadow of 20% of tranparency with a angle of 135 grades-->
<!-- This effect will show a 4px shadow of 20% of tranparency with a angle of 135 grades -->
<Path.Effect>
<DropShadowEffect Opacity="0.2"
Color="Black"
ShadowDepth="4"
BlurRadius="4"
Direction="135"/>
<DropShadowEffect
BlurRadius="4"
Direction="135"
Opacity="0.2"
ShadowDepth="4"
Color="Black" />
</Path.Effect>
</Path>

<Path x:Name="PopupPathRectangleShadow"
Style="{StaticResource PoupPathRectangleStyle}">
<Path x:Name="PopupPathRectangleShadow" Style="{StaticResource PoupPathRectangleStyle}">
<Path.Data>
<RectangleGeometry Rect="{Binding ElementName=BackgroundRectangle, Path=Rect}">
</RectangleGeometry>
<RectangleGeometry Rect="{Binding ElementName=BackgroundRectangle, Path=Rect}" />
</Path.Data>
<!--This effect will show a 4px shadow of 20% of tranparency with a angle of 315 grades-->
<!-- This effect will show a 4px shadow of 20% of tranparency with a angle of 315 grades -->
<Path.Effect>
<DropShadowEffect Opacity="0.2"
Color="Black"
ShadowDepth="4"
BlurRadius="4"
Direction="315"/>
<DropShadowEffect
BlurRadius="4"
Direction="315"
Opacity="0.2"
ShadowDepth="4"
Color="Black" />
</Path.Effect>
</Path>

<!--This Path will draw on the Canvas the pointer (a triangle)-->
<Path x:Name="TooltipPointer"
Data="{Binding Path=TooltipPointerPoints, Converter={StaticResource PointsToPathConverter}}"
Style="{StaticResource PoupPathPointerStyle}">
<!-- This Path will draw on the Canvas the pointer (a triangle) -->
<Path
x:Name="TooltipPointer"
Data="{Binding Path=TooltipPointerPoints, Converter={StaticResource PointsToPathConverter}}"
Style="{StaticResource PoupPathPointerStyle}">
<Path.Effect>
<DropShadowEffect Opacity="0.2"
Color="Black"
ShadowDepth="4"
BlurRadius="4"
Direction="{Binding Path=ShadowTooltipDirection}"/>
<DropShadowEffect
BlurRadius="4"
Direction="{Binding Path=ShadowTooltipDirection}"
Opacity="0.2"
ShadowDepth="4"
Color="Black" />
</Path.Effect>
</Path>

<Grid x:Name="mainPopupGrid" Background="White"
Width="{Binding PopupRectangleWidth}">
<wv2:DynamoWebView2 Name="webView" ></wv2:DynamoWebView2>
<Grid
x:Name="mainPopupGrid"
Width="{Binding PopupRectangleWidth}"
Background="White">
<wv2:DynamoWebView2 Name="webView" />
</Grid>
</Canvas>
</Popup>
32 changes: 32 additions & 0 deletions src/Notifications/View/NotificationUI.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using System.Windows.Threading;

namespace Dynamo.Notifications.View
{
Expand Down Expand Up @@ -40,6 +44,8 @@ public NotificationUI()
mainPopupGrid.Width = notificationsUIViewModel.PopupRectangleWidth;
mainPopupGrid.Height = notificationsUIViewModel.PopupRectangleHeight;
mainPopupGrid.Margin = new Thickness(notificationsUIViewModel.PopupBordersOffSet, notificationsUIViewModel.PopupBordersOffSet, 0, 0);


}

internal void UpdatePopupLocation()
Expand All @@ -60,5 +66,31 @@ internal void UpdatePopupSize()

this.Height = notificationsUIViewModel.PopupRectangleHeight + notificationsUIViewModel.PopupBordersOffSet + 10;
}

// Wait until the Popup is opened to set focus back to the main window.
// This is necessary because Popup opens asynchronously, and WebView2 (HWND-based)
// can steal focus. We explicitly reclaim native focus to ensure correct interaction
// with the main window (e.g., close button, click handling).
private void NotificationUI_Opened(object sender, EventArgs e)
{
// Ensure the application and main window are available and fully loaded.
ForceMainWindowFocus();
}

private void ForceMainWindowFocus()
{
if (Application.Current?.MainWindow == null)
return;

ReleaseCapture();
SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]

Copilot AI Jun 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding SetLastError = true to the DllImport attribute and checking Marshal.GetLastWin32Error() after the call to aid debugging if the native call fails.

Suggested change
ReleaseCapture();
SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle);
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
if (!ReleaseCapture())
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine($"ReleaseCapture failed with error code: {errorCode}");
}
if (!SetForegroundWindow(new WindowInteropHelper(Application.Current.MainWindow).Handle))
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine($"SetForegroundWindow failed with error code: {errorCode}");
}
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's now done.

private static extern bool ReleaseCapture();
}
}
Loading