Fix compilation: restore clean RC1 codebase
- Restore clean BindableProperty.Create syntax from RC1 commit - Remove decompiler artifacts with mangled delegate types - Add Svg.Skia package reference for icon support - Fix duplicate type definitions - Library now compiles successfully (0 errors) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Animations;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
public class GtkMauiContext : IMauiContext
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
private readonly IMauiHandlersFactory _handlers;
|
||||
|
||||
private IAnimationManager? _animationManager;
|
||||
|
||||
private IDispatcher? _dispatcher;
|
||||
|
||||
public IServiceProvider Services => _services;
|
||||
|
||||
public IMauiHandlersFactory Handlers => _handlers;
|
||||
|
||||
public IAnimationManager AnimationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_animationManager == null)
|
||||
{
|
||||
_animationManager = (IAnimationManager?)(((object)_services.GetService<IAnimationManager>()) ?? ((object)new LinuxAnimationManager((ITicker)(object)new LinuxTicker())));
|
||||
}
|
||||
return _animationManager;
|
||||
}
|
||||
}
|
||||
|
||||
public IDispatcher Dispatcher
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dispatcher == null)
|
||||
{
|
||||
_dispatcher = (IDispatcher?)(((object)_services.GetService<IDispatcher>()) ?? ((object)new LinuxDispatcher()));
|
||||
}
|
||||
return _dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
public GtkMauiContext(IServiceProvider services)
|
||||
{
|
||||
_services = services ?? throw new ArgumentNullException("services");
|
||||
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
|
||||
if (LinuxApplication.Current == null)
|
||||
{
|
||||
new LinuxApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using Microsoft.Maui.Hosting;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
public static class HandlerMappingExtensions
|
||||
{
|
||||
public static IMauiHandlersCollection AddHandler<TView, THandler>(this IMauiHandlersCollection handlers) where TView : class where THandler : class
|
||||
{
|
||||
MauiHandlersCollectionExtensions.AddHandler(handlers, typeof(TView), typeof(THandler));
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Maui.Animations;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
internal class LinuxAnimationManager : IAnimationManager
|
||||
{
|
||||
private readonly List<Animation> _animations = new List<Animation>();
|
||||
|
||||
private readonly ITicker _ticker;
|
||||
|
||||
public double SpeedModifier { get; set; } = 1.0;
|
||||
|
||||
public bool AutoStartTicker { get; set; } = true;
|
||||
|
||||
public ITicker Ticker => _ticker;
|
||||
|
||||
public LinuxAnimationManager(ITicker ticker)
|
||||
{
|
||||
_ticker = ticker;
|
||||
_ticker.Fire = OnTickerFire;
|
||||
}
|
||||
|
||||
public void Add(Animation animation)
|
||||
{
|
||||
_animations.Add(animation);
|
||||
if (AutoStartTicker && !_ticker.IsRunning)
|
||||
{
|
||||
_ticker.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(Animation animation)
|
||||
{
|
||||
_animations.Remove(animation);
|
||||
if (_animations.Count == 0 && _ticker.IsRunning)
|
||||
{
|
||||
_ticker.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTickerFire()
|
||||
{
|
||||
Animation[] array = _animations.ToArray();
|
||||
foreach (Animation val in array)
|
||||
{
|
||||
val.Tick(0.016 * SpeedModifier);
|
||||
if (val.HasFinished)
|
||||
{
|
||||
Remove(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
internal class LinuxDispatcher : IDispatcher
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
|
||||
private readonly Queue<Action> _queue = new Queue<Action>();
|
||||
|
||||
private bool _isDispatching;
|
||||
|
||||
public bool IsDispatchRequired => false;
|
||||
|
||||
public IDispatcherTimer CreateTimer()
|
||||
{
|
||||
return (IDispatcherTimer)(object)new LinuxDispatcherTimer();
|
||||
}
|
||||
|
||||
public bool Dispatch(Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lock (_lock)
|
||||
{
|
||||
_queue.Enqueue(action);
|
||||
}
|
||||
ProcessQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DispatchDelayed(TimeSpan delay, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Task.Delay(delay).ContinueWith((Task _) => Dispatch(action));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
if (_isDispatching)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDispatching = true;
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Action action;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_queue.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
action = _queue.Dequeue();
|
||||
}
|
||||
action?.Invoke();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDispatching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
internal class LinuxDispatcherTimer : IDispatcherTimer
|
||||
{
|
||||
private Timer? _timer;
|
||||
|
||||
private TimeSpan _interval = TimeSpan.FromMilliseconds(16L, 0L);
|
||||
|
||||
private bool _isRunning;
|
||||
|
||||
private bool _isRepeating = true;
|
||||
|
||||
public TimeSpan Interval
|
||||
{
|
||||
get
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
set
|
||||
{
|
||||
_interval = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public bool IsRepeating
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isRepeating;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isRepeating = value;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler? Tick;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
_isRunning = true;
|
||||
_timer = new Timer(OnTimerCallback, null, _interval, _isRepeating ? _interval : Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
private void OnTimerCallback(object? state)
|
||||
{
|
||||
this.Tick?.Invoke(this, EventArgs.Empty);
|
||||
if (!_isRepeating)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1,153 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Maui.ApplicationModel;
|
||||
using Microsoft.Maui.ApplicationModel.Communication;
|
||||
using Microsoft.Maui.ApplicationModel.DataTransfer;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Devices;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Networking;
|
||||
using Microsoft.Maui.Platform.Linux.Converters;
|
||||
using Microsoft.Maui.Platform.Linux.Dispatching;
|
||||
using Microsoft.Maui.Platform.Linux.Handlers;
|
||||
using Microsoft.Maui.Platform.Linux.Services;
|
||||
using Microsoft.Maui.Platform.Linux.Converters;
|
||||
using Microsoft.Maui.Storage;
|
||||
using Microsoft.Maui.Platform.Linux.Handlers;
|
||||
using Microsoft.Maui.Controls;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for configuring MAUI applications for Linux.
|
||||
/// </summary>
|
||||
public static class LinuxMauiAppBuilderExtensions
|
||||
{
|
||||
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder)
|
||||
{
|
||||
return builder.UseLinux(null);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configures the MAUI application to run on Linux.
|
||||
/// </summary>
|
||||
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder)
|
||||
{
|
||||
return builder.UseLinux(configure: null);
|
||||
}
|
||||
|
||||
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder, Action<LinuxApplicationOptions>? configure)
|
||||
{
|
||||
LinuxApplicationOptions linuxApplicationOptions = new LinuxApplicationOptions();
|
||||
configure?.Invoke(linuxApplicationOptions);
|
||||
builder.Services.TryAddSingleton<IDispatcherProvider>((IDispatcherProvider)(object)LinuxDispatcherProvider.Instance);
|
||||
builder.Services.TryAddSingleton<IDeviceInfo>((IDeviceInfo)(object)DeviceInfoService.Instance);
|
||||
builder.Services.TryAddSingleton<IDeviceDisplay>((IDeviceDisplay)(object)DeviceDisplayService.Instance);
|
||||
builder.Services.TryAddSingleton<IAppInfo>((IAppInfo)(object)AppInfoService.Instance);
|
||||
builder.Services.TryAddSingleton<IConnectivity>((IConnectivity)(object)ConnectivityService.Instance);
|
||||
builder.Services.TryAddSingleton<ILauncher, LauncherService>();
|
||||
builder.Services.TryAddSingleton<IPreferences, PreferencesService>();
|
||||
builder.Services.TryAddSingleton<IFilePicker, FilePickerService>();
|
||||
builder.Services.TryAddSingleton<IClipboard, ClipboardService>();
|
||||
builder.Services.TryAddSingleton<IShare, ShareService>();
|
||||
builder.Services.TryAddSingleton<ISecureStorage, SecureStorageService>();
|
||||
builder.Services.TryAddSingleton<IVersionTracking, VersionTrackingService>();
|
||||
builder.Services.TryAddSingleton<IAppActions, AppActionsService>();
|
||||
builder.Services.TryAddSingleton<IBrowser, BrowserService>();
|
||||
builder.Services.TryAddSingleton<IEmail, EmailService>();
|
||||
builder.Services.TryAddSingleton((IServiceProvider _) => GtkHostService.Instance);
|
||||
RegisterTypeConverters();
|
||||
HandlerMauiAppBuilderExtensions.ConfigureMauiHandlers(builder, (Action<IMauiHandlersCollection>)delegate(IMauiHandlersCollection handlers)
|
||||
{
|
||||
handlers.AddHandler<IApplication, ApplicationHandler>();
|
||||
handlers.AddHandler<BoxView, BoxViewHandler>();
|
||||
handlers.AddHandler<Button, TextButtonHandler>();
|
||||
handlers.AddHandler<Label, LabelHandler>();
|
||||
handlers.AddHandler<Entry, EntryHandler>();
|
||||
handlers.AddHandler<Editor, EditorHandler>();
|
||||
handlers.AddHandler<CheckBox, CheckBoxHandler>();
|
||||
handlers.AddHandler<Switch, SwitchHandler>();
|
||||
handlers.AddHandler<Slider, SliderHandler>();
|
||||
handlers.AddHandler<Stepper, StepperHandler>();
|
||||
handlers.AddHandler<RadioButton, RadioButtonHandler>();
|
||||
handlers.AddHandler<Grid, GridHandler>();
|
||||
handlers.AddHandler<StackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<VerticalStackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<HorizontalStackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<AbsoluteLayout, LayoutHandler>();
|
||||
handlers.AddHandler<FlexLayout, LayoutHandler>();
|
||||
handlers.AddHandler<ScrollView, ScrollViewHandler>();
|
||||
handlers.AddHandler<Frame, FrameHandler>();
|
||||
handlers.AddHandler<Border, BorderHandler>();
|
||||
handlers.AddHandler<ContentView, BorderHandler>();
|
||||
handlers.AddHandler<Picker, PickerHandler>();
|
||||
handlers.AddHandler<DatePicker, DatePickerHandler>();
|
||||
handlers.AddHandler<TimePicker, TimePickerHandler>();
|
||||
handlers.AddHandler<SearchBar, SearchBarHandler>();
|
||||
handlers.AddHandler<ProgressBar, ProgressBarHandler>();
|
||||
handlers.AddHandler<ActivityIndicator, ActivityIndicatorHandler>();
|
||||
handlers.AddHandler<Image, ImageHandler>();
|
||||
handlers.AddHandler<ImageButton, ImageButtonHandler>();
|
||||
handlers.AddHandler<GraphicsView, GraphicsViewHandler>();
|
||||
handlers.AddHandler<WebView, GtkWebViewHandler>();
|
||||
handlers.AddHandler<CollectionView, CollectionViewHandler>();
|
||||
handlers.AddHandler<ListView, CollectionViewHandler>();
|
||||
handlers.AddHandler<Page, PageHandler>();
|
||||
handlers.AddHandler<ContentPage, ContentPageHandler>();
|
||||
handlers.AddHandler<NavigationPage, NavigationPageHandler>();
|
||||
handlers.AddHandler<Shell, ShellHandler>();
|
||||
handlers.AddHandler<FlyoutPage, FlyoutPageHandler>();
|
||||
handlers.AddHandler<TabbedPage, TabbedPageHandler>();
|
||||
handlers.AddHandler<Application, ApplicationHandler>();
|
||||
handlers.AddHandler<Window, WindowHandler>();
|
||||
});
|
||||
builder.Services.AddSingleton(linuxApplicationOptions);
|
||||
return builder;
|
||||
}
|
||||
/// <summary>
|
||||
/// Configures the MAUI application to run on Linux with options.
|
||||
/// </summary>
|
||||
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder, Action<LinuxApplicationOptions>? configure)
|
||||
{
|
||||
var options = new LinuxApplicationOptions();
|
||||
configure?.Invoke(options);
|
||||
|
||||
private static void RegisterTypeConverters()
|
||||
{
|
||||
TypeDescriptor.AddAttributes(typeof(SKColor), new TypeConverterAttribute(typeof(SKColorTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKRect), new TypeConverterAttribute(typeof(SKRectTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKSize), new TypeConverterAttribute(typeof(SKSizeTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKPoint), new TypeConverterAttribute(typeof(SKPointTypeConverter)));
|
||||
}
|
||||
// Register platform services
|
||||
builder.Services.TryAddSingleton<ILauncher, LauncherService>();
|
||||
builder.Services.TryAddSingleton<IPreferences, PreferencesService>();
|
||||
builder.Services.TryAddSingleton<IFilePicker, FilePickerService>();
|
||||
builder.Services.TryAddSingleton<IClipboard, ClipboardService>();
|
||||
builder.Services.TryAddSingleton<IShare, ShareService>();
|
||||
builder.Services.TryAddSingleton<ISecureStorage, SecureStorageService>();
|
||||
builder.Services.TryAddSingleton<IVersionTracking, VersionTrackingService>();
|
||||
builder.Services.TryAddSingleton<IAppActions, AppActionsService>();
|
||||
builder.Services.TryAddSingleton<IBrowser, BrowserService>();
|
||||
builder.Services.TryAddSingleton<IEmail, EmailService>();
|
||||
|
||||
// Register type converters for XAML support
|
||||
RegisterTypeConverters();
|
||||
|
||||
// Register Linux-specific handlers
|
||||
builder.ConfigureMauiHandlers(handlers =>
|
||||
{
|
||||
// Application handler
|
||||
handlers.AddHandler<IApplication, ApplicationHandler>();
|
||||
|
||||
// Core controls
|
||||
handlers.AddHandler<BoxView, BoxViewHandler>();
|
||||
handlers.AddHandler<Button, TextButtonHandler>();
|
||||
handlers.AddHandler<Label, LabelHandler>();
|
||||
handlers.AddHandler<Entry, EntryHandler>();
|
||||
handlers.AddHandler<Editor, EditorHandler>();
|
||||
handlers.AddHandler<CheckBox, CheckBoxHandler>();
|
||||
handlers.AddHandler<Switch, SwitchHandler>();
|
||||
handlers.AddHandler<Slider, SliderHandler>();
|
||||
handlers.AddHandler<Stepper, StepperHandler>();
|
||||
handlers.AddHandler<RadioButton, RadioButtonHandler>();
|
||||
|
||||
// Layout controls
|
||||
handlers.AddHandler<Grid, GridHandler>();
|
||||
handlers.AddHandler<StackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<VerticalStackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<HorizontalStackLayout, StackLayoutHandler>();
|
||||
handlers.AddHandler<AbsoluteLayout, LayoutHandler>();
|
||||
handlers.AddHandler<FlexLayout, LayoutHandler>();
|
||||
handlers.AddHandler<ScrollView, ScrollViewHandler>();
|
||||
handlers.AddHandler<Frame, FrameHandler>();
|
||||
handlers.AddHandler<Border, BorderHandler>();
|
||||
handlers.AddHandler<ContentView, BorderHandler>();
|
||||
|
||||
// Picker controls
|
||||
handlers.AddHandler<Picker, PickerHandler>();
|
||||
handlers.AddHandler<DatePicker, DatePickerHandler>();
|
||||
handlers.AddHandler<TimePicker, TimePickerHandler>();
|
||||
handlers.AddHandler<SearchBar, SearchBarHandler>();
|
||||
|
||||
// Progress & Activity
|
||||
handlers.AddHandler<ProgressBar, ProgressBarHandler>();
|
||||
handlers.AddHandler<ActivityIndicator, ActivityIndicatorHandler>();
|
||||
|
||||
// Image & Graphics
|
||||
handlers.AddHandler<Image, ImageHandler>();
|
||||
handlers.AddHandler<ImageButton, ImageButtonHandler>();
|
||||
handlers.AddHandler<GraphicsView, GraphicsViewHandler>();
|
||||
|
||||
// Collection Views
|
||||
handlers.AddHandler<CollectionView, CollectionViewHandler>();
|
||||
handlers.AddHandler<ListView, CollectionViewHandler>();
|
||||
|
||||
// Pages & Navigation
|
||||
handlers.AddHandler<Page, PageHandler>();
|
||||
handlers.AddHandler<ContentPage, ContentPageHandler>();
|
||||
handlers.AddHandler<NavigationPage, NavigationPageHandler>();
|
||||
handlers.AddHandler<Shell, ShellHandler>();
|
||||
handlers.AddHandler<FlyoutPage, FlyoutPageHandler>();
|
||||
handlers.AddHandler<TabbedPage, TabbedPageHandler>();
|
||||
|
||||
// Application & Window
|
||||
handlers.AddHandler<Application, ApplicationHandler>();
|
||||
handlers.AddHandler<Microsoft.Maui.Controls.Window, WindowHandler>();
|
||||
});
|
||||
|
||||
// Store options for later use
|
||||
builder.Services.AddSingleton(options);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers custom type converters for Linux platform.
|
||||
/// </summary>
|
||||
private static void RegisterTypeConverters()
|
||||
{
|
||||
// Register SkiaSharp type converters for XAML styling support
|
||||
TypeDescriptor.AddAttributes(typeof(SKColor), new TypeConverterAttribute(typeof(SKColorTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKRect), new TypeConverterAttribute(typeof(SKRectTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKSize), new TypeConverterAttribute(typeof(SKSizeTypeConverter)));
|
||||
TypeDescriptor.AddAttributes(typeof(SKPoint), new TypeConverterAttribute(typeof(SKPointTypeConverter)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler registration extensions.
|
||||
/// </summary>
|
||||
public static class HandlerMappingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a handler for the specified view type.
|
||||
/// </summary>
|
||||
public static IMauiHandlersCollection AddHandler<TView, THandler>(
|
||||
this IMauiHandlersCollection handlers)
|
||||
where TView : class
|
||||
where THandler : class
|
||||
{
|
||||
handlers.AddHandler(typeof(TView), typeof(THandler));
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,299 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Maui.Animations;
|
||||
using Microsoft.Maui.Dispatching;
|
||||
using Microsoft.Maui.Platform;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Linux-specific implementation of IMauiContext.
|
||||
/// Provides the infrastructure for creating handlers and accessing platform services.
|
||||
/// </summary>
|
||||
public class LinuxMauiContext : IMauiContext
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IMauiHandlersFactory _handlers;
|
||||
private readonly LinuxApplication _linuxApp;
|
||||
private IAnimationManager? _animationManager;
|
||||
private IDispatcher? _dispatcher;
|
||||
|
||||
private readonly IMauiHandlersFactory _handlers;
|
||||
public LinuxMauiContext(IServiceProvider services, LinuxApplication linuxApp)
|
||||
{
|
||||
_services = services ?? throw new ArgumentNullException(nameof(services));
|
||||
_linuxApp = linuxApp ?? throw new ArgumentNullException(nameof(linuxApp));
|
||||
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
|
||||
}
|
||||
|
||||
private readonly LinuxApplication _linuxApp;
|
||||
/// <inheritdoc />
|
||||
public IServiceProvider Services => _services;
|
||||
|
||||
private IAnimationManager? _animationManager;
|
||||
/// <inheritdoc />
|
||||
public IMauiHandlersFactory Handlers => _handlers;
|
||||
|
||||
private IDispatcher? _dispatcher;
|
||||
/// <summary>
|
||||
/// Gets the Linux application instance.
|
||||
/// </summary>
|
||||
public LinuxApplication LinuxApp => _linuxApp;
|
||||
|
||||
public IServiceProvider Services => _services;
|
||||
/// <summary>
|
||||
/// Gets the animation manager.
|
||||
/// </summary>
|
||||
public IAnimationManager AnimationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
_animationManager ??= _services.GetService<IAnimationManager>()
|
||||
?? new LinuxAnimationManager(new LinuxTicker());
|
||||
return _animationManager;
|
||||
}
|
||||
}
|
||||
|
||||
public IMauiHandlersFactory Handlers => _handlers;
|
||||
|
||||
public LinuxApplication LinuxApp => _linuxApp;
|
||||
|
||||
public IAnimationManager AnimationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_animationManager == null)
|
||||
{
|
||||
_animationManager = (IAnimationManager?)(((object)_services.GetService<IAnimationManager>()) ?? ((object)new LinuxAnimationManager((ITicker)(object)new LinuxTicker())));
|
||||
}
|
||||
return _animationManager;
|
||||
}
|
||||
}
|
||||
|
||||
public IDispatcher Dispatcher
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dispatcher == null)
|
||||
{
|
||||
_dispatcher = (IDispatcher?)(((object)_services.GetService<IDispatcher>()) ?? ((object)new LinuxDispatcher()));
|
||||
}
|
||||
return _dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxMauiContext(IServiceProvider services, LinuxApplication linuxApp)
|
||||
{
|
||||
_services = services ?? throw new ArgumentNullException("services");
|
||||
_linuxApp = linuxApp ?? throw new ArgumentNullException("linuxApp");
|
||||
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the dispatcher for UI thread operations.
|
||||
/// </summary>
|
||||
public IDispatcher Dispatcher
|
||||
{
|
||||
get
|
||||
{
|
||||
_dispatcher ??= _services.GetService<IDispatcher>()
|
||||
?? new LinuxDispatcher();
|
||||
return _dispatcher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scoped MAUI context for a specific window or view hierarchy.
|
||||
/// </summary>
|
||||
public class ScopedLinuxMauiContext : IMauiContext
|
||||
{
|
||||
private readonly LinuxMauiContext _parent;
|
||||
|
||||
public ScopedLinuxMauiContext(LinuxMauiContext parent)
|
||||
{
|
||||
_parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
}
|
||||
|
||||
public IServiceProvider Services => _parent.Services;
|
||||
public IMauiHandlersFactory Handlers => _parent.Handlers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linux dispatcher for UI thread operations.
|
||||
/// </summary>
|
||||
internal class LinuxDispatcher : IDispatcher
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly Queue<Action> _queue = new();
|
||||
private bool _isDispatching;
|
||||
|
||||
public bool IsDispatchRequired => false; // Linux uses single-threaded event loop
|
||||
|
||||
public IDispatcherTimer CreateTimer()
|
||||
{
|
||||
return new LinuxDispatcherTimer();
|
||||
}
|
||||
|
||||
public bool Dispatch(Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return false;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_queue.Enqueue(action);
|
||||
}
|
||||
|
||||
ProcessQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DispatchDelayed(TimeSpan delay, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return false;
|
||||
|
||||
Task.Delay(delay).ContinueWith(_ => Dispatch(action));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
if (_isDispatching)
|
||||
return;
|
||||
|
||||
_isDispatching = true;
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Action? action;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_queue.Count == 0)
|
||||
break;
|
||||
action = _queue.Dequeue();
|
||||
}
|
||||
action?.Invoke();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDispatching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linux dispatcher timer implementation.
|
||||
/// </summary>
|
||||
internal class LinuxDispatcherTimer : IDispatcherTimer
|
||||
{
|
||||
private Timer? _timer;
|
||||
private TimeSpan _interval = TimeSpan.FromMilliseconds(16); // ~60fps default
|
||||
private bool _isRunning;
|
||||
private bool _isRepeating = true;
|
||||
|
||||
public TimeSpan Interval
|
||||
{
|
||||
get => _interval;
|
||||
set => _interval = value;
|
||||
}
|
||||
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public bool IsRepeating
|
||||
{
|
||||
get => _isRepeating;
|
||||
set => _isRepeating = value;
|
||||
}
|
||||
|
||||
public event EventHandler? Tick;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_isRunning)
|
||||
return;
|
||||
|
||||
_isRunning = true;
|
||||
_timer = new Timer(OnTimerCallback, null, _interval, _isRepeating ? _interval : Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
private void OnTimerCallback(object? state)
|
||||
{
|
||||
Tick?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
if (!_isRepeating)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linux animation manager.
|
||||
/// </summary>
|
||||
internal class LinuxAnimationManager : IAnimationManager
|
||||
{
|
||||
private readonly List<Microsoft.Maui.Animations.Animation> _animations = new();
|
||||
private readonly ITicker _ticker;
|
||||
|
||||
public LinuxAnimationManager(ITicker ticker)
|
||||
{
|
||||
_ticker = ticker;
|
||||
_ticker.Fire = OnTickerFire;
|
||||
}
|
||||
|
||||
public double SpeedModifier { get; set; } = 1.0;
|
||||
public bool AutoStartTicker { get; set; } = true;
|
||||
|
||||
public ITicker Ticker => _ticker;
|
||||
|
||||
public void Add(Microsoft.Maui.Animations.Animation animation)
|
||||
{
|
||||
_animations.Add(animation);
|
||||
|
||||
if (AutoStartTicker && !_ticker.IsRunning)
|
||||
{
|
||||
_ticker.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(Microsoft.Maui.Animations.Animation animation)
|
||||
{
|
||||
_animations.Remove(animation);
|
||||
|
||||
if (_animations.Count == 0 && _ticker.IsRunning)
|
||||
{
|
||||
_ticker.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTickerFire()
|
||||
{
|
||||
var animations = _animations.ToArray();
|
||||
foreach (var animation in animations)
|
||||
{
|
||||
animation.Tick(16.0 / 1000.0 * SpeedModifier); // ~60fps
|
||||
if (animation.HasFinished)
|
||||
{
|
||||
Remove(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Linux ticker for animation timing.
|
||||
/// </summary>
|
||||
internal class LinuxTicker : ITicker
|
||||
{
|
||||
private Timer? _timer;
|
||||
private bool _isRunning;
|
||||
private int _maxFps = 60;
|
||||
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public bool SystemEnabled => true;
|
||||
|
||||
public int MaxFps
|
||||
{
|
||||
get => _maxFps;
|
||||
set => _maxFps = Math.Max(1, Math.Min(120, value));
|
||||
}
|
||||
|
||||
public Action? Fire { get; set; }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_isRunning)
|
||||
return;
|
||||
|
||||
_isRunning = true;
|
||||
var interval = TimeSpan.FromMilliseconds(1000.0 / _maxFps);
|
||||
_timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, interval);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
private void OnTimerCallback(object? state)
|
||||
{
|
||||
Fire?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Maui.Animations;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
internal class LinuxTicker : ITicker
|
||||
{
|
||||
private Timer? _timer;
|
||||
|
||||
private bool _isRunning;
|
||||
|
||||
private int _maxFps = 60;
|
||||
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public bool SystemEnabled => true;
|
||||
|
||||
public int MaxFps
|
||||
{
|
||||
get
|
||||
{
|
||||
return _maxFps;
|
||||
}
|
||||
set
|
||||
{
|
||||
_maxFps = Math.Max(1, Math.Min(120, value));
|
||||
}
|
||||
}
|
||||
|
||||
public Action? Fire { get; set; }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (!_isRunning)
|
||||
{
|
||||
_isRunning = true;
|
||||
TimeSpan period = TimeSpan.FromMilliseconds(1000.0 / (double)_maxFps);
|
||||
_timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, period);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
private void OnTimerCallback(object? state)
|
||||
{
|
||||
Fire?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -1,470 +1,497 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using Microsoft.Maui.Platform;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Renders MAUI views to Skia platform views.
|
||||
/// Handles the conversion of the view hierarchy.
|
||||
/// </summary>
|
||||
public class LinuxViewRenderer
|
||||
{
|
||||
private readonly IMauiContext _mauiContext;
|
||||
private readonly IMauiContext _mauiContext;
|
||||
|
||||
public static Shell? CurrentMauiShell { get; private set; }
|
||||
/// <summary>
|
||||
/// Static reference to the current MAUI Shell for navigation support.
|
||||
/// Used when Shell.Current is not available through normal lifecycle.
|
||||
/// </summary>
|
||||
public static Shell? CurrentMauiShell { get; private set; }
|
||||
|
||||
public static SkiaShell? CurrentSkiaShell { get; private set; }
|
||||
/// <summary>
|
||||
/// Static reference to the current SkiaShell for navigation updates.
|
||||
/// </summary>
|
||||
public static SkiaShell? CurrentSkiaShell { get; private set; }
|
||||
|
||||
public static LinuxViewRenderer? CurrentRenderer { get; set; }
|
||||
/// <summary>
|
||||
/// Navigate to a route using the SkiaShell directly.
|
||||
/// Use this instead of Shell.Current.GoToAsync on Linux.
|
||||
/// </summary>
|
||||
/// <param name="route">The route to navigate to (e.g., "Buttons" or "//Buttons")</param>
|
||||
/// <returns>True if navigation succeeded</returns>
|
||||
public static bool NavigateToRoute(string route)
|
||||
{
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine($"[NavigateToRoute] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool NavigateToRoute(string route)
|
||||
{
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine("[NavigateToRoute] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
string text = route.TrimStart('/');
|
||||
Console.WriteLine("[NavigateToRoute] Navigating to: " + text);
|
||||
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
|
||||
{
|
||||
ShellSection shellSection = CurrentSkiaShell.Sections[i];
|
||||
if (shellSection.Route.Equals(text, StringComparison.OrdinalIgnoreCase) || shellSection.Title.Equals(text, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[NavigateToRoute] Found section {i}: {shellSection.Title}");
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Console.WriteLine("[NavigateToRoute] Route not found: " + text);
|
||||
return false;
|
||||
}
|
||||
// Clean up the route - remove leading // or /
|
||||
var cleanRoute = route.TrimStart('/');
|
||||
Console.WriteLine($"[NavigateToRoute] Navigating to: {cleanRoute}");
|
||||
|
||||
public static bool PushPage(Page page)
|
||||
{
|
||||
Console.WriteLine("[PushPage] Pushing page: " + ((object)page).GetType().Name);
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine("[PushPage] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
if (CurrentRenderer == null)
|
||||
{
|
||||
Console.WriteLine("[PushPage] CurrentRenderer is null");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
SkiaView skiaView = null;
|
||||
ContentPage val = (ContentPage)(object)((page is ContentPage) ? page : null);
|
||||
if (val != null && val.Content != null)
|
||||
{
|
||||
skiaView = CurrentRenderer.RenderView((IView)(object)val.Content);
|
||||
}
|
||||
if (skiaView == null)
|
||||
{
|
||||
Console.WriteLine("[PushPage] Failed to render page content");
|
||||
return false;
|
||||
}
|
||||
if (!(skiaView is SkiaScrollView))
|
||||
{
|
||||
skiaView = new SkiaScrollView
|
||||
{
|
||||
Content = skiaView
|
||||
};
|
||||
}
|
||||
CurrentSkiaShell.PushAsync(skiaView, page.Title ?? "Detail");
|
||||
Console.WriteLine("[PushPage] Successfully pushed page");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[PushPage] Error: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
|
||||
{
|
||||
var section = CurrentSkiaShell.Sections[i];
|
||||
if (section.Route.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase) ||
|
||||
section.Title.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[NavigateToRoute] Found section {i}: {section.Title}");
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PopPage()
|
||||
{
|
||||
Console.WriteLine("[PopPage] Popping page");
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine("[PopPage] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
return CurrentSkiaShell.PopAsync();
|
||||
}
|
||||
Console.WriteLine($"[NavigateToRoute] Route not found: {cleanRoute}");
|
||||
return false;
|
||||
}
|
||||
|
||||
public LinuxViewRenderer(IMauiContext mauiContext)
|
||||
{
|
||||
_mauiContext = mauiContext ?? throw new ArgumentNullException("mauiContext");
|
||||
CurrentRenderer = this;
|
||||
}
|
||||
/// <summary>
|
||||
/// Current renderer instance for page rendering.
|
||||
/// </summary>
|
||||
public static LinuxViewRenderer? CurrentRenderer { get; set; }
|
||||
|
||||
public SkiaView? RenderPage(Page page)
|
||||
{
|
||||
if (page == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Shell val = (Shell)(object)((page is Shell) ? page : null);
|
||||
if (val != null)
|
||||
{
|
||||
return RenderShell(val);
|
||||
}
|
||||
IViewHandler handler = ((VisualElement)page).Handler;
|
||||
if (handler != null)
|
||||
{
|
||||
((IElementHandler)handler).DisconnectHandler();
|
||||
}
|
||||
if (((IElement)(object)page).ToHandler(_mauiContext).PlatformView is SkiaView skiaView)
|
||||
{
|
||||
ContentPage val2 = (ContentPage)(object)((page is ContentPage) ? page : null);
|
||||
if (val2 != null && val2.Content != null)
|
||||
{
|
||||
SkiaView skiaView2 = RenderView((IView)(object)val2.Content);
|
||||
if (skiaView is SkiaPage skiaPage && skiaView2 != null)
|
||||
{
|
||||
skiaPage.Content = skiaView2;
|
||||
}
|
||||
}
|
||||
return skiaView;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Pushes a page onto the navigation stack.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to push</param>
|
||||
/// <returns>True if successful</returns>
|
||||
public static bool PushPage(Page page)
|
||||
{
|
||||
Console.WriteLine($"[PushPage] Pushing page: {page.GetType().Name}");
|
||||
|
||||
private SkiaShell RenderShell(Shell shell)
|
||||
{
|
||||
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0041: Expected I4, but got Unknown
|
||||
CurrentMauiShell = shell;
|
||||
SkiaShell skiaShell = new SkiaShell();
|
||||
skiaShell.Title = ((Page)shell).Title ?? "App";
|
||||
SkiaShell skiaShell2 = skiaShell;
|
||||
FlyoutBehavior flyoutBehavior = shell.FlyoutBehavior;
|
||||
skiaShell2.FlyoutBehavior = (int)flyoutBehavior switch
|
||||
{
|
||||
1 => ShellFlyoutBehavior.Flyout,
|
||||
2 => ShellFlyoutBehavior.Locked,
|
||||
0 => ShellFlyoutBehavior.Disabled,
|
||||
_ => ShellFlyoutBehavior.Flyout,
|
||||
};
|
||||
skiaShell.MauiShell = shell;
|
||||
SkiaShell skiaShell3 = skiaShell;
|
||||
ApplyShellColors(skiaShell3, shell);
|
||||
object flyoutHeader = shell.FlyoutHeader;
|
||||
View val = (View)((flyoutHeader is View) ? flyoutHeader : null);
|
||||
if (val != null)
|
||||
{
|
||||
SkiaView skiaView = RenderView((IView)(object)val);
|
||||
if (skiaView != null)
|
||||
{
|
||||
skiaShell3.FlyoutHeaderView = skiaView;
|
||||
skiaShell3.FlyoutHeaderHeight = (float)((((VisualElement)val).HeightRequest > 0.0) ? ((VisualElement)val).HeightRequest : 140.0);
|
||||
}
|
||||
}
|
||||
Version version = Assembly.GetEntryAssembly()?.GetName().Version;
|
||||
skiaShell3.FlyoutFooterText = $"Version {version?.Major ?? 1}.{version?.Minor ?? 0}.{version?.Build ?? 0}";
|
||||
foreach (ShellItem item in shell.Items)
|
||||
{
|
||||
ProcessShellItem(skiaShell3, item);
|
||||
}
|
||||
CurrentSkiaShell = skiaShell3;
|
||||
skiaShell3.ContentRenderer = CreateShellContentPage;
|
||||
skiaShell3.ColorRefresher = ApplyShellColors;
|
||||
shell.Navigated += OnShellNavigated;
|
||||
shell.Navigating += delegate(object? s, ShellNavigatingEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Navigating: {e.Target}");
|
||||
};
|
||||
Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell3.Sections.Count}");
|
||||
for (int num = 0; num < skiaShell3.Sections.Count; num++)
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Section {num}: Route='{skiaShell3.Sections[num].Route}', Title='{skiaShell3.Sections[num].Title}'");
|
||||
}
|
||||
return skiaShell3;
|
||||
}
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine($"[PushPage] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ApplyShellColors(SkiaShell skiaShell, Shell shell)
|
||||
{
|
||||
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0012: Invalid comparison between Unknown and I4
|
||||
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0236: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_021e: Unknown result type (might be due to invalid IL or missing references)
|
||||
Application current = Application.Current;
|
||||
bool flag = current != null && (int)current.UserAppTheme == 2;
|
||||
Console.WriteLine("[ApplyShellColors] Theme is: " + (flag ? "Dark" : "Light"));
|
||||
if (shell.FlyoutBackgroundColor != null && shell.FlyoutBackgroundColor != Colors.Transparent)
|
||||
{
|
||||
Color flyoutBackgroundColor = shell.FlyoutBackgroundColor;
|
||||
skiaShell.FlyoutBackgroundColor = new SKColor((byte)(flyoutBackgroundColor.Red * 255f), (byte)(flyoutBackgroundColor.Green * 255f), (byte)(flyoutBackgroundColor.Blue * 255f), (byte)(flyoutBackgroundColor.Alpha * 255f));
|
||||
Console.WriteLine($"[ApplyShellColors] FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}");
|
||||
}
|
||||
else
|
||||
{
|
||||
skiaShell.FlyoutBackgroundColor = (flag ? new SKColor((byte)30, (byte)30, (byte)30) : new SKColor(byte.MaxValue, byte.MaxValue, byte.MaxValue));
|
||||
Console.WriteLine($"[ApplyShellColors] Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}");
|
||||
}
|
||||
skiaShell.FlyoutTextColor = (flag ? new SKColor((byte)224, (byte)224, (byte)224) : new SKColor((byte)33, (byte)33, (byte)33));
|
||||
Console.WriteLine($"[ApplyShellColors] FlyoutTextColor: {skiaShell.FlyoutTextColor}");
|
||||
skiaShell.ContentBackgroundColor = (flag ? new SKColor((byte)18, (byte)18, (byte)18) : new SKColor((byte)250, (byte)250, (byte)250));
|
||||
Console.WriteLine($"[ApplyShellColors] ContentBackgroundColor: {skiaShell.ContentBackgroundColor}");
|
||||
if (((VisualElement)shell).BackgroundColor != null && ((VisualElement)shell).BackgroundColor != Colors.Transparent)
|
||||
{
|
||||
Color backgroundColor = ((VisualElement)shell).BackgroundColor;
|
||||
skiaShell.NavBarBackgroundColor = new SKColor((byte)(backgroundColor.Red * 255f), (byte)(backgroundColor.Green * 255f), (byte)(backgroundColor.Blue * 255f), (byte)(backgroundColor.Alpha * 255f));
|
||||
}
|
||||
else
|
||||
{
|
||||
skiaShell.NavBarBackgroundColor = new SKColor((byte)33, (byte)150, (byte)243);
|
||||
}
|
||||
}
|
||||
if (CurrentRenderer == null)
|
||||
{
|
||||
Console.WriteLine($"[PushPage] CurrentRenderer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
|
||||
{
|
||||
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
|
||||
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(70, 3);
|
||||
defaultInterpolatedStringHandler.AppendLiteral("[Navigation] OnShellNavigated called - Source: ");
|
||||
defaultInterpolatedStringHandler.AppendFormatted<ShellNavigationSource>(e.Source);
|
||||
defaultInterpolatedStringHandler.AppendLiteral(", Current: ");
|
||||
ShellNavigationState current = e.Current;
|
||||
defaultInterpolatedStringHandler.AppendFormatted((current != null) ? current.Location : null);
|
||||
defaultInterpolatedStringHandler.AppendLiteral(", Previous: ");
|
||||
ShellNavigationState previous = e.Previous;
|
||||
defaultInterpolatedStringHandler.AppendFormatted((previous != null) ? previous.Location : null);
|
||||
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
|
||||
if (CurrentSkiaShell == null || CurrentMauiShell == null)
|
||||
{
|
||||
Console.WriteLine("[Navigation] CurrentSkiaShell or CurrentMauiShell is null");
|
||||
return;
|
||||
}
|
||||
ShellNavigationState currentState = CurrentMauiShell.CurrentState;
|
||||
string text = ((currentState == null) ? null : currentState.Location?.OriginalString) ?? "";
|
||||
Console.WriteLine($"[Navigation] Location: {text}, Sections: {CurrentSkiaShell.Sections.Count}");
|
||||
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
|
||||
{
|
||||
ShellSection shellSection = CurrentSkiaShell.Sections[i];
|
||||
Console.WriteLine($"[Navigation] Checking section {i}: Route='{shellSection.Route}', Title='{shellSection.Title}'");
|
||||
if (!string.IsNullOrEmpty(shellSection.Route) && text.Contains(shellSection.Route, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}");
|
||||
if (i != CurrentSkiaShell.CurrentSectionIndex)
|
||||
{
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(shellSection.Title) && text.Contains(shellSection.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}");
|
||||
if (i != CurrentSkiaShell.CurrentSectionIndex)
|
||||
{
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Console.WriteLine("[Navigation] No matching section found for location: " + text);
|
||||
}
|
||||
try
|
||||
{
|
||||
// Render the page content
|
||||
SkiaView? pageContent = null;
|
||||
if (page is ContentPage contentPage && contentPage.Content != null)
|
||||
{
|
||||
pageContent = CurrentRenderer.RenderView(contentPage.Content);
|
||||
}
|
||||
|
||||
private void ProcessShellItem(SkiaShell skiaShell, ShellItem item)
|
||||
{
|
||||
FlyoutItem val = (FlyoutItem)(object)((item is FlyoutItem) ? item : null);
|
||||
if (val != null)
|
||||
{
|
||||
ShellSection shellSection = new ShellSection
|
||||
{
|
||||
Title = (((BaseShellItem)val).Title ?? ""),
|
||||
Route = (((BaseShellItem)val).Route ?? ((BaseShellItem)val).Title ?? "")
|
||||
};
|
||||
foreach (ShellSection item2 in ((ShellItem)val).Items)
|
||||
{
|
||||
foreach (ShellContent item3 in item2.Items)
|
||||
{
|
||||
ShellContent shellContent = new ShellContent
|
||||
{
|
||||
Title = (((BaseShellItem)item3).Title ?? ((BaseShellItem)item2).Title ?? ((BaseShellItem)val).Title ?? ""),
|
||||
Route = (((BaseShellItem)item3).Route ?? ""),
|
||||
MauiShellContent = item3
|
||||
};
|
||||
SkiaView skiaView = CreateShellContentPage(item3);
|
||||
if (skiaView != null)
|
||||
{
|
||||
shellContent.Content = skiaView;
|
||||
}
|
||||
shellSection.Items.Add(shellContent);
|
||||
}
|
||||
}
|
||||
if (shellSection.Items.Count == 1)
|
||||
{
|
||||
shellSection.Title = shellSection.Items[0].Title;
|
||||
}
|
||||
skiaShell.AddSection(shellSection);
|
||||
return;
|
||||
}
|
||||
TabBar val2 = (TabBar)(object)((item is TabBar) ? item : null);
|
||||
if (val2 != null)
|
||||
{
|
||||
foreach (ShellSection item4 in ((ShellItem)val2).Items)
|
||||
{
|
||||
ShellSection shellSection2 = new ShellSection
|
||||
{
|
||||
Title = (((BaseShellItem)item4).Title ?? ""),
|
||||
Route = (((BaseShellItem)item4).Route ?? "")
|
||||
};
|
||||
foreach (ShellContent item5 in item4.Items)
|
||||
{
|
||||
ShellContent shellContent2 = new ShellContent
|
||||
{
|
||||
Title = (((BaseShellItem)item5).Title ?? ((BaseShellItem)item4).Title ?? ""),
|
||||
Route = (((BaseShellItem)item5).Route ?? ""),
|
||||
MauiShellContent = item5
|
||||
};
|
||||
SkiaView skiaView2 = CreateShellContentPage(item5);
|
||||
if (skiaView2 != null)
|
||||
{
|
||||
shellContent2.Content = skiaView2;
|
||||
}
|
||||
shellSection2.Items.Add(shellContent2);
|
||||
}
|
||||
skiaShell.AddSection(shellSection2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ShellSection shellSection3 = new ShellSection
|
||||
{
|
||||
Title = (((BaseShellItem)item).Title ?? ""),
|
||||
Route = (((BaseShellItem)item).Route ?? "")
|
||||
};
|
||||
foreach (ShellSection item6 in item.Items)
|
||||
{
|
||||
foreach (ShellContent item7 in item6.Items)
|
||||
{
|
||||
ShellContent shellContent3 = new ShellContent
|
||||
{
|
||||
Title = (((BaseShellItem)item7).Title ?? ""),
|
||||
Route = (((BaseShellItem)item7).Route ?? ""),
|
||||
MauiShellContent = item7
|
||||
};
|
||||
SkiaView skiaView3 = CreateShellContentPage(item7);
|
||||
if (skiaView3 != null)
|
||||
{
|
||||
shellContent3.Content = skiaView3;
|
||||
}
|
||||
shellSection3.Items.Add(shellContent3);
|
||||
}
|
||||
}
|
||||
skiaShell.AddSection(shellSection3);
|
||||
}
|
||||
if (pageContent == null)
|
||||
{
|
||||
Console.WriteLine($"[PushPage] Failed to render page content");
|
||||
return false;
|
||||
}
|
||||
|
||||
private SkiaView? CreateShellContentPage(ShellContent content)
|
||||
{
|
||||
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
|
||||
try
|
||||
{
|
||||
Page val = null;
|
||||
if (content.ContentTemplate != null)
|
||||
{
|
||||
object obj = ((ElementTemplate)content.ContentTemplate).CreateContent();
|
||||
val = (Page)((obj is Page) ? obj : null);
|
||||
}
|
||||
if (val == null)
|
||||
{
|
||||
object content2 = content.Content;
|
||||
Page val2 = (Page)((content2 is Page) ? content2 : null);
|
||||
if (val2 != null)
|
||||
{
|
||||
val = val2;
|
||||
}
|
||||
}
|
||||
ContentPage val3 = (ContentPage)(object)((val is ContentPage) ? val : null);
|
||||
if (val3 != null && val3.Content != null)
|
||||
{
|
||||
SkiaView skiaView = RenderView((IView)(object)val3.Content);
|
||||
if (skiaView != null)
|
||||
{
|
||||
SKColor? value = null;
|
||||
if (((VisualElement)val3).BackgroundColor != null && ((VisualElement)val3).BackgroundColor != Colors.Transparent)
|
||||
{
|
||||
Color backgroundColor = ((VisualElement)val3).BackgroundColor;
|
||||
value = new SKColor((byte)(backgroundColor.Red * 255f), (byte)(backgroundColor.Green * 255f), (byte)(backgroundColor.Blue * 255f), (byte)(backgroundColor.Alpha * 255f));
|
||||
Console.WriteLine($"[CreateShellContentPage] Page BackgroundColor: {value}");
|
||||
}
|
||||
if (skiaView is SkiaScrollView skiaScrollView)
|
||||
{
|
||||
if (value.HasValue)
|
||||
{
|
||||
skiaScrollView.BackgroundColor = value.Value;
|
||||
}
|
||||
return skiaScrollView;
|
||||
}
|
||||
SkiaScrollView skiaScrollView2 = new SkiaScrollView
|
||||
{
|
||||
Content = skiaView
|
||||
};
|
||||
if (value.HasValue)
|
||||
{
|
||||
skiaScrollView2.BackgroundColor = value.Value;
|
||||
}
|
||||
return skiaScrollView2;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Wrap in ScrollView if needed
|
||||
if (pageContent is not SkiaScrollView)
|
||||
{
|
||||
var scrollView = new SkiaScrollView { Content = pageContent };
|
||||
pageContent = scrollView;
|
||||
}
|
||||
|
||||
public SkiaView? RenderView(IView view)
|
||||
{
|
||||
if (view == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
Element val = (Element)(object)((view is Element) ? view : null);
|
||||
if (val != null && val.Handler != null)
|
||||
{
|
||||
val.Handler.DisconnectHandler();
|
||||
}
|
||||
IElementHandler obj = ((IElement)(object)view).ToHandler(_mauiContext);
|
||||
if (!(((obj != null) ? obj.PlatformView : null) is SkiaView result))
|
||||
{
|
||||
return CreateFallbackView(view);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return CreateFallbackView(view);
|
||||
}
|
||||
}
|
||||
// Push onto SkiaShell's navigation stack
|
||||
CurrentSkiaShell.PushAsync(pageContent, page.Title ?? "Detail");
|
||||
Console.WriteLine($"[PushPage] Successfully pushed page");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[PushPage] Error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private SkiaView CreateFallbackView(IView view)
|
||||
{
|
||||
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
|
||||
return new SkiaLabel
|
||||
{
|
||||
Text = "[" + ((object)view).GetType().Name + "]",
|
||||
TextColor = SKColors.Gray,
|
||||
FontSize = 12f
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Pops the current page from the navigation stack.
|
||||
/// </summary>
|
||||
/// <returns>True if successful</returns>
|
||||
public static bool PopPage()
|
||||
{
|
||||
Console.WriteLine($"[PopPage] Popping page");
|
||||
|
||||
if (CurrentSkiaShell == null)
|
||||
{
|
||||
Console.WriteLine($"[PopPage] CurrentSkiaShell is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return CurrentSkiaShell.PopAsync();
|
||||
}
|
||||
|
||||
public LinuxViewRenderer(IMauiContext mauiContext)
|
||||
{
|
||||
_mauiContext = mauiContext ?? throw new ArgumentNullException(nameof(mauiContext));
|
||||
// Store reference for push/pop navigation
|
||||
CurrentRenderer = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a MAUI page and returns the corresponding SkiaView.
|
||||
/// </summary>
|
||||
public SkiaView? RenderPage(Page page)
|
||||
{
|
||||
if (page == null)
|
||||
return null;
|
||||
|
||||
// Special handling for Shell - Shell is our navigation container
|
||||
if (page is Shell shell)
|
||||
{
|
||||
return RenderShell(shell);
|
||||
}
|
||||
|
||||
// Set handler context
|
||||
page.Handler?.DisconnectHandler();
|
||||
var handler = page.ToHandler(_mauiContext);
|
||||
|
||||
if (handler.PlatformView is SkiaView skiaPage)
|
||||
{
|
||||
// For ContentPage, render the content
|
||||
if (page is ContentPage contentPage && contentPage.Content != null)
|
||||
{
|
||||
var contentView = RenderView(contentPage.Content);
|
||||
if (skiaPage is SkiaPage sp && contentView != null)
|
||||
{
|
||||
sp.Content = contentView;
|
||||
}
|
||||
}
|
||||
|
||||
return skiaPage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a MAUI Shell with all its navigation structure.
|
||||
/// </summary>
|
||||
private SkiaShell RenderShell(Shell shell)
|
||||
{
|
||||
// Store reference for navigation - Shell.Current is computed from Application.Current.Windows
|
||||
// Our platform handles navigation through SkiaShell directly
|
||||
CurrentMauiShell = shell;
|
||||
|
||||
var skiaShell = new SkiaShell
|
||||
{
|
||||
Title = shell.Title ?? "App",
|
||||
FlyoutBehavior = shell.FlyoutBehavior switch
|
||||
{
|
||||
FlyoutBehavior.Flyout => ShellFlyoutBehavior.Flyout,
|
||||
FlyoutBehavior.Locked => ShellFlyoutBehavior.Locked,
|
||||
FlyoutBehavior.Disabled => ShellFlyoutBehavior.Disabled,
|
||||
_ => ShellFlyoutBehavior.Flyout
|
||||
}
|
||||
};
|
||||
|
||||
// Process shell items into sections
|
||||
foreach (var item in shell.Items)
|
||||
{
|
||||
ProcessShellItem(skiaShell, item);
|
||||
}
|
||||
|
||||
// Store reference to SkiaShell for navigation
|
||||
CurrentSkiaShell = skiaShell;
|
||||
|
||||
// Subscribe to MAUI Shell navigation events to update SkiaShell
|
||||
shell.Navigated += OnShellNavigated;
|
||||
shell.Navigating += (s, e) => Console.WriteLine($"[Navigation] Navigating: {e.Target}");
|
||||
|
||||
Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}");
|
||||
for (int i = 0; i < skiaShell.Sections.Count; i++)
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'");
|
||||
}
|
||||
|
||||
return skiaShell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles MAUI Shell navigation events and updates SkiaShell accordingly.
|
||||
/// </summary>
|
||||
private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"[Navigation] OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}");
|
||||
|
||||
if (CurrentSkiaShell == null || CurrentMauiShell == null)
|
||||
{
|
||||
Console.WriteLine($"[Navigation] CurrentSkiaShell or CurrentMauiShell is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current route from the Shell
|
||||
var currentState = CurrentMauiShell.CurrentState;
|
||||
var location = currentState?.Location?.OriginalString ?? "";
|
||||
Console.WriteLine($"[Navigation] Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}");
|
||||
|
||||
// Find the matching section in SkiaShell by route
|
||||
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
|
||||
{
|
||||
var section = CurrentSkiaShell.Sections[i];
|
||||
Console.WriteLine($"[Navigation] Checking section {i}: Route='{section.Route}', Title='{section.Title}'");
|
||||
if (!string.IsNullOrEmpty(section.Route) && location.Contains(section.Route, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}");
|
||||
if (i != CurrentSkiaShell.CurrentSectionIndex)
|
||||
{
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(section.Title) && location.Contains(section.Title, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}");
|
||||
if (i != CurrentSkiaShell.CurrentSectionIndex)
|
||||
{
|
||||
CurrentSkiaShell.NavigateToSection(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"[Navigation] No matching section found for location: {location}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a ShellItem (FlyoutItem, TabBar, etc.) into SkiaShell sections.
|
||||
/// </summary>
|
||||
private void ProcessShellItem(SkiaShell skiaShell, ShellItem item)
|
||||
{
|
||||
if (item is FlyoutItem flyoutItem)
|
||||
{
|
||||
// Each FlyoutItem becomes a section
|
||||
var section = new ShellSection
|
||||
{
|
||||
Title = flyoutItem.Title ?? "",
|
||||
Route = flyoutItem.Route ?? flyoutItem.Title ?? ""
|
||||
};
|
||||
|
||||
// Process the items within the FlyoutItem
|
||||
foreach (var shellSection in flyoutItem.Items)
|
||||
{
|
||||
foreach (var content in shellSection.Items)
|
||||
{
|
||||
var shellContent = new ShellContent
|
||||
{
|
||||
Title = content.Title ?? shellSection.Title ?? flyoutItem.Title ?? "",
|
||||
Route = content.Route ?? ""
|
||||
};
|
||||
|
||||
// Create the page content
|
||||
var pageContent = CreateShellContentPage(content);
|
||||
if (pageContent != null)
|
||||
{
|
||||
shellContent.Content = pageContent;
|
||||
}
|
||||
|
||||
section.Items.Add(shellContent);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's only one item, use it as the main section content
|
||||
if (section.Items.Count == 1)
|
||||
{
|
||||
section.Title = section.Items[0].Title;
|
||||
}
|
||||
|
||||
skiaShell.AddSection(section);
|
||||
}
|
||||
else if (item is TabBar tabBar)
|
||||
{
|
||||
// TabBar items get their own sections
|
||||
foreach (var tab in tabBar.Items)
|
||||
{
|
||||
var section = new ShellSection
|
||||
{
|
||||
Title = tab.Title ?? "",
|
||||
Route = tab.Route ?? ""
|
||||
};
|
||||
|
||||
foreach (var content in tab.Items)
|
||||
{
|
||||
var shellContent = new ShellContent
|
||||
{
|
||||
Title = content.Title ?? tab.Title ?? "",
|
||||
Route = content.Route ?? ""
|
||||
};
|
||||
|
||||
var pageContent = CreateShellContentPage(content);
|
||||
if (pageContent != null)
|
||||
{
|
||||
shellContent.Content = pageContent;
|
||||
}
|
||||
|
||||
section.Items.Add(shellContent);
|
||||
}
|
||||
|
||||
skiaShell.AddSection(section);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generic ShellItem
|
||||
var section = new ShellSection
|
||||
{
|
||||
Title = item.Title ?? "",
|
||||
Route = item.Route ?? ""
|
||||
};
|
||||
|
||||
foreach (var shellSection in item.Items)
|
||||
{
|
||||
foreach (var content in shellSection.Items)
|
||||
{
|
||||
var shellContent = new ShellContent
|
||||
{
|
||||
Title = content.Title ?? "",
|
||||
Route = content.Route ?? ""
|
||||
};
|
||||
|
||||
var pageContent = CreateShellContentPage(content);
|
||||
if (pageContent != null)
|
||||
{
|
||||
shellContent.Content = pageContent;
|
||||
}
|
||||
|
||||
section.Items.Add(shellContent);
|
||||
}
|
||||
}
|
||||
|
||||
skiaShell.AddSection(section);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the page content for a ShellContent.
|
||||
/// </summary>
|
||||
private SkiaView? CreateShellContentPage(Controls.ShellContent content)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to create the page from the content template
|
||||
Page? page = null;
|
||||
|
||||
if (content.ContentTemplate != null)
|
||||
{
|
||||
page = content.ContentTemplate.CreateContent() as Page;
|
||||
}
|
||||
|
||||
if (page == null && content.Content is Page contentPage)
|
||||
{
|
||||
page = contentPage;
|
||||
}
|
||||
|
||||
if (page is ContentPage cp && cp.Content != null)
|
||||
{
|
||||
// Wrap in a scroll view if not already scrollable
|
||||
var contentView = RenderView(cp.Content);
|
||||
if (contentView != null)
|
||||
{
|
||||
if (contentView is SkiaScrollView)
|
||||
{
|
||||
return contentView;
|
||||
}
|
||||
else
|
||||
{
|
||||
var scrollView = new SkiaScrollView
|
||||
{
|
||||
Content = contentView
|
||||
};
|
||||
return scrollView;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Silently handle template creation errors
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a MAUI view and returns the corresponding SkiaView.
|
||||
/// </summary>
|
||||
public SkiaView? RenderView(IView view)
|
||||
{
|
||||
if (view == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Disconnect any existing handler
|
||||
if (view is Element element && element.Handler != null)
|
||||
{
|
||||
element.Handler.DisconnectHandler();
|
||||
}
|
||||
|
||||
// Create handler for the view
|
||||
// The handler's ConnectHandler and property mappers handle child views automatically
|
||||
var handler = view.ToHandler(_mauiContext);
|
||||
|
||||
if (handler?.PlatformView is not SkiaView skiaView)
|
||||
{
|
||||
// If no Skia handler, create a fallback
|
||||
return CreateFallbackView(view);
|
||||
}
|
||||
|
||||
// Handlers manage their own children via ConnectHandler and property mappers
|
||||
// No manual child rendering needed here - that caused "View already has a parent" errors
|
||||
return skiaView;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return CreateFallbackView(view);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fallback view for unsupported view types.
|
||||
/// </summary>
|
||||
private SkiaView CreateFallbackView(IView view)
|
||||
{
|
||||
// For views without handlers, create a placeholder
|
||||
return new SkiaLabel
|
||||
{
|
||||
Text = $"[{view.GetType().Name}]",
|
||||
TextColor = SKColors.Gray,
|
||||
FontSize = 12
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for MAUI handler creation.
|
||||
/// </summary>
|
||||
public static class MauiHandlerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a handler for the view and returns it.
|
||||
/// </summary>
|
||||
public static IElementHandler ToHandler(this IElement element, IMauiContext mauiContext)
|
||||
{
|
||||
var handler = mauiContext.Handlers.GetHandler(element.GetType());
|
||||
if (handler != null)
|
||||
{
|
||||
handler.SetMauiContext(mauiContext);
|
||||
handler.SetVirtualView(element);
|
||||
}
|
||||
return handler!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Platform.Linux.Handlers;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
public static class MauiHandlerExtensions
|
||||
{
|
||||
private static readonly Dictionary<Type, Func<IElementHandler>> LinuxHandlerMap = new Dictionary<Type, Func<IElementHandler>>
|
||||
{
|
||||
[typeof(Button)] = () => (IElementHandler)(object)new TextButtonHandler(),
|
||||
[typeof(Label)] = () => (IElementHandler)(object)new LabelHandler(),
|
||||
[typeof(Entry)] = () => (IElementHandler)(object)new EntryHandler(),
|
||||
[typeof(Editor)] = () => (IElementHandler)(object)new EditorHandler(),
|
||||
[typeof(CheckBox)] = () => (IElementHandler)(object)new CheckBoxHandler(),
|
||||
[typeof(Switch)] = () => (IElementHandler)(object)new SwitchHandler(),
|
||||
[typeof(Slider)] = () => (IElementHandler)(object)new SliderHandler(),
|
||||
[typeof(Stepper)] = () => (IElementHandler)(object)new StepperHandler(),
|
||||
[typeof(ProgressBar)] = () => (IElementHandler)(object)new ProgressBarHandler(),
|
||||
[typeof(ActivityIndicator)] = () => (IElementHandler)(object)new ActivityIndicatorHandler(),
|
||||
[typeof(Picker)] = () => (IElementHandler)(object)new PickerHandler(),
|
||||
[typeof(DatePicker)] = () => (IElementHandler)(object)new DatePickerHandler(),
|
||||
[typeof(TimePicker)] = () => (IElementHandler)(object)new TimePickerHandler(),
|
||||
[typeof(SearchBar)] = () => (IElementHandler)(object)new SearchBarHandler(),
|
||||
[typeof(RadioButton)] = () => (IElementHandler)(object)new RadioButtonHandler(),
|
||||
[typeof(WebView)] = () => (IElementHandler)(object)new GtkWebViewHandler(),
|
||||
[typeof(Image)] = () => (IElementHandler)(object)new ImageHandler(),
|
||||
[typeof(ImageButton)] = () => (IElementHandler)(object)new ImageButtonHandler(),
|
||||
[typeof(BoxView)] = () => (IElementHandler)(object)new BoxViewHandler(),
|
||||
[typeof(Frame)] = () => (IElementHandler)(object)new FrameHandler(),
|
||||
[typeof(Border)] = () => (IElementHandler)(object)new BorderHandler(),
|
||||
[typeof(ContentView)] = () => (IElementHandler)(object)new BorderHandler(),
|
||||
[typeof(ScrollView)] = () => (IElementHandler)(object)new ScrollViewHandler(),
|
||||
[typeof(Grid)] = () => (IElementHandler)(object)new GridHandler(),
|
||||
[typeof(StackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
|
||||
[typeof(VerticalStackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
|
||||
[typeof(HorizontalStackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
|
||||
[typeof(AbsoluteLayout)] = () => (IElementHandler)(object)new LayoutHandler(),
|
||||
[typeof(FlexLayout)] = () => (IElementHandler)(object)new LayoutHandler(),
|
||||
[typeof(CollectionView)] = () => (IElementHandler)(object)new CollectionViewHandler(),
|
||||
[typeof(ListView)] = () => (IElementHandler)(object)new CollectionViewHandler(),
|
||||
[typeof(Page)] = () => (IElementHandler)(object)new PageHandler(),
|
||||
[typeof(ContentPage)] = () => (IElementHandler)(object)new ContentPageHandler(),
|
||||
[typeof(NavigationPage)] = () => (IElementHandler)(object)new NavigationPageHandler(),
|
||||
[typeof(Shell)] = () => (IElementHandler)(object)new ShellHandler(),
|
||||
[typeof(FlyoutPage)] = () => (IElementHandler)(object)new FlyoutPageHandler(),
|
||||
[typeof(TabbedPage)] = () => (IElementHandler)(object)new TabbedPageHandler(),
|
||||
[typeof(Application)] = () => (IElementHandler)(object)new ApplicationHandler(),
|
||||
[typeof(Window)] = () => (IElementHandler)(object)new WindowHandler(),
|
||||
[typeof(GraphicsView)] = () => (IElementHandler)(object)new GraphicsViewHandler()
|
||||
};
|
||||
|
||||
public static IElementHandler ToHandler(this IElement element, IMauiContext mauiContext)
|
||||
{
|
||||
return CreateHandler(element, mauiContext);
|
||||
}
|
||||
|
||||
public static IViewHandler? ToViewHandler(this IView view, IMauiContext mauiContext)
|
||||
{
|
||||
IElementHandler? obj = CreateHandler((IElement)(object)view, mauiContext);
|
||||
return (IViewHandler?)(object)((obj is IViewHandler) ? obj : null);
|
||||
}
|
||||
|
||||
private static IElementHandler? CreateHandler(IElement element, IMauiContext mauiContext)
|
||||
{
|
||||
Type type = ((object)element).GetType();
|
||||
IElementHandler val = null;
|
||||
if (LinuxHandlerMap.TryGetValue(type, out Func<IElementHandler> value))
|
||||
{
|
||||
val = value();
|
||||
Console.WriteLine("[ToHandler] Using Linux handler for " + type.Name + ": " + ((object)val).GetType().Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Type type2 = null;
|
||||
Func<IElementHandler> func = null;
|
||||
foreach (KeyValuePair<Type, Func<IElementHandler>> item in LinuxHandlerMap)
|
||||
{
|
||||
if (item.Key.IsAssignableFrom(type) && (type2 == null || type2.IsAssignableFrom(item.Key)))
|
||||
{
|
||||
type2 = item.Key;
|
||||
func = item.Value;
|
||||
}
|
||||
}
|
||||
if (func != null)
|
||||
{
|
||||
val = func();
|
||||
Console.WriteLine($"[ToHandler] Using Linux handler (via base {type2.Name}) for {type.Name}: {((object)val).GetType().Name}");
|
||||
}
|
||||
}
|
||||
if (val == null)
|
||||
{
|
||||
val = mauiContext.Handlers.GetHandler(type);
|
||||
Console.WriteLine("[ToHandler] Using MAUI handler for " + type.Name + ": " + (((object)val)?.GetType().Name ?? "null"));
|
||||
}
|
||||
if (val != null)
|
||||
{
|
||||
val.SetMauiContext(mauiContext);
|
||||
val.SetVirtualView(element);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
public class ScopedLinuxMauiContext : IMauiContext
|
||||
{
|
||||
private readonly LinuxMauiContext _parent;
|
||||
|
||||
public IServiceProvider Services => _parent.Services;
|
||||
|
||||
public IMauiHandlersFactory Handlers => _parent.Handlers;
|
||||
|
||||
public ScopedLinuxMauiContext(LinuxMauiContext parent)
|
||||
{
|
||||
_parent = parent ?? throw new ArgumentNullException("parent");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user