Skip to content

Commit 69ffc44

Browse files
RedthCopilot
andcommitted
Merge core (dbcf82e) and specify the long-press state machine
Merge conflicts were the solution's test folder and the workload-free lane's project list. Core reworded its RefPackCompile commentary and split the sample into its own lane, so core's text was taken verbatim and this branch's two Controls entries re-added around it. Long-press mapping ------------------ dotnet/maui#37861 makes SendLongPressed and SendLongPressing public. It is code/CI complete but NOT merged, so it is absent from the pinned package and is not adopted here. What is landed is the translation that adoption will need, specified and tested now so it is a small change later rather than a fresh translation written at the time. The in-box Tizen handler is deliberately NOT the model. Checked against dotnet/maui net11.0: LongPressGestureHandler.cs has no Continuing branch at all, so a Tizen long press never reports GestureStatus.Running and an app tracking the gesture sees Started jump straight to Completed. iOS maps its equivalent (UIGestureRecognizerState.Changed) to Running - verified in GesturePlatformManager.iOS.cs - and that is what this backend follows: Started -> Started LongPressing Continuing -> Running LongPressing Finished -> Completed LongPressed FIRST, then LongPressing Cancelled -> Canceled LongPressing only; never LongPressed, never Command A canceled press reports a status change but is not a press, so raising LongPressed or running the command there would fire the app's handler for a gesture the user aborted. CompletesLongPress encodes that and is tested per state. The handler previously dropped Continuing, inheriting the in-box gap. It now passes it through, and ToLongPressStatus/CompletesLongPress encode the table above. Reintroducing the gap fails four tests across both the mapping and the handler's ordering, so it cannot come back silently during adoption. 208 tests, up from 196. The API15 compile lane passes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2 parents c4bc396 + dbcf82e commit 69ffc44

25 files changed

Lines changed: 1440 additions & 153 deletions

Maui.Tizen.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<Project Path="tests/UnitTests/Maui.Tizen.UnitTests.csproj" />
3131
<Project Path="tests/Maui.Tizen.Core.UnitTests/Maui.Tizen.Core.UnitTests.csproj" />
3232
<Project Path="tests/Maui.Tizen.Core.RefPackCompile/Maui.Tizen.Core.RefPackCompile.csproj" />
33+
<Project Path="tests/Maui.Tizen.Sample.RefPackCompile/Maui.Tizen.Sample.RefPackCompile.csproj" />
3334
<Project Path="tests/Maui.Tizen.Controls.RefPackCompile/Maui.Tizen.Controls.RefPackCompile.csproj" />
3435
<Project Path="tests/Controls.UnitTests/Maui.Tizen.Controls.UnitTests.csproj" />
3536
<Project Path="eng/tests/PackageGraphProbe/PackageGraphProbe.csproj" />
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env bash
2+
#
3+
# compatibility-collision-probe.sh — reproducible evidence for the Maui.Tizen.Compatibility
4+
# capability decision recorded in /docs/tizen-maps-compatibility-status.md.
5+
#
6+
# Claim being verified: none of the compatibility-renderer base infrastructure
7+
# (VisualElementRenderer<TElement>, ViewHandlerDelegator<TElement>, ItemTemplateAdaptor)
8+
# exists in the REAL, published, plain-net11.0 Microsoft.Maui.Controls.Core package. Unlike
9+
# Maps (see maps-neutral-stub-probe.sh), there is no neutral "*.Standard.cs" fallback for
10+
# these types - they only ever existed inside a platform-specific compilation upstream. That
11+
# means a Tizen ListView/TableView/Frame compatibility renderer cannot be built today against
12+
# any public net11 contract: it would need Maui.Tizen.Controls's own Tizen-specific
13+
# ItemTemplateAdaptor (src/Maui.Tizen.Controls/Core/Handlers/Items/Tizen/ItemTemplateAdaptor.cs)
14+
# and a from-scratch VisualElementRenderer/ViewHandlerDelegator, none of which compile yet
15+
# (Phase 2, not started - see docs/migration.md). This corroborates
16+
# src/Maui.Tizen.Compatibility/README.md's own "provisional, likely deleted" assessment with
17+
# concrete, reproducible evidence rather than leaving it as an unverified guess.
18+
#
19+
# This builds a throwaway console app against the exact package version pinned in
20+
# /Directory.Packages.props, using this repository's own /nuget.config. Plain net11.0 only -
21+
# no Tizen workload involved.
22+
23+
set -euo pipefail
24+
25+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
26+
WORKDIR="$(mktemp -d)"
27+
trap 'rm -rf "$WORKDIR"' EXIT
28+
29+
MAUI_VERSION="$(python3 -c "
30+
import re
31+
text = open('$REPO_ROOT/Directory.Packages.props').read()
32+
m = re.search(r'Microsoft\.Maui\.Controls\.Core\" Version=\"([^\"]+)\"', text)
33+
print(m.group(1))
34+
")"
35+
36+
echo "==> Probing Microsoft.Maui.Controls.Core $MAUI_VERSION (plain net11.0, no Tizen involved)"
37+
38+
cp "$REPO_ROOT/nuget.config" "$WORKDIR/nuget.config"
39+
40+
cat > "$WORKDIR/probe.csproj" <<EOF
41+
<Project Sdk="Microsoft.NET.Sdk">
42+
<PropertyGroup>
43+
<OutputType>Exe</OutputType>
44+
<TargetFramework>net11.0</TargetFramework>
45+
<Nullable>enable</Nullable>
46+
</PropertyGroup>
47+
<ItemGroup>
48+
<PackageReference Include="Microsoft.Maui.Controls.Core" Version="$MAUI_VERSION" />
49+
<PackageReference Include="Microsoft.Maui.Core" Version="$MAUI_VERSION" />
50+
</ItemGroup>
51+
</Project>
52+
EOF
53+
54+
cat > "$WORKDIR/Program.cs" <<'EOF'
55+
using System;
56+
57+
int failures = 0;
58+
59+
void CheckAbsent(string assemblyQualifiedName, string label)
60+
{
61+
var t = Type.GetType(assemblyQualifiedName);
62+
if (t is null)
63+
{
64+
Console.WriteLine($"CONFIRMED ABSENT: {label}");
65+
}
66+
else
67+
{
68+
Console.Error.WriteLine($"FAIL: {label} unexpectedly FOUND ({t}) - re-evaluate the capability decision, a neutral fallback may now exist.");
69+
failures++;
70+
}
71+
}
72+
73+
void CheckPresent(string assemblyQualifiedName, string label)
74+
{
75+
var t = Type.GetType(assemblyQualifiedName);
76+
if (t is not null)
77+
{
78+
Console.WriteLine($"present (as expected, just a sanity check the assembly loaded): {label} -> {t}");
79+
}
80+
else
81+
{
82+
Console.Error.WriteLine($"FAIL: {label} not found - the probe itself may be broken (assembly failed to load).");
83+
failures++;
84+
}
85+
}
86+
87+
// The controls (bindable objects) DO exist in the neutral assembly - only the renderer/
88+
// adaptor infrastructure that would make them actually draw anything is missing. These are
89+
// sanity checks that the probe is loading the right assembly at all.
90+
CheckPresent("Microsoft.Maui.Controls.ListView, Microsoft.Maui.Controls", "ListView (control)");
91+
CheckPresent("Microsoft.Maui.Controls.TableView, Microsoft.Maui.Controls", "TableView (control)");
92+
CheckPresent("Microsoft.Maui.Controls.Frame, Microsoft.Maui.Controls", "Frame (control)");
93+
94+
// The renderer/adaptor infrastructure a Tizen compatibility renderer would need to derive
95+
// from or implement against. None of these have a neutral/no-platform fallback upstream.
96+
CheckAbsent("Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer`1, Microsoft.Maui.Controls", "VisualElementRenderer<TElement>");
97+
CheckAbsent("Microsoft.Maui.Controls.Handlers.Compatibility.ViewHandlerDelegator`1, Microsoft.Maui.Controls", "ViewHandlerDelegator<TElement> (internal)");
98+
CheckAbsent("Microsoft.Maui.Controls.Handlers.Items.ItemTemplateAdaptor, Microsoft.Maui.Controls", "ItemTemplateAdaptor");
99+
100+
Environment.Exit(failures > 0 ? 1 : 0);
101+
EOF
102+
103+
(cd "$WORKDIR" && dotnet run --project probe.csproj)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env bash
2+
#
3+
# maps-neutral-stub-probe.sh — reproducible evidence for the Maui.Tizen.Maps capability
4+
# decision recorded in /docs/tizen-maps-compatibility-status.md.
5+
#
6+
# Claim being verified: the REAL, published Microsoft.Maui.Maps / Microsoft.Maui.Controls.Maps
7+
# packages already register a Map -> MapHandler handler whose behavior - CreatePlatformView()
8+
# and every property mapper throwing NotImplementedException - is *identical* to what
9+
# dotnet/maui's own Tizen-specific MapHandler.Tizen.cs partial would have produced, because
10+
# neither ever implemented real map rendering. If true, Maui.Tizen.Maps has nothing to add:
11+
# shipping a duplicate MapHandler would only create a type-identity collision with the
12+
# neutral assembly's own MapHandler for zero behavioral benefit.
13+
#
14+
# This builds and RUNS a throwaway console app against the exact package versions pinned in
15+
# /Directory.Packages.props, using this repository's own /nuget.config (so it resolves from
16+
# the same dotnet11 dev feed the rest of the repository does). Nothing here touches or
17+
# depends on the Tizen workload - this targets plain net11.0, which is what a
18+
# net11.0-tizen11.0 project's PackageReference falls back to today, since none of these
19+
# packages publish a net11.0-tizen11.0 asset.
20+
#
21+
# Usage: eng or docs consumer runs this directly; requires network access to the dotnet11
22+
# feed and the .NET 11 preview SDK pinned in /global.json.
23+
24+
set -euo pipefail
25+
26+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
27+
WORKDIR="$(mktemp -d)"
28+
trap 'rm -rf "$WORKDIR"' EXIT
29+
30+
MAUI_VERSION="$(python3 -c "
31+
import re
32+
text = open('$REPO_ROOT/Directory.Packages.props').read()
33+
m = re.search(r'Microsoft\.Maui\.Maps\" Version=\"([^\"]+)\"', text)
34+
print(m.group(1))
35+
")"
36+
37+
echo "==> Probing Microsoft.Maui.Maps / Microsoft.Maui.Controls.Maps $MAUI_VERSION (plain net11.0, no Tizen involved)"
38+
39+
cp "$REPO_ROOT/nuget.config" "$WORKDIR/nuget.config"
40+
41+
cat > "$WORKDIR/probe.csproj" <<EOF
42+
<Project Sdk="Microsoft.NET.Sdk">
43+
<PropertyGroup>
44+
<OutputType>Exe</OutputType>
45+
<TargetFramework>net11.0</TargetFramework>
46+
<Nullable>enable</Nullable>
47+
</PropertyGroup>
48+
<ItemGroup>
49+
<PackageReference Include="Microsoft.Maui.Controls.Maps" Version="$MAUI_VERSION" />
50+
<PackageReference Include="Microsoft.Maui.Controls.Core" Version="$MAUI_VERSION" />
51+
<PackageReference Include="Microsoft.Maui.Core" Version="$MAUI_VERSION" />
52+
<PackageReference Include="Microsoft.Maui.Maps" Version="$MAUI_VERSION" />
53+
</ItemGroup>
54+
</Project>
55+
EOF
56+
57+
cat > "$WORKDIR/Program.cs" <<'EOF'
58+
using Microsoft.Maui;
59+
using Microsoft.Maui.Controls.Maps;
60+
using Microsoft.Maui.Controls.Hosting;
61+
using Microsoft.Maui.Hosting;
62+
using Microsoft.Maui.Maps.Handlers;
63+
using Microsoft.Extensions.DependencyInjection;
64+
using System;
65+
using System.Reflection;
66+
67+
var builder = MauiApp.CreateBuilder();
68+
builder.UseMauiMaps();
69+
var app = builder.Build();
70+
var factory = app.Services.GetRequiredService<IMauiHandlersFactory>();
71+
var handlerType = factory.GetHandlerType(typeof(Map));
72+
73+
Console.WriteLine($"Map is registered to handler: {handlerType}");
74+
Console.WriteLine($" from assembly: {handlerType?.Assembly.FullName}");
75+
76+
if (handlerType != typeof(MapHandler))
77+
{
78+
Console.Error.WriteLine("FAIL: Map is not registered to Microsoft.Maui.Maps.Handlers.MapHandler.");
79+
Environment.Exit(1);
80+
}
81+
82+
var handler = new MapHandler();
83+
var createPlatformView = typeof(MapHandler).GetMethod("CreatePlatformView", BindingFlags.NonPublic | BindingFlags.Instance)!;
84+
85+
try
86+
{
87+
createPlatformView.Invoke(handler, null);
88+
Console.Error.WriteLine("FAIL: CreatePlatformView() did not throw - upstream behavior may have changed; re-evaluate the capability decision.");
89+
Environment.Exit(1);
90+
}
91+
catch (TargetInvocationException ex) when (ex.InnerException is NotImplementedException)
92+
{
93+
Console.WriteLine("CONFIRMED: MapHandler.CreatePlatformView() throws NotImplementedException,");
94+
Console.WriteLine(" matching dotnet/maui's own historical Tizen (non-)implementation.");
95+
}
96+
EOF
97+
98+
(cd "$WORKDIR" && dotnet run --project probe.csproj)

docs/net11-status.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,35 @@ centrally configurable in [`eng/Maui.props`](../eng/Maui.props).
1212

1313
The product assembly targets `net11.0-tizen11.0` only. It **cannot be restored or built anywhere**
1414
until Samsung publishes the 11.0.100 workload manifest (see blocker B1). Rather than weaken that
15-
contract with a neutral fallback, verification is done by two projects that compile the *same*
16-
sources.
15+
contract with a neutral fallback, verification is split across two **complementary** lanes. They do
16+
not compile identical sets - that would be impossible, since the platform sources need real TizenFX:
17+
18+
| Lane | Compiles | Assembly | `TIZEN` |
19+
| --- | --- | --- | --- |
20+
| `Maui.Tizen.Core.UnitTests` | portable + handler | test host | no |
21+
| `Maui.Tizen.Core.RefPackCompile` | portable + handler + platform | `Maui.Tizen.Core` | yes |
22+
| `Maui.Tizen.Sample.RefPackCompile` | sample only, references the above | `Maui.Tizen.Sample` | yes |
23+
| `Maui.Tizen.Core` (product) | portable + handler + platform | `Maui.Tizen.Core` | yes |
24+
25+
Between them every owned source is compiled by at least one lane, and everything the product
26+
compiles is also compiled by the ref-pack lane. `SourceLaneCoverageTests` pins that invariant.
27+
28+
The sample gets its **own** lane rather than being folded into the backend's, and that separation is
29+
load-bearing in two ways an MSBuild review had to point out:
30+
31+
* The sample must cross a real assembly boundary. Compiled into the backend lane it produced one
32+
merged Core+sample assembly, so a sample that reached for a backend internal - or for anything
33+
invisible across a package reference - compiled clean. It now reaches the backend through a
34+
`ProjectReference` to an assembly carrying the real product `AssemblyName`.
35+
* PublicAPI ownership is only meaningful while each compilation is checked against its own baseline.
36+
With both pairs attached to one merged surface, moving `TizenFlyoutView` out of the backend
37+
baseline and into the *sample's* still built successfully. It now fails RS0016.
38+
39+
The real `samples/Maui.Tizen.Sample` separately evaluated `Compile=[]` - `TizenPackage.props`
40+
defaults `EnableDefaultCompileItems` to false for the not-yet-ported projects and the sample never
41+
opted back in, so it was an application head that built successfully while containing no code.
42+
`PackageBoundaryTests` asserts the evaluated item lists of the real sample and its lane are
43+
identical, so neither can drift from the other.
1744

1845
| Lane | Command | What it proves |
1946
| --- | --- | --- |

docs/tizen-gesture-support-matrix.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Measured against **MAUI 11.0.0-preview.7.26426.4**, which contains
2626
| `SwipeGestureRecognizer` | `PanGestureDetector` | `ISwipeGestureController` | ✅ Works |
2727
| `TapGestureRecognizer` | `TapGestureDetector` | `SendTapped` | ✅ Works |
2828
| `PointerGestureRecognizer` | `View.TouchEvent` + `View.HoverEvent` | `SendPointerEntered` / `Exited` / `Moved` / `Pressed` / `Released` | ✅ Works |
29-
| `LongPressGestureRecognizer` | `LongPressGestureDetector` |`SendLongPressed` / `SendLongPressing` still internal | ⚠️ Blocked on MAUI |
29+
| `LongPressGestureRecognizer` | `LongPressGestureDetector` |`SendLongPressed` / `SendLongPressing` still internal [#37861](https://github.com/dotnet/maui/pull/37861) open | ⚠️ Blocked on MAUI |
3030
| `DragGestureRecognizer` | ❌ no view-level NUI equivalent | `SendDragStarting` / `SendDropCompleted` | ❌ Not supported (detection) |
3131
| `DropGestureRecognizer` | ❌ no view-level NUI equivalent | `SendDragOver` / `SendDragLeave` / `SendDrop` | ❌ Not supported (detection) |
3232

@@ -181,7 +181,36 @@ non-matching counts are ignored, matching the original backend. Dispatched throu
181181

182182
`LongPressGestureDetector` supports the touch count only.
183183

184-
> **`MinimumPressDuration` is not honoured on Tizen.** `Tizen.NUI.LongPressGestureDetector`
184+
Detection is complete and the state machine is implemented and tested; only the final dispatch
185+
call is blocked. [dotnet/maui#37861](https://github.com/dotnet/maui/pull/37861) makes
186+
`SendLongPressed` and `SendLongPressing` public and is code/CI complete but **not yet merged**, so
187+
it is not in the pinned package.
188+
189+
**The mapping is already specified, so adoption is a small change rather than a fresh
190+
translation.** It follows iOS, which is the reference behaviour:
191+
192+
| Native Tizen state | `GestureStatus` | Events raised |
193+
|---|---|---|
194+
| `Started` | `Started` | `LongPressing` |
195+
| `Continuing` | `Running` | `LongPressing` |
196+
| `Finished` | `Completed` | `LongPressed` **first**, then `LongPressing` |
197+
| `Cancelled` | `Canceled` | `LongPressing` only — never `LongPressed`, never the command |
198+
199+
> **Do not copy the in-box Tizen handler when adopting.** `LongPressGestureHandler.cs` on
200+
> dotnet/maui `net11.0` has **no `Continuing` branch at all**, so a Tizen long press never reports
201+
> `GestureStatus.Running` and an app tracking the gesture sees `Started` jump straight to
202+
> `Completed`. iOS maps its equivalent (`UIGestureRecognizerState.Changed`) to `Running`. This
203+
> backend follows iOS.
204+
>
205+
> `TizenGestureDispatcher.ToLongPressStatus` and `CompletesLongPress` encode the table above and
206+
> are unit tested, including a test named for this specific gap. Reintroducing it fails four tests
207+
> across both the mapping and the handler's ordering.
208+
209+
A canceled press reports a status change but is **not** a press, so it must never raise
210+
`LongPressed` or run the recognizer's `Command` — that would run the app's handler for a gesture
211+
the user aborted.
212+
213+
> **`MinimumPressDuration` is not honourable on Tizen.** `Tizen.NUI.LongPressGestureDetector`
185214
> exposes no minimum-holding-time API — only `SetTouchesRequired`. The system-wide long-press
186215
> duration applies instead.
187216
>
@@ -272,7 +301,7 @@ factory can therefore refine this table without changing any other code.
272301
|---|---|
273302
| Gesture translation (totals, scaling, gesture identity, tap counts, pointer mapping) | `tests/Controls.UnitTests/TizenGestureTranslationTests.cs` |
274303
| Manager and detector lifecycle (attach, detach, enable, dispose, collection changes) | `tests/Controls.UnitTests/TizenGesturePlatformManagerTests.cs` |
275-
| Dispatch through real MAUI recognizers, screen/local/unknown position resolution, button masks, and the one blocked gesture | `tests/Controls.UnitTests/TizenGestureDispatcherTests.cs` |
304+
| Dispatch through real MAUI recognizers, screen/local/unknown position resolution, button masks, long-press status mapping, and the one blocked gesture | `tests/Controls.UnitTests/TizenGestureDispatcherTests.cs` |
276305
| Pixel scaler registration and lazy display-factor lookup | `tests/Controls.UnitTests/TizenServiceRegistrationTests.cs` |
277306
| DI registration and lifetimes | `tests/Controls.UnitTests/TizenServiceRegistrationTests.cs` |
278307
| NUI adapters under `Core/Platform/Nui` | Type-checked against `Samsung.Tizen.Ref.API15` and `Tizen.UIExtensions.NUI` 0.9.2 by `tests/Maui.Tizen.Controls.RefPackCompile`; behaviour needs a device |

0 commit comments

Comments
 (0)