diff --git a/.editorconfig b/.editorconfig index f6bce9cb767..c7a381b730b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -156,6 +156,9 @@ indent_size = 2 [*.{props,targets,config,nuspec}] indent_size = 2 +[*.json] +indent_size = 2 + # Shell scripts [*.sh] end_of_line = lf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..4e34d4b1325 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve Avalonia +title: '' +labels: bug +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. Windows, Mac, Linux (State distribution)] +- Version [e.g. 0.10.0-rc1 or 0.9.12] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..687355d825e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Questions, Discussions, Ideas + url: https://github.com/AvaloniaUI/Avalonia/discussions/new + about: Please ask and answer questions here. + - name: Avalonia Community Support on Gitter + url: https://gitter.im/AvaloniaUI/Avalonia + about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..5f0a04cee3a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index acff8cc117e..46e8665945a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,11 +18,13 @@ - [ ] Added unit tests (if possible)? - [ ] Added XML documentation to any related classes? -- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Avaloniaui.net with user documentation +- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation ## Breaking changes +## Obsoletions / Deprecations + ## Fixed issues + false diff --git a/Documentation/build.md b/Documentation/build.md index 8c2ef57b549..a7d68eb5990 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -9,10 +9,24 @@ git clone https://github.com/AvaloniaUI/Avalonia.git git submodule update --init ``` +### Install the required version of the .NET Core SDK + +Go to https://dotnet.microsoft.com/download/visual-studio-sdks and install the latest version of the .NET Core SDK compatible with Avalonia UI. Make sure to download the SDK (not just the "runtime") package. The version compatible is indicated within the [global.json](https://github.com/AvaloniaUI/Avalonia/blob/master/global.json) file. Note that Avalonia UI does not always use the latest version and is hardcoded to use the last version known to be compatible (SDK releases may break the builds from time-to-time). + ### Open in Visual Studio -Open the `Avalonia.sln` solution in Visual Studio 2019 or newer. The free Visual Studio Community -edition works fine. Run the `Samples\ControlCatalog.Desktop` project to see the sample application. +Open the `Avalonia.sln` solution in Visual Studio 2019 or newer. The free Visual Studio Community edition works fine. Build and run the `Samples\ControlCatalog.Desktop` or `ControlCatalog.NetCore` project to see the sample application. + +### Troubleshooting + + * **Error CS0006: Avalonia.DesktopRuntime.dll could not be found** + + It is common for the first build to fail with the errors below (also discussed in [#4257](https://github.com/AvaloniaUI/Avalonia/issues/4257)). + ``` + >CSC : error CS0006: Metadata file 'C:\...\Avalonia\src\Avalonia.DesktopRuntime\bin\Debug\netcoreapp2.0\Avalonia.DesktopRuntime.dll' could not be found + >CSC : error CS0006: Metadata file 'C:\...\Avalonia\packages\Avalonia\bin\Debug\netcoreapp2.0\Avalonia.dll' could not be found + ``` + To correct this, right click on the `Avalonia.DesktopRuntime` project then press `Build` to build the project manually. Afterwards the solution should build normally and the ControlCatalog can be run. # Linux/macOS @@ -20,9 +34,9 @@ It's *not* possible to build the *whole* project on Linux/macOS. You can only bu MonoDevelop, Xamarin Studio and Visual Studio for Mac aren't capable of properly opening our solution. You can use Rider (at least 2017.2 EAP) or VSCode instead. They will fail to load most of platform specific projects, but you don't need them to run on .NET Core. -### Install the latest version of .NET Core +### Install the latest version of the .NET Core SDK -Go to https://www.microsoft.com/net/core and follow instructions for your OS. You need SDK (not just "runtime") package. +Go to https://www.microsoft.com/net/core and follow the instructions for your OS. Make sure to download the SDK (not just the "runtime") package. ### Additional requirements for macOS @@ -30,7 +44,7 @@ The build process needs [Xcode](https://developer.apple.com/xcode/) to build the Linux operating systems ship with their own respective package managers however we will use [Homebrew](https://brew.sh/) to manage packages on macOS. To install follow the instructions [here](https://docs.brew.sh/Installation). -### Install CastXML +### Install CastXML (pre Nov 2020) Avalonia requires [CastXML](https://github.com/CastXML/CastXML) for XML processing during the build process. The easiest way to install this is via the operating system's package managers, such as below. @@ -60,14 +74,10 @@ git submodule update --init --recursive ### Build native libraries (macOS only) -On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). The steps to get this working correctly are: -- Navigate to the Avalonia/native/Avalonia.Native/src/OSX folder and open the `Avalonia.Native.OSX.xcodeproj` project -- Build the library via the Product->Build menu. This will generate binaries in your local path under ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-*guid* where "guid" is uniquely generated every time you build. -- Manually install the native library by copying it from the build artifacts folder into the shared dynamic library path: +On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time. -``` -cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug -cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvaloniaNative.dylib +```bash +./build.sh CompileNative ``` ### Build and Run Avalonia diff --git a/NuGet.Config b/NuGet.Config index 3abd236d420..7a1f28bea71 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,6 +4,6 @@ - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 721a0415f48..fbd85071931 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,13 +3,6 @@ jobs: pool: vmImage: 'ubuntu-16.04' steps: - - task: CmdLine@2 - displayName: 'Install CastXML' - inputs: - script: | - sudo apt-get update - sudo apt-get install castxml - - task: CmdLine@2 displayName: 'Install Nuke' inputs: @@ -31,8 +24,10 @@ jobs: condition: not(canceled()) - job: macOS + variables: + SolutionDir: '$(Build.SourcesDirectory)' pool: - vmImage: 'macOS-10.14' + vmImage: 'macOS-10.15' steps: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 3.1.401' @@ -46,14 +41,20 @@ jobs: curl -o ./mono.pkg https://download.mono-project.com/archive/5.18.0/macos-10-universal/MonoFramework-MDK-5.18.0.225.macos10.xamarin.universal.pkg sudo installer -verbose -pkg ./mono.pkg -target / + - task: CmdLine@2 + displayName: 'Generate avalonia-native' + inputs: + script: | + cd src/tools/MicroComGenerator; dotnet run -i ../../Avalonia.Native/avn.idl --cpp ../../../native/Avalonia.Native/inc/avalonia-native.h + - task: Xcode@5 inputs: actions: 'build' scheme: '' - sdk: 'macosx10.14' + sdk: 'macosx11.1' configuration: 'Release' xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace' - xcodeVersion: '10' # Options: 8, 9, default, specifyPath + xcodeVersion: '12' # Options: 8, 9, default, specifyPath args: '-derivedDataPath ./' - task: CmdLine@2 @@ -97,6 +98,8 @@ jobs: - job: Windows pool: vmImage: 'windows-2019' + variables: + SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 3.1.401' diff --git a/build.ps1 b/build.ps1 index 57e2f800758..985e8abcee9 100644 --- a/build.ps1 +++ b/build.ps1 @@ -43,7 +43,7 @@ if (Test-Path $DotNetGlobalFile) { } # If dotnet is installed locally, and expected version is not set or installation matches the expected version -if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and ` +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { $env:DOTNET_EXE = (Get-Command "dotnet").Path } @@ -53,7 +53,7 @@ else { # Download install script $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" - md -force $TempDirectory > $null + mkdir -force $TempDirectory > $null (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) # Install by channel or version @@ -62,6 +62,8 @@ else { } else { ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } + + $env:PATH="$DotNetDirectory;$env:PATH" } Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" diff --git a/build.sh b/build.sh index a40e00f815b..bd162fab9bd 100755 --- a/build.sh +++ b/build.sh @@ -47,7 +47,7 @@ if [ -f "$DOTNET_GLOBAL_FILE" ]; then fi # If dotnet is installed locally, and expected version is not set or installation matches the expected version -if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") ]]; then +if [[ -x "$(command -v dotnet)" && (-z ${DOTNET_VERSION+x} || $(dotnet --version) == "$DOTNET_VERSION") || "$SKIP_DOTNET_DOWNLOAD" == "1" ]]; then export DOTNET_EXE="$(command -v dotnet)" else DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" diff --git a/build/AndroidWorkarounds.props b/build/AndroidWorkarounds.props index 67947296b34..de86acc6dec 100644 --- a/build/AndroidWorkarounds.props +++ b/build/AndroidWorkarounds.props @@ -2,7 +2,7 @@ - + diff --git a/build/ApiDiff.props b/build/ApiDiff.props index da82fbcc513..666417addfd 100644 --- a/build/ApiDiff.props +++ b/build/ApiDiff.props @@ -1,12 +1,12 @@  - 0.10.0-preview3 + 0.10.0 $(PackageId) Avalonia - + diff --git a/build/Assets/Icon.png b/build/Assets/Icon.png new file mode 100644 index 00000000000..41a2a618fb0 Binary files /dev/null and b/build/Assets/Icon.png differ diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index d17eec01350..fff00041c3c 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -15,6 +15,7 @@ + diff --git a/build/EmbedXaml.props b/build/EmbedXaml.props index 7ce0366dea8..0bb8da4f47a 100644 --- a/build/EmbedXaml.props +++ b/build/EmbedXaml.props @@ -4,8 +4,9 @@ %(Filename) + Designer - \ No newline at end of file + diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index e636461ad99..13419eb173a 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,6 +1,6 @@  - - + + diff --git a/build/MicroCom.targets b/build/MicroCom.targets new file mode 100644 index 00000000000..49d2cdce726 --- /dev/null +++ b/build/MicroCom.targets @@ -0,0 +1,34 @@ + + + + + + false + all + true + + + + + + + + + + + + + + + + + + <_AvaloniaPatchComInterop>true + + + diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props index d8e86e917e1..c3b136d41df 100644 --- a/build/ReactiveUI.props +++ b/build/ReactiveUI.props @@ -1,5 +1,5 @@ - + diff --git a/build/Rx.props b/build/Rx.props index 8a15ccd6a9a..fde1f80ea1d 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,5 +1,5 @@  - + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index d3cebef4183..75bada4bfc9 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -3,17 +3,24 @@ Avalonia 0.10.999 - Copyright 2020 © The AvaloniaUI Project + Copyright 2021 © The AvaloniaUI Project https://avaloniaui.net https://github.com/AvaloniaUI/Avalonia/ true CS1591 latest MIT - https://avatars2.githubusercontent.com/u/14075148?s=200 + Icon.png Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS. avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin https://github.com/AvaloniaUI/Avalonia/releases git + $(MSBuildThisFileDirectory)\avalonia.snk + true + $(DefineConstants);SIGNED_BUILD + + + + diff --git a/build/SourceLink.props b/build/SourceLink.props index 0c9b6a34f82..1e007e01eb7 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -1,5 +1,26 @@ + + true + false + true + embedded + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + true + + + + true + + - + - \ No newline at end of file + + + + + + diff --git a/build/XUnit.props b/build/XUnit.props index 079565d184d..a75e1bac863 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -11,4 +11,8 @@ + + $(MSBuildThisFileDirectory)\avalonia.snk + False + diff --git a/build/avalonia.snk b/build/avalonia.snk new file mode 100644 index 00000000000..10b49deb317 Binary files /dev/null and b/build/avalonia.snk differ diff --git a/dirs.proj b/dirs.proj index bf32abef723..594f2c22d3f 100644 --- a/dirs.proj +++ b/dirs.proj @@ -21,6 +21,7 @@ + diff --git a/native/Avalonia.Native/inc/.gitignore b/native/Avalonia.Native/inc/.gitignore new file mode 100644 index 00000000000..e7aa7fc6a50 --- /dev/null +++ b/native/Avalonia.Native/inc/.gitignore @@ -0,0 +1 @@ +avalonia-native.h diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h deleted file mode 100644 index 9ff6130e5fa..00000000000 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ /dev/null @@ -1,516 +0,0 @@ -#include "com.h" -#include "key.h" -#include "stddef.h" - -#define AVNCOM(name, id) COMINTERFACE(name, 2e2cda0a, 9ae5, 4f1b, 8e, 20, 08, 1a, 04, 27, 9f, id) - -struct IAvnWindowEvents; -struct IAvnWindow; -struct IAvnPopup; -struct IAvnMacOptions; -struct IAvnPlatformThreadingInterface; -struct IAvnSystemDialogEvents; -struct IAvnSystemDialogs; -struct IAvnScreens; -struct IAvnClipboard; -struct IAvnCursor; -struct IAvnCursorFactory; -struct IAvnGlFeature; -struct IAvnGlContext; -struct IAvnGlDisplay; -struct IAvnGlSurfaceRenderTarget; -struct IAvnGlSurfaceRenderingSession; -struct IAvnMenu; -struct IAvnMenuItem; -struct IAvnStringArray; -struct IAvnDndResultCallback; -struct IAvnGCHandleDeallocatorCallback; -struct IAvnMenuEvents; -struct IAvnNativeControlHost; -struct IAvnNativeControlHostTopLevelAttachment; -enum SystemDecorations { - SystemDecorationsNone = 0, - SystemDecorationsBorderOnly = 1, - SystemDecorationsFull = 2, -}; - -struct AvnSize -{ - double Width, Height; -}; - -struct AvnPixelSize -{ - int Width, Height; -}; - -struct AvnRect -{ - double X, Y, Width, Height; -}; - -struct AvnVector -{ - double X, Y; -}; - -struct AvnPoint -{ - double X, Y; -}; - -struct AvnScreen -{ - AvnRect Bounds; - AvnRect WorkingArea; - float PixelDensity; - bool Primary; -}; - -enum AvnPixelFormat -{ - kAvnRgb565, - kAvnRgba8888, - kAvnBgra8888 -}; - -struct AvnFramebuffer -{ - void* Data; - int Width; - int Height; - int Stride; - AvnVector Dpi; - AvnPixelFormat PixelFormat; -}; - -struct AvnColor -{ - unsigned char Alpha; - unsigned char Red; - unsigned char Green; - unsigned char Blue; -}; - -enum AvnRawMouseEventType -{ - LeaveWindow, - LeftButtonDown, - LeftButtonUp, - RightButtonDown, - RightButtonUp, - MiddleButtonDown, - MiddleButtonUp, - XButton1Down, - XButton1Up, - XButton2Down, - XButton2Up, - Move, - Wheel, - NonClientLeftButtonDown, - TouchBegin, - TouchUpdate, - TouchEnd, - TouchCancel -}; - -enum AvnRawKeyEventType -{ - KeyDown, - KeyUp -}; - -enum AvnInputModifiers -{ - AvnInputModifiersNone = 0, - Alt = 1, - Control = 2, - Shift = 4, - Windows = 8, - LeftMouseButton = 16, - RightMouseButton = 32, - MiddleMouseButton = 64, - XButton1MouseButton = 128, - XButton2MouseButton = 256 -}; - -enum class AvnDragDropEffects -{ - None = 0, - Copy = 1, - Move = 2, - Link = 4, -}; - -enum class AvnDragEventType -{ - Enter, - Over, - Leave, - Drop -}; - -enum AvnWindowState -{ - Normal, - Minimized, - Maximized, - FullScreen, -}; - -enum AvnStandardCursorType -{ - CursorArrow, - CursorIbeam, - CursorWait, - CursorCross, - CursorUpArrow, - CursorSizeWestEast, - CursorSizeNorthSouth, - CursorSizeAll, - CursorNo, - CursorHand, - CursorAppStarting, - CursorHelp, - CursorTopSide, - CursorBottomSize, - CursorLeftSide, - CursorRightSide, - CursorTopLeftCorner, - CursorTopRightCorner, - CursorBottomLeftCorner, - CursorBottomRightCorner, - CursorDragMove, - CursorDragCopy, - CursorDragLink, - CursorNone -}; - -enum AvnWindowEdge -{ - WindowEdgeNorthWest, - WindowEdgeNorth, - WindowEdgeNorthEast, - WindowEdgeWest, - WindowEdgeEast, - WindowEdgeSouthWest, - WindowEdgeSouth, - WindowEdgeSouthEast -}; - -enum AvnMenuItemToggleType -{ - None, - CheckMark, - Radio -}; - -enum AvnExtendClientAreaChromeHints -{ - AvnNoChrome = 0, - AvnSystemChrome = 0x01, - AvnPreferSystemChrome = 0x02, - AvnOSXThickTitleBar = 0x08, - AvnDefaultChrome = AvnSystemChrome, -}; - -AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown -{ -public: - 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; - virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0; - 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; - virtual HRESULT CreateMenu (IAvnMenuEvents* cb, IAvnMenu** ppv) = 0; - virtual HRESULT CreateMenuItem (IAvnMenuItem** ppv) = 0; - virtual HRESULT CreateMenuItemSeperator (IAvnMenuItem** ppv) = 0; -}; - -AVNCOM(IAvnString, 17) : IUnknown -{ - virtual HRESULT Pointer(void**retOut) = 0; - virtual HRESULT Length(int*ret) = 0; -}; - -AVNCOM(IAvnWindowBase, 02) : IUnknown -{ - virtual HRESULT Show() = 0; - virtual HRESULT Hide () = 0; - virtual HRESULT Close() = 0; - virtual HRESULT Activate () = 0; - virtual HRESULT GetClientSize(AvnSize*ret) = 0; - virtual HRESULT GetScaling(double*ret)=0; - virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) = 0; - virtual HRESULT Resize(double width, double height) = 0; - virtual HRESULT Invalidate (AvnRect rect) = 0; - virtual HRESULT BeginMoveDrag () = 0; - virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) = 0; - virtual HRESULT GetPosition (AvnPoint*ret) = 0; - virtual HRESULT SetPosition (AvnPoint point) = 0; - virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0; - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0; - virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0; - virtual HRESULT SetTopMost (bool value) = 0; - virtual HRESULT SetCursor(IAvnCursor* cursor) = 0; - virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0; - virtual HRESULT SetMainMenu(IAvnMenu* menu) = 0; - virtual HRESULT ObtainNSWindowHandle(void** retOut) = 0; - virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0; - virtual HRESULT ObtainNSViewHandle(void** retOut) = 0; - virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0; - virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) = 0; - virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, - IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0; - virtual HRESULT SetBlurEnabled (bool enable) = 0; -}; - -AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase -{ - -}; - -AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase -{ - virtual HRESULT SetEnabled (bool enable) = 0; - virtual HRESULT SetParent (IAvnWindow* parent) = 0; - virtual HRESULT SetCanResize(bool value) = 0; - virtual HRESULT SetDecorations(SystemDecorations value) = 0; - virtual HRESULT SetTitle (void* utf8Title) = 0; - virtual HRESULT SetTitleBarColor (AvnColor color) = 0; - virtual HRESULT SetWindowState(AvnWindowState state) = 0; - virtual HRESULT GetWindowState(AvnWindowState*ret) = 0; - virtual HRESULT TakeFocusFromChildren() = 0; - virtual HRESULT SetExtendClientArea (bool enable) = 0; - virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) = 0; - virtual HRESULT GetExtendTitleBarHeight (double*ret) = 0; - virtual HRESULT SetExtendTitleBarHeight (double value) = 0; -}; - -AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown -{ - virtual HRESULT Paint() = 0; - virtual void Closed() = 0; - virtual void Activated() = 0; - virtual void Deactivated() = 0; - virtual void Resized(const AvnSize& size) = 0; - virtual void PositionChanged (AvnPoint position) = 0; - virtual void RawMouseEvent (AvnRawMouseEventType type, - unsigned int timeStamp, - AvnInputModifiers modifiers, - AvnPoint point, - AvnVector delta) = 0; - virtual bool RawKeyEvent (AvnRawKeyEventType type, unsigned int timeStamp, AvnInputModifiers modifiers, unsigned int key) = 0; - virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0; - virtual void ScalingChanged(double scaling) = 0; - virtual void RunRenderPriorityJobs() = 0; - virtual void LostFocus() = 0; - virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position, - AvnInputModifiers modifiers, AvnDragDropEffects effects, - IAvnClipboard* clipboard, void* dataObjectHandle) = 0; -}; - - -AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents -{ - /** - * Closing Event - * Called when the user presses the OS window close button. - * return true to allow the close, return false to prevent close. - */ - virtual bool Closing () = 0; - - virtual void WindowStateChanged (AvnWindowState state) = 0; - - virtual void GotInputWhenDisabled () = 0; -}; - -AVNCOM(IAvnMacOptions, 07) : IUnknown -{ - virtual HRESULT SetShowInDock(int show) = 0; - virtual HRESULT SetApplicationTitle (void* utf8string) = 0; -}; - -AVNCOM(IAvnActionCallback, 08) : IUnknown -{ - virtual void Run() = 0; -}; - -AVNCOM(IAvnSignaledCallback, 09) : IUnknown -{ - virtual void Signaled(int priority, bool priorityContainsMeaningfulValue) = 0; -}; - -AVNCOM(IAvnLoopCancellation, 0a) : IUnknown -{ - virtual void Cancel() = 0; -}; - -AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown -{ - virtual bool GetCurrentThreadIsLoopThread() = 0; - virtual void SetSignaledCallback(IAvnSignaledCallback* cb) = 0; - virtual IAvnLoopCancellation* CreateLoopCancellation() = 0; - virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) = 0; - // Can't pass int* to sharpgentools for some reason - virtual void Signal(int priority) = 0; - virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0; -}; - -AVNCOM(IAvnSystemDialogEvents, 0c) : IUnknown -{ - virtual void OnCompleted (int numResults, void* ptrFirstResult) = 0; -}; - -AVNCOM(IAvnSystemDialogs, 0d) : IUnknown -{ - virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle, - IAvnSystemDialogEvents* events, - const char* title, - const char* initialPath) = 0; - - virtual void OpenFileDialog (IAvnWindow* parentWindowHandle, - IAvnSystemDialogEvents* events, - bool allowMultiple, - const char* title, - const char* initialDirectory, - const char* initialFile, - const char* filters) = 0; - - virtual void SaveFileDialog (IAvnWindow* parentWindowHandle, - IAvnSystemDialogEvents* events, - const char* title, - const char* initialDirectory, - const char* initialFile, - const char* filters) = 0; -}; - -AVNCOM(IAvnScreens, 0e) : IUnknown -{ - virtual HRESULT GetScreenCount (int* ret) = 0; - virtual HRESULT GetScreen (int index, AvnScreen* ret) = 0; -}; - -AVNCOM(IAvnClipboard, 0f) : IUnknown -{ - 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 SetBytes(char* type, void* utf8Text, int len) = 0; - virtual HRESULT GetBytes(char* type, IAvnString**ppv) = 0; - - virtual HRESULT Clear() = 0; -}; - -AVNCOM(IAvnCursor, 10) : IUnknown -{ -}; - -AVNCOM(IAvnCursorFactory, 11) : IUnknown -{ - virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) = 0; -}; - -AVNCOM(IAvnGlDisplay, 13) : IUnknown -{ - virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) = 0; - virtual void LegacyClearCurrentContext() = 0; - virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) = 0; - virtual void* GetProcAddress(char* proc) = 0; -}; - -AVNCOM(IAvnGlContext, 14) : IUnknown -{ - virtual HRESULT MakeCurrent(IUnknown** ppv) = 0; - virtual HRESULT LegacyMakeCurrent() = 0; - virtual int GetSampleCount() = 0; - virtual int GetStencilSize() = 0; - virtual void* GetNativeHandle() = 0; -}; - -AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown -{ - virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) = 0; -}; - -AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown -{ - virtual HRESULT GetPixelSize(AvnPixelSize* ret) = 0; - virtual HRESULT GetScaling(double* ret) = 0; -}; - -AVNCOM(IAvnMenu, 17) : IUnknown -{ - virtual HRESULT InsertItem (int index, IAvnMenuItem* item) = 0; - virtual HRESULT RemoveItem (IAvnMenuItem* item) = 0; - virtual HRESULT SetTitle (void* utf8String) = 0; - virtual HRESULT Clear () = 0; -}; - -AVNCOM(IAvnPredicateCallback, 18) : IUnknown -{ - virtual bool Evaluate() = 0; -}; - -AVNCOM(IAvnMenuItem, 19) : IUnknown -{ - virtual HRESULT SetSubMenu (IAvnMenu* menu) = 0; - virtual HRESULT SetTitle (void* utf8String) = 0; - virtual HRESULT SetGesture (void* utf8String, AvnInputModifiers modifiers) = 0; - virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) = 0; - virtual HRESULT SetIsChecked (bool isChecked) = 0; - virtual HRESULT SetToggleType (AvnMenuItemToggleType toggleType) = 0; - virtual HRESULT SetIcon (void* data, size_t length) = 0; -}; - -AVNCOM(IAvnMenuEvents, 1A) : IUnknown -{ - /** - * NeedsUpdate - */ - 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; -}; - -AVNCOM(IAvnNativeControlHost, 20) : IUnknown -{ - virtual HRESULT CreateDefaultChild(void* parent, void** retOut) = 0; - virtual IAvnNativeControlHostTopLevelAttachment* CreateAttachment() = 0; - virtual void DestroyDefaultChild(void* child) = 0; -}; - -AVNCOM(IAvnNativeControlHostTopLevelAttachment, 21) : IUnknown -{ - virtual void* GetParentHandle() = 0; - virtual HRESULT InitializeWithChildHandle(void* child) = 0; - virtual HRESULT AttachTo(IAvnNativeControlHost* host) = 0; - virtual void ShowInBounds(float x, float y, float width, float height) = 0; - virtual void HideWithSize(float width, float height) = 0; - virtual void ReleaseChild() = 0; -}; - - -extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative(); diff --git a/native/Avalonia.Native/inc/comimpl.h b/native/Avalonia.Native/inc/comimpl.h index 0ff64b72158..47b0a3c5f27 100644 --- a/native/Avalonia.Native/inc/comimpl.h +++ b/native/Avalonia.Native/inc/comimpl.h @@ -8,8 +8,109 @@ #include +/** + START_COM_CALL causes AddRef to be called at the beggining of a function. + When a function is exited, it causes ReleaseRef to be called. + This ensures that the object cannot be destroyed whilst the function is running. + For example: Window Show is called, which triggers an event, and user calls Close inside the event + causing the refcount to reach 0, and the object to be destroyed. Function then continues and this pointer + will now be invalid. + + START_COM_CALL protects against this scenario. + */ +#define START_COM_CALL auto r = this->UnknownSelf() + __IID_DEF(IUnknown, 0, 0, 0, C0, 00, 00, 00, 00, 00, 00, 46); +template +class ComPtr +{ +private: + TInterface* _obj; +public: + ComPtr() + { + _obj = 0; + } + + ComPtr(TInterface* pObj) + { + _obj = 0; + + if (pObj) + { + _obj = pObj; + _obj->AddRef(); + } + } + + ComPtr(const ComPtr& ptr) + { + _obj = 0; + + if (ptr._obj) + { + _obj = ptr._obj; + _obj->AddRef(); + } + + } + + ComPtr& operator=(ComPtr other) + { + if(_obj != NULL) + _obj->Release(); + _obj = other._obj; + if(_obj != NULL) + _obj->AddRef(); + return *this; + } + + ~ComPtr() + { + if (_obj) + { + _obj->Release(); + _obj = 0; + } + } + + TInterface* getRaw() + { + return _obj; + } + + TInterface* getRetainedReference() + { + if(_obj == NULL) + return NULL; + _obj->AddRef(); + return _obj; + } + + TInterface** getPPV() + { + return &_obj; + } + + operator TInterface*() const + { + return _obj; + } + TInterface& operator*() const + { + return *_obj; + } + TInterface** operator&() + { + return &_obj; + } + TInterface* operator->() const + { + return _obj; + } +}; + class ComObject : public virtual IUnknown { private: @@ -58,6 +159,12 @@ class ComObject : public virtual IUnknown _refCount++; return S_OK; } + +protected: + ComPtr UnknownSelf() + { + return this; + } }; @@ -104,94 +211,5 @@ template class ComSingleObject : public ComO virtual ~ComSingleObject(){} }; -template -class ComPtr -{ -private: - TInterface* _obj; -public: - ComPtr() - { - _obj = 0; - } - - ComPtr(TInterface* pObj) - { - _obj = 0; - - if (pObj) - { - _obj = pObj; - _obj->AddRef(); - } - } - - ComPtr(const ComPtr& ptr) - { - _obj = 0; - - if (ptr._obj) - { - _obj = ptr._obj; - _obj->AddRef(); - } - - } - - ComPtr& operator=(ComPtr other) - { - if(_obj != NULL) - _obj->Release(); - _obj = other._obj; - if(_obj != NULL) - _obj->AddRef(); - return *this; - } - - ~ComPtr() - { - if (_obj) - { - _obj->Release(); - _obj = 0; - } - } - - TInterface* getRaw() - { - return _obj; - } - - TInterface* getRetainedReference() - { - if(_obj == NULL) - return NULL; - _obj->AddRef(); - return _obj; - } - - TInterface** getPPV() - { - return &_obj; - } - - operator TInterface*() const - { - return _obj; - } - TInterface& operator*() const - { - return *_obj; - } - TInterface** operator&() - { - return &_obj; - } - TInterface* operator->() const - { - return _obj; - } -}; - #endif // COMIMPL_H_INCLUDED #pragma clang diagnostic pop diff --git a/native/Avalonia.Native/inc/key.h b/native/Avalonia.Native/inc/key.h deleted file mode 100644 index 12d283cc171..00000000000 --- a/native/Avalonia.Native/inc/key.h +++ /dev/null @@ -1,1020 +0,0 @@ -#ifndef _KEY_H_ -#define _KEY_H_ - -/// -/// Defines the keys available on a keyboard. -/// -enum AvnKey -{ - /// - /// No key pressed. - /// - AvnKeyNone = 0, - - /// - /// The Cancel key. - /// - AvnKeyCancel = 1, - - /// - /// The Back key. - /// - AvnKeyBack = 2, - - /// - /// The Tab key. - /// - AvnKeyTab = 3, - - /// - /// The Linefeed key. - /// - AvnKeyLineFeed = 4, - - /// - /// The Clear key. - /// - AvnKeyClear = 5, - - /// - /// The Return key. - /// - AvnKeyReturn = 6, - - /// - /// The Enter key. - /// - AvnKeyEnter = 6, - - /// - /// The Pause key. - /// - AvnKeyPause = 7, - - /// - /// The Caps Lock key. - /// - AvnKeyCapsLock = 8, - - /// - /// The Caps Lock key. - /// - AvnKeyCapital = 8, - - /// - /// The IME Hangul mode key. - /// - AvnKeyHangulMode = 9, - - /// - /// The IME Kana mode key. - /// - AvnKeyKanaMode = 9, - - /// - /// The IME Junja mode key. - /// - AvnKeyJunjaMode = 10, - - /// - /// The IME Final mode key. - /// - AvnKeyFinalMode = 11, - - /// - /// The IME Kanji mode key. - /// - AvnKeyKanjiMode = 12, - - /// - /// The IME Hanja mode key. - /// - HanjaMode = 12, - - /// - /// The Escape key. - /// - Escape = 13, - - /// - /// The IME Convert key. - /// - ImeConvert = 14, - - /// - /// The IME NonConvert key. - /// - ImeNonConvert = 15, - - /// - /// The IME Accept key. - /// - ImeAccept = 16, - - /// - /// The IME Mode change key. - /// - ImeModeChange = 17, - - /// - /// The space bar. - /// - Space = 18, - - /// - /// The Page Up key. - /// - PageUp = 19, - - /// - /// The Page Up key. - /// - Prior = 19, - - /// - /// The Page Down key. - /// - PageDown = 20, - - /// - /// The Page Down key. - /// - Next = 20, - - /// - /// The End key. - /// - End = 21, - - /// - /// The Home key. - /// - Home = 22, - - /// - /// The Left arrow key. - /// - Left = 23, - - /// - /// The Up arrow key. - /// - Up = 24, - - /// - /// The Right arrow key. - /// - Right = 25, - - /// - /// The Down arrow key. - /// - Down = 26, - - /// - /// The Select key. - /// - Select = 27, - - /// - /// The Print key. - /// - Print = 28, - - /// - /// The Execute key. - /// - Execute = 29, - - /// - /// The Print Screen key. - /// - Snapshot = 30, - - /// - /// The Print Screen key. - /// - PrintScreen = 30, - - /// - /// The Insert key. - /// - Insert = 31, - - /// - /// The Delete key. - /// - Delete = 32, - - /// - /// The Help key. - /// - Help = 33, - - /// - /// The 0 key. - /// - D0 = 34, - - /// - /// The 1 key. - /// - D1 = 35, - - /// - /// The 2 key. - /// - D2 = 36, - - /// - /// The 3 key. - /// - D3 = 37, - - /// - /// The 4 key. - /// - D4 = 38, - - /// - /// The 5 key. - /// - D5 = 39, - - /// - /// The 6 key. - /// - D6 = 40, - - /// - /// The 7 key. - /// - D7 = 41, - - /// - /// The 8 key. - /// - D8 = 42, - - /// - /// The 9 key. - /// - D9 = 43, - - /// - /// The A key. - /// - A = 44, - - /// - /// The B key. - /// - B = 45, - - /// - /// The C key. - /// - C = 46, - - /// - /// The D key. - /// - D = 47, - - /// - /// The E key. - /// - E = 48, - - /// - /// The F key. - /// - F = 49, - - /// - /// The G key. - /// - G = 50, - - /// - /// The H key. - /// - H = 51, - - /// - /// The I key. - /// - I = 52, - - /// - /// The J key. - /// - J = 53, - - /// - /// The K key. - /// - AvnKeyK = 54, - - /// - /// The L key. - /// - L = 55, - - /// - /// The M key. - /// - M = 56, - - /// - /// The N key. - /// - N = 57, - - /// - /// The O key. - /// - O = 58, - - /// - /// The P key. - /// - P = 59, - - /// - /// The Q key. - /// - Q = 60, - - /// - /// The R key. - /// - R = 61, - - /// - /// The S key. - /// - S = 62, - - /// - /// The T key. - /// - T = 63, - - /// - /// The U key. - /// - U = 64, - - /// - /// The V key. - /// - V = 65, - - /// - /// The W key. - /// - W = 66, - - /// - /// The X key. - /// - X = 67, - - /// - /// The Y key. - /// - Y = 68, - - /// - /// The Z key. - /// - Z = 69, - - /// - /// The left Windows key. - /// - LWin = 70, - - /// - /// The right Windows key. - /// - RWin = 71, - - /// - /// The Application key. - /// - Apps = 72, - - /// - /// The Sleep key. - /// - Sleep = 73, - - /// - /// The 0 key on the numeric keypad. - /// - NumPad0 = 74, - - /// - /// The 1 key on the numeric keypad. - /// - NumPad1 = 75, - - /// - /// The 2 key on the numeric keypad. - /// - NumPad2 = 76, - - /// - /// The 3 key on the numeric keypad. - /// - NumPad3 = 77, - - /// - /// The 4 key on the numeric keypad. - /// - NumPad4 = 78, - - /// - /// The 5 key on the numeric keypad. - /// - NumPad5 = 79, - - /// - /// The 6 key on the numeric keypad. - /// - NumPad6 = 80, - - /// - /// The 7 key on the numeric keypad. - /// - NumPad7 = 81, - - /// - /// The 8 key on the numeric keypad. - /// - NumPad8 = 82, - - /// - /// The 9 key on the numeric keypad. - /// - NumPad9 = 83, - - /// - /// The Multiply key. - /// - Multiply = 84, - - /// - /// The Add key. - /// - Add = 85, - - /// - /// The Separator key. - /// - Separator = 86, - - /// - /// The Subtract key. - /// - Subtract = 87, - - /// - /// The Decimal key. - /// - Decimal = 88, - - /// - /// The Divide key. - /// - Divide = 89, - - /// - /// The F1 key. - /// - F1 = 90, - - /// - /// The F2 key. - /// - F2 = 91, - - /// - /// The F3 key. - /// - F3 = 92, - - /// - /// The F4 key. - /// - F4 = 93, - - /// - /// The F5 key. - /// - F5 = 94, - - /// - /// The F6 key. - /// - F6 = 95, - - /// - /// The F7 key. - /// - F7 = 96, - - /// - /// The F8 key. - /// - F8 = 97, - - /// - /// The F9 key. - /// - F9 = 98, - - /// - /// The F10 key. - /// - F10 = 99, - - /// - /// The F11 key. - /// - F11 = 100, - - /// - /// The F12 key. - /// - F12 = 101, - - /// - /// The F13 key. - /// - F13 = 102, - - /// - /// The F14 key. - /// - F14 = 103, - - /// - /// The F15 key. - /// - F15 = 104, - - /// - /// The F16 key. - /// - F16 = 105, - - /// - /// The F17 key. - /// - F17 = 106, - - /// - /// The F18 key. - /// - F18 = 107, - - /// - /// The F19 key. - /// - F19 = 108, - - /// - /// The F20 key. - /// - F20 = 109, - - /// - /// The F21 key. - /// - F21 = 110, - - /// - /// The F22 key. - /// - F22 = 111, - - /// - /// The F23 key. - /// - F23 = 112, - - /// - /// The F24 key. - /// - F24 = 113, - - /// - /// The Numlock key. - /// - NumLock = 114, - - /// - /// The Scroll key. - /// - Scroll = 115, - - /// - /// The left Shift key. - /// - LeftShift = 116, - - /// - /// The right Shift key. - /// - RightShift = 117, - - /// - /// The left Ctrl key. - /// - LeftCtrl = 118, - - /// - /// The right Ctrl key. - /// - RightCtrl = 119, - - /// - /// The left Alt key. - /// - LeftAlt = 120, - - /// - /// The right Alt key. - /// - RightAlt = 121, - - /// - /// The browser Back key. - /// - BrowserBack = 122, - - /// - /// The browser Forward key. - /// - BrowserForward = 123, - - /// - /// The browser Refresh key. - /// - BrowserRefresh = 124, - - /// - /// The browser Stop key. - /// - BrowserStop = 125, - - /// - /// The browser Search key. - /// - BrowserSearch = 126, - - /// - /// The browser Favorites key. - /// - BrowserFavorites = 127, - - /// - /// The browser Home key. - /// - BrowserHome = 128, - - /// - /// The Volume Mute key. - /// - VolumeMute = 129, - - /// - /// The Volume Down key. - /// - VolumeDown = 130, - - /// - /// The Volume Up key. - /// - VolumeUp = 131, - - /// - /// The media Next Track key. - /// - MediaNextTrack = 132, - - /// - /// The media Previous Track key. - /// - MediaPreviousTrack = 133, - - /// - /// The media Stop key. - /// - MediaStop = 134, - - /// - /// The media Play/Pause key. - /// - MediaPlayPause = 135, - - /// - /// The Launch Mail key. - /// - LaunchMail = 136, - - /// - /// The Select Media key. - /// - SelectMedia = 137, - - /// - /// The Launch Application 1 key. - /// - LaunchApplication1 = 138, - - /// - /// The Launch Application 2 key. - /// - LaunchApplication2 = 139, - - /// - /// The OEM Semicolon key. - /// - OemSemicolon = 140, - - /// - /// The OEM 1 key. - /// - Oem1 = 140, - - /// - /// The OEM Plus key. - /// - OemPlus = 141, - - /// - /// The OEM Comma key. - /// - OemComma = 142, - - /// - /// The OEM Minus key. - /// - OemMinus = 143, - - /// - /// The OEM Period key. - /// - OemPeriod = 144, - - /// - /// The OEM Question Mark key. - /// - OemQuestion = 145, - - /// - /// The OEM 2 key. - /// - Oem2 = 145, - - /// - /// The OEM Tilde key. - /// - OemTilde = 146, - - /// - /// The OEM 3 key. - /// - Oem3 = 146, - - /// - /// The ABNT_C1 (Brazilian) key. - /// - AbntC1 = 147, - - /// - /// The ABNT_C2 (Brazilian) key. - /// - AbntC2 = 148, - - /// - /// The OEM Open Brackets key. - /// - OemOpenBrackets = 149, - - /// - /// The OEM 4 key. - /// - Oem4 = 149, - - /// - /// The OEM Pipe key. - /// - OemPipe = 150, - - /// - /// The OEM 5 key. - /// - Oem5 = 150, - - /// - /// The OEM Close Brackets key. - /// - OemCloseBrackets = 151, - - /// - /// The OEM 6 key. - /// - Oem6 = 151, - - /// - /// The OEM Quotes key. - /// - OemQuotes = 152, - - /// - /// The OEM 7 key. - /// - Oem7 = 152, - - /// - /// The OEM 8 key. - /// - Oem8 = 153, - - /// - /// The OEM Backslash key. - /// - OemBackslash = 154, - - /// - /// The OEM 3 key. - /// - Oem102 = 154, - - /// - /// A special key masking the real key being processed by an IME. - /// - ImeProcessed = 155, - - /// - /// A special key masking the real key being processed as a system key. - /// - System = 156, - - /// - /// The OEM ATTN key. - /// - OemAttn = 157, - - /// - /// The DBE_ALPHANUMERIC key. - /// - DbeAlphanumeric = 157, - - /// - /// The OEM Finish key. - /// - OemFinish = 158, - - /// - /// The DBE_KATAKANA key. - /// - DbeKatakana = 158, - - /// - /// The DBE_HIRAGANA key. - /// - DbeHiragana = 159, - - /// - /// The OEM Copy key. - /// - OemCopy = 159, - - /// - /// The DBE_SBCSCHAR key. - /// - DbeSbcsChar = 160, - - /// - /// The OEM Auto key. - /// - OemAuto = 160, - - /// - /// The DBE_DBCSCHAR key. - /// - DbeDbcsChar = 161, - - /// - /// The OEM ENLW key. - /// - OemEnlw = 161, - - /// - /// The OEM BackTab key. - /// - OemBackTab = 162, - - /// - /// The DBE_ROMAN key. - /// - DbeRoman = 162, - - /// - /// The DBE_NOROMAN key. - /// - DbeNoRoman = 163, - - /// - /// The ATTN key. - /// - Attn = 163, - - /// - /// The CRSEL key. - /// - CrSel = 164, - - /// - /// The DBE_ENTERWORDREGISTERMODE key. - /// - DbeEnterWordRegisterMode = 164, - - /// - /// The EXSEL key. - /// - ExSel = 165, - - /// - /// The DBE_ENTERIMECONFIGMODE key. - /// - DbeEnterImeConfigureMode = 165, - - /// - /// The ERASE EOF Key. - /// - EraseEof = 166, - - /// - /// The DBE_FLUSHSTRING key. - /// - DbeFlushString = 166, - - /// - /// The Play key. - /// - Play = 167, - - /// - /// The DBE_CODEINPUT key. - /// - DbeCodeInput = 167, - - /// - /// The DBE_NOCODEINPUT key. - /// - DbeNoCodeInput = 168, - - /// - /// The Zoom key. - /// - Zoom = 168, - - /// - /// Reserved for future use. - /// - NoName = 169, - - /// - /// The DBE_DETERMINESTRING key. - /// - DbeDetermineString = 169, - - /// - /// The DBE_ENTERDLGCONVERSIONMODE key. - /// - DbeEnterDialogConversionMode = 170, - - /// - /// The PA1 key. - /// - Pa1 = 170, - - /// - /// The OEM Clear key. - /// - OemClear = 171, - - /// - /// The key is used with another key to create a single combined character. - /// - DeadCharProcessed = 172, -}; - -#endif diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index d5cad4d1caf..dba3ee6d31d 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -8,19 +8,20 @@ /* Begin PBXBuildFile section */ 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; + 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; }; - 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; - 1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.mm */; }; 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 */; }; + 1AFD334123E03C4F0042899B /* controlhost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AFD334023E03C4F0042899B /* controlhost.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 */; }; 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; }; 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; }; 520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; }; + 522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 522D5958258159C1006F7F7A /* Carbon.framework */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; @@ -32,13 +33,13 @@ /* Begin PBXFileReference section */ 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; + 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; }; - 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; - 1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = ""; }; 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = ""; }; 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 = ""; }; + 1AFD334023E03C4F0042899B /* controlhost.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = controlhost.mm; sourceTree = ""; }; 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; @@ -49,6 +50,7 @@ 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; 520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = ""; }; + 522D5958258159C1006F7F7A /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; }; 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; }; @@ -69,6 +71,7 @@ 1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */, 1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */, AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */, + 522D5959258159C1006F7F7A /* Carbon.framework in Frameworks */, AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -79,6 +82,7 @@ AB661C1C2148230E00291242 /* Frameworks */ = { isa = PBXGroup; children = ( + 522D5958258159C1006F7F7A /* Carbon.framework */, 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */, 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */, AB1E522B217613570091CD71 /* OpenGL.framework */, diff --git a/native/Avalonia.Native/src/OSX/AvnString.h b/native/Avalonia.Native/src/OSX/AvnString.h index 5d299374e5c..3ce83d370a7 100644 --- a/native/Avalonia.Native/src/OSX/AvnString.h +++ b/native/Avalonia.Native/src/OSX/AvnString.h @@ -11,6 +11,7 @@ extern IAvnString* CreateAvnString(NSString* string); extern IAvnStringArray* CreateAvnStringArray(NSArray* array); +extern IAvnStringArray* CreateAvnStringArray(NSArray* array); extern IAvnStringArray* CreateAvnStringArray(NSString* string); extern IAvnString* CreateByteArray(void* data, int len); #endif /* AvnString_h */ diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm index 00b748ef63f..cd0e2cdf941 100644 --- a/native/Avalonia.Native/src/OSX/AvnString.mm +++ b/native/Avalonia.Native/src/OSX/AvnString.mm @@ -43,6 +43,8 @@ virtual HRESULT Pointer(void**retOut) override { + START_COM_CALL; + @autoreleasepool { if(retOut == nullptr) @@ -58,14 +60,19 @@ virtual HRESULT Pointer(void**retOut) override virtual HRESULT Length(int*retOut) override { - if(retOut == nullptr) + START_COM_CALL; + + @autoreleasepool { - return E_POINTER; + if(retOut == nullptr) + { + return E_POINTER; + } + + *retOut = _length; + + return S_OK; } - - *retOut = _length; - - return S_OK; } }; @@ -85,6 +92,16 @@ virtual HRESULT Length(int*retOut) override } } + AvnStringArrayImpl(NSArray* array) + { + for(int c = 0; c < [array count]; c++) + { + ComPtr s; + *s.getPPV() = new AvnStringImpl([array objectAtIndex:c].absoluteString); + _list.push_back(s); + } + } + AvnStringArrayImpl(NSString* string) { ComPtr s; @@ -99,10 +116,15 @@ virtual unsigned int GetCount() override virtual HRESULT Get(unsigned int index, IAvnString**ppv) override { - if(_list.size() <= index) - return E_INVALIDARG; - *ppv = _list[index].getRetainedReference(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + if(_list.size() <= index) + return E_INVALIDARG; + *ppv = _list[index].getRetainedReference(); + return S_OK; + } } }; @@ -117,6 +139,11 @@ virtual HRESULT Get(unsigned int index, IAvnString**ppv) override return new AvnStringArrayImpl(array); } +IAvnStringArray* CreateAvnStringArray(NSArray * array) +{ + return new AvnStringArrayImpl(array); +} + IAvnStringArray* CreateAvnStringArray(NSString* string) { return new AvnStringArrayImpl(string); diff --git a/native/Avalonia.Native/src/OSX/KeyTransform.h b/native/Avalonia.Native/src/OSX/KeyTransform.h index ea4fbecd5c6..2f434570c9e 100644 --- a/native/Avalonia.Native/src/OSX/KeyTransform.h +++ b/native/Avalonia.Native/src/OSX/KeyTransform.h @@ -1,9 +1,14 @@ #ifndef keytransform_h #define keytransform_h #include "common.h" -#include "key.h" #include extern std::map s_KeyMap; +extern std::map s_AvnKeyMap; + +extern std::map s_QwertyKeyMap; + +extern std::map s_UnicodeKeyMap; + #endif diff --git a/native/Avalonia.Native/src/OSX/KeyTransform.mm b/native/Avalonia.Native/src/OSX/KeyTransform.mm index ff1bf6b1aff..4817ad0ccf4 100644 --- a/native/Avalonia.Native/src/OSX/KeyTransform.mm +++ b/native/Avalonia.Native/src/OSX/KeyTransform.mm @@ -120,6 +120,139 @@ //const int kVK_JIS_Eisu = 0x66; const int kVK_JIS_Kana = 0x68; +// converts from AvaloniaKeys to UnicodeSpecial keys. +std::map s_UnicodeKeyMap = +{ + { Up, NSUpArrowFunctionKey }, + { Down, NSDownArrowFunctionKey }, + { Left, NSLeftArrowFunctionKey }, + { Right, NSRightArrowFunctionKey }, + { F1, NSF1FunctionKey }, + { F2, NSF2FunctionKey }, + { F3, NSF3FunctionKey }, + { F4, NSF4FunctionKey }, + { F5, NSF5FunctionKey }, + { F6, NSF6FunctionKey }, + { F7, NSF7FunctionKey }, + { F8, NSF8FunctionKey }, + { F9, NSF9FunctionKey }, + { F10, NSF10FunctionKey }, + { F11, NSF11FunctionKey }, + { F12, NSF12FunctionKey }, + { F13, NSF13FunctionKey }, + { F14, NSF14FunctionKey }, + { F15, NSF15FunctionKey }, + { F16, NSF16FunctionKey }, + { F17, NSF17FunctionKey }, + { F18, NSF18FunctionKey }, + { F19, NSF19FunctionKey }, + { F20, NSF20FunctionKey }, + { F21, NSF21FunctionKey }, + { F22, NSF22FunctionKey }, + { F23, NSF23FunctionKey }, + { F24, NSF24FunctionKey }, + { Insert, NSInsertFunctionKey }, + { Delete, NSDeleteFunctionKey }, + { Home, NSHomeFunctionKey }, + //{ Begin, NSBeginFunctionKey }, + { End, NSEndFunctionKey }, + { PageUp, NSPageUpFunctionKey }, + { PageDown, NSPageDownFunctionKey }, + { PrintScreen, NSPrintScreenFunctionKey }, + { Scroll, NSScrollLockFunctionKey }, + //{ SysReq, NSSysReqFunctionKey }, + //{ Break, NSBreakFunctionKey }, + //{ Reset, NSResetFunctionKey }, + //{ Stop, NSStopFunctionKey }, + //{ Menu, NSMenuFunctionKey }, + //{ UserFunction, NSUserFunctionKey }, + //{ SystemFunction, NSSystemFunctionKey }, + { Print, NSPrintFunctionKey }, + //{ ClearLine, NSClearLineFunctionKey }, + //{ ClearDisplay, NSClearDisplayFunctionKey }, +}; + +// Converts from Ansi virtual keys to Qwerty Keyboard map. +std::map s_QwertyKeyMap = +{ + { 0, "a" }, + { 1, "s" }, + { 2, "d" }, + { 3, "f" }, + { 4, "h" }, + { 5, "g" }, + { 6, "z" }, + { 7, "x" }, + { 8, "c" }, + { 9, "v" }, + { 10, "§" }, + { 11, "b" }, + { 12, "q" }, + { 13, "w" }, + { 14, "e" }, + { 15, "r" }, + { 16, "y" }, + { 17, "t" }, + { 18, "1" }, + { 19, "2" }, + { 20, "3" }, + { 21, "4" }, + { 22, "6" }, + { 23, "5" }, + { 24, "=" }, + { 25, "9" }, + { 26, "7" }, + { 27, "-" }, + { 28, "8" }, + { 29, "0" }, + { 30, "]" }, + { 31, "o" }, + { 32, "u" }, + { 33, "[" }, + { 34, "i" }, + { 35, "p" }, + { 37, "l" }, + { 38, "j" }, + { 39, "'" }, + { 40, "k" }, + { 41, ";" }, + { 42, "\\" }, + { 43, "," }, + { 44, "/" }, + { 45, "n" }, + { 46, "m" }, + { 47, "." }, + { 48, "\t" }, + { 49, " " }, + { 50, "`" }, + { 51, "" }, + { 52, "" }, + { 53, "" }, + { 65, "." }, + { 66, "" }, + { 67, "*" }, + { 69, "+" }, + { 70, "" }, + { 71, "" }, + { 72, "" }, + { 75, "/" }, + { 76, "" }, + { 77, "" }, + { 78, "-" }, + { 81, "=" }, + { 82, "0" }, + { 83, "1" }, + { 84, "2" }, + { 85, "3" }, + { 86, "4" }, + { 87, "5" }, + { 88, "6" }, + { 89, "7" }, + { 91, "8" }, + { 92, "9" } +}; + +// converts from ansi virtualkeys to AvnKeys. std::map s_KeyMap = { {kVK_ANSI_A, A}, @@ -237,3 +370,22 @@ {kVK_UpArrow, Up}, {kVK_JIS_Kana, AvnKeyKanaMode}, }; + +static std::map BuildAvnKeyMap () +{ + std::map result; + + for( auto it = s_KeyMap.begin(); it != s_KeyMap.end(); ++it ) + { + int key = it->first; + AvnKey value = it->second; + + result[value] = key; + } + + return result; +} + +// Converts AvnKeys to Ansi VirtualKeys +std::map s_AvnKeyMap = BuildAvnKeyMap(); + diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm index 455cfa2e415..b9c75ed7427 100644 --- a/native/Avalonia.Native/src/OSX/Screens.mm +++ b/native/Avalonia.Native/src/OSX/Screens.mm @@ -5,15 +5,11 @@ public: FORWARD_IUNKNOWN() - private: - CGFloat PrimaryDisplayHeight() - { - return NSMaxY([[[NSScreen screens] firstObject] frame]); - } - public: virtual HRESULT GetScreenCount (int* ret) override { + START_COM_CALL; + @autoreleasepool { *ret = (int)[NSScreen screens].count; @@ -24,6 +20,8 @@ virtual HRESULT GetScreenCount (int* ret) override virtual HRESULT GetScreen (int index, AvnScreen* ret) override { + START_COM_CALL; + @autoreleasepool { if(index < 0 || index >= [NSScreen screens].count) @@ -36,12 +34,12 @@ virtual HRESULT GetScreen (int index, AvnScreen* ret) override ret->Bounds.Height = [screen frame].size.height; ret->Bounds.Width = [screen frame].size.width; ret->Bounds.X = [screen frame].origin.x; - ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height; + ret->Bounds.Y = ConvertPointY(ToAvnPoint([screen frame].origin)).Y - ret->Bounds.Height; ret->WorkingArea.Height = [screen visibleFrame].size.height; ret->WorkingArea.Width = [screen visibleFrame].size.width; ret->WorkingArea.X = [screen visibleFrame].origin.x; - ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height; + ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height; ret->PixelDensity = [screen backingScaleFactor]; diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 814b91cb629..e1972b22f4d 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -1,10 +1,20 @@ #include "common.h" +#include "AvnString.h" @interface AvnAppDelegate : NSObject +-(AvnAppDelegate* _Nonnull) initWithEvents: (IAvnApplicationEvents* _Nonnull) events; @end NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; @implementation AvnAppDelegate +ComPtr _events; + +- (AvnAppDelegate *)initWithEvents:(IAvnApplicationEvents *)events +{ + _events = events; + return self; +} + - (void)applicationWillFinishLaunching:(NSNotification *)notification { if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy) @@ -27,11 +37,29 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; } +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames +{ + auto array = CreateAvnStringArray(filenames); + + _events->FilesOpened(array); +} + +- (void)application:(NSApplication *)application openURLs:(NSArray *)urls +{ + auto array = CreateAvnStringArray(urls); + + _events->FilesOpened(array); +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + return _events->TryShutdown() ? NSTerminateNow : NSTerminateCancel; +} + @end @interface AvnApplication : NSApplication - @end @implementation AvnApplication @@ -63,9 +91,9 @@ - (void)setHandlingSendEvent:(BOOL)handlingSendEvent @end -extern void InitializeAvnApp() +extern void InitializeAvnApp(IAvnApplicationEvents* events) { NSApplication* app = [AvnApplication sharedApplication]; - id delegate = [AvnAppDelegate new]; + id delegate = [[AvnAppDelegate alloc] initWithEvents:events]; [app setDelegate:delegate]; } diff --git a/native/Avalonia.Native/src/OSX/cgl.mm b/native/Avalonia.Native/src/OSX/cgl.mm index a9d94cdf04d..085037978eb 100644 --- a/native/Avalonia.Native/src/OSX/cgl.mm +++ b/native/Avalonia.Native/src/OSX/cgl.mm @@ -69,6 +69,8 @@ static CGLContextObj CreateCglContext(CGLContextObj share) virtual HRESULT LegacyMakeCurrent() override { + START_COM_CALL; + if(CGLSetCurrentContext(Context) != 0) return E_FAIL; return S_OK; @@ -76,6 +78,8 @@ virtual HRESULT LegacyMakeCurrent() override virtual HRESULT MakeCurrent(IUnknown** ppv) override { + START_COM_CALL; + CGLContextObj saved = CGLGetCurrentContext(); CGLLockContext(Context); if(CGLSetCurrentContext(Context) != 0) @@ -128,6 +132,8 @@ virtual int GetStencilSize() override virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) override { + START_COM_CALL; + CGLContextObj shareContext = nil; if(share != nil) { @@ -144,6 +150,8 @@ virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) override virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) override { + START_COM_CALL; + if(native == nil) return E_INVALIDARG; *ppv = new AvnGlContext((CGLContextObj) native); diff --git a/native/Avalonia.Native/src/OSX/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm index 116a08670e7..9966971b735 100644 --- a/native/Avalonia.Native/src/OSX/clipboard.mm +++ b/native/Avalonia.Native/src/OSX/clipboard.mm @@ -25,6 +25,8 @@ virtual HRESULT GetText (char* type, IAvnString**ppv) override { + START_COM_CALL; + @autoreleasepool { if(ppv == nullptr) @@ -42,6 +44,8 @@ virtual HRESULT GetText (char* type, IAvnString**ppv) override virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override { + START_COM_CALL; + @autoreleasepool { *ppv= nil; @@ -56,7 +60,7 @@ virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override return S_OK; } - NSArray* arr = (NSArray*)data; + NSArray* arr = (NSArray*)data; for(int c = 0; c < [arr count]; c++) if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]]) @@ -67,58 +71,73 @@ virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override } } - virtual HRESULT SetText (char* type, void* utf8String) override + virtual HRESULT SetText (char* type, char* utf8String) override { - Clear(); + START_COM_CALL; + @autoreleasepool { + Clear(); + 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; + return S_OK; + } } virtual HRESULT SetBytes(char* type, void* bytes, int len) override { - auto typeString = [NSString stringWithUTF8String:(const char*)type]; - auto data = [NSData dataWithBytes:bytes length:len]; - if(_item == nil) - [_pb setData:data forType:typeString]; - else - [_item setData:data forType:typeString]; - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + auto typeString = [NSString stringWithUTF8String:(const char*)type]; + auto data = [NSData dataWithBytes:bytes length:len]; + if(_item == nil) + [_pb setData:data forType:typeString]; + else + [_item setData:data forType:typeString]; + return S_OK; + } } virtual HRESULT GetBytes(char* type, IAvnString**ppv) override { - *ppv = nil; - auto typeString = [NSString stringWithUTF8String:(const char*)type]; - NSData*data; - @try + START_COM_CALL; + + @autoreleasepool { - if(_item) - data = [_item dataForType:typeString]; - else - data = [_pb dataForType:typeString]; - if(data == nil) + *ppv = nil; + auto typeString = [NSString stringWithUTF8String:(const char*)type]; + NSData*data; + @try + { + if(_item) + data = [_item dataForType:typeString]; + else + data = [_pb dataForType:typeString]; + if(data == nil) + return E_FAIL; + } + @catch(NSException* e) + { return E_FAIL; + } + *ppv = CreateByteArray((void*)data.bytes, (int)data.length); + return S_OK; } - @catch(NSException* e) - { - return E_FAIL; - } - *ppv = CreateByteArray((void*)data.bytes, (int)data.length); - return S_OK; } virtual HRESULT Clear() override { + START_COM_CALL; + @autoreleasepool { if(_item != nil) @@ -128,15 +147,20 @@ virtual HRESULT Clear() override [_pb clearContents]; [_pb setString:@"" forType:NSPasteboardTypeString]; } - } - return S_OK; + return S_OK; + } } virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override { - *ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]); + return S_OK; + } } }; diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 871bca086d1..c082003ccfd 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -23,17 +23,20 @@ extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnGlDisplay* GetGlDisplay(); extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events); extern IAvnMenuItem* CreateAppMenuItem(); -extern IAvnMenuItem* CreateAppMenuItemSeperator(); +extern IAvnMenuItem* CreateAppMenuItemSeparator(); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); +extern void SetAutoGenerateDefaultAppMenuItems (bool enabled); +extern bool GetAutoGenerateDefaultAppMenuItems (); -extern void InitializeAvnApp(); +extern void InitializeAvnApp(IAvnApplicationEvents* events); extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSPoint ToNSPoint (AvnPoint p); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); +extern CGFloat PrimaryDisplayHeight(); extern NSSize ToNSSize (AvnSize s); #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) diff --git a/native/Avalonia.Native/src/OSX/controlhost.mm b/native/Avalonia.Native/src/OSX/controlhost.mm index 5ee2344ac75..f8e9a3b6d18 100644 --- a/native/Avalonia.Native/src/OSX/controlhost.mm +++ b/native/Avalonia.Native/src/OSX/controlhost.mm @@ -16,11 +16,16 @@ virtual HRESULT CreateDefaultChild(void* parent, void** retOut) override { - NSView* view = [NSView new]; - [view setWantsLayer: true]; + START_COM_CALL; - *retOut = (__bridge_retained void*)view; - return S_OK; + @autoreleasepool + { + NSView* view = [NSView new]; + [view setWantsLayer: true]; + + *retOut = (__bridge_retained void*)view; + return S_OK; + } }; virtual IAvnNativeControlHostTopLevelAttachment* CreateAttachment() override @@ -69,32 +74,42 @@ virtual void DestroyDefaultChild(void* child) override virtual HRESULT InitializeWithChildHandle(void* child) override { - if(_child != nil) - return E_FAIL; - _child = (__bridge NSView*)child; - if(_child == nil) - return E_FAIL; - [_holder addSubview:_child]; - [_child setHidden: false]; - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + if(_child != nil) + return E_FAIL; + _child = (__bridge NSView*)child; + if(_child == nil) + return E_FAIL; + [_holder addSubview:_child]; + [_child setHidden: false]; + return S_OK; + } }; virtual HRESULT AttachTo(IAvnNativeControlHost* host) override { - if(host == nil) - { - [_holder removeFromSuperview]; - [_holder setHidden: true]; - } - else + START_COM_CALL; + + @autoreleasepool { - AvnNativeControlHost* chost = dynamic_cast(host); - if(chost == nil || chost->View == nil) - return E_FAIL; - [_holder setHidden:true]; - [chost->View addSubview:_holder]; + if(host == nil) + { + [_holder removeFromSuperview]; + [_holder setHidden: true]; + } + else + { + AvnNativeControlHost* chost = dynamic_cast(host); + if(chost == nil || chost->View == nil) + return E_FAIL; + [_holder setHidden:true]; + [chost->View addSubview:_holder]; + } + return S_OK; } - return S_OK; }; virtual void ShowInBounds(float x, float y, float width, float height) override diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm index b6f9ed5071c..dc38294a189 100644 --- a/native/Avalonia.Native/src/OSX/cursor.mm +++ b/native/Avalonia.Native/src/OSX/cursor.mm @@ -53,14 +53,46 @@ virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) override { - *retOut = s_cursorMap[cursorType]; + START_COM_CALL; - if(*retOut != nullptr) + @autoreleasepool { - (*retOut)->AddRef(); + *retOut = s_cursorMap[cursorType]; + + if(*retOut != nullptr) + { + (*retOut)->AddRef(); + } + + return S_OK; } + } + + virtual HRESULT CreateCustomCursor (void* bitmapData, size_t length, AvnPixelSize hotPixel, IAvnCursor** retOut) override + { + START_COM_CALL; + + @autoreleasepool + { + if(bitmapData == nullptr || retOut == nullptr) + { + return E_POINTER; + } - return S_OK; + NSData *imageData = [NSData dataWithBytes:bitmapData length:length]; + NSImage *image = [[NSImage alloc] initWithData:imageData]; + + + NSPoint hotSpot; + hotSpot.x = hotPixel.Width; + hotSpot.y = hotPixel.Height; + + *retOut = new Cursor([[NSCursor new] initWithImage: image hotSpot: hotSpot]); + + (*retOut)->AddRef(); + + return S_OK; + } } }; diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index e6c4a861fdd..3e152a61255 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -2,6 +2,7 @@ #define COM_GUIDS_MATERIALIZE #include "common.h" +static bool s_generateDefaultAppMenuItems = true; static NSString* s_appTitle = @"Avalonia"; // Copyright (c) 2011 The Chromium Authors. All rights reserved. @@ -104,23 +105,44 @@ typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN, public: FORWARD_IUNKNOWN() - virtual HRESULT SetApplicationTitle(void* utf8String) override + virtual HRESULT SetApplicationTitle(char* utf8String) override { - auto appTitle = [NSString stringWithUTF8String:(const char*)utf8String]; + START_COM_CALL; - [[NSProcessInfo processInfo] setProcessName:appTitle]; - - - SetProcessName(appTitle); - - return S_OK; + @autoreleasepool + { + auto appTitle = [NSString stringWithUTF8String: utf8String]; + + [[NSProcessInfo processInfo] setProcessName:appTitle]; + + + SetProcessName(appTitle); + + return S_OK; + } } virtual HRESULT SetShowInDock(int show) override { - AvnDesiredActivationPolicy = show - ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + AvnDesiredActivationPolicy = show + ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory; + return S_OK; + } + } + + virtual HRESULT SetDisableDefaultApplicationMenuItems (bool enabled) override + { + START_COM_CALL; + + @autoreleasepool + { + SetAutoGenerateDefaultAppMenuItems(!enabled); + return S_OK; + } } }; @@ -156,13 +178,15 @@ - (void) do public: FORWARD_IUNKNOWN() - virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) override + virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator, IAvnApplicationEvents* events) override { + START_COM_CALL; + _deallocator = deallocator; @autoreleasepool{ [[ThreadingInitializer new] do]; } - InitializeAvnApp(); + InitializeAvnApp(events); return S_OK; }; @@ -173,89 +197,154 @@ virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) overrid virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) override { - if(cb == nullptr || ppv == nullptr) - return E_POINTER; - *ppv = CreateAvnWindow(cb, gl); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + if(cb == nullptr || ppv == nullptr) + return E_POINTER; + *ppv = CreateAvnWindow(cb, gl); + return S_OK; + } }; virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) override { - if(cb == nullptr || ppv == nullptr) - return E_POINTER; + START_COM_CALL; - *ppv = CreateAvnPopup(cb, gl); - return S_OK; + @autoreleasepool + { + if(cb == nullptr || ppv == nullptr) + return E_POINTER; + + *ppv = CreateAvnPopup(cb, gl); + return S_OK; + } } virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) override { - *ppv = CreatePlatformThreading(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = CreatePlatformThreading(); + return S_OK; + } } virtual HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv) override { - *ppv = ::CreateSystemDialogs(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateSystemDialogs(); + return S_OK; + } } virtual HRESULT CreateScreens (IAvnScreens** ppv) override { - *ppv = ::CreateScreens (); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateScreens (); + return S_OK; + } } virtual HRESULT CreateClipboard(IAvnClipboard** ppv) override { - *ppv = ::CreateClipboard (nil, nil); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateClipboard (nil, nil); + return S_OK; + } } virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) override { - *ppv = ::CreateClipboard (nil, [NSPasteboardItem new]); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateClipboard (nil, [NSPasteboardItem new]); + return S_OK; + } } virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) override { - *ppv = ::CreateCursorFactory(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateCursorFactory(); + return S_OK; + } } virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) override { - auto rv = ::GetGlDisplay(); - if(rv == NULL) - return E_FAIL; - rv->AddRef(); - *ppv = rv; - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + auto rv = ::GetGlDisplay(); + if(rv == NULL) + return E_FAIL; + rv->AddRef(); + *ppv = rv; + return S_OK; + } } virtual HRESULT CreateMenu (IAvnMenuEvents* cb, IAvnMenu** ppv) override { - *ppv = ::CreateAppMenu(cb); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateAppMenu(cb); + return S_OK; + } } virtual HRESULT CreateMenuItem (IAvnMenuItem** ppv) override { - *ppv = ::CreateAppMenuItem(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateAppMenuItem(); + return S_OK; + } } - virtual HRESULT CreateMenuItemSeperator (IAvnMenuItem** ppv) override + virtual HRESULT CreateMenuItemSeparator (IAvnMenuItem** ppv) override { - *ppv = ::CreateAppMenuItemSeperator(); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreateAppMenuItemSeparator(); + return S_OK; + } } virtual HRESULT SetAppMenu (IAvnMenu* appMenu) override { - ::SetAppMenu(s_appTitle, appMenu); - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + ::SetAppMenu(s_appTitle, appMenu); + return S_OK; + } } }; @@ -299,10 +388,24 @@ AvnPoint ToAvnPoint (NSPoint p) AvnPoint ConvertPointY (AvnPoint p) { - auto sw = [NSScreen.screens objectAtIndex:0].frame; + auto primaryDisplayHeight = NSMaxY([[[NSScreen screens] firstObject] frame]); - auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height); - p.Y = t - p.Y; + p.Y = primaryDisplayHeight - p.Y; return p; } + +CGFloat PrimaryDisplayHeight() +{ + return NSMaxY([[[NSScreen screens] firstObject] frame]); +} + +void SetAutoGenerateDefaultAppMenuItems (bool enabled) +{ + s_generateDefaultAppMenuItems = enabled; +} + +bool GetAutoGenerateDefaultAppMenuItems () +{ + return s_generateDefaultAppMenuItems; +} diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h index bfbc6801f84..186fcf255b6 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -31,21 +31,21 @@ class AvnAppMenuItem : public ComSingleObject NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem IAvnActionCallback* _callback; IAvnPredicateCallback* _predicate; - bool _isSeperator; + bool _isSeparator; bool _isCheckable; public: FORWARD_IUNKNOWN() - AvnAppMenuItem(bool isSeperator); + AvnAppMenuItem(bool isSeparator); NSMenuItem* GetNative(); virtual HRESULT SetSubMenu (IAvnMenu* menu) override; - virtual HRESULT SetTitle (void* utf8String) override; + virtual HRESULT SetTitle (char* utf8String) override; - virtual HRESULT SetGesture (void* key, AvnInputModifiers modifiers) override; + virtual HRESULT SetGesture (AvnKey key, AvnInputModifiers modifiers) override; virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override; @@ -60,7 +60,6 @@ class AvnAppMenuItem : public ComSingleObject void RaiseOnClicked(); }; - class AvnAppMenu : public ComSingleObject { private: @@ -71,16 +70,18 @@ class AvnAppMenu : public ComSingleObject FORWARD_IUNKNOWN() AvnAppMenu(IAvnMenuEvents* events); - + AvnMenu* GetNative(); void RaiseNeedsUpdate (); + void RaiseOpening(); + void RaiseClosed(); virtual HRESULT InsertItem (int index, IAvnMenuItem* item) override; virtual HRESULT RemoveItem (IAvnMenuItem* item) override; - virtual HRESULT SetTitle (void* utf8String) override; + virtual HRESULT SetTitle (char* utf8String) override; virtual HRESULT Clear () override; }; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index dc1245cd238..38f8c2a7cb2 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -2,6 +2,9 @@ #include "common.h" #include "menu.h" #include "window.h" +#include "KeyTransform.h" +#include +#include /* For kVK_ constants, and TIS functions. */ @implementation AvnMenu { @@ -68,12 +71,12 @@ - (void)didSelectItem:(nullable id)sender } @end -AvnAppMenuItem::AvnAppMenuItem(bool isSeperator) +AvnAppMenuItem::AvnAppMenuItem(bool isSeparator) { _isCheckable = false; - _isSeperator = isSeperator; + _isSeparator = isSeparator; - if(isSeperator) + if(isSeparator) { _native = [NSMenuItem separatorItem]; } @@ -92,6 +95,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenuItem::SetSubMenu (IAvnMenu* menu) { + START_COM_CALL; + @autoreleasepool { if(menu != nullptr) @@ -109,8 +114,10 @@ - (void)didSelectItem:(nullable id)sender } } -HRESULT AvnAppMenuItem::SetTitle (void* utf8String) +HRESULT AvnAppMenuItem::SetTitle (char* utf8String) { + START_COM_CALL; + @autoreleasepool { if (utf8String != nullptr) @@ -122,23 +129,59 @@ - (void)didSelectItem:(nullable id)sender } } -HRESULT AvnAppMenuItem::SetGesture (void* key, AvnInputModifiers modifiers) + +HRESULT AvnAppMenuItem::SetGesture (AvnKey key, AvnInputModifiers modifiers) { + START_COM_CALL; + @autoreleasepool { - NSEventModifierFlags flags = 0; - - if (modifiers & Control) - flags |= NSEventModifierFlagControl; - if (modifiers & Shift) - flags |= NSEventModifierFlagShift; - if (modifiers & Alt) - flags |= NSEventModifierFlagOption; - if (modifiers & Windows) - flags |= NSEventModifierFlagCommand; + if(key != AvnKeyNone) + { + NSEventModifierFlags flags = 0; + + if (modifiers & Control) + flags |= NSEventModifierFlagControl; + if (modifiers & Shift) + flags |= NSEventModifierFlagShift; + if (modifiers & Alt) + flags |= NSEventModifierFlagOption; + if (modifiers & Windows) + flags |= NSEventModifierFlagCommand; + + auto it = s_UnicodeKeyMap.find(key); + + if(it != s_UnicodeKeyMap.end()) + { + auto keyString= [NSString stringWithFormat:@"%C", (unsigned short)it->second]; + + [_native setKeyEquivalent: keyString]; + [_native setKeyEquivalentModifierMask:flags]; + + return S_OK; + } + else + { + auto it = s_AvnKeyMap.find(key); // check if a virtual key is mapped. + + if(it != s_AvnKeyMap.end()) + { + auto it1 = s_QwertyKeyMap.find(it->second); // convert virtual key to qwerty string. + + if(it1 != s_QwertyKeyMap.end()) + { + [_native setKeyEquivalent: [NSString stringWithUTF8String: it1->second]]; + [_native setKeyEquivalentModifierMask:flags]; + + return S_OK; + } + } + } + } - [_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]]; - [_native setKeyEquivalentModifierMask:flags]; + // Nothing matched... clear. + [_native setKeyEquivalent: @""]; + [_native setKeyEquivalentModifierMask: 0]; return S_OK; } @@ -146,6 +189,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenuItem::SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) { + START_COM_CALL; + @autoreleasepool { _predicate = predicate; @@ -156,6 +201,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenuItem::SetIsChecked (bool isChecked) { + START_COM_CALL; + @autoreleasepool { [_native setState:(isChecked && _isCheckable ? NSOnState : NSOffState)]; @@ -165,6 +212,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenuItem::SetToggleType(AvnMenuItemToggleType toggleType) { + START_COM_CALL; + @autoreleasepool { switch(toggleType) @@ -194,6 +243,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenuItem::SetIcon(void *data, size_t length) { + START_COM_CALL; + @autoreleasepool { if(data != nullptr) @@ -261,8 +312,27 @@ - (void)didSelectItem:(nullable id)sender } } +void AvnAppMenu::RaiseOpening() +{ + if(_baseEvents != nullptr) + { + _baseEvents->Opening(); + } +} + +void AvnAppMenu::RaiseClosed() +{ + if(_baseEvents != nullptr) + { + _baseEvents->Closed(); + } +} + + HRESULT AvnAppMenu::InsertItem(int index, IAvnMenuItem *item) { + START_COM_CALL; + @autoreleasepool { if([_native hasGlobalMenuItem]) @@ -283,6 +353,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenu::RemoveItem (IAvnMenuItem* item) { + START_COM_CALL; + @autoreleasepool { auto avnMenuItem = dynamic_cast(item); @@ -296,8 +368,10 @@ - (void)didSelectItem:(nullable id)sender } } -HRESULT AvnAppMenu::SetTitle (void* utf8String) +HRESULT AvnAppMenu::SetTitle (char* utf8String) { + START_COM_CALL; + @autoreleasepool { if (utf8String != nullptr) @@ -311,6 +385,8 @@ - (void)didSelectItem:(nullable id)sender HRESULT AvnAppMenu::Clear() { + START_COM_CALL; + @autoreleasepool { [_native removeAllItems]; @@ -345,6 +421,15 @@ - (void)menuNeedsUpdate:(NSMenu *)menu _parent->RaiseNeedsUpdate(); } +- (void)menuWillOpen:(NSMenu *)menu +{ + _parent->RaiseOpening(); +} + +- (void)menuDidClose:(NSMenu *)menu +{ + _parent->RaiseClosed(); +} @end @@ -364,7 +449,7 @@ - (void)menuNeedsUpdate:(NSMenu *)menu } } -extern IAvnMenuItem* CreateAppMenuItemSeperator() +extern IAvnMenuItem* CreateAppMenuItemSeparator() { @autoreleasepool { @@ -408,47 +493,50 @@ extern void SetAppMenu (NSString* appName, IAvnMenu* menu) auto appMenu = [s_appMenuItem submenu]; - [appMenu addItem:[NSMenuItem separatorItem]]; - - // Services item and menu - auto servicesItem = [[NSMenuItem alloc] init]; - servicesItem.title = @"Services"; - NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; - servicesItem.submenu = servicesMenu; - [NSApplication sharedApplication].servicesMenu = servicesMenu; - [appMenu addItem:servicesItem]; - - [appMenu addItem:[NSMenuItem separatorItem]]; - - // Hide Application - auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"]; - - [appMenu addItem:hideItem]; - - // Hide Others - auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" - action:@selector(hideOtherApplications:) - keyEquivalent:@"h"]; - - hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption; - [appMenu addItem:hideAllOthersItem]; - - // Show All - auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All" - action:@selector(unhideAllApplications:) - keyEquivalent:@""]; - - [appMenu addItem:showAllItem]; - - [appMenu addItem:[NSMenuItem separatorItem]]; - - // Quit Application - auto quitItem = [[NSMenuItem alloc] init]; - quitItem.title = [@"Quit " stringByAppendingString:appName]; - quitItem.keyEquivalent = @"q"; - quitItem.target = [AvnWindow class]; - quitItem.action = @selector(closeAll); - [appMenu addItem:quitItem]; + if(GetAutoGenerateDefaultAppMenuItems()) + { + [appMenu addItem:[NSMenuItem separatorItem]]; + + // Services item and menu + auto servicesItem = [[NSMenuItem alloc] init]; + servicesItem.title = @"Services"; + NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; + servicesItem.submenu = servicesMenu; + [NSApplication sharedApplication].servicesMenu = servicesMenu; + [appMenu addItem:servicesItem]; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // Hide Application + auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"]; + + [appMenu addItem:hideItem]; + + // Hide Others + auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"]; + + hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption; + [appMenu addItem:hideAllOthersItem]; + + // Show All + auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + + [appMenu addItem:showAllItem]; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // Quit Application + auto quitItem = [[NSMenuItem alloc] init]; + quitItem.title = [@"Quit " stringByAppendingString:appName]; + quitItem.keyEquivalent = @"q"; + quitItem.target = [AvnWindow class]; + quitItem.action = @selector(closeAll); + [appMenu addItem:quitItem]; + } } else { diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index f93436d157d..6d5bd4aa02c 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -101,7 +101,7 @@ virtual void Cancel() override virtual bool GetCurrentThreadIsLoopThread() override { - return [[NSThread currentThread] isMainThread]; + return [NSThread isMainThread]; } virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override { @@ -114,6 +114,8 @@ virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) override { + START_COM_CALL; + auto can = dynamic_cast(cancel); if(can->Cancelled) return S_OK; diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 93a33bbbb0c..dc5c24e41e5 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -2,6 +2,7 @@ #include "rendertarget.h" #import #import +#import #include #include @@ -110,7 +111,11 @@ -(void) dealloc if(_renderbuffer != 0) glDeleteRenderbuffers(1, &_renderbuffer); } - CFRelease(surface); + + if(surface != nullptr) + { + CFRelease(surface); + } } @end @@ -143,13 +148,23 @@ - (CALayer *)layer { return _layer; } -- (void)resize:(AvnPixelSize)size withScale: (float) scale;{ +- (void)resize:(AvnPixelSize)size withScale: (float) scale{ + + if(size.Height <= 0) + size.Height = 1; + if(size.Width <= 0) + size.Width = 1; + @synchronized (lock) { if(surface == nil || surface->size.Width != size.Width || surface->size.Height != size.Height || surface->scale != scale) + { surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()]; + + [self updateLayer]; + } } } @@ -159,12 +174,15 @@ - (void)updateLayer { @synchronized (lock) { if(_layer == nil) return; + [CATransaction begin]; [_layer setContents: nil]; if(surface != nil) { [_layer setContentsScale: surface->scale]; [_layer setContents: (__bridge IOSurface*) surface->surface]; } + [CATransaction commit]; + [CATransaction flush]; } } else @@ -229,6 +247,8 @@ -(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget virtual HRESULT GetPixelSize(AvnPixelSize* ret) override { + START_COM_CALL; + if(!_surface) return E_FAIL; *ret = _surface->size; @@ -237,6 +257,8 @@ virtual HRESULT GetPixelSize(AvnPixelSize* ret) override virtual HRESULT GetScaling(double* ret) override { + START_COM_CALL; + if(!_surface) return E_FAIL; *ret = _surface->scale; @@ -263,6 +285,8 @@ virtual HRESULT GetScaling(double* ret) override virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) override { + START_COM_CALL; + ComPtr releaseContext; @synchronized (_target->lock) { if(_target->surface == nil) diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index b1f64bca880..3a54bd4b790 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -10,6 +10,8 @@ class WindowBaseImpl; -(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; -(void) onClosed; -(AvnPixelSize) getPixelSize; +-(AvnPlatformResizeReason) getResizeReason; +-(void) setResizeReason:(AvnPlatformResizeReason)reason; @end @interface AutoFitContentView : NSView @@ -34,6 +36,7 @@ class WindowBaseImpl; -(double) getScaling; -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; +-(bool) isDialog; @end struct INSWindowHolder @@ -50,4 +53,23 @@ struct IWindowStateChanged virtual AvnWindowState WindowState () = 0; }; +class ResizeScope +{ +public: + ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason) + { + _view = view; + _restore = [view getResizeReason]; + [view setResizeReason:reason]; + } + + ~ResizeScope() + { + [_view setResizeReason:_restore]; + } +private: + AvnView* _Nonnull _view; + AvnPlatformResizeReason _restore; +}; + #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index fb945a105e7..14fe60ab0b9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -29,10 +29,12 @@ IAvnMenu* _mainMenu; bool _shown; + bool _inResize; WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) { _shown = false; + _inResize = false; _mainMenu = nullptr; BaseEvents = events; _glContext = gl; @@ -55,6 +57,8 @@ virtual HRESULT ObtainNSWindowHandle(void** ret) override { + START_COM_CALL; + if (ret == nullptr) { return E_POINTER; @@ -67,6 +71,8 @@ virtual HRESULT ObtainNSWindowHandle(void** ret) override virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override { + START_COM_CALL; + if (ret == nullptr) { return E_POINTER; @@ -79,6 +85,8 @@ virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override virtual HRESULT ObtainNSViewHandle(void** ret) override { + START_COM_CALL; + if (ret == nullptr) { return E_POINTER; @@ -91,6 +99,8 @@ virtual HRESULT ObtainNSViewHandle(void** ret) override virtual HRESULT ObtainNSViewHandleRetained(void** ret) override { + START_COM_CALL; + if (ret == nullptr) { return E_POINTER; @@ -106,25 +116,31 @@ virtual HRESULT ObtainNSViewHandleRetained(void** ret) override return Window; } - virtual HRESULT Show() override + virtual HRESULT Show(bool activate, bool isDialog) override { + START_COM_CALL; + @autoreleasepool { SetPosition(lastPositionSet); UpdateStyle(); - if(ShouldTakeFocusOnShow()) + + [Window setTitle:_lastTitle]; + + if(ShouldTakeFocusOnShow() && activate) { + [Window orderFront: Window]; [Window makeKeyAndOrderFront:Window]; + [Window makeFirstResponder:View]; [NSApp activateIgnoringOtherApps:YES]; } else { [Window orderFront: Window]; } - [Window setTitle:_lastTitle]; _shown = true; - + return S_OK; } } @@ -136,6 +152,8 @@ virtual bool ShouldTakeFocusOnShow() virtual HRESULT Hide () override { + START_COM_CALL; + @autoreleasepool { if(Window != nullptr) @@ -150,6 +168,8 @@ virtual HRESULT Hide () override virtual HRESULT Activate () override { + START_COM_CALL; + @autoreleasepool { if(Window != nullptr) @@ -164,6 +184,8 @@ virtual HRESULT Activate () override virtual HRESULT SetTopMost (bool value) override { + START_COM_CALL; + @autoreleasepool { [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; @@ -174,11 +196,16 @@ virtual HRESULT SetTopMost (bool value) override virtual HRESULT Close() override { + START_COM_CALL; + @autoreleasepool { if (Window != nullptr) { - [Window close]; + auto window = Window; + Window = nullptr; + + [window close]; } return S_OK; @@ -187,19 +214,40 @@ virtual HRESULT Close() override virtual HRESULT GetClientSize(AvnSize* ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) return E_POINTER; + auto frame = [View frame]; ret->Width = frame.size.width; ret->Height = frame.size.height; + + return S_OK; + } + } + + virtual HRESULT GetFrameSize(AvnSize* ret) override + { + @autoreleasepool + { + if(ret == nullptr) + return E_POINTER; + + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + return S_OK; } } virtual HRESULT GetScaling (double* ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) @@ -218,6 +266,8 @@ virtual HRESULT GetScaling (double* ret) override virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override { + START_COM_CALL; + @autoreleasepool { [Window setMinSize: ToNSSize(minSize)]; @@ -227,8 +277,18 @@ virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override } } - virtual HRESULT Resize(double x, double y) override + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override { + if(_inResize) + { + return S_OK; + } + + _inResize = true; + + START_COM_CALL; + auto resizeBlock = ResizeScope(View, reason); + @autoreleasepool { auto maxSize = [Window maxSize]; @@ -254,7 +314,19 @@ virtual HRESULT Resize(double x, double y) override y = maxSize.height; } - [Window setContentSize:NSSize{x, y}]; + @try + { + if(!_shown) + { + BaseEvents->Resized(AvnSize{x,y}, reason); + } + + [Window setContentSize:NSSize{x, y}]; + } + @finally + { + _inResize = false; + } return S_OK; } @@ -262,6 +334,8 @@ virtual HRESULT Resize(double x, double y) override virtual HRESULT Invalidate (AvnRect rect) override { + START_COM_CALL; + @autoreleasepool { [View setNeedsDisplayInRect:[View frame]]; @@ -272,6 +346,8 @@ virtual HRESULT Invalidate (AvnRect rect) override virtual HRESULT SetMainMenu(IAvnMenu* menu) override { + START_COM_CALL; + _mainMenu = menu; auto nativeMenu = dynamic_cast(menu); @@ -290,6 +366,8 @@ virtual HRESULT SetMainMenu(IAvnMenu* menu) override virtual HRESULT BeginMoveDrag () override { + START_COM_CALL; + @autoreleasepool { auto lastEvent = [View lastMouseDownEvent]; @@ -307,11 +385,15 @@ virtual HRESULT BeginMoveDrag () override virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override { + START_COM_CALL; + return S_OK; } virtual HRESULT GetPosition (AvnPoint* ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) @@ -332,6 +414,8 @@ virtual HRESULT GetPosition (AvnPoint* ret) override virtual HRESULT SetPosition (AvnPoint point) override { + START_COM_CALL; + @autoreleasepool { lastPositionSet = point; @@ -343,6 +427,8 @@ virtual HRESULT SetPosition (AvnPoint point) override virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) @@ -361,6 +447,8 @@ virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) @@ -378,12 +466,16 @@ virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override { + START_COM_CALL; + [View setSwRenderedFrame: fb dispose: dispose]; return S_OK; } virtual HRESULT SetCursor(IAvnCursor* cursor) override { + START_COM_CALL; + @autoreleasepool { Cursor* avnCursor = dynamic_cast(cursor); @@ -413,6 +505,8 @@ virtual void UpdateCursor() virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override { + START_COM_CALL; + if(View == NULL) return E_FAIL; *ppv = [renderTarget createSurfaceRenderTarget]; @@ -421,6 +515,8 @@ virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override { + START_COM_CALL; + if(View == NULL) return E_FAIL; *retOut = ::CreateNativeControlHost(View); @@ -429,6 +525,8 @@ virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override virtual HRESULT SetBlurEnabled (bool enable) override { + START_COM_CALL; + [StandardContainer ShowBlur:enable]; return S_OK; @@ -438,6 +536,8 @@ virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint p IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) override { + START_COM_CALL; + auto item = TryGetPasteboardItem(clipboard); [item setString:@"" forType:GetAvnCustomDataType()]; if(item == nil) @@ -478,6 +578,11 @@ virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint p return S_OK; } + virtual bool IsDialog() + { + return false; + } + protected: virtual NSWindowStyleMask GetStyle() { @@ -503,10 +608,12 @@ virtual void OnResized () bool _fullScreenActive; SystemDecorations _decorations; AvnWindowState _lastWindowState; + AvnWindowState _actualWindowState; bool _inSetWindowState; NSRect _preZoomSize; bool _transitioningWindowState; bool _isClientAreaExtended; + bool _isDialog; AvnExtendClientAreaChromeHints _extendClientHints; FORWARD_IUNKNOWN() @@ -529,6 +636,7 @@ virtual void OnResized () _transitioningWindowState = false; _inSetWindowState = false; _lastWindowState = Normal; + _actualWindowState = Normal; WindowEvents = events; [Window setCanBecomeKeyAndMain]; [Window disableCursorRects]; @@ -537,6 +645,11 @@ virtual void OnResized () void HideOrShowTrafficLights () { + if (Window == nil) + { + return; + } + for (id subview in Window.contentView.superview.subviews) { if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { NSView *titlebarView = [subview subviews][0]; @@ -561,11 +674,14 @@ void HideOrShowTrafficLights () } } - virtual HRESULT Show () override + virtual HRESULT Show (bool activate, bool isDialog) override { + START_COM_CALL; + @autoreleasepool - { - WindowBaseImpl::Show(); + { + _isDialog = isDialog; + WindowBaseImpl::Show(activate, isDialog); HideOrShowTrafficLights(); @@ -575,6 +691,8 @@ virtual HRESULT Show () override virtual HRESULT SetEnabled (bool enable) override { + START_COM_CALL; + @autoreleasepool { [Window setEnabled:enable]; @@ -584,6 +702,8 @@ virtual HRESULT SetEnabled (bool enable) override virtual HRESULT SetParent (IAvnWindow* parent) override { + START_COM_CALL; + @autoreleasepool { if(parent == nullptr) @@ -593,6 +713,12 @@ virtual HRESULT SetParent (IAvnWindow* parent) override if(cparent == nullptr) return E_INVALIDARG; + // If one tries to show a child window with a minimized parent window, then the parent window will be + // restored but MacOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; UpdateStyle(); @@ -623,7 +749,7 @@ AvnWindowState WindowState () override void WindowStateChanged () override { - if(!_inSetWindowState && !_transitioningWindowState) + if(_shown && !_inSetWindowState && !_transitioningWindowState) { AvnWindowState state; GetWindowState(&state); @@ -660,6 +786,7 @@ void WindowStateChanged () override } _lastWindowState = state; + _actualWindowState = state; WindowEvents->WindowStateChanged(state); } } @@ -695,6 +822,8 @@ void DoZoom() virtual HRESULT SetCanResize(bool value) override { + START_COM_CALL; + @autoreleasepool { _canResize = value; @@ -705,6 +834,8 @@ virtual HRESULT SetCanResize(bool value) override virtual HRESULT SetDecorations(SystemDecorations value) override { + START_COM_CALL; + @autoreleasepool { auto currentWindowState = _lastWindowState; @@ -768,8 +899,10 @@ virtual HRESULT SetDecorations(SystemDecorations value) override } } - virtual HRESULT SetTitle (void* utf8title) override + virtual HRESULT SetTitle (char* utf8title) override { + START_COM_CALL; + @autoreleasepool { _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title]; @@ -781,6 +914,8 @@ virtual HRESULT SetTitle (void* utf8title) override virtual HRESULT SetTitleBarColor(AvnColor color) override { + START_COM_CALL; + @autoreleasepool { float a = (float)color.Alpha / 255.0f; @@ -810,6 +945,8 @@ virtual HRESULT SetTitleBarColor(AvnColor color) override virtual HRESULT GetWindowState (AvnWindowState*ret) override { + START_COM_CALL; + @autoreleasepool { if(ret == nullptr) @@ -843,86 +980,111 @@ virtual HRESULT GetWindowState (AvnWindowState*ret) override virtual HRESULT TakeFocusFromChildren () override { - if(Window == nil) - return S_OK; - if([Window isKeyWindow]) - [Window makeFirstResponder: View]; + START_COM_CALL; - return S_OK; + @autoreleasepool + { + if(Window == nil) + return S_OK; + if([Window isKeyWindow]) + [Window makeFirstResponder: View]; + + return S_OK; + } } virtual HRESULT SetExtendClientArea (bool enable) override { - _isClientAreaExtended = enable; + START_COM_CALL; - if(enable) + @autoreleasepool { - Window.titleVisibility = NSWindowTitleHidden; - - [Window setTitlebarAppearsTransparent:true]; - - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - if (wantsTitleBar) - { - [StandardContainer ShowTitleBar:true]; - } - else - { - [StandardContainer ShowTitleBar:false]; - } + _isClientAreaExtended = enable; - if(_extendClientHints & AvnOSXThickTitleBar) + if(enable) { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; + Window.titleVisibility = NSWindowTitleHidden; + + [Window setTitlebarAppearsTransparent:true]; + + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + if (wantsTitleBar) + { + [StandardContainer ShowTitleBar:true]; + } + else + { + [StandardContainer ShowTitleBar:false]; + } + + if(_extendClientHints & AvnOSXThickTitleBar) + { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } + else + { + Window.toolbar = nullptr; + } } else { + Window.titleVisibility = NSWindowTitleVisible; Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; } + + [Window setIsExtended:enable]; + + HideOrShowTrafficLights(); + + UpdateStyle(); + + return S_OK; } - else - { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - - [Window setIsExtended:enable]; - - HideOrShowTrafficLights(); - - UpdateStyle(); - - return S_OK; } virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override { - _extendClientHints = hints; + START_COM_CALL; - SetExtendClientArea(_isClientAreaExtended); - return S_OK; + @autoreleasepool + { + _extendClientHints = hints; + + SetExtendClientArea(_isClientAreaExtended); + return S_OK; + } } virtual HRESULT GetExtendTitleBarHeight (double*ret) override { - if(ret == nullptr) + START_COM_CALL; + + @autoreleasepool { - return E_POINTER; + if(ret == nullptr) + { + return E_POINTER; + } + + *ret = [Window getExtendedTitleBarHeight]; + + return S_OK; } - - *ret = [Window getExtendedTitleBarHeight]; - - return S_OK; } virtual HRESULT SetExtendTitleBarHeight (double value) override { - [StandardContainer SetTitleBarHeightHint:value]; - return S_OK; + START_COM_CALL; + + @autoreleasepool + { + [StandardContainer SetTitleBarHeightHint:value]; + return S_OK; + } } void EnterFullScreenMode () @@ -951,16 +1113,23 @@ void ExitFullScreenMode () virtual HRESULT SetWindowState (AvnWindowState state) override { + START_COM_CALL; + @autoreleasepool { - if(_lastWindowState == state) + if(Window == nullptr) + { + return S_OK; + } + + if(_actualWindowState == state) { return S_OK; } _inSetWindowState = true; - auto currentState = _lastWindowState; + auto currentState = _actualWindowState; _lastWindowState = state; if(currentState == Normal) @@ -1039,8 +1208,12 @@ virtual HRESULT SetWindowState (AvnWindowState state) override } break; } + + _actualWindowState = _lastWindowState; + WindowEvents->WindowStateChanged(_actualWindowState); } + _inSetWindowState = false; return S_OK; @@ -1055,6 +1228,11 @@ virtual void OnResized () override } } + virtual bool IsDialog() override + { + return _isDialog; + } + protected: virtual NSWindowStyleMask GetStyle() override { @@ -1134,6 +1312,9 @@ -(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content [_blurBehind setWantsLayer:true]; _blurBehind.hidden = true; + [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:_blurBehind]; [self addSubview:_content]; @@ -1169,9 +1350,6 @@ -(void)setFrameSize:(NSSize)newSize _settingSize = true; [super setFrameSize:newSize]; - [_blurBehind setFrameSize:newSize]; - [_content setFrameSize:newSize]; - auto window = objc_cast([self window]); // TODO get actual titlebar size @@ -1187,6 +1365,7 @@ -(void)setFrameSize:(NSSize)newSize [_titleBarMaterial setFrame:tbar]; tbar.size.height = height < 1 ? 0 : 1; [_titleBarUnderline setFrame:tbar]; + _settingSize = false; } @@ -1214,6 +1393,7 @@ @implementation AvnView bool _lastKeyHandled; AvnPixelSize _lastPixelSize; NSObject* _renderTarget; + AvnPlatformResizeReason _resizeReason; } - (void)onClosed @@ -1325,7 +1505,8 @@ -(void)setFrameSize:(NSSize)newSize _lastPixelSize.Height = (int)fsize.height; [self updateRenderTarget]; - _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); + auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; + _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); } } @@ -1338,6 +1519,12 @@ - (void)updateLayer } _parent->BaseEvents->RunRenderPriorityJobs(); + + if (_parent == nullptr) + { + return; + } + _parent->BaseEvents->Paint(); } @@ -1385,17 +1572,20 @@ - (void) viewDidChangeBackingProperties [super viewDidChangeBackingProperties]; } -- (bool) ignoreUserInput +- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { auto parentWindow = objc_cast([self window]); if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) { - auto window = dynamic_cast(_parent.getRaw()); - - if(window != nullptr) + if(trigerInputWhenDisabled) { - window->WindowEvents->GotInputWhenDisabled(); + auto window = dynamic_cast(_parent.getRaw()); + + if(window != nullptr) + { + window->WindowEvents->GotInputWhenDisabled(); + } } return TRUE; @@ -1406,7 +1596,9 @@ - (bool) ignoreUserInput - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type { - if([self ignoreUserInput]) + bool triggerInputWhenDisabled = type != Move; + + if([self ignoreUserInput: triggerInputWhenDisabled]) { return; } @@ -1572,7 +1764,7 @@ - (void)mouseExited:(NSEvent *)event - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type { - if([self ignoreUserInput]) + if([self ignoreUserInput: false]) { return; } @@ -1813,6 +2005,16 @@ - (void)concludeDragOperation:(nullable id )sender } +- (AvnPlatformResizeReason)getResizeReason +{ + return _resizeReason; +} + +- (void)setResizeReason:(AvnPlatformResizeReason)reason +{ + _resizeReason = reason; +} + @end @@ -1832,6 +2034,11 @@ -(void) setIsExtended:(bool)value; _isExtended = value; } +-(bool) isDialog +{ + return _parent->IsDialog(); +} + -(double) getScaling { return _lastScaling; @@ -1861,13 +2068,7 @@ -(double) getExtendedTitleBarHeight +(void)closeAll { - NSArray* windows = [NSArray arrayWithArray:[NSApp windows]]; - auto numWindows = [windows count]; - - for(int i = 0; i < numWindows; i++) - { - [[windows objectAtIndex:i] performClose:nil]; - } + [[NSApplication sharedApplication] terminate:self]; } - (void)performClose:(id)sender @@ -1880,7 +2081,7 @@ - (void)performClose:(id)sender { if(![self windowShouldClose:self]) return; } - + [self close]; } @@ -1920,6 +2121,10 @@ -(void) showWindowMenuWithAppMenu [NSApp setMenu:_menu]; } + else + { + [self showAppMenuOnly]; + } } -(void) showAppMenuOnly @@ -1976,7 +2181,6 @@ -(AvnWindow*) initWithParent: (WindowBaseImpl*) parent _lastScaling = [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; - [self invalidateShadow]; _isExtended = false; return self; } @@ -2013,7 +2217,22 @@ - (void)windowWillClose:(NSNotification *)notification -(BOOL)canBecomeKeyWindow { - return _canBecomeKeyAndMain; + if (_canBecomeKeyAndMain) + { + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) + { + auto ch = objc_cast(uch); + if(ch == nil) + continue; + if (ch.isDialog) + return false; + } + + return true; + } + + return false; } -(BOOL)canBecomeMainWindow @@ -2021,22 +2240,6 @@ -(BOOL)canBecomeMainWindow return _canBecomeKeyAndMain; } --(bool) activateAppropriateChild: (bool)activating -{ - for(NSWindow* uch in [self childWindows]) - { - auto ch = objc_cast(uch); - if(ch == nil) - continue; - [ch activateAppropriateChild:false]; - return FALSE; - } - - if(!activating) - [self makeKeyAndOrderFront:self]; - return TRUE; -} - -(bool)shouldTryToHandleEvents { return _isEnabled; @@ -2047,27 +2250,16 @@ -(void) setEnabled:(bool)enable _isEnabled = enable; } --(void)makeKeyWindow -{ - if([self activateAppropriateChild: true]) - { - [super makeKeyWindow]; - } -} - -(void)becomeKeyWindow { - if([self activateAppropriateChild: true]) + [self showWindowMenuWithAppMenu]; + + if(_parent != nullptr) { - [self showWindowMenuWithAppMenu]; - - if(_parent != nullptr) - { - _parent->BaseEvents->Activated(); - } - - [super becomeKeyWindow]; + _parent->BaseEvents->Activated(); } + + [super becomeKeyWindow]; } -(void) restoreParentWindow; @@ -2076,7 +2268,6 @@ -(void) restoreParentWindow; if(parent != nil) { [parent removeChildWindow:self]; - [parent activateAppropriateChild: false]; } } @@ -2211,13 +2402,18 @@ virtual NSWindowStyleMask GetStyle() override return NSWindowStyleMaskBorderless; } - virtual HRESULT Resize(double x, double y) override + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override { + START_COM_CALL; + @autoreleasepool { - [Window setContentSize:NSSize{x, y}]; + if (Window != nullptr) + { + [Window setContentSize:NSSize{x, y}]; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + } return S_OK; } diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 097815cc69b..6f4afd9191c 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -16,7 +16,6 @@ using Nuke.Common.Tools.Npm; using Nuke.Common.Utilities; using Nuke.Common.Utilities.Collections; -using Pharmacist.Core; using static Nuke.Common.EnvironmentInfo; using static Nuke.Common.IO.FileSystemTasks; using static Nuke.Common.IO.PathConstruction; @@ -89,10 +88,6 @@ void ExecWait(string preamble, string command, string args) Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit(); } ExecWait("dotnet version:", "dotnet", "--version"); - if (Parameters.IsRunningOnUnix) - ExecWait("Mono version:", "mono", "--version"); - - } IReadOnlyCollection MsBuildCommon( @@ -107,7 +102,7 @@ IReadOnlyCollection MsBuildCommon( .AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_8_X64"))) .AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", true) - .SetToolPath(MsBuildExe.Value) + .SetProcessToolPath(MsBuildExe.Value) .SetConfiguration(Parameters.Configuration) .SetVerbosity(MSBuildVerbosity.Minimal) .Apply(configurator)); @@ -132,15 +127,16 @@ IReadOnlyCollection MsBuildCommon( var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; NpmTasks.NpmInstall(c => c - .SetWorkingDirectory(webappDir) - .SetArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessWorkingDirectory(webappDir) + .SetProcessArgumentConfigurator(a => a.Add("--silent"))); NpmTasks.NpmRun(c => c - .SetWorkingDirectory(webappDir) + .SetProcessWorkingDirectory(webappDir) .SetCommand("dist")); }); Target CompileNative => _ => _ .DependsOn(Clean) + .DependsOn(GenerateCppHeaders) .OnlyWhenStatic(() => EnvironmentInfo.IsOsx) .Executes(() => { @@ -156,7 +152,7 @@ IReadOnlyCollection MsBuildCommon( { if (Parameters.IsRunningOnWindows) MsBuildCommon(Parameters.MSBuildSolution, c => c - .SetArgumentConfigurator(a => a.Add("/r")) + .SetProcessArgumentConfigurator(a => a.Add("/r")) .AddTargets("Build") ); @@ -166,44 +162,8 @@ IReadOnlyCollection MsBuildCommon( .AddProperty("PackageVersion", Parameters.Version) .SetConfiguration(Parameters.Configuration) ); - - await CompileReactiveEvents(); }); - async Task CompileReactiveEvents() - { - var avaloniaBuildOutput = Path.Combine(RootDirectory, "packages", "Avalonia", "bin", Parameters.Configuration); - var avaloniaAssemblies = GlobFiles(avaloniaBuildOutput, "**/Avalonia*.dll") - .Where(file => !file.Contains("Avalonia.Build.Tasks") && - !file.Contains("Avalonia.Remote.Protocol")); - - var eventsDirectory = GlobDirectories($"{RootDirectory}/src/**/Avalonia.ReactiveUI.Events").First(); - var eventsBuildFile = Path.Combine(eventsDirectory, "Events_Avalonia.cs"); - if (File.Exists(eventsBuildFile)) - File.Delete(eventsBuildFile); - - using (var stream = File.Create(eventsBuildFile)) - using (var writer = new StreamWriter(stream)) - { - await ObservablesForEventGenerator.ExtractEventsFromAssemblies( - writer, avaloniaAssemblies, new string[0], "netstandard2.0" - ); - } - - var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj"); - if (Parameters.IsRunningOnWindows) - MsBuildCommon(eventsProject, c => c - .SetArgumentConfigurator(a => a.Add("/r")) - .AddTargets("Build") - ); - else - DotNetBuild(c => c - .SetProjectFile(eventsProject) - .AddProperty("PackageVersion", Parameters.Version) - .SetConfiguration(Parameters.Configuration) - ); - } - void RunCoreTest(string projectName) { Information($"Running tests from {projectName}"); @@ -233,6 +193,21 @@ void RunCoreTest(string projectName) } } + Target RunHtmlPreviewerTests => _ => _ + .DependsOn(CompileHtmlPreviewer) + .OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests)) + .Executes(() => + { + var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp"; + + NpmTasks.NpmInstall(c => c + .SetProcessWorkingDirectory(webappTestDir) + .SetProcessArgumentConfigurator(a => a.Add("--silent"))); + NpmTasks.NpmRun(c => c + .SetProcessWorkingDirectory(webappTestDir) + .SetCommand("test")); + }); + Target RunCoreLibsTests => _ => _ .OnlyWhenStatic(() => !Parameters.SkipTests) .DependsOn(Compile) @@ -289,14 +264,19 @@ void RunCoreTest(string projectName) .Executes(() => { var data = Parameters; + var pathToProjectSource = RootDirectory / "samples" / "ControlCatalog.NetCore"; + var pathToPublish = pathToProjectSource / "bin" / data.Configuration / "publish"; + + DotNetPublish(c => c + .SetProject(pathToProjectSource / "ControlCatalog.NetCore.csproj") + .EnableNoBuild() + .SetConfiguration(data.Configuration) + .AddProperty("PackageVersion", data.Version) + .AddProperty("PublishDir", pathToPublish)); + Zip(data.ZipCoreArtifacts, data.BinRoot); Zip(data.ZipNuGetArtifacts, data.NugetRoot); - Zip(data.ZipTargetControlCatalogDesktopDir, - GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dll").Concat( - GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.config")).Concat( - GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.so")).Concat( - GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.dylib")).Concat( - GlobFiles(data.ZipSourceControlCatalogDesktopDir, "*.exe"))); + Zip(data.ZipTargetControlCatalogNetCoreDir, pathToPublish); }); Target CreateIntermediateNugetPackages => _ => _ @@ -332,6 +312,7 @@ void RunCoreTest(string projectName) .DependsOn(RunCoreLibsTests) .DependsOn(RunRenderTests) .DependsOn(RunDesignerTests) + .DependsOn(RunHtmlPreviewerTests) .DependsOn(RunLeakTests); Target Package => _ => _ diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index a167e9d8920..a92c988fbdd 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -58,11 +58,10 @@ public class BuildParameters public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } - public AbsolutePath ZipSourceControlCatalogDesktopDir { get; } - public AbsolutePath ZipTargetControlCatalogDesktopDir { get; } + public AbsolutePath ZipTargetControlCatalogNetCoreDir { get; } - public BuildParameters(Build b) + public BuildParameters(Build b) { // ARGUMENTS Configuration = b.Configuration ?? "Release"; @@ -129,9 +128,7 @@ public BuildParameters(Build b) FileZipSuffix = Version + ".zip"; ZipCoreArtifacts = ZipRoot / ("Avalonia-" + FileZipSuffix); ZipNuGetArtifacts = ZipRoot / ("Avalonia-NuGet-" + FileZipSuffix); - ZipSourceControlCatalogDesktopDir = - RootDirectory / ("samples/ControlCatalog.Desktop/bin/" + DirSuffix + "/net461"); - ZipTargetControlCatalogDesktopDir = ZipRoot / ("ControlCatalog.Desktop-" + FileZipSuffix); + ZipTargetControlCatalogNetCoreDir = ZipRoot / ("ControlCatalog.NetCore-" + FileZipSuffix); } string GetVersion() diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs index 44f01da6690..e3766ae23fe 100644 --- a/nukebuild/BuildTasksPatcher.cs +++ b/nukebuild/BuildTasksPatcher.cs @@ -29,7 +29,11 @@ public static void PatchBuildTasksInPackage(string packagePath) InputAssemblies = new[] { temp, typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0] - .FullyQualifiedName + .FullyQualifiedName, + typeof(Mono.Cecil.Rocks.MethodBodyRocks).Assembly.GetModules()[0].FullyQualifiedName, + typeof(Mono.Cecil.Pdb.PdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName, + typeof(Mono.Cecil.Mdb.MdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName + }, SearchDirectories = new string[0], OutputFile = output diff --git a/nukebuild/MicroComGen.cs b/nukebuild/MicroComGen.cs new file mode 100644 index 00000000000..06c8acbf238 --- /dev/null +++ b/nukebuild/MicroComGen.cs @@ -0,0 +1,14 @@ +using System.IO; +using MicroComGenerator; +using Nuke.Common; + +partial class Build : NukeBuild +{ + Target GenerateCppHeaders => _ => _.Executes(() => + { + var text = File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl"); + var ast = AstParser.Parse(text); + File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h", + CppGen.GenerateCpp(ast)); + }); +} \ No newline at end of file diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index b06e49f2eb4..e08ffd0413c 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -10,14 +10,13 @@ - + - - + @@ -39,4 +38,8 @@ + + + + diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index cd3ce9adcd8..44e2290a0d4 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,9 +5,10 @@ - - - + + + all + @@ -15,9 +16,7 @@ - + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netcoreapp2.0/Avalonia.Designer.HostApp.dll"> @@ -32,21 +31,23 @@ + true - build\ + build\;buildTransitive\ true - build\ + build\;buildTransitive\ true - build\ + build\;buildTransitive\ + diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 84a62bb5c07..45a7f1aa449 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -3,6 +3,8 @@ <_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild) <_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false low + <_AvaloniaPatchComInterop Condition="'$(_AvaloniaPatchComInterop)' == ''">false + <_AvaloniaSkipXamlCompilation Condition="'$(_AvaloniaSkipXamlCompilation)' == ''">false @@ -87,6 +89,11 @@ ProjectDirectory="$(MSBuildProjectDirectory)" VerifyIl="$(AvaloniaXamlIlVerifyIl)" ReportImportance="$(AvaloniaXamlReportImportance)" + AssemblyOriginatorKeyFile="$(AssemblyOriginatorKeyFile)" + SignAssembly="$(SignAssembly)" + DelaySign="$(DelaySign)" + EnableComInteropPatching="$(_AvaloniaPatchComInterop)" + SkipXamlCompilation="$(_AvaloniaSkipXamlCompilation)" /> [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) -Avalonia - ## 📖 About AvaloniaUI Avalonia is a cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows via .NET Framework and .NET Core, Linux via Xorg, macOS. Avalonia is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. -> **Note:** The UI theme you see in the picture above is still work-in-progress and will be available in the upcoming Avalonia 0.10.0 release. However, you can connect to our nightly build feed and install latest pre-release versions of Avalonia NuGet packages, if you are willing to help out with the development and testing. See [Using nightly build feed](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed) for more info. +([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) To see the status of some of our features, please see our [Roadmap](https://github.com/AvaloniaUI/Avalonia/issues/2239). You can also see what [breaking changes](https://github.com/AvaloniaUI/Avalonia/issues/3538) we have planned and what our [past breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) have been. [Awesome Avalonia](https://github.com/AvaloniaCommunity/awesome-avalonia) is community-curated list of awesome Avalonia UI tools, libraries, projects and resources. Go and see what people are building with Avalonia! ## 🚀 Getting Started -The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://avaloniaui.net/docs/quickstart/create-new-project). +The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](https://docs.avaloniaui.net/docs/getting-started). Avalonia is delivered via NuGet package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/ @@ -31,15 +29,22 @@ Install-Package Avalonia.Desktop Examples of UIs built with Avalonia ![image](https://user-images.githubusercontent.com/4672627/84707589-5b69a880-af35-11ea-87a6-7ad57a31d314.png) +([Synfonia](https://github.com/jmacato/Synfonia)) + ![image](https://user-images.githubusercontent.com/4672627/85069644-d8419000-b18a-11ea-8732-be9055bb61fd.PNG) +([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ![image](https://user-images.githubusercontent.com/4672627/85069659-dc6dad80-b18a-11ea-8375-39ef95315b5c.PNG) +([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ![image](https://user-images.githubusercontent.com/4672627/84708947-c3b98980-af37-11ea-8c9d-503334615bbf.png) +([Xaml Control Gallery](https://github.com/AvaloniaUI/xamlcontrolsgallery)) ## JetBrains Rider -If you need to develop Avalonia app with JetBrains Rider, go and *vote* on [this issue](https://youtrack.jetbrains.com/issue/RIDER-39247) in their tracker. JetBrains won't do things without their users telling them that they want the feature, so only **YOU** can make it happen. +[JetBrains Rider](https://www.jetbrains.com/rider/whatsnew/?mkt_tok=eyJpIjoiTURBNU1HSmhNV0kwTUdFMiIsInQiOiJtNnU2VEc1TlNLa1ZRVkROYmdZYVpYREJsaU1qdUhmS3dxSzRHczdYWHl0RVlTNDMwSFwvNUs3VENTNVM0bVcyNFdaRmVYZzVWTTF1N3VrQWNGTkJreEhlam1hMlB4UVVWcHBGM1dNOUxoXC95YnRQdGgyUXl1YmZCM3h3d3BVWWdBIn0%3D#avalonia-support) now has official support for Avalonia. + +Code completion, inspections and refactorings are supported out of the box, for XAML previewer add `https://plugins.jetbrains.com/plugins/dev/14839` to plugin repositories and install [AvaloniaRider](https://github.com/ForNeVeR/AvaloniaRider) plugin. ## Bleeding Edge Builds @@ -47,7 +52,7 @@ We also have a [nightly build](https://github.com/AvaloniaUI/Avalonia/wiki/Using ## Documentation -Documentation can be found on our website at https://avaloniaui.net/docs/. We also have a [tutorial](https://avaloniaui.net/docs/tutorial/) over there for newcomers. +Documentation can be found at https://docs.avaloniaui.net. We also have a [tutorial](https://docs.avaloniaui.net/docs/getting-started/programming-with-avalonia) over there for newcomers. ## Building and Using diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index 13875aeb219..8a5364c70be 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -1,8 +1,6 @@ -using System; using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace BindingDemo { @@ -13,13 +11,19 @@ public override void Initialize() AvaloniaXamlLoader.Load(this); } - private static void Main() + public override void OnFrameworkInitializationCompleted() { - AppBuilder.Configure() - .UsePlatformDetect() - .UseReactiveUI() - .LogToDebug() - .Start(); + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); } + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .LogToTrace(); } } diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index 817023fd711..d898b737a98 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -6,12 +6,11 @@ - + - diff --git a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs index 0fe12a8ef70..7de083351ee 100644 --- a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs +++ b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs @@ -1,9 +1,9 @@ -using ReactiveUI; +using MiniMvvm; using System; namespace BindingDemo.ViewModels { - public class ExceptionErrorViewModel : ReactiveObject + public class ExceptionErrorViewModel : ViewModelBase { private int _lessThan10; diff --git a/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs b/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs index caf75c846cf..9ae8d9558f8 100644 --- a/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs +++ b/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs @@ -1,11 +1,11 @@ -using ReactiveUI; +using MiniMvvm; using System; using System.ComponentModel; using System.Collections; namespace BindingDemo.ViewModels { - public class IndeiErrorViewModel : ReactiveObject, INotifyDataErrorInfo + public class IndeiErrorViewModel : ViewModelBase, INotifyDataErrorInfo { private int _maximum = 10; private int _value; diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs index f0241cad486..18a7a01a694 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -5,14 +5,14 @@ using System.Reactive.Linq; using System.Threading.Tasks; using System.Threading; -using ReactiveUI; +using MiniMvvm; using Avalonia.Controls; using Avalonia.Metadata; using Avalonia.Controls.Selection; namespace BindingDemo.ViewModels { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private string _booleanString = "True"; private double _doubleValue = 5.0; @@ -32,13 +32,13 @@ public MainWindowViewModel() Selection = new SelectionModel { SingleSelect = false }; - ShuffleItems = ReactiveCommand.Create(() => + ShuffleItems = MiniCommand.Create(() => { var r = new Random(); Items.Move(r.Next(Items.Count), 1); }); - StringValueCommand = ReactiveCommand.Create(param => + StringValueCommand = MiniCommand.Create(param => { BooleanFlag = !BooleanFlag; StringValue = param.ToString(); @@ -60,7 +60,7 @@ public MainWindowViewModel() public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public ReactiveCommand ShuffleItems { get; } + public MiniCommand ShuffleItems { get; } public string BooleanString { @@ -93,7 +93,7 @@ public string CurrentTime } public IObservable CurrentTimeObservable { get; } - public ReactiveCommand StringValueCommand { get; } + public MiniCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel(); diff --git a/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs b/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs index 0e9139ab98f..1c2222b0b0f 100644 --- a/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs +++ b/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs @@ -1,18 +1,18 @@ -using ReactiveUI; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; +using MiniMvvm; namespace BindingDemo.ViewModels { - public class NestedCommandViewModel : ReactiveObject + public class NestedCommandViewModel : ViewModelBase { public NestedCommandViewModel() { - Command = ReactiveCommand.Create(() => { }); + Command = MiniCommand.Create(() => { }); } public ICommand Command { get; } diff --git a/samples/BindingDemo/ViewModels/TestItem.cs b/samples/BindingDemo/ViewModels/TestItem.cs index 5a9f192f58d..2f49a3c99f4 100644 --- a/samples/BindingDemo/ViewModels/TestItem.cs +++ b/samples/BindingDemo/ViewModels/TestItem.cs @@ -1,8 +1,8 @@ -using ReactiveUI; +using MiniMvvm; namespace BindingDemo.ViewModels { - public class TestItem : ReactiveObject + public class TestItem : ViewModelBase { private string _stringValue = "String Value"; private string _detail; diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 5b82e2caee0..1a68c4d7327 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -16,7 +16,7 @@ Resources\Resource.Designer.cs Off False - v9.0 + v11.0 Properties\AndroidManifest.xml @@ -71,21 +71,23 @@ + - - Designer - + - + + - + + Resources\drawable\Icon.png + diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs index 40d001a1950..2ab03551b60 100644 --- a/samples/ControlCatalog.Android/MainActivity.cs +++ b/samples/ControlCatalog.Android/MainActivity.cs @@ -1,31 +1,18 @@ -using System; -using Android.App; +using Android.App; using Android.OS; using Android.Content.PM; using Avalonia.Android; -using Avalonia.Controls; -using Avalonia.Controls.Templates; -using Avalonia.Markup.Xaml; -using Avalonia.Media; -using Avalonia.Styling; -using Avalonia.Themes.Default; -using Avalonia; namespace ControlCatalog.Android { - [Activity(Label = "ControlCatalog.Android", MainLauncher = true, Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)] + [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)] public class MainActivity : AvaloniaActivity { protected override void OnCreate(Bundle savedInstanceState) { - if (Avalonia.Application.Current == null) - { - AppBuilder.Configure() - .UseAndroid() - .SetupWithoutStarting(); - Content = new MainView(); - } base.OnCreate(savedInstanceState); + + Content = new MainView(); } } } diff --git a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml index e39ec39f1ca..9effda7e797 100644 --- a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml +++ b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index 96f0e76fd81..b1ca548e2c7 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -2,7 +2,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -15,7 +14,7 @@ namespace ControlCatalog.Android { - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { @@ -26,8 +25,6 @@ static Resource() public static void UpdateIdValues() { - global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName; - global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello; } public partial class Attribute @@ -43,69 +40,59 @@ private Attribute() } } - public partial class Drawable + public partial class Color { - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; + // aapt resource value: 0x7F010000 + public const int splash_background = 2130771968; - static Drawable() + static Color() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } - private Drawable() + private Color() { } } - public partial class Id + public partial class Drawable { - // aapt resource value: 0x7f050000 - public const int MyButton = 2131034112; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { + // aapt resource value: 0x7F020000 + public const int Icon = 2130837504; - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; + // aapt resource value: 0x7F020001 + public const int splash_screen = 2130837505; - static Layout() + static Drawable() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } - private Layout() + private Drawable() { } } - public partial class String + public partial class Style { - // aapt resource value: 0x7f040001 - public const int ApplicationName = 2130968577; + // aapt resource value: 0x7F030000 + public const int MyTheme = 2130903040; + + // aapt resource value: 0x7F030001 + public const int MyTheme_NoActionBar = 2130903041; - // aapt resource value: 0x7f040000 - public const int Hello = 2130968576; + // aapt resource value: 0x7F030002 + public const int MyTheme_Splash = 2130903042; - static String() + static Style() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } - private String() + private Style() { } } diff --git a/samples/ControlCatalog.Android/Resources/drawable/Icon.png b/samples/ControlCatalog.Android/Resources/drawable/Icon.png deleted file mode 100644 index 8074c4c571b..00000000000 Binary files a/samples/ControlCatalog.Android/Resources/drawable/Icon.png and /dev/null differ diff --git a/samples/ControlCatalog.Android/Resources/drawable/splash_screen.xml b/samples/ControlCatalog.Android/Resources/drawable/splash_screen.xml new file mode 100644 index 00000000000..2e920b4b3be --- /dev/null +++ b/samples/ControlCatalog.Android/Resources/drawable/splash_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/samples/ControlCatalog.Android/Resources/layout/Main.axml b/samples/ControlCatalog.Android/Resources/layout/Main.axml deleted file mode 100644 index 570c96ad728..00000000000 --- a/samples/ControlCatalog.Android/Resources/layout/Main.axml +++ /dev/null @@ -1,13 +0,0 @@ - - - + + + + + + + + Hello world + + + Inner context flyout + + + + + + + diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs new file mode 100644 index 00000000000..10f9ee1de8f --- /dev/null +++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs @@ -0,0 +1,91 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; +using Avalonia.Interactivity; +using System; +using System.ComponentModel; + +namespace ControlCatalog.Pages +{ + public class ContextFlyoutPage : UserControl + { + private TextBox _textBox; + + public ContextFlyoutPage() + { + InitializeComponent(); + + DataContext = new ContextPageViewModel(); + + _textBox = this.FindControl("TextBox"); + + var cutButton = this.FindControl + + + diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml.cs b/samples/ControlCatalog/Pages/CursorPage.xaml.cs new file mode 100644 index 00000000000..9e9e9ba8b96 --- /dev/null +++ b/samples/ControlCatalog/Pages/CursorPage.xaml.cs @@ -0,0 +1,20 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ControlCatalog.ViewModels; + +namespace ControlCatalog.Pages +{ + public class CursorPage : UserControl + { + public CursorPage() + { + this.InitializeComponent(); + DataContext = new CursorPageViewModel(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index cacc2204bde..340b3376f54 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -1,5 +1,5 @@ @@ -23,23 +23,29 @@ - - - - - - - - - + + + + + + + + + + + + + - - + + diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs index 2a30f4d91bf..dc5cc49a902 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml.cs @@ -24,8 +24,10 @@ public DataGridPage() dg1.LoadingRow += Dg1_LoadingRow; dg1.Sorting += (s, a) => { - var property = ((a.Column as DataGridBoundColumn)?.Binding as Binding).Path; - if (property == dataGridSortDescription.PropertyPath + var binding = (a.Column as DataGridBoundColumn)?.Binding as Binding; + + if (binding?.Path is string property + && property == dataGridSortDescription.PropertyPath && !collectionView1.SortDescriptions.Contains(dataGridSortDescription)) { collectionView1.SortDescriptions.Add(dataGridSortDescription); diff --git a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml index af6b6e86055..45056a9a765 100644 --- a/samples/ControlCatalog/Pages/DateTimePickerPage.xaml +++ b/samples/ControlCatalog/Pages/DateTimePickerPage.xaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:sys="clr-namespace:System;assembly=netstandard" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ControlCatalog.Pages.DateTimePickerPage"> @@ -30,6 +31,16 @@ + + + + + + + + + A DatePicker with day formatted and year hidden. + + + + + + + + A TimePicker with a header and minute increments specified. diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index cf6c771e34b..49921fb7f68 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -4,6 +4,7 @@ using System.Reflection; using Avalonia.Controls; using Avalonia.Dialogs; +using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 @@ -112,11 +113,29 @@ List GetFilters() private Window CreateSampleWindow() { - var window = new Window(); - window.Height = 200; - window.Width = 200; - window.Content = new TextBlock { Text = "Hello world!" }; - window.WindowStartupLocation = WindowStartupLocation.CenterOwner; + Button button; + + var window = new Window + { + Height = 200, + Width = 200, + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock { Text = "Hello world!" }, + (button = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Click to close" + }) + } + }, + WindowStartupLocation = WindowStartupLocation.CenterOwner + }; + + button.Click += (_, __) => window.Close(); return window; } diff --git a/samples/ControlCatalog/Pages/FlyoutsPage.axaml b/samples/ControlCatalog/Pages/FlyoutsPage.axaml new file mode 100644 index 00000000000..c4d0bc3e67e --- /dev/null +++ b/samples/ControlCatalog/Pages/FlyoutsPage.axaml @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs b/samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs new file mode 100644 index 00000000000..0803d178b9b --- /dev/null +++ b/samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs @@ -0,0 +1,81 @@ +using Avalonia.Controls; +using Avalonia.Controls.Primitives; +using Avalonia.Markup.Xaml; +using Avalonia.Interactivity; + +namespace ControlCatalog.Pages +{ + public class FlyoutsPage : UserControl + { + public FlyoutsPage() + { + InitializeComponent(); + + var afp = this.FindControl("AttachedFlyoutPanel"); + if (afp != null) + { + afp.DoubleTapped += Afp_DoubleTapped; + } + + SetXamlTexts(); + } + + private void Afp_DoubleTapped(object sender, RoutedEventArgs e) + { + if (sender is Panel p) + { + FlyoutBase.ShowAttachedFlyout(p); + } + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void SetXamlTexts() + { + var bfxt = this.FindControl("ButtonFlyoutXamlText"); + bfxt.Text = ""; + + var mfxt = this.FindControl("MenuFlyoutXamlText"); + mfxt.Text = ""; + + var afxt = this.FindControl("AttachedFlyoutXamlText"); + afxt.Text = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n\n In DoubleTapped handler:\n" + + "FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);"; + + var sfxt = this.FindControl("SharedFlyoutXamlText"); + sfxt.Text = "Declare a flyout in Resources:\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n\n\n" + + "Then attach the flyout where you want it:\n" + + " + + + + + diff --git a/samples/ControlCatalog/Pages/LabelsPage.axaml.cs b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs new file mode 100644 index 00000000000..a14978fd2c9 --- /dev/null +++ b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs @@ -0,0 +1,42 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using ControlCatalog.Models; + +namespace ControlCatalog.Pages +{ + public class LabelsPage : UserControl + { + private Person _person; + + public LabelsPage() + { + CreateDefaultPerson(); + this.InitializeComponent(); + } + + private void CreateDefaultPerson() + { + DataContext = _person = new Person + { + FirstName = "John", + LastName = "Doe", + IsBanned = true, + }; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void DoSave() + { + + } + public void DoCancel() + { + CreateDefaultPerson(); + } + } +} diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index 3521ad71a97..f515db84d40 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -20,6 +20,6 @@ + SelectionMode="{Binding SelectionMode^}"/> diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs index 46dbe3dcada..5999510b6c8 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs @@ -6,7 +6,6 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; -using ReactiveUI; namespace ControlCatalog.Pages { diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml index 0d7e5da17fa..2f422e2d64b 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml @@ -1,7 +1,9 @@  - + Numeric up-down control Numeric up-down control provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel. @@ -67,15 +69,36 @@ + - - Usage of NumericUpDown: - - + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs index 92da64d87ee..5856194430f 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -26,17 +26,32 @@ private void InitializeComponent() } - public class NumbersPageViewModel : ReactiveObject + public class NumbersPageViewModel : ViewModelBase { private IList _formats; private FormatObject _selectedFormat; private IList _spinnerLocations; + private double _doubleValue; + private decimal _decimalValue; + public NumbersPageViewModel() { SelectedFormat = Formats.FirstOrDefault(); } + public double DoubleValue + { + get { return _doubleValue; } + set { this.RaiseAndSetIfChanged(ref _doubleValue, value); } + } + + public decimal DecimalValue + { + get { return _decimalValue; } + set { this.RaiseAndSetIfChanged(ref _decimalValue, value); } + } + public IList Formats { get diff --git a/samples/ControlCatalog/Pages/ProgressBarPage.xaml b/samples/ControlCatalog/Pages/ProgressBarPage.xaml index 2ec0b48c76b..da8ef6cf078 100644 --- a/samples/ControlCatalog/Pages/ProgressBarPage.xaml +++ b/samples/ControlCatalog/Pages/ProgressBarPage.xaml @@ -15,6 +15,13 @@ + + + + + + + diff --git a/samples/ControlCatalog/Pages/RelativePanelPage.axaml b/samples/ControlCatalog/Pages/RelativePanelPage.axaml index 3657d52bd9d..4dcdcc195fa 100644 --- a/samples/ControlCatalog/Pages/RelativePanelPage.axaml +++ b/samples/ControlCatalog/Pages/RelativePanelPage.axaml @@ -23,16 +23,16 @@ - + - + - + @@ -41,19 +41,19 @@ - + - + - + - + - + diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index c39f414b446..4edb0f137ad 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -29,7 +29,7 @@ public override void Render(DrawingContext context) var screens = w.Screens.All; var scaling = ((IRenderRoot)w).RenderScaling; - var drawBrush = Brushes.Green; + var drawBrush = Brushes.Black; Pen p = new Pen(drawBrush); if (screens != null) foreach (Screen screen in screens) @@ -45,18 +45,16 @@ public override void Render(DrawingContext context) screen.Bounds.Height / 10f); Rect workingAreaRect = new Rect(screen.WorkingArea.X / 10f + Math.Abs(_leftMost), screen.WorkingArea.Y / 10f, screen.WorkingArea.Width / 10f, screen.WorkingArea.Height / 10f); + context.DrawRectangle(p, boundsRect); context.DrawRectangle(p, workingAreaRect); - - FormattedText text = new FormattedText() - { - Typeface = Typeface.Default - }; - text.Text = $"Bounds: {screen.Bounds.Width}:{screen.Bounds.Height}"; + var text = new FormattedText() { Typeface = new Typeface("Arial"), FontSize = 18 }; + + text.Text = $"Bounds: {screen.Bounds.TopLeft} {screen.Bounds.Width}:{screen.Bounds.Height}"; context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height), text); - text.Text = $"WorkArea: {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"; + text.Text = $"WorkArea: {screen.WorkingArea.TopLeft} {screen.WorkingArea.Width}:{screen.WorkingArea.Height}"; context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 20), text); text.Text = $"Scaling: {screen.PixelDensity * 100}%"; @@ -69,7 +67,7 @@ public override void Render(DrawingContext context) context.DrawText(drawBrush, boundsRect.Position.WithY(boundsRect.Size.Height + 80), text); } - context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10, w.Bounds.Width / 10, w.Bounds.Height / 10)); + context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); } } } diff --git a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs index 36d3768b131..dcd7a88a56a 100644 --- a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs @@ -2,11 +2,11 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { - public class ScrollViewerPageViewModel : ReactiveObject + public class ScrollViewerPageViewModel : ViewModelBase { private bool _allowAutoHide; private ScrollBarVisibility _horizontalScrollVisibility; diff --git a/samples/ControlCatalog/Pages/SliderPage.xaml b/samples/ControlCatalog/Pages/SliderPage.xaml index ea31ed00502..b4901ec780d 100644 --- a/samples/ControlCatalog/Pages/SliderPage.xaml +++ b/samples/ControlCatalog/Pages/SliderPage.xaml @@ -1,5 +1,6 @@ Slider @@ -21,6 +22,35 @@ IsSnapToTickEnabled="True" Ticks="0,20,25,40,75,100" Width="300" /> + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs index a38a3ab4cb8..f49b13091b2 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia.Media.Imaging; using Avalonia.Platform; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -56,7 +56,7 @@ private IBitmap LoadBitmap(string uri) return new Bitmap(assets.Open(new Uri(uri))); } - private class PageViewModel : ReactiveObject + private class PageViewModel : ViewModelBase { private Dock _tabPlacement; diff --git a/samples/ControlCatalog/Pages/TextBlockPage.xaml b/samples/ControlCatalog/Pages/TextBlockPage.xaml index 4a1c196917a..d4f72f161a1 100644 --- a/samples/ControlCatalog/Pages/TextBlockPage.xaml +++ b/samples/ControlCatalog/Pages/TextBlockPage.xaml @@ -18,8 +18,8 @@ - - + + diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml b/samples/ControlCatalog/Pages/TextBoxPage.xaml index 481a459159a..1ac447ea693 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml @@ -1,9 +1,10 @@ + x:Class="ControlCatalog.Pages.TextBoxPage" + xmlns:sys="clr-namespace:System;assembly=netstandard"> - TextBox - A control into which the user can input text + + - - + + + + + + + + - + - - resm fonts - - - - - - - - res fonts - - - - - + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs b/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs index cd5f7903127..9eeefebb023 100644 --- a/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TextBoxPage.xaml.cs @@ -13,6 +13,12 @@ public TextBoxPage() private void InitializeComponent() { AvaloniaXamlLoader.Load(this); + + this.Get("numericWatermark") + .TextInputOptionsQuery += (s, a) => + { + a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number; + }; } } } diff --git a/samples/ControlCatalog/Pages/ToolTipPage.xaml b/samples/ControlCatalog/Pages/ToolTipPage.xaml index ec073d48a9b..de23c7a169d 100644 --- a/samples/ControlCatalog/Pages/ToolTipPage.xaml +++ b/samples/ControlCatalog/Pages/ToolTipPage.xaml @@ -6,7 +6,7 @@ ToolTip A control which pops up a hint when a control is hovered - @@ -38,6 +38,31 @@ ToolTip bottom placement + + + + + Moving offset + diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml index e78cf2bc224..ef802db33ee 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml @@ -1,66 +1,36 @@ - - - F1 M 16.6309,18.6563C 17.1309, - 8.15625 29.8809,14.1563 29.8809, - 14.1563C 30.8809,11.1563 34.1308, - 11.4063 34.1308,11.4063C 33.5,12 - 34.6309,13.1563 34.6309,13.1563C - 32.1309,13.1562 31.1309,14.9062 - 31.1309,14.9062C 41.1309,23.9062 - 32.6309,27.9063 32.6309,27.9062C - 24.6309,24.9063 21.1309,22.1562 - 16.6309,18.6563 Z M 16.6309,19.9063C - 21.6309,24.1563 25.1309,26.1562 - 31.6309,28.6562C 31.6309,28.6562 - 26.3809,39.1562 18.3809,36.1563C - 18.3809,36.1563 18,38 16.3809,36.9063C - 15,36 16.3809,34.9063 16.3809,34.9063C - 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z - - - + Viewbox A control used to scale single child. - - None - Fill - Uniform - UniformToFill - - Hello World! - - - Hello World! - - - Hello World! - - - Hello World! - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs index 1b5f4bc7f46..94b3f3ea14c 100644 --- a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs @@ -1,5 +1,6 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Media; namespace ControlCatalog.Pages { @@ -7,7 +8,25 @@ public class ViewboxPage : UserControl { public ViewboxPage() { - this.InitializeComponent(); + InitializeComponent(); + + var stretchSelector = this.FindControl("StretchSelector"); + + stretchSelector.Items = new[] + { + Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None + }; + + stretchSelector.SelectedIndex = 0; + + var stretchDirectionSelector = this.FindControl("StretchDirectionSelector"); + + stretchDirectionSelector.Items = new[] + { + StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly + }; + + stretchDirectionSelector.SelectedIndex = 0; } private void InitializeComponent() diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index 7c911e91e9e..2b5215a3feb 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -16,7 +16,6 @@ diff --git a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs similarity index 80% rename from samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs rename to samples/ControlCatalog/ViewModels/ContextPageViewModel.cs index 5c2f74d2d5f..b46ffeb0074 100644 --- a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ContextPageViewModel.cs @@ -3,18 +3,18 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class ContextMenuPageViewModel + public class ContextPageViewModel { public Control View { get; set; } - public ContextMenuPageViewModel() + public ContextPageViewModel() { - OpenCommand = ReactiveCommand.CreateFromTask(Open); - SaveCommand = ReactiveCommand.Create(Save); - OpenRecentCommand = ReactiveCommand.Create(OpenRecent); + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); MenuItems = new[] { @@ -44,9 +44,9 @@ public ContextMenuPageViewModel() } public IReadOnlyList MenuItems { get; set; } - public ReactiveCommand OpenCommand { get; } - public ReactiveCommand SaveCommand { get; } - public ReactiveCommand OpenRecentCommand { get; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } public async Task Open() { diff --git a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs new file mode 100644 index 00000000000..f1cc0637dc6 --- /dev/null +++ b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia; +using Avalonia.Input; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using MiniMvvm; + +namespace ControlCatalog.ViewModels +{ + public class CursorPageViewModel : ViewModelBase + { + public CursorPageViewModel() + { + StandardCursors = Enum.GetValues(typeof(StandardCursorType)) + .Cast() + .Select(x => new StandardCursorModel(x)) + .ToList(); + + var loader = AvaloniaLocator.Current.GetService(); + var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png")); + var bitmap = new Bitmap(s); + CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16)); + } + + public IEnumerable StandardCursors { get; } + + public Cursor CustomCursor { get; } + + public class StandardCursorModel + { + public StandardCursorModel(StandardCursorType type) + { + Type = type; + Cursor = new Cursor(type); + } + + public StandardCursorType Type { get; } + + public Cursor Cursor { get; } + } + } +} diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs index f893a6e28e9..ee1fa6ae777 100644 --- a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs @@ -2,11 +2,11 @@ using System.Collections.ObjectModel; using System.Linq; using Avalonia.Media; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class ItemsRepeaterPageViewModel : ReactiveObject + public class ItemsRepeaterPageViewModel : ViewModelBase { private int _newItemIndex = 1; private int _newGenerationIndex = 0; @@ -59,7 +59,7 @@ private ObservableCollection CreateItems() })); } - public class Item : ReactiveObject + public class Item : ViewModelBase { private double _height = double.NaN; diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs index f75bc32105e..7f2d6e95727 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -4,18 +4,18 @@ using System.Reactive; using Avalonia.Controls; using Avalonia.Controls.Selection; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class ListBoxPageViewModel : ReactiveObject + public class ListBoxPageViewModel : ViewModelBase { private bool _multiple; private bool _toggle; private bool _alwaysSelected; private bool _autoScrollToSelectedItem = true; private int _counter; - private ObservableAsPropertyHelper _selectionMode; + private IObservable _selectionMode; public ListBoxPageViewModel() { @@ -29,14 +29,13 @@ public ListBoxPageViewModel() x => x.Toggle, x => x.AlwaysSelected, (m, t, a) => - (m ? SelectionMode.Multiple : 0) | - (t ? SelectionMode.Toggle : 0) | - (a ? SelectionMode.AlwaysSelected : 0)) - .ToProperty(this, x => x.SelectionMode); + (m ? Avalonia.Controls.SelectionMode.Multiple : 0) | + (t ? Avalonia.Controls.SelectionMode.Toggle : 0) | + (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0)); - AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem())); + AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem())); - RemoveItemCommand = ReactiveCommand.Create(() => + RemoveItemCommand = MiniCommand.Create(() => { var items = Selection.SelectedItems.ToList(); @@ -46,7 +45,7 @@ public ListBoxPageViewModel() } }); - SelectRandomItemCommand = ReactiveCommand.Create(() => + SelectRandomItemCommand = MiniCommand.Create(() => { var random = new Random(); @@ -60,7 +59,7 @@ public ListBoxPageViewModel() public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public SelectionMode SelectionMode => _selectionMode.Value; + public IObservable SelectionMode => _selectionMode; public bool Multiple { @@ -86,9 +85,9 @@ public bool AutoScrollToSelectedItem set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value); } - public ReactiveCommand AddItemCommand { get; } - public ReactiveCommand RemoveItemCommand { get; } - public ReactiveCommand SelectRandomItemCommand { get; } + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } private string GenerateItem() => $"Item {_counter++.ToString()}"; } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 4356a032fad..4b3cfa9c9d0 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -5,11 +5,11 @@ using Avalonia.Dialogs; using Avalonia.Platform; using System; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - class MainWindowViewModel : ReactiveObject + class MainWindowViewModel : ViewModelBase { private IManagedNotificationManager _notificationManager; @@ -27,22 +27,22 @@ public MainWindowViewModel(IManagedNotificationManager notificationManager) { _notificationManager = notificationManager; - ShowCustomManagedNotificationCommand = ReactiveCommand.Create(() => + ShowCustomManagedNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" }); }); - ShowManagedNotificationCommand = ReactiveCommand.Create(() => + ShowManagedNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); }); - ShowNativeNotificationCommand = ReactiveCommand.Create(() => + ShowNativeNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error)); }); - AboutCommand = ReactiveCommand.CreateFromTask(async () => + AboutCommand = MiniCommand.CreateFromTask(async () => { var dialog = new AboutAvaloniaDialog(); @@ -51,12 +51,12 @@ public MainWindowViewModel(IManagedNotificationManager notificationManager) await dialog.ShowDialog(mainWindow); }); - ExitCommand = ReactiveCommand.Create(() => + ExitCommand = MiniCommand.Create(() => { (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown(); }); - ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() => + ToggleMenuItemCheckedCommand = MiniCommand.Create(() => { IsMenuItemChecked = !IsMenuItemChecked; }); @@ -153,16 +153,16 @@ public bool IsMenuItemChecked set { this.RaiseAndSetIfChanged(ref _isMenuItemChecked, value); } } - public ReactiveCommand ShowCustomManagedNotificationCommand { get; } + public MiniCommand ShowCustomManagedNotificationCommand { get; } - public ReactiveCommand ShowManagedNotificationCommand { get; } + public MiniCommand ShowManagedNotificationCommand { get; } - public ReactiveCommand ShowNativeNotificationCommand { get; } + public MiniCommand ShowNativeNotificationCommand { get; } - public ReactiveCommand AboutCommand { get; } + public MiniCommand AboutCommand { get; } - public ReactiveCommand ExitCommand { get; } + public MiniCommand ExitCommand { get; } - public ReactiveCommand ToggleMenuItemCheckedCommand { get; } + public MiniCommand ToggleMenuItemCheckedCommand { get; } } } diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs index 9e7ae8b716f..ecbd59c5d73 100644 --- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -13,9 +13,9 @@ public class MenuPageViewModel public Control View { get; set; } public MenuPageViewModel() { - OpenCommand = ReactiveCommand.CreateFromTask(Open); - SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false)); - OpenRecentCommand = ReactiveCommand.Create(OpenRecent); + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); var recentItems = new[] { @@ -65,9 +65,9 @@ public MenuPageViewModel() public IReadOnlyList MenuItems { get; set; } public IReadOnlyList RecentItems { get; set; } - public ReactiveCommand OpenCommand { get; } - public ReactiveCommand SaveCommand { get; } - public ReactiveCommand OpenRecentCommand { get; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } public async Task Open() { diff --git a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs index 8724ba344b1..2052481015c 100644 --- a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs +++ b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs @@ -1,6 +1,6 @@ using System.Reactive; using Avalonia.Controls.Notifications; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -8,12 +8,12 @@ public class NotificationViewModel { public NotificationViewModel(INotificationManager manager) { - YesCommand = ReactiveCommand.Create(() => + YesCommand = MiniCommand.Create(() => { manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); }); - NoCommand = ReactiveCommand.Create(() => + NoCommand = MiniCommand.Create(() => { manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); }); @@ -22,9 +22,9 @@ public NotificationViewModel(INotificationManager manager) public string Title { get; set; } public string Message { get; set; } - public ReactiveCommand YesCommand { get; } + public MiniCommand YesCommand { get; } - public ReactiveCommand NoCommand { get; } + public MiniCommand NoCommand { get; } } } diff --git a/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs b/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs index f27f605a8b5..9e6932bb76a 100644 --- a/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs @@ -1,10 +1,10 @@ using System; using Avalonia.Controls; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class SplitViewPageViewModel : ReactiveObject + public class SplitViewPageViewModel : ViewModelBase { private bool _isLeft = true; private int _displayMode = 3; //CompactOverlay diff --git a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs index 210e281ed67..c03379330fb 100644 --- a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs @@ -3,11 +3,11 @@ using System.Linq; using System.Reactive; using Avalonia.Controls; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class TreeViewPageViewModel : ReactiveObject + public class TreeViewPageViewModel : ViewModelBase { private readonly Node _root; private SelectionMode _selectionMode; @@ -19,16 +19,16 @@ public TreeViewPageViewModel() Items = _root.Children; SelectedItems = new ObservableCollection(); - AddItemCommand = ReactiveCommand.Create(AddItem); - RemoveItemCommand = ReactiveCommand.Create(RemoveItem); - SelectRandomItemCommand = ReactiveCommand.Create(SelectRandomItem); + AddItemCommand = MiniCommand.Create(AddItem); + RemoveItemCommand = MiniCommand.Create(RemoveItem); + SelectRandomItemCommand = MiniCommand.Create(SelectRandomItem); } public ObservableCollection Items { get; } public ObservableCollection SelectedItems { get; } - public ReactiveCommand AddItemCommand { get; } - public ReactiveCommand RemoveItemCommand { get; } - public ReactiveCommand SelectRandomItemCommand { get; } + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } public SelectionMode SelectionMode { diff --git a/samples/MiniMvvm/MiniCommand.cs b/samples/MiniMvvm/MiniCommand.cs new file mode 100644 index 00000000000..c6a9273c201 --- /dev/null +++ b/samples/MiniMvvm/MiniCommand.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace MiniMvvm +{ + public sealed class MiniCommand : MiniCommand, ICommand + { + private readonly Action _cb; + private bool _busy; + private Func _acb; + + public MiniCommand(Action cb) + { + _cb = cb; + } + + public MiniCommand(Func cb) + { + _acb = cb; + } + + private bool Busy + { + get => _busy; + set + { + _busy = value; + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } + + + public override event EventHandler CanExecuteChanged; + public override bool CanExecute(object parameter) => !_busy; + + public override async void Execute(object parameter) + { + if(Busy) + return; + try + { + Busy = true; + if (_cb != null) + _cb((T)parameter); + else + await _acb((T)parameter); + } + finally + { + Busy = false; + } + } + } + + public abstract class MiniCommand : ICommand + { + public static MiniCommand Create(Action cb) => new MiniCommand(_ => cb()); + public static MiniCommand Create(Action cb) => new MiniCommand(cb); + public static MiniCommand CreateFromTask(Func cb) => new MiniCommand(_ => cb()); + + public abstract bool CanExecute(object parameter); + public abstract void Execute(object parameter); + public abstract event EventHandler CanExecuteChanged; + } +} diff --git a/samples/MiniMvvm/MiniMvvm.csproj b/samples/MiniMvvm/MiniMvvm.csproj new file mode 100644 index 00000000000..6535b2bdbd0 --- /dev/null +++ b/samples/MiniMvvm/MiniMvvm.csproj @@ -0,0 +1,6 @@ + + + netstandard2.0 + + + diff --git a/samples/MiniMvvm/PropertyChangedExtensions.cs b/samples/MiniMvvm/PropertyChangedExtensions.cs new file mode 100644 index 00000000000..f1065c75303 --- /dev/null +++ b/samples/MiniMvvm/PropertyChangedExtensions.cs @@ -0,0 +1,108 @@ +using System; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; + +namespace MiniMvvm +{ + public static class PropertyChangedExtensions + { + class PropertyObservable : IObservable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + + public PropertyObservable(INotifyPropertyChanged target, PropertyInfo info) + { + _target = target; + _info = info; + } + + class Subscription : IDisposable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + private readonly IObserver _observer; + + public Subscription(INotifyPropertyChanged target, PropertyInfo info, IObserver observer) + { + _target = target; + _info = info; + _observer = observer; + _target.PropertyChanged += OnPropertyChanged; + _observer.OnNext((T)_info.GetValue(_target)); + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == _info.Name) + _observer.OnNext((T)_info.GetValue(_target)); + } + + public void Dispose() + { + _target.PropertyChanged -= OnPropertyChanged; + _observer.OnCompleted(); + } + } + + public IDisposable Subscribe(IObserver observer) + { + return new Subscription(_target, _info, observer); + } + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> expr) where TModel : INotifyPropertyChanged + { + var l = (LambdaExpression)expr; + var ma = (MemberExpression)l.Body; + var prop = (PropertyInfo)ma.Member; + return new PropertyObservable(model, prop); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Func cb + ) where TModel : INotifyPropertyChanged + { + return model.WhenAnyValue(v1).Select(cb); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Func cb + ) where TModel : INotifyPropertyChanged => + Observable.CombineLatest( + model.WhenAnyValue(v1), + model.WhenAnyValue(v2), + cb); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, (a1, a2) => (a1, a2)); + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3, + Func cb + ) where TModel : INotifyPropertyChanged => + Observable.CombineLatest( + model.WhenAnyValue(v1), + model.WhenAnyValue(v2), + model.WhenAnyValue(v3), + cb); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, v3, (a1, a2, a3) => (a1, a2, a3)); + } +} diff --git a/samples/MiniMvvm/ViewModelBase.cs b/samples/MiniMvvm/ViewModelBase.cs new file mode 100644 index 00000000000..7256b05ceff --- /dev/null +++ b/samples/MiniMvvm/ViewModelBase.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Reactive.Joins; +using System.Runtime.CompilerServices; + +namespace MiniMvvm +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (!EqualityComparer.Default.Equals(field, value)) + { + field = value; + RaisePropertyChanged(propertyName); + return true; + } + return false; + } + + + protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/samples/Previewer/App.xaml.cs b/samples/Previewer/App.xaml.cs index fffa987a278..ab83d45cd3d 100644 --- a/samples/Previewer/App.xaml.cs +++ b/samples/Previewer/App.xaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace Previewer @@ -9,6 +10,13 @@ public override void Initialize() { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } } -} \ No newline at end of file +} diff --git a/samples/Previewer/Previewer.csproj b/samples/Previewer/Previewer.csproj index cd3daf61e1e..cfedb7ad9ed 100644 --- a/samples/Previewer/Previewer.csproj +++ b/samples/Previewer/Previewer.csproj @@ -8,7 +8,6 @@ %(Filename) - diff --git a/samples/Previewer/Program.cs b/samples/Previewer/Program.cs index 48363e27f2d..b12b93974a5 100644 --- a/samples/Previewer/Program.cs +++ b/samples/Previewer/Program.cs @@ -1,13 +1,14 @@ -using System; -using Avalonia; +using Avalonia; namespace Previewer { class Program { - static void Main(string[] args) - { - AppBuilder.Configure().UsePlatformDetect().Start(); - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect(); + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } -} \ No newline at end of file +} diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml index 61e4d2385b1..7cdcea2e1de 100644 --- a/samples/RenderDemo/App.xaml +++ b/samples/RenderDemo/App.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="RenderDemo.App"> - + diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index 233160b0252..8054b069648 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -1,6 +1,6 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace RenderDemo { @@ -11,15 +11,26 @@ public override void Initialize() AvaloniaXamlLoader.Load(this); } + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } + // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. - static void Main(string[] args) => BuildAvaloniaApp().Start(); + static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); // App configuration, used by the entry point and previewer static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() + .With(new Win32PlatformOptions + { + OverlayPopups = true, + }) .UsePlatformDetect() - .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); } } diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index 93fbe5e412a..f37df56b737 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -36,6 +36,9 @@ + + + @@ -57,6 +60,9 @@ + + + diff --git a/samples/RenderDemo/MainWindow.xaml.cs b/samples/RenderDemo/MainWindow.xaml.cs index b45a605e04d..877eb8016aa 100644 --- a/samples/RenderDemo/MainWindow.xaml.cs +++ b/samples/RenderDemo/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using RenderDemo.ViewModels; -using ReactiveUI; +using MiniMvvm; namespace RenderDemo { diff --git a/samples/RenderDemo/Pages/AnimationsPage.xaml b/samples/RenderDemo/Pages/AnimationsPage.xaml index 12fb31ea59e..9043bac33e7 100644 --- a/samples/RenderDemo/Pages/AnimationsPage.xaml +++ b/samples/RenderDemo/Pages/AnimationsPage.xaml @@ -1,7 +1,8 @@ + x:Class="RenderDemo.Pages.AnimationsPage" + MaxWidth="600"> @@ -160,6 +161,151 @@ + + + + + + + + @@ -167,8 +313,8 @@ - - Hover to activate Transform Keyframe Animations. + + Hover to activate Keyframe Animations.