Browse Source

Merge branch 'fixes/calculate-bounds-for-drawline' of https://github.com/AvaloniaUI/Avalonia into fixes/calculate-bounds-for-drawline

pull/3909/head
Steven Kirk 6 years ago
parent
commit
6353ba51df
  1. 49
      native/Avalonia.Native/inc/avalonia-native.h
  2. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  3. 3
      native/Avalonia.Native/src/OSX/AvnString.h
  4. 49
      native/Avalonia.Native/src/OSX/AvnString.mm
  5. 90
      native/Avalonia.Native/src/OSX/clipboard.mm
  6. 8
      native/Avalonia.Native/src/OSX/common.h
  7. 89
      native/Avalonia.Native/src/OSX/dnd.mm
  8. 19
      native/Avalonia.Native/src/OSX/main.mm
  9. 2
      native/Avalonia.Native/src/OSX/window.h
  10. 108
      native/Avalonia.Native/src/OSX/window.mm
  11. 5
      packages/Avalonia/AvaloniaBuildTasks.targets
  12. 12
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  13. 92
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  14. 1
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  15. 100
      samples/ControlCatalog/Pages/TreeViewPage.xaml.cs
  16. 115
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  17. 11
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  18. 9
      src/Avalonia.Build.Tasks/Extensions.cs
  19. 31
      src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
  20. 4
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  21. 2
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  22. 4
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  23. 4
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  24. 4
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  25. 4
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  26. 8
      src/Avalonia.Controls/Application.cs
  27. 4
      src/Avalonia.Controls/AutoCompleteBox.cs
  28. 2
      src/Avalonia.Controls/ButtonSpinner.cs
  29. 4
      src/Avalonia.Controls/Calendar/Calendar.cs
  30. 3
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  31. 4
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  32. 4
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  33. 4
      src/Avalonia.Controls/Calendar/DatePicker.cs
  34. 4
      src/Avalonia.Controls/ComboBox.cs
  35. 3
      src/Avalonia.Controls/ListBox.cs
  36. 4
      src/Avalonia.Controls/MenuItem.cs
  37. 4
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  38. 2
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  39. 4
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  40. 11
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  41. 2
      src/Avalonia.Controls/ProgressBar.cs
  42. 11
      src/Avalonia.Controls/SelectionModel.cs
  43. 134
      src/Avalonia.Controls/SelectionNode.cs
  44. 2
      src/Avalonia.Controls/Slider.cs
  45. 4
      src/Avalonia.Controls/TabControl.cs
  46. 2
      src/Avalonia.Controls/TextBox.cs
  47. 10
      src/Avalonia.Controls/TreeView.cs
  48. 3
      src/Avalonia.Controls/TreeViewItem.cs
  49. 76
      src/Avalonia.Native/AvaloniaNativeDragSource.cs
  50. 16
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  51. 39
      src/Avalonia.Native/AvnString.cs
  52. 98
      src/Avalonia.Native/ClipboardImpl.cs
  53. 31
      src/Avalonia.Native/WindowImplBase.cs
  54. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  55. 802
      tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs
  56. 3
      tests/Avalonia.Controls.UnitTests/TestTemplatedControl.cs
  57. 13
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

49
native/Avalonia.Native/inc/avalonia-native.h

@ -22,6 +22,9 @@ struct IAvnGlSurfaceRenderTarget;
struct IAvnGlSurfaceRenderingSession;
struct IAvnMenu;
struct IAvnMenuItem;
struct IAvnStringArray;
struct IAvnDndResultCallback;
struct IAvnGCHandleDeallocatorCallback;
struct IAvnMenuEvents;
enum SystemDecorations {
@ -130,6 +133,22 @@ enum AvnInputModifiers
XButton2MouseButton = 256
};
enum class AvnDragDropEffects
{
None = 0,
Copy = 1,
Move = 2,
Link = 4,
};
enum class AvnDragEventType
{
Enter,
Over,
Leave,
Drop
};
enum AvnWindowState
{
Normal,
@ -188,7 +207,7 @@ enum AvnMenuItemToggleType
AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
{
public:
virtual HRESULT Initialize() = 0;
virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) = 0;
virtual IAvnMacOptions* GetMacOptions() = 0;
virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) = 0;
virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) = 0;
@ -196,6 +215,7 @@ public:
virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) = 0;
virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0;
virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) = 0;
virtual HRESULT SetAppMenu(IAvnMenu* menu) = 0;
@ -236,6 +256,8 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandle(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0;
};
AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
@ -271,6 +293,9 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
virtual void ScalingChanged(double scaling) = 0;
virtual void RunRenderPriorityJobs() = 0;
virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers, AvnDragDropEffects effects,
IAvnClipboard* clipboard, void* dataObjectHandle) = 0;
};
@ -354,8 +379,10 @@ AVNCOM(IAvnScreens, 0e) : IUnknown
AVNCOM(IAvnClipboard, 0f) : IUnknown
{
virtual HRESULT GetText (IAvnString**ppv) = 0;
virtual HRESULT SetText (void* utf8Text) = 0;
virtual HRESULT GetText (char* type, IAvnString**ppv) = 0;
virtual HRESULT SetText (char* type, void* utf8Text) = 0;
virtual HRESULT ObtainFormats(IAvnStringArray**ppv) = 0;
virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) = 0;
virtual HRESULT Clear() = 0;
};
@ -428,4 +455,20 @@ AVNCOM(IAvnMenuEvents, 1A) : IUnknown
virtual void NeedsUpdate () = 0;
};
AVNCOM(IAvnStringArray, 20) : IUnknown
{
virtual unsigned int GetCount() = 0;
virtual HRESULT Get(unsigned int index, IAvnString**ppv) = 0;
};
AVNCOM(IAvnDndResultCallback, 21) : IUnknown
{
virtual void OnDragAndDropComplete(AvnDragDropEffects effecct) = 0;
};
AVNCOM(IAvnGCHandleDeallocatorCallback, 22) : IUnknown
{
virtual void FreeGCHandle(void* handle) = 0;
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();

4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -12,6 +12,7 @@
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.mm */; };
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
@ -33,6 +34,7 @@
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.mm; sourceTree = "<group>"; };
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
@ -92,6 +94,7 @@
5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
1A465D0F246AB61600C5858B /* dnd.mm */,
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
AB661C212148288600291242 /* common.h */,
379860FE214DA0C000CD0246 /* KeyTransform.h */,
@ -196,6 +199,7 @@
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
AB661C202148286E00291242 /* window.mm in Sources */,

3
native/Avalonia.Native/src/OSX/AvnString.h

@ -10,5 +10,6 @@
#define AvnString_h
extern IAvnString* CreateAvnString(NSString* string);
extern IAvnStringArray* CreateAvnStringArray(NSArray<NSString*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string);
#endif /* AvnString_h */

49
native/Avalonia.Native/src/OSX/AvnString.mm

@ -7,6 +7,7 @@
//
#include "common.h"
#include <vector>
class AvnStringImpl : public virtual ComSingleObject<IAvnString, &IID_IAvnString>
{
@ -61,7 +62,55 @@ public:
}
};
class AvnStringArrayImpl : public virtual ComSingleObject<IAvnStringArray, &IID_IAvnStringArray>
{
private:
std::vector<ComPtr<IAvnString>> _list;
public:
FORWARD_IUNKNOWN()
AvnStringArrayImpl(NSArray<NSString*>* array)
{
for(int c = 0; c < [array count]; c++)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl([array objectAtIndex:c]);
_list.push_back(s);
}
}
AvnStringArrayImpl(NSString* string)
{
ComPtr<IAvnString> s;
*s.getPPV() = new AvnStringImpl(string);
_list.push_back(s);
}
virtual unsigned int GetCount() override
{
return (unsigned int)_list.size();
}
virtual HRESULT Get(unsigned int index, IAvnString**ppv) override
{
if(_list.size() <= index)
return E_INVALIDARG;
*ppv = _list[index].getRetainedReference();
return S_OK;
}
};
IAvnString* CreateAvnString(NSString* string)
{
return new AvnStringImpl(string);
}
IAvnStringArray* CreateAvnStringArray(NSArray<NSString*> * array)
{
return new AvnStringArrayImpl(array);
}
IAvnStringArray* CreateAvnStringArray(NSString* string)
{
return new AvnStringArrayImpl(string);
}

90
native/Avalonia.Native/src/OSX/clipboard.mm

@ -3,16 +3,27 @@
class Clipboard : public ComSingleObject<IAvnClipboard, &IID_IAvnClipboard>
{
private:
NSPasteboard* _pb;
NSPasteboardItem* _item;
public:
FORWARD_IUNKNOWN()
Clipboard()
Clipboard(NSPasteboard* pasteboard, NSPasteboardItem* item)
{
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard stringForType:NSPasteboardTypeString];
if(pasteboard == nil && item == nil)
pasteboard = [NSPasteboard generalPasteboard];
_pb = pasteboard;
_item = item;
}
virtual HRESULT GetText (IAvnString**ppv) override
NSPasteboardItem* TryGetItem()
{
return _item;
}
virtual HRESULT GetText (char* type, IAvnString**ppv) override
{
@autoreleasepool
{
@ -20,20 +31,53 @@ public:
{
return E_POINTER;
}
NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
NSString* string = _item == nil ? [_pb stringForType:typeString] : [_item stringForType:typeString];
*ppv = CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]);
*ppv = CreateAvnString(string);
return S_OK;
}
}
virtual HRESULT SetText (void* utf8String) override
virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override
{
@autoreleasepool
{
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard clearContents];
[pasteBoard setString:[NSString stringWithUTF8String:(const char*)utf8String] forType:NSPasteboardTypeString];
*ppv= nil;
NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
NSObject* data = _item == nil ? [_pb propertyListForType: typeString] : [_item propertyListForType: typeString];
if(data == nil)
return S_OK;
if([data isKindOfClass: [NSString class]])
{
*ppv = CreateAvnStringArray((NSString*) data);
return S_OK;
}
NSArray* arr = (NSArray*)data;
for(int c = 0; c < [arr count]; c++)
if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]])
return E_INVALIDARG;
*ppv = CreateAvnStringArray(arr);
return S_OK;
}
}
virtual HRESULT SetText (char* type, void* utf8String) override
{
Clear();
@autoreleasepool
{
auto string = [NSString stringWithUTF8String:(const char*)utf8String];
auto typeString = [NSString stringWithUTF8String:(const char*)type];
if(_item == nil)
[_pb setString: string forType: typeString];
else
[_item setString: string forType:typeString];
}
return S_OK;
@ -43,16 +87,34 @@ public:
{
@autoreleasepool
{
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard clearContents];
[pasteBoard setString:@"" forType:NSPasteboardTypeString];
if(_item != nil)
_item = [NSPasteboardItem new];
else
{
[_pb clearContents];
[_pb setString:@"" forType:NSPasteboardTypeString];
}
}
return S_OK;
}
virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override
{
*ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]);
return S_OK;
}
};
extern IAvnClipboard* CreateClipboard()
extern IAvnClipboard* CreateClipboard(NSPasteboard* pb, NSPasteboardItem* item)
{
return new Clipboard(pb, item);
}
extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*cb)
{
return new Clipboard();
auto clipboard = dynamic_cast<Clipboard*>(cb);
if(clipboard == nil)
return nil;
return clipboard->TryGetItem();
}

8
native/Avalonia.Native/src/OSX/common.h

@ -8,11 +8,17 @@
#include <pthread.h>
extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
extern void FreeAvnGCHandle(void* handle);
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl);
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl);
extern IAvnSystemDialogs* CreateSystemDialogs();
extern IAvnScreens* CreateScreens();
extern IAvnClipboard* CreateClipboard();
extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*);
extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*);
extern NSObject<NSDraggingSource>* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle);
extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject<NSDraggingInfo>* info);
extern NSString* GetAvnCustomDataType();
extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop);
extern IAvnCursorFactory* CreateCursorFactory();
extern IAvnGlDisplay* GetGlDisplay();
extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events);

89
native/Avalonia.Native/src/OSX/dnd.mm

@ -0,0 +1,89 @@
#include "common.h"
extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop)
{
int effects = 0;
if((nsop & NSDragOperationCopy) != 0)
effects |= (int)AvnDragDropEffects::Copy;
if((nsop & NSDragOperationMove) != 0)
effects |= (int)AvnDragDropEffects::Move;
if((nsop & NSDragOperationLink) != 0)
effects |= (int)AvnDragDropEffects::Link;
return (AvnDragDropEffects)effects;
};
extern NSString* GetAvnCustomDataType()
{
char buffer[256];
sprintf(buffer, "net.avaloniaui.inproc.uti.n%in", getpid());
return [NSString stringWithUTF8String:buffer];
}
@interface AvnDndSource : NSObject<NSDraggingSource>
@end
@implementation AvnDndSource
{
NSDragOperation _operation;
ComPtr<IAvnDndResultCallback> _cb;
void* _sourceHandle;
};
- (NSDragOperation)draggingSession:(nonnull NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
return NSDragOperationCopy;
}
- (AvnDndSource*) initWithOperation: (NSDragOperation)operation
andCallback: (IAvnDndResultCallback*) cb
andSourceHandle: (void*) handle
{
self = [super init];
_operation = operation;
_cb = cb;
_sourceHandle = handle;
return self;
}
- (void)draggingSession:(NSDraggingSession *)session
endedAtPoint:(NSPoint)screenPoint
operation:(NSDragOperation)operation
{
if(_cb != nil)
{
auto cb = _cb;
_cb = nil;
cb->OnDragAndDropComplete(ConvertDragDropEffects(operation));
}
if(_sourceHandle != nil)
{
FreeAvnGCHandle(_sourceHandle);
_sourceHandle = nil;
}
}
- (void*) gcHandle
{
return _sourceHandle;
}
@end
extern NSObject<NSDraggingSource>* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle)
{
return [[AvnDndSource alloc] initWithOperation:op andCallback:cb andSourceHandle:handle];
};
extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject<NSDraggingInfo>* info)
{
id obj = [info draggingSource];
if(obj == nil)
return nil;
if([obj isKindOfClass: [AvnDndSource class]])
{
auto src = (AvnDndSource*)obj;
return [src gcHandle];
}
return nil;
}

19
native/Avalonia.Native/src/OSX/main.mm

@ -150,14 +150,15 @@ public:
}
@end
static ComPtr<IAvnGCHandleDeallocatorCallback> _deallocator;
class AvaloniaNative : public ComSingleObject<IAvaloniaNativeFactory, &IID_IAvaloniaNativeFactory>
{
public:
FORWARD_IUNKNOWN()
virtual HRESULT Initialize() override
virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) override
{
_deallocator = deallocator;
@autoreleasepool{
[[ThreadingInitializer new] do];
}
@ -207,7 +208,13 @@ public:
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) override
{
*ppv = ::CreateClipboard ();
*ppv = ::CreateClipboard (nil, nil);
return S_OK;
}
virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) override
{
*ppv = ::CreateClipboard (nil, [NSPasteboardItem new]);
return S_OK;
}
@ -257,6 +264,12 @@ extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
return new AvaloniaNative();
};
extern void FreeAvnGCHandle(void* handle)
{
if(_deallocator != nil)
_deallocator->FreeGCHandle(handle);
}
NSSize ToNSSize (AvnSize s)
{
NSSize result;

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

@ -3,7 +3,7 @@
class WindowBaseImpl;
@interface AvnView : NSView<NSTextInputClient>
@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination>
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent;
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;

108
native/Avalonia.Native/src/OSX/window.mm

@ -382,6 +382,50 @@ public:
*ppv = [renderTarget createSurfaceRenderTarget];
return *ppv == nil ? E_FAIL : S_OK;
}
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
IAvnClipboard* clipboard, IAvnDndResultCallback* cb,
void* sourceHandle) override
{
auto item = TryGetPasteboardItem(clipboard);
[item setString:@"" forType:GetAvnCustomDataType()];
if(item == nil)
return E_INVALIDARG;
if(View == NULL)
return E_FAIL;
auto nsevent = [NSApp currentEvent];
auto nseventType = [nsevent type];
// If current event isn't a mouse one (probably due to malfunctioning user app)
// attempt to forge a new one
if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
|| (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged)))
{
auto nspoint = [Window convertBaseToScreen: ToNSPoint(point)];
CGPoint cgpoint = NSPointToCGPoint(nspoint);
auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
nsevent = [NSEvent eventWithCGEvent: cgevent];
CFRelease(cgevent);
}
auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item];
auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height};
[dragItem setDraggingFrame: dragItemRect contents: dragItemImage];
int op = 0; int ieffects = (int)effects;
if((ieffects & (int)AvnDragDropEffects::Copy) != 0)
op |= NSDragOperationCopy;
if((ieffects & (int)AvnDragDropEffects::Link) != 0)
op |= NSDragOperationLink;
if((ieffects & (int)AvnDragDropEffects::Move) != 0)
op |= NSDragOperationMove;
[View beginDraggingSessionWithItems: @[dragItem] event: nsevent
source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
return S_OK;
}
protected:
virtual NSWindowStyleMask GetStyle()
@ -911,7 +955,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_area = nullptr;
_lastPixelSize.Height = 100;
_lastPixelSize.Width = 100;
[self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
return self;
}
@ -1302,6 +1346,68 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return result;
}
- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info
{
auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
auto avnPoint = [self toAvnPoint:localPoint];
auto point = [self translateLocalPoint:avnPoint];
auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
NSDragOperation nsop = [info draggingSourceOperationMask];
auto effects = ConvertDragDropEffects(nsop);
int reffects = (int)_parent->BaseEvents
->DragEvent(type, point, modifiers, effects,
CreateClipboard([info draggingPasteboard], nil),
GetAvnDataObjectHandleFromDraggingInfo(info));
NSDragOperation ret = 0;
// Ensure that the managed part didn't add any new effects
reffects = (int)effects & (int)reffects;
// OSX requires exactly one operation
if((reffects & (int)AvnDragDropEffects::Copy) != 0)
ret = NSDragOperationCopy;
else if((reffects & (int)AvnDragDropEffects::Move) != 0)
ret = NSDragOperationMove;
else if((reffects & (int)AvnDragDropEffects::Link) != 0)
ret = NSDragOperationLink;
if(ret == 0)
ret = NSDragOperationNone;
return ret;
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender];
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{
return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender];
}
- (void)draggingExited:(id <NSDraggingInfo>)sender
{
[self triggerAvnDragEvent: AvnDragEventType::Leave info:sender];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
{
return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone;
}
- (void)concludeDragOperation:(nullable id <NSDraggingInfo>)sender
{
}
@end

5
packages/Avalonia/AvaloniaBuildTasks.targets

@ -2,6 +2,7 @@
<PropertyGroup>
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)</_AvaloniaUseExternalMSBuild>
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false</_AvaloniaUseExternalMSBuild>
<AvaloniaXamlReportImportance Condition="'$(AvaloniaXamlReportImportance)' == ''">low</AvaloniaXamlReportImportance>
</PropertyGroup>
<UsingTask TaskName="GenerateAvaloniaResourcesTask"
@ -38,7 +39,8 @@
Output="$(AvaloniaResourcesTemporaryFilePath)"
Root="$(MSBuildProjectDirectory)"
Resources="@(AvaloniaResource)"
EmbeddedResources="@(EmbeddedResources)"/>
EmbeddedResources="@(EmbeddedResources)"
ReportImportance="$(AvaloniaXamlReportImportance)"/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/>
@ -67,6 +69,7 @@
OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)"
VerifyIl="$(AvaloniaXamlIlVerifyIl)"
ReportImportance="$(AvaloniaXamlReportImportance)"
/>
<Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"

12
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -9,9 +9,15 @@
Margin="0,16,0,0"
HorizontalAlignment="Center"
Spacing="16">
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMe">
<TextBlock Name="DragState">Drag Me</TextBlock>
</Border>
<StackPanel>
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMeText">
<TextBlock Name="DragStateText">Drag Me</TextBlock>
</Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMeCustom">
<TextBlock Name="DragStateCustom">Drag Me (custom)</TextBlock>
</Border>
</StackPanel>
<Border Background="{DynamicResource ThemeAccentBrush2}" Padding="16"
DragDrop.AllowDrop="True">
<TextBlock Name="DropState">Drop some text or files here</TextBlock>

92
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -3,69 +3,85 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages
{
public class DragAndDropPage : UserControl
{
private TextBlock _DropState;
private TextBlock _DragState;
private Border _DragMe;
private int DragCount = 0;
TextBlock _DropState;
private const string CustomFormat = "application/xxx-avalonia-controlcatalog-custom";
public DragAndDropPage()
{
this.InitializeComponent();
_DropState = this.Find<TextBlock>("DropState");
_DragMe.PointerPressed += DoDrag;
int textCount = 0;
SetupDnd("Text", d => d.Set(DataFormats.Text,
$"Text was dragged {++textCount} times"));
AddHandler(DragDrop.DropEvent, Drop);
AddHandler(DragDrop.DragOverEvent, DragOver);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"));
}
private async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects = DragDropEffects.Copy)
{
DataObject dragData = new DataObject();
dragData.Set(DataFormats.Text, $"You have dragged text {++DragCount} times");
var dragMe = this.Find<Border>("DragMe" + suffix);
var dragState = this.Find<TextBlock>("DragState"+suffix);
var result = await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
switch(result)
async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
{
case DragDropEffects.Copy:
_DragState.Text = "The text was copied"; break;
case DragDropEffects.Link:
_DragState.Text = "The text was linked"; break;
case DragDropEffects.None:
_DragState.Text = "The drag operation was canceled"; break;
var dragData = new DataObject();
factory(dragData);
var result = await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
switch (result)
{
case DragDropEffects.Copy:
dragState.Text = "Data was copied";
break;
case DragDropEffects.Link:
dragState.Text = "Data was linked";
break;
case DragDropEffects.None:
dragState.Text = "The drag operation was canceled";
break;
}
}
}
private void DragOver(object sender, DragEventArgs e)
{
// Only allow Copy or Link as Drop Operations.
e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link);
void DragOver(object sender, DragEventArgs e)
{
// Only allow Copy or Link as Drop Operations.
e.DragEffects = e.DragEffects & (DragDropEffects.Copy | DragDropEffects.Link);
// Only allow if the dragged data contains text or filenames.
if (!e.Data.Contains(DataFormats.Text) && !e.Data.Contains(DataFormats.FileNames))
e.DragEffects = DragDropEffects.None;
}
// Only allow if the dragged data contains text or filenames.
if (!e.Data.Contains(DataFormats.Text)
&& !e.Data.Contains(DataFormats.FileNames)
&& !e.Data.Contains(CustomFormat))
e.DragEffects = DragDropEffects.None;
}
private void Drop(object sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
_DropState.Text = e.Data.GetText();
else if (e.Data.Contains(DataFormats.FileNames))
_DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames());
void Drop(object sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
_DropState.Text = e.Data.GetText();
else if (e.Data.Contains(DataFormats.FileNames))
_DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames());
else if (e.Data.Contains(CustomFormat))
_DropState.Text = "Custom: " + e.Data.Get(CustomFormat);
}
dragMe.PointerPressed += DoDrag;
AddHandler(DragDrop.DropEvent, Drop);
AddHandler(DragDrop.DragOverEvent, DragOver);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_DropState = this.Find<TextBlock>("DropState");
_DragState = this.Find<TextBlock>("DragState");
_DragMe = this.Find<Border>("DragMe");
}
}
}

1
samples/ControlCatalog/Pages/TreeViewPage.xaml

@ -19,7 +19,6 @@
</TreeView>
<Button Command="{Binding AddItemCommand}">Add</Button>
<Button Command="{Binding RemoveItemCommand}">Remove</Button>
<ComboBox SelectedIndex="{Binding SelectionMode, Mode=TwoWay}">

100
samples/ControlCatalog/Pages/TreeViewPage.xaml.cs

@ -1,9 +1,6 @@
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ReactiveUI;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
@ -12,105 +9,12 @@ namespace ControlCatalog.Pages
public TreeViewPage()
{
InitializeComponent();
DataContext = new PageViewModel();
DataContext = new TreeViewPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private class PageViewModel : ReactiveObject
{
private SelectionMode _selectionMode;
public PageViewModel()
{
Node root = new Node();
Items = root.Children;
Selection = new SelectionModel();
AddItemCommand = ReactiveCommand.Create(() =>
{
Node parentItem = Selection.SelectedItems.Count > 0 ?
(Node)Selection.SelectedItems[0] : root;
parentItem.AddNewItem();
});
RemoveItemCommand = ReactiveCommand.Create(() =>
{
while (Selection.SelectedItems.Count > 0)
{
Node lastItem = (Node)Selection.SelectedItems[0];
RecursiveRemove(Items, lastItem);
Selection.DeselectAt(Selection.SelectedIndices[0]);
}
bool RecursiveRemove(ObservableCollection<Node> items, Node selectedItem)
{
if (items.Remove(selectedItem))
{
return true;
}
foreach (Node item in items)
{
if (item.AreChildrenInitialized && RecursiveRemove(item.Children, selectedItem))
{
return true;
}
}
return false;
}
});
}
public ObservableCollection<Node> Items { get; }
public SelectionModel Selection { get; }
public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
public SelectionMode SelectionMode
{
get => _selectionMode;
set
{
Selection.ClearSelection();
this.RaiseAndSetIfChanged(ref _selectionMode, value);
}
}
}
private class Node
{
private int _counter;
private ObservableCollection<Node> _children;
public string Header { get; private set; }
public bool AreChildrenInitialized => _children != null;
public ObservableCollection<Node> Children
{
get
{
if (_children == null)
{
_children = new ObservableCollection<Node>(Enumerable.Range(1, 10).Select(i => CreateNewNode()));
}
return _children;
}
}
public void AddNewItem() => Children.Add(CreateNewNode());
public override string ToString() => Header;
private Node CreateNewNode() => new Node { Header = $"Item {_counter++}" };
}
}
}

115
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using Avalonia.Controls;
using ReactiveUI;
namespace ControlCatalog.ViewModels
{
public class TreeViewPageViewModel : ReactiveObject
{
private readonly Node _root;
private SelectionMode _selectionMode;
public TreeViewPageViewModel()
{
_root = new Node();
Items = _root.Children;
Selection = new SelectionModel();
Selection.SelectionChanged += SelectionChanged;
AddItemCommand = ReactiveCommand.Create(AddItem);
RemoveItemCommand = ReactiveCommand.Create(RemoveItem);
}
public ObservableCollection<Node> Items { get; }
public SelectionModel Selection { get; }
public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
public SelectionMode SelectionMode
{
get => _selectionMode;
set
{
Selection.ClearSelection();
this.RaiseAndSetIfChanged(ref _selectionMode, value);
}
}
private void AddItem()
{
var parentItem = Selection.SelectedItems.Count > 0 ? (Node)Selection.SelectedItems[0] : _root;
parentItem.AddItem();
}
private void RemoveItem()
{
while (Selection.SelectedItems.Count > 0)
{
Node lastItem = (Node)Selection.SelectedItems[0];
RecursiveRemove(Items, lastItem);
Selection.DeselectAt(Selection.SelectedIndices[0]);
}
bool RecursiveRemove(ObservableCollection<Node> items, Node selectedItem)
{
if (items.Remove(selectedItem))
{
return true;
}
foreach (Node item in items)
{
if (item.AreChildrenInitialized && RecursiveRemove(item.Children, selectedItem))
{
return true;
}
}
return false;
}
}
private void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
{
var selected = string.Join(",", e.SelectedIndices);
var deselected = string.Join(",", e.DeselectedIndices);
System.Diagnostics.Debug.WriteLine($"Selected '{selected}', Deselected '{deselected}'");
}
public class Node
{
private ObservableCollection<Node> _children;
private int _childIndex = 10;
public Node()
{
Header = "Item";
}
public Node(Node parent, int index)
{
Parent = parent;
Header = parent.Header + ' ' + index;
}
public Node Parent { get; }
public string Header { get; }
public bool AreChildrenInitialized => _children != null;
public ObservableCollection<Node> Children => _children ??= CreateChildren();
public void AddItem() => Children.Add(new Node(this, _childIndex++));
public void RemoveItem(Node child) => Children.Remove(child);
public override string ToString() => Header;
private ObservableCollection<Node> CreateChildren()
{
return new ObservableCollection<Node>(
Enumerable.Range(0, 10).Select(i => new Node(this, i)));
}
}
}
}

11
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -12,6 +12,8 @@ namespace Avalonia.Build.Tasks
{
public bool Execute()
{
Enum.TryParse(ReportImportance, true, out MessageImportance outputImportance);
OutputPath = OutputPath ?? AssemblyFile;
var outputPdb = GetPdbPath(OutputPath);
var input = AssemblyFile;
@ -32,9 +34,12 @@ namespace Avalonia.Build.Tasks
}
}
var msg = $"CompileAvaloniaXamlTask -> AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}";
BuildEngine.LogMessage(msg, outputImportance < MessageImportance.Low ? MessageImportance.High : outputImportance);
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath, VerifyIl);
ProjectDirectory, OutputPath, VerifyIl, outputImportance);
if (!res.Success)
return false;
if (!res.WrittenFile)
@ -68,7 +73,9 @@ namespace Avalonia.Build.Tasks
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
public string ReportImportance { get; set; }
public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; }
}

9
src/Avalonia.Build.Tasks/Extensions.cs

@ -9,14 +9,19 @@ namespace Avalonia.Build.Tasks
public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message)
{
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message,
engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message,
"", "Avalonia"));
}
public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message)
{
engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message,
"", "Avalonia"));
}
public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp)
{
engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp));
}
}
}

31
src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs

@ -22,6 +22,10 @@ namespace Avalonia.Build.Tasks
[Required]
public ITaskItem[] EmbeddedResources { get; set; }
public string ReportImportance { get; set; }
private MessageImportance _reportImportance;
class Source
{
public string Path { get; set; }
@ -29,15 +33,11 @@ namespace Avalonia.Build.Tasks
private byte[] _data;
private string _sourcePath;
public Source(string file, string root)
public Source(string relativePath, string root)
{
file = SPath.GetFullPath(file);
root = SPath.GetFullPath(root);
var fileUri = new Uri(file, UriKind.Absolute);
var rootUri = new Uri(root, UriKind.Absolute);
rootUri = new Uri(rootUri.ToString().TrimEnd('/') + '/');
Path = '/' + rootUri.MakeRelativeUri(fileUri).ToString().TrimStart('/');
_sourcePath = file;
Path = "/" + relativePath.Replace('\\', '/');
_sourcePath = SPath.Combine(root, relativePath);
Size = (int)new FileInfo(_sourcePath).Length;
}
@ -65,7 +65,14 @@ namespace Avalonia.Build.Tasks
}
}
List<Source> BuildResourceSources() => Resources.Select(r => new Source(r.ItemSpec, Root)).ToList();
List<Source> BuildResourceSources()
=> Resources.Select(r =>
{
var src = new Source(r.ItemSpec, Root);
BuildEngine.LogMessage($"avares -> name:{src.Path}, path: {src.SystemPath}, size:{src.Size}, ItemSpec:{r.ItemSpec}", _reportImportance);
return src;
}).ToList();
private void Pack(Stream output, List<Source> sources)
{
@ -136,10 +143,14 @@ namespace Avalonia.Build.Tasks
sources.Add(new Source("/!AvaloniaResourceXamlInfo", ms.ToArray()));
return true;
}
public bool Execute()
{
foreach(var r in EmbeddedResources.Where(r=>r.ItemSpec.EndsWith(".xaml")||r.ItemSpec.EndsWith(".paml")))
Enum.TryParse<MessageImportance>(ReportImportance, out _reportImportance);
BuildEngine.LogMessage($"GenerateAvaloniaResourcesTask -> Root: {Root}, {Resources?.Count()} resources, Output:{Output}", _reportImportance < MessageImportance.Low ? MessageImportance.High : _reportImportance);
foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml")))
BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec,
"XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
var resources = BuildResourceSources();

4
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -40,7 +40,7 @@ namespace Avalonia.Build.Tasks
}
public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory,
string output, bool verifyIl)
string output, bool verifyIl, MessageImportance logImportance)
{
var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input);
var asm = typeSystem.TargetAssemblyDefinition;
@ -121,6 +121,8 @@ namespace Avalonia.Build.Tasks
{
try
{
engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance);
// StreamReader is needed here to handle BOM
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlIlParser.Parse(xaml);

2
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -2245,7 +2245,7 @@ namespace Avalonia.Controls
/// Builds the visual tree for the column header when a new template is applied.
/// </summary>
//TODO Validation UI
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
// The template has changed, so we need to refresh the visuals
_measured = false;

4
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -121,10 +121,8 @@ namespace Avalonia.Controls
/// <summary>
/// Builds the visual tree for the cell control when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
UpdatePseudoClasses();
_rightGridLine = e.NameScope.Find<Rectangle>(DATAGRIDCELL_elementRightGridLine);
if (_rightGridLine != null && OwningColumn == null)

4
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -536,10 +536,8 @@ namespace Avalonia.Controls
/// <summary>
/// Builds the visual tree for the column header when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
RootElement = e.NameScope.Find<Panel>(DATAGRIDROW_elementRoot);
if (RootElement != null)
{

4
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -168,7 +168,7 @@ namespace Avalonia.Controls
private IDisposable _expanderButtonSubscription;
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_rootElement = e.NameScope.Find<Panel>(DataGridRow.DATAGRIDROW_elementRoot);
@ -199,8 +199,6 @@ namespace Avalonia.Controls
_itemCountElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_itemCountElement);
_propertyNameElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_propertyNameElement);
UpdateTitleElements();
base.OnTemplateApplied(e);
}
internal void ApplyHeaderStatus()

4
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@ -94,10 +94,8 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// Builds the visual tree for the row header when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
_rootElement = e.NameScope.Find<Control>(DATAGRIDROWHEADER_elementRootName);
if (_rootElement != null)
{

8
src/Avalonia.Controls/Application.cs

@ -254,8 +254,12 @@ namespace Avalonia
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IStyler>().ToConstant(_styler)
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)
.Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);
// TODO: Fix this, for now we keep this behavior since someone might be relying on it in 0.9.x
if (AvaloniaLocator.Current.GetService<IPlatformDragSource>() == null)
AvaloniaLocator.CurrentMutable
.Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
var clock = new RenderLoopClock();
AvaloniaLocator.CurrentMutable

4
src/Avalonia.Controls/AutoCompleteBox.cs

@ -1212,7 +1212,7 @@ namespace Avalonia.Controls
/// <see cref="T:Avalonia.Controls.AutoCompleteBox" /> control
/// when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (DropDownPopup != null)
@ -1240,7 +1240,7 @@ namespace Avalonia.Controls
OpeningDropDown(false);
}
base.OnTemplateApplied(e);
base.OnApplyTemplate(e);
}
/// <summary>

2
src/Avalonia.Controls/ButtonSpinner.cs

@ -121,7 +121,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc />
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
IncreaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
DecreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");

4
src/Avalonia.Controls/Calendar/Calendar.cs

@ -2079,10 +2079,8 @@ namespace Avalonia.Controls
/// <see cref="T:System.Windows.Controls.Calendar" /> when a new
/// template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
Root = e.NameScope.Find<Panel>(PART_ElementRoot);
SelectedMonth = DisplayDate;

3
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -98,9 +98,8 @@ namespace Avalonia.Controls.Primitives
/// <see cref="T:System.Windows.Controls.Primitives.CalendarButton" />
/// when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
SetPseudoClasses();
}

4
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -150,11 +150,11 @@ namespace Avalonia.Controls.Primitives
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
SetPseudoClasses();
}
private void SetPseudoClasses()
{
if (_ignoringMouseOverState)

4
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -268,10 +268,8 @@ namespace Avalonia.Controls.Primitives
/// <see cref="T:System.Windows.Controls.Primitives.CalendarItem" />
/// when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
HeaderButton = e.NameScope.Find<Button>(PART_ElementHeaderButton);
PreviousButton = e.NameScope.Find<Button>(PART_ElementPreviousButton);
NextButton = e.NameScope.Find<Button>(PART_ElementNextButton);

4
src/Avalonia.Controls/Calendar/DatePicker.cs

@ -413,7 +413,7 @@ namespace Avalonia.Controls
DisplayDate = DateTime.Today;
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_calendar != null)
{
@ -508,8 +508,6 @@ namespace Avalonia.Controls
SetSelectedDate();
}
}
base.OnTemplateApplied(e);
}
protected override void OnPropertyChanged<T>(

4
src/Avalonia.Controls/ComboBox.cs

@ -219,7 +219,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_popup != null)
{
@ -230,8 +230,6 @@ namespace Avalonia.Controls
_popup = e.NameScope.Get<Popup>("PART_Popup");
_popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
base.OnTemplateApplied(e);
}
/// <summary>

3
src/Avalonia.Controls/ListBox.cs

@ -161,10 +161,9 @@ namespace Avalonia.Controls
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
Scroll = e.NameScope.Find<IScrollable>("PART_ScrollViewer");
base.OnTemplateApplied(e);
}
}
}

4
src/Avalonia.Controls/MenuItem.cs

@ -417,10 +417,8 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
if (_popup != null)
{
_popup.Opened -= PopupOpened;

4
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -77,10 +77,8 @@ namespace Avalonia.Controls.Notifications
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
var itemsControl = e.NameScope.Find<Panel>("PART_Items");
_items = itemsControl?.Children;
}

2
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -295,7 +295,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc />
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (TextBox != null)
{

4
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -147,10 +147,8 @@ namespace Avalonia.Controls.Primitives
}
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
if (_lineUpButton != null)
{
_lineUpButton.Click -= LineUpClick;

11
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -263,7 +263,10 @@ namespace Avalonia.Controls.Primitives
if (nameScope == null)
nameScope = new NameScope();
OnTemplateApplied(new TemplateAppliedEventArgs(nameScope));
var e = new TemplateAppliedEventArgs(nameScope);
OnApplyTemplate(e);
OnTemplateApplied(e);
RaiseEvent(e);
}
_appliedTemplate = template;
@ -306,13 +309,17 @@ namespace Avalonia.Controls.Primitives
base.OnDetachedFromLogicalTree(e);
}
protected virtual void OnApplyTemplate(TemplateAppliedEventArgs e)
{
}
/// <summary>
/// Called when the control's template is applied.
/// </summary>
/// <param name="e">The event args.</param>
[Obsolete("Use OnApplyTemplate")]
protected virtual void OnTemplateApplied(TemplateAppliedEventArgs e)
{
RaiseEvent(e);
}
/// <summary>

2
src/Avalonia.Controls/ProgressBar.cs

@ -102,7 +102,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_indicator = e.NameScope.Get<Border>("PART_Indicator");

11
src/Avalonia.Controls/SelectionModel.cs

@ -774,7 +774,10 @@ namespace Avalonia.Controls
winrtEnd,
info =>
{
info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select);
if (info.Path >= winrtStart && info.Path <= winrtEnd)
{
info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select);
}
});
}
@ -808,7 +811,11 @@ namespace Avalonia.Controls
}
OnSelectionChanged(e);
_rootNode.Cleanup();
if (_operationCount == 0)
{
_rootNode.Cleanup();
}
}
private void ApplyAutoSelect()

134
src/Avalonia.Controls/SelectionNode.cs

@ -8,6 +8,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Controls.Utils;
#nullable enable
@ -214,7 +215,21 @@ namespace Avalonia.Controls
public void SetChildrenObservable(IObservable<object?> resolver)
{
_childrenSubscription = resolver.Subscribe(x => Source = x);
_childrenSubscription = resolver.Subscribe(x =>
{
if (Source != null)
{
using (_manager.Update())
{
SelectionTreeHelper.Traverse(
this,
realizeChildren: false,
info => info.Node.Clear());
}
}
Source = x;
});
}
public int SelectedCount { get; private set; }
@ -544,11 +559,14 @@ namespace Avalonia.Controls
private void ClearChildNodes()
{
foreach (var child in _childrenNodes)
for (int i = 0; i < _childrenNodes.Count; i++)
{
var child = _childrenNodes[i];
if (child != null && child != _manager.SharedLeafNode)
{
child.Dispose();
_childrenNodes[i] = null;
}
}
@ -713,101 +731,67 @@ namespace Avalonia.Controls
var selectionInvalidated = false;
var removed = new List<object?>();
var count = items.Count;
// Remove the items from the selection for leaf
if (ItemsSourceView!.Count > 0)
{
bool isSelected = false;
var isSelected = false;
for (int i = 0; i <= count - 1; i++)
for (int i = 0; i <= count - 1; i++)
{
if (IsSelected(index + i))
{
if (IsSelected(index + i))
{
isSelected = true;
removed.Add(items[i]);
}
isSelected = true;
removed.Add(items[i]);
}
}
if (isSelected)
{
var removeRange = new IndexRange(index, index + count - 1);
SelectedCount -= IndexRange.Remove(_selected, removeRange);
selectionInvalidated = true;
if (_selectedItems != null)
{
foreach (var i in items)
{
_selectedItems.Remove(i);
}
}
}
if (isSelected)
{
var removeRange = new IndexRange(index, index + count - 1);
SelectedCount -= IndexRange.Remove(_selected, removeRange);
selectionInvalidated = true;
for (int i = 0; i < _selected.Count; i++)
if (_selectedItems != null)
{
var range = _selected[i];
// The range is after the removed items, need to shift the range left
if (range.End > index)
foreach (var i in items)
{
// Shift the range to the left
_selected[i] = new IndexRange(range.Begin - count, range.End - count);
selectionInvalidated = true;
_selectedItems.Remove(i);
}
}
}
// Update for non-leaf if we are tracking non-leaf nodes
if (_childrenNodes.Count > 0)
{
selectionInvalidated = true;
for (int i = 0; i < count; i++)
{
if (_childrenNodes[index] != null)
{
removed.AddRange(_childrenNodes[index]!.SelectedItems);
RealizedChildrenNodeCount--;
_childrenNodes[index]!.Dispose();
}
_childrenNodes.RemoveAt(index);
}
}
for (int i = 0; i < _selected.Count; i++)
{
var range = _selected[i];
//Adjust the anchor
if (AnchorIndex >= index)
// The range is after the removed items, need to shift the range left
if (range.End > index)
{
AnchorIndex -= count;
// Shift the range to the left
_selected[i] = new IndexRange(range.Begin - count, range.End - count);
selectionInvalidated = true;
}
}
else
{
// No more items in the list, clear
ClearSelection();
RealizedChildrenNodeCount = 0;
selectionInvalidated = true;
}
// Check if removing a node invalidated an ancestors
// selection state. For example if parent was partially selected before
// removing an item, it could be selected now.
if (!selectionInvalidated)
// Update for non-leaf if we are tracking non-leaf nodes
if (_childrenNodes.Count > 0)
{
var parent = _parent;
while (parent != null)
selectionInvalidated = true;
for (int i = 0; i < count; i++)
{
var isSelected = parent.IsSelectedWithPartial();
// If a parent is partially selected, then it will become selected.
// If it is selected or not selected - there is no change.
if (!isSelected.HasValue)
if (_childrenNodes[index] != null)
{
selectionInvalidated = true;
break;
removed.AddRange(_childrenNodes[index]!.SelectedItems);
RealizedChildrenNodeCount--;
_childrenNodes[index]!.Dispose();
}
parent = parent._parent;
_childrenNodes.RemoveAt(index);
}
}
//Adjust the anchor
if (AnchorIndex >= index)
{
AnchorIndex -= count;
}
return (selectionInvalidated, removed);
}

2
src/Avalonia.Controls/Slider.cs

@ -82,7 +82,7 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_decreaseButton != null)
{

4
src/Avalonia.Controls/TabControl.cs

@ -217,10 +217,8 @@ namespace Avalonia.Controls
return new TabItemContainerGenerator(this);
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
ItemsPresenterPart = e.NameScope.Get<ItemsPresenter>("PART_ItemsPresenter");
}

2
src/Avalonia.Controls/TextBox.cs

@ -341,7 +341,7 @@ namespace Avalonia.Controls
set { SetAndRaise(NewLineProperty, ref _newLine, value); }
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");

10
src/Avalonia.Controls/TreeView.cs

@ -4,6 +4,7 @@ using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Utils;
@ -395,7 +396,14 @@ namespace Avalonia.Controls
private void OnSelectionModelChildrenRequested(object sender, SelectionModelChildrenRequestedEventArgs e)
{
var container = ItemContainerGenerator.Index.ContainerFromItem(e.Source) as ItemsControl;
e.Children = container?.GetObservable(ItemsProperty);
if (container is object)
{
e.Children = Observable.CombineLatest(
container.GetObservable(TreeViewItem.IsExpandedProperty),
container.GetObservable(ItemsProperty),
(expanded, items) => expanded ? items : null);
}
}
private TreeViewItem GetContainerInDirection(

3
src/Avalonia.Controls/TreeViewItem.cs

@ -162,9 +162,8 @@ namespace Avalonia.Controls
// Don't call base.OnKeyDown - let events bubble up to containing TreeView.
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
_header = e.NameScope.Find<IControl>("PART_Header");
}

76
src/Avalonia.Native/AvaloniaNativeDragSource.cs

@ -0,0 +1,76 @@
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Native.Interop;
using Avalonia.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Native
{
class AvaloniaNativeDragSource : IPlatformDragSource
{
private readonly IAvaloniaNativeFactory _factory;
public AvaloniaNativeDragSource(IAvaloniaNativeFactory factory)
{
_factory = factory;
}
TopLevel FindRoot(IInteractive interactive)
{
while (interactive != null && !(interactive is IVisual))
interactive = interactive.InteractiveParent;
if (interactive == null)
return null;
var visual = (IVisual)interactive;
return visual.VisualRoot as TopLevel;
}
class DndCallback : CallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource<DragDropEffects> _tcs;
public DndCallback(TaskCompletionSource<DragDropEffects> tcs)
{
_tcs = tcs;
}
public void OnDragAndDropComplete(AvnDragDropEffects effect)
{
_tcs?.TrySetResult((DragDropEffects)effect);
_tcs = null;
}
}
public Task<DragDropEffects> DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects)
{
// Sanity check
var tl = FindRoot(triggerEvent.Source);
var view = tl?.PlatformImpl as WindowBaseImpl;
if (view == null)
throw new ArgumentException();
triggerEvent.Pointer.Capture(null);
var tcs = new TaskCompletionSource<DragDropEffects>();
var clipboardImpl = _factory.CreateDndClipboard();
using (var clipboard = new ClipboardImpl(clipboardImpl))
using (var cb = new DndCallback(tcs))
{
if (data.Contains(DataFormats.Text))
// API is synchronous, so it's OK
clipboard.SetTextAsync(data.GetText()).Wait();
view.BeginDraggingSession((AvnDragDropEffects)allowedEffects,
triggerEvent.GetPosition(tl).ToAvnPoint(), clipboardImpl, cb,
GCHandle.ToIntPtr(GCHandle.Alloc(data)));
}
return tcs.Task;
}
}
}

16
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -77,17 +77,25 @@ namespace Avalonia.Native
_factory = factory;
}
class GCHandleDeallocator : CallbackBase, IAvnGCHandleDeallocatorCallback
{
public void FreeGCHandle(IntPtr handle)
{
GCHandle.FromIntPtr(handle).Free();
}
}
void DoInitialize(AvaloniaNativePlatformOptions options)
{
_options = options;
_factory.Initialize();
_factory.Initialize(new GCHandleDeallocator());
if (_factory.MacOptions != null)
{
var macOpts = AvaloniaLocator.Current.GetService<MacOSPlatformOptions>();
_factory.MacOptions.ShowInDock = macOpts?.ShowInDock != false ? 1 : 0;
}
AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>()
.ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
@ -101,7 +109,9 @@ namespace Avalonia.Native
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta))
.Bind<IMountedVolumeInfoProvider>().ToConstant(new MacOSMountedVolumeInfoProvider());
.Bind<IMountedVolumeInfoProvider>().ToConstant(new MacOSMountedVolumeInfoProvider())
.Bind<IPlatformDragSource>().ToConstant(new AvaloniaNativeDragSource(_factory))
;
if (_options.UseGpu)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>()
.ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay()));

39
src/Avalonia.Native/AvnString.cs

@ -0,0 +1,39 @@
using System.Runtime.InteropServices;
namespace Avalonia.Native.Interop
{
unsafe partial class IAvnString
{
private string _managed;
public string String
{
get
{
if (_managed == null)
{
var ptr = Pointer();
if (ptr == null)
return null;
_managed = System.Text.Encoding.UTF8.GetString((byte*)ptr.ToPointer(), Length());
}
return _managed;
}
}
public override string ToString() => String;
}
partial class IAvnStringArray
{
public string[] ToStringArray()
{
var arr = new string[Count];
for(uint c = 0; c<arr.Length;c++)
using (var s = Get(c))
arr[c] = s.String;
return arr;
}
}
}

98
src/Avalonia.Native/ClipboardImpl.cs

@ -1,15 +1,22 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Native.Interop;
using Avalonia.Platform.Interop;
namespace Avalonia.Native
{
class ClipboardImpl : IClipboard
class ClipboardImpl : IClipboard, IDisposable
{
private IAvnClipboard _native;
private const string NSPasteboardTypeString = "public.utf8-plain-text";
private const string NSFilenamesPboardType = "NSFilenamesPboardType";
private const string NSPasteboardTypeFileUrl = "public.file-url";
public ClipboardImpl(IAvnClipboard native)
{
_native = native;
@ -22,14 +29,10 @@ namespace Avalonia.Native
return Task.CompletedTask;
}
public unsafe Task<string> GetTextAsync()
public Task<string> GetTextAsync()
{
using (var text = _native.GetText())
{
var result = System.Text.Encoding.UTF8.GetString((byte*)text.Pointer(), text.Length());
return Task.FromResult(result);
}
using (var text = _native.GetText(NSPasteboardTypeString))
return Task.FromResult(text.String);
}
public Task SetTextAsync(string text)
@ -40,11 +43,84 @@ namespace Avalonia.Native
{
using (var buffer = new Utf8Buffer(text))
{
_native.SetText(buffer.DangerousGetHandle());
_native.SetText(NSPasteboardTypeString, buffer.DangerousGetHandle());
}
}
return Task.CompletedTask;
}
public IEnumerable<string> GetFormats()
{
var rv = new List<string>();
using (var formats = _native.ObtainFormats())
{
var cnt = formats.Count;
for (uint c = 0; c < cnt; c++)
{
using (var fmt = formats.Get(c))
{
if(fmt.String == NSPasteboardTypeString)
rv.Add(DataFormats.Text);
if(fmt.String == NSFilenamesPboardType)
rv.Add(DataFormats.FileNames);
}
}
}
return rv;
}
public void Dispose()
{
_native?.Dispose();
_native = null;
}
public IEnumerable<string> GetFileNames()
{
using (var strings = _native.GetStrings(NSFilenamesPboardType))
return strings.ToStringArray();
}
}
class ClipboardDataObject : IDataObject, IDisposable
{
private ClipboardImpl _clipboard;
private List<string> _formats;
public ClipboardDataObject(IAvnClipboard clipboard)
{
_clipboard = new ClipboardImpl(clipboard);
}
public void Dispose()
{
_clipboard?.Dispose();
_clipboard = null;
}
List<string> Formats => _formats ??= _clipboard.GetFormats().ToList();
public IEnumerable<string> GetDataFormats() => Formats;
public bool Contains(string dataFormat) => Formats.Contains(dataFormat);
public string GetText()
{
// bad idea in general, but API is synchronous anyway
return _clipboard.GetTextAsync().Result;
}
public IEnumerable<string> GetFileNames() => _clipboard.GetFileNames();
public object Get(string dataFormat)
{
if (dataFormat == DataFormats.Text)
return GetText();
if (dataFormat == DataFormats.FileNames)
return GetFileNames();
return null;
}
}
}

31
src/Avalonia.Native/WindowImplBase.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
@ -200,6 +201,30 @@ namespace Avalonia.Native
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
}
public AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers,
AvnDragDropEffects effects,
IAvnClipboard clipboard, IntPtr dataObjectHandle)
{
var device = AvaloniaLocator.Current.GetService<IDragDropDevice>();
IDataObject dataObject = null;
if (dataObjectHandle != IntPtr.Zero)
dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject;
using(var clipboardDataObject = new ClipboardDataObject(clipboard))
{
if (dataObject == null)
dataObject = clipboardDataObject;
var args = new RawDragEvent(device, (RawDragEventType)type,
_parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects,
(RawInputModifiers)modifiers);
_parent.Input(args);
return (AvnDragDropEffects)args.Effects;
}
}
}
public void Activate()
@ -358,6 +383,12 @@ namespace Avalonia.Native
}
internal void BeginDraggingSession(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard clipboard,
IAvnDndResultCallback callback, IntPtr sourceHandle)
{
_native.BeginDragAndDropOperation(effects, point, clipboard, callback, sourceHandle);
}
public IPlatformHandle Handle { get; private set; }
}
}

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -352,7 +352,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
set => SetValue(PopupContentProperty, value);
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
Popup = (Popup)this.GetVisualChildren().Single();
}

802
tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs

File diff suppressed because it is too large

3
tests/Avalonia.Controls.UnitTests/TestTemplatedControl.cs

@ -12,9 +12,8 @@ namespace Avalonia.Controls.UnitTests
VisualChildren.Add(visual);
}
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
OnTemplateAppliedCalled = true;
}
}

13
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -130,6 +130,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
@ -157,6 +158,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
@ -188,6 +190,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item1 = tree[0].Children[1].Children[0];
var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
@ -225,6 +228,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -264,6 +268,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -297,6 +302,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -330,6 +336,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -376,6 +383,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
@ -402,6 +410,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
@ -579,6 +588,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = data[0].Children[0];
var node = target.ItemContainerGenerator.Index.ContainerFromItem(item);
@ -614,6 +624,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -651,6 +662,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
@ -697,6 +709,7 @@ namespace Avalonia.Controls.UnitTests
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];

Loading…
Cancel
Save