diff --git a/Avalonia.sln b/Avalonia.sln index 74a2dbb94b..75f1dd8407 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\to EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -2116,6 +2118,30 @@ Global {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.Build.0 = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2176,6 +2202,7 @@ Global {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C} {11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/Documentation/build.md b/Documentation/build.md index 2f59146a48..5f75290424 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -60,15 +60,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: -- (for revisions after 2 Nov 2020) Run `./build.sh GenerateCppHeaders` to generate `avalonia-native.h` from `avn.idl` -- 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 3abd236d42..7a1f28bea7 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,6 +4,6 @@ - + diff --git a/build/ApiDiff.props b/build/ApiDiff.props index 3d322f56d5..fb65ef6e87 100644 --- a/build/ApiDiff.props +++ b/build/ApiDiff.props @@ -1,6 +1,6 @@  - 0.10.0-preview3 + 0.10.0-preview6 $(PackageId) Avalonia diff --git a/build/EmbedXaml.props b/build/EmbedXaml.props index 7ce0366dea..0bb8da4f47 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/MicroCom.targets b/build/MicroCom.targets index 3a07950616..b48e377fd4 100644 --- a/build/MicroCom.targets +++ b/build/MicroCom.targets @@ -2,7 +2,7 @@ - + false all true @@ -12,10 +12,10 @@ - + @@ -24,7 +24,7 @@ - + <_AvaloniaPatchComInterop>true diff --git a/build/Rx.props b/build/Rx.props index 8a15ccd6a9..fde1f80ea1 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,5 +1,5 @@  - + diff --git a/build/SharedVersion.props b/build/SharedVersion.props index a5c0aa1bea..43ec995ed9 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -16,7 +16,7 @@ https://github.com/AvaloniaUI/Avalonia/releases git $(MSBuildThisFileDirectory)\avalonia.snk - false + true $(DefineConstants);SIGNED_BUILD diff --git a/native/Avalonia.Native/inc/key.h b/native/Avalonia.Native/inc/key.h deleted file mode 100644 index 12d283cc17..0000000000 --- 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 d5cad4d1ca..dba3ee6d31 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/KeyTransform.h b/native/Avalonia.Native/src/OSX/KeyTransform.h index ea4fbecd5c..2f434570c9 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 ff1bf6b1af..6b7d95b619 100644 --- a/native/Avalonia.Native/src/OSX/KeyTransform.mm +++ b/native/Avalonia.Native/src/OSX/KeyTransform.mm @@ -120,6 +120,138 @@ const int kVK_UpArrow = 0x7E; //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, "." }, + { 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 +369,22 @@ const int kVK_JIS_Kana = 0x68; {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/menu.h b/native/Avalonia.Native/src/OSX/menu.h index 0e43b22846..564fdc132b 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -45,7 +45,7 @@ public: virtual HRESULT SetTitle (char* utf8String) override; - virtual HRESULT SetGesture (char* key, AvnInputModifiers modifiers) override; + virtual HRESULT SetGesture (AvnKey key, AvnInputModifiers modifiers) override; virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 1356388b85..198b01714f 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 { @@ -122,23 +125,57 @@ HRESULT AvnAppMenuItem::SetTitle (char* utf8String) } } -HRESULT AvnAppMenuItem::SetGesture (char* key, AvnInputModifiers modifiers) + +HRESULT AvnAppMenuItem::SetGesture (AvnKey key, AvnInputModifiers modifiers) { @autoreleasepool { - NSEventModifierFlags flags = 0; - - if (modifiers & Control) - flags |= NSEventModifierFlagControl; - if (modifiers & Shift) - flags |= NSEventModifierFlagShift; - if (modifiers & Alt) - flags |= NSEventModifierFlagOption; - if (modifiers & Windows) - flags |= NSEventModifierFlagCommand; - - [_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]]; - [_native setKeyEquivalentModifierMask:flags]; + 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; + } + } + } + } + + // Nothing matched... clear. + [_native setKeyEquivalent: @""]; + [_native setKeyEquivalentModifierMask: 0]; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index f93436d157..e83bf53331 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -101,7 +101,7 @@ public: virtual bool GetCurrentThreadIsLoopThread() override { - return [[NSThread currentThread] isMainThread]; + return [NSThread isMainThread]; } virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override { diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 93a33bbbb0..00b6dab219 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 @@ -143,13 +144,17 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta return _layer; } -- (void)resize:(AvnPixelSize)size withScale: (float) scale;{ +- (void)resize:(AvnPixelSize)size withScale: (float) scale{ @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 +164,15 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta @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 diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index e3996a1fae..8419258fe9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -106,13 +106,13 @@ public: return Window; } - virtual HRESULT Show() override + virtual HRESULT Show(bool activate) override { @autoreleasepool { SetPosition(lastPositionSet); UpdateStyle(); - if(ShouldTakeFocusOnShow()) + if(ShouldTakeFocusOnShow() && activate) { [Window makeKeyAndOrderFront:Window]; [NSApp activateIgnoringOtherApps:YES]; @@ -561,11 +561,11 @@ private: } } - virtual HRESULT Show () override + virtual HRESULT Show (bool activate) override { @autoreleasepool { - WindowBaseImpl::Show(); + WindowBaseImpl::Show(activate); HideOrShowTrafficLights(); diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 97647a1c59..8e331edab4 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -107,7 +107,7 @@ partial class Build : NukeBuild .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,10 +132,10 @@ partial class Build : NukeBuild 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")); }); @@ -157,7 +157,7 @@ partial class Build : NukeBuild { if (Parameters.IsRunningOnWindows) MsBuildCommon(Parameters.MSBuildSolution, c => c - .SetArgumentConfigurator(a => a.Add("/r")) + .SetProcessArgumentConfigurator(a => a.Add("/r")) .AddTargets("Build") ); @@ -194,7 +194,7 @@ partial class Build : NukeBuild var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj"); if (Parameters.IsRunningOnWindows) MsBuildCommon(eventsProject, c => c - .SetArgumentConfigurator(a => a.Add("/r")) + .SetProcessArgumentConfigurator(a => a.Add("/r")) .AddTargets("Build") ); else @@ -242,10 +242,10 @@ partial class Build : NukeBuild var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp"; NpmTasks.NpmInstall(c => c - .SetWorkingDirectory(webappTestDir) - .SetArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessWorkingDirectory(webappTestDir) + .SetProcessArgumentConfigurator(a => a.Add("--silent"))); NpmTasks.NpmRun(c => c - .SetWorkingDirectory(webappTestDir) + .SetProcessWorkingDirectory(webappTestDir) .SetCommand("test")); }); diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 745c727be2..b0380cc92b 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index eb2da03a7e..8a5364c70b 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace BindingDemo { @@ -25,7 +24,6 @@ namespace BindingDemo public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); } } diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index 817023fd71..d898b737a9 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 0fe12a8ef7..7de083351e 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 caf75c846c..9ae8d9558f 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 f0241cad48..18a7a01a69 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -5,14 +5,14 @@ using System.Reactive; 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 @@ namespace BindingDemo.ViewModels 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 @@ namespace BindingDemo.ViewModels public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public ReactiveCommand ShuffleItems { get; } + public MiniCommand ShuffleItems { get; } public string BooleanString { @@ -93,7 +93,7 @@ namespace BindingDemo.ViewModels } 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 0e9139ab98..1c2222b0b0 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 5a9f192f58..2f49a3c99f 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.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 5af646b180..7b8b27fff7 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -3,7 +3,6 @@ using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.ReactiveUI; namespace ControlCatalog { @@ -19,8 +18,7 @@ namespace ControlCatalog public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .LogToTrace() - .UsePlatformDetect() - .UseReactiveUI(); + .UsePlatformDetect(); private static void ConfigureAssetAssembly(AppBuilder builder) { diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 675ea2e10f..1dc8c09c0e 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -10,7 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Dialogs; using Avalonia.Headless; using Avalonia.LogicalTree; -using Avalonia.ReactiveUI; using Avalonia.Threading; namespace ControlCatalog.NetCore @@ -118,7 +117,6 @@ namespace ControlCatalog.NetCore AllowEglInitialization = true }) .UseSkia() - .UseReactiveUI() .UseManagedSystemDialogs() .LogToTrace(); diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 22f4e9be1f..020fb2fff3 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -23,7 +23,7 @@ namespace ControlCatalog { new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml") + Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml") }, DataGridFluent }; @@ -32,7 +32,7 @@ namespace ControlCatalog { new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml") + Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml") }, DataGridFluent }; diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 8a88b89b48..1aa926a2a6 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -24,8 +24,8 @@ - + diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml index 8e4c97b7f0..5251a2fa55 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml +++ b/samples/ControlCatalog/DecoratedWindow.xaml @@ -6,25 +6,21 @@ - - - - - - - - - - - + + + + + + + + + - - - - - - + + + + diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 97bd88f5e4..6a70bb082f 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -16,47 +16,39 @@ - - - - - - - - - - - - - + + + + + + + + + - - - - - - + + + + - - - - - - - + + + + + diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index a316321cd1..723351ae57 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -67,7 +67,7 @@ namespace ControlCatalog if (Application.Current.Styles.Contains(App.FluentDark) || Application.Current.Styles.Contains(App.FluentLight)) { - var theme = new Avalonia.Themes.Fluent.FluentTheme(); + var theme = new Avalonia.Themes.Fluent.Controls.FluentControls(); theme.TryGetResource("Button", out _); } else diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index cf6c771e34..49921fb7f6 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Dialogs; +using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 @@ -112,11 +113,29 @@ namespace ControlCatalog.Pages 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/LabelsPage.axaml.cs b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs index b8503d6ae6..a14978fd2c 100644 --- a/samples/ControlCatalog/Pages/LabelsPage.axaml.cs +++ b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.Models; -using ReactiveUI; namespace ControlCatalog.Pages { diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index 3521ad71a9..f515db84d4 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 46dbe3dcad..5999510b6c 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs @@ -6,7 +6,6 @@ using System.Windows.Input; using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; -using ReactiveUI; namespace ControlCatalog.Pages { diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs index 92da64d87e..31749edf08 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -26,7 +26,7 @@ namespace ControlCatalog.Pages } - public class NumbersPageViewModel : ReactiveObject + public class NumbersPageViewModel : ViewModelBase { private IList _formats; private FormatObject _selectedFormat; diff --git a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs index 36d3768b13..dcd7a88a56 100644 --- a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; 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/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs index a38a3ab4cb..f49b13091b 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -56,7 +56,7 @@ namespace ControlCatalog.Pages return new Bitmap(assets.Open(new Uri(uri))); } - private class PageViewModel : ReactiveObject + private class PageViewModel : ViewModelBase { private Dock _tabPlacement; diff --git a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs index 5c2f74d2d5..3f5d0cd93c 100644 --- a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs @@ -3,7 +3,7 @@ using System.Reactive; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -12,9 +12,9 @@ namespace ControlCatalog.ViewModels public Control View { get; set; } public ContextMenuPageViewModel() { - 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 @@ namespace ControlCatalog.ViewModels } 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/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs index f893a6e28e..ee1fa6ae77 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 @@ namespace ControlCatalog.ViewModels })); } - 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 f75bc32105..7f2d6e9572 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -4,18 +4,18 @@ using System.Linq; 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 @@ namespace ControlCatalog.ViewModels 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 @@ namespace ControlCatalog.ViewModels } }); - SelectRandomItemCommand = ReactiveCommand.Create(() => + SelectRandomItemCommand = MiniCommand.Create(() => { var random = new Random(); @@ -60,7 +59,7 @@ namespace ControlCatalog.ViewModels public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public SelectionMode SelectionMode => _selectionMode.Value; + public IObservable SelectionMode => _selectionMode; public bool Multiple { @@ -86,9 +85,9 @@ namespace ControlCatalog.ViewModels 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 4356a032fa..4b3cfa9c9d 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -5,11 +5,11 @@ using Avalonia.Controls.Notifications; 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 @@ namespace ControlCatalog.ViewModels { _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 @@ namespace ControlCatalog.ViewModels 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 @@ namespace ControlCatalog.ViewModels 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 9e7ae8b716..ecbd59c5d7 100644 --- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs @@ -4,7 +4,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -13,9 +13,9 @@ namespace ControlCatalog.ViewModels 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 @@ namespace ControlCatalog.ViewModels 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 8724ba344b..2052481015 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 @@ namespace ControlCatalog.ViewModels { 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 @@ namespace ControlCatalog.ViewModels 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 f27f605a8b..9e6932bb76 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 210e281ed6..c03379330f 100644 --- a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs @@ -3,11 +3,11 @@ using System.Collections.ObjectModel; 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 @@ namespace ControlCatalog.ViewModels 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 0000000000..c6a9273c20 --- /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 0000000000..6535b2bdbd --- /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 0000000000..f1065c7530 --- /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 0000000000..7256b05cef --- /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/Previewer.csproj b/samples/Previewer/Previewer.csproj index cd3daf61e1..cfedb7ad9e 100644 --- a/samples/Previewer/Previewer.csproj +++ b/samples/Previewer/Previewer.csproj @@ -8,7 +8,6 @@ %(Filename) - diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml index 61e4d2385b..7cdcea2e1d 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 e6ec963d75..8054b06964 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace RenderDemo { @@ -32,7 +31,6 @@ namespace RenderDemo OverlayPopups = true, }) .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); } } diff --git a/samples/RenderDemo/MainWindow.xaml.cs b/samples/RenderDemo/MainWindow.xaml.cs index b45a605e04..877eb8016a 100644 --- a/samples/RenderDemo/MainWindow.xaml.cs +++ b/samples/RenderDemo/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using RenderDemo.ViewModels; -using ReactiveUI; +using MiniMvvm; namespace RenderDemo { diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index d1654f4b54..0d33b4c111 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -9,12 +9,11 @@ - + - diff --git a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs index 7b89b7944c..f8ba01f3d1 100644 --- a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs @@ -1,10 +1,10 @@ using System; -using ReactiveUI; +using MiniMvvm; using Avalonia.Animation; namespace RenderDemo.ViewModels { - public class AnimationsPageViewModel : ReactiveObject + public class AnimationsPageViewModel : ViewModelBase { private bool _isPlaying = true; diff --git a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs index eda5e80530..19917c20df 100644 --- a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs @@ -1,11 +1,10 @@ using System.Reactive; using System.Threading.Tasks; - -using ReactiveUI; +using MiniMvvm; namespace RenderDemo.ViewModels { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private bool drawDirtyRects = false; private bool drawFps = true; @@ -14,9 +13,9 @@ namespace RenderDemo.ViewModels public MainWindowViewModel() { - ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); - ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps); - ResizeWindow = ReactiveCommand.CreateFromTask(ResizeWindowAsync); + ToggleDrawDirtyRects = MiniCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); + ToggleDrawFps = MiniCommand.Create(() => DrawFps = !DrawFps); + ResizeWindow = MiniCommand.CreateFromTask(ResizeWindowAsync); } public bool DrawDirtyRects @@ -43,9 +42,9 @@ namespace RenderDemo.ViewModels set => this.RaiseAndSetIfChanged(ref height, value); } - public ReactiveCommand ToggleDrawDirtyRects { get; } - public ReactiveCommand ToggleDrawFps { get; } - public ReactiveCommand ResizeWindow { get; } + public MiniCommand ToggleDrawDirtyRects { get; } + public MiniCommand ToggleDrawFps { get; } + public MiniCommand ResizeWindow { get; } private async Task ResizeWindowAsync() { diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml index 699781eb94..f601f9f78f 100644 --- a/samples/Sandbox/App.axaml +++ b/samples/Sandbox/App.axaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sandbox.App"> - + diff --git a/samples/Sandbox/MainWindow.axaml.cs b/samples/Sandbox/MainWindow.axaml.cs index b7222e043d..3d54036d29 100644 --- a/samples/Sandbox/MainWindow.axaml.cs +++ b/samples/Sandbox/MainWindow.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Win32.WinRT.Composition; namespace Sandbox { diff --git a/samples/Sandbox/Program.cs b/samples/Sandbox/Program.cs index 7d41a8b8c0..1e74105196 100644 --- a/samples/Sandbox/Program.cs +++ b/samples/Sandbox/Program.cs @@ -1,5 +1,4 @@ using Avalonia; -using Avalonia.ReactiveUI; namespace Sandbox { @@ -9,7 +8,6 @@ namespace Sandbox { AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace() .StartWithClassicDesktopLifetime(args); } diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj index 1a0a8a7ce5..0c19440a1e 100644 --- a/samples/Sandbox/Sandbox.csproj +++ b/samples/Sandbox/Sandbox.csproj @@ -8,7 +8,6 @@ - diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs index 46c05f74b2..febda46450 100644 --- a/samples/VirtualizationDemo/Program.cs +++ b/samples/VirtualizationDemo/Program.cs @@ -1,5 +1,4 @@ using Avalonia; -using Avalonia.ReactiveUI; namespace VirtualizationDemo { @@ -8,7 +7,6 @@ namespace VirtualizationDemo public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); public static int Main(string[] args) diff --git a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs b/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs index cf34980b40..9ba505ffe5 100644 --- a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs @@ -1,9 +1,9 @@ using System; -using ReactiveUI; +using MiniMvvm; namespace VirtualizationDemo.ViewModels { - internal class ItemViewModel : ReactiveObject + internal class ItemViewModel : ViewModelBase { private string _prefix; private int _index; diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs index 852c01399f..514df691ae 100644 --- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs @@ -5,13 +5,13 @@ using System.Reactive; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Primitives; -using ReactiveUI; using Avalonia.Layout; using Avalonia.Controls.Selection; +using MiniMvvm; namespace VirtualizationDemo.ViewModels { - internal class MainWindowViewModel : ReactiveObject + internal class MainWindowViewModel : ViewModelBase { private int _itemCount = 200; private string _newItemString = "New Item"; @@ -26,15 +26,15 @@ namespace VirtualizationDemo.ViewModels public MainWindowViewModel() { this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems); - RecreateCommand = ReactiveCommand.Create(() => Recreate()); + RecreateCommand = MiniCommand.Create(() => Recreate()); - AddItemCommand = ReactiveCommand.Create(() => AddItem()); + AddItemCommand = MiniCommand.Create(() => AddItem()); - RemoveItemCommand = ReactiveCommand.Create(() => Remove()); + RemoveItemCommand = MiniCommand.Create(() => Remove()); - SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0)); + SelectFirstCommand = MiniCommand.Create(() => SelectItem(0)); - SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1)); + SelectLastCommand = MiniCommand.Create(() => SelectItem(Items.Count - 1)); } public string NewItemString @@ -90,11 +90,11 @@ namespace VirtualizationDemo.ViewModels public IEnumerable VirtualizationModes => Enum.GetValues(typeof(ItemVirtualizationMode)).Cast(); - public ReactiveCommand AddItemCommand { get; private set; } - public ReactiveCommand RecreateCommand { get; private set; } - public ReactiveCommand RemoveItemCommand { get; private set; } - public ReactiveCommand SelectFirstCommand { get; private set; } - public ReactiveCommand SelectLastCommand { get; private set; } + public MiniCommand AddItemCommand { get; private set; } + public MiniCommand RecreateCommand { get; private set; } + public MiniCommand RemoveItemCommand { get; private set; } + public MiniCommand SelectFirstCommand { get; private set; } + public MiniCommand SelectLastCommand { get; private set; } public void RandomizeSize() { diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj index 817023fd71..d898b737a9 100644 --- a/samples/VirtualizationDemo/VirtualizationDemo.csproj +++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj @@ -6,12 +6,11 @@ - + - diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj index bd6b6f170f..cd9963a2e5 100644 --- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj +++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj @@ -22,9 +22,9 @@ - + diff --git a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs index d39a21cd07..21679a99c5 100644 --- a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs +++ b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using ReactiveUI; +using MiniMvvm; namespace Direct3DInteropSample { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private double _rotationX; diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index c067d38595..8394d7cb13 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -136,10 +136,6 @@ {42472427-4774-4c81-8aff-9f27b8e31721} Avalonia.Layout - - {6417b24e-49c2-4985-8db2-3ab9d898ec91} - Avalonia.ReactiveUI - {eb582467-6abb-43a1-b052-e981ba910e3a} Avalonia.Visuals @@ -190,4 +186,4 @@ - \ No newline at end of file + diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index b8697e0ca2..f880e48282 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -127,10 +127,6 @@ {42472427-4774-4c81-8aff-9f27b8e31721} Avalonia.Layout - - {6417b24e-49c2-4985-8db2-3ab9d898ec91} - Avalonia.ReactiveUI - {eb582467-6abb-43a1-b052-e981ba910e3a} Avalonia.Visuals diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt index 5d19373a92..d17f25ec49 100644 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -1,4 +1,4 @@ Compat issues with assembly Avalonia.Base: -CannotAddAbstractMembers : Member 'protected System.IObservable Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract. -TypesMustExist : Type 'Avalonia.Logging.DebugLogSink' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Threading.AvaloniaSynchronizationContext..ctor(Avalonia.Threading.AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider)' does not exist in the implementation but it does exist in the contract. +TypesMustExist : Type 'Avalonia.Threading.AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider' does not exist in the implementation but it does exist in the contract. Total Issues: 2 diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index d43b4e04bb..5681214222 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -63,6 +63,15 @@ namespace Avalonia.Collections _inner = new List(); } + /// + /// Initializes a new instance of the . + /// + /// Initial list capacity. + public AvaloniaList(int capacity) + { + _inner = new List(capacity); + } + /// /// Initializes a new instance of the class. /// @@ -175,6 +184,15 @@ namespace Avalonia.Collections set { this[index] = (T)value; } } + /// + /// Gets or sets the total number of elements the internal data structure can hold without resizing. + /// + public int Capacity + { + get => _inner.Capacity; + set => _inner.Capacity = value; + } + /// /// Adds an item to the collection. /// diff --git a/src/Avalonia.Base/Data/Core/TypeCastNode.cs b/src/Avalonia.Base/Data/Core/TypeCastNode.cs new file mode 100644 index 0000000000..476fd5527f --- /dev/null +++ b/src/Avalonia.Base/Data/Core/TypeCastNode.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Avalonia.Data.Core +{ + public class TypeCastNode : ExpressionNode + { + public override string Description => $"as {TargetType.FullName}"; + + public Type TargetType { get; } + + public TypeCastNode(Type type) + { + TargetType = type; + } + + protected virtual object Cast(object value) + { + return TargetType.IsInstanceOfType(value) ? value : null; + } + + protected override void StartListeningCore(WeakReference reference) + { + if (reference.TryGetTarget(out object target)) + { + target = Cast(target); + reference = target == null ? NullReference : new WeakReference(target); + } + + base.StartListeningCore(reference); + } + } +} diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index e06cb75a4d..b054c186ae 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -11,10 +11,12 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Visuals, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] #else [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests")] [assembly: InternalsVisibleTo("Avalonia.UnitTests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests")] +[assembly: InternalsVisibleTo("Avalonia.Visuals")] #endif diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 40cf81358f..1a78792173 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -9,20 +9,6 @@ namespace Avalonia.Threading /// public class AvaloniaSynchronizationContext : SynchronizationContext { - public interface INonPumpingPlatformWaitProvider - { - int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout); - } - - private readonly INonPumpingPlatformWaitProvider _waitProvider; - - public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider) - { - _waitProvider = waitProvider; - if (_waitProvider != null) - SetWaitNotificationRequired(); - } - /// /// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer. /// @@ -38,8 +24,7 @@ namespace Avalonia.Threading return; } - SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current - .GetService())); + SetSynchronizationContext(new AvaloniaSynchronizationContext()); } /// @@ -57,12 +42,6 @@ namespace Avalonia.Threading Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait(); } - [PrePrepareMethod] - public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) - { - if (_waitProvider != null) - return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout); - return base.Wait(waitHandles, waitAll, millisecondsTimeout); - } + } } diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs index 0238446892..6e52b6770a 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +#nullable enable namespace Avalonia.Utilities { @@ -9,12 +12,14 @@ namespace Avalonia.Utilities /// Stored value type. internal sealed class AvaloniaPropertyValueStore { + // The last item in the list is always int.MaxValue. + private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } }; + private Entry[] _entries; public AvaloniaPropertyValueStore() { - // The last item in the list is always int.MaxValue - _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } }; + _entries = s_emptyEntries; } private (int, bool) TryFindEntry(int propertyId) @@ -86,7 +91,7 @@ namespace Avalonia.Utilities return (0, false); } - public bool TryGetValue(AvaloniaProperty property, out TValue value) + public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value) { (int index, bool found) = TryFindEntry(property.Id); if (!found) @@ -132,7 +137,18 @@ namespace Avalonia.Utilities if (found) { - Entry[] entries = new Entry[_entries.Length - 1]; + var newLength = _entries.Length - 1; + + // Special case - one element left means that value store is empty so we can just reuse our "empty" array. + if (newLength == 1) + { + _entries = s_emptyEntries; + + return; + } + + var entries = new Entry[newLength]; + int ix = 0; for (int i = 0; i < _entries.Length; ++i) diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs index 2a92e75f58..446b366dc8 100644 --- a/src/Avalonia.Base/Utilities/MathUtilities.cs +++ b/src/Avalonia.Base/Utilities/MathUtilities.cs @@ -5,7 +5,10 @@ namespace Avalonia.Utilities /// /// Provides math utilities not provided in System.Math. /// - public static class MathUtilities +#if !BUILDTASK + public +#endif + static class MathUtilities { // smallest such that 1.0+DoubleEpsilon != 1.0 internal static readonly double DoubleEpsilon = 2.2204460492503131e-016; diff --git a/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs new file mode 100644 index 0000000000..fd4e5d2ace --- /dev/null +++ b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs @@ -0,0 +1,14 @@ +using System; + +namespace Avalonia.Utilities +{ + public class NonPumpingLockHelper + { + public interface IHelperImpl + { + IDisposable Use(); + } + + public static IDisposable Use() => AvaloniaLocator.Current.GetService()?.Use(); + } +} diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs index a159bfb72e..24e99febb9 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -4,7 +4,10 @@ using static System.Char; namespace Avalonia.Utilities { - public struct StringTokenizer : IDisposable +#if !BUILDTASK + public +#endif + struct StringTokenizer : IDisposable { private const char DefaultSeparatorChar = ','; diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 94ad4adb7d..90f6abc873 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -1,7 +1,7 @@  netstandard2.0 - netstandard2.0;netcoreapp2.0 + netstandard2.0;netcoreapp3.1 exe false tools @@ -45,6 +45,12 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + Markup/%(RecursiveDir)%(FileName)%(Extension) @@ -57,6 +63,33 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index d5c406293e..f8960f56ec 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -1,3 +1,4 @@ +#if !NETCOREAPP3_1 namespace System { // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory @@ -63,6 +64,8 @@ namespace System } public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); + + public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr)); } static class SpanCompatExtensions @@ -71,3 +74,4 @@ namespace System } } +#endif diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 0b9b50e771..6ef8a98fae 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -46,7 +46,9 @@ namespace Avalonia.Build.Tasks string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation) { - var typeSystem = new CecilTypeSystem(references.Concat(new[] { input }), input); + var typeSystem = new CecilTypeSystem(references + .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) + .Concat(new[] { input }), input); var asm = typeSystem.TargetAssemblyDefinition; diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt index 82472c505a..fcc74cf864 100644 --- a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt @@ -1,5 +1 @@ -Compat issues with assembly Avalonia.Controls.DataGrid: -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.DataGridTextColumn.FontFamilyProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.String Avalonia.Controls.DataGridTextColumn.FontFamily.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.DataGridTextColumn.FontFamily.set(System.String)' does not exist in the implementation but it does exist in the contract. -Total Issues: 3 +Total Issues: 0 diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index cf062e0920..09d19c8e43 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -137,12 +137,37 @@ + diff --git a/src/Avalonia.Themes.Default/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/ToggleSwitch.xaml index 9d1c024eb9..f11f77df5a 100644 --- a/src/Avalonia.Themes.Default/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Default/ToggleSwitch.xaml @@ -5,7 +5,7 @@ 0,0,0,6 6 6 - 154 + 0 0 1 diff --git a/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt new file mode 100644 index 0000000000..44e250b889 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt @@ -0,0 +1,4 @@ +Compat issues with assembly Avalonia.Themes.Fluent: +CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Themes.Fluent.FluentTheme' does not inherit from base type 'Avalonia.Styling.Styles' in the implementation but it does in the contract. +MembersMustExist : Member 'public void Avalonia.Themes.Fluent.FluentTheme..ctor()' does not exist in the implementation but it does exist in the contract. +Total Issues: 2 diff --git a/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml diff --git a/src/Avalonia.Themes.Fluent/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Button.xaml rename to src/Avalonia.Themes.Fluent/Controls/Button.xaml diff --git a/src/Avalonia.Themes.Fluent/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ButtonSpinner.xaml rename to src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml diff --git a/src/Avalonia.Themes.Fluent/Calendar.xaml b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Calendar.xaml rename to src/Avalonia.Themes.Fluent/Controls/Calendar.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarDayButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarDayButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml diff --git a/src/Avalonia.Themes.Fluent/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml similarity index 91% rename from src/Avalonia.Themes.Fluent/CaptionButtons.xaml rename to src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml index f288372b77..1f8887c283 100644 --- a/src/Avalonia.Themes.Fluent/CaptionButtons.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml @@ -3,7 +3,7 @@ - + + diff --git a/src/Avalonia.Themes.Fluent/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/PopupRoot.xaml rename to src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml diff --git a/src/Avalonia.Themes.Fluent/ProgressBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ProgressBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml diff --git a/src/Avalonia.Themes.Fluent/RadioButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/RadioButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml diff --git a/src/Avalonia.Themes.Fluent/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/RepeatButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml diff --git a/src/Avalonia.Themes.Fluent/ScrollBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ScrollBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml diff --git a/src/Avalonia.Themes.Fluent/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ScrollViewer.xaml rename to src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml diff --git a/src/Avalonia.Themes.Fluent/Separator.xaml b/src/Avalonia.Themes.Fluent/Controls/Separator.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Separator.xaml rename to src/Avalonia.Themes.Fluent/Controls/Separator.xaml diff --git a/src/Avalonia.Themes.Fluent/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Slider.xaml rename to src/Avalonia.Themes.Fluent/Controls/Slider.xaml diff --git a/src/Avalonia.Themes.Fluent/SplitView.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/SplitView.xaml rename to src/Avalonia.Themes.Fluent/Controls/SplitView.xaml diff --git a/src/Avalonia.Themes.Fluent/TabControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabControl.xaml diff --git a/src/Avalonia.Themes.Fluent/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabItem.xaml diff --git a/src/Avalonia.Themes.Fluent/TabStrip.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabStrip.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml diff --git a/src/Avalonia.Themes.Fluent/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabStripItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml diff --git a/src/Avalonia.Themes.Fluent/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TextBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/TextBox.xaml diff --git a/src/Avalonia.Themes.Fluent/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TimePicker.xaml rename to src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml diff --git a/src/Avalonia.Themes.Fluent/TitleBar.xaml b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TitleBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml diff --git a/src/Avalonia.Themes.Fluent/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ToggleButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml diff --git a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml similarity index 99% rename from src/Avalonia.Themes.Fluent/ToggleSwitch.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml index 2491225a44..45537e41ad 100644 --- a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml @@ -5,7 +5,7 @@ 0,0,0,6 6 6 - 154 + 0 diff --git a/src/Avalonia.Themes.Fluent/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ToolTip.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml diff --git a/src/Avalonia.Themes.Fluent/TreeView.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TreeView.xaml rename to src/Avalonia.Themes.Fluent/Controls/TreeView.xaml diff --git a/src/Avalonia.Themes.Fluent/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TreeViewItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml diff --git a/src/Avalonia.Themes.Fluent/UserControl.xaml b/src/Avalonia.Themes.Fluent/Controls/UserControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/UserControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/UserControl.xaml diff --git a/src/Avalonia.Themes.Fluent/Window.xaml b/src/Avalonia.Themes.Fluent/Controls/Window.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Window.xaml rename to src/Avalonia.Themes.Fluent/Controls/Window.xaml diff --git a/src/Avalonia.Themes.Fluent/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/WindowNotificationManager.xaml rename to src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml similarity index 85% rename from src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml rename to src/Avalonia.Themes.Fluent/FluentDark.xaml index 9ef92a44d5..74b583a240 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml +++ b/src/Avalonia.Themes.Fluent/FluentDark.xaml @@ -5,5 +5,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml similarity index 85% rename from src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml rename to src/Avalonia.Themes.Fluent/FluentLight.xaml index 8c92040122..1bc51f655e 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml +++ b/src/Avalonia.Themes.Fluent/FluentLight.xaml @@ -5,5 +5,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs new file mode 100644 index 0000000000..43b71567fa --- /dev/null +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +#nullable enable + +namespace Avalonia.Themes.Fluent +{ + public enum FluentThemeMode + { + Light, + Dark, + } + + /// + /// Includes the fluent theme in an application. + /// + public class FluentTheme : IStyle, IResourceProvider + { + private readonly Uri _baseUri; + private IStyle[]? _loaded; + private bool _isLoading; + + /// + /// Initializes a new instance of the class. + /// + /// The base URL for the XAML context. + public FluentTheme(Uri baseUri) + { + _baseUri = baseUri; + } + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public FluentTheme(IServiceProvider serviceProvider) + { + _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + } + + /// + /// Gets or sets the mode of the fluent theme (light, dark). + /// + public FluentThemeMode Mode { get; set; } + + public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; + + /// + /// Gets the loaded style. + /// + public IStyle Loaded + { + get + { + if (_loaded == null) + { + _isLoading = true; + var loaded = (IStyle)AvaloniaXamlLoader.Load(GetUri(), _baseUri); + _loaded = new[] { loaded }; + _isLoading = false; + } + + return _loaded?[0]!; + } + } + + bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + + IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); + + public event EventHandler OwnerChanged + { + add + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged += value; + } + } + remove + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged -= value; + } + } + } + + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + + public bool TryGetResource(object key, out object? value) + { + if (!_isLoading && Loaded is IResourceProvider p) + { + return p.TryGetResource(key, out value); + } + + value = null; + return false; + } + + void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); + void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); + + private Uri GetUri() => Mode switch + { + FluentThemeMode.Dark => new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml", UriKind.Absolute), + _ => new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml", UriKind.Absolute), + }; + } +} diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml deleted file mode 100644 index 4e0e4fe862..0000000000 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs b/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0bad226ba7 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Avalonia.Metadata; + +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Themes.Fluent")] diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 2d00d82a46..62f4def701 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,39 +1,5 @@ Compat issues with assembly Avalonia.Visuals: -MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Boolean Avalonia.Media.Geometry.StrokeContains(Avalonia.Media.Pen, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract. -TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IDrawingContextLayerImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.IRef Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 37 +MembersMustExist : Member 'public Avalonia.StyledProperty> Avalonia.StyledProperty> Avalonia.Media.DashStyle.DashesProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.DashStyle.Dashes.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.DashStyle.Dashes.set(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. +Total Issues: 3 diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Visuals/CornerRadius.cs index c02aacfb4d..037bb16e31 100644 --- a/src/Avalonia.Visuals/CornerRadius.cs +++ b/src/Avalonia.Visuals/CornerRadius.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Represents the radii of a rectangle's corners. /// - public readonly struct CornerRadius : IEquatable +#if !BUILDTASK + public +#endif + readonly struct CornerRadius : IEquatable { static CornerRadius() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(CornerRadius).IsAssignableFrom(prop.PropertyType)); +#endif } public CornerRadius(double uniformRadius) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 206b842220..8136f843df 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -7,7 +7,10 @@ namespace Avalonia /// /// A 2x3 matrix. /// - public readonly struct Matrix : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Matrix : IEquatable { private readonly double _m11; private readonly double _m12; @@ -279,25 +282,44 @@ namespace Avalonia } /// - /// Inverts the Matrix. + /// Attempts to invert the Matrix. /// - /// The inverted matrix. - public Matrix Invert() + /// The inverted matrix or when matrix is not invertible. + public bool TryInvert(out Matrix inverted) { double d = GetDeterminant(); if (MathUtilities.IsZero(d)) { - throw new InvalidOperationException("Transform is not invertible."); + inverted = default; + + return false; } - return new Matrix( + inverted = new Matrix( _m22 / d, -_m12 / d, -_m21 / d, _m11 / d, ((_m21 * _m32) - (_m22 * _m31)) / d, ((_m12 * _m31) - (_m11 * _m32)) / d); + + return true; + } + + /// + /// Inverts the Matrix. + /// + /// Matrix is not invertible. + /// The inverted matrix. + public Matrix Invert() + { + if (!TryInvert(out Matrix inverted)) + { + throw new InvalidOperationException("Transform is not invertible."); + } + + return inverted; } /// diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 16b4f90d57..a57a962db4 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -1,17 +1,24 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif namespace Avalonia.Media { /// /// An ARGB color. /// - public readonly struct Color : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Color : IEquatable { static Color() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)); +#endif } /// @@ -223,7 +230,12 @@ namespace Avalonia.Media if (input.Length == 3 || input.Length == 4) { var extendedLength = 2 * input.Length; + +#if !BUILDTASK Span extended = stackalloc char[extendedLength]; +#else + char[] extended = new char[extendedLength]; +#endif for (int i = 0; i < input.Length; i++) { diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Visuals/Media/DashStyle.cs index 1e813edc13..acba2d3615 100644 --- a/src/Avalonia.Visuals/Media/DashStyle.cs +++ b/src/Avalonia.Visuals/Media/DashStyle.cs @@ -1,11 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using Avalonia.Animation; +using Avalonia.Collections; +using Avalonia.Media.Immutable; + +#nullable enable + namespace Avalonia.Media { - using System; - using System.Collections.Generic; - using System.Linq; - using Avalonia.Animation; - using Avalonia.Media.Immutable; - /// /// Represents the sequence of dashes and gaps that will be applied by a . /// @@ -14,8 +17,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty> DashesProperty = - AvaloniaProperty.Register>(nameof(Dashes)); + public static readonly StyledProperty> DashesProperty = + AvaloniaProperty.Register>(nameof(Dashes)); /// /// Defines the property. @@ -23,10 +26,10 @@ namespace Avalonia.Media public static readonly StyledProperty OffsetProperty = AvaloniaProperty.Register(nameof(Offset)); - private static ImmutableDashStyle s_dash; - private static ImmutableDashStyle s_dot; - private static ImmutableDashStyle s_dashDot; - private static ImmutableDashStyle s_dashDotDot; + private static ImmutableDashStyle? s_dash; + private static ImmutableDashStyle? s_dot; + private static ImmutableDashStyle? s_dashDot; + private static ImmutableDashStyle? s_dashDotDot; /// /// Initializes a new instance of the class. @@ -41,9 +44,9 @@ namespace Avalonia.Media /// /// The dashes collection. /// The dash sequence offset. - public DashStyle(IEnumerable dashes, double offset) + public DashStyle(IEnumerable? dashes, double offset) { - Dashes = (IReadOnlyList)dashes?.ToList() ?? Array.Empty(); + Dashes = (dashes as AvaloniaList) ?? new AvaloniaList(dashes ?? Array.Empty()); Offset = offset; } @@ -61,31 +64,27 @@ namespace Avalonia.Media /// /// Represents a dashed . /// - public static IDashStyle Dash => - s_dash ?? (s_dash = new ImmutableDashStyle(new double[] { 2, 2 }, 1)); + public static IDashStyle Dash => s_dash ??= new ImmutableDashStyle(new double[] { 2, 2 }, 1); /// /// Represents a dotted . /// - public static IDashStyle Dot => - s_dot ?? (s_dot = new ImmutableDashStyle(new double[] { 0, 2 }, 0)); + public static IDashStyle Dot => s_dot ??= new ImmutableDashStyle(new double[] { 0, 2 }, 0); /// /// Represents a dashed dotted . /// - public static IDashStyle DashDot => - s_dashDot ?? (s_dashDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1)); + public static IDashStyle DashDot => s_dashDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1); /// /// Represents a dashed double dotted . /// - public static IDashStyle DashDotDot => - s_dashDotDot ?? (s_dashDotDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1)); + public static IDashStyle DashDotDot => s_dashDotDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1); /// /// Gets or sets the length of alternating dashes and gaps. /// - public IReadOnlyList Dashes + public AvaloniaList Dashes { get => GetValue(DashesProperty); set => SetValue(DashesProperty, value); @@ -100,15 +99,43 @@ namespace Avalonia.Media set => SetValue(OffsetProperty, value); } + IReadOnlyList IDashStyle.Dashes => Dashes; + /// /// Raised when the dash style changes. /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; /// /// Returns an immutable clone of the . /// /// public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == DashesProperty) + { + var oldValue = change.OldValue.GetValueOrDefault>(); + var newValue = change.NewValue.GetValueOrDefault>(); + + if (oldValue is object) + { + oldValue.CollectionChanged -= DashesChanged; + } + + if (newValue is object) + { + newValue.CollectionChanged += DashesChanged; + } + } + } + + private void DashesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + Invalidated?.Invoke(this, e); + } } } diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs index fd73eee69a..bae6dc3d8c 100644 --- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs +++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs @@ -12,10 +12,28 @@ namespace Avalonia.Media /// public static readonly StyledProperty RectProperty = AvaloniaProperty.Register(nameof(Rect)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusXProperty = + AvaloniaProperty.Register(nameof(RadiusX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusYProperty = + AvaloniaProperty.Register(nameof(RadiusY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterProperty = + AvaloniaProperty.Register(nameof(Center)); static EllipseGeometry() { - AffectsGeometry(RectProperty); + AffectsGeometry(RectProperty, RadiusXProperty, RadiusYProperty, CenterProperty); } /// @@ -43,6 +61,33 @@ namespace Avalonia.Media set => SetValue(RectProperty, value); } + /// + /// Gets or sets a double that defines the radius in the X-axis of the ellipse. + /// + public double RadiusX + { + get => GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// Gets or sets a double that defines the radius in the Y-axis of the ellipse. + /// + public double RadiusY + { + get => GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Gets or sets a point that defines the center of the ellipse. + /// + public Point Center + { + get => GetValue(CenterProperty); + set => SetValue(CenterProperty, value); + } + /// public override Geometry Clone() { @@ -54,7 +99,14 @@ namespace Avalonia.Media { var factory = AvaloniaLocator.Current.GetService(); - return factory.CreateEllipseGeometry(Rect); + if (Rect != default) return factory.CreateEllipseGeometry(Rect); + + var originX = Center.X - RadiusX; + var originY = Center.Y - RadiusY; + var width = RadiusX * 2; + var height = RadiusY * 2; + + return factory.CreateEllipseGeometry(new Rect(originX, originY, width, height)); } } } diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Visuals/Media/KnownColors.cs index 0887d2c913..fe09f5f538 100644 --- a/src/Avalonia.Visuals/Media/KnownColors.cs +++ b/src/Avalonia.Visuals/Media/KnownColors.cs @@ -8,7 +8,9 @@ namespace Avalonia.Media { private static readonly IReadOnlyDictionary _knownColorNames; private static readonly IReadOnlyDictionary _knownColors; +#if !BUILDTASK private static readonly Dictionary _knownBrushes; +#endif static KnownColors() { @@ -32,14 +34,19 @@ namespace Avalonia.Media _knownColorNames = knownColorNames; _knownColors = knownColors; + +#if !BUILDTASK _knownBrushes = new Dictionary(); +#endif } +#if !BUILDTASK public static ISolidColorBrush GetKnownBrush(string s) { var color = GetKnownColor(s); return color != KnownColor.None ? color.ToBrush() : null; } +#endif public static KnownColor GetKnownColor(string s) { @@ -61,6 +68,7 @@ namespace Avalonia.Media return Color.FromUInt32((uint)color); } +#if !BUILDTASK public static ISolidColorBrush ToBrush(this KnownColor color) { lock (_knownBrushes) @@ -74,6 +82,7 @@ namespace Avalonia.Media return brush; } } +#endif } internal enum KnownColor : uint diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index d0eb67ba39..caf86cb234 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -1,3 +1,7 @@ +#nullable enable +using System; +using System.Linq; +using Avalonia.Collections; using Avalonia.Metadata; namespace Avalonia.Media @@ -8,22 +12,36 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty IsClosedProperty - = AvaloniaProperty.Register(nameof(IsClosed), true); + = AvaloniaProperty.Register(nameof(IsClosed), true); + /// /// Defines the property. /// public static readonly StyledProperty IsFilledProperty - = AvaloniaProperty.Register(nameof(IsFilled), true); + = AvaloniaProperty.Register(nameof(IsFilled), true); + /// /// Defines the property. /// - public static readonly DirectProperty SegmentsProperty - = AvaloniaProperty.RegisterDirect(nameof(Segments), f => f.Segments, (f, s) => f.Segments = s); + public static readonly DirectProperty SegmentsProperty + = AvaloniaProperty.RegisterDirect( + nameof(Segments), + f => f.Segments, + (f, s) => f.Segments = s); + /// /// Defines the property. /// public static readonly StyledProperty StartPointProperty - = AvaloniaProperty.Register(nameof(StartPoint)); + = AvaloniaProperty.Register(nameof(StartPoint)); + + internal event EventHandler? SegmentsInvalidated; + + private PathSegments? _segments; + + private IDisposable? _segmentsDisposable; + + private IDisposable? _segmentsPropertiesDisposable; /// /// Initializes a new instance of the class. @@ -33,6 +51,31 @@ namespace Avalonia.Media Segments = new PathSegments(); } + static PathFigure() + { + SegmentsProperty.Changed.AddClassHandler( + (s, e) => + s.OnSegmentsChanged()); + } + + private void OnSegmentsChanged() + { + _segmentsDisposable?.Dispose(); + _segmentsPropertiesDisposable?.Dispose(); + + _segmentsDisposable = _segments?.ForEachItem( + _ => InvalidateSegments(), + _ => InvalidateSegments(), + InvalidateSegments); + + _segmentsPropertiesDisposable = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments()); + } + + private void InvalidateSegments() + { + SegmentsInvalidated?.Invoke(this, EventArgs.Empty); + } + /// /// Gets or sets a value indicating whether this instance is closed. /// @@ -64,7 +107,7 @@ namespace Avalonia.Media /// The segments. /// [Content] - public PathSegments Segments + public PathSegments? Segments { get { return _segments; } set { SetAndRaise(SegmentsProperty, ref _segments, value); } @@ -81,22 +124,23 @@ namespace Avalonia.Media get { return GetValue(StartPointProperty); } set { SetValue(StartPointProperty, value); } } + + public override string ToString() + => $"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty())}{(IsClosed ? "Z" : "")}"; internal void ApplyTo(StreamGeometryContext ctx) { ctx.BeginFigure(StartPoint, IsFilled); - foreach (var segment in Segments) + if (Segments != null) { - segment.ApplyTo(ctx); + foreach (var segment in Segments) + { + segment.ApplyTo(ctx); + } } ctx.EndFigure(IsClosed); } - - private PathSegments _segments; - - public override string ToString() - => $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}"; } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs index fbc29aedc8..3d11c19b7d 100644 --- a/src/Avalonia.Visuals/Media/PathGeometry.cs +++ b/src/Avalonia.Visuals/Media/PathGeometry.cs @@ -104,12 +104,26 @@ namespace Avalonia.Media _figuresPropertiesObserver?.Dispose(); _figuresObserver = figures?.ForEachItem( - _ => InvalidateGeometry(), - _ => InvalidateGeometry(), - () => InvalidateGeometry()); + s => + { + s.SegmentsInvalidated += InvalidateGeometryFromSegments; + InvalidateGeometry(); + }, + s => + { + s.SegmentsInvalidated -= InvalidateGeometryFromSegments; + InvalidateGeometry(); + }, + InvalidateGeometry); + _figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry()); + } + private void InvalidateGeometryFromSegments(object _, EventArgs __) + { + InvalidateGeometry(); + } public override string ToString() => $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}"; diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 7324f5fbd0..6febe9c802 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Defines a point. /// - public readonly struct Point : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Point : IEquatable { static Point() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Point).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 15e14935ca..3bd8e05ee2 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -99,6 +99,11 @@ namespace Avalonia.Rendering /// public string DebugFramesPath { get; set; } + /// + /// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered + /// + public bool RenderOnlyOnRenderThread { get; set; } + /// public event EventHandler SceneInvalidated; @@ -180,11 +185,44 @@ namespace Avalonia.Rendering /// public void Paint(Rect rect) { - var t = (IRenderLoopTask)this; - if(t.NeedsUpdate) - UpdateScene(); - if(_scene?.Item != null) - Render(true); + if (RenderOnlyOnRenderThread) + { + // Renderer is stopped and doesn't tick on the render thread + // This indicates a bug somewhere in our code + // (currently happens when a window gets minimized with Show desktop on Windows 10) + if(!_running) + return; + + while (true) + { + Scene scene; + bool? updated; + lock (_sceneLock) + { + updated = UpdateScene(); + scene = _scene?.Item; + } + + // Renderer is in invalid state, skip drawing + if(updated == null) + return; + + // Wait for the scene to be rendered or disposed + scene?.Rendered.Wait(); + + // That was an up-to-date scene, we can return immediately + if (updated == true) + return; + } + } + else + { + var t = (IRenderLoopTask)this; + if (t.NeedsUpdate) + UpdateScene(); + if (_scene?.Item != null) + Render(true); + } } /// @@ -270,13 +308,20 @@ namespace Avalonia.Rendering { if (scene?.Item != null) { - var overlay = DrawDirtyRects || DrawFps; - if (DrawDirtyRects) - _dirtyRectsDisplay.Tick(); - if (overlay) - RenderOverlay(scene.Item, ref context); - if (updated || forceComposite || overlay) - RenderComposite(scene.Item, ref context); + try + { + var overlay = DrawDirtyRects || DrawFps; + if (DrawDirtyRects) + _dirtyRectsDisplay.Tick(); + if (overlay) + RenderOverlay(scene.Item, ref context); + if (updated || forceComposite || overlay) + RenderComposite(scene.Item, ref context); + } + finally + { + scene.Item.MarkAsRendered(); + } } } } @@ -559,15 +604,16 @@ namespace Avalonia.Rendering UpdateScene(); } - private void UpdateScene() + private bool? UpdateScene() { Dispatcher.UIThread.VerifyAccess(); + using var noPump = NonPumpingLockHelper.Use(); lock (_sceneLock) { if (_disposed) - return; + return null; if (_scene?.Item.Generation > _lastSceneId) - return; + return false; } if (_root.IsVisible) { @@ -619,6 +665,8 @@ namespace Avalonia.Rendering SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect)); } + + return true; } else { @@ -628,6 +676,8 @@ namespace Avalonia.Rendering _scene = null; oldScene?.Dispose(); } + + return null; } } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs index 4f5c97cdff..6a4c532d4a 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Avalonia.Collections.Pooled; using Avalonia.VisualTree; @@ -13,6 +14,7 @@ namespace Avalonia.Rendering.SceneGraph public class Scene : IDisposable { private readonly Dictionary _index; + private readonly TaskCompletionSource _rendered = new TaskCompletionSource(); /// /// Initializes a new instance of the class. @@ -41,6 +43,8 @@ namespace Avalonia.Rendering.SceneGraph root.LayerRoot = root.Visual; } + public Task Rendered => _rendered.Task; + /// /// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned. /// @@ -97,6 +101,7 @@ namespace Avalonia.Rendering.SceneGraph public void Dispose() { + _rendered.TrySetResult(false); foreach (var node in _index.Values) { node.Dispose(); @@ -340,5 +345,7 @@ namespace Avalonia.Rendering.SceneGraph } } } + + public void MarkAsRendered() => _rendered.TrySetResult(true); } } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 872f69c884..7d5d62a091 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -24,7 +24,8 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { - Update(context, scene, (VisualNode)scene.Root, scene.Root.Visual.Bounds, true); + var clip = new Rect(scene.Root.Visual.Bounds.Size); + Update(context, scene, (VisualNode)scene.Root, clip, true); } } @@ -77,7 +78,7 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { - var clip = scene.Root.Visual.Bounds; + var clip = new Rect(scene.Root.Visual.Bounds.Size); if (node.Parent != null) { @@ -174,7 +175,9 @@ namespace Avalonia.Rendering.SceneGraph if (visual.IsVisible) { - var m = Matrix.CreateTranslation(visual.Bounds.Position); + var m = node != scene.Root ? + Matrix.CreateTranslation(visual.Bounds.Position) : + Matrix.Identity; var renderTransform = Matrix.Identity; diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index d87d2c5fc2..8a805dc6c5 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Defines a size. /// - public readonly struct Size : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Size : IEquatable { static Size() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Size).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index 6d69c4d9a9..06ebc9bfe7 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Describes the thickness of a frame around a rectangle. /// - public readonly struct Thickness : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Thickness : IEquatable { static Thickness() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Thickness).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Visuals/Vector.cs index 2fcf804f14..1b9f5c67d5 100644 --- a/src/Avalonia.Visuals/Vector.cs +++ b/src/Avalonia.Visuals/Vector.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; #nullable enable @@ -10,11 +12,16 @@ namespace Avalonia /// /// Defines a vector. /// - public readonly struct Vector : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Vector : IEquatable { static Vector() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 283d9deb52..9327531b46 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Specialized; using Avalonia.Collections; using Avalonia.Data; @@ -484,7 +485,7 @@ namespace Avalonia BindingPriority.LocalValue); } - protected override sealed void LogBindingError(AvaloniaProperty property, Exception e) + protected internal sealed override void LogBindingError(AvaloniaProperty property, Exception e) { // Don't log a binding error unless the control is attached to a logical or visual tree. // In theory this should only need to check for logical tree attachment, but in practise @@ -619,34 +620,30 @@ namespace Avalonia switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (Visual v in e.NewItems) - { - v.SetVisualParent(this); - } - + SetVisualParent(e.NewItems, this); break; case NotifyCollectionChangedAction.Remove: - foreach (Visual v in e.OldItems) - { - v.SetVisualParent(null); - } - + SetVisualParent(e.OldItems, null); break; case NotifyCollectionChangedAction.Replace: - foreach (Visual v in e.OldItems) - { - v.SetVisualParent(null); - } - - foreach (Visual v in e.NewItems) - { - v.SetVisualParent(this); - } - + SetVisualParent(e.OldItems, null); + SetVisualParent(e.NewItems, this); break; } } + + private static void SetVisualParent(IList children, Visual parent) + { + var count = children.Count; + + for (var i = 0; i < count; i++) + { + var visual = (Visual) children[i]; + + visual.SetVisualParent(parent); + } + } } } diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6079e5941f..e6523a1469 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -50,7 +50,13 @@ namespace Avalonia { var thisOffset = GetOffsetFrom(common, from); var thatOffset = GetOffsetFrom(common, to); - return -thatOffset * thisOffset; + + if (!thatOffset.TryInvert(out var thatOffsetInverted)) + { + return null; + } + + return thatOffsetInverted * thisOffset; } return null; diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 2cd3b973d8..b061920d6a 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -189,6 +189,11 @@ namespace Avalonia.X11 if (platform.Options.UseDBusMenu) NativeMenuExporter = DBusMenuExporter.TryCreate(_handle); NativeControlHost = new X11NativeControlHost(_platform, this); + DispatcherTimer.Run(() => + { + Paint?.Invoke(default); + return _handle != IntPtr.Zero; + }, TimeSpan.FromMilliseconds(100)); } class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo @@ -338,7 +343,10 @@ namespace Avalonia.X11 return customRendererFactory.Create(root, loop); return _platform.Options.UseDeferredRendering ? - new DeferredRenderer(root, loop) : + new DeferredRenderer(root, loop) + { + RenderOnlyOnRenderThread = true + } : (IRenderer)new X11ImmediateRendererProxy(root, loop); } @@ -815,7 +823,7 @@ namespace Avalonia.X11 XSetTransientForHint(_x11.Display, _handle, parent.Handle.Handle); } - public void Show() + public void Show(bool activate) { _wasMappedAtLeastOnce = true; XMapWindow(_x11.Display, _handle); diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index b3a24e6c37..8cdf24cc7b 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -351,7 +351,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); + Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj index 514556d0b9..db9c414840 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj @@ -4,6 +4,7 @@ netstandard2.0 true Avalonia.Markup.Xaml.Loader + $(DefineConstants);XAMLX_INTERNAL diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs new file mode 100644 index 0000000000..0f4efc9f65 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlAvaloniaListConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly IXamlType _elementType; + private readonly IReadOnlyList _values; + private readonly IXamlConstructor _constructor; + private readonly IXamlMethod _listAddMethod; + private readonly IXamlMethod _listSetCapacityMethod; + + public AvaloniaXamlIlAvaloniaListConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType listType, IXamlType elementType, IReadOnlyList values) : base(lineInfo) + { + _constructor = listType.GetConstructor(); + _listAddMethod = listType.GetMethod(new FindMethodMethodSignature("Add", types.XamlIlTypes.Void, elementType)); + _listSetCapacityMethod = listType.GetMethod(new FindMethodMethodSignature("set_Capacity", types.XamlIlTypes.Void, types.Int)); + + _elementType = elementType; + _values = values; + + Type = new XamlAstClrTypeReference(lineInfo, listType, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen.Newobj(_constructor); + + codeGen + .Dup() + .Ldc_I4(_values.Count) + .EmitCall(_listSetCapacityMethod); + + foreach (var value in _values) + { + codeGen.Dup(); + + context.Emit(value, codeGen, _elementType); + + codeGen.EmitCall(_listAddMethod); + } + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs new file mode 100644 index 0000000000..218c49512c --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs @@ -0,0 +1,34 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly AvaloniaXamlIlWellKnownTypes _types; + private readonly GridLength _gridLength; + + public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo) + { + _types = types; + _gridLength = gridLength; + + Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen + .Ldc_R8(_gridLength.Value) + .Ldc_I4((int)_gridLength.GridUnitType) + .Newobj(_types.GridLengthConstructorValueType); + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs new file mode 100644 index 0000000000..35cc9b3cf2 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs @@ -0,0 +1,54 @@ +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlVectorLikeConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly IXamlConstructor _constructor; + private readonly double[] _values; + + public AvaloniaXamlIlVectorLikeConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType type, IXamlConstructor constructor, double[] values) : base(lineInfo) + { + var parameters = constructor.Parameters; + + if (parameters.Count != values.Length) + { + throw new XamlTypeSystemException($"Constructor that takes {values.Length} parameters is expected, got {parameters.Count} instead."); + } + + var elementType = types.XamlIlTypes.Double; + + foreach (var parameter in parameters) + { + if (parameter != elementType) + { + throw new XamlTypeSystemException($"Expected parameter of type {elementType}, got {parameter} instead."); + } + } + + _constructor = constructor; + _values = values; + + Type = new XamlAstClrTypeReference(lineInfo, type, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + foreach (var value in _values) + { + codeGen.Ldc_R8(value); + } + + codeGen.Newobj(_constructor); + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 15413689f8..a82f5b9e60 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Avalonia.Controls; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using Avalonia.Media; using XamlX; using XamlX.Ast; using XamlX.Emit; @@ -175,37 +177,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } var text = textNode.Text; - var types = context.GetAvaloniaTypes(); - if (type.FullName == "System.TimeSpan") - { - var tsText = text.Trim(); - - if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) - { - // // shorthand seconds format (ie. "0.25") - if (!tsText.Contains(":") && double.TryParse(tsText, - NumberStyles.Float | NumberStyles.AllowThousands, - CultureInfo.InvariantCulture, out var seconds)) - timeSpan = TimeSpan.FromSeconds(seconds); - else - throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); - } - - - result = new XamlStaticOrTargetedReturnMethodCallNode(node, - type.FindMethod("FromTicks", type, false, types.Long), - new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); - return true; - } - - if (type.Equals(types.FontFamily)) + if (AvaloniaXamlIlLanguageParseIntrinsics.TryConvert(context, node, text, type, types, out result)) { - result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return true; } - + if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs new file mode 100644 index 0000000000..7c4cc8d28b --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using Avalonia.Media; +using XamlX.Ast; +using XamlX.Transform; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions +{ + class AvaloniaXamlIlLanguageParseIntrinsics + { + public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result) + { + if (type.FullName == "System.TimeSpan") + { + var tsText = text.Trim(); + + if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) + { + // // shorthand seconds format (ie. "0.25") + if (!tsText.Contains(":") && double.TryParse(tsText, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, out var seconds)) + timeSpan = TimeSpan.FromSeconds(seconds); + else + throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.FindMethod("FromTicks", type, false, types.Long), + new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); + return true; + } + + if (type.Equals(types.FontFamily)) + { + result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); + return true; + } + + if (type.Equals(types.Thickness)) + { + try + { + var thickness = Thickness.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, + new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); + } + } + + if (type.Equals(types.Point)) + { + try + { + var point = Point.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, + new[] { point.X, point.Y }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); + } + } + + if (type.Equals(types.Vector)) + { + try + { + var vector = Vector.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, + new[] { vector.X, vector.Y }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); + } + } + + if (type.Equals(types.Size)) + { + try + { + var size = Size.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, + new[] { size.Width, size.Height }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); + } + } + + if (type.Equals(types.Matrix)) + { + try + { + var matrix = Matrix.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, + new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); + } + } + + if (type.Equals(types.CornerRadius)) + { + try + { + var cornerRadius = CornerRadius.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, + new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); + } + } + + if (type.Equals(types.Color)) + { + if (!Color.TryParse(text, out Color color)) + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.GetMethod( + new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), + new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); + + return true; + } + + if (type.Equals(types.GridLength)) + { + try + { + var gridLength = GridLength.Parse(text); + + result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); + } + } + + if (type.Equals(types.Cursor)) + { + if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) + { + var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); + + result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List { enumConstantNode }); + + return true; + } + } + + if (type.Equals(types.ColumnDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result); + } + + if (type.Equals(types.RowDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result); + } + + result = null; + return false; + } + + private static bool ConvertDefinitionList( + IXamlAstValueNode node, + string text, + AvaloniaXamlIlWellKnownTypes types, + IXamlType listType, + IXamlType elementType, + string errorDisplayName, + out IXamlAstValueNode result) + { + try + { + var lengths = GridLength.ParseLengths(text); + + var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false); + + var definitionConstructorGridLength = elementType.GetConstructor(new List {types.GridLength}); + + IXamlAstValueNode CreateDefinitionNode(GridLength length) + { + var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length); + + return new XamlAstNewClrObjectNode(node, definitionTypeRef, + definitionConstructorGridLength, new List {lengthNode}); + } + + var definitionNodes = + new List(lengths.Select(CreateDefinitionNode)); + + result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node); + } + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index d78ceeb918..f87e73a783 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject"); if (avaloniaObject.IsAssignableFrom(targetRef.Type) && avaloniaObject.IsAssignableFrom(declaringRef.Type) - && !targetRef.Type.IsAssignableFrom(declaringRef.Type)) + && !declaringRef.Type.IsAssignableFrom(targetRef.Type)) { // Instance property var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 58f4ddfe31..125701ca9e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using XamlX.Emit; using XamlX.IL; using XamlX.Transform; @@ -48,11 +49,34 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ReflectionBindingExtension { get; } public IXamlType RelativeSource { get; } + public IXamlType UInt { get; } + public IXamlType Int { get; } public IXamlType Long { get; } public IXamlType Uri { get; } public IXamlType FontFamily { get; } public IXamlConstructor FontFamilyConstructorUriName { get; } - + public IXamlType Thickness { get; } + public IXamlConstructor ThicknessFullConstructor { get; } + public IXamlType Point { get; } + public IXamlConstructor PointFullConstructor { get; } + public IXamlType Vector { get; } + public IXamlConstructor VectorFullConstructor { get; } + public IXamlType Size { get; } + public IXamlConstructor SizeFullConstructor { get; } + public IXamlType Matrix { get; } + public IXamlConstructor MatrixFullConstructor { get; } + public IXamlType CornerRadius { get; } + public IXamlConstructor CornerRadiusFullConstructor { get; } + public IXamlType GridLength { get; } + public IXamlConstructor GridLengthConstructorValueType { get; } + public IXamlType Color { get; } + public IXamlType StandardCursorType { get; } + public IXamlType Cursor { get; } + public IXamlConstructor CursorTypeConstructor { get; } + public IXamlType RowDefinition { get; } + public IXamlType RowDefinitions { get; } + public IXamlType ColumnDefinition { get; } + public IXamlType ColumnDefinitions { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -110,10 +134,38 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater"); ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource"); + UInt = cfg.TypeSystem.GetType("System.UInt32"); + Int = cfg.TypeSystem.GetType("System.Int32"); Long = cfg.TypeSystem.GetType("System.Int64"); Uri = cfg.TypeSystem.GetType("System.Uri"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); - FontFamilyConstructorUriName = FontFamily.FindConstructor(new List { Uri, XamlIlTypes.String }); + FontFamilyConstructorUriName = FontFamily.GetConstructor(new List { Uri, XamlIlTypes.String }); + + (IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount) + { + var type = cfg.TypeSystem.GetType(name); + var ctor = type.GetConstructor(Enumerable.Range(0, componentCount).Select(_ => componentType).ToList()); + + return (type, ctor); + } + + (Thickness, ThicknessFullConstructor) = GetNumericTypeInfo("Avalonia.Thickness", XamlIlTypes.Double, 4); + (Point, PointFullConstructor) = GetNumericTypeInfo("Avalonia.Point", XamlIlTypes.Double, 2); + (Vector, VectorFullConstructor) = GetNumericTypeInfo("Avalonia.Vector", XamlIlTypes.Double, 2); + (Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); + (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); + (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + + GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); + GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); + Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); + StandardCursorType = cfg.TypeSystem.GetType("Avalonia.Input.StandardCursorType"); + Cursor = cfg.TypeSystem.GetType("Avalonia.Input.Cursor"); + CursorTypeConstructor = Cursor.GetConstructor(new List { StandardCursorType }); + ColumnDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinition"); + ColumnDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinitions"); + RowDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinition"); + RowDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinitions"); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs index 03ec32b9cf..1974dfe3bc 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs @@ -203,6 +203,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions .ParentNodes() .OfType() .Where(x => styledElementType.IsAssignableFrom(x.Type.GetClrType())) + .Skip(1) .ElementAtOrDefault(ancestor.Level) ?.Type.GetClrType(); @@ -242,6 +243,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions case RawSourceBindingExpressionNode rawSource: nodes.Add(new RawSourcePathElementNode(rawSource.RawSource)); break; + case BindingExpressionGrammar.TypeCastNode typeCastNode: + var castType = GetType(typeCastNode.Namespace, typeCastNode.TypeName); + + if (castType is null) + { + throw new XamlX.XamlParseException($"Unable to resolve cast to type {typeCastNode.Namespace}:{typeCastNode.TypeName} based on XAML tree.", lineInfo); + } + + nodes.Add(new TypeCastPathElementNode(castType)); + break; } } @@ -422,7 +433,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { codeGen.Ldtype(Type) .Ldc_I4(_level) - .EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "FindAncestor")); + .EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "Ancestor")); } } @@ -608,10 +619,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions private readonly IXamlAstValueNode _rawSource; public RawSourcePathElementNode(IXamlAstValueNode rawSource) - :base(rawSource) + : base(rawSource) { _rawSource = rawSource; - + } public IXamlType Type => _rawSource.Type.GetClrType(); @@ -625,6 +636,21 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + class TypeCastPathElementNode : IXamlIlBindingPathElementNode + { + public TypeCastPathElementNode(IXamlType ancestorType) + { + Type = ancestorType; + } + + public IXamlType Type { get; } + + public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen) + { + codeGen.EmitCall(context.GetAvaloniaTypes().CompiledBindingPathBuilder.FindMethod(m => m.Name == "TypeCast").MakeGenericMethod(new[] { Type })); + } + } + class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode { private readonly List _transformElements; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 5420df861c..ea80a607c5 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 5420df861ce6f2be5ead9efa078fe7242ce88f18 +Subproject commit ea80a607c5e9d8f000160dbbb48c27ed4cfafbc9 diff --git a/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt index c87dcacda4..fcc74cf864 100644 --- a/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt +++ b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt @@ -1,4 +1 @@ -Compat issues with assembly Avalonia.Markup.Xaml: -MembersMustExist : Member 'public Avalonia.Data.Binding Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSource.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSource.set(Avalonia.Data.Binding)' does not exist in the implementation but it does exist in the contract. -Total Issues: 2 +Total Issues: 0 diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 3e15d2f700..217da2d50d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -23,6 +23,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs index aab733cb43..da39920eb3 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindingExtension.cs @@ -26,6 +26,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { Path = Path, Converter = Converter, + ConverterParameter = ConverterParameter, + TargetNullValue = TargetNullValue, FallbackValue = FallbackValue, Mode = Mode, Priority = Priority, diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs index d627fe3cd3..f6636664c1 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Avalonia.Controls; using Avalonia.Data.Core; using Avalonia.Data.Core.Plugins; @@ -53,6 +54,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings case IStronglyTypedStreamElement stream: node = new StreamNode(stream.CreatePlugin()); break; + case ITypeCastElement typeCast: + node = new StrongTypeCastNode(typeCast.Type, typeCast.Cast); + break; default: throw new InvalidOperationException($"Unknown binding path element type {element.GetType().FullName}"); } @@ -66,6 +70,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal SourceMode SourceMode => _elements.Count > 0 && _elements[0] is IControlSourceBindingPathElement ? SourceMode.Control : SourceMode.Data; internal object RawSource { get; } + + public override string ToString() + => string.Concat(_elements.Select(e => e.ToString())); } public class CompiledBindingPathBuilder @@ -126,6 +133,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings return this; } + public CompiledBindingPathBuilder TypeCast() + { + _elements.Add(new TypeCastPathElement()); + return this; + } + public CompiledBindingPathBuilder SetRawSource(object rawSource) { _rawSource = rawSource; @@ -157,6 +170,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public IPropertyInfo Property { get; } public Func, IPropertyInfo, IPropertyAccessor> AccessorFactory { get; } + + public override string ToString() + => $".{Property.Name}"; } internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement @@ -164,6 +180,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings IStreamPlugin CreatePlugin(); } + internal interface ITypeCastElement : ICompiledBindingPathElement + { + Type Type { get; } + + Func Cast { get; } + } + internal class TaskStreamPathElement : IStronglyTypedStreamElement { public static readonly TaskStreamPathElement Instance = new TaskStreamPathElement(); @@ -181,6 +204,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal class SelfPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement { public static readonly SelfPathElement Instance = new SelfPathElement(); + + public override string ToString() + => "$self"; } internal class AncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement @@ -193,6 +219,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public Type AncestorType { get; } public int Level { get; } + + public override string ToString() + => $"$parent[{AncestorType?.Name},{Level}]"; } internal class VisualAncestorPathElement : ICompiledBindingPathElement, IControlSourceBindingPathElement @@ -217,6 +246,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public INameScope NameScope { get; } public string Name { get; } + + public override string ToString() + => $"#{Name}"; } internal class ArrayElementPathElement : ICompiledBindingPathElement @@ -229,5 +261,24 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings public int[] Indices { get; } public Type ElementType { get; } + public override string ToString() + => $"[{string.Join(",", Indices)}]"; + } + + internal class TypeCastPathElement : ITypeCastElement + { + private static object TryCast(object obj) + { + if (obj is T result) + return result; + return null; + } + + public Type Type => typeof(T); + + public Func Cast => TryCast; + + public override string ToString() + => $"({Type.FullName})"; } } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs new file mode 100644 index 0000000000..1252ec7eca --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/StrongTypeCastNode.cs @@ -0,0 +1,18 @@ +using System; +using Avalonia.Data.Core; + +namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings +{ + public class StrongTypeCastNode : TypeCastNode + { + private Func _cast; + + public StrongTypeCastNode(Type type, Func cast) : base(type) + { + _cast = cast; + } + + protected override object Cast(object value) + => _cast(value); + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 03fd2e60dd..95380be65f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -10,8 +10,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { public class DynamicResourceExtension : IBinding { - private IStyledElement? _anchor; - private IResourceProvider? _resourceProvider; + private object? _anchor; public DynamicResourceExtension() { @@ -30,12 +29,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (!(provideTarget.TargetObject is IStyledElement)) { - _anchor = serviceProvider.GetFirstParent(); - - if (_anchor is null) - { - _resourceProvider = serviceProvider.GetFirstParent(); - } + _anchor = serviceProvider.GetFirstParent() ?? + serviceProvider.GetFirstParent() ?? + (object?)serviceProvider.GetFirstParent(); } return this; @@ -52,16 +48,16 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions return null; } - var control = target as IStyledElement ?? _anchor as IStyledElement; + var control = target as IResourceHost ?? _anchor as IResourceHost; if (control != null) { var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source); } - else if (_resourceProvider is object) + else if (_anchor is IResourceProvider resourceProvider) { - var source = _resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); + var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source); } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs index 8e5631e198..7c362e24cc 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs @@ -46,6 +46,10 @@ namespace Avalonia.Markup.Parsers state = ParseIndexer(ref r, nodes); break; + case State.TypeCast: + state = ParseTypeCast(ref r, nodes); + break; + case State.ElementName: state = ParseElementName(ref r, nodes); mode = SourceMode.Control; @@ -84,6 +88,11 @@ namespace Avalonia.Markup.Parsers } else if (ParseOpenBrace(ref r)) { + if (PeekOpenBrace(ref r)) + { + return State.TypeCast; + } + return State.AttachedProperty; } else if (PeekOpenBracket(ref r)) @@ -124,6 +133,10 @@ namespace Avalonia.Markup.Parsers { return State.Indexer; } + else if (ParseOpenBrace(ref r)) + { + return State.TypeCast; + } return State.End; } @@ -132,6 +145,11 @@ namespace Avalonia.Markup.Parsers { if (ParseOpenBrace(ref r)) { + if (PeekOpenBrace(ref r)) + { + return State.TypeCast; + } + return State.AttachedProperty; } else @@ -152,6 +170,12 @@ namespace Avalonia.Markup.Parsers { var (ns, owner) = ParseTypeName(ref r); + if(!r.End && r.TakeIf(')')) + { + nodes.Add(new TypeCastNode() { Namespace = ns, TypeName = owner }); + return State.AfterMember; + } + if (r.End || !r.TakeIf('.')) { throw new ExpressionParseException(r.Position, "Invalid attached property name."); @@ -159,6 +183,11 @@ namespace Avalonia.Markup.Parsers var name = r.ParseIdentifier(); + if (name.Length == 0) + { + throw new ExpressionParseException(r.Position, "Attached Property name expected after '.'."); + } + if (r.End || !r.TakeIf(')')) { throw new ExpressionParseException(r.Position, "Expected ')'."); @@ -186,6 +215,39 @@ namespace Avalonia.Markup.Parsers return State.AfterMember; } + private static State ParseTypeCast(ref CharacterReader r, List nodes) + { + bool parseMemberBeforeAddCast = ParseOpenBrace(ref r); + + var (ns, typeName) = ParseTypeName(ref r); + + var result = State.AfterMember; + + if (parseMemberBeforeAddCast) + { + if (!ParseCloseBrace(ref r)) + { + throw new ExpressionParseException(r.Position, "Expected ')'."); + } + + result = ParseBeforeMember(ref r, nodes); + + if(r.Peek == '[') + { + result = ParseIndexer(ref r, nodes); + } + } + + nodes.Add(new TypeCastNode { Namespace = ns, TypeName = typeName }); + + if (r.End || !r.TakeIf(')')) + { + throw new ExpressionParseException(r.Position, "Expected ')'."); + } + + return result; + } + private static State ParseElementName(ref CharacterReader r, List nodes) { var name = r.ParseIdentifier(); @@ -288,11 +350,21 @@ namespace Avalonia.Markup.Parsers return !r.End && r.TakeIf('('); } + private static bool ParseCloseBrace(ref CharacterReader r) + { + return !r.End && r.TakeIf(')'); + } + private static bool PeekOpenBracket(ref CharacterReader r) { return !r.End && r.Peek == '['; } + private static bool PeekOpenBrace(ref CharacterReader r) + { + return !r.End && r.Peek == '('; + } + private static bool ParseStreamOperator(ref CharacterReader r) { return !r.End && r.TakeIf('^'); @@ -322,6 +394,7 @@ namespace Avalonia.Markup.Parsers BeforeMember, AttachedProperty, Indexer, + TypeCast, End, } @@ -343,9 +416,9 @@ namespace Avalonia.Markup.Parsers } } - public interface INode {} + public interface INode { } - public interface ITransformNode {} + public interface ITransformNode { } public class EmptyExpressionNode : INode { } @@ -366,11 +439,11 @@ namespace Avalonia.Markup.Parsers public IList Arguments { get; set; } } - public class NotNode : INode, ITransformNode {} + public class NotNode : INode, ITransformNode { } - public class StreamNode : INode {} + public class StreamNode : INode { } - public class SelfNode : INode {} + public class SelfNode : INode { } public class NameNode : INode { @@ -383,5 +456,11 @@ namespace Avalonia.Markup.Parsers public string TypeName { get; set; } public int Level { get; set; } } + + public class TypeCastNode : INode + { + public string Namespace { get; set; } + public string TypeName { get; set; } + } } } diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs index 1048148c1f..558130e23f 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/ExpressionParser.cs @@ -59,6 +59,9 @@ namespace Avalonia.Markup.Parsers case BindingExpressionGrammar.NameNode elementName: nextNode = new ElementNameNode(_nameScope, elementName.Name); break; + case BindingExpressionGrammar.TypeCastNode typeCast: + nextNode = ParseTypeCastNode(typeCast); + break; } if (rootNode is null) { @@ -92,6 +95,22 @@ namespace Avalonia.Markup.Parsers return new FindAncestorNode(ancestorType, ancestorLevel); } + private TypeCastNode ParseTypeCastNode(BindingExpressionGrammar.TypeCastNode node) + { + Type castType = null; + if (!(node.Namespace is null) && !(node.TypeName is null)) + { + if (_typeResolver == null) + { + throw new InvalidOperationException("Cannot parse a binding path with a typed Cast without a type resolver. Maybe you can use a LINQ Expression binding path instead?"); + } + + castType = _typeResolver(node.Namespace, node.TypeName); + } + + return new TypeCastNode(castType); + } + private AvaloniaPropertyAccessorNode ParseAttachedProperty(BindingExpressionGrammar.AttachedPropertyNameNode node) { if (_typeResolver == null) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 44e0c82110..2a79a4bb50 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -434,7 +434,7 @@ namespace Avalonia.Skia /// public IDrawingContextLayerImpl CreateLayer(Size size) { - return CreateRenderTarget( size); + return CreateRenderTarget(size, true); } /// @@ -673,7 +673,7 @@ namespace Avalonia.Skia private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) { var calc = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSizeWithDpi(_dpi), targetSize); - var intermediate = CreateRenderTarget(calc.IntermediateSize); + var intermediate = CreateRenderTarget(calc.IntermediateSize, false); paintWrapper.AddDisposable(intermediate); @@ -748,7 +748,7 @@ namespace Avalonia.Skia if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) { - var intermediate = CreateRenderTarget(intermediateSize); + var intermediate = CreateRenderTarget(intermediateSize, false); using (var ctx = intermediate.CreateDrawingContext(visualBrushRenderer)) { @@ -978,9 +978,10 @@ namespace Avalonia.Skia /// Create new render target compatible with this drawing context. /// /// The size of the render target in DIPs. + /// Whether the render target is being created for a layer. /// Pixel format. /// - private SurfaceRenderTarget CreateRenderTarget(Size size, PixelFormat? format = null) + private SurfaceRenderTarget CreateRenderTarget(Size size, bool isLayer, PixelFormat? format = null) { var pixelSize = PixelSize.FromSizeWithDpi(size, _dpi); var createInfo = new SurfaceRenderTarget.CreateInfo @@ -992,7 +993,8 @@ namespace Avalonia.Skia DisableTextLcdRendering = !_canTextUseLcdRendering, GrContext = _grContext, Gpu = _gpu, - Session = _session + Session = _session, + DisableManualFbo = !isLayer, }; return new SurfaceRenderTarget(createInfo); diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index d6f76a2c20..6d0be9f64d 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; + using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; using Avalonia.OpenGL; @@ -124,7 +126,8 @@ namespace Avalonia.Skia Width = size.Width, Height = size.Height, Dpi = dpi, - DisableTextLcdRendering = false + DisableTextLcdRendering = false, + DisableManualFbo = true, }; return new SurfaceRenderTarget(createInfo); @@ -165,12 +168,13 @@ namespace Avalonia.Skia LinearMetrics = true }; - private static readonly SKTextBlobBuilder s_textBlobBuilder = new SKTextBlobBuilder(); + private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder()); /// public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width) { var count = glyphRun.GlyphIndices.Length; + var textBlobBuilder = s_textBlobBuilderThreadLocal.Value; var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl; @@ -190,15 +194,15 @@ namespace Avalonia.Skia { if (glyphTypeface.IsFixedPitch) { - s_textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); + textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); width = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[0]) * scale * glyphRun.GlyphIndices.Length; } else { - var buffer = s_textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); + var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); var positions = buffer.GetPositionSpan(); @@ -218,12 +222,12 @@ namespace Avalonia.Skia buffer.SetGlyphs(glyphRun.GlyphIndices.Buffer.Span); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } } else { - var buffer = s_textBlobBuilder.AllocatePositionedRun(s_font, count); + var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count); var glyphPositions = buffer.GetPositionSpan(); @@ -249,7 +253,7 @@ namespace Avalonia.Skia width = currentX; - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } return new GlyphRunImpl(textBlob); diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 6347c64aed..01b7449b64 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -51,7 +51,8 @@ namespace Avalonia.Skia _grContext = createInfo.GrContext; _gpu = createInfo.Gpu; - _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session); + if (!createInfo.DisableManualFbo) + _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session); if (_surface == null) _surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format)); @@ -220,6 +221,8 @@ namespace Avalonia.Skia public ISkiaGpu Gpu; public ISkiaGpuRenderSession Session; + + public bool DisableManualFbo; } } } diff --git a/src/Windows/Avalonia.Win32/.gitignore b/src/Windows/Avalonia.Win32/.gitignore new file mode 100644 index 0000000000..7882fa6540 --- /dev/null +++ b/src/Windows/Avalonia.Win32/.gitignore @@ -0,0 +1 @@ +*.Generated.cs diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index 5889d919df..fe5f806fbe 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -5,9 +5,11 @@ Avalonia.Win32 + - + + diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index 7d9e0a8bd2..047b7c361f 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -12,10 +12,17 @@ namespace Avalonia.Win32 { internal class ClipboardImpl : IClipboard { + private const int OleRetryCount = 10; + private const int OleRetryDelay = 100; + private async Task OpenClipboard() { + var i = OleRetryCount; + while (!UnmanagedMethods.OpenClipboard(IntPtr.Zero)) { + if (--i == 0) + throw new TimeoutException("Timeout opening clipboard."); await Task.Delay(100); } @@ -72,20 +79,32 @@ namespace Avalonia.Win32 { Dispatcher.UIThread.VerifyAccess(); var wrapper = new DataObject(data); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleSetClipboard(wrapper) == 0) + var hr = UnmanagedMethods.OleSetClipboard(wrapper); + + if (hr == 0) break; - await Task.Delay(100); + + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } public async Task GetFormatsAsync() { Dispatcher.UIThread.VerifyAccess(); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleGetClipboard(out var dataObject) == 0) + var hr = UnmanagedMethods.OleGetClipboard(out var dataObject); + + if (hr == 0) { var wrapper = new OleDataObject(dataObject); var formats = wrapper.GetDataFormats().ToArray(); @@ -93,16 +112,23 @@ namespace Avalonia.Win32 return formats; } - await Task.Delay(100); + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } public async Task GetDataAsync(string format) { Dispatcher.UIThread.VerifyAccess(); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleGetClipboard(out var dataObject) == 0) + var hr = UnmanagedMethods.OleGetClipboard(out var dataObject); + + if (hr == 0) { var wrapper = new OleDataObject(dataObject); var rv = wrapper.Get(format); @@ -110,7 +136,10 @@ namespace Avalonia.Win32 return rv; } - await Task.Delay(100); + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } } diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs deleted file mode 100644 index 4c090e797c..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Avalonia.Win32 -{ - internal class CompositionBlurHost : IBlurHost - { - Windows.UI.Composition.Visual _blurVisual; - - public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual) - { - _blurVisual = blurVisual; - } - - public void SetBlur(bool enabled) - { - _blurVisual.IsVisible = enabled; - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs deleted file mode 100644 index e0dfdf282c..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Logging; -using Avalonia.OpenGL; -using Avalonia.OpenGL.Angle; -using Avalonia.OpenGL.Egl; -using Windows.UI.Composition; -using Windows.UI.Composition.Interop; -using WinRT; - -namespace Avalonia.Win32 -{ - internal class CompositionConnector - { - private Compositor _compositor; - private Windows.System.DispatcherQueueController _dispatcherQueueController; - private CompositionGraphicsDevice _graphicsDevice; - - internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE - { - DQTAT_COM_NONE = 0, - DQTAT_COM_ASTA = 1, - DQTAT_COM_STA = 2 - }; - - internal enum DISPATCHERQUEUE_THREAD_TYPE - { - DQTYPE_THREAD_DEDICATED = 1, - DQTYPE_THREAD_CURRENT = 2, - }; - - [StructLayout(LayoutKind.Sequential)] - internal struct DispatcherQueueOptions - { - public int dwSize; - - [MarshalAs(UnmanagedType.I4)] - public DISPATCHERQUEUE_THREAD_TYPE threadType; - - [MarshalAs(UnmanagedType.I4)] - public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; - }; - - [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)] - internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController); - - public static CompositionConnector TryCreate(EglPlatformOpenGlInterface egl) - { - const int majorRequired = 10; - const int buildRequired = 16299; - - var majorInstalled = Win32Platform.WindowsVersion.Major; - var buildInstalled = Win32Platform.WindowsVersion.Build; - - if (majorInstalled >= majorRequired && - buildInstalled >= buildRequired) - { - try - { - return new CompositionConnector(egl); - } - catch (Exception e) - { - Logger.TryGet(LogEventLevel.Error, "WinUIComposition")?.Log(null, "Unable to initialize WinUI compositor: {0}", e); - - return null; - } - } - - var osVersionNotice = $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed."; - - Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null, - $"Unable to initialize WinUI compositor: {osVersionNotice}"); - - return null; - } - - public CompositionConnector(EglPlatformOpenGlInterface egl) - { - EnsureDispatcherQueue(); - - if (_dispatcherQueueController != null) - _compositor = new Compositor(); - - var interop = _compositor.As(); - - var display = egl.Display as AngleWin32EglDisplay; - - _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); - } - - public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual, out IBlurHost blurHost) - { - var target = CreateDesktopWindowTarget(hwnd); - - var surface = _graphicsDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0), - Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized, - Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied); - - var surfaceInterop = surface.As(); - - var brush = _compositor.CreateSurfaceBrush(surface); - var visual = _compositor.CreateSpriteVisual(); - - visual.Brush = brush; - visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1); - - var container = _compositor.CreateContainerVisual(); - - target.Root = container; - - var blur = CreateBlur(); - - blurHost = new CompositionBlurHost(blur); - - container.Children.InsertAtTop(blur); - - container.Children.InsertAtTop(visual); - - visual.CompositeMode = CompositionCompositeMode.SourceOver; - - surfaceVisual = container; - - return surfaceInterop; - } - - private SpriteVisual CreateBlur() - { - var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter("backdrop")); - - var backDropBrush = _compositor.CreateBackdropBrush(); - - var saturateEffect = new SaturationEffect(blurEffect); - var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); - var satBrush = satEffectFactory.CreateBrush(); - - satBrush.SetSourceParameter("backdrop", backDropBrush); - - var visual = _compositor.CreateSpriteVisual(); - visual.IsVisible = false; - - visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f); - visual.Brush = satBrush; - - return visual; - } - - private CompositionTarget CreateDesktopWindowTarget(IntPtr window) - { - var interop = _compositor.As(); - - interop.CreateDesktopWindowTarget(window, false, out var windowTarget); - return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget); - } - - private void EnsureDispatcherQueue() - { - if (_dispatcherQueueController == null) - { - DispatcherQueueOptions options = new DispatcherQueueOptions(); - options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE; - options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT; - options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions)); - - CreateDispatcherQueueController(options, out var queue); - _dispatcherQueueController = Windows.System.DispatcherQueueController.FromAbi(queue); - } - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs deleted file mode 100644 index 441da93787..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.OpenGL; -using Avalonia.OpenGL.Angle; -using Avalonia.OpenGL.Egl; -using Avalonia.OpenGL.Surfaces; -using Windows.UI.Composition.Interop; - -namespace Avalonia.Win32 -{ - internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase - { - private EglPlatformOpenGlInterface _egl; - private readonly IEglWindowGlPlatformSurfaceInfo _info; - private ICompositionDrawingSurfaceInterop _surfaceInterop; - private Windows.UI.Composition.Visual _surface; - - public CompositionEglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base() - { - _egl = egl; - _info = info; - } - - public IBlurHost AttachToCompositionTree(CompositionConnector connector, IntPtr hwnd) - { - using (_egl.PrimaryContext.MakeCurrent()) - { - _surfaceInterop = connector.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost); - return blurHost; - } - } - - public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() - { - return new CompositionRenderTarget(_egl, _surface, _surfaceInterop, _info); - } - - class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase - { - private readonly EglPlatformOpenGlInterface _egl; - private readonly IEglWindowGlPlatformSurfaceInfo _info; - private PixelSize _currentSize; - private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; - private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); - private Windows.UI.Composition.Visual _compositionVisual; - - public CompositionRenderTarget(EglPlatformOpenGlInterface egl, - Windows.UI.Composition.Visual compositionVisual, - ICompositionDrawingSurfaceInterop interopSurface, - IEglWindowGlPlatformSurfaceInfo info) - : base(egl) - { - _egl = egl; - _surfaceInterop = interopSurface; - _info = info; - _currentSize = info.Size; - _compositionVisual = compositionVisual; - - using (_egl.PrimaryContext.MakeCurrent()) - { - _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); - } - - _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); - } - - public override IGlPlatformSurfaceRenderingSession BeginDraw() - { - IntPtr texture; - EglSurface surface; - - using (_egl.PrimaryEglContext.EnsureCurrent()) - { - if (_info.Size != _currentSize) - { - _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height }); - _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); - _currentSize = _info.Size; - } - - var offset = new POINT(); - - _surfaceInterop.BeginDraw( - IntPtr.Zero, - ref s_Iid, - out texture, ref offset); - - surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); - } - - return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); - } - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs deleted file mode 100644 index 1c761ee082..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; - - -namespace Avalonia.Win32 -{ - class D2DEffects - { - public static readonly Guid CLSID_D2D12DAffineTransform = new Guid(0x6AA97485, 0x6354, 0x4CFC, 0x90, 0x8C, 0xE4, 0xA7, 0x4F, 0x62, 0xC9, 0x6C); - - public static readonly Guid CLSID_D2D13DPerspectiveTransform = new Guid(0xC2844D0B, 0x3D86, 0x46E7, 0x85, 0xBA, 0x52, 0x6C, 0x92, 0x40, 0xF3, 0xFB); - - public static readonly Guid CLSID_D2D13DTransform = new Guid(0xE8467B04, 0xEC61, 0x4B8A, 0xB5, 0xDE, 0xD4, 0xD7, 0x3D, 0xEB, 0xEA, 0x5A); - - public static readonly Guid CLSID_D2D1ArithmeticComposite = new Guid(0xFC151437, 0x049A, 0x4784, 0xA2, 0x4A, 0xF1, 0xC4, 0xDA, 0xF2, 0x09, 0x87); - - public static readonly Guid CLSID_D2D1Atlas = new Guid(0x913E2BE4, 0xFDCF, 0x4FE2, 0xA5, 0xF0, 0x24, 0x54, 0xF1, 0x4F, 0xF4, 0x08); - - public static readonly Guid CLSID_D2D1BitmapSource = new Guid(0x5FB6C24D, 0xC6DD, 0x4231, 0x94, 0x4, 0x50, 0xF4, 0xD5, 0xC3, 0x25, 0x2D); - - public static readonly Guid CLSID_D2D1Blend = new Guid(0x81C5B77B, 0x13F8, 0x4CDD, 0xAD, 0x20, 0xC8, 0x90, 0x54, 0x7A, 0xC6, 0x5D); - - public static readonly Guid CLSID_D2D1Border = new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27); - - public static readonly Guid CLSID_D2D1Brightness = new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87); - - public static readonly Guid CLSID_D2D1ColorManagement = new Guid(0x1A28524C, 0xFDD6, 0x4AA4, 0xAE, 0x8F, 0x83, 0x7E, 0xB8, 0x26, 0x7B, 0x37); - - public static readonly Guid CLSID_D2D1ColorMatrix = new Guid(0x921F03D6, 0x641C, 0x47DF, 0x85, 0x2D, 0xB4, 0xBB, 0x61, 0x53, 0xAE, 0x11); - - public static readonly Guid CLSID_D2D1Composite = new Guid(0x48FC9F51, 0xF6AC, 0x48F1, 0x8B, 0x58, 0x3B, 0x28, 0xAC, 0x46, 0xF7, 0x6D); - - public static readonly Guid CLSID_D2D1ConvolveMatrix = new Guid(0x407F8C08, 0x5533, 0x4331, 0xA3, 0x41, 0x23, 0xCC, 0x38, 0x77, 0x84, 0x3E); - - public static readonly Guid CLSID_D2D1Crop = new Guid(0xE23F7110, 0x0E9A, 0x4324, 0xAF, 0x47, 0x6A, 0x2C, 0x0C, 0x46, 0xF3, 0x5B); - - public static readonly Guid CLSID_D2D1DirectionalBlur = new Guid(0x174319A6, 0x58E9, 0x49B2, 0xBB, 0x63, 0xCA, 0xF2, 0xC8, 0x11, 0xA3, 0xDB); - - public static readonly Guid CLSID_D2D1DiscreteTransfer = new Guid(0x90866FCD, 0x488E, 0x454B, 0xAF, 0x06, 0xE5, 0x04, 0x1B, 0x66, 0xC3, 0x6C); - - public static readonly Guid CLSID_D2D1DisplacementMap = new Guid(0xEDC48364, 0x417, 0x4111, 0x94, 0x50, 0x43, 0x84, 0x5F, 0xA9, 0xF8, 0x90); - - public static readonly Guid CLSID_D2D1DistantDiffuse = new Guid(0x3E7EFD62, 0xA32D, 0x46D4, 0xA8, 0x3C, 0x52, 0x78, 0x88, 0x9A, 0xC9, 0x54); - - public static readonly Guid CLSID_D2D1DistantSpecular = new Guid(0x428C1EE5, 0x77B8, 0x4450, 0x8A, 0xB5, 0x72, 0x21, 0x9C, 0x21, 0xAB, 0xDA); - - public static readonly Guid CLSID_D2D1DpiCompensation = new Guid(0x6C26C5C7, 0x34E0, 0x46FC, 0x9C, 0xFD, 0xE5, 0x82, 0x37, 0x6, 0xE2, 0x28); - - public static readonly Guid CLSID_D2D1Flood = new Guid(0x61C23C20, 0xAE69, 0x4D8E, 0x94, 0xCF, 0x50, 0x07, 0x8D, 0xF6, 0x38, 0xF2); - - public static readonly Guid CLSID_D2D1GammaTransfer = new Guid(0x409444C4, 0xC419, 0x41A0, 0xB0, 0xC1, 0x8C, 0xD0, 0xC0, 0xA1, 0x8E, 0x42); - - public static readonly Guid CLSID_D2D1GaussianBlur = new Guid(0x1FEB6D69, 0x2FE6, 0x4AC9, 0x8C, 0x58, 0x1D, 0x7F, 0x93, 0xE7, 0xA6, 0xA5); - - public static readonly Guid CLSID_D2D1Scale = new Guid(0x9DAF9369, 0x3846, 0x4D0E, 0xA4, 0x4E, 0xC, 0x60, 0x79, 0x34, 0xA5, 0xD7); - - public static readonly Guid CLSID_D2D1Histogram = new Guid(0x881DB7D0, 0xF7EE, 0x4D4D, 0xA6, 0xD2, 0x46, 0x97, 0xAC, 0xC6, 0x6E, 0xE8); - - public static readonly Guid CLSID_D2D1HueRotation = new Guid(0x0F4458EC, 0x4B32, 0x491B, 0x9E, 0x85, 0xBD, 0x73, 0xF4, 0x4D, 0x3E, 0xB6); - - public static readonly Guid CLSID_D2D1LinearTransfer = new Guid(0xAD47C8FD, 0x63EF, 0x4ACC, 0x9B, 0x51, 0x67, 0x97, 0x9C, 0x03, 0x6C, 0x06); - - public static readonly Guid CLSID_D2D1LuminanceToAlpha = new Guid(0x41251AB7, 0x0BEB, 0x46F8, 0x9D, 0xA7, 0x59, 0xE9, 0x3F, 0xCC, 0xE5, 0xDE); - - public static readonly Guid CLSID_D2D1Morphology = new Guid(0xEAE6C40D, 0x626A, 0x4C2D, 0xBF, 0xCB, 0x39, 0x10, 0x01, 0xAB, 0xE2, 0x02); - - public static readonly Guid CLSID_D2D1OpacityMetadata = new Guid(0x6C53006A, 0x4450, 0x4199, 0xAA, 0x5B, 0xAD, 0x16, 0x56, 0xFE, 0xCE, 0x5E); - - public static readonly Guid CLSID_D2D1PointDiffuse = new Guid(0xB9E303C3, 0xC08C, 0x4F91, 0x8B, 0x7B, 0x38, 0x65, 0x6B, 0xC4, 0x8C, 0x20); - - public static readonly Guid CLSID_D2D1PointSpecular = new Guid(0x09C3CA26, 0x3AE2, 0x4F09, 0x9E, 0xBC, 0xED, 0x38, 0x65, 0xD5, 0x3F, 0x22); - - public static readonly Guid CLSID_D2D1Premultiply = new Guid(0x06EAB419, 0xDEED, 0x4018, 0x80, 0xD2, 0x3E, 0x1D, 0x47, 0x1A, 0xDE, 0xB2); - - public static readonly Guid CLSID_D2D1Saturation = new Guid(0x5CB2D9CF, 0x327D, 0x459F, 0xA0, 0xCE, 0x40, 0xC0, 0xB2, 0x08, 0x6B, 0xF7); - - public static readonly Guid CLSID_D2D1Shadow = new Guid(0xC67EA361, 0x1863, 0x4E69, 0x89, 0xDB, 0x69, 0x5D, 0x3E, 0x9A, 0x5B, 0x6B); - - public static readonly Guid CLSID_D2D1SpotDiffuse = new Guid(0x818A1105, 0x7932, 0x44F4, 0xAA, 0x86, 0x08, 0xAE, 0x7B, 0x2F, 0x2C, 0x93); - - public static readonly Guid CLSID_D2D1SpotSpecular = new Guid(0xEDAE421E, 0x7654, 0x4A37, 0x9D, 0xB8, 0x71, 0xAC, 0xC1, 0xBE, 0xB3, 0xC1); - - public static readonly Guid CLSID_D2D1TableTransfer = new Guid(0x5BF818C3, 0x5E43, 0x48CB, 0xB6, 0x31, 0x86, 0x83, 0x96, 0xD6, 0xA1, 0xD4); - - public static readonly Guid CLSID_D2D1Tile = new Guid(0xB0784138, 0x3B76, 0x4BC5, 0xB1, 0x3B, 0x0F, 0xA2, 0xAD, 0x02, 0x65, 0x9F); - - public static readonly Guid CLSID_D2D1Turbulence = new Guid(0xCF2BB6AE, 0x889A, 0x4AD7, 0xBA, 0x29, 0xA2, 0xFD, 0x73, 0x2C, 0x9F, 0xC9); - - public static readonly Guid CLSID_D2D1UnPremultiply = new Guid(0xFB9AC489, 0xAD8D, 0x41ED, 0x99, 0x99, 0xBB, 0x63, 0x47, 0xD1, 0x10, 0xF7); - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/EffectBase.cs b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs deleted file mode 100644 index ca5b15971e..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/EffectBase.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Windows.Graphics.Effects; -using Windows.Graphics.Effects.Interop; - - -namespace Avalonia.Win32 -{ - abstract class EffectBase : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop - { - private IGraphicsEffectSource[] _sources; - - public EffectBase(params IGraphicsEffectSource[] sources) - { - _sources = sources; - } - - private IGraphicsEffectSource _source; - - public virtual string Name { get; set; } - - public abstract Guid EffectId { get; } - - public abstract uint PropertyCount { get; } - - public uint SourceCount => (uint)_sources.Length; - - public IGraphicsEffectSource GetSource(uint index) - { - if(index < _sources.Length) - { - return _sources[index]; - } - - return null; - } - - public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) - { - throw new NotImplementedException(); - } - - public abstract object GetProperty(uint index); - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs deleted file mode 100644 index f5d5fc4ad3..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Windows.Graphics.Effects.Interop -{ - public enum GRAPHICS_EFFECT_PROPERTY_MAPPING - { - GRAPHICS_EFFECT_PROPERTY_MAPPING_UNKNOWN, - GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT, - GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORX, - GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORY, - GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORZ, - GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORW, - GRAPHICS_EFFECT_PROPERTY_MAPPING_RECT_TO_VECTOR4, - GRAPHICS_EFFECT_PROPERTY_MAPPING_RADIANS_TO_DEGREES, - GRAPHICS_EFFECT_PROPERTY_MAPPING_COLORMATRIX_ALPHA_MODE, - GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR3, - GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR4 - }; -} - diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs deleted file mode 100644 index 342e68eeb4..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using Windows.Graphics.Effects; - -namespace Avalonia.Win32 -{ - class GaussianBlurEffect : EffectBase - { - public GaussianBlurEffect(IGraphicsEffectSource source) : base(source) - { - } - - enum D2D1_GAUSSIANBLUR_OPTIMIZATION - { - D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED, - D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED, - D2D1_GAUSSIANBLUR_OPTIMIZATION_QUALITY, - D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD - }; - - enum D2D1_BORDER_MODE - { - D2D1_BORDER_MODE_SOFT, - D2D1_BORDER_MODE_HARD, - D2D1_BORDER_MODE_FORCE_DWORD - }; - - enum D2D1GaussianBlurProp - { - D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, - D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION, - D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, - D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD - }; - - public override Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur; - - public override uint PropertyCount => 3; - - public override object GetProperty(uint index) - { - switch ((D2D1GaussianBlurProp)index) - { - case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION: - return 30.0f; - - case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION: - return (uint)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED; - - case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE: - return (uint)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD; - } - - return null; - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs deleted file mode 100644 index 6ab470d81c..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Avalonia.Win32 -{ - internal interface IBlurHost - { - void SetBlur(bool enabled); - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs deleted file mode 100644 index 2eac796376..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using WinRT; - -namespace Windows.UI.Composition.Interop -{ - public struct POINT - { - public int X; - public int Y; - } - - public struct RECT - { - public int left; - public int top; - public int right; - public int bottom; - - public int Width => right - left; - public int Height => bottom - top; - } - - [WindowsRuntimeType] - [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] - public interface ICompositionDrawingSurfaceInterop - { - void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point); - - void EndDraw(); - - void Resize(POINT sizePixels); - - void ResumeDraw(); - - void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY); - - void SuspendDraw(); - } -} - -namespace ABI.Windows.UI.Composition.Interop -{ - using global::System; - using global::System.Runtime.InteropServices; - using global::Windows.UI.Composition; - using global::Windows.UI.Composition.Interop; - - [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] - internal class ICompositionDrawingSurfaceInterop : global::Windows.UI.Composition.Interop.ICompositionDrawingSurfaceInterop - - { - [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")] - public unsafe struct Vftbl - { - public delegate int _BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset); - public delegate int _EndDraw(IntPtr ThisPtr); - public delegate int _Resize(IntPtr ThisPtr, POINT sizePixels); - public delegate int _ResumeDraw(IntPtr ThisPtr); - public delegate int _Scroll(IntPtr ThisPtr, RECT scrollRect, RECT clipRect, int offsetX, int offsetY); - public delegate int _SuspendDraw(IntPtr ThisPtr); - - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public _BeginDraw BeginDraw; - public _EndDraw EndDraw; - public _Resize Resize; - public _ResumeDraw ResumeDraw; - public _Scroll Scroll; - public _SuspendDraw SuspendDraw; - - public static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - - BeginDraw = Do_Abi_BeginDraw, - EndDraw = Do_Abi_EndDraw, - Resize = Do_Abi_Resize - - - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); - } - - private static int Do_Abi_BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset) - { - updateObject = IntPtr.Zero; - return 0; - } - - private static int Do_Abi_EndDraw(IntPtr ThisPtr) - { - return 0; - } - - private static int Do_Abi_Resize(IntPtr ThisPtr, POINT sizePixels) - { - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator ICompositionDrawingSurfaceInterop(IObjectReference obj) => (obj != null) ? new ICompositionDrawingSurfaceInterop(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - - public ICompositionDrawingSurfaceInterop(IObjectReference obj) : this(obj.As()) { } - internal ICompositionDrawingSurfaceInterop(ObjectReference obj) - { - _obj = obj; - } - - public void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, updateRect, ref iid, out updateObject, ref point)); - } - - public void EndDraw() - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.EndDraw(ThisPtr)); - } - - public void Resize(POINT sizePixels) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.Resize(ThisPtr, sizePixels)); - } - - public void ResumeDraw() - { - throw new NotImplementedException(); - } - - public void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY) - { - throw new NotImplementedException(); - } - - public void SuspendDraw() - { - throw new NotImplementedException(); - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs deleted file mode 100644 index 1d4cd3450f..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using WinRT; - -namespace Windows.UI.Composition.Desktop -{ - [WindowsRuntimeType] - [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] - public interface ICompositorDesktopInterop - { - void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test); - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs deleted file mode 100644 index 1c3f06d679..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs +++ /dev/null @@ -1,69 +0,0 @@ -using WinRT; - -namespace ABI.Windows.UI.Composition.Desktop -{ - using global::System; - using global::System.Runtime.InteropServices; - - [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] - internal class ICompositorDesktopInterop : global::Windows.UI.Composition.Desktop.ICompositorDesktopInterop - - { - [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] - public struct Vftbl - { - public delegate int _CreateDesktopWindowTarget(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget); - - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public _CreateDesktopWindowTarget CreateDesktopWindowTarget; - - - public static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - CreateDesktopWindowTarget = Do_Abi_Create_Desktop_Window_Target - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); - } - - private static int Do_Abi_Create_Desktop_Window_Target(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget) - { - try - { - ComWrappersSupport.FindObject(thisPtr).CreateDesktopWindowTarget(hwndTarget, isTopMost != 0, out desktopWindowTarget); - return 0; - } - catch (Exception ex) - { - desktopWindowTarget = IntPtr.Zero; - return Marshal.GetHRForException(ex); - } - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator ICompositorDesktopInterop(IObjectReference obj) => (obj != null) ? new ICompositorDesktopInterop(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public ICompositorDesktopInterop(IObjectReference obj) : this(obj.As()) { } - internal ICompositorDesktopInterop(ObjectReference obj) - { - _obj = obj; - } - - public void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateDesktopWindowTarget(ThisPtr, hwndTarget, isTopmost ? (byte)1 : (byte)0, out test)); - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs deleted file mode 100644 index d9b25e497e..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using WinRT; - -namespace Windows.UI.Composition.Interop -{ - [WindowsRuntimeType] - [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] - public interface ICompositorInterop - { - ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain); - - ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain); - - CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice); - } -} - -namespace ABI.Windows.UI.Composition.Interop -{ - using global::System; - using global::System.Runtime.InteropServices; - using global::Windows.UI.Composition; - - [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] - internal class ICompositorInterop : global::Windows.UI.Composition.Interop.ICompositorInterop - - { - [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] - public struct Vftbl - { - public delegate int _CreateCompositionSurfaceForHandle(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result); - public delegate int _CreateCompositionSurfaceForSwapChain(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result); - public delegate int _CreateGraphicsDevice(IntPtr ThisPtr, IntPtr renderingDevice, out IntPtr result); - - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public _CreateCompositionSurfaceForHandle CreateCompositionSurfaceForHandle; - public _CreateCompositionSurfaceForSwapChain CreateCompositionSurfaceForSwapChain; - public _CreateGraphicsDevice CreateGraphicsDevice; - - - public static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - - CreateCompositionSurfaceForHandle = Do_Abi_Create_Composition_Surface_For_Handle, - CreateCompositionSurfaceForSwapChain = Do_Abi_Create_Composition_Surface_For_SwapChain, - CreateGraphicsDevice= Do_Abi_Create_Graphics_Device - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); - } - - private static int Do_Abi_Create_Composition_Surface_For_Handle(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface) - { - try - { - surface = IntPtr.Zero; - //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForHandle(swapChain); - return 0; - } - catch (Exception ex) - { - surface = IntPtr.Zero; - return Marshal.GetHRForException(ex); - } - } - - private static int Do_Abi_Create_Composition_Surface_For_SwapChain(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface) - { - try - { - surface = IntPtr.Zero; - //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForSwapChain(swapChain); - return 0; - } - catch (Exception ex) - { - surface = IntPtr.Zero; - return Marshal.GetHRForException(ex); - } - } - - private static int Do_Abi_Create_Graphics_Device(IntPtr thisPtr, IntPtr renderingDevice, out IntPtr graphicsDevice) - { - try - { - graphicsDevice = ComWrappersSupport.FindObject(thisPtr).CreateGraphicsDevice(renderingDevice).ThisPtr; - return 0; - } - catch (Exception ex) - { - graphicsDevice = IntPtr.Zero; - return Marshal.GetHRForException(ex); - } - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator ICompositorInterop(IObjectReference obj) => (obj != null) ? new ICompositorInterop(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public ICompositorInterop(IObjectReference obj) : this(obj.As()) { } - internal ICompositorInterop(ObjectReference obj) - { - _obj = obj; - } - - public ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForHandle(ThisPtr, swapChain, out var compositionSurface)); - - return null; - } - - public ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForSwapChain(ThisPtr, swapChain, out var compositionSurface)); - - return null; - } - - public CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateGraphicsDevice(ThisPtr, renderingDevice, out var graphicsDevice)); - - return CompositionGraphicsDevice.FromAbi(graphicsDevice); - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs deleted file mode 100644 index 74d3939a98..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using WinRT; - -namespace Windows.Graphics.Effects.Interop -{ - [WindowsRuntimeType] - [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] - public interface IGraphicsEffectD2D1Interop - { - Guid EffectId { get; } - - uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping); - - object GetProperty(uint index); - - uint PropertyCount { get; } - - IGraphicsEffectSource GetSource(uint index); - - uint SourceCount { get; } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs deleted file mode 100644 index 8466b05fb5..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs +++ /dev/null @@ -1,217 +0,0 @@ -using WinRT; - -namespace ABI.Windows.Graphics.Effects.Interop -{ - using global::System; - using global::System.Runtime.InteropServices; - - [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] - internal class IGraphicsEffectD2D1Interop : global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop - - { - [Guid("2FC57384-A068-44D7-A331-30982FCF7177")] - public struct Vftbl - { - public delegate int _GetEffectId(IntPtr thisPtr, out Guid guid); - public delegate int _GetNamedPropertyMapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping); - public delegate int _GetProperty(IntPtr thisPtr, uint index, out IntPtr value); - public unsafe delegate int _GetPropertyCount(IntPtr thisPtr, uint* count); - public delegate int _GetSource(IntPtr thisPtr, uint index, out IntPtr source); - public delegate int _GetSourceCount(IntPtr thisPtr, out uint count); - - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public _GetEffectId GetEffectId; - public _GetNamedPropertyMapping GetNamedPropertyMapping; - public _GetPropertyCount GetPropertyCount; - public _GetProperty GetProperty; - public _GetSource GetSource; - public _GetSourceCount GetSourceCount; - - public static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - unsafe static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - GetEffectId = Do_Abi_Get_Effect_Id, - GetNamedPropertyMapping = Do_Abi_Get_Property_Mapping, - GetPropertyCount = Do_Abi_Get_Property_Count, - GetProperty = Do_Abi_Get_Property, - GetSource = Do_Abi_Get_Source, - GetSourceCount = Do_Abi_Get_Source_Count - - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); - } - - private static int Do_Abi_Get_Effect_Id(IntPtr thisPtr, out Guid guid) - { - guid = default; - - try - { - guid = ComWrappersSupport.FindObject(thisPtr).EffectId; - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - - private static int Do_Abi_Get_Property_Mapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping) - { - try - { - ComWrappersSupport.FindObject(thisPtr).GetNamedPropertyMapping(MarshalString.FromAbi(name), out var mappingResult); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - - private static int Do_Abi_Get_Property(IntPtr thisPtr, uint index, out IntPtr value) - { - value = default; - - try - { - value = MarshalInspectable.CreateMarshaler( - ComWrappersSupport.FindObject(thisPtr).GetProperty(index)) - .As(Guid.Parse("4BD682DD-7554-40E9-9A9B-82654EDE7E62")) - .GetRef(); - - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - - unsafe private static int Do_Abi_Get_Property_Count(IntPtr thisPtr, uint* count) - { - - try - { - var res = ComWrappersSupport.FindObject(thisPtr).PropertyCount; - - if (count != null) - { - *count = res; - } - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - - private static int Do_Abi_Get_Source(IntPtr thisPtr, uint index, out IntPtr value) - { - value = default; - - try - { - var source = ComWrappersSupport.FindObject(thisPtr).GetSource(index); - - value = MarshalInterface.FromManaged(source); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - - private static int Do_Abi_Get_Source_Count(IntPtr thisPtr, out uint count) - { - count = default; - - try - { - count = ComWrappersSupport.FindObject(thisPtr).SourceCount; - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IGraphicsEffectD2D1Interop(IObjectReference obj) => (obj != null) ? new IGraphicsEffectD2D1Interop(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IGraphicsEffectD2D1Interop(IObjectReference obj) : this(obj.As()) { } - internal IGraphicsEffectD2D1Interop(ObjectReference obj) - { - _obj = obj; - } - - public Guid EffectId - { - get - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetEffectId(ThisPtr, out Guid guid)); - return guid; - } - } - - public uint PropertyCount - { - get - { - unsafe - { - uint count = default; - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetPropertyCount(ThisPtr, &count)); - return count; - } - } - } - - public uint SourceCount - { - get - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSourceCount(ThisPtr, out uint count)); - return count; - } - } - - public uint GetNamedPropertyMapping(string name, out global::Windows.Graphics.Effects.Interop.GRAPHICS_EFFECT_PROPERTY_MAPPING mapping) - { - throw new NotImplementedException(); - } - - public object GetProperty(uint index) - { - // Marshal.ThrowExceptionForHR(_obj.Vftbl.GetProperty(ThisPtr, index, out IntPtr value)); - throw new NotImplementedException(); - } - - public global::Windows.Graphics.Effects.IGraphicsEffectSource GetSource(uint index) - { - throw new NotImplementedException(); - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs deleted file mode 100644 index 3495829f3a..0000000000 --- a/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using Windows.Graphics.Effects; - - -namespace Avalonia.Win32 -{ - class SaturationEffect : EffectBase - { - public SaturationEffect(IGraphicsEffectSource source) : base(source) - { - } - - enum D2D1_SATURATION_PROP - { - D2D1_SATURATION_PROP_SATURATION, - D2D1_SATURATION_PROP_FORCE_DWORD - }; - - public override Guid EffectId => D2DEffects.CLSID_D2D1Saturation; - - public override uint PropertyCount => 1; - - public override object GetProperty(uint index) - { - switch ((D2D1_SATURATION_PROP)index) - { - case D2D1_SATURATION_PROP.D2D1_SATURATION_PROP_SATURATION: - return 2.0f; - } - - return null; - } - } -} - diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index b164bcc611..b21741453c 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1225,6 +1225,9 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibrary(string fileName); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, int flags); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); @@ -1382,6 +1385,9 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern int DwmIsCompositionEnabled(out bool enabled); + [DllImport("dwmapi.dll")] + public static extern void DwmFlush(); + [DllImport("dwmapi.dll")] public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult); @@ -1613,6 +1619,12 @@ namespace Avalonia.Win32.Interop public int X; public int Y; } + + public struct SIZE + { + public int X; + public int Y; + } public struct RECT { diff --git a/src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs b/src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs new file mode 100644 index 0000000000..5577664cc4 --- /dev/null +++ b/src/Windows/Avalonia.Win32/NonPumpingSyncContext.cs @@ -0,0 +1,52 @@ +using System; +using System.Runtime.ConstrainedExecution; +using System.Threading; +using Avalonia.Threading; +using Avalonia.Utilities; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32 +{ + internal class NonPumpingSyncContext : SynchronizationContext, IDisposable + { + private readonly SynchronizationContext _inner; + + private NonPumpingSyncContext(SynchronizationContext inner) + { + _inner = inner; + SetWaitNotificationRequired(); + SetSynchronizationContext(this); + } + + public override void Post(SendOrPostCallback d, object state) => _inner.Post(d, state); + public override void Send(SendOrPostCallback d, object state) => _inner.Send(d, state); + + [PrePrepareMethod] + public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + { + return UnmanagedMethods.WaitForMultipleObjectsEx(waitHandles.Length, waitHandles, waitAll, + millisecondsTimeout, false); + } + + public void Dispose() => SetSynchronizationContext(_inner); + + public static IDisposable Use() + { + var current = Current; + if (current == null) + { + if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) + return null; + } + if (current is NonPumpingSyncContext) + return null; + + return new NonPumpingSyncContext(current); + } + + internal class HelperImpl : NonPumpingLockHelper.IHelperImpl + { + IDisposable NonPumpingLockHelper.IHelperImpl.Use() => NonPumpingSyncContext.Use(); + } + } +} diff --git a/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs b/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs deleted file mode 100644 index a0160fcfbd..0000000000 --- a/src/Windows/Avalonia.Win32/NonPumpingWaitProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Avalonia.Threading; -using Avalonia.Win32.Interop; - -namespace Avalonia.Win32 -{ - internal class NonPumpingWaitProvider : AvaloniaSynchronizationContext.INonPumpingPlatformWaitProvider - { - public int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) - { - return UnmanagedMethods.WaitForMultipleObjectsEx(waitHandles.Length, waitHandles, waitAll, - millisecondsTimeout, false); - } - } -} diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs index 7fb146899b..dd3fd1342c 100644 --- a/src/Windows/Avalonia.Win32/PopupImpl.cs +++ b/src/Windows/Avalonia.Win32/PopupImpl.cs @@ -17,8 +17,9 @@ namespace Avalonia.Win32 [ThreadStatic] private static IntPtr s_parentHandle; - public override void Show() + public override void Show(bool activate) { + // Popups are always shown non-activated. UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); // We need to steal focus if it's held by a child window of our toplevel window diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs index 4363b5fd29..b51f80e397 100644 --- a/src/Windows/Avalonia.Win32/Win32GlManager.cs +++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs @@ -3,6 +3,7 @@ using Avalonia.OpenGL; using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Egl; using Avalonia.Win32.OpenGl; +using Avalonia.Win32.WinRT.Composition; namespace Avalonia.Win32 { @@ -24,13 +25,8 @@ namespace Avalonia.Win32 var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); if (egl is { } && - opts?.UseWindowsUIComposition == true) - { - var compositionConnector = CompositionConnector.TryCreate(egl); - - if (compositionConnector != null) - AvaloniaLocator.CurrentMutable.BindToSelf(compositionConnector); - } + opts?.UseWindowsUIComposition == true) + WinUICompositorConnection.TryCreateAndRegister(egl); return egl; } diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index e854ceae0b..97eb0c6119 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -16,6 +16,7 @@ using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; +using Avalonia.Utilities; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; @@ -110,7 +111,7 @@ namespace Avalonia.Win32 .Bind().ToConstant(s_instance) .Bind().ToSingleton() .Bind().ToConstant(s_instance) - .Bind().ToConstant(new NonPumpingWaitProvider()) + .Bind().ToConstant(new NonPumpingSyncContext.HelperImpl()) .Bind().ToConstant(new WindowsMountedVolumeInfoProvider()); Win32GlManager.Initialize(); @@ -242,7 +243,7 @@ namespace Avalonia.Win32 public IWindowImpl CreateEmbeddableWindow() { var embedded = new EmbeddedWindowImpl(); - embedded.Show(); + embedded.Show(true); return embedded; } diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs new file mode 100644 index 0000000000..bef5a55b06 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs @@ -0,0 +1,130 @@ +using System; + +namespace Avalonia.Win32.WinRT.Composition +{ + class D2DEffects + { + public static readonly Guid CLSID_D2D12DAffineTransform = + new Guid(0x6AA97485, 0x6354, 0x4CFC, 0x90, 0x8C, 0xE4, 0xA7, 0x4F, 0x62, 0xC9, 0x6C); + + public static readonly Guid CLSID_D2D13DPerspectiveTransform = + new Guid(0xC2844D0B, 0x3D86, 0x46E7, 0x85, 0xBA, 0x52, 0x6C, 0x92, 0x40, 0xF3, 0xFB); + + public static readonly Guid CLSID_D2D13DTransform = + new Guid(0xE8467B04, 0xEC61, 0x4B8A, 0xB5, 0xDE, 0xD4, 0xD7, 0x3D, 0xEB, 0xEA, 0x5A); + + public static readonly Guid CLSID_D2D1ArithmeticComposite = + new Guid(0xFC151437, 0x049A, 0x4784, 0xA2, 0x4A, 0xF1, 0xC4, 0xDA, 0xF2, 0x09, 0x87); + + public static readonly Guid CLSID_D2D1Atlas = + new Guid(0x913E2BE4, 0xFDCF, 0x4FE2, 0xA5, 0xF0, 0x24, 0x54, 0xF1, 0x4F, 0xF4, 0x08); + + public static readonly Guid CLSID_D2D1BitmapSource = + new Guid(0x5FB6C24D, 0xC6DD, 0x4231, 0x94, 0x4, 0x50, 0xF4, 0xD5, 0xC3, 0x25, 0x2D); + + public static readonly Guid CLSID_D2D1Blend = + new Guid(0x81C5B77B, 0x13F8, 0x4CDD, 0xAD, 0x20, 0xC8, 0x90, 0x54, 0x7A, 0xC6, 0x5D); + + public static readonly Guid CLSID_D2D1Border = + new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27); + + public static readonly Guid CLSID_D2D1Brightness = + new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87); + + public static readonly Guid CLSID_D2D1ColorManagement = + new Guid(0x1A28524C, 0xFDD6, 0x4AA4, 0xAE, 0x8F, 0x83, 0x7E, 0xB8, 0x26, 0x7B, 0x37); + + public static readonly Guid CLSID_D2D1ColorMatrix = + new Guid(0x921F03D6, 0x641C, 0x47DF, 0x85, 0x2D, 0xB4, 0xBB, 0x61, 0x53, 0xAE, 0x11); + + public static readonly Guid CLSID_D2D1Composite = + new Guid(0x48FC9F51, 0xF6AC, 0x48F1, 0x8B, 0x58, 0x3B, 0x28, 0xAC, 0x46, 0xF7, 0x6D); + + public static readonly Guid CLSID_D2D1ConvolveMatrix = + new Guid(0x407F8C08, 0x5533, 0x4331, 0xA3, 0x41, 0x23, 0xCC, 0x38, 0x77, 0x84, 0x3E); + + public static readonly Guid CLSID_D2D1Crop = + new Guid(0xE23F7110, 0x0E9A, 0x4324, 0xAF, 0x47, 0x6A, 0x2C, 0x0C, 0x46, 0xF3, 0x5B); + + public static readonly Guid CLSID_D2D1DirectionalBlur = + new Guid(0x174319A6, 0x58E9, 0x49B2, 0xBB, 0x63, 0xCA, 0xF2, 0xC8, 0x11, 0xA3, 0xDB); + + public static readonly Guid CLSID_D2D1DiscreteTransfer = + new Guid(0x90866FCD, 0x488E, 0x454B, 0xAF, 0x06, 0xE5, 0x04, 0x1B, 0x66, 0xC3, 0x6C); + + public static readonly Guid CLSID_D2D1DisplacementMap = + new Guid(0xEDC48364, 0x417, 0x4111, 0x94, 0x50, 0x43, 0x84, 0x5F, 0xA9, 0xF8, 0x90); + + public static readonly Guid CLSID_D2D1DistantDiffuse = + new Guid(0x3E7EFD62, 0xA32D, 0x46D4, 0xA8, 0x3C, 0x52, 0x78, 0x88, 0x9A, 0xC9, 0x54); + + public static readonly Guid CLSID_D2D1DistantSpecular = + new Guid(0x428C1EE5, 0x77B8, 0x4450, 0x8A, 0xB5, 0x72, 0x21, 0x9C, 0x21, 0xAB, 0xDA); + + public static readonly Guid CLSID_D2D1DpiCompensation = + new Guid(0x6C26C5C7, 0x34E0, 0x46FC, 0x9C, 0xFD, 0xE5, 0x82, 0x37, 0x6, 0xE2, 0x28); + + public static readonly Guid CLSID_D2D1Flood = + new Guid(0x61C23C20, 0xAE69, 0x4D8E, 0x94, 0xCF, 0x50, 0x07, 0x8D, 0xF6, 0x38, 0xF2); + + public static readonly Guid CLSID_D2D1GammaTransfer = + new Guid(0x409444C4, 0xC419, 0x41A0, 0xB0, 0xC1, 0x8C, 0xD0, 0xC0, 0xA1, 0x8E, 0x42); + + public static readonly Guid CLSID_D2D1GaussianBlur = + new Guid(0x1FEB6D69, 0x2FE6, 0x4AC9, 0x8C, 0x58, 0x1D, 0x7F, 0x93, 0xE7, 0xA6, 0xA5); + + public static readonly Guid CLSID_D2D1Scale = + new Guid(0x9DAF9369, 0x3846, 0x4D0E, 0xA4, 0x4E, 0xC, 0x60, 0x79, 0x34, 0xA5, 0xD7); + + public static readonly Guid CLSID_D2D1Histogram = + new Guid(0x881DB7D0, 0xF7EE, 0x4D4D, 0xA6, 0xD2, 0x46, 0x97, 0xAC, 0xC6, 0x6E, 0xE8); + + public static readonly Guid CLSID_D2D1HueRotation = + new Guid(0x0F4458EC, 0x4B32, 0x491B, 0x9E, 0x85, 0xBD, 0x73, 0xF4, 0x4D, 0x3E, 0xB6); + + public static readonly Guid CLSID_D2D1LinearTransfer = + new Guid(0xAD47C8FD, 0x63EF, 0x4ACC, 0x9B, 0x51, 0x67, 0x97, 0x9C, 0x03, 0x6C, 0x06); + + public static readonly Guid CLSID_D2D1LuminanceToAlpha = + new Guid(0x41251AB7, 0x0BEB, 0x46F8, 0x9D, 0xA7, 0x59, 0xE9, 0x3F, 0xCC, 0xE5, 0xDE); + + public static readonly Guid CLSID_D2D1Morphology = + new Guid(0xEAE6C40D, 0x626A, 0x4C2D, 0xBF, 0xCB, 0x39, 0x10, 0x01, 0xAB, 0xE2, 0x02); + + public static readonly Guid CLSID_D2D1OpacityMetadata = + new Guid(0x6C53006A, 0x4450, 0x4199, 0xAA, 0x5B, 0xAD, 0x16, 0x56, 0xFE, 0xCE, 0x5E); + + public static readonly Guid CLSID_D2D1PointDiffuse = + new Guid(0xB9E303C3, 0xC08C, 0x4F91, 0x8B, 0x7B, 0x38, 0x65, 0x6B, 0xC4, 0x8C, 0x20); + + public static readonly Guid CLSID_D2D1PointSpecular = + new Guid(0x09C3CA26, 0x3AE2, 0x4F09, 0x9E, 0xBC, 0xED, 0x38, 0x65, 0xD5, 0x3F, 0x22); + + public static readonly Guid CLSID_D2D1Premultiply = + new Guid(0x06EAB419, 0xDEED, 0x4018, 0x80, 0xD2, 0x3E, 0x1D, 0x47, 0x1A, 0xDE, 0xB2); + + public static readonly Guid CLSID_D2D1Saturation = + new Guid(0x5CB2D9CF, 0x327D, 0x459F, 0xA0, 0xCE, 0x40, 0xC0, 0xB2, 0x08, 0x6B, 0xF7); + + public static readonly Guid CLSID_D2D1Shadow = + new Guid(0xC67EA361, 0x1863, 0x4E69, 0x89, 0xDB, 0x69, 0x5D, 0x3E, 0x9A, 0x5B, 0x6B); + + public static readonly Guid CLSID_D2D1SpotDiffuse = + new Guid(0x818A1105, 0x7932, 0x44F4, 0xAA, 0x86, 0x08, 0xAE, 0x7B, 0x2F, 0x2C, 0x93); + + public static readonly Guid CLSID_D2D1SpotSpecular = + new Guid(0xEDAE421E, 0x7654, 0x4A37, 0x9D, 0xB8, 0x71, 0xAC, 0xC1, 0xBE, 0xB3, 0xC1); + + public static readonly Guid CLSID_D2D1TableTransfer = + new Guid(0x5BF818C3, 0x5E43, 0x48CB, 0xB6, 0x31, 0x86, 0x83, 0x96, 0xD6, 0xA1, 0xD4); + + public static readonly Guid CLSID_D2D1Tile = + new Guid(0xB0784138, 0x3B76, 0x4BC5, 0xB1, 0x3B, 0x0F, 0xA2, 0xAD, 0x02, 0x65, 0x9F); + + public static readonly Guid CLSID_D2D1Turbulence = + new Guid(0xCF2BB6AE, 0x889A, 0x4AD7, 0xBA, 0x29, 0xA2, 0xFD, 0x73, 0x2C, 0x9F, 0xC9); + + public static readonly Guid CLSID_D2D1UnPremultiply = + new Guid(0xFB9AC489, 0xAD8D, 0x41ED, 0x99, 0x99, 0xBB, 0x63, 0x47, 0xD1, 0x10, 0xF7); + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs new file mode 100644 index 0000000000..4ae9c08410 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositedWindow.cs @@ -0,0 +1,99 @@ +using System; +using System.Numerics; +using System.Reactive.Disposables; +using System.Threading; +using Avalonia.MicroCom; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Egl; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32.WinRT.Composition +{ + public class WinUICompositedWindow : IDisposable + { + private EglContext _syncContext; + private readonly object _pumpLock; + private readonly IVisual _blurVisual; + private ICompositionTarget _compositionTarget; + private IVisual _contentVisual; + private ICompositionDrawingSurfaceInterop _surfaceInterop; + private PixelSize _size; + + private static Guid IID_ID3D11Texture2D = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); + private ICompositor _compositor; + + + internal WinUICompositedWindow(EglContext syncContext, + ICompositor compositor, + object pumpLock, + ICompositionTarget compositionTarget, + ICompositionDrawingSurfaceInterop surfaceInterop, + IVisual contentVisual, IVisual blurVisual) + { + _compositor = compositor.CloneReference(); + _syncContext = syncContext; + _pumpLock = pumpLock; + _blurVisual = blurVisual.CloneReference(); + _compositionTarget = compositionTarget.CloneReference(); + _contentVisual = contentVisual.CloneReference(); + _surfaceInterop = surfaceInterop.CloneReference(); + } + + + public void ResizeIfNeeded(PixelSize size) + { + using (_syncContext.EnsureLocked()) + { + if (_size != size) + { + _surfaceInterop.Resize(new UnmanagedMethods.POINT { X = size.Width, Y = size.Height }); + _contentVisual.SetSize(new Vector2(size.Width, size.Height)); + _size = size; + } + } + } + + public unsafe IUnknown BeginDrawToTexture(out PixelPoint offset) + { + if (!_syncContext.IsCurrent) + throw new InvalidOperationException(); + + var iid = IID_ID3D11Texture2D; + void* pTexture; + var off = _surfaceInterop.BeginDraw(null, &iid, &pTexture); + offset = new PixelPoint(off.X, off.Y); + return MicroComRuntime.CreateProxyFor(pTexture, true); + } + + public void EndDraw() + { + if (!_syncContext.IsCurrent) + throw new InvalidOperationException(); + _surfaceInterop.EndDraw(); + } + + public void SetBlur(bool enable) + { + using (_syncContext.EnsureLocked()) + _blurVisual.SetIsVisible(enable ? 1 : 0); + } + + public IDisposable BeginTransaction() + { + Monitor.Enter(_pumpLock); + return Disposable.Create(() => Monitor.Exit(_pumpLock)); + } + + public void Dispose() + { + if (_syncContext == null) + { + _compositor.Dispose(); + _blurVisual.Dispose(); + _contentVisual.Dispose(); + _surfaceInterop.Dispose(); + _compositionTarget.Dispose(); + } + } + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs new file mode 100644 index 0000000000..2aa82436f6 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -0,0 +1,238 @@ +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Logging; +using Avalonia.MicroCom; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Angle; +using Avalonia.OpenGL.Egl; +using Avalonia.Rendering; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32.WinRT.Composition +{ + class WinUICompositorConnection : IRenderTimer + { + private readonly EglContext _syncContext; + private IntPtr _queue; + private ICompositor _compositor; + private ICompositor2 _compositor2; + private ICompositor5 _compositor5; + private ICompositorInterop _compositorInterop; + private AngleWin32EglDisplay _angle; + private ICompositionGraphicsDevice _device; + private EglPlatformOpenGlInterface _gl; + private ICompositorDesktopInterop _compositorDesktopInterop; + private ICompositionBrush _blurBrush; + private object _pumpLock = new object(); + + public WinUICompositorConnection(EglPlatformOpenGlInterface gl, object pumpLock) + { + _gl = gl; + _pumpLock = pumpLock; + _syncContext = _gl.PrimaryEglContext; + _angle = (AngleWin32EglDisplay)_gl.Display; + _compositor = NativeWinRTMethods.CreateInstance("Windows.UI.Composition.Compositor"); + _compositor2 = _compositor.QueryInterface(); + _compositor5 = _compositor.QueryInterface(); + _compositorInterop = _compositor.QueryInterface(); + _compositorDesktopInterop = _compositor.QueryInterface(); + using var device = MicroComRuntime.CreateProxyFor(_angle.GetDirect3DDevice(), true); + + _device = _compositorInterop.CreateGraphicsDevice(device); + _blurBrush = CreateBlurBrush(); + + } + + public EglPlatformOpenGlInterface Egl => _gl; + + static bool TryCreateAndRegisterCore(EglPlatformOpenGlInterface angle) + { + var tcs = new TaskCompletionSource(); + var pumpLock = new object(); + var th = new Thread(() => + { + WinUICompositorConnection connect; + try + { + NativeWinRTMethods.CreateDispatcherQueueController(new NativeWinRTMethods.DispatcherQueueOptions + { + apartmentType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE, + dwSize = Marshal.SizeOf(), + threadType = NativeWinRTMethods.DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT + }); + connect = new WinUICompositorConnection(angle, pumpLock); + AvaloniaLocator.CurrentMutable.BindToSelf(connect); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(connect); + tcs.SetResult(true); + + } + catch (Exception e) + { + tcs.SetException(e); + return; + } + connect.RunLoop(); + }) + { + IsBackground = true + }; + th.SetApartmentState(ApartmentState.STA); + th.Start(); + return tcs.Task.Result; + } + + class RunLoopHandler : IAsyncActionCompletedHandler, IMicroComShadowContainer + { + private readonly WinUICompositorConnection _parent; + private Stopwatch _st = Stopwatch.StartNew(); + + public RunLoopHandler(WinUICompositorConnection parent) + { + _parent = parent; + } + public void Dispose() + { + + } + + public void Invoke(IAsyncAction asyncInfo, AsyncStatus asyncStatus) + { + _parent.Tick?.Invoke(_st.Elapsed); + using var act = _parent._compositor5.RequestCommitAsync(); + act.SetCompleted(this); + } + + public MicroComShadow Shadow { get; set; } + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() + { + } + } + + private void RunLoop() + { + { + var st = Stopwatch.StartNew(); + using (var act = _compositor5.RequestCommitAsync()) + act.SetCompleted(new RunLoopHandler(this)); + while (true) + { + UnmanagedMethods.GetMessage(out var msg, IntPtr.Zero, 0, 0); + lock (_pumpLock) + UnmanagedMethods.DispatchMessage(ref msg); + } + } + } + + public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle) + { + const int majorRequired = 10; + const int buildRequired = 17134; + + var majorInstalled = Win32Platform.WindowsVersion.Major; + var buildInstalled = Win32Platform.WindowsVersion.Build; + + if (majorInstalled >= majorRequired && + buildInstalled >= buildRequired) + { + try + { + TryCreateAndRegisterCore(angle); + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error, "WinUIComposition") + ?.Log(null, "Unable to initialize WinUI compositor: {0}", e); + + } + } + + var osVersionNotice = + $"Windows {majorRequired} Build {buildRequired} is required. Your machine has Windows {majorInstalled} Build {buildInstalled} installed."; + + Logger.TryGet(LogEventLevel.Warning, "WinUIComposition")?.Log(null, + $"Unable to initialize WinUI compositor: {osVersionNotice}"); + } + + + public WinUICompositedWindow CreateWindow(IntPtr hWnd) + { + using var sc = _syncContext.EnsureLocked(); + using var desktopTarget = _compositorDesktopInterop.CreateDesktopWindowTarget(hWnd, 0); + using var target = desktopTarget.QueryInterface(); + + using var drawingSurface = _device.CreateDrawingSurface(new UnmanagedMethods.SIZE(), DirectXPixelFormat.B8G8R8A8UIntNormalized, + DirectXAlphaMode.Premultiplied); + using var surface = drawingSurface.QueryInterface(); + using var surfaceInterop = drawingSurface.QueryInterface(); + + using var surfaceBrush = _compositor.CreateSurfaceBrushWithSurface(surface); + using var brush = surfaceBrush.QueryInterface(); + + using var spriteVisual = _compositor.CreateSpriteVisual(); + spriteVisual.SetBrush(brush); + using var visual = spriteVisual.QueryInterface(); + using var visual2 = spriteVisual.QueryInterface(); + using var container = _compositor.CreateContainerVisual(); + using var containerVisual = container.QueryInterface(); + using var containerVisual2 = container.QueryInterface(); + containerVisual2.SetRelativeSizeAdjustment(new Vector2(1, 1)); + using var containerChildren = container.Children; + + target.SetRoot(containerVisual); + + using var blur = CreateBlurVisual(); + + containerChildren.InsertAtTop(blur); + containerChildren.InsertAtTop(visual); + + return new WinUICompositedWindow(_syncContext, _compositor, _pumpLock, target, surfaceInterop, visual, blur); + } + + + private unsafe ICompositionBrush CreateBlurBrush() + { + using var backDropParameterFactory = NativeWinRTMethods.CreateActivationFactory( + "Windows.UI.Composition.CompositionEffectSourceParameter"); + using var backdropString = new HStringInterop("backdrop"); + using var backDropParameter = + backDropParameterFactory.Create(backdropString.Handle); + using var backDropParameterAsSource = backDropParameter.QueryInterface(); + var blurEffect = new WinUIGaussianBlurEffect(backDropParameterAsSource); + using var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect); + using var backdrop = _compositor2.CreateBackdropBrush(); + using var backdropBrush = backdrop.QueryInterface(); + + + var saturateEffect = new SaturationEffect(blurEffect); + using var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect); + using var sat = satEffectFactory.CreateBrush(); + sat.SetSourceParameter(backdropString.Handle, backdropBrush); + return sat.QueryInterface(); + } + + private unsafe IVisual CreateBlurVisual() + { + using var spriteVisual = _compositor.CreateSpriteVisual(); + using var visual = spriteVisual.QueryInterface(); + using var visual2 = spriteVisual.QueryInterface(); + + + spriteVisual.SetBrush(_blurBrush); + visual.SetIsVisible(0); + visual2.SetRelativeSizeAdjustment(new Vector2(1.0f, 1.0f)); + + return visual.CloneReference(); + } + + + public event Action Tick; + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs new file mode 100644 index 0000000000..ea75a2f311 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using Avalonia.MicroCom; + +namespace Avalonia.Win32.WinRT.Composition +{ + abstract class WinUIEffectBase : WinRTInspectable, IGraphicsEffect, IGraphicsEffectSource, IGraphicsEffectD2D1Interop + { + private IGraphicsEffectSource[] _sources; + + public WinUIEffectBase(params IGraphicsEffectSource[] _sources) + { + this._sources = _sources.Select(e => + { + if (e is WinUIEffectBase) + return e; + return e.CloneReference(); + }).ToArray(); + } + + public IntPtr Name => IntPtr.Zero; + + public void SetName(IntPtr name) + { + + } + + public abstract Guid EffectId { get; } + public unsafe void GetNamedPropertyMapping(IntPtr name, uint* index, GRAPHICS_EFFECT_PROPERTY_MAPPING* mapping) => + throw new COMException("Not supported", unchecked((int)0x80004001)); + + public abstract uint PropertyCount { get; } + public abstract IPropertyValue GetProperty(uint index); + + public IGraphicsEffectSource GetSource(uint index) + { + if (_sources == null || index> _sources.Length) + throw new COMException("Invalid index", unchecked((int)0x80070057)); + return _sources[index]; + } + + public uint SourceCount => (uint)(_sources?.Length ?? 0); + + public override void OnUnreferencedFromNative() + { + if (_sources == null) + return; + + /*foreach(var s in _sources) + s.Dispose();*/ + _sources = null; + } + } + + class WinUIGaussianBlurEffect : WinUIEffectBase + { + public WinUIGaussianBlurEffect(IGraphicsEffectSource source) : base(source) + { + } + + enum D2D1_GAUSSIANBLUR_OPTIMIZATION + { + D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED, + D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED, + D2D1_GAUSSIANBLUR_OPTIMIZATION_QUALITY, + D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD + }; + + enum D2D1_BORDER_MODE + { + D2D1_BORDER_MODE_SOFT, + D2D1_BORDER_MODE_HARD, + D2D1_BORDER_MODE_FORCE_DWORD + }; + + enum D2D1GaussianBlurProp + { + D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, + D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION, + D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, + D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD + }; + + public override Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur; + + public override uint PropertyCount => 3; + + public override IPropertyValue GetProperty(uint index) + { + switch ((D2D1GaussianBlurProp)index) + { + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION: + return new WinRTPropertyValue(30.0f); + + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION: + return new WinRTPropertyValue((uint)D2D1_GAUSSIANBLUR_OPTIMIZATION + .D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED); + + case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE: + return new WinRTPropertyValue((uint)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD); + } + + return null; + } + } + + class SaturationEffect : WinUIEffectBase + { + public SaturationEffect(IGraphicsEffectSource source) : base(source) + { + } + + enum D2D1_SATURATION_PROP + { + D2D1_SATURATION_PROP_SATURATION, + D2D1_SATURATION_PROP_FORCE_DWORD + }; + + public override Guid EffectId => D2DEffects.CLSID_D2D1Saturation; + + public override uint PropertyCount => 1; + + public override IPropertyValue GetProperty(uint index) + { + switch ((D2D1_SATURATION_PROP)index) + { + case D2D1_SATURATION_PROP.D2D1_SATURATION_PROP_SATURATION: + return new WinRTPropertyValue(2.0f); + } + + return null; + } + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs new file mode 100644 index 0000000000..f59d50860a --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindowSurface.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.MicroCom; +using Avalonia.OpenGL.Angle; +using Avalonia.OpenGL.Egl; +using Avalonia.OpenGL.Surfaces; +using Avalonia.Utilities; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32.WinRT.Composition +{ + internal class WinUiCompositedWindowSurface : EglGlPlatformSurfaceBase, IBlurHost, IDisposable + { + private readonly WinUICompositorConnection _connection; + private EglPlatformOpenGlInterface _egl; + private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; + private IRef _window; + private bool _enableBlur; + + public WinUiCompositedWindowSurface(WinUICompositorConnection connection, IEglWindowGlPlatformSurfaceInfo info) : base() + { + _connection = connection; + _egl = connection.Egl; + _info = info; + } + + public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() + { + using (_egl.PrimaryContext.EnsureCurrent()) + { + if (_window?.Item == null) + { + _window = RefCountable.Create(_connection.CreateWindow(_info.Handle)); + _window.Item.SetBlur(_enableBlur); + } + + return new CompositionRenderTarget(_egl, _window, _info); + } + } + + class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase + { + private readonly EglPlatformOpenGlInterface _egl; + private readonly IRef _window; + private readonly IEglWindowGlPlatformSurfaceInfo _info; + + public CompositionRenderTarget(EglPlatformOpenGlInterface egl, + IRef window, + IEglWindowGlPlatformSurfaceInfo info) + : base(egl) + { + _egl = egl; + _window = window.Clone(); + _info = info; + _window.Item.ResizeIfNeeded(_info.Size); + } + + public override IGlPlatformSurfaceRenderingSession BeginDraw() + { + var contextLock = _egl.PrimaryEglContext.EnsureCurrent(); + IUnknown texture = null; + EglSurface surface = null; + IDisposable transaction = null; + var success = false; + try + { + if (_window?.Item == null) + throw new ObjectDisposedException(GetType().FullName); + + var size = _info.Size; + transaction = _window.Item.BeginTransaction(); + _window.Item.ResizeIfNeeded(size); + texture = _window.Item.BeginDrawToTexture(out var offset); + + surface = ((AngleWin32EglDisplay) _egl.Display).WrapDirect3D11Texture(_egl, + texture.GetNativeIntPtr(), + offset.X, offset.Y, size.Width, size.Height); + + var res = base.BeginDraw(surface, _info, () => + { + surface?.Dispose(); + texture?.Dispose(); + _window.Item.EndDraw(); + transaction?.Dispose(); + contextLock?.Dispose(); + }, true); + success = true; + return res; + } + finally + { + if (!success) + { + surface?.Dispose(); + texture?.Dispose(); + transaction?.Dispose(); + contextLock.Dispose(); + } + } + } + } + + public void SetBlur(bool enable) + { + _enableBlur = enable; + _window?.Item?.SetBlur(enable); + } + + public void Dispose() + { + using (_egl.PrimaryEglContext.EnsureLocked()) + { + _window?.Dispose(); + _window = null; + } + } + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs new file mode 100644 index 0000000000..81c0e3e185 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs @@ -0,0 +1,7 @@ +namespace Avalonia.Win32.WinRT +{ + public interface IBlurHost + { + void SetBlur(bool enable); + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs new file mode 100644 index 0000000000..087bd2fd43 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using Avalonia.MicroCom; +using Avalonia.Win32.Interop; + +namespace Avalonia.Win32.WinRT +{ + internal static class NativeWinRTMethods + { + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, + PreserveSig = false)] + internal static extern unsafe IntPtr WindowsCreateString( + [MarshalAs(UnmanagedType.LPWStr)] string sourceString, + int length); + + internal static IntPtr WindowsCreateString(string sourceString) + => WindowsCreateString(sourceString, sourceString.Length); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", + CallingConvention = CallingConvention.StdCall, PreserveSig = false)] + internal static extern unsafe IntPtr WindowsDeleteString(IntPtr hString); + + [DllImport("Windows.UI.Composition", EntryPoint = "DllGetActivationFactory", + CallingConvention = CallingConvention.StdCall, PreserveSig = false)] + private extern static IntPtr GetWindowsUICompositionActivationFactory( + IntPtr activatableClassId); + + internal static IActivationFactory GetWindowsUICompositionActivationFactory(string className) + {//"Windows.UI.Composition.Compositor" + var s = WindowsCreateString(className); + var factory = GetWindowsUICompositionActivationFactory(s); + return MicroComRuntime.CreateProxyFor(factory, true); + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + delegate int GetActivationFactoryDelegate(IntPtr classId, out IntPtr ppv); + + internal static T CreateInstance(string fullName) where T : IUnknown + { + var s = WindowsCreateString(fullName); + EnsureRoInitialized(); + var pUnk = RoActivateInstance(s); + using var unk = MicroComRuntime.CreateProxyFor(pUnk, true); + WindowsDeleteString(s); + return MicroComRuntime.QueryInterface(unk); + } + + internal static TFactory CreateActivationFactory(string fullName) where TFactory : IUnknown + { + var s = WindowsCreateString(fullName); + EnsureRoInitialized(); + var guid = MicroComRuntime.GetGuidFor(typeof(TFactory)); + var pUnk = RoGetActivationFactory(s, ref guid); + using var unk = MicroComRuntime.CreateProxyFor(pUnk, true); + WindowsDeleteString(s); + return MicroComRuntime.QueryInterface(unk); + } + + internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE + { + DQTAT_COM_NONE = 0, + DQTAT_COM_ASTA = 1, + DQTAT_COM_STA = 2 + }; + + internal enum DISPATCHERQUEUE_THREAD_TYPE + { + DQTYPE_THREAD_DEDICATED = 1, + DQTYPE_THREAD_CURRENT = 2, + }; + + [StructLayout(LayoutKind.Sequential)] + internal struct DispatcherQueueOptions + { + public int dwSize; + + [MarshalAs(UnmanagedType.I4)] + public DISPATCHERQUEUE_THREAD_TYPE threadType; + + [MarshalAs(UnmanagedType.I4)] + public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; + }; + + [DllImport("coremessaging.dll", PreserveSig = false)] + internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options); + + internal enum RO_INIT_TYPE + { + RO_INIT_SINGLETHREADED = 0, // Single-threaded application + RO_INIT_MULTITHREADED = 1, // COM calls objects on any thread. + } + + [DllImport("combase.dll", PreserveSig = false)] + private static extern void RoInitialize(RO_INIT_TYPE initType); + + [DllImport("combase.dll", PreserveSig = false)] + private static extern IntPtr RoActivateInstance(IntPtr activatableClassId); + + [DllImport("combase.dll", PreserveSig = false)] + private static extern IntPtr RoGetActivationFactory(IntPtr activatableClassId, ref Guid iid); + + private static bool _initialized; + private static void EnsureRoInitialized() + { + if (_initialized) + return; + RoInitialize(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA ? + RO_INIT_TYPE.RO_INIT_SINGLETHREADED : + RO_INIT_TYPE.RO_INIT_MULTITHREADED); + _initialized = true; + } + } + + class HStringInterop : IDisposable + { + private IntPtr _s; + + public HStringInterop(string s) + { + _s = s == null ? IntPtr.Zero : NativeWinRTMethods.WindowsCreateString(s); + } + + public IntPtr Handle => _s; + + public void Dispose() + { + if (_s != IntPtr.Zero) + { + NativeWinRTMethods.WindowsDeleteString(_s); + _s = IntPtr.Zero; + } + } + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs new file mode 100644 index 0000000000..786d698daa --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/WinRTColor.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; + +namespace Avalonia.Win32.WinRT +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct WinRTColor + { + public byte A; + public byte R; + public byte G; + public byte B; + + public static WinRTColor FromArgb(byte a, byte r, byte g, byte b) => new WinRTColor() + { + A = a, R = r, G = g, B = b + }; + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs new file mode 100644 index 0000000000..d2ec957b8e --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/WinRTInspectable.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Avalonia.MicroCom; + +namespace Avalonia.Win32.WinRT +{ + class WinRTInspectable : IInspectable, IMicroComShadowContainer + { + public virtual void Dispose() + { + + } + + public unsafe void GetIids(ulong* iidCount, Guid** iids) + { + var interfaces = GetType().GetInterfaces().Where(typeof(IUnknown).IsAssignableFrom) + .Select(MicroComRuntime.GetGuidFor).ToArray(); + var mem = (Guid*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * interfaces.Length); + for (var c = 0; c < interfaces.Length; c++) + mem[c] = interfaces[c]; + *iids = mem; + *iidCount = (ulong) interfaces.Length; + } + + public IntPtr RuntimeClassName => NativeWinRTMethods.WindowsCreateString(GetType().FullName); + public TrustLevel TrustLevel => TrustLevel.BaseTrust; + public MicroComShadow Shadow { get; set; } + public virtual void OnReferencedFromNative() + { + } + + public virtual void OnUnreferencedFromNative() + { + } + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs b/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs new file mode 100644 index 0000000000..684e7ff7b5 --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs @@ -0,0 +1,89 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Win32.WinRT +{ + class WinRTPropertyValue : WinRTInspectable, IPropertyValue + { + public WinRTPropertyValue(float f) + { + Type = PropertyType.Single; + Single = f; + } + + public WinRTPropertyValue(uint u) + { + UInt32 = u; + Type = PropertyType.UInt32; + } + + public PropertyType Type { get; } + public int IsNumericScalar { get; } + public byte UInt8 { get; } + public short Int16 { get; } + public ushort UInt16 { get; } + public int Int32 { get; } + public uint UInt32 { get; } + public long Int64 { get; } + public ulong UInt64 { get; } + public float Single { get; } + public double Double { get; } + public char Char16 { get; } + public int Boolean { get; } + public IntPtr String { get; } + public Guid Guid { get; } + + private static COMException NotImplemented => new COMException("Not supported", unchecked((int)0x80004001)); + + public unsafe void GetDateTime(void* value) => throw NotImplemented; + + public unsafe void GetTimeSpan(void* value) => throw NotImplemented; + + public unsafe void GetPoint(void* value) => throw NotImplemented; + + public unsafe void GetSize(void* value) => throw NotImplemented; + + public unsafe void GetRect(void* value) => throw NotImplemented; + + public unsafe byte* GetUInt8Array(uint* __valueSize) => throw NotImplemented; + + public unsafe short* GetInt16Array(uint* __valueSize) => throw NotImplemented; + + public unsafe ushort* GetUInt16Array(uint* __valueSize) => throw NotImplemented; + + public unsafe int* GetInt32Array(uint* __valueSize) + { + throw NotImplemented; + } + + public unsafe uint* GetUInt32Array(uint* __valueSize) => throw NotImplemented; + + public unsafe long* GetInt64Array(uint* __valueSize) => throw NotImplemented; + + public unsafe ulong* GetUInt64Array(uint* __valueSize) => throw NotImplemented; + + public unsafe float* GetSingleArray(uint* __valueSize) => throw NotImplemented; + + public unsafe double* GetDoubleArray(uint* __valueSize) => throw NotImplemented; + + public unsafe char* GetChar16Array(uint* __valueSize) => throw NotImplemented; + + public unsafe int* GetBooleanArray(uint* __valueSize) => throw NotImplemented; + + public unsafe IntPtr* GetStringArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void** GetInspectableArray(uint* __valueSize) => throw NotImplemented; + + public unsafe Guid* GetGuidArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void* GetDateTimeArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void* GetTimeSpanArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void* GetPointArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void* GetSizeArray(uint* __valueSize) => throw NotImplemented; + + public unsafe void* GetRectArray(uint* __valueSize) => throw NotImplemented; + } +} diff --git a/src/Windows/Avalonia.Win32/WinRT/winrt.idl b/src/Windows/Avalonia.Win32/WinRT/winrt.idl new file mode 100644 index 0000000000..929377999c --- /dev/null +++ b/src/Windows/Avalonia.Win32/WinRT/winrt.idl @@ -0,0 +1,718 @@ +@clr-namespace Avalonia.Win32.WinRT +@clr-access internal +@clr-map FLOAT float +@clr-map HSTRING IntPtr +@clr-map Vector2 System.Numerics.Vector2 +@clr-map Vector3 System.Numerics.Vector3 +@clr-map Quaternion System.Numerics.Quaternion +@clr-map Matrix4x4 System.Numerics.Matrix4x4 +@clr-map RECT Avalonia.Win32.Interop.UnmanagedMethods.RECT +@clr-map SIZE Avalonia.Win32.Interop.UnmanagedMethods.SIZE +@clr-map POINT Avalonia.Win32.Interop.UnmanagedMethods.POINT +@clr-map HWND IntPtr +@clr-map BOOL int +@clr-map DWORD int +@clr-map boolean int +@clr-map BYTE byte +@clr-map INT16 short +@clr-map INT32 int +@clr-map INT64 long +@clr-map UINT16 ushort +@clr-map UINT32 uint +@clr-map UINT64 ulong +@clr-map DOUBLE double +@clr-map GUID System.Guid +@clr-map WCHAR System.Char +@clr-map Color Avalonia.Win32.WinRT.WinRTColor + +enum TrustLevel +{ + BaseTrust, + PartialTrust, + FullTrust +} + +enum DirectXAlphaMode +{ + Unspecified, + Premultiplied, + Straight, + Ignore +} + +enum DirectXPixelFormat +{ + Unknown = 0, + R32G32B32A32Typeless = 1, + R32G32B32A32Float = 2, + R32G32B32A32UInt = 3, + R32G32B32A32Int = 4, + R32G32B32Typeless = 5, + R32G32B32Float = 6, + R32G32B32UInt = 7, + R32G32B32Int = 8, + R16G16B16A16Typeless = 9, + R16G16B16A16Float = 10, + R16G16B16A16UIntNormalized = 11, + R16G16B16A16UInt = 12, + R16G16B16A16IntNormalized = 13, + R16G16B16A16Int = 14, + R32G32Typeless = 15, + R32G32Float = 16, + R32G32UInt = 17, + R32G32Int = 18, + R32G8X24Typeless = 19, + D32FloatS8X24UInt = 20, + R32FloatX8X24Typeless = 21, + X32TypelessG8X24UInt = 22, + R10G10B10A2Typeless = 23, + R10G10B10A2UIntNormalized = 24, + R10G10B10A2UInt = 25, + R11G11B10Float = 26, + R8G8B8A8Typeless = 27, + R8G8B8A8UIntNormalized = 28, + R8G8B8A8UIntNormalizedSrgb = 29, + R8G8B8A8UInt = 30, + R8G8B8A8IntNormalized = 31, + R8G8B8A8Int = 32, + R16G16Typeless = 33, + R16G16Float = 34, + R16G16UIntNormalized = 35, + R16G16UInt = 36, + R16G16IntNormalized = 37, + R16G16Int = 38, + R32Typeless = 39, + D32Float = 40, + R32Float = 41, + R32UInt = 42, + R32Int = 43, + R24G8Typeless = 44, + D24UIntNormalizedS8UInt = 45, + R24UIntNormalizedX8Typeless = 46, + X24TypelessG8UInt = 47, + R8G8Typeless = 48, + R8G8UIntNormalized = 49, + R8G8UInt = 50, + R8G8IntNormalized = 51, + R8G8Int = 52, + R16Typeless = 53, + R16Float = 54, + D16UIntNormalized = 55, + R16UIntNormalized = 56, + R16UInt = 57, + R16IntNormalized = 58, + R16Int = 59, + R8Typeless = 60, + R8UIntNormalized = 61, + R8UInt = 62, + R8IntNormalized = 63, + R8Int = 64, + A8UIntNormalized = 65, + R1UIntNormalized = 66, + R9G9B9E5SharedExponent = 67, + R8G8B8G8UIntNormalized = 68, + G8R8G8B8UIntNormalized = 69, + BC1Typeless = 70, + BC1UIntNormalized = 71, + BC1UIntNormalizedSrgb = 72, + BC2Typeless = 73, + BC2UIntNormalized = 74, + BC2UIntNormalizedSrgb = 75, + BC3Typeless = 76, + BC3UIntNormalized = 77, + BC3UIntNormalizedSrgb = 78, + BC4Typeless = 79, + BC4UIntNormalized = 80, + BC4IntNormalized = 81, + BC5Typeless = 82, + BC5UIntNormalized = 83, + BC5IntNormalized = 84, + B5G6R5UIntNormalized = 85, + B5G5R5A1UIntNormalized = 86, + B8G8R8A8UIntNormalized = 87, + B8G8R8X8UIntNormalized = 88, + R10G10B10XRBiasA2UIntNormalized = 89, + B8G8R8A8Typeless = 90, + B8G8R8A8UIntNormalizedSrgb = 91, + B8G8R8X8Typeless = 92, + B8G8R8X8UIntNormalizedSrgb = 93, + BC6HTypeless = 94, + BC6H16UnsignedFloat = 95, + BC6H16Float = 96, + BC7Typeless = 97, + BC7UIntNormalized = 98, + BC7UIntNormalizedSrgb = 99, + Ayuv = 100, + Y410 = 101, + Y416 = 102, + NV12 = 103, + P010 = 104, + P016 = 105, + Opaque420 = 106, + Yuy2 = 107, + Y210 = 108, + Y216 = 109, + NV11 = 110, + AI44 = 111, + IA44 = 112, + P8 = 113, + A8P8 = 114, + B4G4R4A4UIntNormalized = 115, + P208 = 130, + V208 = 131, + V408 = 132, +} + +[uuid(AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90)] +interface IInspectable : IUnknown +{ + HRESULT GetIids(ulong * iidCount, Guid ** iids); + HRESULT GetRuntimeClassName( [out] IntPtr* className); + HRESULT GetTrustLevel([out] TrustLevel * trustLevel); +} + +enum PropertyType +{ + Empty = 0, + UInt8 = 1, + Int16 = 2, + UInt16 = 3, + Int32 = 4, + UInt32 = 5, + Int64 = 6, + UInt64 = 7, + Single = 8, + Double = 9, + Char16 = 10, + Boolean = 11, + String = 12, + Inspectable = 13, + DateTime = 14, + TimeSpan = 15, + Guid = 16, + Point = 17, + Size = 18, + Rect = 19, + OtherType = 20, + UInt8Array = 1025, + Int16Array = 1026, + UInt16Array = 1027, + Int32Array = 1028, + UInt32Array = 1029, + Int64Array = 1030, + UInt64Array = 1031, + SingleArray = 1032, + DoubleArray = 1033, + Char16Array = 1034, + BooleanArray = 1035, + StringArray = 1036, + InspectableArray = 1037, + DateTimeArray = 1038, + TimeSpanArray = 1039, + GuidArray = 1040, + PointArray = 1041, + SizeArray = 1042, + RectArray = 1043, + OtherTypeArray = 1044 +} + +[uuid(4BD682DD-7554-40E9-9A9B-82654EDE7E62)] +interface IPropertyValue : IInspectable +{ + [propget] HRESULT Type([out] [retval] PropertyType* value); + [propget] HRESULT IsNumericScalar([out] [retval] boolean* value); + HRESULT GetUInt8([out] [retval] BYTE* value); + HRESULT GetInt16([out] [retval] INT16* value); + HRESULT GetUInt16([out] [retval] UINT16* value); + HRESULT GetInt32([out] [retval] INT32* value); + HRESULT GetUInt32([out] [retval] UINT32* value); + HRESULT GetInt64([out] [retval] INT64* value); + HRESULT GetUInt64([out] [retval] UINT64* value); + HRESULT GetSingle([out] [retval] FLOAT* value); + HRESULT GetDouble([out] [retval] DOUBLE* value); + HRESULT GetChar16([out] [retval] WCHAR* value); + HRESULT GetBoolean([out] [retval] boolean* value); + HRESULT GetString([out] [retval] HSTRING* value); + HRESULT GetGuid([out] [retval] GUID* value); + HRESULT GetDateTime( void* value); + HRESULT GetTimeSpan(void* value); + HRESULT GetPoint(void* value); + HRESULT GetSize(void* value); + HRESULT GetRect(void* value); + HRESULT GetUInt8Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] BYTE** value); + HRESULT GetInt16Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] INT16** value); + HRESULT GetUInt16Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] UINT16** value); + HRESULT GetInt32Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] INT32** value); + HRESULT GetUInt32Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] UINT32** value); + HRESULT GetInt64Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] INT64** value); + HRESULT GetUInt64Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] UINT64** value); + HRESULT GetSingleArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] FLOAT** value); + HRESULT GetDoubleArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] DOUBLE** value); + HRESULT GetChar16Array([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] WCHAR** value); + HRESULT GetBooleanArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] boolean** value); + HRESULT GetStringArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] HSTRING** value); + HRESULT GetInspectableArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void*** value); + HRESULT GetGuidArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] GUID** value); + HRESULT GetDateTimeArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void** value); + HRESULT GetTimeSpanArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void** value); + HRESULT GetPointArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void** value); + HRESULT GetSizeArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void** value); + HRESULT GetRectArray([out] UINT32* __valueSize, [out] [size_is(, *__valueSize)] void** value); +} + +enum AsyncStatus { + Started = 0, + Completed, + Canceled, + Error, +} + +[uuid(A4ED5C81-76C9-40BD-8BE6-B1D90FB20AE7)] +interface IAsyncActionCompletedHandler : IUnknown +{ + HRESULT Invoke([in] IAsyncAction* asyncInfo, [in] AsyncStatus asyncStatus); +} + +[uuid(5A648006-843A-4DA9-865B-9D26E5DFAD7B)] +interface IAsyncAction : IInspectable +{ + HRESULT SetCompleted([in] IAsyncActionCompletedHandler* handler); + HRESULT GetCompleted([out] [retval] IAsyncActionCompletedHandler** ppv); + HRESULT GetResults(); +} + +[uuid(603E88E4-A338-4FFE-A457-A5CFB9CEB899)] +interface IDispatcherQueue : IInspectable +{ + //TODO +} + +[uuid(22F34E66-50DB-4E36-A98D-61C01B384D20)] +interface IDispatcherQueueController : IInspectable +{ + [propget] HRESULT DispatcherQueue([out] [retval] IDispatcherQueue** value); + HRESULT ShutdownQueueAsync([out] [retval] IAsyncAction** operation); +} + +[uuid(00000035-0000-0000-C000-000000000046)] +interface IActivationFactory : IInspectable +{ + HRESULT ActivateInstance([out, retval] IntPtr* instance); +} + +[flags] +enum CompositionBatchTypes +{ + None = 0x0, + Animation = 0x1, + Effect = 0x2, + InfiniteAnimation = 0x4, + AllAnimations = 0x5 +} + +[uuid(B403CA50-7F8C-4E83-985F-CC45060036D8)] +interface ICompositor : IInspectable +{ + HRESULT CreateColorKeyFrameAnimation([out] [retval] void** result); + [overload("CreateColorBrush")] HRESULT CreateColorBrush([out] [retval]void** result); + [overload("CreateColorBrush")] HRESULT CreateColorBrushWithColor([in] Color* color, [out] [retval] ICompositionColorBrush** result); + HRESULT CreateContainerVisual([out] [retval] IContainerVisual** result); + HRESULT CreateCubicBezierEasingFunction([in] Vector2 controlPoint1, [in] Vector2 controlPoint2, [out] [retval] void** result); + [overload("CreateEffectFactory")] HRESULT CreateEffectFactory([in] IGraphicsEffect* graphicsEffect, [out] [retval] ICompositionEffectFactory** result); + [overload("CreateEffectFactory")] HRESULT CreateEffectFactoryWithProperties([in] void* graphicsEffect, [in] void* animatableProperties, [out] [retval] void** result); + [overload("CreateExpressionAnimation")] HRESULT CreateExpressionAnimation([out] [retval] void** result); + [overload("CreateExpressionAnimation")] HRESULT CreateExpressionAnimationWithExpression([in] HSTRING expression, [out] [retval] void** result); + [overload("CreateInsetClip")] HRESULT CreateInsetClip([out] [retval] void** result); + [overload("CreateInsetClip")] HRESULT CreateInsetClipWithInsets([in] FLOAT leftInset, [in] FLOAT topInset, [in] FLOAT rightInset, [in] FLOAT bottomInset, [out] [retval] void** result); + HRESULT CreateLinearEasingFunction([out] [retval] void** result); + HRESULT CreatePropertySet([out] [retval] void** result); + HRESULT CreateQuaternionKeyFrameAnimation([out] [retval] void** result); + HRESULT CreateScalarKeyFrameAnimation([out] [retval] void** result); + HRESULT CreateScopedBatch([in] CompositionBatchTypes batchType, [out] [retval] ICompositionScopedBatch** result); + HRESULT CreateSpriteVisual([out] [retval] ISpriteVisual** result); + HRESULT CreateSurfaceBrush([out] [retval] ICompositionSurfaceBrush** result); + HRESULT CreateSurfaceBrushWithSurface([in] ICompositionSurface* surface, + [out] [retval] ICompositionSurfaceBrush** result); + HRESULT CreateTargetForCurrentView([out] [retval] void** result); + HRESULT CreateVector2KeyFrameAnimation([out] [retval] void** result); + HRESULT CreateVector3KeyFrameAnimation([out] [retval] void** result); + HRESULT CreateVector4KeyFrameAnimation([out] [retval] void** result); + HRESULT GetCommitBatch([in] CompositionBatchTypes batchType, [out] [retval] void** result); +} + +[uuid(735081DC-5E24-45DA-A38F-E32CC349A9A0)] +interface ICompositor2 : IInspectable +{ + HRESULT CreateAmbientLight([out] [retval] void** result); + HRESULT CreateAnimationGroup([out] [retval] void** result); + HRESULT CreateBackdropBrush([out] [retval] ICompositionBackdropBrush** result); + HRESULT CreateDistantLight([out] [retval] void** result); + HRESULT CreateDropShadow([out] [retval] void** result); + HRESULT CreateImplicitAnimationCollection([out] [retval] void** result); + HRESULT CreateLayerVisual([out] [retval] void** result); + HRESULT CreateMaskBrush([out] [retval] void** result); + HRESULT CreateNineGridBrush([out] [retval] void** result); + HRESULT CreatePointLight([out] [retval] void** result); + HRESULT CreateSpotLight([out] [retval] void** result); + [overload("CreateStepEasingFunction")] HRESULT CreateStepEasingFunction([out] [retval] void** result); + [overload("CreateStepEasingFunction")] HRESULT CreateStepEasingFunctionWithStepCount([in] INT32 stepCount, [out] [retval] void** result); +} + +[uuid(08E05581-1AD1-4F97-9757-402D76E4233B)] +interface ISpriteVisual : IInspectable +{ + [propget] HRESULT GetBrush([out] [retval] ICompositionBrush** value); + [propput] HRESULT SetBrush([in] ICompositionBrush* value); +} + +[uuid(FD04E6E3-FE0C-4C3C-AB19-A07601A576EE)] +interface ICompositionDrawingSurfaceInterop : IUnknown +{ + HRESULT BeginDraw(RECT* updateRect, Guid* iid, void** updateObject, [out, retval]POINT* updateOffset); + HRESULT EndDraw(); + HRESULT Resize(POINT sizePixels); + HRESULT Scroll(RECT * scrollRect, RECT * clipRect, int offsetX, int offsetY); + HRESULT ResumeDraw(); + HRESULT SuspendDraw(); +}; + +[uuid(A116FF71-F8BF-4C8A-9C98-70779A32A9C8)] +interface ICompositionGraphicsDeviceInterop : IUnknown +{ + HRESULT GetRenderingDevice([out] IUnknown ** value); + HRESULT SetRenderingDevice([out] IUnknown * value); +}; + +[uuid(25297D5C-3AD4-4C9C-B5CF-E36A38512330)] +interface ICompositorInterop : IUnknown +{ + HRESULT CreateCompositionSurfaceForHandle(IntPtr swapChain, [out] ICompositionSurface ** res); + HRESULT CreateCompositionSurfaceForSwapChain(IUnknown* swapChain, [out] ICompositionSurface ** result); + HRESULT CreateGraphicsDevice(IUnknown * renderingDevice, [out] ICompositionGraphicsDevice ** result); +}; + +[uuid(26f496a0-7f38-45fb-88f7-faaabe67dd59)] +interface ISwapChainInterop : IUnknown +{ + HRESULT SetSwapChain(IUnknown * swapChain); +}; + +/* +[uuid(2C9DB356-E70D-4642-8298-BC4AA5B4865C)] +interface ICompositionCapabilitiesInteropFactory : IInspectable +{ + HRESULT GetForWindow(IntPtr hwnd, [out] ICompositionCapabilities ** result); +}*/ + +[uuid(29E691FA-4567-4DCA-B319-D0F207EB6807)] +interface ICompositorDesktopInterop : IUnknown +{ + HRESULT CreateDesktopWindowTarget(HWND hwndTarget, BOOL isTopmost, [out] IDesktopWindowTarget ** result); + HRESULT EnsureOnThread(DWORD threadId); +}; + + +[uuid(35DBF59E-E3F9-45B0-81E7-FE75F4145DC9)] +interface IDesktopWindowTargetInterop : IUnknown +{ + HRESULT GetHWnd([out] IntPtr* value); +}; + +[uuid(37642806-F421-4FD0-9F82-23AE7C776182)] +interface IDesktopWindowContentBridgeInterop : IUnknown +{ + HRESULT Initialize(ICompositor* compositor, HWND parentHwnd); + HRESULT GetHWnd([out] IntPtr* value); + + HRESULT GetAppliedScaleFactor([out] float* value); +}; + +[uuid(FB22C6E1-80A2-4667-9936-DBEAF6EEFE95)] +interface ICompositionGraphicsDevice : IInspectable +{ + HRESULT CreateDrawingSurface([in] SIZE sizePixels, [in] DirectXPixelFormat pixelFormat, + [in] DirectXAlphaMode alphaMode, [out] [retval] ICompositionDrawingSurface** result); + HRESULT AddRenderingDeviceReplaced(void* handler, void* token); + HRESULT RemoveRenderingDeviceReplaced([in] int token); +} + +[uuid(1527540D-42C7-47A6-A408-668F79A90DFB)] +interface ICompositionSurface : IInspectable +{ +} + +[uuid(6329D6CA-3366-490E-9DB3-25312929AC51)] +interface IDesktopWindowTarget : IInspectable +{ + [propget] HRESULT IsTopmost([out] [retval] int* value); +} + + +[uuid(A166C300-FAD0-4D11-9E67-E433162FF49E)] +interface ICompositionDrawingSurface : IInspectable +{ + [propget] HRESULT GetAlphaMode([out] [retval] DirectXAlphaMode* value); + [propget] HRESULT GetPixelFormat([out] [retval] DirectXPixelFormat* value); + [propget] HRESULT GetSize([out] [retval] POINT* value); +} + +enum CompositionBitmapInterpolationMode +{ + NearestNeighbor, + Linear, + MagLinearMinLinearMipLinear, + MagLinearMinLinearMipNearest, + MagLinearMinNearestMipLinear, + MagLinearMinNearestMipNearest, + MagNearestMinLinearMipLinear, + MagNearestMinLinearMipNearest, + MagNearestMinNearestMipLinear, + MagNearestMinNearestMipNearest, +} + +enum CompositionStretch +{ + None, + Fill, + Uniform, + UniformToFill, +} + +[uuid(AD016D79-1E4C-4C0D-9C29-83338C87C162)] +interface ICompositionSurfaceBrush : IInspectable +{ + [propget] HRESULT BitmapInterpolationMode([out] [retval] CompositionBitmapInterpolationMode* value); + [propput] HRESULT BitmapInterpolationMode([in] CompositionBitmapInterpolationMode value); + [propget] HRESULT HorizontalAlignmentRatio([out] [retval] FLOAT* value); + [propput] HRESULT HorizontalAlignmentRatio([in] FLOAT value); + [propget] HRESULT Stretch([out] [retval] CompositionStretch* value); + [propput] HRESULT Stretch([in] CompositionStretch value); + [propget] HRESULT Surface([out] [retval] ICompositionSurface** value); + [propput] HRESULT Surface([in] ICompositionSurface* value); + [propget] HRESULT VerticalAlignmentRatio([out] [retval] FLOAT* value); + [propput] HRESULT VerticalAlignmentRatio([in] FLOAT value); +} + +[uuid(AB0D7608-30C0-40E9-B568-B60A6BD1FB46)] +interface ICompositionBrush : IInspectable +{ +} + +enum CompositionBackfaceVisibility +{ + Inherit, + Visible, + Hidden +} + +enum CompositionBorderMode +{ + Inherit, + Soft, + Hard +} + +enum CompositionCompositeMode +{ + Inherit, + SourceOver, + DestinationInvert, + MinBlend, +} + +[uuid(117E202D-A859-4C89-873B-C2AA566788E3)] +interface IVisual : IInspectable +{ + [propget] HRESULT AnchorPoint([out] [retval] Vector2* value); + [propput] HRESULT AnchorPoint([in] Vector2 value); + [propget] HRESULT BackfaceVisibility([out] [retval] CompositionBackfaceVisibility* value); + [propput] HRESULT BackfaceVisibility([in] CompositionBackfaceVisibility value); + [propget] HRESULT BorderMode([out] [retval] CompositionBorderMode* value); + [propput] HRESULT BorderMode([in] CompositionBorderMode value); + [propget] HRESULT CenterPoint([out] [retval] Vector3* value); + [propput] HRESULT CenterPoint([in] Vector3 value); + [propget] HRESULT Clip([out] [retval]void** value); + [propput] HRESULT Clip([in] void* value); + [propget] HRESULT CompositeMode([out] [retval] CompositionCompositeMode* value); + [propput] HRESULT CompositeMode([in] CompositionCompositeMode value); + [propget] HRESULT IsVisible([out] [retval] boolean* value); + [propput] HRESULT IsVisible([in] boolean value); + [propget] HRESULT Offset([out] [retval] Vector3* value); + [propput] HRESULT Offset([in] Vector3 value); + [propget] HRESULT Opacity([out] [retval] FLOAT* value); + [propput] HRESULT Opacity([in] FLOAT value); + [propget] HRESULT Orientation([out] [retval] Quaternion* value); + [propput] HRESULT Orientation([in] Quaternion value); + [propget] HRESULT Parent([out] [retval] IContainerVisual** value); + [propget] HRESULT RotationAngle([out] [retval] FLOAT* value); + [propput] HRESULT RotationAngle([in] FLOAT value); + [propget] HRESULT RotationAngleInDegrees([out] [retval] FLOAT* value); + [propput] HRESULT RotationAngleInDegrees([in] FLOAT value); + [propget] HRESULT RotationAxis([out] [retval] Vector3* value); + [propput] HRESULT RotationAxis([in] Vector3 value); + [propget] HRESULT Scale([out] [retval] Vector3* value); + [propput] HRESULT Scale([in] Vector3 value); + [propget] HRESULT Size([out] [retval] Vector2* value); + [propput] HRESULT Size([in] Vector2 value); + [propget] HRESULT TransformMatrix([out] [retval] Matrix4x4* value); + [propput] HRESULT TransformMatrix([in] Matrix4x4 value); +} + +[uuid(3052B611-56C3-4C3E-8BF3-F6E1AD473F06)] +interface IVisual2 : IInspectable +{ + [propget] HRESULT ParentForTransform([out] [retval] IVisual** value); + [propput] HRESULT ParentForTransform([in] IVisual* value); + [propget] HRESULT RelativeOffsetAdjustment([out] [retval] Vector3* value); + [propput] HRESULT RelativeOffsetAdjustment([in] Vector3 value); + [propget] HRESULT RelativeSizeAdjustment([out] [retval] Vector2* value); + [propput] HRESULT RelativeSizeAdjustment([in] Vector2 value); +} + +[uuid(02F6BC74-ED20-4773-AFE6-D49B4A93DB32)] +interface IContainerVisual : IInspectable +{ + [propget] HRESULT GetChildren([out] [retval] IVisualCollection** value); +} + +[uuid(8B745505-FD3E-4A98-84A8-E949468C6BCB)] +interface IVisualCollection : IInspectable +{ + [propget] HRESULT GetCount([out] [retval] INT32* value); + HRESULT InsertAbove([in] IVisual* newChild, [in] IVisual* sibling); + HRESULT InsertAtBottom([in] IVisual* newChild); + HRESULT InsertAtTop([in] IVisual* newChild); + HRESULT InsertBelow([in] IVisual* newChild, [in] IVisual* sibling); + HRESULT Remove([in] IVisual* child); + HRESULT RemoveAll(); +} + +[uuid(A1BEA8BA-D726-4663-8129-6B5E7927FFA6)] +interface ICompositionTarget : IInspectable +{ + [propget] HRESULT Root([out] [retval] IVisual** value); + [propput] HRESULT Root([in] IVisual* value); +} + + +[uuid(CB51C0CE-8FE6-4636-B202-861FAA07D8F3)] +interface IGraphicsEffect : IInspectable +{ + [propget] HRESULT Name([out] [retval] HSTRING* name); + [propput] HRESULT Name([in] HSTRING name); +} + +[uuid(2D8F9DDC-4339-4EB9-9216-F9DEB75658A2)] +interface IGraphicsEffectSource : IInspectable +{ +} + +enum GRAPHICS_EFFECT_PROPERTY_MAPPING +{ + GRAPHICS_EFFECT_PROPERTY_MAPPING_UNKNOWN, + GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORX, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORY, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORZ, + GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORW, + GRAPHICS_EFFECT_PROPERTY_MAPPING_RECT_TO_VECTOR4, + GRAPHICS_EFFECT_PROPERTY_MAPPING_RADIANS_TO_DEGREES, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLORMATRIX_ALPHA_MODE, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR3, + GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR4 +} + +[uuid(2FC57384-A068-44D7-A331-30982FCF7177)] +interface IGraphicsEffectD2D1Interop : IUnknown +{ + HRESULT GetEffectId([out] Guid* id); + HRESULT GetNamedPropertyMapping(IntPtr name, uint* index, GRAPHICS_EFFECT_PROPERTY_MAPPING* mapping); + HRESULT GetPropertyCount([out] uint* count); + HRESULT GetProperty(uint index, [out]IPropertyValue** value); + HRESULT GetSource(uint index, [out, retval] IGraphicsEffectSource** source); + HRESULT GetSourceCount([retval]uint *count); +}; + +[uuid(858AB13A-3292-4E4E-B3BB-2B6C6544A6EE)] +interface ICompositionEffectSourceParameter : IInspectable +{ + [propget] HRESULT Name([out] [retval] HSTRING* value); +} + +[uuid(B3D9F276-ABA3-4724-ACF3-D0397464DB1C)] +interface ICompositionEffectSourceParameterFactory : IInspectable +{ + HRESULT Create([in] HSTRING name, [out] [retval] ICompositionEffectSourceParameter** instance); +} + +enum CompositionEffectFactoryLoadStatus +{ + Success = 0, + EffectTooComplex = 1, + Pending = 2, + Other = -1 +} + +[uuid(BE5624AF-BA7E-4510-9850-41C0B4FF74DF)] +interface ICompositionEffectFactory : IInspectable +{ + HRESULT CreateBrush([out] [retval] ICompositionEffectBrush** result); + [propget] HRESULT ExtendedError([out] [retval] int* value); + [propget] HRESULT LoadStatus([out] [retval] CompositionEffectFactoryLoadStatus* value); +} + +[uuid(BF7F795E-83CC-44BF-A447-3E3C071789EC)] +interface ICompositionEffectBrush : IInspectable +{ + HRESULT GetSourceParameter([in] HSTRING name, [out] [retval] ICompositionBrush** result); + HRESULT SetSourceParameter([in] HSTRING name, [in] ICompositionBrush* source); +} + +[uuid(C5ACAE58-3898-499E-8D7F-224E91286A5D)] +interface ICompositionBackdropBrush : IInspectable +{ +} + +[uuid(2B264C5E-BF35-4831-8642-CF70C20FFF2F)] +interface ICompositionColorBrush : IInspectable +{ + [propget] HRESULT Color([out] [retval] Color* value); + [propput] HRESULT Color([in] Color value); +} + +[uuid(0D00DAD0-FB07-46FD-8C72-6280D1A3D1DD)] +interface ICompositionScopedBatch : IInspectable +{ + [propget] HRESULT IsActive([out] [retval] boolean* value); + [propget] HRESULT IsEnded([out] [retval] boolean* value); + HRESULT End(); + HRESULT Resume(); + HRESULT Suspend(); + [eventadd] HRESULT AddCompleted([in] void* handler, [out] [retval] int* token); + [eventremove] HRESULT RemoveCompleted([in] int token); +} + +[uuid(48EA31AD-7FCD-4076-A79C-90CC4B852C9B)] +interface ICompositor5 : IInspectable +{ + [propget] HRESULT GetComment([out] [retval] HSTRING* value); + [propput] HRESULT SetComment([in] HSTRING value); + [propget] HRESULT GetGlobalPlaybackRate([out] [retval] FLOAT* value); + [propput] HRESULT SetGlobalPlaybackRate([in] FLOAT value); + HRESULT CreateBounceScalarAnimation([out] [retval] void** result); + HRESULT CreateBounceVector2Animation([out] [retval] void** result); + HRESULT CreateBounceVector3Animation([out] [retval] void** result); + HRESULT CreateContainerShape([out] [retval] void** result); + HRESULT CreateEllipseGeometry([out] [retval] void** result); + HRESULT CreateLineGeometry([out] [retval] void** result); + [overload("CreatePathGeometry")] HRESULT CreatePathGeometry([out] [retval] void** result); + [overload("CreatePathGeometry")] HRESULT CreatePathGeometryWithPath([in] void* path, [out] [retval] void** result); + HRESULT CreatePathKeyFrameAnimation([out] [retval] void** result); + HRESULT CreateRectangleGeometry([out] [retval] void** result); + HRESULT CreateRoundedRectangleGeometry([out] [retval] void** result); + HRESULT CreateShapeVisual([out] [retval] void** result); + [overload("CreateSpriteShape")] HRESULT CreateSpriteShape([out] [retval] void** result); + [overload("CreateSpriteShape")] HRESULT CreateSpriteShapeWithGeometry([in] void* geometry, [out] [retval] void** result); + HRESULT CreateViewBox([out] [retval] void** result); + HRESULT RequestCommitAsync([out] [retval] IAsyncAction** operation); +} diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index d770f4b211..e7d16f731c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -65,23 +65,10 @@ namespace Avalonia.Win32 return IntPtr.Zero; } - // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 - // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us. - // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks - // Our window closed callback will set enabled state to a correct value after child window gets destroyed. - // We need to verify if parent is still alive (perhaps it got destroyed somehow). - if (_parent != null && IsWindow(_parent._hwnd)) - { - var wasActive = GetActiveWindow() == _hwnd; - - _parent.SetEnabled(true); + BeforeCloseCleanup(false); - // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. - if (wasActive) - { - SetActiveWindow(_parent._hwnd); - } - } + // Used to distinguish between programmatic and regular close requests. + _isCloseRequested = true; break; } @@ -361,6 +348,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_PAINT: { + using(NonPumpingSyncContext.Use()) using (_rendererLock.Lock()) { if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) @@ -378,6 +366,7 @@ namespace Avalonia.Win32 case WindowsMessage.WM_SIZE: { + using(NonPumpingSyncContext.Use()) using (_rendererLock.Lock()) { // Do nothing here, just block until the pending frame render is completed on the render thread diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 715c8fc01d..3c09d2b7a0 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -15,6 +15,8 @@ using Avalonia.Rendering; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; using Avalonia.Win32.OpenGl; +using Avalonia.Win32.WinRT; +using Avalonia.Win32.WinRT.Composition; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 @@ -81,6 +83,7 @@ namespace Avalonia.Win32 private POINT _maxTrackSize; private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; + private bool _isCloseRequested; public WindowImpl() { @@ -107,7 +110,7 @@ namespace Avalonia.Win32 var glPlatform = AvaloniaLocator.Current.GetService(); - var compositionConnector = AvaloniaLocator.Current.GetService(); + var compositionConnector = AvaloniaLocator.Current.GetService(); _isUsingComposition = compositionConnector is { } && glPlatform is EglPlatformOpenGlInterface egl && @@ -121,8 +124,8 @@ namespace Avalonia.Win32 { if (_isUsingComposition) { - var cgl = new CompositionEglGlPlatformSurface(glPlatform as EglPlatformOpenGlInterface, this); - _blurHost = cgl.AttachToCompositionTree(compositionConnector, _hwnd); + var cgl = new WinUiCompositedWindowSurface(compositionConnector, this); + _blurHost = cgl; _gl = cgl; @@ -240,7 +243,7 @@ namespace Avalonia.Win32 { if (IsWindowVisible(_hwnd)) { - ShowWindow(value); + ShowWindow(value, true); } else { @@ -452,9 +455,14 @@ namespace Avalonia.Win32 if (customRendererFactory != null) return customRendererFactory.Create(root, loop); - return Win32Platform.UseDeferredRendering ? - (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) : - new ImmediateRenderer(root); + return Win32Platform.UseDeferredRendering + ? _isUsingComposition + ? new DeferredRenderer(root, loop) + { + RenderOnlyOnRenderThread = true + } + : (IRenderer)new DeferredRenderer(root, loop, rendererLock: _rendererLock) + : new ImmediateRenderer(root); } public void Resize(Size value) @@ -489,6 +497,8 @@ namespace Avalonia.Win32 public void Dispose() { + (_gl as IDisposable)?.Dispose(); + if (_dropTarget != null) { OleContext.Current?.UnregisterDragDrop(Handle); @@ -497,6 +507,13 @@ namespace Avalonia.Win32 if (_hwnd != IntPtr.Zero) { + // Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called + // and we didn't prepare this window for destruction. + if (!_isCloseRequested) + { + BeforeCloseCleanup(true); + } + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -550,10 +567,11 @@ namespace Avalonia.Win32 UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); } - public virtual void Show() + public virtual void Show(bool activate) { SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent != null ? _parent._hwnd : IntPtr.Zero); - ShowWindow(_showWindowState); + + ShowWindow(_showWindowState, activate); } public Action GotInputWhenDisabled { get; set; } @@ -891,7 +909,7 @@ namespace Avalonia.Win32 ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended); } - private void ShowWindow(WindowState state) + private void ShowWindow(WindowState state, bool activate) { ShowWindowCommand? command; @@ -901,7 +919,7 @@ namespace Avalonia.Win32 { case WindowState.Minimized: newWindowProperties.IsFullScreen = false; - command = ShowWindowCommand.Minimize; + command = activate ? ShowWindowCommand.Minimize : ShowWindowCommand.ShowMinNoActive; break; case WindowState.Maximized: newWindowProperties.IsFullScreen = false; @@ -910,7 +928,8 @@ namespace Avalonia.Win32 case WindowState.Normal: newWindowProperties.IsFullScreen = false; - command = ShowWindowCommand.Restore; + command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : + activate ? ShowWindowCommand.Normal : ShowWindowCommand.ShowNoActivate; break; case WindowState.FullScreen: @@ -939,6 +958,32 @@ namespace Avalonia.Win32 SetFocus(_hwnd); } } + + private void BeforeCloseCleanup(bool isDisposing) + { + // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 + // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us (or last active window). + // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks + // We need to verify if parent is still alive (perhaps it got destroyed somehow). + if (_parent != null && IsWindow(_parent._hwnd)) + { + var wasActive = GetActiveWindow() == _hwnd; + + // We can only set enabled state if we are not disposing - generally Dispose happens after enabled state has been set. + // Ignoring this would cause us to enable a window that might be disabled. + if (!isDisposing) + { + // Our window closed callback will set enabled state to a correct value after child window gets destroyed. + _parent.SetEnabled(true); + } + + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. + if (wasActive) + { + SetActiveWindow(_parent._hwnd); + } + } + } private void MaximizeWithoutCoveringTaskbar() { diff --git a/src/tools/MicroComGenerator/AstParser.cs b/src/tools/MicroComGenerator/AstParser.cs index 732c0496b3..388a8eb018 100644 --- a/src/tools/MicroComGenerator/AstParser.cs +++ b/src/tools/MicroComGenerator/AstParser.cs @@ -13,7 +13,8 @@ namespace MicroComGenerator while (!parser.Eof) { var attrs = ParseLocalAttributes(ref parser); - + if (parser.TryConsume(";")) + continue; if (parser.TryParseKeyword("enum")) idl.Enums.Add(ParseEnum(attrs, ref parser)); else if (parser.TryParseKeyword("struct")) @@ -64,7 +65,7 @@ namespace MicroComGenerator static AstAttributes ParseLocalAttributes(ref TokenParser parser) { var rv = new AstAttributes(); - if (parser.TryConsume("[")) + while (parser.TryConsume("[")) { while (!parser.TryConsume("]") && !parser.Eof) { @@ -78,7 +79,7 @@ namespace MicroComGenerator if (parser.TryConsume(']')) { rv.Add(new AstAttributeNode(ident, null)); - return rv; + break; } // No value, next attribute else if (parser.TryConsume(',')) diff --git a/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs b/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs index 91ece81bd0..adb8faf938 100644 --- a/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs +++ b/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs @@ -21,7 +21,7 @@ namespace MicroComGenerator { public string Name; public string NativeType; - + public AstAttributes Attributes { get; set; } public virtual StatementSyntax CreateFixed(StatementSyntax inner) => inner; public virtual void PreMarshal(List body) @@ -161,6 +161,13 @@ namespace MicroComGenerator return type; } + Arg ConvertArg(AstInterfaceMemberArgumentNode node) + { + var arg = ConvertArg(node.Name, node.Type); + arg.Attributes = node.Attributes.Clone(); + return arg; + } + Arg ConvertArg(string name, AstTypeNode type) { type = new AstTypeNode { Name = ConvertNativeType(type.Name), PointerLevel = type.PointerLevel }; @@ -190,12 +197,19 @@ namespace MicroComGenerator List vtblCtor, int num) { // Prepare method information - var args = member.Select(a => ConvertArg(a.Name, a.Type)).ToList(); + if (member.Name == "GetRenderingDevice") + Console.WriteLine(); + var args = member.Select(ConvertArg).ToList(); var returnArg = ConvertArg("__result", member.ReturnType); bool isHresult = member.ReturnType.Name == "HRESULT"; bool isHresultLastArgumentReturn = isHresult && args.Count > 0 - && (args.Last().Name == "ppv" || args.Last().Name == "retOut" || args.Last().Name == "ret") + && (args.Last().Name == "ppv" + || args.Last().Name == "retOut" + || args.Last().Name == "ret" + || args.Last().Attributes.HasAttribute("out") + || args.Last().Attributes.HasAttribute("retval") + ) && ((member.Last().Type.PointerLevel > 0 && !IsInterface(member.Last().Type)) || member.Last().Type.PointerLevel == 2); @@ -334,16 +348,27 @@ namespace MicroComGenerator BlockSyntax backBodyBlock = Block().AddStatements(backPreMarshal.ToArray()).AddStatements(backCallStatement); + var exceptions = new List() + { + CatchClause( + CatchDeclaration(ParseTypeName("System.Exception"), Identifier("__exception__")), null, + Block( + ParseStatement( + "Avalonia.MicroCom.MicroComRuntime.UnhandledException(__target, __exception__);"), + isHresult ? ParseStatement("return unchecked((int)0x80004005u);") + : isVoidReturn ? EmptyStatement() : ParseStatement("return default;") + )) + }; + + if (isHresult) + exceptions.Insert(0, CatchClause( + CatchDeclaration(ParseTypeName("System.Runtime.InteropServices.COMException"), + Identifier("__com_exception__")), + null, Block(ParseStatement("return __com_exception__.ErrorCode;")))); + backBodyBlock = Block( TryStatement( - SingletonList(CatchClause( - CatchDeclaration(ParseTypeName("System.Exception"), Identifier("__exception__")), null, - Block( - ParseStatement( - "Avalonia.MicroCom.MicroComRuntime.UnhandledException(__target, __exception__);"), - isHresult ? ParseStatement("return unchecked((int)0x80004005u);") - : isVoidReturn ? EmptyStatement() : ParseStatement("return default;") - )))) + List(exceptions)) .WithBlock(Block(backBodyBlock)) ); if (isHresult) diff --git a/src/tools/MicroComGenerator/CSharpGen.Utils.cs b/src/tools/MicroComGenerator/CSharpGen.Utils.cs index 3a62220d12..da845b0ecd 100644 --- a/src/tools/MicroComGenerator/CSharpGen.Utils.cs +++ b/src/tools/MicroComGenerator/CSharpGen.Utils.cs @@ -68,10 +68,11 @@ namespace MicroComGenerator bool IsPropertyRewriteCandidate(MethodDeclarationSyntax method) { - if(method.Identifier.Text.Contains("GetScaling")) - Console.WriteLine(); - return (method.Identifier.Text.StartsWith("Get") && - method.ParameterList.Parameters.Count == 0); + + return + method.ReturnType.ToFullString() != "void" + && method.Identifier.Text.StartsWith("Get") + && method.ParameterList.Parameters.Count == 0; } TypeDeclarationSyntax RewriteMethodsToProperties(T decl) where T : TypeDeclarationSyntax diff --git a/src/tools/MicroComGenerator/CSharpGen.cs b/src/tools/MicroComGenerator/CSharpGen.cs index 688036ffc2..ff4c351fd9 100644 --- a/src/tools/MicroComGenerator/CSharpGen.cs +++ b/src/tools/MicroComGenerator/CSharpGen.cs @@ -22,7 +22,11 @@ namespace MicroComGenerator public CSharpGen(AstIdlNode idl) { _idl = idl.Clone(); - new AstRewriter().VisitAst(_idl); + new AstRewriter(_idl.Attributes.Where(a => a.Name == "clr-map") + .Select(x => x.Value.Trim().Split(' ')) + .ToDictionary(x => x[0], x => x[1]) + ).VisitAst(_idl); + _extraUsings = _idl.Attributes.Where(u => u.Name == "clr-using").Select(u => u.Value).ToList(); _namespace = _idl.GetAttribute("clr-namespace"); var visibilityString = _idl.GetAttribute("clr-access"); @@ -37,6 +41,13 @@ namespace MicroComGenerator class AstRewriter : AstVisitor { + private readonly Dictionary _typeMap = new Dictionary(); + + public AstRewriter(Dictionary typeMap) + { + _typeMap = typeMap; + } + void ConvertIntPtr(AstTypeNode type) { if (type.Name == "void" && type.PointerLevel > 0) @@ -60,6 +71,9 @@ namespace MicroComGenerator type.PointerLevel++; type.IsLink = false; } + + if (_typeMap.TryGetValue(type.Name, out var mapped)) + type.Name = mapped; base.VisitType(type); } @@ -80,6 +94,10 @@ namespace MicroComGenerator { if (member.HasAttribute("intptr")) ConvertIntPtr(member.ReturnType); + if (member.HasAttribute("propget") && !member.Name.StartsWith("Get")) + member.Name = "Get" + member.Name; + if (member.HasAttribute("propput") && !member.Name.StartsWith("Set")) + member.Name = "Set" + member.Name; base.VisitInterfaceMember(member); } } @@ -103,7 +121,8 @@ namespace MicroComGenerator NamespaceDeclarationSyntax GenerateEnums(NamespaceDeclarationSyntax ns) { return ns.AddMembers(_idl.Enums.Select(e => - EnumDeclaration(e.Name) + { + var dec = EnumDeclaration(e.Name) .WithModifiers(TokenList(Token(_visibility))) .WithMembers(SeparatedList(e.Select(m => { @@ -111,8 +130,11 @@ namespace MicroComGenerator if (m.Value != null) return member.WithEqualsValue(EqualsValueClause(ParseExpression(m.Value))); return member; - }))) - ).ToArray()); + }))); + if (e.HasAttribute("flags")) + dec = dec.AddAttribute("System.Flags"); + return dec; + }).ToArray()); } NamespaceDeclarationSyntax GenerateStructs(NamespaceDeclarationSyntax ns) diff --git a/src/tools/MicroComGenerator/CppGen.cs b/src/tools/MicroComGenerator/CppGen.cs index 68192ebffe..b053088ca9 100644 --- a/src/tools/MicroComGenerator/CppGen.cs +++ b/src/tools/MicroComGenerator/CppGen.cs @@ -14,7 +14,10 @@ namespace MicroComGenerator name = "unsigned char"; else if(name == "uint") name = "unsigned int"; - return name + new string('*', type.PointerLevel); + + type = type.Clone(); + type.Name = name; + return type.Format(); } public static string GenerateCpp(AstIdlNode idl) diff --git a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj index dd50eff2b6..5b686dea4c 100644 --- a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj +++ b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 0d87f0eb03..badfd09430 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -10,6 +10,7 @@ + @@ -17,4 +18,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs index 7170f6d7d4..3493dd0f53 100644 --- a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs @@ -17,7 +17,8 @@ namespace Avalonia.Benchmarks.Layout _app = UnitTestApplication.Start( TestServices.StyledWindow.With( renderInterface: new NullRenderingPlatform(), - threadingInterface: new NullThreadingPlatform())); + threadingInterface: new NullThreadingPlatform(), + standardCursorFactory: new NullCursorFactory())); _root = new TestRoot(true, null) { diff --git a/tests/Avalonia.Benchmarks/NullCursorFactory.cs b/tests/Avalonia.Benchmarks/NullCursorFactory.cs new file mode 100644 index 0000000000..012adce0f2 --- /dev/null +++ b/tests/Avalonia.Benchmarks/NullCursorFactory.cs @@ -0,0 +1,14 @@ +using System; +using Avalonia.Input; +using Avalonia.Platform; + +namespace Avalonia.Benchmarks +{ + internal class NullCursorFactory : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new PlatformHandle(IntPtr.Zero, "null"); + } + } +} diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index f632d85c26..1570205456 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -28,7 +28,7 @@ namespace Avalonia.Benchmarks public IGeometryImpl CreateRectangleGeometry(Rect rect) { - throw new NotImplementedException(); + return new MockStreamGeometryImpl(); } public IStreamGeometryImpl CreateStreamGeometry() diff --git a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj index 396dfee691..c0c9303767 100644 --- a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj +++ b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 7a6d77ef46..6b17427eda 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 783215fb5d..c8a30a42e9 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -40,6 +40,7 @@ namespace Avalonia.Controls.UnitTests Items = items, SelectedIndex = 0, }; + var root = new TestRoot(target); var rectangle = target.GetValue(ComboBox.SelectionBoxItemProperty) as Rectangle; Assert.NotNull(rectangle); diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index c179aef9ac..39a3250686 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -79,7 +79,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); var sut = new ContextMenu(); @@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests _mouse.Click(target); Assert.False(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Once); + popupImpl.Verify(x => x.Show(true), Times.Once); popupImpl.Verify(x => x.Hide(), Times.Once); } } @@ -109,7 +109,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); var sut = new ContextMenu(); @@ -130,7 +130,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(sut.IsOpen); popupImpl.Verify(x => x.Hide(), Times.Once); - popupImpl.Verify(x => x.Show(), Times.Exactly(2)); + popupImpl.Verify(x => x.Show(true), Times.Exactly(2)); } } @@ -177,7 +177,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); bool eventCalled = false; var sut = new ContextMenu(); @@ -193,7 +193,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(eventCalled); Assert.False(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Never); + popupImpl.Verify(x => x.Show(true), Times.Never); } } @@ -297,7 +297,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); bool eventCalled = false; @@ -321,7 +321,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(eventCalled); Assert.True(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Once()); + popupImpl.Verify(x => x.Show(true), Times.Once()); popupImpl.Verify(x => x.Hide(), Times.Never); } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index 84f212d1b3..8109b037c5 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -137,7 +137,7 @@ namespace Avalonia.Controls.UnitTests var target = new TestWindowBase(windowImpl.Object); target.IsVisible = true; - windowImpl.Verify(x => x.Show()); + windowImpl.Verify(x => x.Show(true)); } } diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index 1cfc1a6ab8..c59e59be63 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -27,4 +27,5 @@ + diff --git a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj index e346ce944d..42229ba456 100644 --- a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj +++ b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj @@ -7,6 +7,7 @@ + @@ -22,4 +23,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj index 463bc50008..74cc6e292b 100644 --- a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj +++ b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 27f3223c6c..d49a859b89 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -7,6 +7,7 @@ + @@ -25,4 +26,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj index ec5b2f0ed1..7d1285c025 100644 --- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj +++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs index 45cf28773f..7c48a975ef 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs @@ -129,7 +129,7 @@ namespace Avalonia.Markup.UnitTests.Parsers { var data = new Class1(); - Assert.Throws(() => ExpressionObserverBuilder.Build(data, "(Owner)", typeResolver: _typeResolver)); + Assert.Throws(() => ExpressionObserverBuilder.Build(data, "(Owner.)", typeResolver: _typeResolver)); } [Fact] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index ad3592294d..44b604197d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs index 9ea2cd643a..20ed22e84f 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs @@ -84,6 +84,54 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions } } + [Fact] + public void SupportCastToTypeInExpression() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(dataContext.StringProperty, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpression_DifferentTypeEvaluatesToNull() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = "foo"; + + window.DataContext = dataContext; + + Assert.Equal(null, contentControl.Content); + } + } private class FooBar { public object Foo { get; } = null; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 8a82ad048b..1cf9e0877d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; using System.Reactive.Subjects; using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Data.Converters; using Avalonia.Data.Core; using Avalonia.Markup.Data; using Avalonia.UnitTests; @@ -372,7 +374,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -390,7 +392,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -447,7 +449,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -597,7 +599,49 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions x:CompileBindings='true'> "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + } + } + + [Fact] + public void SupportParentInPath() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + Assert.Equal("foo", contentControl.Content); + } + } + + [Fact] + public void SupportConverterWithParameter() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.DataContext = new TestDataContext() { StringProperty = "Foo" }; + + Assert.Equal("Foo+Bar", textBlock.Text); } } @@ -613,26 +657,218 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions x:DataType='local:TestDataContext' x:CompileBindings='notabool'> "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } - } + [Fact] + public void SupportCastToTypeInExpression() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = new TestDataContext(); + + window.DataContext = dataContext; + + Assert.Equal(dataContext, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpression_DifferentTypeEvaluatesToNull() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = "foo"; + + window.DataContext = dataContext; + + Assert.Equal(null, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpressionWithProperty() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(dataContext.StringProperty, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpressionWithProperty1() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(dataContext.StringProperty, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpressionWithPropertyIndexer() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var data = new TestData() + { + StringProperty = "Foo" + }; + var dataContext = new TestDataContext + { + ObjectsArrayProperty = new object[] { data } + }; + + window.DataContext = dataContext; + + Assert.Equal(data.StringProperty, contentControl.Content); + } + } + + [Fact] + public void SupportCastToTypeInExpressionWithProperty_DifferentTypeEvaluatesToNull() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var contentControl = window.FindControl("contentControl"); + + var dataContext = new TestDataContext + { + StringProperty = "foobar" + }; + + window.DataContext = dataContext; + + Assert.Equal(dataContext.StringProperty, contentControl.Content); + + window.DataContext = "foo"; + + Assert.Equal(null, contentControl.Content); + } + } + + void Throws(string type, Action cb) + { + try + { + cb(); + } + catch (Exception e) when (e.GetType().Name == type) + { + return; + } + + throw new Exception("Expected " + type); + } + + void ThrowsXamlParseException(Action cb) => Throws("XamlParseException", cb); + void ThrowsXamlTransformException(Action cb) => Throws("XamlTransformException", cb); + } + public interface INonIntegerIndexer { - string this[string key] {get; set;} + string this[string key] { get; set; } } public interface INonIntegerIndexerDerived : INonIntegerIndexer - {} + { } public interface IHasProperty { - string StringProperty {get; set; } + string StringProperty { get; set; } } public interface IHasPropertyDerived : IHasProperty - {} + { } + + public class AppendConverter : IValueConverter + { + public static IValueConverter Instance { get; } = new AppendConverter(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => string.Format("{0}+{1}", value, parameter); + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => throw new NotImplementedException(); + + } + + public class TestData + { + public string StringProperty { get; set; } + } public class TestDataContext : IHasPropertyDerived { @@ -646,6 +882,8 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public string[] ArrayProperty { get; set; } + public object[] ObjectsArrayProperty { get; set; } + public List ListProperty { get; set; } = new List(); public NonIntegerIndexer NonIntegerIndexerProperty { get; set; } = new NonIntegerIndexer(); diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 47a73cb360..9152131ab3 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -345,6 +345,23 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + [Fact] + public void DynamicResource_Can_Be_Assigned_To_Resource_Property_In_Application() + { + var xaml = @" + + + #ff506070 + + +"; + + var application = (Application)AvaloniaRuntimeXamlLoader.Load(xaml); + var brush = (SolidColorBrush)application.Resources["brush"]; + + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } [Fact] public void DynamicResource_Can_Be_Assigned_To_ItemTemplate_Property() diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs new file mode 100644 index 0000000000..bd577e84f8 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs @@ -0,0 +1,24 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class ShapeTests : XamlTestBase + { + [Fact] + public void Can_Specify_DashStyle_In_XAML() + { + var xaml = @" + + + + +"; + + var target = AvaloniaRuntimeXamlLoader.Parse(xaml); + + Assert.NotNull(target); + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 67e46d25c3..77a4932ccc 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -295,8 +295,27 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal("Test", templated.Text); } } + + [Fact] + public void Should_Work_With_Base_Property() + { + var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Load(@" + + + + + + +"); + + Assert.NotNull(parsed.ItemTemplate); + } } - + public class XamlIlBugTestsEventHandlerCodeBehind : Window { public object SavedContext; diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index 65df5e7cb0..14d0f4debf 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -29,4 +29,5 @@ + diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index 584eaf6fa9..ef69865e32 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -8,6 +8,7 @@ + @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj index 2b7f6263a7..a07ec050a6 100644 --- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj +++ b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj @@ -11,6 +11,7 @@ + diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index 6b5c7a66ff..d4abf9416a 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -28,4 +28,5 @@ + diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 9ec8ca2d36..8a24a8366f 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -58,7 +58,7 @@ namespace Avalonia.UnitTests windowImpl.Object.Resized?.Invoke(clientSize); }); - windowImpl.Setup(x => x.Show()).Callback(() => + windowImpl.Setup(x => x.Show(true)).Callback(() => { windowImpl.Object.Activated?.Invoke(); }); diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj index aee54e87a9..13a04be5db 100644 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj @@ -13,6 +13,7 @@ + diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs new file mode 100644 index 0000000000..0737b4dc88 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs @@ -0,0 +1,34 @@ +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests.Media +{ + public class PathSegmentTests + { + [Fact] + public void PathSegment_Triggers_Invalidation_On_Property_Change() + { + var targetSegment = new ArcSegment() + { + Size = new Size(10, 10), + Point = new Point(5, 5) + }; + + var target = new PathGeometry + { + Figures = new PathFigures + { + new PathFigure { IsClosed = false, Segments = new PathSegments { targetSegment } } + } + }; + + var changed = false; + + target.Changed += (s, e) => changed = true; + + targetSegment.Size = new Size(20, 20); + + Assert.True(changed); + } + } +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs index 418ac7576b..c2a1a5f9e4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs @@ -1,4 +1,5 @@ -using Avalonia.Media; +using Avalonia.Collections; +using Avalonia.Media; using Avalonia.Media.Immutable; using Xunit; @@ -39,7 +40,20 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.Invalidated += (s, e) => raised = true; - dashes.Dashes = new[] { 0.1, 0.2 }; + dashes.Dashes = new AvaloniaList { 0.1, 0.2 }; + + Assert.True(raised); + } + + [Fact] + public void Adding_DashStyle_Dashes_Raises_Invalidated() + { + var dashes = new DashStyle(); + var target = new Pen { DashStyle = dashes }; + var raised = false; + + target.Invalidated += (s, e) => raised = true; + dashes.Dashes.Add(0.3); Assert.True(raised); } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs index 447a68aa69..38131fbfca 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs @@ -235,6 +235,25 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new Point(100, 100), point); } + [Fact] + public void TransformToVisual_With_NonInvertible_RenderTransform_Should_Work() + { + var child = new Decorator + { + Width = 100, + Height = 100, + RenderTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 0 } + }; + var root = new TestRoot() { Child = child, Width = 400, Height = 400 }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(new Point(), root.DesiredSize)); + + var tr = root.TransformToVisual(child); + + Assert.Null(tr); + } + [Fact] public void Should_Not_Log_Binding_Error_When_Not_Attached_To_Logical_Tree() {