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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
| ButtonHandler.cs | [x] | Contains TextButtonHandler - Verified |
|
||||
| CheckBoxHandler.cs | [x] | Verified |
|
||||
| CollectionViewHandler.cs | [ ] | |
|
||||
| DatePickerHandler.cs | [ ] | |
|
||||
| DatePickerHandler.cs | [x] | Verified |
|
||||
| EditorHandler.cs | [x] | Verified |
|
||||
| EntryHandler.cs | [x] | Verified |
|
||||
| FlexLayoutHandler.cs | [ ] | |
|
||||
| FlyoutPageHandler.cs | [ ] | |
|
||||
| FlyoutPageHandler.cs | [x] | Verified - matches decompiled |
|
||||
| FrameHandler.cs | [ ] | |
|
||||
| GestureManager.cs | [ ] | |
|
||||
| GraphicsViewHandler.cs | [ ] | |
|
||||
@@ -34,19 +34,19 @@
|
||||
| ItemsViewHandler.cs | [ ] | |
|
||||
| LabelHandler.cs | [x] | Verified |
|
||||
| LayoutHandler.cs | [x] | Contains GridHandler, StackLayoutHandler, LayoutHandlerUpdate - Verified |
|
||||
| NavigationPageHandler.cs | [ ] | Contains RelayCommand |
|
||||
| NavigationPageHandler.cs | [x] | FIXED - Added LoadToolbarIcon, Icon loading, content handling, animated params |
|
||||
| PageHandler.cs | [x] | Added MapBackgroundColor |
|
||||
| PickerHandler.cs | [ ] | |
|
||||
| PickerHandler.cs | [x] | Verified |
|
||||
| ProgressBarHandler.cs | [x] | Verified |
|
||||
| RadioButtonHandler.cs | [ ] | |
|
||||
| RadioButtonHandler.cs | [x] | Verified - matches decompiled |
|
||||
| ScrollViewHandler.cs | [x] | Verified |
|
||||
| SearchBarHandler.cs | [ ] | |
|
||||
| ShellHandler.cs | [ ] | |
|
||||
| SearchBarHandler.cs | [x] | Verified - matches decompiled |
|
||||
| ShellHandler.cs | [x] | Verified - matches decompiled |
|
||||
| SliderHandler.cs | [x] | Verified |
|
||||
| StepperHandler.cs | [ ] | |
|
||||
| StepperHandler.cs | [x] | FIXED - Added MapIncrement, MapIsEnabled, dark theme colors |
|
||||
| SwitchHandler.cs | [x] | Verified |
|
||||
| TabbedPageHandler.cs | [ ] | |
|
||||
| TimePickerHandler.cs | [ ] | |
|
||||
| TabbedPageHandler.cs | [x] | Verified - matches decompiled |
|
||||
| TimePickerHandler.cs | [x] | FIXED - Added dark theme colors |
|
||||
| WebViewHandler.cs | [x] | Fixed namespace-qualified event args |
|
||||
| WindowHandler.cs | [ ] | Contains SkiaWindow, SizeChangedEventArgs, LinuxApplicationContext |
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
| SkiaLayoutView.cs | [ ] | Contains SkiaGrid, SkiaStackLayout, SkiaAbsoluteLayout, GridLength, GridPosition |
|
||||
| SkiaMenuBar.cs | [ ] | Contains MenuItem, MenuBarItem |
|
||||
| SkiaNavigationPage.cs | [ ] | |
|
||||
| SkiaPage.cs | [ ] | Contains SkiaContentPage, SkiaToolbarItem |
|
||||
| SkiaPage.cs | [x] | Added SkiaToolbarItem.Icon property |
|
||||
| SkiaPicker.cs | [ ] | |
|
||||
| SkiaProgressBar.cs | [ ] | |
|
||||
| SkiaRadioButton.cs | [ ] | |
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.9" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.9" />
|
||||
<PackageReference Include="Svg.Skia" Version="1.0.0" />
|
||||
|
||||
<!-- HarfBuzz for advanced text shaping -->
|
||||
<PackageReference Include="HarfBuzzSharp" Version="7.3.0.3" />
|
||||
|
||||
@@ -449,6 +449,7 @@ public class SkiaContentPage : SkiaPage
|
||||
public class SkiaToolbarItem
|
||||
{
|
||||
public string Text { get; set; } = "";
|
||||
public SKBitmap? Icon { get; set; }
|
||||
public SkiaToolbarItemOrder Order { get; set; } = SkiaToolbarItemOrder.Primary;
|
||||
public System.Windows.Input.ICommand? Command { get; set; }
|
||||
public SKRect HitBounds { get; set; }
|
||||
|
||||
Reference in New Issue
Block a user