diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 75922c1cb117..d3d8c3a585b9 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -42,7 +42,10 @@ body: label: Version with bug description: In what version do you see this issue? Run `dotnet workload list` to find your version. options: + - 10.0.0-preview.3 + - 10.0.0-preview.2 - 10.0.0-preview.1 + - 9.0.60 SR6 - 9.0.50 SR5 - 9.0.40 SR4 - 9.0.30 SR3 @@ -165,7 +168,10 @@ body: - 9.0.30 SR3 - 9.0.40 SR4 - 9.0.50 SR5 + - 9.0.60 SR6 - 10.0.0-preview.1 + - 10.0.0-preview.2 + - 10.0.0-preview.3 validations: required: true - type: dropdown diff --git a/.github/prompts/contributors.json b/.github/prompts/contributors.json new file mode 100644 index 000000000000..39c75906ee2a --- /dev/null +++ b/.github/prompts/contributors.json @@ -0,0 +1,3926 @@ +[ + { + "login": "rmarinho", + "id": 1235097, + "type": "User", + "contributions": 2079 + }, + { + "login": "PureWeen", + "id": 5375137, + "type": "User", + "contributions": 1710 + }, + { + "login": "mattleibow", + "id": 1096616, + "type": "User", + "contributions": 1679 + }, + { + "login": "jsuarezruiz", + "id": 6755973, + "type": "User", + "contributions": 1035 + }, + { + "login": "hartez", + "id": 538025, + "type": "User", + "contributions": 958 + }, + { + "login": "dotnet-maestro[bot]", + "id": 42748379, + "type": "Bot", + "contributions": 895 + }, + { + "login": "StephaneDelcroix", + "id": 313003, + "type": "User", + "contributions": 856 + }, + { + "login": "Redth", + "id": 271950, + "type": "User", + "contributions": 781 + }, + { + "login": "github-actions[bot]", + "id": 41898282, + "type": "Bot", + "contributions": 443 + }, + { + "login": "jfversluis", + "id": 939291, + "type": "User", + "contributions": 430 + }, + { + "login": "samhouts", + "id": 7827070, + "type": "User", + "contributions": 415 + }, + { + "login": "jamesmontemagno", + "id": 1676321, + "type": "User", + "contributions": 370 + }, + { + "login": "dependabot[bot]", + "id": 49699333, + "type": "Bot", + "contributions": 337 + }, + { + "login": "jonathanpeppers", + "id": 840039, + "type": "User", + "contributions": 327 + }, + { + "login": "jonlipsky", + "id": 1466386, + "type": "User", + "contributions": 169 + }, + { + "login": "kingces95", + "id": 4120386, + "type": "User", + "contributions": 145 + }, + { + "login": "Eilon", + "id": 202643, + "type": "User", + "contributions": 134 + }, + { + "login": "kubaflo", + "id": 42434498, + "type": "User", + "contributions": 117 + }, + { + "name": "Pavel Yakovlev", + "email": "v-payako@microsoft.com", + "type": "Anonymous", + "contributions": 109 + }, + { + "login": "adrianknight89", + "id": 16855542, + "type": "User", + "contributions": 107 + }, + { + "login": "rookiejava", + "id": 1029134, + "type": "User", + "contributions": 105 + }, + { + "login": "pauldipietro", + "id": 1251024, + "type": "User", + "contributions": 94 + }, + { + "login": "rachelkang", + "id": 21988533, + "type": "User", + "contributions": 92 + }, + { + "login": "anandhan-rajagopal", + "id": 97146406, + "type": "User", + "contributions": 86 + }, + { + "login": "Clancey", + "id": 256046, + "type": "User", + "contributions": 79 + }, + { + "name": "Jason Smith", + "email": "jason.smith@xamarin.com", + "type": "Anonymous", + "contributions": 76 + }, + { + "login": "simonrozsival", + "id": 374616, + "type": "User", + "contributions": 69 + }, + { + "login": "Foda", + "id": 890772, + "type": "User", + "contributions": 66 + }, + { + "login": "pjcollins", + "id": 2000163, + "type": "User", + "contributions": 64 + }, + { + "login": "AndreiMisiukevich", + "id": 10124814, + "type": "User", + "contributions": 61 + }, + { + "login": "myroot", + "id": 1029155, + "type": "User", + "contributions": 60 + }, + { + "login": "tj-devel709", + "id": 50846373, + "type": "User", + "contributions": 60 + }, + { + "login": "MartyIX", + "id": 203266, + "type": "User", + "contributions": 57 + }, + { + "login": "davidortinau", + "id": 41873, + "type": "User", + "contributions": 55 + }, + { + "login": "mandel-macaque", + "id": 2190086, + "type": "User", + "contributions": 55 + }, + { + "login": "pictos", + "id": 20712372, + "type": "User", + "contributions": 51 + }, + { + "login": "drasticactions", + "id": 898335, + "type": "User", + "contributions": 47 + }, + { + "login": "dimonovdd", + "id": 59065470, + "type": "User", + "contributions": 47 + }, + { + "login": "eerhardt", + "id": 8291187, + "type": "User", + "contributions": 42 + }, + { + "login": "albyrock87", + "id": 1423005, + "type": "User", + "contributions": 40 + }, + { + "name": "Mike Corsaro", + "email": "mikecorsaro@microsoft.com", + "type": "Anonymous", + "contributions": 39 + }, + { + "login": "swharden", + "id": 4165489, + "type": "User", + "contributions": 37 + }, + { + "login": "MichaelNorman", + "id": 1462796, + "type": "User", + "contributions": 37 + }, + { + "login": "Tamilarasan-Paranthaman", + "id": 93904422, + "type": "User", + "contributions": 34 + }, + { + "login": "TanayParikh", + "id": 14852843, + "type": "User", + "contributions": 34 + }, + { + "login": "jknaudt21", + "id": 32918747, + "type": "User", + "contributions": 33 + }, + { + "login": "moljac", + "id": 1768576, + "type": "User", + "contributions": 33 + }, + { + "login": "jstedfast", + "id": 338984, + "type": "User", + "contributions": 32 + }, + { + "login": "andreinitescu", + "id": 743918, + "type": "User", + "contributions": 32 + }, + { + "name": "Schilli, Niklas", + "email": "schilli@ams-berlin.de", + "type": "Anonymous", + "contributions": 31 + }, + { + "login": "vividos", + "id": 429902, + "type": "User", + "contributions": 31 + }, + { + "login": "jimmgarrido", + "id": 4087358, + "type": "User", + "contributions": 29 + }, + { + "login": "sbanni", + "id": 1885888, + "type": "User", + "contributions": 28 + }, + { + "login": "shyunMin", + "id": 20968023, + "type": "User", + "contributions": 28 + }, + { + "login": "ravinderjangra", + "id": 9795917, + "type": "User", + "contributions": 28 + }, + { + "name": "Jason Smith", + "email": "jas@microsoft.com", + "type": "Anonymous", + "contributions": 27 + }, + { + "login": "antonfirsov", + "id": 6835152, + "type": "User", + "contributions": 27 + }, + { + "name": "Jonathan Dick", + "email": "jond@hey.com", + "type": "Anonymous", + "contributions": 25 + }, + { + "login": "kvpt", + "id": 1446221, + "type": "User", + "contributions": 25 + }, + { + "login": "mohachouch", + "id": 25939826, + "type": "User", + "contributions": 25 + }, + { + "login": "akihikodaki", + "id": 17036990, + "type": "User", + "contributions": 24 + }, + { + "login": "dustin-wojciechowski", + "id": 89540402, + "type": "User", + "contributions": 24 + }, + { + "login": "NirmalKumarYuvaraj", + "id": 97871636, + "type": "User", + "contributions": 22 + }, + { + "login": "JoonghyunCho", + "id": 14328614, + "type": "User", + "contributions": 22 + }, + { + "login": "jcmanke", + "id": 6132629, + "type": "User", + "contributions": 20 + }, + { + "login": "yurkinh", + "id": 17849938, + "type": "User", + "contributions": 20 + }, + { + "login": "bmacombe", + "id": 15127788, + "type": "User", + "contributions": 20 + }, + { + "login": "BradChase2011", + "id": 13279060, + "type": "User", + "contributions": 20 + }, + { + "login": "devanathan-vaithiyanathan", + "id": 114395405, + "type": "User", + "contributions": 19 + }, + { + "login": "HarishKumarSF4517", + "id": 171316288, + "type": "User", + "contributions": 19 + }, + { + "name": "Luke Westendorf", + "email": "lukewest@microsoft.com", + "type": "Anonymous", + "contributions": 18 + }, + { + "login": "Vignesh-SF3580", + "id": 102575140, + "type": "User", + "contributions": 18 + }, + { + "login": "BretJohnson", + "id": 245892, + "type": "User", + "contributions": 18 + }, + { + "login": "janusw", + "id": 484108, + "type": "User", + "contributions": 17 + }, + { + "login": "lutzroeder", + "id": 438516, + "type": "User", + "contributions": 17 + }, + { + "login": "spadapet", + "id": 2523431, + "type": "User", + "contributions": 17 + }, + { + "login": "newky2k", + "id": 6675766, + "type": "User", + "contributions": 16 + }, + { + "login": "melimion", + "id": 33512073, + "type": "User", + "contributions": 16 + }, + { + "login": "cshung", + "id": 3410332, + "type": "User", + "contributions": 15 + }, + { + "login": "sung-su", + "id": 10704816, + "type": "User", + "contributions": 15 + }, + { + "login": "javiercn", + "id": 6995051, + "type": "User", + "contributions": 14 + }, + { + "login": "MackinnonBuck", + "id": 10456961, + "type": "User", + "contributions": 14 + }, + { + "name": "Niklas Schilli", + "email": "schillinik@yahoo.de", + "type": "Anonymous", + "contributions": 14 + }, + { + "login": "dotMorten", + "id": 1378165, + "type": "User", + "contributions": 14 + }, + { + "login": "symbiogenesis", + "id": 1724472, + "type": "User", + "contributions": 13 + }, + { + "name": "GitHub Actions Autoformatter", + "email": "autoformat@example.com", + "type": "Anonymous", + "contributions": 13 + }, + { + "login": "jkurdek", + "id": 59935235, + "type": "User", + "contributions": 13 + }, + { + "login": "LogishaSelvarajSF4525", + "id": 171314950, + "type": "User", + "contributions": 13 + }, + { + "login": "MarkoBL", + "id": 273464, + "type": "User", + "contributions": 13 + }, + { + "login": "SuthiYuvaraj", + "id": 92777079, + "type": "User", + "contributions": 12 + }, + { + "login": "lytico", + "id": 1479980, + "type": "User", + "contributions": 12 + }, + { + "login": "emaf", + "id": 3286258, + "type": "User", + "contributions": 12 + }, + { + "login": "nivetha-nagalingam", + "id": 170066931, + "type": "User", + "contributions": 11 + }, + { + "login": "dellis1972", + "id": 810617, + "type": "User", + "contributions": 11 + }, + { + "login": "BagavathiPerumal", + "id": 93652794, + "type": "User", + "contributions": 11 + }, + { + "login": "VladislavAntonyuk", + "id": 33021114, + "type": "User", + "contributions": 10 + }, + { + "login": "ivanpovazan", + "id": 55002338, + "type": "User", + "contributions": 10 + }, + { + "login": "alanmcgovern", + "id": 264396, + "type": "User", + "contributions": 10 + }, + { + "login": "vpenades", + "id": 5433822, + "type": "User", + "contributions": 10 + }, + { + "login": "Depechie", + "id": 351693, + "type": "User", + "contributions": 10 + }, + { + "login": "csigs", + "id": 13859395, + "type": "User", + "contributions": 10 + }, + { + "name": "Damian Karzon", + "email": "dkarzon@skedulo.com", + "type": "Anonymous", + "contributions": 10 + }, + { + "login": "kzu", + "id": 169707, + "type": "User", + "contributions": 10 + }, + { + "login": "krdmllr", + "id": 11095003, + "type": "User", + "contributions": 10 + }, + { + "login": "SteveSandersonMS", + "id": 1101362, + "type": "User", + "contributions": 10 + }, + { + "login": "mikescandy", + "id": 2494505, + "type": "User", + "contributions": 9 + }, + { + "login": "alanag13", + "id": 5409649, + "type": "User", + "contributions": 9 + }, + { + "login": "Ahamed-Ali", + "id": 102580874, + "type": "User", + "contributions": 9 + }, + { + "login": "japarson", + "id": 59936622, + "type": "User", + "contributions": 9 + }, + { + "login": "activa", + "id": 650710, + "type": "User", + "contributions": 9 + }, + { + "login": "buyaa-n", + "id": 45579687, + "type": "User", + "contributions": 8 + }, + { + "login": "dalexsoto", + "id": 204671, + "type": "User", + "contributions": 8 + }, + { + "login": "ylatuya", + "id": 111134, + "type": "User", + "contributions": 8 + }, + { + "login": "NanthiniMahalingam", + "id": 105482474, + "type": "User", + "contributions": 8 + }, + { + "name": "Mrnikbobjeff", + "email": "schillinik@yahoo.de", + "type": "Anonymous", + "contributions": 8 + }, + { + "login": "mjbond-msft", + "id": 35508883, + "type": "User", + "contributions": 8 + }, + { + "login": "techduggu", + "id": 17450941, + "type": "User", + "contributions": 7 + }, + { + "login": "etvorun", + "id": 48451158, + "type": "User", + "contributions": 7 + }, + { + "login": "felipebaltazar", + "id": 19656249, + "type": "User", + "contributions": 7 + }, + { + "login": "henricm", + "id": 3715975, + "type": "User", + "contributions": 7 + }, + { + "login": "VitalyKnyazev", + "id": 12721191, + "type": "User", + "contributions": 7 + }, + { + "login": "almirvuk", + "id": 15986629, + "type": "User", + "contributions": 7 + }, + { + "login": "brunck", + "id": 4502210, + "type": "User", + "contributions": 7 + }, + { + "login": "conceptdev", + "id": 199373, + "type": "User", + "contributions": 7 + }, + { + "login": "yourina", + "id": 20967778, + "type": "User", + "contributions": 6 + }, + { + "login": "kicsiede", + "id": 4233752, + "type": "User", + "contributions": 6 + }, + { + "login": "dotnet-bot", + "id": 9011267, + "type": "User", + "contributions": 6 + }, + { + "login": "bentmar", + "id": 10244000, + "type": "User", + "contributions": 6 + }, + { + "login": "knocte", + "id": 331303, + "type": "User", + "contributions": 6 + }, + { + "login": "dansiegel", + "id": 3860573, + "type": "User", + "contributions": 6 + }, + { + "login": "ionixjunior", + "id": 519642, + "type": "User", + "contributions": 6 + }, + { + "login": "WayaFlyfeather", + "id": 3807144, + "type": "User", + "contributions": 6 + }, + { + "login": "GalaxiaGuy", + "id": 475450, + "type": "User", + "contributions": 6 + }, + { + "login": "sps014", + "id": 45932883, + "type": "User", + "contributions": 6 + }, + { + "login": "TimBarham", + "id": 9665847, + "type": "User", + "contributions": 6 + }, + { + "login": "Viridovics", + "id": 6348792, + "type": "User", + "contributions": 6 + }, + { + "login": "mkieres", + "id": 8372764, + "type": "User", + "contributions": 5 + }, + { + "login": "eltociear", + "id": 22633385, + "type": "User", + "contributions": 5 + }, + { + "login": "bill2004158", + "id": 211855, + "type": "User", + "contributions": 5 + }, + { + "login": "bruzkovsky", + "id": 10390959, + "type": "User", + "contributions": 5 + }, + { + "login": "molesmoke", + "id": 3227864, + "type": "User", + "contributions": 5 + }, + { + "login": "lindexi", + "id": 16054566, + "type": "User", + "contributions": 5 + }, + { + "login": "VSadov", + "id": 8218165, + "type": "User", + "contributions": 5 + }, + { + "login": "vs-mobiletools-engineering-service2", + "id": 67918504, + "type": "User", + "contributions": 5 + }, + { + "login": "peterfoot", + "id": 3985053, + "type": "User", + "contributions": 5 + }, + { + "login": "dhindrik", + "id": 6691971, + "type": "User", + "contributions": 5 + }, + { + "login": "TheCodeTraveler", + "id": 13558917, + "type": "User", + "contributions": 5 + }, + { + "login": "Dhivya-SF4094", + "id": 127717131, + "type": "User", + "contributions": 5 + }, + { + "login": "edsnider", + "id": 557602, + "type": "User", + "contributions": 5 + }, + { + "login": "f1nzer", + "id": 1970236, + "type": "User", + "contributions": 5 + }, + { + "login": "MSLukeWest", + "id": 42553283, + "type": "User", + "contributions": 5 + }, + { + "login": "mgoertz-msft", + "id": 14282894, + "type": "User", + "contributions": 5 + }, + { + "login": "mrlacey", + "id": 189547, + "type": "User", + "contributions": 5 + }, + { + "name": "Matthew Richardson", + "email": "matthew@velocitysystems.co.nz", + "type": "Anonymous", + "contributions": 5 + }, + { + "login": "mhutch", + "id": 183285, + "type": "User", + "contributions": 5 + }, + { + "login": "NafeelaNazhir", + "id": 171174978, + "type": "User", + "contributions": 5 + }, + { + "login": "bhavanesh2001", + "id": 184209926, + "type": "User", + "contributions": 4 + }, + { + "login": "slang25", + "id": 1341446, + "type": "User", + "contributions": 4 + }, + { + "login": "Cheesebaron", + "id": 249719, + "type": "User", + "contributions": 4 + }, + { + "login": "WonyoungChoi", + "id": 1029205, + "type": "User", + "contributions": 4 + }, + { + "login": "KSemenenko", + "id": 4385716, + "type": "User", + "contributions": 4 + }, + { + "login": "memu8", + "id": 66331238, + "type": "User", + "contributions": 4 + }, + { + "login": "prakashKannanSf3972", + "id": 127308739, + "type": "User", + "contributions": 4 + }, + { + "login": "vishnumenon2684", + "id": 95695113, + "type": "User", + "contributions": 4 + }, + { + "login": "jonpryor", + "id": 155958, + "type": "User", + "contributions": 4 + }, + { + "login": "IlGalvo", + "id": 17992288, + "type": "User", + "contributions": 4 + }, + { + "login": "CliffAgius", + "id": 5613809, + "type": "User", + "contributions": 4 + }, + { + "login": "mfkl", + "id": 3928834, + "type": "User", + "contributions": 4 + }, + { + "login": "vhugogarcia", + "id": 1047398, + "type": "User", + "contributions": 4 + }, + { + "login": "bares43", + "id": 956930, + "type": "User", + "contributions": 4 + }, + { + "login": "flish", + "id": 3818800, + "type": "User", + "contributions": 4 + }, + { + "login": "BrayanKhosravian", + "id": 35541212, + "type": "User", + "contributions": 4 + }, + { + "login": "daltzctr", + "id": 105223895, + "type": "User", + "contributions": 4 + }, + { + "login": "ederbond", + "id": 12549812, + "type": "User", + "contributions": 4 + }, + { + "login": "adamped", + "id": 13672662, + "type": "User", + "contributions": 4 + }, + { + "login": "filipnavara", + "id": 1764393, + "type": "User", + "contributions": 4 + }, + { + "login": "praeclarum", + "id": 323548, + "type": "User", + "contributions": 4 + }, + { + "login": "J-Swift", + "id": 734158, + "type": "User", + "contributions": 4 + }, + { + "login": "jvansickle", + "id": 7631608, + "type": "User", + "contributions": 4 + }, + { + "login": "MartinZikmund", + "id": 1075116, + "type": "User", + "contributions": 4 + }, + { + "login": "BioTurboNick", + "id": 1438610, + "type": "User", + "contributions": 4 + }, + { + "login": "legistek", + "id": 13384523, + "type": "User", + "contributions": 4 + }, + { + "login": "pranavkm", + "id": 174281, + "type": "User", + "contributions": 4 + }, + { + "login": "rolfbjarne", + "id": 249268, + "type": "User", + "contributions": 4 + }, + { + "login": "scastria", + "id": 4534494, + "type": "User", + "contributions": 4 + }, + { + "name": "Paul DiPietro", + "email": "paul.dipietro@microsoft.com", + "type": "Anonymous", + "contributions": 3 + }, + { + "login": "rogihee", + "id": 4984486, + "type": "User", + "contributions": 3 + }, + { + "login": "stmoor", + "id": 77985069, + "type": "User", + "contributions": 3 + }, + { + "login": "sthewissen", + "id": 2419439, + "type": "User", + "contributions": 3 + }, + { + "login": "SubhikshaSf4851", + "id": 184361895, + "type": "User", + "contributions": 3 + }, + { + "login": "TamilarasanSF4853", + "id": 184361845, + "type": "User", + "contributions": 3 + }, + { + "login": "YZahringer", + "id": 4254116, + "type": "User", + "contributions": 3 + }, + { + "login": "Youssef1313", + "id": 31348972, + "type": "User", + "contributions": 3 + }, + { + "login": "jzeferino", + "id": 10722952, + "type": "User", + "contributions": 3 + }, + { + "login": "puppetSpace", + "id": 14003064, + "type": "User", + "contributions": 3 + }, + { + "login": "solomonfried", + "id": 36649129, + "type": "User", + "contributions": 3 + }, + { + "login": "tessarolli", + "id": 11618432, + "type": "User", + "contributions": 3 + }, + { + "login": "magoolation", + "id": 626364, + "type": "User", + "contributions": 3 + }, + { + "login": "beeradmoore", + "id": 904737, + "type": "User", + "contributions": 3 + }, + { + "login": "jgold6", + "id": 4606509, + "type": "User", + "contributions": 3 + }, + { + "login": "akoeplinger", + "id": 1376924, + "type": "User", + "contributions": 3 + }, + { + "login": "patridge", + "id": 713665, + "type": "User", + "contributions": 3 + }, + { + "login": "aaronfranke", + "id": 1646875, + "type": "User", + "contributions": 3 + }, + { + "login": "artemious7", + "id": 16724889, + "type": "User", + "contributions": 3 + }, + { + "login": "artemvalieiev", + "id": 3391032, + "type": "User", + "contributions": 3 + }, + { + "login": "Axemasta", + "id": 33064621, + "type": "User", + "contributions": 3 + }, + { + "login": "BjornVanslembrouck", + "id": 44607491, + "type": "User", + "contributions": 3 + }, + { + "login": "daniel-luberda", + "id": 13569983, + "type": "User", + "contributions": 3 + }, + { + "login": "DianaSoltani", + "id": 31975705, + "type": "User", + "contributions": 3 + }, + { + "login": "ooikengsiang", + "id": 19687809, + "type": "User", + "contributions": 3 + }, + { + "login": "mikeparker104", + "id": 12763221, + "type": "User", + "contributions": 3 + }, + { + "login": "MichaelRumpler", + "id": 8309363, + "type": "User", + "contributions": 3 + }, + { + "login": "mkhamoyan", + "id": 96171496, + "type": "User", + "contributions": 3 + }, + { + "login": "Lehonti", + "id": 17771375, + "type": "User", + "contributions": 3 + }, + { + "login": "jeffhandley", + "id": 1031940, + "type": "User", + "contributions": 3 + }, + { + "login": "SotoiGhost", + "id": 5924210, + "type": "User", + "contributions": 3 + }, + { + "login": "GiampaoloGabba", + "id": 8319337, + "type": "User", + "contributions": 3 + }, + { + "login": "felipemomm", + "id": 6777353, + "type": "User", + "contributions": 3 + }, + { + "login": "espenrl", + "id": 4621581, + "type": "User", + "contributions": 3 + }, + { + "login": "jpobst", + "id": 179295, + "type": "User", + "contributions": 2 + }, + { + "login": "RodgerLeblanc", + "id": 6235690, + "type": "User", + "contributions": 2 + }, + { + "login": "SpiegelSoft", + "id": 557824, + "type": "User", + "contributions": 2 + }, + { + "login": "rick-palmsens", + "id": 57390463, + "type": "User", + "contributions": 2 + }, + { + "login": "nogginbox", + "id": 729381, + "type": "User", + "contributions": 2 + }, + { + "login": "JTOne123", + "id": 3457140, + "type": "User", + "contributions": 2 + }, + { + "login": "PaulAndersonS", + "id": 42271912, + "type": "User", + "contributions": 2 + }, + { + "login": "ntherning", + "id": 135765, + "type": "User", + "contributions": 2 + }, + { + "login": "pingzing", + "id": 5133649, + "type": "User", + "contributions": 2 + }, + { + "login": "manutdkid77", + "id": 14297705, + "type": "User", + "contributions": 2 + }, + { + "login": "nwestfall", + "id": 4753021, + "type": "User", + "contributions": 2 + }, + { + "login": "Moha69100", + "id": 6330427, + "type": "User", + "contributions": 2 + }, + { + "login": "mjmostachetti", + "id": 6486831, + "type": "User", + "contributions": 2 + }, + { + "login": "migueldeicaza", + "id": 36863, + "type": "User", + "contributions": 2 + }, + { + "login": "mauroa", + "id": 2921919, + "type": "User", + "contributions": 2 + }, + { + "login": "mattjohnsonpint", + "id": 1396388, + "type": "User", + "contributions": 2 + }, + { + "login": "martincostello", + "id": 1439341, + "type": "User", + "contributions": 2 + }, + { + "login": "marcmognol", + "id": 8171300, + "type": "User", + "contributions": 2 + }, + { + "login": "domagojmedo", + "id": 12157224, + "type": "User", + "contributions": 2 + }, + { + "login": "DavidRoqueni", + "id": 45717673, + "type": "User", + "contributions": 2 + }, + { + "login": "Dresel", + "id": 1769090, + "type": "User", + "contributions": 2 + }, + { + "login": "Adam--", + "id": 2179171, + "type": "User", + "contributions": 2 + }, + { + "login": "workgroupengineering", + "id": 12531229, + "type": "User", + "contributions": 2 + }, + { + "login": "ruby-verma", + "id": 20953456, + "type": "User", + "contributions": 2 + }, + { + "login": "praveenkumarkarunanithi", + "id": 100338903, + "type": "User", + "contributions": 2 + }, + { + "login": "neolithos", + "id": 9724420, + "type": "User", + "contributions": 2 + }, + { + "login": "maonaoda", + "id": 32831595, + "type": "User", + "contributions": 2 + }, + { + "login": "flyofsky", + "id": 16184582, + "type": "User", + "contributions": 2 + }, + { + "login": "dotnet-policy-service[bot]", + "id": 123482357, + "type": "Bot", + "contributions": 2 + }, + { + "name": "dend", + "email": "dend@outlook.com", + "type": "Anonymous", + "contributions": 2 + }, + { + "login": "campersau", + "id": 4009570, + "type": "User", + "contributions": 2 + }, + { + "login": "WeihanLi", + "id": 7604648, + "type": "User", + "contributions": 2 + }, + { + "login": "vitek-karas", + "id": 10670590, + "type": "User", + "contributions": 2 + }, + { + "login": "surayya-MS", + "id": 114938397, + "type": "User", + "contributions": 2 + }, + { + "login": "stephen-hawley", + "id": 15199050, + "type": "User", + "contributions": 2 + }, + { + "login": "sanyandreichuk", + "id": 8818052, + "type": "User", + "contributions": 2 + }, + { + "login": "Rustamxon", + "id": 123984585, + "type": "User", + "contributions": 2 + }, + { + "login": "maxkoshevoi", + "id": 6609929, + "type": "User", + "contributions": 2 + }, + { + "login": "dmariogatto", + "id": 8588913, + "type": "User", + "contributions": 2 + }, + { + "login": "breenbob", + "id": 6000851, + "type": "User", + "contributions": 2 + }, + { + "login": "cadsit", + "id": 9328605, + "type": "User", + "contributions": 2 + }, + { + "login": "cschwarz", + "id": 717934, + "type": "User", + "contributions": 2 + }, + { + "login": "csteeg", + "id": 431177, + "type": "User", + "contributions": 2 + }, + { + "login": "chabiss", + "id": 14151258, + "type": "User", + "contributions": 2 + }, + { + "login": "bricelam", + "id": 1226749, + "type": "User", + "contributions": 2 + }, + { + "login": "Bobface", + "id": 10945014, + "type": "User", + "contributions": 2 + }, + { + "login": "BitooBit", + "id": 59031286, + "type": "User", + "contributions": 2 + }, + { + "login": "BurningLights", + "id": 14267553, + "type": "User", + "contributions": 2 + }, + { + "login": "mkArtakMSFT", + "id": 34246760, + "type": "User", + "contributions": 2 + }, + { + "login": "andres-fg", + "id": 16245738, + "type": "User", + "contributions": 2 + }, + { + "login": "SkyeHoefling", + "id": 17751436, + "type": "User", + "contributions": 2 + }, + { + "login": "AArnott", + "id": 3548, + "type": "User", + "contributions": 2 + }, + { + "login": "andreas-nesheim", + "id": 11583629, + "type": "User", + "contributions": 2 + }, + { + "login": "aheubusch", + "id": 11393955, + "type": "User", + "contributions": 2 + }, + { + "login": "appsourcers", + "id": 36886162, + "type": "User", + "contributions": 2 + }, + { + "login": "agaluzzi", + "id": 5098044, + "type": "User", + "contributions": 2 + }, + { + "login": "akamud", + "id": 954102, + "type": "User", + "contributions": 2 + }, + { + "login": "HavenDV", + "id": 3002068, + "type": "User", + "contributions": 2 + }, + { + "login": "KarthikRajaKalaimani", + "id": 92777139, + "type": "User", + "contributions": 2 + }, + { + "login": "jorisvergeer", + "id": 1906639, + "type": "User", + "contributions": 2 + }, + { + "login": "Suplanus", + "id": 2002916, + "type": "User", + "contributions": 2 + }, + { + "login": "lateralusX", + "id": 11529140, + "type": "User", + "contributions": 2 + }, + { + "login": "joelmartinez", + "id": 90380, + "type": "User", + "contributions": 2 + }, + { + "login": "jsmarcus", + "id": 1919776, + "type": "User", + "contributions": 2 + }, + { + "name": "Dave Humphreys", + "email": "dave@thing.com", + "type": "Anonymous", + "contributions": 2 + }, + { + "login": "dend", + "id": 1389609, + "type": "User", + "contributions": 2 + }, + { + "login": "breyed", + "id": 1299073, + "type": "User", + "contributions": 2 + }, + { + "login": "thudugala", + "id": 4112014, + "type": "User", + "contributions": 2 + }, + { + "login": "EmilAlipiev", + "id": 10422347, + "type": "User", + "contributions": 2 + }, + { + "name": "Israel Soto", + "email": "issoto@microsoft.com", + "type": "Anonymous", + "contributions": 2 + }, + { + "login": "terrajobst", + "id": 5169960, + "type": "User", + "contributions": 2 + }, + { + "login": "ethanis", + "id": 8703324, + "type": "User", + "contributions": 2 + }, + { + "login": "fredeil", + "id": 17533404, + "type": "User", + "contributions": 2 + }, + { + "login": "ghuntley", + "id": 127353, + "type": "User", + "contributions": 2 + }, + { + "name": "Stefan de Vogelaere", + "email": "stefan@cavebirdlabs.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Stefan Gerasch", + "email": "stefangerasch1991@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Stanislav", + "email": "stasbav@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sreeraj P R", + "email": "sreerajpr.mec@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Soundman32", + "email": "neil.scales@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Soichi Ikebe", + "email": "souichi1997@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Softlion (Benjamin Mayrargue)", + "email": "benjamin@vapolia.fr", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Softlion (Benjamin Mayrargue)", + "email": "benjamin@mayrargue.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Smit Patel", + "email": "smitpatel@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sivamuthu Kumar", + "email": "sivamuthukumar07@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sivakumar", + "email": "sivakumarr@syncfusion.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Simon Cropp", + "email": "simon.cropp@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Shalini-Ashokan", + "email": "102292178+shalini-ashokan@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sergio Escalada", + "email": "sescalada@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sergej Derjabkin", + "email": "sergej@sendev.de", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sebastian Rauer", + "email": "info@sebastianrauer.de", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sean Killeen", + "email": "seankilleen@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sean Davies", + "email": "spdavies2@live.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sandro Cavazzoni", + "email": "sandro@ideaful.it", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Samir GC", + "email": "55045516+samirgcofficial@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Todd", + "email": "thenderson21@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "TingtingAn", + "email": "antingting2009@qq.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Timothé Larivière", + "email": "timothe.lariviere@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tim Johnson", + "email": "t-johnson@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tianqi Zhang", + "email": "tianqizhang@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Thomas Muller", + "email": "imuller@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Thomas Mijieux", + "email": "tmijieux@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Thetyne", + "email": "thetyne@live.fr", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Theodore Tsirpanis", + "email": "teo-tsirpanis@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Takym (たかやま)", + "email": "15681312+takym@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Takaaki Suzuki", + "email": "xin9le@live.jp", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sławomir Kulików", + "email": "slawomir.kulikow@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sławomir Kulików", + "email": "kulikow@logotec.pl", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sören Nils Kuklau", + "email": "chucker@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Symbai", + "email": "14368203+symbai@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sylvain Gravel", + "email": "lrp-sgravel@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "SyedAbdulAzeemSF4852", + "email": "syedabdulazeem.a@syncfusion.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sven Boemer", + "email": "sbomer@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Surayya Huseyn Zada", + "email": "shuseynzada@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sunner", + "email": "sunnerlp@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "StevenGranados", + "email": "51818199+stevengranados@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Steve", + "email": "steverichey@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Stephen Halter", + "email": "halter73@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Sam", + "email": "28056243+faheys@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Peter Spada", + "email": "spadapet@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "PawKanarek", + "email": "paw.kanarek@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "PavloLukianets", + "email": "109070173+pavlolukianets@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Pavel Yakovlev (Akvelon)", + "email": "v-payako@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Patrick Long", + "email": "pat@munkiisoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Patrick Hartman", + "email": "patrick@leglock.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Patrick Allwood", + "email": "patchandthat@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Parker Bibus", + "email": "pbibus@hotmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Parker Bibus", + "email": "parkerbibus@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "P3PPP", + "email": "ticktackmob@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Oren Novotny", + "email": "oren@novotny.org", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ooi Keng Siang", + "email": "ks@ooiks.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Omniq-APL-Rollving", + "email": "61408543+omniq-apl-rollving@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Oleksandr Dobrynin", + "email": "shuron1988@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Oleh Kaliuzhnyi", + "email": "oleg.kaliuzhny@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Oddbjørn Bakke", + "email": "oddbjorn.bakke@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Oberdan Bitencourt Ferreira", + "email": "oberdan.bitencourt@arctouch.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Noam Yogev", + "email": "noamyogev84@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Niklas Therning", + "email": "nitherni@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Niklas Schilli", + "email": "niklasschilli@mailbox.org", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Niklas Schilli", + "email": "n.schilli@inno-focus.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Niels van der Knaap", + "email": "niels@nielsknaap.nl", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "CartBlanche", + "id": 271363, + "type": "User", + "contributions": 1 + }, + { + "name": "Salar K", + "email": "1272095+salarcode@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Safia Abdalla", + "email": "safia@safia.rocks", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ryo Tsunoda", + "email": "try0.development@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ryan Robidou", + "email": "robidou@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ryan Davis", + "email": "ryandavis.au@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ryan Buckley", + "email": "9486206+rabuckley@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Rui Marinho", + "email": "ruimarinho@msft-m3-pro.ihome", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Roy", + "email": "royvou@hotmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ronny Gunawan", + "email": "3048897+ronnygunawan@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Ronan", + "email": "burkuscat@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Roger Hardiman", + "email": "rogerhardiman@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Rasto", + "email": "rasto@duracellko.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Rastislav Novotný", + "email": "rasto@duracellko.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Raka Rasell", + "email": "rrrraka@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Rafal", + "email": "rkdevel@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Precious Nyasulu", + "email": "preciousnyasulu441@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Praga Siva", + "email": "praga@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Poppyto", + "email": "poppyto@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Pieter Nijs", + "email": "pieternijs@live.be", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Pierre Henri", + "email": "pierrehenrikt@kleartouch.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Pierre Galaup", + "email": "pierregalaup@outlook.fr", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Philipp Sumi", + "email": "hardcodet@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Phil Henning", + "email": "phenning@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "nextfool", + "email": "nextfool@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "nels83", + "email": "nels+git@pgroupe.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "nalka0", + "email": "30848600+nalka0@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "mos379", + "email": "45105519+mos379@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "mohsen bagheri", + "email": "85581484+mohsenbgi@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "michalpobuta", + "email": "52126292+michalpobuta@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "matt4pi", + "email": "matt@4pi.com.au", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "masonyc", + "email": "mason.yunchen@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "mallibone", + "email": "mallibone@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "maexsp", + "email": "maex.sp@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "luyajun0205", + "email": "v-yajlu@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "lewcianci", + "email": "lewcianci@hotmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "kadiryazgan", + "email": "kadiryazgan@msn.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "juliuszint", + "email": "julius.zint@awin-software.de", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "jpiechowiak", + "email": "jerzy.piechowiak@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "jonlipsky", + "email": "jonlipsky@github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "john-larson", + "email": "21021544+john-larson@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "joepgrooten", + "email": "52749854+joepgrooten@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "jaytilly", + "email": "jt@jaybirdlabs.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "jacobmcgoogan", + "email": "jacob.mcgoogan@allscripts.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "inforithmics", + "email": "thomas.stocker@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "indydawgy", + "email": "tophe112@yahoo.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Xiangwei Cai", + "email": "v-caxian@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "zhuXinghan", + "email": "1097278366@qq.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "z3ut", + "email": "z3ut@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "ytn3rd", + "email": "b.moore@4pilabs.com.au", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "yone64", + "email": "yone64@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "wplong11", + "email": "wplong11@naver.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "woutermeuwis", + "email": "wouter.meuwis@3factr.be", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "webwarrior-ws", + "email": "webwarrior-ws@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "weases", + "email": "richard.wiesinger@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "v-haroha", + "email": "51649153+v-haroha@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "tomeverin", + "email": "24466690+tomeverin@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "tj-devel709", + "email": "tj.devel709@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "thisisthekap", + "email": "christian.kapplmueller@tonestro.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "thisisthekap", + "email": "c.kapplmueller@me.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "spruceDevelopment", + "email": "33689503+sprucedevelopment@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "softlion", + "email": "benjamin@vapolia.fr", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "sheiksyedm", + "email": "sheiksyedm@syncfusion.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "sahi82", + "email": "sahi82@yahoo.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "rjantz3", + "email": "rjantz3@msn.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "qmark", + "email": "dimka.qmark@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "pit", + "email": "37137614+pitmobile@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "piotrkonowalski", + "email": "piotrkonowalski@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "pRopia", + "email": "pratik.ropia@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "nor0x", + "email": "nor0x@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Will Davies", + "email": "wdavies973@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "WdeBruin", + "email": "wouterdebruin88@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vũ Đức Tuyến", + "email": "tuyen@naxam.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Víctor Martos", + "email": "vmareg@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Víctor Guzmán", + "email": "vgzman+github@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vojtěch Mádr", + "email": "madrvojtech@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vlada Shubina", + "email": "vshubina@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vinicius Jarina", + "email": "viniciusjarina@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vincent Hoogendoorn", + "email": "vincenth.net@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vincent Dondain", + "email": "vincentdondain@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vincent Costel", + "email": "vincent.costel@apcurium.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Vijay Anand E G", + "email": "81947404+egvijayanand@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tushar Koshti", + "email": "koshtitushar1994@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Trung Nguyen", + "email": "57174311+trungnt2910@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Trivalik", + "email": "3148279+trivalik@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Toni Petrina", + "email": "tonipetrina@hotmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tomáš Ondřej", + "email": "45392392+tomond@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tommi Gustafsson", + "email": "tommi.gustafsson@hyvanmielenpelit.fi", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tomasz Ścisłowicz", + "email": "toomasz@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tomasz Cielecki", + "email": "249719+cheesebaron@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tom Meschter", + "email": "tom.meschter@valinor.org", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Tom Gilder", + "email": "tom@tom.me.uk", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "hydrogen-software-ff", + "email": "81678363+hydrogen-software-ff@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "humhei", + "email": "humhei@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "gleblebedev", + "email": "gleb@gleblebedev.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "genriquez", + "email": "genriquez@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "fengrui", + "email": "fengrui358@163.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "dottienet", + "email": "61212782+dottienet@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "dogukandemir", + "email": "dogukandemir@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "digitaldirk", + "email": "22691956+digitaldirk@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "dgeller-OUHSC", + "email": "dgeller@ouhsc.edu", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "dartasen", + "email": "10561268+dartasen@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "cliff v", + "email": "powerdude@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "ckrempp91", + "email": "chrisk@msidata.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "chenrensong", + "email": "chenrensong@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "cat0363", + "email": "125236133+cat0363@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "borrmann", + "email": "max.borrmann@gmx.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "atoghyani", + "email": "ariantoghyani@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "adammeaney", + "email": "meaneykid2@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "acuntex", + "email": "werner@welsch.cc", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Yusuke Yamada", + "email": "yamachu.dev@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Yunus Efendi", + "email": "19399214+yunusefendi52@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Yun Chen", + "email": "mason.yunchen@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Youssef Victor", + "email": "31348972+youssef1313@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Yaser Moradi", + "email": "ysmoradi@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "LyalinDotCom", + "id": 2363526, + "type": "User", + "contributions": 1 + }, + { + "login": "ChasakisD", + "id": 23040695, + "type": "User", + "contributions": 1 + }, + { + "login": "humblehacker", + "id": 117582, + "type": "User", + "contributions": 1 + }, + { + "login": "EislerDavid", + "id": 48833197, + "type": "User", + "contributions": 1 + }, + { + "login": "davidbritch", + "id": 8092460, + "type": "User", + "contributions": 1 + }, + { + "name": "Dave Humphreys", + "email": "dave@none.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "danielwilliamson", + "id": 6487407, + "type": "User", + "contributions": 1 + }, + { + "login": "danielmarbach", + "id": 174258, + "type": "User", + "contributions": 1 + }, + { + "login": "danielchalmers", + "id": 7112040, + "type": "User", + "contributions": 1 + }, + { + "login": "danmoseley", + "id": 6385855, + "type": "User", + "contributions": 1 + }, + { + "login": "DamianMehers", + "id": 1457446, + "type": "User", + "contributions": 1 + }, + { + "login": "dkarzon", + "id": 214449, + "type": "User", + "contributions": 1 + }, + { + "login": "Csaba8472", + "id": 483692, + "type": "User", + "contributions": 1 + }, + { + "login": "craigloewen-msft", + "id": 42221804, + "type": "User", + "contributions": 1 + }, + { + "login": "CleanCodeDeveloper", + "id": 16760760, + "type": "User", + "contributions": 1 + }, + { + "login": "csdinon", + "id": 24420845, + "type": "User", + "contributions": 1 + }, + { + "login": "Choza-rajan", + "id": 92723643, + "type": "User", + "contributions": 1 + }, + { + "login": "Chase-William", + "id": 46757278, + "type": "User", + "contributions": 1 + }, + { + "login": "ChaseMarsh", + "id": 19700403, + "type": "User", + "contributions": 1 + }, + { + "login": "charlesroddie", + "id": 19760720, + "type": "User", + "contributions": 1 + }, + { + "name": "Charles Petzold", + "email": "chape@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "kvochko", + "id": 2308830, + "type": "User", + "contributions": 1 + }, + { + "name": "Max Brister", + "email": "a+github@2bass.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "Giviruk", + "id": 42971690, + "type": "User", + "contributions": 1 + }, + { + "login": "giuseppe-guerra", + "id": 11569903, + "type": "User", + "contributions": 1 + }, + { + "login": "Ghostbird", + "id": 1202149, + "type": "User", + "contributions": 1 + }, + { + "login": "tarasverq", + "id": 8226275, + "type": "User", + "contributions": 1 + }, + { + "login": "JoergMeier106", + "id": 22917826, + "type": "User", + "contributions": 1 + }, + { + "login": "gabor-nemeth", + "id": 37656640, + "type": "User", + "contributions": 1 + }, + { + "login": "FriedrichRehren", + "id": 22966022, + "type": "User", + "contributions": 1 + }, + { + "login": "fredyadriano90", + "id": 25233851, + "type": "User", + "contributions": 1 + }, + { + "login": "kiok85", + "id": 158862689, + "type": "User", + "contributions": 1 + }, + { + "login": "francedot", + "id": 11706033, + "type": "User", + "contributions": 1 + }, + { + "login": "fenxu", + "id": 12146082, + "type": "User", + "contributions": 1 + }, + { + "login": "IIFabixn", + "id": 92400234, + "type": "User", + "contributions": 1 + }, + { + "login": "EP01", + "id": 22454118, + "type": "User", + "contributions": 1 + }, + { + "login": "eth-ellis", + "id": 13865151, + "type": "User", + "contributions": 1 + }, + { + "login": "Eschryn", + "id": 8217655, + "type": "User", + "contributions": 1 + }, + { + "login": "modplug", + "id": 789588, + "type": "User", + "contributions": 1 + }, + { + "login": "jingliancui", + "id": 16309962, + "type": "User", + "contributions": 1 + }, + { + "login": "eliaspuurunen", + "id": 695294, + "type": "User", + "contributions": 1 + }, + { + "login": "Elashi", + "id": 12378171, + "type": "User", + "contributions": 1 + }, + { + "login": "wachs", + "id": 12235778, + "type": "User", + "contributions": 1 + }, + { + "name": "Edwin Wachs", + "email": "edwin.wachs@tecfinance.com.br", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "Eden-Mor", + "id": 20691183, + "type": "User", + "contributions": 1 + }, + { + "name": "Andrew Hoefling", + "email": "andrew@hoeflingsoftware.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "awaescher", + "id": 3630638, + "type": "User", + "contributions": 1 + }, + { + "login": "AndersRasmussen", + "id": 213707, + "type": "User", + "contributions": 1 + }, + { + "login": "AmrAlSayed0", + "id": 2955032, + "type": "User", + "contributions": 1 + }, + { + "login": "AlleSchonWeg", + "id": 6727366, + "type": "User", + "contributions": 1 + }, + { + "login": "aritchie", + "id": 1431555, + "type": "User", + "contributions": 1 + }, + { + "login": "alfredmyers", + "id": 12210687, + "type": "User", + "contributions": 1 + }, + { + "login": "AxelUser", + "id": 7935489, + "type": "User", + "contributions": 1 + }, + { + "login": "cacti-acaprais", + "id": 43949501, + "type": "User", + "contributions": 1 + }, + { + "login": "NordAlex", + "id": 6042648, + "type": "User", + "contributions": 1 + }, + { + "login": "ahouben", + "id": 2446666, + "type": "User", + "contributions": 1 + }, + { + "name": "Alexander Hardwicke", + "email": "alex.hardwicke@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "Pandalink", + "id": 18328783, + "type": "User", + "contributions": 1 + }, + { + "login": "adrianhall", + "id": 1489465, + "type": "User", + "contributions": 1 + }, + { + "login": "adamsitnik", + "id": 6011991, + "type": "User", + "contributions": 1 + }, + { + "login": "APoukar", + "id": 36044908, + "type": "User", + "contributions": 1 + }, + { + "name": "Adam Pedley", + "email": "adam.pedley@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "adamivancza", + "id": 19835917, + "type": "User", + "contributions": 1 + }, + { + "login": "adambarath", + "id": 14049972, + "type": "User", + "contributions": 1 + }, + { + "login": "1iveowl", + "id": 12871120, + "type": "User", + "contributions": 1 + }, + { + "login": "1d0n7kn0w", + "id": 3910210, + "type": "User", + "contributions": 1 + }, + { + "login": "1c3f0x84", + "id": 35239416, + "type": "User", + "contributions": 1 + }, + { + "name": "Chad Kimes", + "email": "chad.kimes@iticentral.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "kericbowman", + "id": 12725288, + "type": "User", + "contributions": 1 + }, + { + "login": "breezy64", + "id": 3731287, + "type": "User", + "contributions": 1 + }, + { + "login": "bkaankose", + "id": 12009960, + "type": "User", + "contributions": 1 + }, + { + "login": "Brucepimenta", + "id": 37835327, + "type": "User", + "contributions": 1 + }, + { + "login": "brainoffline", + "id": 1763877, + "type": "User", + "contributions": 1 + }, + { + "login": "bradencohen", + "id": 11482832, + "type": "User", + "contributions": 1 + }, + { + "login": "TrueGeek", + "id": 90821, + "type": "User", + "contributions": 1 + }, + { + "login": "bbenetskyy", + "id": 8330262, + "type": "User", + "contributions": 1 + }, + { + "name": "Bohdan Benetskyi", + "email": "bbenetskyi@pgs-soft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "bwoebi", + "id": 3154871, + "type": "User", + "contributions": 1 + }, + { + "login": "BinaryCraX", + "id": 5560801, + "type": "User", + "contributions": 1 + }, + { + "login": "billvenhaus", + "id": 6692379, + "type": "User", + "contributions": 1 + }, + { + "login": "BenBtg", + "id": 70525, + "type": "User", + "contributions": 1 + }, + { + "login": "baskren", + "id": 2528888, + "type": "User", + "contributions": 1 + }, + { + "login": "BarryNolte", + "id": 4474173, + "type": "User", + "contributions": 1 + }, + { + "login": "BaY1251", + "id": 48309205, + "type": "User", + "contributions": 1 + }, + { + "login": "axelgorris", + "id": 11756311, + "type": "User", + "contributions": 1 + }, + { + "login": "atsushieno", + "id": 53929, + "type": "User", + "contributions": 1 + }, + { + "login": "artemutin", + "id": 7271331, + "type": "User", + "contributions": 1 + }, + { + "login": "mellson", + "id": 167574, + "type": "User", + "contributions": 1 + }, + { + "login": "AndrewLang", + "id": 3375370, + "type": "User", + "contributions": 1 + }, + { + "login": "AndreKraemer", + "id": 312671, + "type": "User", + "contributions": 1 + }, + { + "name": "Mausam Shrestha", + "email": "46900712+mausam-shrestha@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mauro Agnoletti", + "email": "mauro.agnoletti@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mattias Sjögren", + "email": "github@msjogren.net", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matthijs ter Woord", + "email": "matthijsterwoord@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matthew Robbins", + "email": "matthew.ch.robbins@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Ward", + "email": "ward.matt@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Ward", + "email": "matt.ward@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Soucoup", + "email": "masoucou@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Regul", + "email": "mareg@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Mitchell", + "email": "mmitche@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Matt Goldman", + "email": "matt.goldman@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mathias Storm", + "email": "mathias@storm.lc", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mateus Luiz Camilo", + "email": "37350787+mathewlc@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Martin Kuckert", + "email": "m.kuckert@salt-and-pepper.eu", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Martin", + "email": "modermatt@tuta.io", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Martijn van Dijk", + "email": "mhvdijk@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mark", + "email": "mrcull@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "Marioo1357", + "id": 45981364, + "type": "User", + "contributions": 1 + }, + { + "login": "MariovanZeist", + "id": 12493059, + "type": "User", + "contributions": 1 + }, + { + "login": "Marcus-L", + "id": 1369184, + "type": "User", + "contributions": 1 + }, + { + "login": "mgierlasinski", + "id": 19707868, + "type": "User", + "contributions": 1 + }, + { + "login": "john-hollander", + "id": 11653688, + "type": "User", + "contributions": 1 + }, + { + "name": "Nicolò Carandini", + "email": "ncarandini@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "NicolD", + "email": "nicolgit@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Nick Kovalsky", + "email": "taublast@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Nick Gamroth", + "email": "thebeekeeper@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Neville Nazerane", + "email": "31866047+neville-nazerane@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Muzib", + "email": "thisismuzib@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mitchell Hwang", + "email": "16830051+mdh1418@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mikhail", + "email": "om2804@mail.ru", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mike Schwörer", + "email": "mailport@mikescher.de", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mike Parker", + "email": "mjbparker@outlook.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mike Parker", + "email": "mikeparker104", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mike Norman", + "email": "mike.norman@xamarin.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mike Irving", + "email": "79252299+mikeirvingweb@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mihhail Maslakov", + "email": "mihhail.maslakov@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael Yanni", + "email": "miyanni@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael S. Scherotter", + "email": "mischero@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael Dwan", + "email": "m@dwan.io", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael Cummings (MSFT)", + "email": "mcumming@microsoft.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael Cao", + "email": "poppop208@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Michael", + "email": "michael@zpf.fr", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Mendy Berger", + "email": "12537668+mendyberger@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "MemeMan", + "email": "52583316+imememani@users.noreply.github.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "name": "Maximo Piva", + "email": "maximo.piva@gmail.com", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "johannesegger", + "id": 237707, + "type": "User", + "contributions": 1 + }, + { + "login": "johankson", + "id": 1113636, + "type": "User", + "contributions": 1 + }, + { + "login": "JogyBlack", + "id": 1275686, + "type": "User", + "contributions": 1 + }, + { + "name": "Joakim Carselind", + "email": "joakim.carselind@cub.se", + "type": "Anonymous", + "contributions": 1 + }, + { + "login": "jimbobbennett", + "id": 1710385, + "type": "User", + "contributions": 1 + }, + { + "login": "jrjrguo", + "id": 27794646, + "type": "User", + "contributions": 1 + }, + { + "login": "Powerhelmsman", + "id": 33136632, + "type": "User", + "contributions": 1 + }, + { + "login": "solvingj", + "id": 8557737, + "type": "User", + "contributions": 1 + }, + { + "login": "lirkki", + "id": 665778, + "type": "User", + "contributions": 1 + }, + { + "login": "JanKrivanek", + "id": 3809076, + "type": "User", + "contributions": 1 + }, + { + "login": "JimBobSquarePants", + "id": 385879, + "type": "User", + "contributions": 1 + }, + { + "login": "licon4812", + "id": 32421608, + "type": "User", + "contributions": 1 + }, + { + "login": "jadenrogers", + "id": 8000621, + "type": "User", + "contributions": 1 + }, + { + "login": "lamest", + "id": 7779341, + "type": "User", + "contributions": 1 + }, + { + "login": "RussKie", + "id": 4403806, + "type": "User", + "contributions": 1 + }, + { + "login": "ice-j", + "id": 3597015, + "type": "User", + "contributions": 1 + }, + { + "login": "ChummerUA", + "id": 12427312, + "type": "User", + "contributions": 1 + }, + { + "login": "hemantbeast", + "id": 10476066, + "type": "User", + "contributions": 1 + }, + { + "login": "Happypig375", + "id": 19922066, + "type": "User", + "contributions": 1 + }, + { + "login": "tracplus-hpaterson", + "id": 87885287, + "type": "User", + "contributions": 1 + }, + { + "login": "gatm50", + "id": 440179, + "type": "User", + "contributions": 1 + }, + { + "login": "GuidoNeele", + "id": 1301586, + "type": "User", + "contributions": 1 + }, + { + "login": "MarcelStommel", + "id": 45386201, + "type": "User", + "contributions": 1 + }, + { + "login": "FaithfulDev", + "id": 16925901, + "type": "User", + "contributions": 1 + }, + { + "login": "manishkungwani", + "id": 673367, + "type": "User", + "contributions": 1 + }, + { + "login": "mairaw", + "id": 12971179, + "type": "User", + "contributions": 1 + }, + { + "login": "MagicAndre1981", + "id": 8274816, + "type": "User", + "contributions": 1 + }, + { + "login": "guardrex", + "id": 1622880, + "type": "User", + "contributions": 1 + }, + { + "login": "lizharems", + "id": 103444841, + "type": "User", + "contributions": 1 + }, + { + "login": "leoMehlig", + "id": 9119485, + "type": "User", + "contributions": 1 + }, + { + "login": "rs-lkroneman", + "id": 66680815, + "type": "User", + "contributions": 1 + }, + { + "login": "lobrien", + "id": 181571, + "type": "User", + "contributions": 1 + }, + { + "login": "lewing", + "id": 24063, + "type": "User", + "contributions": 1 + }, + { + "login": "KyNam", + "id": 1797185, + "type": "User", + "contributions": 1 + }, + { + "login": "pineapple216", + "id": 7579094, + "type": "User", + "contributions": 1 + }, + { + "login": "koviant", + "id": 23036313, + "type": "User", + "contributions": 1 + }, + { + "login": "thekbb", + "id": 8903473, + "type": "User", + "contributions": 1 + }, + { + "login": "kentcb", + "id": 1901832, + "type": "User", + "contributions": 1 + }, + { + "login": "khuongntrd", + "id": 8498480, + "type": "User", + "contributions": 1 + }, + { + "login": "dahlbyk", + "id": 133987, + "type": "User", + "contributions": 1 + }, + { + "login": "Kahbazi", + "id": 19396090, + "type": "User", + "contributions": 1 + }, + { + "login": "garuma", + "id": 104105, + "type": "User", + "contributions": 1 + }, + { + "login": "tondat", + "id": 383380, + "type": "User", + "contributions": 1 + }, + { + "login": "JFMG", + "id": 100067782, + "type": "User", + "contributions": 1 + }, + { + "login": "Jon2G", + "id": 24820069, + "type": "User", + "contributions": 1 + } +] diff --git a/.github/prompts/release_notes.prompt.md b/.github/prompts/release_notes.prompt.md new file mode 100644 index 000000000000..4210177b4ed1 --- /dev/null +++ b/.github/prompts/release_notes.prompt.md @@ -0,0 +1,419 @@ +# .NET MAUI Release Notes Generator + +You are an Open Source release notes generator assistant responsible for classifying and generating comprehensive release notes between two commits from the repository the user specifies. + +## Understanding Categories + +You will classify all commits into exactly ONE of the following categories: + +1. **MAUI Product Fixes**: Bug fixes, improvements, and features related to the MAUI product itself +2. **Dependency Updates**: Updates to dependencies, packages, libraries, or SDKs +3. **Testing**: Test-related changes, test infrastructure, and test improvements +4. **Docs**: Documentation changes, samples, and tutorials +5. **Housekeeping**: Build system changes, CI pipeline, code cleanup, formatting, and any other changes + +Every commit must be classified into exactly one category. When uncertain about where to place a commit, follow the classification rules below or default to Housekeeping. + +## Process for Creating Release Notes + +When asked to create release notes for a particular branch, follow these steps: + +### 1. Finding the Commits to Compare + +* When user specifies two branches or commits, use these for comparison +* If only one branch/commit is provided, you'll need to determine the previous release point, ask the user to tell you what is the previous release branch you can try something like `git branch -a | grep -E "release/10.0.*preview"` +* If needed, ask the user for the comparison point or the previous branch + +### 2. Retrieving the Commit Log + +* Use `git log` or equivalent to get the commits between the two commits/branches and save it to a file like this exmaple `git log --pretty=format:"%h - %s - #%cd (%an - %ae)" --date=short release/10.0.1xx-preview2..release/10.0.1xx-preview3 > release_notes_commits.txt` +* Ensure you capture all commits within the specified range +* Pay attention to merge commits that might indicate important feature merges + +### 3. Find the correct Github username for each commit + +* For each commit on the list, look up the corresponding GitHub username from the actual GitHub PR, not just the commit author email +* Use the `get_pull_request` tool if available to fetch the actual PR information including the correct GitHub username that created the PR +* Use the [text](contributors.json) file to help map commit authors to GitHub usernames +* When encountering an email address in a commit author, check if it exists in the contributors.json file and use the corresponding GitHub login +* Verify all usernames are consistent with GitHub's format (e.g., @jsuarezruiz instead of @javiersuarezruiz) +* Some common username transformations to check for: + - Internal usernames may differ from GitHub usernames + - Email addresses should be converted to GitHub handles + - Employee IDs or numbers in usernames should be included if they are part of the GitHub username (e.g., @HarishKumarSF4517 not @harish.kumar) +* Keep the '@' prefix for all usernames to maintain consistency +* For "Anonymous" type contributors in [text](contributors.json), use their name as shown but try to find their corresponding GitHub username if possible +* For automated systems like dependabot or github-actions, use the standard bot usernames (@dependabot[bot], @github-actions[bot], etc.) + +### 4. Classifying the Commits + +To help when doing your category analizing use lower case of the commit messages +Apply these classification rules: + +* **MAUI Product Fixes**: + - Bug fixes with platform tags like [iOS], [Android], [Windows], [Mac], [MacCatalyst], [android], [x], [xaml], [core], [ios], [android], [windows], [mac], [maccatalyst] + - Feature additions or improvements to MAUI components + - Performance improvements + - API changes and enhancements + - New features or fixes related to Aspire + - Remove for fixing pending TODOs + - Trimmer and AOT related changes + +* **Testing**: + - Has tag [testing], [test], [uitests] or contains terms like "test", "add test", "UI test", "unit test", "UItests", "uitests", if any of these exists the commit is from Testing category + - Changes to test infrastructure, test frameworks, or CI test configurations + - Test coverage improvements + +* **Docs**: + - Has tag [docs] or contains terms like "documentation", "docs", "sample", "example" + - README updates, API documentation, code comments + - No other commits should belong here + +* **Dependency Updates**: + - Updates to package references, dependencies, or SDKs + - Commits from automation bots updating dependencies (e.g., @dotnet-maestro) + - Version bumps of external libraries + - Changes to NuGet packages + +* **Housekeeping**: + - CI pipeline changes, formatting fixes, repo maintenance + - Build system modifications, tooling updates + - Refactoring with no functional changes + - Any commit that doesn't clearly fit other categories + - Merging a branch to another branch + - Has tag [ci] + + +### 5. Organizing for the Response + +* Group commits by category as defined in section 1 +* Within each category, list in descending order by PR number (newest PRs first) +* For PR numbers: + - Ensure they are formatted as '#XXXXX' (e.g., #28804) + - When creating GitHub links, use full URLs: https://github.com/dotnet/maui/pull/XXXXX +* For contributor attribution: + - Use ONLY the GitHub username that appears in the PR, not the commit author + - Always prefix usernames with '@' (e.g., @kubaflo) + - Be especially careful with usernames that have employee IDs or numbers at the end + - For automated actions, use @github-actions or @dotnet-bot as appropriate +* Save the results to a markdown file like docs/release_notes_{releasename}.md + +### 6. Special Cases & Edge Cases + +* **Reverts**: Classify reverted commits to the same category as the original commit +* **Automated PRs**: Place automation-driven changes (like dependency updates) in appropriate categories like Dependency Updates +* **Cross-cutting changes**: When a commit spans multiple categories, prioritize based on the primary focus +* **Breaking changes**: Highlight any breaking changes prominently in the summary +* **New contributors**: Include a separate section acknowledging first-time contributors + +## Response Format + +Structure your release notes in the following categorized format, and save them to a file like docs/release_notes_{releasename}.md: + +```markdown +### MAUI Product Fixes +* [Commit title] by @[correct-github-username] in https://github.com/dotnet/maui/pull/[PR number] +* ... + +### Testing +* [Commit title] by @[correct-github-username] in https://github.com/dotnet/maui/pull/[PR number] +* ... + +### Dependency Updates +* [Commit title] by @[correct-github-username] in https://github.com/dotnet/maui/pull/[PR number] +* ... + +### Docs +* [Commit title] by @[correct-github-username] in https://github.com/dotnet/maui/pull/[PR number] +* ... + +### Housekeeping +* [Commit title] by @[correct-github-username] in https://github.com/dotnet/maui/pull/[PR number] +* ... + +## New Contributors +* @[correct-github-username] made their first contribution in https://github.com/dotnet/maui/pull/[PR number] +* ... + +**Full Changelog**: https://github.com/dotnet/maui/compare/[previous-branch]...[current-branch] +``` + +## Contributors list + +[text](contributors.json) + + +## Example + +Here's a shortened example of properly formatted release notes: + +## What's Changed + +* Internalize/remove MessagingCenter by @jfversluis in https://github.com/dotnet/maui/pull/27842 +* [release/10.0.1xx-preview2] Obsolete TableView by @github-actions in https://github.com/dotnet/maui/pull/28327 + +### MAUI Product Fixes +* Radio button's default template improvements by @kubaflo in https://github.com/dotnet/maui/pull/26719 +* [Windows] - Fixed Window Title Not Shown When Reverting from TitleBar to Default State by @prakashKannanSf3972 in https://github.com/dotnet/maui/pull/27148 +* [Windows] Fixed Margin Not Applied to Shell Flyout Template Items on First Display by @prakashKannanSf3972 in https://github.com/dotnet/maui/pull/27060 +* [iOS] Fix for Left SwipeView Items Conflict with Shell Menu Swipe Gesture by @Tamilarasan-Paranthaman in https://github.com/dotnet/maui/pull/26976 +* [iOS]Fix for Character Spacing Not Updating Correctly in Editor for Dynamically Added Text by @devanathan-vaithiyanathan in https://github.com/dotnet/maui/pull/25347 +* Make ImageSource more async-friendly by @symbiogenesis in https://github.com/dotnet/maui/pull/22098 +* [XC] don't call ProvideValue on compiled bindings by @StephaneDelcroix in https://github.com/dotnet/maui/pull/27509 +* [XC] trim x:Name values by @StephaneDelcroix in https://github.com/dotnet/maui/pull/27452 +* [Mac] TitleBar not always initally set by @tj-devel709 in https://github.com/dotnet/maui/pull/27487 +* [X] don't expand types to Extension for x:Static by @StephaneDelcroix in https://github.com/dotnet/maui/pull/17276 +* Fix Android TextView being truncated under some conditions by @albyrock87 in https://github.com/dotnet/maui/pull/27179 +* Improve debugger display XP by @pictos in https://github.com/dotnet/maui/pull/27489 +* [Windows]Fixed Shell Navigating event issue when switching tabs by @Vignesh-SF3580 in https://github.com/dotnet/maui/pull/27197 +* Revert "Implementation of Customizable Search Button Color for SearchBar Across Platforms (#26759)" by @jfversluis in https://github.com/dotnet/maui/pull/27568 +* [net10.0] Revert "Implementation of Customizable Search Button Color for SearchBar Across Platforms (#26759)" by @github-actions in https://github.com/dotnet/maui/pull/27578 +* Use TCS for BusySetSignalName tests by @PureWeen in https://github.com/dotnet/maui/pull/27583 +* [Windows] - Resolved FlyoutBehavior "Locked" State Reset Issue After Navigation by @prakashKannanSf3972 in https://github.com/dotnet/maui/pull/27379 +* Fixed the vertical orientation issue in the CarouselViewHandler2 on iOS by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27273 +* [Android] Fixed the CarouselView Items overlap issue with PeekAreaInsets by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27499 +* Fixed CollectionView's HeaderTemplate is not rendering in iOS and MacCatalyst platform. by @KarthikRajaKalaimani in https://github.com/dotnet/maui/pull/27466 +* Obsolete iOS Compatibility AccessibilityExtensions by @jfversluis in https://github.com/dotnet/maui/pull/27593 +* Make HybridWebView.InvokeJavaScriptAsync public by @jfversluis in https://github.com/dotnet/maui/pull/27594 +* Replace Android ToSpannableString overload by @jfversluis in https://github.com/dotnet/maui/pull/27597 +* [net10.0] Set `UseRidGraph=false` on Windows by @MartyIX in https://github.com/dotnet/maui/pull/27595 +* Reapply "Implementation of Customizable Search Button Color for Search Across Platforms (#26759)" by @jfversluis in https://github.com/dotnet/maui/pull/27586 +* [Android] Fix for Flyout closing when updating the FlyoutPage.Detail by @Tamilarasan-Paranthaman in https://github.com/dotnet/maui/pull/26425 +* Fix for [Windows]ToolbarItem visibility until Page Disappearing by @SuthiYuvaraj in https://github.com/dotnet/maui/pull/26915 +* [net10.0] Set `UseRidGraph=false` on Windows (2) by @MartyIX in https://github.com/dotnet/maui/pull/27634 +* Fix for MenuFlyoutItem stops working after navigating away from and back to page by @BagavathiPerumal in https://github.com/dotnet/maui/pull/25170 +* Fix Issue13551 to use WaitForElement by @PureWeen in https://github.com/dotnet/maui/pull/27644 +* [Android] Fix Flickering issue when calling Navigation.PopAsync by @devanathan-vaithiyanathan in https://github.com/dotnet/maui/pull/24887 +* Fix GC Race condition with tests by @PureWeen in https://github.com/dotnet/maui/pull/27652 +* Fix 19647 by @StephaneDelcroix in https://github.com/dotnet/maui/pull/20127 +* [net10.0] Make CV2 default for net10 by @rmarinho in https://github.com/dotnet/maui/pull/27567 +* Timeout Android emulator start by @PureWeen in https://github.com/dotnet/maui/pull/27657 +* [net10] Revert Windows RID graph changes by @jfversluis in https://github.com/dotnet/maui/pull/27671 +* BindableLayout should disconnect handlers by @albyrock87 in https://github.com/dotnet/maui/pull/27450 +* Improve shadow rendering on Android, fix shadow clipping on iOS by @albyrock87 in https://github.com/dotnet/maui/pull/26789 +* [X] deprecate fontImageExtension by @StephaneDelcroix in https://github.com/dotnet/maui/pull/23657 +* 26598 - Fix for Tabbar disappears when navigating back from page with hidden TabBar in iOS 18 by @SuthiYuvaraj in https://github.com/dotnet/maui/pull/27582 +* Avoid compiler error when using init properties with BindingSourceGenerator by @rabuckley in https://github.com/dotnet/maui/pull/27655 +* Make iOS WebView delegates virtual by @jfversluis in https://github.com/dotnet/maui/pull/27601 +* Improve TextToSpeech function by adding a speech rate parameter by @Zerod159 in https://github.com/dotnet/maui/pull/24798 +* Adds very basic CSS support for Border by @sthewissen in https://github.com/dotnet/maui/pull/27529 +* [iOS] Added PermissionStatus.Limited for Contacts by @kubaflo in https://github.com/dotnet/maui/pull/27694 +* Make some internal methods public by @jfversluis in https://github.com/dotnet/maui/pull/27598 +* Add support for iOS/Mac specific modals styled as popovers by @piersdeseilligny in https://github.com/dotnet/maui/pull/23984 +* Adds CSS support for shadows and a simpler way of defining shadows in XAML by @sthewissen in https://github.com/dotnet/maui/pull/27180 +* Fix the Collection view empty view not fill the vertical space by @Shalini-Ashokan in https://github.com/dotnet/maui/pull/27464 +* [Windows] Fix for SearchHandler.Focused and Unfocused event never fires by @BagavathiPerumal in https://github.com/dotnet/maui/pull/27577 +* [Android] Fix Cursor Not Closing in File Picker to Prevent Log Spam. by @bhavanesh2001 in https://github.com/dotnet/maui/pull/27718 +* [Android] Fixed the SoftInputMode issues with modal pages by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27553 +* [iOS] Fix Gray Line Appears on the Right Side of GraphicsView with Decimal WidthRequest by @devanathan-vaithiyanathan in https://github.com/dotnet/maui/pull/26368 +* Page cannot scroll to the bottom while using RoundRectangle by @Dhivya-SF4094 in https://github.com/dotnet/maui/pull/27451 +* Support for Setting Switch Off State Color by @devanathan-vaithiyanathan in https://github.com/dotnet/maui/pull/25068 +* [iOS] Fix ShellContent Title Does Not Update at Runtime by @devanathan-vaithiyanathan in https://github.com/dotnet/maui/pull/26062 +* Fixed [iOS] Navigation breaks when modal pages use PageSheet by @NanthiniMahalingam in https://github.com/dotnet/maui/pull/27765 +* BarBackground with Brush in TabbedPage on theme change by @kubaflo in https://github.com/dotnet/maui/pull/24425 +* [iOS] Using long-press navigation on back button with shell pages - fix by @kubaflo in https://github.com/dotnet/maui/pull/24003 +* Fix for DatePicker displays incorrect date selection when navigating to next month. by @BagavathiPerumal in https://github.com/dotnet/maui/pull/26064 +* Fixed Unnecessary SizeChanged Event Triggering by @Dhivya-SF4094 in https://github.com/dotnet/maui/pull/27476 +* [Essentials] Longitude Validation by @kubaflo in https://github.com/dotnet/maui/pull/27784 +* Fixed latitude->longitude typo by @kubaflo in https://github.com/dotnet/maui/pull/27834 +* [iOS] CollectionView with header or footer has incorrect height - fix by @kubaflo in https://github.com/dotnet/maui/pull/27809 +* Fix concurrency issues and leak reliability by @PureWeen in https://github.com/dotnet/maui/pull/27815 +* [iOS] Fixed the Application crash when ToolbarItem is created with invalid IconImageSource name by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27175 +* Revert "Fix concurrency issues and leak reliability" by @rmarinho in https://github.com/dotnet/maui/pull/27870 +* [Android] Fix app crash caused by dynamic template switching in ListView by @BagavathiPerumal in https://github.com/dotnet/maui/pull/24808 +* Don't need to register ApplicationStub by @PureWeen in https://github.com/dotnet/maui/pull/27885 +* Fixed Toolbar IconImageSource not updating with Binding Changes by @NirmalKumarYuvaraj in https://github.com/dotnet/maui/pull/27402 +* [iOS] Fixed a crash in CarouselViewHandler2 on iOS 15. by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27871 +* [Android] Android: Native View not set exception on modal page - fix by @kubaflo in https://github.com/dotnet/maui/pull/27891 +* [iOS/MacCatalyst] Use newer API in FilePicker by @MartyIX in https://github.com/dotnet/maui/pull/27521 +* [Android] Map FlowDirection of shell to PlatformView on Android by @mohsenbgi in https://github.com/dotnet/maui/pull/23473 +* Fix CSS Hot Reload - Handle fingerprint when hot reloading scoped CSS bundles by @spadapet in https://github.com/dotnet/maui/pull/27788 +* [iOS] CV1's footer doesn't increase its size - fix by @kubaflo in https://github.com/dotnet/maui/pull/27979 +* [iOS] CollectionView 1's doesn't adjust its offset when resizing a footer by @kubaflo in https://github.com/dotnet/maui/pull/27963 +* [Android] Properly Resolve File Paths in FilePicker When MANAGE_EXTERNAL_STORAGE is Granted by @bhavanesh2001 in https://github.com/dotnet/maui/pull/27975 +* [net10.0] Set `UseRidGraph=false` on Windows (attempt 2) by @MartyIX in https://github.com/dotnet/maui/pull/27679 +* [Android] Fix crash starting the swipe on SwipeView inside CollectionView by @jsuarezruiz in https://github.com/dotnet/maui/pull/27669 +* Applying visibility change to child controls by @kubaflo in https://github.com/dotnet/maui/pull/20154 +* Fixed CheckBox enabled color is not updated properly by @NanthiniMahalingam in https://github.com/dotnet/maui/pull/26399 +* [android] move `IsDispatchRequiredImplementation()` to Java by @jonathanpeppers in https://github.com/dotnet/maui/pull/27936 +* remove Dispose call on ShellItemRenderer by @pictos in https://github.com/dotnet/maui/pull/27890 +* [iOS] CollectionView with grouped data crashes on iOS when the groups change - fix by @kubaflo in https://github.com/dotnet/maui/pull/27991 +* [BindingSG] Added Binding.Create support for xaml generated sources by @jkurdek in https://github.com/dotnet/maui/pull/27610 +* Make ShadowTypeConverter public & nullable for .NET 10 by @jfversluis in https://github.com/dotnet/maui/pull/27984 +* add DebuggerTypeProxy for Shell by @pictos in https://github.com/dotnet/maui/pull/27989 +* [Windows] Fixed NRE when clearing ListView after navigating back by @SubhikshaSf4851 in https://github.com/dotnet/maui/pull/27274 +* [Windows] Fix for issues caused by setting Shell.FlyoutWidth on WinUI when binding context values are changed by @Tamilarasan-Paranthaman in https://github.com/dotnet/maui/pull/27151 +* [MacCatalyst] Picker focus events by @kubaflo in https://github.com/dotnet/maui/pull/27973 +* [Android] Fixed the ScrollbarVisibility issues by @Ahamed-Ali in https://github.com/dotnet/maui/pull/27613 +* Make more internal methods public by @jsuarezruiz in https://github.com/dotnet/maui/pull/28059 +* Fixed FontImageSource icon color does not change in the TabbedPage when dynamically updated. by @NirmalKumarYuvaraj in https://github.com/dotnet/maui/pull/27742 +* [ci] Remove macios workaround to build net9 by @rmarinho in https://github.com/dotnet/maui/pull/28363 + + +### Tests + +* [Testing] Fix for MacCatalyst flaky tests in CI which fails due window position below the dock layer by @anandhan-rajagopal in https://github.com/dotnet/maui/pull/27279 +* Revert "Run every category separately" by @rmarinho in https://github.com/dotnet/maui/pull/27469 +* [Testing] UITest to measure layout passes on a common scenario by @albyrock87 in https://github.com/dotnet/maui/pull/25671 +* [Testing] Enabling some UITests from Issues folder in Appium-13 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27257 +* [Testing] Fix flaky UITests failing sometimes 3 by @jsuarezruiz in https://github.com/dotnet/maui/pull/27277 +* [testing] Disable BlazorWebview tests by @rmarinho in https://github.com/dotnet/maui/pull/27557 +* Fix UITest screenshot taking on MacCatalyst by @albyrock87 in https://github.com/dotnet/maui/pull/27531 +* [Testing] Fix flaky test 4 by @jsuarezruiz in https://github.com/dotnet/maui/pull/27607 +* [testing] Update tests demands by @rmarinho in https://github.com/dotnet/maui/pull/27560 +* [Testing] Implement Appium swipe action on Catalyst using the Mac Driver by @jsuarezruiz in https://github.com/dotnet/maui/pull/27441 +* [Testing] Implement PressEnter Appium action on Windows by @jsuarezruiz in https://github.com/dotnet/maui/pull/27602 +* [Testing] Implement ContextMenu UITest extension methods by @jsuarezruiz in https://github.com/dotnet/maui/pull/26204 +* [Testing] Enabling more UI Tests by removing platform specific condition - 6 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27581 +* [tests] Run tests on x64 only for devices controls by @rmarinho in https://github.com/dotnet/maui/pull/27714 +* [Testing, CI] Increased threshold value to make Resizetizer unit tests pass on arm64 machines by @anandhan-rajagopal in https://github.com/dotnet/maui/pull/27684 +* [Testing] Enabling more UI Tests by removing platform specific condition - 7 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27639 +* [Testing] Implement TapCoordinates Appium action on macOS by @jsuarezruiz in https://github.com/dotnet/maui/pull/27603 +* [Testing] Enabling more UI Tests by removing platform specific condition - 8 by @nivetha-nagalingam in https://github.com/dotnet/maui/pull/27681 +* [Testing] Enabling more UI Tests by removing platform specific condition - 3 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27501 +* [Testing] Fix DragCoordinates Appium action on Mac by @jsuarezruiz in https://github.com/dotnet/maui/pull/27339 +* [Testing] Enabling WebView UITests from Issues folder in Appium by @NafeelaNazhir in https://github.com/dotnet/maui/pull/27284 +* [Testing] Fix flaky test 5 by @jsuarezruiz in https://github.com/dotnet/maui/pull/27733 +* [Testing] Enabling more UI Tests by removing platform specific condition - 1 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27454 +* Add missing screen shot for Issue25502 on MAC by @PureWeen in https://github.com/dotnet/maui/pull/27813 +* [Testing] Enabling ContextMenu UITests from Xamarin.UITests to Appium by @NafeelaNazhir in https://github.com/dotnet/maui/pull/27403 +* Mark VerifyInitialEntryReturnTypeChange and VerifyGraphicsViewWithoutGrayLine tests as flaky by @jfversluis in https://github.com/dotnet/maui/pull/27776 +* [Testing] Fix for flaky UITests in CI that occasionally fail - 2 by @nivetha-nagalingam in https://github.com/dotnet/maui/pull/27878 +* [Testing] Enabling more UI Tests by removing platform specific condition - 5 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27564 +* Update Appium Versions by @PureWeen in https://github.com/dotnet/maui/pull/27933 +* Revert "Update Appium Versions" by @PureWeen in https://github.com/dotnet/maui/pull/27938 +* [Android] Testcase for Shell FlowDirection issue by @Vignesh-SF3580 in https://github.com/dotnet/maui/pull/27931 +* [Testing] Run tests verifying snapshots on mac now that's possible by @jsuarezruiz in https://github.com/dotnet/maui/pull/27893 +* [Testing] More changes in capabilities to adjust Appium timeouts by @jsuarezruiz in https://github.com/dotnet/maui/pull/27675 +* [Testing] Migration of Compatibility.Core platform-specific unit tests into device tests - 1 by @anandhan-rajagopal in https://github.com/dotnet/maui/pull/27695 +* [Testing] Fix flaky tests 6 by @jsuarezruiz in https://github.com/dotnet/maui/pull/27874 +* [Testing] Enabling more UI Tests by removing platform specific condition - 12 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27804 +* [Testing] Enabling more UI Tests by removing platform specific condition - 13 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27904 +* [Testing] Enabling more UI Tests by removing platform specific condition - 4 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27561 +* [Testing] Enable the Issue417 test on iOS and Catalyst by @kubaflo in https://github.com/dotnet/maui/pull/27987 +* [Testing] Enabling more UI Tests by removing platform specific condition - 2 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27500 +* [Testing] Enabling more UI Tests by removing platform specific condition - 14 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27906 +* Fix Android device tests on .NET 10 by @jfversluis in https://github.com/dotnet/maui/pull/28009 +* [Testing] Enabling ContextMenu UITests from Xamarin.UITests to Appium - 2 by @nivetha-nagalingam in https://github.com/dotnet/maui/pull/27405 +* [Testing] Enabling more UI Tests by removing platform specific condition - 9 by @HarishKumarSF4517 in https://github.com/dotnet/maui/pull/27743 +* [Testing] Enabling more UI Tests by removing platform specific condition - 10 by @nivetha-nagalingam in https://github.com/dotnet/maui/pull/27751 +* [Testing] Fix for flaky UITests in CI that occasionally fail - 3 by @NafeelaNazhir in https://github.com/dotnet/maui/pull/27905 +* [Testing] Fix for flaky UITests in CI that occasionally fail. by @nivetha-nagalingam in https://github.com/dotnet/maui/pull/27453 +* [Testing] Enabling more UI Tests by removing platform specific condition - 11 by @LogishaSelvarajSF4525 in https://github.com/dotnet/maui/pull/27764 +* [Testing] Feature Matrix UITest Cases for Slider Control by @NafeelaNazhir in https://github.com/dotnet/maui/pull/27433 +* [Testing] Fix flaky UITests 7 by @jsuarezruiz in https://github.com/dotnet/maui/pull/28000 +* [Testing] Implement the option to change system theme on Appium by @jsuarezruiz in https://github.com/dotnet/maui/pull/28025 +* [Testing] Resolved Shell TabBar DeviceTests CI failures on macOS by @anandhan-rajagopal in https://github.com/dotnet/maui/pull/28072 +* [release/10.0.1xx-preview2] [net10.0] Move iOS 18.0 simulators by @github-actions in https://github.com/dotnet/maui/pull/28277 +* [release/10.0.1xx-preview2] [ci] Fix platform for UItests for iOS by @github-actions in https://github.com/dotnet/maui/pull/28345 + + +### Dependency Updates + +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27510 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27540 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27570 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27587 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27615 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27643 +* [main] Update dependencies from dotnet/xharness by @dotnet-maestro in https://github.com/dotnet/maui/pull/27662 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27687 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27719 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27725 +* [Windows] Upgrade Windows App SDK from 1.6.4 to 1.6.5 by @MartyIX in https://github.com/dotnet/maui/pull/27729 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27758 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27791 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27810 +* [main] Update dependencies from dotnet/xharness by @dotnet-maestro in https://github.com/dotnet/maui/pull/27835 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27887 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27902 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27937 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/27960 +* [net10.0] Update aspnet as sdk, enable Blazor tests by @rmarinho in https://github.com/dotnet/maui/pull/27796 +* [net10.0] Update dependencies from dotnet/android by @dotnet-maestro in https://github.com/dotnet/maui/pull/28019 +* [release/10.0.1xx-preview2] Update with final preview2 versions by @rmarinho in https://github.com/dotnet/maui/pull/28231 +* [release/10.0.1xx-preview2] Update preview2 ios android sdk by @rmarinho in https://github.com/dotnet/maui/pull/28287 +* [release/10.0.1xx-preview2] Update sdk and runtime by @rmarinho in https://github.com/dotnet/maui/pull/28307 +* [release/10.0.1xx-preview2] Update dependencies from dotnet/sdk by @dotnet-maestro in https://github.com/dotnet/maui/pull/28329 +* [release/10.0.1xx-preview2] Update dependencies from dotnet/sdk by @dotnet-maestro in https://github.com/dotnet/maui/pull/28390 + + +### Housekeeping + +* [ci] Remove references to sdk-insertions by @pjcollins in https://github.com/dotnet/maui/pull/27480 +* [ci] Fix yaml by @rmarinho in https://github.com/dotnet/maui/pull/27497 +* Fix color checking from blocking and add logging by @PureWeen in https://github.com/dotnet/maui/pull/27400 +* [Localization] Simply Logic for Localization Handoff & Handback by @tj-devel709 in https://github.com/dotnet/maui/pull/27508 +* [ci] Run device tests in any machine by @rmarinho in https://github.com/dotnet/maui/pull/27518 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27504 +* [net10.0] Update net10.0 with main by @rmarinho in https://github.com/dotnet/maui/pull/27539 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27547 +* Removes setting up the JDK in pipelines by @jfversluis in https://github.com/dotnet/maui/pull/27396 +* [Localization] Fix blocking typo by @tj-devel709 in https://github.com/dotnet/maui/pull/27566 +* Localized file check-in by OneLocBuild Task: Build definition ID 13330: Build ID 10953111 by @dotnet-bot in https://github.com/dotnet/maui/pull/27569 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27571 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27621 +* [ci] Update variables for signing and not used by @rmarinho in https://github.com/dotnet/maui/pull/27640 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27645 +* Update android.cake cmdline paths by @PureWeen in https://github.com/dotnet/maui/pull/27686 +* Update Versions.props to .NET 9 SR5 Branding by @PureWeen in https://github.com/dotnet/maui/pull/27691 +* Update comments by @APoukar in https://github.com/dotnet/maui/pull/27658 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27693 +* Update bug-report.yml with 9.0.40 by @PureWeen in https://github.com/dotnet/maui/pull/27723 +* [net10.0] Merge main to net10.0 by @rmarinho in https://github.com/dotnet/maui/pull/27720 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27726 +* Move Microsoft.Maui.Packages.slnf to eng folder by @jfversluis in https://github.com/dotnet/maui/pull/27032 +* Move Microsoft.Maui.Samples.slnf to eng folder by @jfversluis in https://github.com/dotnet/maui/pull/27033 +* Make auto applied labels more relevant for Syncfusion partner team by @jfversluis in https://github.com/dotnet/maui/pull/27295 +* Move Microsoft.Maui.Graphics.slnf to subfolder (clean up repo root) by @jfversluis in https://github.com/dotnet/maui/pull/27035 +* [ci] Builds should only take 2h max by @rmarinho in https://github.com/dotnet/maui/pull/27747 +* Add ISO information to Locale API documentation by @jfversluis in https://github.com/dotnet/maui/pull/27746 +* Delete maui.code-workspace (clean up repo root by @jfversluis in https://github.com/dotnet/maui/pull/27767 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27757 +* [Localization] Add Localization tests and other fixes by @tj-devel709 in https://github.com/dotnet/maui/pull/25620 +* [ci] Update sdk, aspnet and runtime by @rmarinho in https://github.com/dotnet/maui/pull/27790 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27816 +* [ci] Fix dnceng builds by @rmarinho in https://github.com/dotnet/maui/pull/27855 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27863 +* [ci] Update slnf location and arcade by @rmarinho in https://github.com/dotnet/maui/pull/27860 +* Localized file check-in by OneLocBuild Task: Build definition ID 13330: Build ID 11029468 by @dotnet-bot in https://github.com/dotnet/maui/pull/27789 +* [ci] Move to Sequoia machines by @rmarinho in https://github.com/dotnet/maui/pull/27787 +* Update DEVELOPMENT.md with net10.0 by @PureWeen in https://github.com/dotnet/maui/pull/27913 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27918 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27942 +* [ci] Remove conditions for devdiv by @rmarinho in https://github.com/dotnet/maui/pull/27958 +* [net10.0] Merge main to net10 by @rmarinho in https://github.com/dotnet/maui/pull/27967 +* [ci] Move more runs to Sequoia by @rmarinho in https://github.com/dotnet/maui/pull/27972 +* LEGO: Pull request from lego/hb_7241b85a-f216-4d55-a9fa-d8030c736df5_20250219211456120 to main by @csigs in https://github.com/dotnet/maui/pull/27915 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/27986 +* Localized file check-in by OneLocBuild Task: Build definition ID 13330: Build ID 11064473 by @dotnet-bot in https://github.com/dotnet/maui/pull/27982 +* Enable generation of API docs in CI for .NET 10 by @jfversluis in https://github.com/dotnet/maui/pull/28004 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/28021 +* [ci] Publish workload VS insertion zips by @pjcollins in https://github.com/dotnet/maui/pull/28016 +* Update DEVELOPMENT.md to clarify .NET SDK version needed by @jfversluis in https://github.com/dotnet/maui/pull/28031 +* [ci] Remove usage on cake script by @rmarinho in https://github.com/dotnet/maui/pull/28037 +* [ci] Update autoformat prs version by @rmarinho in https://github.com/dotnet/maui/pull/28042 +* [ci] Last move sequoia by @rmarinho in https://github.com/dotnet/maui/pull/28029 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/28045 +* [ci] Run provisionator devdiv by @rmarinho in https://github.com/dotnet/maui/pull/28056 +* Revert "[ci] Run provisionator devdiv" by @rmarinho in https://github.com/dotnet/maui/pull/28079 +* [housekeeping] Automated PR to fix formatting errors by @github-actions in https://github.com/dotnet/maui/pull/28068 +* [code style] Prefer file-scope namespaces by @MartyIX in https://github.com/dotnet/maui/pull/28040 +* [net10.0] Merge main to net10.0 by @rmarinho in https://github.com/dotnet/maui/pull/28044 +* [iOS] Ignore for now obsoletes on iOS by @rmarinho in https://github.com/dotnet/maui/pull/28136 + + +## New Contributors +* @Ahamed-Ali made their first contribution in https://github.com/dotnet/maui/pull/27273 +* @KarthikRajaKalaimani made their first contribution in https://github.com/dotnet/maui/pull/27466 +* @rabuckley made their first contribution in https://github.com/dotnet/maui/pull/27655 +* @Zerod159 made their first contribution in https://github.com/dotnet/maui/pull/24798 +* @sthewissen made their first contribution in https://github.com/dotnet/maui/pull/27529 +* @APoukar made their first contribution in https://github.com/dotnet/maui/pull/27658 +* @piersdeseilligny made their first contribution in https://github.com/dotnet/maui/pull/23984 +* @Shalini-Ashokan made their first contribution in https://github.com/dotnet/maui/pull/27464 +* @bhavanesh2001 made their first contribution in https://github.com/dotnet/maui/pull/27718 +* @Dhivya-SF4094 made their first contribution in https://github.com/dotnet/maui/pull/27451 +* @mohsenbgi made their first contribution in https://github.com/dotnet/maui/pull/23473 + +**Full Changelog**: https://github.com/dotnet/maui/compare/{branch}..{previous branch} \ No newline at end of file diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 000000000000..0273b732a393 --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,26 @@ +{ + "inputs": [ + { + "type": "promptString", + "id": "github-key", + "password": true, // Encrypted at-rest + "description": "GitHub PAT" + } + ], + "servers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github-key}" + } + } + } +} \ No newline at end of file diff --git a/NuGet.config b/NuGet.config index 58d274efbdd7..3ef655cf3880 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,7 +6,6 @@ - diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 60ddda22533c..7d858414240f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/sdk - 90a1f93877806fe281973ec619c3e30fbbbbc3dc + a081488f32ee97b730ac15197a1b0044a89a3995 https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index 5fd9d3279199..ed3671d93dba 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -29,7 +29,7 @@ 9.0.14 - 10.0.100-preview.4.25210.19 + 10.0.100-preview.4.25211.22 $(MicrosoftNETSdkPackageVersion) 10.0.0-preview.4.25209.13 diff --git a/eng/pipelines/common/ui-tests-steps.yml b/eng/pipelines/common/ui-tests-steps.yml index aeeae13db741..7fca26aeff51 100644 --- a/eng/pipelines/common/ui-tests-steps.yml +++ b/eng/pipelines/common/ui-tests-steps.yml @@ -108,11 +108,11 @@ steps: displayName: "Install node" - pwsh: | - $skipAppiumDoctor = if ($IsMacOS) { "true" } else { "false" } + $skipAppiumDoctor = if ($IsMacOS -or $IsLinux) { "true" } else { "false" } dotnet build ./src/Provisioning/Provisioning.csproj -t:ProvisionAppium -p:SkipAppiumDoctor="$skipAppiumDoctor" -bl:"$(LogDirectory)/provision-appium.binlog" displayName: "Install Appium" continueOnError: false - retryCountOnTaskFailure: 1 + retryCountOnTaskFailure: 2 timeoutInMinutes: 10 env: APPIUM_HOME: $(APPIUM_HOME) diff --git a/eng/pipelines/maui-release.yml b/eng/pipelines/maui-release.yml index c011b4a69746..30b3cfa7c774 100644 --- a/eng/pipelines/maui-release.yml +++ b/eng/pipelines/maui-release.yml @@ -27,6 +27,7 @@ schedules: branches: include: - main + - inflight/current variables: - template: /eng/pipelines/common/variables.yml@self diff --git a/maui.code-workspace b/maui.code-workspace new file mode 100644 index 000000000000..ccd94e18a897 --- /dev/null +++ b/maui.code-workspace @@ -0,0 +1,10 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "dotnet.defaultSolution": "Microsoft.Maui-vscode.sln" + } +} \ No newline at end of file diff --git a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs index 537fbb0698d3..a51cf6a2c0a9 100644 --- a/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs +++ b/src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs @@ -1410,6 +1410,25 @@ static IEnumerable SetBinding(VariableDefinition parent, FieldRefer static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLineInfo iXmlLineInfo, ILContext context) { + static bool CanSetValue (TypeReference bpTypeRef, VariableDefinition varValue, ILContext context, IXmlLineInfo iXmlLineInfo) + { + // If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here. + // Worst case scenario ? InvalidCastException at runtime + if (varValue.VariableType.FullName == "System.Object") + return true; + var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(context.Cache, bpTypeRef, context.Body.Method.Module); + if (implicitOperator != null) + return true; + + //as we're in the SetValue Scenario, we can accept value types, they'll be boxed + if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object") + return true; + + if (varValue.VariableType.InheritsFromOrImplements(context.Cache, bpTypeRef) || varValue.VariableType.FullName == "System.Object") + return true; + return false; + } + var module = context.Body.Method.Module; if (bpRef == null) @@ -1424,22 +1443,23 @@ static bool CanSetValue(FieldReference bpRef, bool attached, INode node, IXmlLin if (!context.Variables.TryGetValue(elementNode, out VariableDefinition varValue)) return false; + var bpTypeRef = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module); - // If it's an attached BP, there's no second chance to handle IMarkupExtensions, so we try here. - // Worst case scenario ? InvalidCastException at runtime - if (varValue.VariableType.FullName == "System.Object") - return true; - var implicitOperator = varValue.VariableType.GetImplicitOperatorTo(context.Cache, bpTypeRef, module); - if (implicitOperator != null) - return true; - //as we're in the SetValue Scenario, we can accept value types, they'll be boxed - if (varValue.VariableType.IsValueType && bpTypeRef.FullName == "System.Object") + if (CanSetValue(bpTypeRef, varValue, context, iXmlLineInfo)) return true; - return varValue.VariableType.InheritsFromOrImplements(context.Cache, bpTypeRef) || varValue.VariableType.FullName == "System.Object"; + if (bpTypeRef.ResolveCached(context.Cache).FullName == "System.Nullable`1") + { + bpTypeRef = ((GenericInstanceType)bpTypeRef).GenericArguments[0]; + if (CanSetValue(bpTypeRef, varValue, context, iXmlLineInfo)) + return true; + } + + return false; } + static bool CanGetValue(VariableDefinition parent, FieldReference bpRef, bool attached, IXmlLineInfo iXmlLineInfo, ILContext context, out TypeReference propertyType) { var module = context.Body.Method.Module; @@ -1483,32 +1503,37 @@ static IEnumerable SetValue(VariableDefinition parent, FieldReferen var @else = Create(OpCodes.Nop); var endif = Create(OpCodes.Nop); - if (context.Variables[elementNode].VariableType.FullName == "System.Object") + + var varValue = context.Variables[elementNode]; + if (varValue.VariableType.FullName == "System.Object") { //if(value != null && value.GetType().IsAssignableFrom(typeof(BindingBase))) - yield return Create(Ldloc, context.Variables[elementNode]); + yield return Create(Ldloc, varValue); yield return Create(Brfalse, @else); - yield return Create(Ldtoken, module.ImportReference(context.Cache, ("Microsoft.Maui.Controls", "Microsoft.Maui.Controls", "BindingBase"))); yield return Create(Call, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "GetTypeFromHandle", parameterTypes: new[] { ("mscorlib", "System", "RuntimeTypeHandle") }, isStatic: true)); - yield return Create(Ldloc, context.Variables[elementNode]); + yield return Create(Ldloc, varValue); yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Object"), methodName: "GetType", paramCount: 0)); yield return Create(Callvirt, module.ImportMethodReference(context.Cache, ("mscorlib", "System", "Type"), methodName: "IsAssignableFrom", parameterTypes: new[] { ("mscorlib", "System", "Type") })); yield return Create(Brfalse, @else); //then - yield return Create(Ldloc, context.Variables[elementNode]); + yield return Create(Ldloc, varValue); yield return Create(Br, endif); //else yield return @else; } var bpTypeRef = bpRef.GetBindablePropertyType(context.Cache, iXmlLineInfo, module); - foreach (var instruction in context.Variables[elementNode].LoadAs(context.Cache, bpTypeRef, module)) + foreach (var instruction in varValue.LoadAs(context.Cache, bpTypeRef, module)) yield return instruction; if (bpTypeRef.IsValueType) + { + if ( bpTypeRef.ResolveCached(context.Cache).FullName == "System.Nullable`1" + && TypeRefComparer.Default.Equals(varValue.VariableType, ((GenericInstanceType)bpTypeRef).GenericArguments[0])) + bpTypeRef = ((GenericInstanceType)bpTypeRef).GenericArguments[0]; yield return Create(Box, module.ImportReference(bpTypeRef)); - + } //endif - if (context.Variables[elementNode].VariableType.FullName == "System.Object") + if (varValue.VariableType.FullName == "System.Object") yield return endif; } diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/SearchHandlerAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/SearchHandlerAppearanceTracker.cs index 6b21640b0ca2..52fadb15e8fe 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/SearchHandlerAppearanceTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/SearchHandlerAppearanceTracker.cs @@ -54,6 +54,10 @@ protected virtual void SearchHandlerPropertyChanged(object sender, System.Compon { UpdateTextTransform(); } + else if (e.Is(SearchHandler.PlaceholderProperty)) + { + UpdatePlaceholder(); + } else if (e.Is(SearchHandler.PlaceholderColorProperty)) { UpdatePlaceholderColor(); @@ -82,6 +86,28 @@ protected virtual void SearchHandlerPropertyChanged(object sender, System.Compon { UpdateAutomationId(); } + else if (e.Is(SearchHandler.QueryProperty)) + { + UpdateText(); + } + } + + void UpdateText() + { + int cursorPosition = _editText.SelectionStart; + bool selectionExists = _editText.HasSelection; + + _editText.Text = _searchHandler.Query ?? string.Empty; + + UpdateTextTransform(); + + // If we had a selection, place the cursor at the end of text + // Otherwise try to maintain the cursor at its previous position + int textLength = _editText.Text?.Length ?? 0; + int newPosition = selectionExists ? textLength : Math.Min(cursorPosition, textLength); + + // Prevents the cursor from resetting to position zero when text is set programmatically + _editText.SetSelection(newPosition); } void EditTextFocusChange(object s, AView.FocusChangeEventArgs args) @@ -115,6 +141,11 @@ void UpdateFont() _editText.SetTextSize(ComplexUnitType.Sp, (float)_searchHandler.FontSize); } + void UpdatePlaceholder() + { + _editText.Hint = _searchHandler.Placeholder; + } + void UpdatePlaceholderColor() { _editText.UpdatePlaceholderColor(_searchHandler.PlaceholderColor); diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs index ecea8c950e70..7d8e11076e63 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cs @@ -115,6 +115,19 @@ void SearchHandlerPropertyChanged(object sender, System.ComponentModel.PropertyC { UpdateSearchBarVerticalTextAlignment(_uiSearchBar.FindDescendantView()); } + else if (e.Is(SearchHandler.QueryProperty)) + { + UpdateText(_uiSearchBar.FindDescendantView()); + } + } + + void UpdateText(UITextField uiTextField) + { + if (uiTextField is null) + return; + + uiTextField.Text = _searchHandler.Query; + UpdateTextTransform(uiTextField); } void GetDefaultSearchBarColors(UISearchBar searchBar) diff --git a/src/Controls/src/Core/Compatibility/Handlers/iOS/VisualElementRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/iOS/VisualElementRenderer.cs index df68dfc77367..f1188b1bc7ef 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/iOS/VisualElementRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/iOS/VisualElementRenderer.cs @@ -99,7 +99,11 @@ void IPlatformMeasureInvalidationController.InvalidateAncestorsMeasuresWhenMoved _invalidateParentWhenMovedToWindow = true; } - void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) => SetNeedsLayout(); + bool IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) + { + SetNeedsLayout(); + return true; + } public override void MovedToWindow() { diff --git a/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs index ca07824e4a11..afa9ea62f139 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs @@ -32,6 +32,20 @@ public class CarouselViewController : ItemsViewController bool _isRotating; + public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection) + { + if (previousTraitCollection.VerticalSizeClass == TraitCollection.VerticalSizeClass) + { + return; + } + + if (ItemsView?.Loop == false || _carouselViewLoopManager is null) + { + CollectionView.ReloadData(); + InitialPositionSet = false; + } + } + public CarouselViewController(CarouselView itemsView, ItemsViewLayout layout) : base(itemsView, layout) { CollectionView.AllowsSelection = false; diff --git a/src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs b/src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs index 8aa58993ea85..ee8ceb04c568 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs @@ -28,7 +28,7 @@ void IPlatformMeasureInvalidationController.InvalidateAncestorsMeasuresWhenMoved _invalidateParentWhenMovedToWindow = true; } - void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) + bool IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) { if (isPropagating) { @@ -36,6 +36,7 @@ void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating } SetNeedsLayout(); + return !isPropagating; } [UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)] diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewController.cs index 8878d36194b6..63dc064fd595 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewController.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewController.cs @@ -21,6 +21,11 @@ public ReorderableItemsViewController(TItemsView reorderableItemsView, ItemsView // For some reason it only seemed to work when the CollectionView was inside the Flyout section of a FlyoutPage. // The UILongPressGestureRecognizer is simple enough to set up so let's just add our own. InstallsStandardGestureForInteractiveMovement = false; +#if MACCATALYST + // On Mac Catalyst, the default normal press and drag interactions occur, causing the CanMixGroups = false logic to not work. + // Since all reordering logic is handled exclusively by UILongPressGestureRecognizer, we can set DragInteractionEnabled to false, ensuring that only the long press gesture is used. + CollectionView.DragInteractionEnabled = false; +#endif } public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath) diff --git a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs index d3c238ec10e4..b0a15d4f3562 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs @@ -306,14 +306,17 @@ public override bool Selected protected abstract (bool, Size) NeedsContentSizeUpdate(Size currentSize); - void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) + bool IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) { // If the cell is not bound (or getting unbounded), we don't want to measure it // and cause a useless and harming InvalidateLayout on the collection view layout if (!_measureInvalidated && _bound) { _measureInvalidated = true; + return true; } + + return false; } protected void OnContentSizeChanged() diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewController2.cs index 12e33e65ef62..2114d4eecc35 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewController2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewController2.cs @@ -21,6 +21,11 @@ public ReorderableItemsViewController2(TItemsView reorderableItemsView, UICollec // For some reason it only seemed to work when the CollectionView was inside the Flyout section of a FlyoutPage. // The UILongPressGestureRecognizer is simple enough to set up so let's just add our own. InstallsStandardGestureForInteractiveMovement = false; +#if MACCATALYST + // On Mac Catalyst, the default normal press and drag interactions occur, causing the CanMixGroups = false logic to not work. + // Since all reordering logic is handled exclusively by UILongPressGestureRecognizer, we can set DragInteractionEnabled to false, ensuring that only the long press gesture is used. + CollectionView.DragInteractionEnabled = false; +#endif } public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath) diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs index 16ba34065d9d..c78e60d4a604 100644 --- a/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs +++ b/src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs @@ -306,14 +306,17 @@ void IPlatformMeasureInvalidationController.InvalidateAncestorsMeasuresWhenMoved // This is a no-op } - void IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) + bool IPlatformMeasureInvalidationController.InvalidateMeasure(bool isPropagating) { // If the cell is not bound (or getting unbounded), we don't want to measure it // and cause a useless and harming InvalidateLayout on the collection view layout if (!_measureInvalidated && _bound) { _measureInvalidated = true; + return true; } + + return false; } } } diff --git a/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs b/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs index e6cc1113cb59..cb679d4fc62c 100644 --- a/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs +++ b/src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs @@ -180,7 +180,11 @@ void UpdateBackButton() // Set this before BackButtonVisible triggers an update to the handler // This way all useful information is present - if (Parent is FlyoutPage flyout && flyout.ShouldShowToolbarButton() && !anyPagesPushed.Value) + if (Parent is FlyoutPage flyout && flyout.ShouldShowToolbarButton() +#if !WINDOWS // TODO NET 10 : Move this logic to ShouldShowToolbarButton + && !anyPagesPushed.Value +#endif + ) _drawerToggleVisible = true; else _drawerToggleVisible = false; diff --git a/src/Controls/src/Core/Page/Page.cs b/src/Controls/src/Core/Page/Page.cs index 0b0748d5531c..a119fe23e725 100644 --- a/src/Controls/src/Core/Page/Page.cs +++ b/src/Controls/src/Core/Page/Page.cs @@ -21,7 +21,7 @@ namespace Microsoft.Maui.Controls /// is primarily a base class for more useful derived types. Objects that are derived from the class are most prominently used as the top level UI element in .NET MAUI applications. In addition to their role as the main pages of applications, objects and their descendants can be used with navigation classes, such as or , among others, to provide rich user experiences that conform to the expected behaviors on each platform. /// [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - public partial class Page : VisualElement, ILayout, IPageController, IElementConfiguration, IPaddingElement, ISafeAreaView, ISafeAreaView2, IView, ITitledElement, IToolbarElement + public partial class Page : VisualElement, ILayout, IPageController, IElementConfiguration, IPaddingElement, ISafeAreaView, ISafeAreaView2, IView, ITitledElement, IToolbarElement, IConstrainedView #if IOS ,IiOSPageSpecifics #endif @@ -212,6 +212,8 @@ public bool IgnoresContainerArea /// bool ISafeAreaView.IgnoreSafeArea => !On().UsingSafeArea(); + bool IConstrainedView.HasFixedConstraints => true; + #if IOS /// bool IiOSPageSpecifics.IsHomeIndicatorAutoHidden diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs index 1fe23332fa72..7348dbd80992 100644 --- a/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs +++ b/src/Controls/src/Core/Platform/Windows/Extensions/ToolbarExtensions.cs @@ -87,9 +87,9 @@ public static void UpdateToolbarDynamicOverflowEnabled(this MauiToolbar platform private static void UpdateBackButtonVisibility(MauiToolbar platformToolbar, Toolbar toolbar) { - platformToolbar.IsBackButtonVisible = - toolbar.BackButtonVisible - ? NavigationViewBackButtonVisible.Visible + platformToolbar.IsBackButtonVisible = + toolbar.BackButtonVisible + ? NavigationViewBackButtonVisible.Visible : NavigationViewBackButtonVisible.Collapsed; } } diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index a5cea6dbf069..ae0b36700c23 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -98,6 +98,7 @@ Microsoft.Maui.Controls.HybridWebView.SetInvokeJavaScriptTarget(T! target) -> ~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, System.IServiceProvider serviceProvider = null, bool expandToExtension = true) -> System.Type ~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.RequireServiceAttribute(System.Type[] serviceTypes) -> void ~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.ServiceTypes.get -> System.Type[] +~override Microsoft.Maui.Controls.Handlers.Items.CarouselViewController.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void ~override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.CreateDelegator() -> UIKit.UICollectionViewDelegateFlowLayout ~override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.CreateItemsViewSource() -> Microsoft.Maui.Controls.Handlers.Items.IItemsViewSource ~override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.DetermineCellReuseId(Foundation.NSIndexPath indexPath) -> string diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index e8340e236e02..045f59e5714a 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -98,6 +98,7 @@ Microsoft.Maui.Controls.HybridWebView.SetInvokeJavaScriptTarget(T! target) -> ~Microsoft.Maui.Controls.Xaml.IXamlTypeResolver.Resolve(string qualifiedTypeName, System.IServiceProvider serviceProvider = null, bool expandToExtension = true) -> System.Type ~Microsoft.Maui.Controls.WebViewProcessTerminatedEventArgs.PlatformArgs.get -> Microsoft.Maui.Controls.PlatformWebViewProcessTerminatedEventArgs ~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.RequireServiceAttribute(System.Type[] serviceTypes) -> void +~override Microsoft.Maui.Controls.Handlers.Items.CarouselViewController.TraitCollectionDidChange(UIKit.UITraitCollection previousTraitCollection) -> void ~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.ServiceTypes.get -> System.Type[] ~override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.CreateDelegator() -> UIKit.UICollectionViewDelegateFlowLayout ~override Microsoft.Maui.Controls.Handlers.Items2.CarouselViewController2.CreateItemsViewSource() -> Microsoft.Maui.Controls.Handlers.Items.IItemsViewSource diff --git a/src/Controls/src/Core/ShellToolbar.cs b/src/Controls/src/Core/ShellToolbar.cs index 2b3ebfb63c6a..beb4e6374a62 100644 --- a/src/Controls/src/Core/ShellToolbar.cs +++ b/src/Controls/src/Core/ShellToolbar.cs @@ -91,7 +91,11 @@ internal void ApplyChanges() } var flyoutBehavior = (_shell as IFlyoutView).FlyoutBehavior; +#if WINDOWS + _drawerToggleVisible = flyoutBehavior is FlyoutBehavior.Flyout; +#else _drawerToggleVisible = stack.Count <= 1 && flyoutBehavior is FlyoutBehavior.Flyout; +#endif BackButtonVisible = backButtonVisible && stack.Count > 1; BackButtonEnabled = _backButtonBehavior?.IsEnabled ?? true; ToolbarItems = _toolbarTracker.ToolbarItems; diff --git a/src/Controls/src/Core/VisualElement/VisualElement.cs b/src/Controls/src/Core/VisualElement/VisualElement.cs index c392162019ce..4827cea259dd 100644 --- a/src/Controls/src/Core/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement/VisualElement.cs @@ -22,7 +22,7 @@ namespace Microsoft.Maui.Controls /// [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController, IView, IControlsVisualElement + public partial class VisualElement : NavigableElement, IAnimatable, IVisualElementController, IResourcesProvider, IStyleElement, IFlowDirectionController, IPropertyPropagationController, IVisualController, IWindowController, IView, IControlsVisualElement, IConstrainedView { /// Bindable property for . public new static readonly BindableProperty NavigationProperty = NavigableElement.NavigationProperty; @@ -945,6 +945,8 @@ internal LayoutConstraint ComputedConstraint internal LayoutConstraint Constraint => ComputedConstraint | SelfConstraint; + bool IConstrainedView.HasFixedConstraints => Constraint == LayoutConstraint.Fixed; + /// /// Gets a value that indicates that layout for this element is disabled. /// diff --git a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Android.cs index 19341a3908bf..454725e65713 100644 --- a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Android.cs @@ -2,11 +2,13 @@ using System.ComponentModel; using System.Threading.Tasks; using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Handlers; using Microsoft.Maui.Graphics; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; using Xunit; + namespace Microsoft.Maui.DeviceTests { public partial class BoxViewTests @@ -96,6 +98,25 @@ public async Task RotationConsistent() var platformRotation = await InvokeOnMainThreadAsync(() => platformBoxView.Rotation); Assert.Equal(expected, platformRotation); } + + [Fact] + [Description("The IsEnabled property of a BoxView should match with native IsEnabled")] + public async Task VerifyBoxViewIsEnabledProperty() + { + var boxView = new BoxView + { + IsEnabled = false + }; + var expectedValue = boxView.IsEnabled; + + var handler = await CreateHandlerAsync(boxView); + var nativeView = GetNativeBoxView(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + Assert.Equal(expectedValue, isEnabled); + }); + } Task GetPlatformIsVisible(ShapeViewHandler boxViewViewHandler) { diff --git a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Windows.cs index 88e0735833b1..da087c8ae9d7 100644 --- a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.Windows.cs @@ -3,6 +3,10 @@ using Microsoft.Maui.Graphics.Platform; using Microsoft.Maui.Graphics.Win2D; using Microsoft.Maui.Handlers; +using Xunit; +using System.ComponentModel; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Handlers; namespace Microsoft.Maui.DeviceTests { @@ -20,6 +24,25 @@ Task GetPlatformOpacity(ShapeViewHandler handler) }); } + [Fact] + [Description("The IsEnabled property of a BoxView should match with native IsEnabled")] + public async Task BoxViewIsEnabled() + { + var boxView = new BoxView + { + IsEnabled = false + }; + var expectedValue = boxView.IsEnabled; + + var handler = await CreateHandlerAsync(boxView); + var nativeView = GetNativeBoxView(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.IsEnabled; + Assert.Equal(expectedValue, isEnabled); + }); + } + Task GetPlatformIsVisible(ShapeViewHandler boxViewHandler) { return InvokeOnMainThreadAsync(() => diff --git a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.cs b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.cs index cf574048faf1..5f271391ad18 100644 --- a/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/BoxView/BoxViewTests.cs @@ -4,9 +4,9 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; using Microsoft.Maui.Handlers; +using Microsoft.Maui.Controls.Handlers; using Microsoft.Maui.Hosting; using Xunit; -using Microsoft.Maui.Controls.Handlers; namespace Microsoft.Maui.DeviceTests { @@ -47,6 +47,21 @@ public async Task BoxViewBackgroundColorConsistent() await ValidateHasColor(boxView, expected, typeof(ShapeViewHandler)); } + [Fact] + [Description("The Background of a BoxView should match with native Background")] + public async Task BoxViewBackgroundConsistent() + { + var boxView = new BoxView + { + HeightRequest = 100, + WidthRequest = 200, + Background = Brush.Red + }; + var expected = (boxView.Background as SolidColorBrush)?.Color; + + await ValidateHasColor(boxView, expected, typeof(BoxViewHandler)); + } + [Fact] [Description("The Opacity property of a BoxView should match with native Opacity")] public async Task VerifyBoxViewOpacityProperty() diff --git a/src/Controls/tests/DeviceTests/Elements/Button/ButtonTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Button/ButtonTests.iOS.cs index ed61420c0467..a8011a5b30f4 100644 --- a/src/Controls/tests/DeviceTests/Elements/Button/ButtonTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Button/ButtonTests.iOS.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.ComponentModel; using System.Threading.Tasks; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; @@ -89,5 +90,24 @@ await InvokeOnMainThreadAsync(async () => Assert.True(button.Width < gridWidth, $"Button shouldn't occupy entire layout width. Expected: {gridWidth}<, was {button.Width}"); Assert.True(button.Height < gridHeight, $"Button shouldn't occupy entire layout height. Expected: {gridHeight}<, was {button.Height}"); } + + [Fact] + [Description("The CornerRadius of a Button should match with native CornerRadius")] + public async Task ButtonCornerRadius() + { + var button = new Button + { + CornerRadius = 15, + }; + var expectedValue = button.CornerRadius; + + var handler = await CreateHandlerAsync(button); + var nativeView = GetPlatformButton(handler); + await InvokeOnMainThreadAsync(() => + { + var platformCornerRadius = nativeView.Layer.CornerRadius; + Assert.Equal(expectedValue, platformCornerRadius); + }); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Android.cs index 801dc4caa566..3908c07c9d58 100644 --- a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Android.cs @@ -98,6 +98,22 @@ public async Task RotationConsistent() Assert.Equal(expected, platformRotation); } + [Fact("The IsEnabled of a CheckBox should match with native IsEnabled")] + public async Task CheckBoxIsEnabled() + { + var checkBox = new CheckBox(); + checkBox.IsEnabled = false; + var expectedValue = checkBox.IsEnabled; + + var handler = await CreateHandlerAsync(checkBox); + var nativeView = GetNativeCheckBox(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + Assert.Equal(expectedValue, isEnabled); + }); + } + Task GetPlatformIsVisible(CheckBoxHandler checkBoxHandler) { return InvokeOnMainThreadAsync(() => diff --git a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Windows.cs index b37682a406a4..da7a3b43ae4f 100644 --- a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.Windows.cs @@ -3,6 +3,7 @@ using Microsoft.Maui.Graphics; using Microsoft.Maui.Handlers; using Microsoft.UI.Xaml.Controls; +using Xunit; namespace Microsoft.Maui.DeviceTests { @@ -19,6 +20,22 @@ Task GetPlatformOpacity(CheckBoxHandler checkBoxHandler) return (float)nativeView.Opacity; }); } + + [Fact("The IsEnabled of a CheckBox should match with native IsEnabled")] + public async Task CheckBoxIsEnabled() + { + var checkBox = new Microsoft.Maui.Controls.CheckBox(); + checkBox.IsEnabled = false; + var expectedValue = checkBox.IsEnabled; + + var handler = await CreateHandlerAsync(checkBox); + var nativeView = GetNativeCheckBox(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.IsEnabled; + Assert.Equal(expectedValue, isEnabled); + }); + } Task GetPlatformIsVisible(CheckBoxHandler checkBoxHandler) { diff --git a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.iOS.cs index c907dcf606ff..c1cbbfe2a5a5 100644 --- a/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/CheckBox/CheckBoxTests.iOS.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; using Xunit; @@ -21,6 +22,22 @@ Task GetPlatformOpacity(CheckBoxHandler checkBoxHandler) return (float)nativeView.Alpha; }); } + + [Fact("The IsEnabled of a CheckBox should match with native IsEnabled")] + public async Task CheckBoxIsEnabled() + { + var checkBox = new CheckBox(); + checkBox.IsEnabled = false; + var expectedValue = checkBox.IsEnabled; + + var handler = await CreateHandlerAsync(checkBox); + var nativeView = GetNativeCheckBox(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + Assert.Equal(expectedValue, isEnabled); + }); + } Task GetPlatformIsVisible(CheckBoxHandler checkBoxHandler) { diff --git a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs index 974d13a379f8..75557f717c99 100644 --- a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Android.cs @@ -150,5 +150,25 @@ public async Task RotationConsistent() var platformRotation = await InvokeOnMainThreadAsync(() => PlatformEditor.Rotation); Assert.Equal(expected, platformRotation); } + + [Fact] + [Description("The IsEnabled property of a Editor should match with native IsEnabled")] + public async Task VerifyEditorIsEnabledProperty() + { + var editor = new Editor + { + IsEnabled = false + }; + var expectedValue = editor.IsEnabled; + + var handler = await CreateHandlerAsync(editor); + var nativeView = GetPlatformControl(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + + Assert.Equal(expectedValue, isEnabled); + }); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Windows.cs index e9bb56dc2acf..6dc9ab2f9d7c 100644 --- a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.Windows.cs @@ -1,7 +1,14 @@ #nullable enable +using Xunit; +using System; +using System.ComponentModel; using System.Threading.Tasks; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Dispatching; using Microsoft.Maui.Handlers; using Microsoft.UI.Xaml.Controls; +using WFlowDirection = Microsoft.UI.Xaml.FlowDirection; +using WTextAlignment = Microsoft.UI.Xaml.TextAlignment; namespace Microsoft.Maui.DeviceTests { @@ -41,5 +48,35 @@ Task GetPlatformIsVisible(EditorHandler editorHandler) return nativeView.Visibility == Microsoft.UI.Xaml.Visibility.Visible; }); } + //src/Compatibility/Core/tests/WinUI/FlowDirectionTests.cs + [Theory] + [InlineData(true, FlowDirection.RightToLeft, WTextAlignment.Left, WFlowDirection.RightToLeft)] + [InlineData(true, FlowDirection.LeftToRight, WTextAlignment.Left, WFlowDirection.LeftToRight)] + [InlineData(false, FlowDirection.LeftToRight, WTextAlignment.Left, WFlowDirection.LeftToRight)] + [Description("The Editor's text alignment and flow direction should match the expected values when FlowDirection is applied explicitly or implicitly.")] + public async Task EditorAlignmentMatchesFlowDirection(bool isExplicit, FlowDirection flowDirection, WTextAlignment expectedAlignment, WFlowDirection expectedFlowDirection) + { + var editor = new Editor { Text = " تسجيل الدخول" }; + var contentPage = new ContentPage { Title = "Flow Direction", Content = editor }; + + if (isExplicit) + { + editor.FlowDirection = flowDirection; + } + else + { + contentPage.FlowDirection = flowDirection; + } + + var handler = await CreateHandlerAsync(editor); + var (nativeAlignment, nativeFlowDirection) = await contentPage.Dispatcher.DispatchAsync(() => + { + var textField = GetPlatformControl(handler); + return (textField.TextAlignment, textField.FlowDirection); + }); + + Assert.Equal(expectedAlignment, nativeAlignment); + Assert.Equal(expectedFlowDirection, nativeFlowDirection); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs index 6be8fb8fb7b4..6f89153da58c 100644 --- a/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs @@ -1,11 +1,14 @@ -using System.Linq; +using UIKit; +using Xunit; +using System.Linq; using System.Threading.Tasks; using Microsoft.Maui.Controls; +using Microsoft.Maui.Dispatching; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform; -using Xunit; using static Microsoft.Maui.DeviceTests.AssertHelpers; +using System.ComponentModel; namespace Microsoft.Maui.DeviceTests { @@ -48,7 +51,7 @@ Task GetPlatformOpacity(EditorHandler editorHandler) return InvokeOnMainThreadAsync(() => { var nativeView = GetPlatformControl(editorHandler); - return (float)nativeView.Alpha; + return (float)nativeView.Alpha; }); } @@ -103,5 +106,35 @@ await CreateHandlerAndAddToWindow(contentPage, async () => }); } } + + //src/Compatibility/Core/tests/iOS/FlowDirectionTests.cs + [Theory] + [InlineData(true, FlowDirection.LeftToRight, UITextAlignment.Left)] + [InlineData(true, FlowDirection.RightToLeft, UITextAlignment.Right)] + [InlineData(false, FlowDirection.LeftToRight, UITextAlignment.Left)] + [Description("The Editor's text alignment should match the expected alignment when FlowDirection is applied explicitly or implicitly")] + public async Task EditorAlignmentMatchesFlowDirection(bool isExplicit, FlowDirection flowDirection, UITextAlignment expectedAlignment) + { + var editor = new Editor { Text = "Checking flow direction" }; + var contentPage = new ContentPage { Title = "Flow Direction", Content = editor }; + + if (isExplicit) + { + editor.FlowDirection = flowDirection; + } + else + { + contentPage.FlowDirection = flowDirection; + } + + var handler = await CreateHandlerAsync(editor); + var nativeAlignment = await contentPage.Dispatcher.DispatchAsync(() => + { + var textField = GetPlatformControl(handler); + return textField.TextAlignment; + }); + + Assert.Equal(expectedAlignment, nativeAlignment); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Windows.cs index f76b6526f23b..a41c816bea69 100644 --- a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.Windows.cs @@ -1,7 +1,12 @@ #nullable enable +using Xunit; +using System.ComponentModel; using System.Threading.Tasks; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Dispatching; using Microsoft.Maui.Handlers; using Microsoft.UI.Xaml.Controls; +using WTextAlignment = Microsoft.UI.Xaml.TextAlignment; namespace Microsoft.Maui.DeviceTests { @@ -41,5 +46,35 @@ Task GetPlatformIsVisible(EntryHandler entryHandler) return nativeView.Visibility == Microsoft.UI.Xaml.Visibility.Visible; }); } + //src/Compatibility/Core/tests/WinUI/FlowDirectionTests.cs + [Theory] + [InlineData(true, FlowDirection.LeftToRight, WTextAlignment.Left)] + [InlineData(true, FlowDirection.RightToLeft, WTextAlignment.Left)] + [InlineData(false, FlowDirection.LeftToRight, WTextAlignment.Left)] + [InlineData(false, FlowDirection.RightToLeft, WTextAlignment.Left)] + [Description("The Entry's text alignment should match the expected alignment when FlowDirection is applied explicitly or implicitly.")] + public async Task EntryAlignmentMatchesFlowDirection(bool isExplicit, FlowDirection flowDirection, WTextAlignment expectedAlignment) + { + var entry = new Entry { Text = "Checking flow direction", HorizontalTextAlignment = TextAlignment.Start }; + var contentPage = new ContentPage { Title = "Flow Direction", Content = entry }; + + if (isExplicit) + { + entry.FlowDirection = flowDirection; + } + else + { + contentPage.FlowDirection = flowDirection; + } + + var handler = await CreateHandlerAsync(entry); + var nativeAlignment = await contentPage.Dispatcher.DispatchAsync(() => + { + var textField = GetPlatformControl(handler); + return textField.TextAlignment; + }); + + Assert.Equal(expectedAlignment, nativeAlignment); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs index 694c82b6a090..67ac30dbb120 100644 --- a/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Entry/EntryTests.iOS.cs @@ -1,13 +1,15 @@ -using System.Linq; +using UIKit; +using Xunit; +using System.ComponentModel; +using System.Linq; using System.Threading.Tasks; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Handlers.Compatibility; using Microsoft.Maui.DeviceTests.TestCases; +using Microsoft.Maui.Dispatching; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform; -using UIKit; -using Xunit; using static Microsoft.Maui.DeviceTests.AssertHelpers; namespace Microsoft.Maui.DeviceTests @@ -44,13 +46,13 @@ static int GetPlatformSelectionLength(EntryHandler entryHandler) return -1; } - + Task GetPlatformOpacity(EntryHandler entryHandler) { return InvokeOnMainThreadAsync(() => { var nativeView = GetPlatformControl(entryHandler); - return (float)nativeView.Alpha; + return (float)nativeView.Alpha; }); } @@ -340,5 +342,35 @@ await CreateHandlerAndAddToWindow(contentPage, async () => }); } } + + //src/Compatibility/Core/tests/iOS/FlowDirectionTests.cs + [Theory] + [InlineData(true, FlowDirection.LeftToRight, UITextAlignment.Left)] + [InlineData(true, FlowDirection.RightToLeft, UITextAlignment.Right)] + [InlineData(false, FlowDirection.LeftToRight, UITextAlignment.Left)] + [Description("The Entry's text alignment should match the expected alignment when FlowDirection is applied explicitly or implicitly")] + public async Task EntryAlignmentMatchesFlowDirection(bool isExplicit, FlowDirection flowDirection, UITextAlignment expectedAlignment) + { + var entry = new Entry { Text = "Checking flow direction", HorizontalTextAlignment = TextAlignment.Start }; + var contentPage = new ContentPage { Title = "Flow Direction", Content = entry }; + + if (isExplicit) + { + entry.FlowDirection = flowDirection; + } + else + { + contentPage.FlowDirection = flowDirection; + } + + var handler = await CreateHandlerAsync(entry); + var nativeAlignment = await contentPage.Dispatcher.DispatchAsync(() => + { + var textField = GetPlatformControl(handler); + return textField.TextAlignment; + }); + + Assert.Equal(expectedAlignment, nativeAlignment); + } } } \ No newline at end of file diff --git a/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.Android.cs index 63056dd9c89b..5fff673e2c3b 100644 --- a/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/Label/LabelTests.Android.cs @@ -162,6 +162,26 @@ public async Task RotationConsistent() var platformRotation = await InvokeOnMainThreadAsync(() => platformLabel.Rotation); Assert.Equal(expected, platformRotation); } + + [Fact] + [Description("The IsEnabled property of a Label should match with native IsEnabled")] + public async Task VerifyLabelIsEnabledProperty() + { + var label = new Label + { + IsEnabled = false + }; + var expectedValue = label.IsEnabled; + + var handler = await CreateHandlerAsync(label); + var nativeView = GetPlatformLabel(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + Assert.Equal(expectedValue, isEnabled); + }); + } + TextView GetPlatformLabel(LabelHandler labelHandler) => labelHandler.PlatformView; diff --git a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.iOS.cs index eca6b8284954..89cd1b7ed98b 100644 --- a/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.iOS.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using Microsoft.Maui.Controls.Handlers.Compatibility; using Microsoft.Maui.Controls.Platform; using Microsoft.Maui.DeviceTests.Stubs; +using Microsoft.Maui.Dispatching; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; using Microsoft.Maui.Platform; @@ -65,5 +67,29 @@ public async Task TranslucentNavigationBar(bool enabled) var translucent = await GetValueAsync(navPage, (handler) => (handler.ViewController as UINavigationController).NavigationBar.Translucent); Assert.Equal(enabled, translucent); } + + //src/Compatibility/Core/tests/iOS/NavigationTests.cs + [Fact] + [Description("Multiple calls to NavigationRenderer.Dispose shouldn't crash")] + public async Task NavigationRendererDoubleDisposal() + { + SetupBuilder(); + + var root = new ContentPage() + { + Title = "root", + Content = new Label { Text = "Hello" } + }; + + await root.Dispatcher.DispatchAsync(() => + { + var navPage = new NavigationPage(root); + var handler = CreateHandler(navPage); + + // Calling Dispose more than once should be fine + (handler as NavigationRenderer).Dispose(); + (handler as NavigationRenderer).Dispose(); + }); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/Page/PageTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/Page/PageTests.Android.cs new file mode 100644 index 000000000000..8ee2bf4a9524 --- /dev/null +++ b/src/Controls/tests/DeviceTests/Elements/Page/PageTests.Android.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Dispatching; +using Microsoft.Maui.Handlers; +using Microsoft.Maui.Platform; +using Xunit; +using AView = Android.Views.View; +using AndroidX.Fragment.App; + +namespace Microsoft.Maui.DeviceTests +{ + public partial class PageTests : ControlsHandlerTestBase + { + //src/Compatibility/Core/tests/Android/EmbeddingTests.cs + [Fact(DisplayName = "Can Create Platform View From ContentPage")] + public async Task CanCreatePlatformViewFromContentPage() + { + + var contentPage = new ContentPage { Title = "Embedded Page" }; + var handler = CreateHandler(contentPage); + var mauiContext = handler.MauiContext; + + await contentPage.Dispatcher.DispatchAsync(() => + { + + AView platformView = contentPage.ToPlatform(mauiContext); + if (platformView is FragmentContainerView containerView) + { + var activity = mauiContext.Context as AndroidX.Fragment.App.FragmentActivity; + var fragmentManager = activity.SupportFragmentManager; + var fragment = fragmentManager.FindFragmentById(containerView.Id); + Assert.NotNull(fragment); + } + }); + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/DeviceTests/Elements/Page/PageTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/Page/PageTests.iOS.cs index 1c71d20140cb..045e31fb2c72 100644 --- a/src/Controls/tests/DeviceTests/Elements/Page/PageTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/Page/PageTests.iOS.cs @@ -3,9 +3,12 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.PlatformConfiguration; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Microsoft.Maui.Dispatching; +using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics; using Microsoft.Maui.Handlers; using Microsoft.Maui.Hosting; +using UIKit; using Xunit; namespace Microsoft.Maui.DeviceTests @@ -40,5 +43,23 @@ await CreateHandlerAndAddToWindow(shell, (handler) => } }); } + + //src/Compatibility/Core/tests/iOS/EmbeddingTests.cs + [Fact(DisplayName = "Can Create Platform View From ContentPage")] + public async Task CanCreateViewControllerFromContentPage() + { + var contentPage = new ContentPage { Title = "Embedded Page" }; + await contentPage.Dispatcher.DispatchAsync(async () => + { + var handler = CreateHandler(contentPage); + var mauiContext = handler.MauiContext; + + await contentPage.Dispatcher.DispatchAsync(() => + { + UIViewController viewController = contentPage.ToUIViewController(mauiContext); + Assert.NotNull(viewController); + }); + }); + } } } diff --git a/src/Controls/tests/DeviceTests/Elements/RadioButton/RadioButtonTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/RadioButton/RadioButtonTests.Windows.cs index 30853568b48e..84e80f647bcd 100644 --- a/src/Controls/tests/DeviceTests/Elements/RadioButton/RadioButtonTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/RadioButton/RadioButtonTests.Windows.cs @@ -1,8 +1,8 @@ using System.ComponentModel; +using Xunit; using System.Threading.Tasks; using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; -using Xunit; namespace Microsoft.Maui.DeviceTests { @@ -27,14 +27,56 @@ public async Task VerifyRadioButtonOpacityProperty() var handler = await CreateHandlerAsync(radioButton); var nativeView = GetNativeRadioButton(handler); await InvokeOnMainThreadAsync(() => - { + { var nativeOpacityValue = (float)nativeView.Opacity; Assert.Equal(expectedValue, nativeOpacityValue); }); } [Fact] - [Description("The IsVisible property of a RadioButton should match with native IsVisible")] + [Description("The CornerRadius of a RadioButton should match with native CornerRadius")] + public async Task RadioButtonCornerRadius() + { + var radioButton = new RadioButton(); + radioButton.CornerRadius = 15; + var expectedValue = radioButton.CornerRadius; + + var handler = await CreateHandlerAsync(radioButton); + var nativeView = GetNativeRadioButton(handler); + await InvokeOnMainThreadAsync(() => + { + var cornerRadiusTopLeft = (float)nativeView.CornerRadius.TopLeft; + var cornerRadiusTopRight = (float)nativeView.CornerRadius.TopRight; + var cornerRadiusBottomLeft = (float)nativeView.CornerRadius.BottomLeft; + var cornerRadiusBottomRight = (float)nativeView.CornerRadius.BottomRight; + Assert.Equal(expectedValue, cornerRadiusTopLeft); + Assert.Equal(expectedValue, cornerRadiusTopRight); + Assert.Equal(expectedValue, cornerRadiusBottomLeft); + Assert.Equal(expectedValue, cornerRadiusBottomRight); + }); + } + + [Fact] + [Description("The IsEnabled of a RadioButton should match with native IsEnabled")] + public async Task VerifyRadioButtonIsEnabledProperty() + { + var radioButton = new RadioButton + { + IsEnabled = false + }; + var expectedValue = radioButton.IsEnabled; + + var handler = await CreateHandlerAsync(radioButton); + var nativeView = GetNativeRadioButton(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.IsEnabled; + Assert.Equal(expectedValue, isEnabled); + }); + } + + [Fact] + [Description("The IsVisible property of a RadioButton should match with native IsVisible")] public async Task VerifyRadioButtonIsVisibleProperty() { var radioButton = new RadioButton(); @@ -47,7 +89,7 @@ await InvokeOnMainThreadAsync(() => { var isVisible = nativeView.Visibility == Microsoft.UI.Xaml.Visibility.Visible; Assert.Equal(expectedValue, isVisible); - }); + }); } } -} +} \ No newline at end of file diff --git a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Android.cs index de7e2457fa70..40bb53689d0f 100644 --- a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Android.cs @@ -117,6 +117,26 @@ public async Task RotationConsistent() Assert.Equal(expected, platformRotation); } + [Fact] + [Description("The IsEnabled of a SearchBar should match with native IsEnabled")] + public async Task VerifySearchBarIsEnabledProperty() + { + var searchBar = new SearchBar + { + IsEnabled = false + }; + var expectedValue = searchBar.IsEnabled; + + var handler = await CreateHandlerAsync(searchBar); + var nativeView = GetPlatformControl(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + + Assert.Equal(expectedValue, isEnabled); + }); + } + Task GetPlatformIsVisible(SearchBarHandler searchBarHandler) { return InvokeOnMainThreadAsync(() => diff --git a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Windows.cs index 30a2ed3c6d6a..3fbf5f0cdfdf 100644 --- a/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Windows.cs +++ b/src/Controls/tests/DeviceTests/Elements/SearchBar/SearchBarTests.Windows.cs @@ -1,8 +1,11 @@ #nullable enable +using System.ComponentModel; using System.Threading.Tasks; +using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers; using Microsoft.Maui.Platform; using Microsoft.UI.Xaml.Controls; +using Xunit; namespace Microsoft.Maui.DeviceTests { @@ -52,6 +55,25 @@ Task GetPlatformOpacity(SearchBarHandler searchBarHandler) return (float)nativeView.Opacity; }); } + + [Fact] + [Description("The IsEnabled of a SearchBar should match with native IsEnabled")] + public async Task VerifySearchBarIsEnabledProperty() + { + var searchBar = new SearchBar + { + IsEnabled = false + }; + var expectedValue = searchBar.IsEnabled; + + var handler = await CreateHandlerAsync(searchBar); + var nativeView = GetPlatformControl(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.IsEnabled; + Assert.Equal(expectedValue, isEnabled); + }); + } Task GetPlatformIsVisible(SearchBarHandler searchBarHandler) { diff --git a/src/Controls/tests/DeviceTests/Elements/SwipeView/SwipeViewTests.Android.cs b/src/Controls/tests/DeviceTests/Elements/SwipeView/SwipeViewTests.Android.cs index c0c3d61bbc62..7def68b4ed47 100644 --- a/src/Controls/tests/DeviceTests/Elements/SwipeView/SwipeViewTests.Android.cs +++ b/src/Controls/tests/DeviceTests/Elements/SwipeView/SwipeViewTests.Android.cs @@ -189,5 +189,24 @@ await InvokeOnMainThreadAsync(() => Assert.Equal(expectedValue, isVisible); }); } + + [Fact] + [Description("The IsEnabled of a SwipeView should match with native IsEnabled")] + public async Task VerifySwipeViewIsEnabledProperty() + { + var swipeView = new SwipeView + { + IsEnabled = false + }; + var expectedValue = swipeView.IsEnabled; + + var handler = await CreateHandlerAsync(swipeView); + var nativeView = GetPlatformControl(handler); + await InvokeOnMainThreadAsync(() => + { + var isEnabled = nativeView.Enabled; + Assert.Equal(expectedValue, isEnabled); + }); + } } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CarouselViewItemShouldScaleProperly.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CarouselViewItemShouldScaleProperly.png new file mode 100644 index 000000000000..daf55575bde6 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CarouselViewItemShouldScaleProperly.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ReorderBetweenGroupsShouldNotOccurWhenCanMixGroupsIsFalse.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ReorderBetweenGroupsShouldNotOccurWhenCanMixGroupsIsFalse.png new file mode 100644 index 000000000000..8825c19a19b9 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/ReorderBetweenGroupsShouldNotOccurWhenCanMixGroupsIsFalse.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png new file mode 100644 index 000000000000..3065fce58707 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifySearchHandlerPlaceholderText.png differ diff --git a/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs b/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs index 242c643c89e5..b0ec25e6be54 100644 --- a/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs +++ b/src/Controls/tests/TestCases.HostApp/CoreViews/CorePageView.cs @@ -77,6 +77,7 @@ public override string ToString() new GalleryPageFactory(() => new TimePickerCoreGalleryPage(), "Time Picker Gallery"), new GalleryPageFactory(() => new WebViewCoreGalleryPage(), "WebView Gallery"), new GalleryPageFactory(() => new SliderControlPage(), "Slider Feature Matrix"), + new GalleryPageFactory(() => new HeaderFooterMainPage(), "CollectionView Feature Matrix"), }; public CorePageView(Page rootPage) diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml new file mode 100644 index 000000000000..5c49a597e156 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml.cs new file mode 100644 index 000000000000..27e08b4408c5 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewControlPage.xaml.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Maui.Controls; +using System.Collections.ObjectModel; + +namespace Maui.Controls.Sample +{ + public partial class CollectionViewControlPage : ContentPage + { + private CollectionViewViewModel _viewModel; + + public CollectionViewControlPage() + { + InitializeComponent(); + _viewModel = new CollectionViewViewModel(); + _viewModel.ItemsSourceType = ItemsSourceType.ObservableCollection5T; + BindingContext = _viewModel; + } + + private async void NavigateToOptionsPage_Clicked(object sender, EventArgs e) + { + BindingContext = _viewModel = new CollectionViewViewModel(); + _viewModel.ItemsSourceType = ItemsSourceType.ObservableCollection5T; + await Navigation.PushAsync(new CollectionViewOptionsPage(_viewModel)); + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml new file mode 100644 index 000000000000..7460999f5a1e --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml @@ -0,0 +1,275 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml.cs new file mode 100644 index 000000000000..ccf26c04cc54 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewOptionsPage.xaml.cs @@ -0,0 +1,278 @@ +using System; +using Microsoft.Maui.Controls; +using System.Collections.ObjectModel; +using Maui.Controls.Sample.CollectionViewGalleries; + +namespace Maui.Controls.Sample +{ + public partial class CollectionViewOptionsPage : ContentPage + { + private CollectionViewViewModel _viewModel; + + public CollectionViewOptionsPage(CollectionViewViewModel viewModel) + { + InitializeComponent(); + _viewModel = viewModel; + BindingContext = _viewModel; + } + + private void ApplyButton_Clicked(object sender, EventArgs e) + { + Navigation.PopAsync(); + } + + private void OnEmptyViewChanged(object sender, CheckedChangedEventArgs e) + { + if (EmptyViewNone.IsChecked) + { + _viewModel.EmptyView = null; + } + else if (EmptyViewString.IsChecked) + { + _viewModel.EmptyView = "No Items Available(String)"; + } + } + + private void OnHeaderChanged(object sender, CheckedChangedEventArgs e) + { + if (HeaderNone.IsChecked) + { + _viewModel.Header = null; + } + else if (HeaderString.IsChecked) + { + _viewModel.Header = "CollectionView Header(String)"; + } + else if (HeaderGrid.IsChecked) + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "CollectionView Header(Grid View)", + FontSize = 18, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Blue, + AutomationId = "HeaderViewLabel" + }); + _viewModel.Header = grid; + } + } + + private void OnFooterChanged(object sender, CheckedChangedEventArgs e) + { + if (FooterNone.IsChecked) + { + _viewModel.Footer = null; + } + else if (FooterString.IsChecked) + { + _viewModel.Footer = "CollectionView Footer(String)"; + } + else if (FooterGrid.IsChecked) + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "CollectionView Footer(Grid View)", + FontSize = 18, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Red, + AutomationId = "FooterViewLabel" + }); + _viewModel.Footer = grid; + } + } + + private void OnHeaderTemplateChanged(object sender, CheckedChangedEventArgs e) + { + if (HeaderTemplateNone.IsChecked) + { + _viewModel.HeaderTemplate = null; + } + else if (HeaderTemplateGrid.IsChecked) + { + _viewModel.HeaderTemplate = new DataTemplate(() => + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "Header Template(Grid View)", + FontSize = 18, + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Blue, + AutomationId = "HeaderTemplateLabel" + }); + return grid; + }); + } + } + + private void OnFooterTemplateChanged(object sender, CheckedChangedEventArgs e) + { + if (FooterTemplateNone.IsChecked) + { + _viewModel.FooterTemplate = null; + } + else if (FooterTemplateGrid.IsChecked) + { + _viewModel.FooterTemplate = new DataTemplate(() => + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "Footer Template(Grid View)", + FontSize = 18, + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Green, + AutomationId = "FooterTemplateLabel" + }); + return grid; + }); + } + } + + private void OnGroupHeaderTemplateChanged(object sender, CheckedChangedEventArgs e) + { + if (GroupHeaderTemplateNone.IsChecked) + { + _viewModel.GroupHeaderTemplate = null; + } + else if (GroupHeaderTemplateGrid.IsChecked) + { + _viewModel.GroupHeaderTemplate = new DataTemplate(() => + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "Group Header Template(Grid View)", + FontSize = 18, + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Green, + AutomationId = "GroupHeaderTemplateLabel" + }); + return grid; + }); + } + } + private void OnGroupFooterTemplateChanged(object sender, CheckedChangedEventArgs e) + { + if (GroupFooterTemplateNone.IsChecked) + { + _viewModel.GroupFooterTemplate = null; + } + else if (GroupFooterTemplateGrid.IsChecked) + { + _viewModel.GroupFooterTemplate = new DataTemplate(() => + { + Grid grid = new Grid + { + BackgroundColor = Colors.LightGray, + Padding = new Thickness(10) + }; + grid.Children.Add(new Label + { + Text = "Group Footer Template(Grid View)", + FontSize = 18, + FontAttributes = FontAttributes.Bold, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + TextColor = Colors.Red, + AutomationId = "GroupFooterTemplateLabel" + }); + return grid; + }); + } + } + private void OnIsGroupedChanged(object sender, CheckedChangedEventArgs e) + { + if (IsGroupedFalse.IsChecked) + { + _viewModel.IsGrouped = false; + } + else if (IsGroupedTrue.IsChecked) + { + _viewModel.IsGrouped = true; + } + } + + private void OnItemTemplateChanged(object sender, CheckedChangedEventArgs e) + { + if (ItemTemplateNone.IsChecked) + { + _viewModel.ItemTemplate = null; + } + else if (ItemTemplateBasic.IsChecked) + { + _viewModel.ItemTemplate = new DataTemplate(() => + { + var label = new Label(); + label.SetBinding(Label.TextProperty, new Binding("Caption")); + + return label; + }); + } + } + + private void OnItemsLayoutChanged(object sender, CheckedChangedEventArgs e) + { + if (ItemsLayoutVerticalList.IsChecked) + { + _viewModel.ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical); + } + else if (ItemsLayoutHorizontalList.IsChecked) + { + _viewModel.ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal); + } + else if (ItemsLayoutVerticalGrid.IsChecked) + { + _viewModel.ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical); // 2 columns + } + else if (ItemsLayoutHorizontalGrid.IsChecked) + { + _viewModel.ItemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Horizontal); // 2 rows + } + } + + private void OnItemsSourceChanged(object sender, CheckedChangedEventArgs e) + { + if (!(sender is RadioButton radioButton) || !e.Value) + return; + if (radioButton == ItemsSourceObservableCollection25) + _viewModel.ItemsSourceType = ItemsSourceType.ObservableCollection25T; + else if (radioButton == ItemsSourceObservableCollection5) + _viewModel.ItemsSourceType = ItemsSourceType.ObservableCollection5T; + else if (radioButton == ItemsSourceGroupedList) + _viewModel.ItemsSourceType = ItemsSourceType.GroupedListT; + else if (radioButton == ItemsSourceNone) + _viewModel.ItemsSourceType = ItemsSourceType.None; + } + } +} diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs new file mode 100644 index 000000000000..76d61f1ed6f3 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/CollectionViewViewModel.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Graphics; +using Maui.Controls.Sample.CollectionViewGalleries; + +namespace Maui.Controls.Sample +{ + public class Grouping : ObservableCollection + { + public TKey Key { get; } + + public Grouping(TKey key, IEnumerable items) : base(items) + { + Key = key; + } + } + + public enum ItemsSourceType + { + None, + ObservableCollection25T, + ObservableCollection5T, + GroupedListT, + EmptyGroupedListT, + EmptyObservableCollectionT + } + public class CollectionViewViewModel : INotifyPropertyChanged + { + private object _emptyView; + private object _header; + private object _footer; + private DataTemplate _emptyViewTemplate; + private DataTemplate _headerTemplate; + private DataTemplate _footerTemplate; + private DataTemplate _groupHeaderTemplate; + private DataTemplate _groupFooterTemplate; + private DataTemplate _itemTemplate; + private IItemsLayout _itemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical); + private ItemsSourceType _itemsSourceType = ItemsSourceType.None; + private bool _isGrouped = false; + private ObservableCollection _observableCollection25; + private ObservableCollection _observableCollection5; + private ObservableCollection _emptyObservableCollection; + private List> _groupedList; + private List> _emptyGroupedList; + public event PropertyChangedEventHandler PropertyChanged; + + public CollectionViewViewModel() + { + LoadItems(); + ItemTemplate = new DataTemplate(() => + { + var stackLayout = new StackLayout + { + Padding = new Thickness(10), + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + + var label = new Label + { + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center + }; + label.SetBinding(Label.TextProperty, "Caption"); + stackLayout.Children.Add(label); + return stackLayout; + }); + + GroupHeaderTemplate = new DataTemplate(() => + { + var stackLayout = new StackLayout + { + BackgroundColor = Colors.LightGray + }; + var label = new Label + { + FontAttributes = FontAttributes.Bold, + FontSize = 18 + }; + label.SetBinding(Label.TextProperty, "Key"); + stackLayout.Children.Add(label); + return stackLayout; + }); + } + + public object EmptyView + { + get => _emptyView; + set { _emptyView = value; OnPropertyChanged(); } + } + + public object Header + { + get => _header; + set { _header = value; OnPropertyChanged(); } + } + + public object Footer + { + get => _footer; + set { _footer = value; OnPropertyChanged(); } + } + + public DataTemplate EmptyViewTemplate + { + get => _emptyViewTemplate; + set { _emptyViewTemplate = value; OnPropertyChanged(); } + } + + public DataTemplate HeaderTemplate + { + get => _headerTemplate; + set { _headerTemplate = value; OnPropertyChanged(); } + } + + public DataTemplate FooterTemplate + { + get => _footerTemplate; + set { _footerTemplate = value; OnPropertyChanged(); } + } + + public DataTemplate GroupHeaderTemplate + { + get => _groupHeaderTemplate; + set { _groupHeaderTemplate = value; OnPropertyChanged(); } + } + + public DataTemplate GroupFooterTemplate + { + get => _groupFooterTemplate; + set { _groupFooterTemplate = value; OnPropertyChanged(); } + } + + public DataTemplate ItemTemplate + { + get => _itemTemplate; + set { _itemTemplate = value; OnPropertyChanged(); } + } + + public IItemsLayout ItemsLayout + { + get => _itemsLayout; + set + { + if (_itemsLayout != value) + { + _itemsLayout = value; + OnPropertyChanged(); + } + } + } + + public ItemsSourceType ItemsSourceType + { + get => _itemsSourceType; + set + { + if (_itemsSourceType != value) + { + _itemsSourceType = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(ItemsSource)); + } + } + } + + public bool IsGrouped + { + get => _isGrouped; + set + { + if (_isGrouped != value) + { + _isGrouped = value; + OnPropertyChanged(); + } + } + } + + public object ItemsSource + { + get + { + return ItemsSourceType switch + { + ItemsSourceType.ObservableCollection25T => _observableCollection25, + ItemsSourceType.ObservableCollection5T => _observableCollection5, + ItemsSourceType.GroupedListT => _groupedList, + ItemsSourceType.EmptyGroupedListT => _emptyGroupedList, + ItemsSourceType.EmptyObservableCollectionT => _emptyObservableCollection, + ItemsSourceType.None => null, + _ => null + }; + } + } + + + private void LoadItems() + { + _observableCollection25 = new ObservableCollection(); + _observableCollection5 = new ObservableCollection(); + AddItems(_observableCollection5, 5, "Fruits"); + AddItems(_observableCollection25, 10, "Fruits"); + AddItems(_observableCollection25, 10, "Vegetables"); + + _emptyObservableCollection = new ObservableCollection(); + + _groupedList = new List> + { + new Grouping("Fruits", new List()), + new Grouping("Vegetables", new List()) + }; + + AddItems(_groupedList[0], 4, "Fruits"); + AddItems(_groupedList[1], 4, "Vegetables"); + + _emptyGroupedList = new List>(); + } + + + private void AddItems(IList list, int count, string category) + { + string[] fruits = + { + "Apple", "Banana", "Orange", "Grapes", "Mango", + "Pineapple", "Strawberry", "Blueberry", "Peach", "Cherry", + "Watermelon", "Papaya", "Kiwi", "Pear", "Plum", + "Avocado", "Fig", "Guava", "Lychee", "Pomegranate", + "Lime", "Lemon", "Coconut", "Apricot", "Blackberry" + }; + + string[] vegetables = + { + "Carrot", "Broccoli", "Spinach", "Potato", "Tomato", + "Cucumber", "Lettuce", "Onion", "Garlic", "Pepper", + "Zucchini", "Pumpkin", "Radish", "Beetroot", "Cabbage", + "Sweet Potato", "Turnip", "Cauliflower", "Celery", "Asparagus", + "Eggplant", "Chili", "Corn", "Peas", "Mushroom" + }; + + string[] items = category == "Fruits" ? fruits : vegetables; + + for (int n = 0; n < count; n++) + { + list.Add(new CollectionViewTestItem(items[n % items.Length], n)); // Pass the index + } + } + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + if (propertyName == nameof(IsGrouped)) + { + OnPropertyChanged(nameof(ItemsSource)); + } + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public class CustomDataTemplateSelector : DataTemplateSelector + { + public DataTemplate Template1 { get; set; } + public DataTemplate Template2 { get; set; } + + protected override DataTemplate OnSelectTemplate(object item, BindableObject container) + { + if (item is CollectionViewTestItem testItem) + { + return testItem.Index % 2 == 0 ? Template1 : Template2; + } + + return Template1; + } + } + + public class CollectionViewTestItem + { + public string Caption { get; set; } + public int Index { get; set; } + + public CollectionViewTestItem(string caption, int index) + { + Caption = caption; + Index = index; + } + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/HeaderFooterMainPage.xaml b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/HeaderFooterMainPage.xaml new file mode 100644 index 000000000000..c98e25007ce5 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/FeatureMatrix/CollectionView/HeaderFooterMainPage.xaml @@ -0,0 +1,15 @@ + + + +