Browse Source

Merge branch 'fixes/osx-dialog-keep-on-top-when-deactivated' into feature/window-integration-tests

pull/8232/head
Dan Walmsley 4 years ago
parent
commit
69bc9d5329
  1. 2
      native/Avalonia.Native/src/OSX/AvnPanelWindow.mm
  2. 34
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  3. 2
      native/Avalonia.Native/src/OSX/INSWindowHolder.h
  4. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  5. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  6. 9
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  7. 5
      src/Avalonia.Controls/ContextMenu.cs
  8. 3
      src/Avalonia.Controls/Flyouts/FlyoutBase.cs
  9. 10
      src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
  10. 103
      src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
  11. 21
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  12. 25
      tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
  13. 32
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs

2
native/Avalonia.Native/src/OSX/AvnPanelWindow.mm

@ -3,8 +3,6 @@
// Copyright (c) 2022 Avalonia. All rights reserved.
//
#pragma once
#define IS_NSPANEL
#include "AvnWindow.mm"

34
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -68,7 +68,7 @@
}
}
- (void)performClose:(id)sender
- (void)performClose:(id _Nullable )sender
{
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
{
@ -147,7 +147,7 @@
}
}
-(void) applyMenu:(AvnMenu *)menu
-(void) applyMenu:(AvnMenu *_Nullable)menu
{
if(menu == nullptr)
{
@ -157,7 +157,7 @@
_menu = menu;
}
-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
-(CLASS_NAME*_Nonnull) initWithParent: (WindowBaseImpl*_Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
{
// https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/
// create nswindow with specific contentRect, otherwise we wont be able to resize the window
@ -183,7 +183,7 @@
return self;
}
- (BOOL)windowShouldClose:(NSWindow *)sender
- (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender
{
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw());
@ -195,14 +195,14 @@
return true;
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification
- (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification
{
[self backingScaleFactor];
}
- (void)windowWillClose:(NSNotification *)notification
- (void)windowWillClose:(NSNotification *_Nonnull)notification
{
_closed = true;
if(_parent)
@ -274,12 +274,12 @@
[super becomeKeyWindow];
}
- (void)windowDidBecomeKey:(NSNotification *)notification
- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification
{
_parent->BringToFront();
}
- (void)windowDidMiniaturize:(NSNotification *)notification
- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -289,7 +289,7 @@
}
}
- (void)windowDidDeminiaturize:(NSNotification *)notification
- (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -299,7 +299,7 @@
}
}
- (void)windowDidResize:(NSNotification *)notification
- (void)windowDidResize:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -309,7 +309,7 @@
}
}
- (void)windowWillExitFullScreen:(NSNotification *)notification
- (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -319,7 +319,7 @@
}
}
- (void)windowDidExitFullScreen:(NSNotification *)notification
- (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -342,7 +342,7 @@
}
}
- (void)windowWillEnterFullScreen:(NSNotification *)notification
- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -352,7 +352,7 @@
}
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification
- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification
{
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
@ -363,7 +363,7 @@
}
}
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
{
return true;
}
@ -380,7 +380,7 @@
[super resignKeyWindow];
}
- (void)windowDidMove:(NSNotification *)notification
- (void)windowDidMove:(NSNotification *_Nonnull)notification
{
AvnPoint position;
@ -412,7 +412,7 @@
return pt;
}
- (void)sendEvent:(NSEvent *)event
- (void)sendEvent:(NSEvent *_Nonnull)event
{
[super sendEvent:event];

2
native/Avalonia.Native/src/OSX/INSWindowHolder.h

@ -11,7 +11,7 @@
struct INSWindowHolder
{
virtual NSWindow* _Nonnull GetNSWindow () = 0;
virtual NSView* _Nonnull GetNSView () = 0;
virtual AvnView* _Nonnull GetNSView () = 0;
};
#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H

2
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -38,7 +38,7 @@ BEGIN_INTERFACE_MAP()
virtual NSWindow *GetNSWindow() override;
virtual NSView *GetNSView() override;
virtual AvnView *GetNSView() override;
virtual HRESULT Show(bool activate, bool isDialog) override;

2
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -70,7 +70,7 @@ NSWindow *WindowBaseImpl::GetNSWindow() {
return Window;
}
NSView *WindowBaseImpl::GetNSView() {
AvnView *WindowBaseImpl::GetNSView() {
return View;
}

9
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@ -33,7 +33,14 @@ namespace Avalonia.Data.Converters
if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1)
{
return new MethodToCommandConverter(d);
if (d.Method.IsPrivate == false)
{
return new MethodToCommandConverter(d);
}
else
{
return new BindingNotification(new InvalidCastException("You can't bind to private methods!"), BindingErrorType.Error);
}
}
if (TypeUtilities.TryConvert(targetType, value, culture, out var result))

5
src/Avalonia.Controls/ContextMenu.cs

@ -450,6 +450,11 @@ namespace Avalonia.Controls
if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu)
{
if (contextMenu._popup?.Parent == control)
{
((ISetLogicalParent)contextMenu._popup).SetParent(null);
}
contextMenu.Close();
}
}

3
src/Avalonia.Controls/Flyouts/FlyoutBase.cs

@ -175,7 +175,8 @@ namespace Avalonia.Controls.Primitives
IsOpen = false;
Popup.IsOpen = false;
((ISetLogicalParent)Popup).SetParent(null);
// Ensure this isn't active
_transientDisposable?.Dispose();
_transientDisposable = null;

10
src/Web/Avalonia.Web.Blazor/AvaloniaView.razor

@ -1,14 +1,12 @@
<div id="container" class="avalonia-container" tabindex="0" oncontextmenu="return false;"
ontouchstart="@OnTouchStart"
ontouchend="@OnTouchEnd"
ontouchcancel="@OnTouchCancel"
ontouchmove="@OnTouchMove"
onmousemove="@OnMouseMove"
onmousedown="@OnMouseDown"
onmouseup="@OnMouseUp"
onwheel="@OnWheel"
onkeydown="@OnKeyDown"
onkeyup="@OnKeyUp">
onkeyup="@OnKeyUp"
onpointerdown="@OnPointerDown"
onpointerup="@OnPointerUp"
onpointermove="@OnPointerMove">
<canvas id="htmlCanvas" @ref="_htmlCanvas" @attributes="AdditionalAttributes"/>

103
src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs

@ -56,90 +56,91 @@ namespace Avalonia.Web.Blazor
{
return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
}
private void OnTouchStart(TouchEventArgs e)
private void OnTouchCancel(TouchEventArgs e)
{
foreach (var touch in e.ChangedTouches)
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(touch.ClientX, touch.ClientY),
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
}
}
private void OnTouchEnd(TouchEventArgs e)
private void OnTouchMove(TouchEventArgs e)
{
foreach (var touch in e.ChangedTouches)
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(touch.ClientX, touch.ClientY),
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
}
}
private void OnTouchCancel(TouchEventArgs e)
private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
foreach (var touch in e.ChangedTouches)
if (e.PointerType != "touch")
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
_topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
}
private void OnTouchMove(TouchEventArgs e)
private void OnPointerUp(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
foreach (var touch in e.ChangedTouches)
if (e.PointerType == "touch")
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(e.ClientX, e.ClientY),
GetModifiers(e), e.PointerId);
}
}
private void OnMouseMove(MouseEventArgs e)
{
_topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
else
{
RawPointerEventType type = default;
private void OnMouseUp(MouseEventArgs e)
{
RawPointerEventType type = default;
switch (e.Button)
{
case 0:
type = RawPointerEventType.LeftButtonUp;
break;
switch (e.Button)
{
case 0:
type = RawPointerEventType.LeftButtonUp;
break;
case 1:
type = RawPointerEventType.MiddleButtonUp;
break;
case 1:
type = RawPointerEventType.MiddleButtonUp;
break;
case 2:
type = RawPointerEventType.RightButtonUp;
break;
}
case 2:
type = RawPointerEventType.RightButtonUp;
break;
_topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
_topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
private void OnMouseDown(MouseEventArgs e)
private void OnPointerDown(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
RawPointerEventType type = default;
switch (e.Button)
if (e.PointerType == "touch")
{
case 0:
type = RawPointerEventType.LeftButtonDown;
break;
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(e.ClientX, e.ClientY),
GetModifiers(e), e.PointerId);
}
else
{
RawPointerEventType type = default;
switch (e.Button)
{
case 0:
type = RawPointerEventType.LeftButtonDown;
break;
case 1:
type = RawPointerEventType.MiddleButtonDown;
break;
case 1:
type = RawPointerEventType.MiddleButtonDown;
break;
case 2:
type = RawPointerEventType.RightButtonDown;
break;
}
case 2:
type = RawPointerEventType.RightButtonDown;
break;
}
_topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
_topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
}
private void OnWheel(WheelEventArgs e)
@ -189,7 +190,7 @@ namespace Avalonia.Web.Blazor
return modifiers;
}
private static RawInputModifiers GetModifiers(MouseEventArgs e)
private static RawInputModifiers GetModifiers(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
var modifiers = RawInputModifiers.None;

21
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -446,6 +446,27 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Should_Reset_Popup_Parent_On_Target_Detached()
{
using (Application())
{
var userControl = new UserControl();
var window = PreparedWindow(userControl);
window.Show();
var menu = new ContextMenu();
userControl.ContextMenu = menu;
menu.Open();
var popup = Assert.IsType<Popup>(menu.Parent);
Assert.NotNull(popup.Parent);
window.Content = null;
Assert.Null(popup.Parent);
}
}
[Fact]
public void Context_Menu_In_Resources_Can_Be_Shared()
{

25
tests/Avalonia.Controls.UnitTests/FlyoutTests.cs

@ -432,6 +432,26 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Should_Reset_Popup_Parent_On_Target_Detached()
{
using (CreateServicesWithFocus())
{
var userControl = new UserControl();
var window = PreparedWindow(userControl);
window.Show();
var flyout = new TestFlyout();
flyout.ShowAt(userControl);
var popup = Assert.IsType<Popup>(flyout.Popup);
Assert.NotNull(popup.Parent);
window.Content = null;
Assert.Null(popup.Parent);
}
}
[Fact]
public void ContextFlyout_Can_Be_Set_In_Styles()
{
@ -549,5 +569,10 @@ namespace Avalonia.Controls.UnitTests
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
KeyModifiers.None);
}
public class TestFlyout : Flyout
{
public new Popup Popup => base.Popup;
}
}
}

32
tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs

@ -0,0 +1,32 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Xunit;
namespace Avalonia.Markup.UnitTests.Data
{
public class BindingTests_Method
{
[Fact]
public void Binding_To_Private_Methods_Shouldnt_Work()
{
var vm = new TestClass();
var target = new Button
{
DataContext = vm,
[!Button.CommandProperty] = new Binding("MyMethod"),
};
target.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
Assert.False(vm.IsSet);
}
class TestClass
{
public bool IsSet { get; set; }
private void MyMethod() => IsSet = true;
}
}
}
Loading…
Cancel
Save