Fix NavigationPageHandler, StepperHandler, TimePickerHandler from decompiled

NavigationPageHandler:
- Added LoadToolbarIcon() method for PNG/SVG toolbar icons
- Added icon loading in MapToolbarItems()
- Fixed OnVirtualViewPushed to set Title and handle null content
- Fixed animation parameters to match decompiled

StepperHandler:
- Added MapIncrement() and MapIsEnabled() methods
- Added dark theme color support in ConnectHandler

TimePickerHandler:
- Added dark theme color support in ConnectHandler

SkiaPage:
- Added Icon property to SkiaToolbarItem class

Also added Svg.Skia package reference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 12:52:33 -05:00
parent 1cdf66c44b
commit b0b3746968
6 changed files with 136 additions and 16 deletions

View File

@@ -1,12 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
using Svg.Skia;
using System.Collections.Specialized;
namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -164,7 +166,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
contentPage.ToolbarItems.Clear();
foreach (var item in page.ToolbarItems)
{
Console.WriteLine($"[NavigationPageHandler] Adding toolbar item: '{item.Text}', Order={item.Order}");
Console.WriteLine($"[NavigationPageHandler] Adding toolbar item: '{item.Text}', IconImageSource={item.IconImageSource}, Order={item.Order}");
// Default and Primary should both be treated as Primary (shown in toolbar)
// Only Secondary goes to overflow menu
var order = item.Order == ToolbarItemOrder.Secondary
@@ -188,9 +190,17 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
}
});
// Load icon if specified
SKBitmap? icon = null;
if (item.IconImageSource is FileImageSource fileSource && !string.IsNullOrEmpty(fileSource.File))
{
icon = LoadToolbarIcon(fileSource.File);
}
contentPage.ToolbarItems.Add(new SkiaToolbarItem
{
Text = item.Text ?? "",
Icon = icon,
Order = order,
Command = clickCommand
});
@@ -211,6 +221,56 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
}
}
private SKBitmap? LoadToolbarIcon(string fileName)
{
try
{
string baseDirectory = AppContext.BaseDirectory;
string pngPath = Path.Combine(baseDirectory, fileName);
string svgPath = Path.Combine(baseDirectory, Path.ChangeExtension(fileName, ".svg"));
Console.WriteLine($"[NavigationPageHandler] LoadToolbarIcon: Looking for {fileName}");
Console.WriteLine($"[NavigationPageHandler] Trying PNG: {pngPath} (exists: {File.Exists(pngPath)})");
Console.WriteLine($"[NavigationPageHandler] Trying SVG: {svgPath} (exists: {File.Exists(svgPath)})");
// Try SVG first
if (File.Exists(svgPath))
{
using var svg = new SKSvg();
svg.Load(svgPath);
if (svg.Picture != null)
{
var cullRect = svg.Picture.CullRect;
float scale = 24f / Math.Max(cullRect.Width, cullRect.Height);
var bitmap = new SKBitmap(24, 24, false);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Transparent);
canvas.Scale(scale);
canvas.DrawPicture(svg.Picture, null);
Console.WriteLine($"[NavigationPageHandler] Loaded SVG icon: {svgPath}");
return bitmap;
}
}
// Try PNG
if (File.Exists(pngPath))
{
using var stream = File.OpenRead(pngPath);
var result = SKBitmap.Decode(stream);
Console.WriteLine($"[NavigationPageHandler] Loaded PNG icon: {pngPath}");
return result;
}
Console.WriteLine($"[NavigationPageHandler] Icon not found: {fileName}");
return null;
}
catch (Exception ex)
{
Console.WriteLine($"[NavigationPageHandler] Error loading icon {fileName}: {ex.Message}");
return null;
}
}
private void OnVirtualViewPushed(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e)
{
try
@@ -232,12 +292,30 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
skiaPage.ShowNavigationBar = true;
skiaPage.TitleBarColor = PlatformView.BarBackgroundColor;
skiaPage.TitleTextColor = PlatformView.BarTextColor;
skiaPage.Title = e.Page.Title ?? "";
// Handle content if null
if (skiaPage.Content == null && e.Page is ContentPage contentPage && contentPage.Content != null)
{
Console.WriteLine($"[NavigationPageHandler] Content is null, creating handler for: {contentPage.Content.GetType().Name}");
if (contentPage.Content.Handler == null)
{
contentPage.Content.Handler = contentPage.Content.ToViewHandler(MauiContext);
}
if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent)
{
skiaPage.Content = skiaContent;
Console.WriteLine($"[NavigationPageHandler] Set content to: {skiaContent.GetType().Name}");
}
}
Console.WriteLine($"[NavigationPageHandler] Mapping toolbar items");
MapToolbarItems(skiaPage, e.Page);
Console.WriteLine($"[NavigationPageHandler] Pushing page to platform");
PlatformView.Push(skiaPage, true);
Console.WriteLine($"[NavigationPageHandler] Push complete");
PlatformView.Push(skiaPage, false);
Console.WriteLine($"[NavigationPageHandler] Push complete, thread={Environment.CurrentManagedThreadId}");
}
Console.WriteLine("[NavigationPageHandler] OnVirtualViewPushed returning");
}
catch (Exception ex)
{
@@ -251,13 +329,13 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
{
Console.WriteLine($"[NavigationPageHandler] VirtualView Popped: {e.Page?.Title}");
// Pop on the platform side to sync with MAUI navigation
PlatformView?.Pop(true);
PlatformView?.Pop();
}
private void OnVirtualViewPoppedToRoot(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e)
{
Console.WriteLine($"[NavigationPageHandler] VirtualView PoppedToRoot");
PlatformView?.PopToRoot(true);
PlatformView?.PopToRoot();
}
private void OnPushed(object? sender, NavigationEventArgs e)

View File

@@ -3,6 +3,7 @@
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using SkiaSharp;
@@ -19,7 +20,9 @@ public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
[nameof(IStepper.Value)] = MapValue,
[nameof(IStepper.Minimum)] = MapMinimum,
[nameof(IStepper.Maximum)] = MapMaximum,
["Increment"] = MapIncrement,
[nameof(IView.Background)] = MapBackground,
[nameof(IView.IsEnabled)] = MapIsEnabled,
};
public static CommandMapper<IStepper, StepperHandler> CommandMapper =
@@ -45,6 +48,17 @@ public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
{
base.ConnectHandler(platformView);
platformView.ValueChanged += OnValueChanged;
// Apply dark theme colors if needed
if (Application.Current?.UserAppTheme == AppTheme.Dark)
{
platformView.ButtonBackgroundColor = new SKColor(66, 66, 66);
platformView.ButtonPressedColor = new SKColor(97, 97, 97);
platformView.ButtonDisabledColor = new SKColor(48, 48, 48);
platformView.SymbolColor = new SKColor(224, 224, 224);
platformView.SymbolDisabledColor = new SKColor(97, 97, 97);
platformView.BorderColor = new SKColor(97, 97, 97);
}
}
protected override void DisconnectHandler(SkiaStepper platformView)
@@ -86,4 +100,20 @@ public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
}
}
public static void MapIncrement(StepperHandler handler, IStepper stepper)
{
if (handler.PlatformView is null) return;
if (stepper is Stepper stepperControl)
{
handler.PlatformView.Increment = stepperControl.Increment;
}
}
public static void MapIsEnabled(StepperHandler handler, IStepper stepper)
{
if (handler.PlatformView is null) return;
handler.PlatformView.IsEnabled = stepper.IsEnabled;
}
}

View File

@@ -47,6 +47,16 @@ public partial class TimePickerHandler : ViewHandler<ITimePicker, SkiaTimePicker
{
base.ConnectHandler(platformView);
platformView.TimeSelected += OnTimeSelected;
// Apply dark theme colors if needed
if (Application.Current?.UserAppTheme == AppTheme.Dark)
{
platformView.ClockBackgroundColor = new SKColor(30, 30, 30);
platformView.ClockFaceColor = new SKColor(45, 45, 45);
platformView.TextColor = new SKColor(224, 224, 224);
platformView.BorderColor = new SKColor(97, 97, 97);
platformView.BackgroundColor = new SKColor(45, 45, 45);
}
}
protected override void DisconnectHandler(SkiaTimePicker platformView)