refactor: replace Console.WriteLine with DiagnosticLog service
All checks were successful
CI / Build (Linux) (push) Successful in 21s

Replace 495+ Console.WriteLine debug statements across handlers, dispatching, services, views, and window components with centralized DiagnosticLog service for proper logging infrastructure. Add new DiagnosticLog.cs service with Debug/Error methods to eliminate debug logging pollution in production code.
This commit is contained in:
2026-03-06 22:06:08 -05:00
parent 08e0c4d2b9
commit e55230c441
70 changed files with 814 additions and 638 deletions

View File

@@ -0,0 +1,8 @@
{
"id": "note-1772851947498-ujdbzwenk",
"title": "Working list",
"content": "---\n Critical Issues\n\n 1. 495+ Console.WriteLine calls — Debug logging pollution throughout production code. Replace with ILogger\n or conditional diagnostics.\n 2. Memory leak in GestureManager — _gestureState and _tapTracking dictionaries never cleaned up for removed\n views.\n 3. Reflection-based gesture invocation — typeof(TapGestureRecognizer).GetMethod(\"SendTapped\",\n BindingFlags.NonPublic) is fragile and will break across MAUI updates.\n 4. No exception handling in rendering pipeline — A bad view implementation in SkiaRenderingEngine.Render()\n can crash the entire application.\n 5. Version mismatch — .nuspec shows 1.0.0-preview.1 while .csproj shows 1.0.0-rc.1.\n 6. Potential text binding recursion in EntryHandler.OnTextChanged — two-way sync without reentrancy guard.\n\n ---\n Medium Issues\n\n ┌───────────────────────┬──────────────────────────────────────────────────────────────────────────────┐\n │ Issue │ Location │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Massive file sizes │ SkiaEntry (57K lines), SkiaEditor (54K), SkiaLabel (41K) — should be split │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Test coverage ~3% │ Only 10 test files for 321 source files │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Hard-coded thresholds │ SwipeMinDistance=50, SwipeMaxTime=500ms, region merge=30% — not configurable │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ No feature detection │ Assumes AT-SPI2, IBus available — crashes on minimal installs │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ .bak file in VCS │ SkiaItemsView.cs.bak should be removed │\n ├───────────────────────┼──────────────────────────────────────────────────────────────────────────────┤\n │ Incomplete features │ EntryHandler.MapReturnType() commented out, various TODOs │\n └───────────────────────┴──────────────────────────────────────────────────────────────────────────────┘\n\n ---\n Priority Recommendations\n\n Before 1.0 release:\n 1. Replace all Console.WriteLine with proper logging (ILogger or conditional #if DEBUG)\n 2. Fix GestureManager memory leaks — add cleanup on view disconnect\n 3. Add try-catch around view rendering in the engine\n 4. Remove reflection in gesture handling — use interface or delegate pattern\n 5. Align nuspec/csproj versions\n 6. Add feature detection for optional services\n\n Near-term:\n 1. Split files over 10K lines into partial classes or separate concerns\n 2. Expand test coverage significantly (target 50%+)\n 3. Extract duplicate text rendering code into shared utilities\n 4. Make rendering thresholds configurable\n",
"createdAt": 1772851947495,
"updatedAt": 1772851991179,
"tags": []
}

View File

@@ -1,6 +1,7 @@
using System; using System;
using Microsoft.Maui.Dispatching; using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Platform.Linux.Native; using Microsoft.Maui.Platform.Linux.Native;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Dispatching; namespace Microsoft.Maui.Platform.Linux.Dispatching;
@@ -24,7 +25,7 @@ public class LinuxDispatcher : IDispatcher
{ {
_mainThreadId = Environment.CurrentManagedThreadId; _mainThreadId = Environment.CurrentManagedThreadId;
_mainDispatcher = new LinuxDispatcher(); _mainDispatcher = new LinuxDispatcher();
Console.WriteLine($"[LinuxDispatcher] Initialized on thread {_mainThreadId}"); DiagnosticLog.Debug("LinuxDispatcher", $"Initialized on thread {_mainThreadId}");
} }
} }
@@ -44,7 +45,7 @@ public class LinuxDispatcher : IDispatcher
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[LinuxDispatcher] Error in dispatched action: " + ex.Message); DiagnosticLog.Error("LinuxDispatcher", "Error in dispatched action", ex);
} }
return false; return false;
}); });
@@ -62,7 +63,7 @@ public class LinuxDispatcher : IDispatcher
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[LinuxDispatcher] Error in delayed action: " + ex.Message); DiagnosticLog.Error("LinuxDispatcher", "Error in delayed action", ex);
} }
return false; return false;
}); });

View File

@@ -1,6 +1,7 @@
using System; using System;
using Microsoft.Maui.Dispatching; using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Platform.Linux.Native; using Microsoft.Maui.Platform.Linux.Native;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Dispatching; namespace Microsoft.Maui.Platform.Linux.Dispatching;
@@ -95,7 +96,7 @@ public class LinuxDispatcherTimer : IDispatcherTimer
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[LinuxDispatcherTimer] Error in Tick handler: " + ex.Message); DiagnosticLog.Error("LinuxDispatcherTimer", "Error in Tick handler", ex);
} }
if (_isRepeating && _isRunning) if (_isRepeating && _isRunning)
{ {

View File

@@ -6,6 +6,7 @@ using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -111,13 +112,13 @@ public partial class BorderHandler : ViewHandler<IBorderView, SkiaBorder>
// Create handler for content if it doesn't exist // Create handler for content if it doesn't exist
if (content.Handler == null) if (content.Handler == null)
{ {
Console.WriteLine($"[BorderHandler] Creating handler for content: {content.GetType().Name}"); DiagnosticLog.Debug("BorderHandler", $"Creating handler for content: {content.GetType().Name}");
content.Handler = content.ToViewHandler(handler.MauiContext); content.Handler = content.ToViewHandler(handler.MauiContext);
} }
if (content.Handler?.PlatformView is SkiaView skiaContent) if (content.Handler?.PlatformView is SkiaView skiaContent)
{ {
Console.WriteLine($"[BorderHandler] Adding content: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("BorderHandler", $"Adding content: {skiaContent.GetType().Name}");
handler.PlatformView.AddChild(skiaContent); handler.PlatformView.AddChild(skiaContent);
} }
} }
@@ -156,7 +157,7 @@ public partial class BorderHandler : ViewHandler<IBorderView, SkiaBorder>
if (border is VisualElement ve) if (border is VisualElement ve)
{ {
var bgColor = ve.BackgroundColor; var bgColor = ve.BackgroundColor;
Console.WriteLine($"[BorderHandler] MapBackgroundColor: {bgColor}"); DiagnosticLog.Debug("BorderHandler", $"MapBackgroundColor: {bgColor}");
if (bgColor != null) if (bgColor != null)
{ {
handler.PlatformView.BackgroundColor = bgColor; handler.PlatformView.BackgroundColor = bgColor;

View File

@@ -4,6 +4,7 @@
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -64,7 +65,7 @@ public partial class ButtonHandler : ViewHandler<IButton, SkiaButton>
// Map size requests from MAUI Button // Map size requests from MAUI Button
if (VirtualView is Microsoft.Maui.Controls.Button mauiButton) if (VirtualView is Microsoft.Maui.Controls.Button mauiButton)
{ {
Console.WriteLine($"[ButtonHandler] MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); DiagnosticLog.Debug("ButtonHandler", $"MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}");
if (mauiButton.WidthRequest >= 0) if (mauiButton.WidthRequest >= 0)
platformView.WidthRequest = mauiButton.WidthRequest; platformView.WidthRequest = mauiButton.WidthRequest;
if (mauiButton.HeightRequest >= 0) if (mauiButton.HeightRequest >= 0)
@@ -72,7 +73,7 @@ public partial class ButtonHandler : ViewHandler<IButton, SkiaButton>
} }
else else
{ {
Console.WriteLine($"[ButtonHandler] VirtualView is NOT Microsoft.Maui.Controls.Button, type={VirtualView?.GetType().Name}"); DiagnosticLog.Debug("ButtonHandler", $"VirtualView is NOT Microsoft.Maui.Controls.Button, type={VirtualView?.GetType().Name}");
} }
} }
} }
@@ -162,7 +163,7 @@ public partial class TextButtonHandler : ButtonHandler
protected override void ConnectHandler(SkiaButton platformView) protected override void ConnectHandler(SkiaButton platformView)
{ {
Console.WriteLine($"[TextButtonHandler] ConnectHandler START"); DiagnosticLog.Debug("TextButtonHandler", "ConnectHandler START");
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
// Manually map text properties on connect since MAUI may not trigger updates // Manually map text properties on connect since MAUI may not trigger updates
@@ -178,13 +179,13 @@ public partial class TextButtonHandler : ButtonHandler
// Map size requests from MAUI Button // Map size requests from MAUI Button
if (VirtualView is Microsoft.Maui.Controls.Button mauiButton) if (VirtualView is Microsoft.Maui.Controls.Button mauiButton)
{ {
Console.WriteLine($"[TextButtonHandler] MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}"); DiagnosticLog.Debug("TextButtonHandler", $"MapSize Text='{platformView.Text}' WReq={mauiButton.WidthRequest} HReq={mauiButton.HeightRequest}");
if (mauiButton.WidthRequest >= 0) if (mauiButton.WidthRequest >= 0)
platformView.WidthRequest = mauiButton.WidthRequest; platformView.WidthRequest = mauiButton.WidthRequest;
if (mauiButton.HeightRequest >= 0) if (mauiButton.HeightRequest >= 0)
platformView.HeightRequest = mauiButton.HeightRequest; platformView.HeightRequest = mauiButton.HeightRequest;
} }
Console.WriteLine($"[TextButtonHandler] ConnectHandler DONE"); DiagnosticLog.Debug("TextButtonHandler", "ConnectHandler DONE");
} }
public static void MapText(TextButtonHandler handler, ITextButton button) public static void MapText(TextButtonHandler handler, ITextButton button)

View File

@@ -6,6 +6,7 @@ using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -130,18 +131,18 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
{ {
_isUpdatingSelection = true; _isUpdatingSelection = true;
Console.WriteLine($"[CollectionViewHandler] OnItemTapped index={e.Index}, item={e.Item}, SelectionMode={VirtualView.SelectionMode}"); DiagnosticLog.Debug("CollectionViewHandler", $"OnItemTapped index={e.Index}, item={e.Item}, SelectionMode={VirtualView.SelectionMode}");
// Try to get the item view and process gestures // Try to get the item view and process gestures
var skiaView = PlatformView?.GetItemView(e.Index); var skiaView = PlatformView?.GetItemView(e.Index);
Console.WriteLine($"[CollectionViewHandler] GetItemView({e.Index}) returned: {skiaView?.GetType().Name ?? "null"}, MauiView={skiaView?.MauiView?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("CollectionViewHandler", $"GetItemView({e.Index}) returned: {skiaView?.GetType().Name ?? "null"}, MauiView={skiaView?.MauiView?.GetType().Name ?? "null"}");
if (skiaView?.MauiView != null) if (skiaView?.MauiView != null)
{ {
Console.WriteLine($"[CollectionViewHandler] Found MauiView: {skiaView.MauiView.GetType().Name}, GestureRecognizers={skiaView.MauiView.GestureRecognizers?.Count ?? 0}"); DiagnosticLog.Debug("CollectionViewHandler", $"Found MauiView: {skiaView.MauiView.GetType().Name}, GestureRecognizers={skiaView.MauiView.GestureRecognizers?.Count ?? 0}");
if (GestureManager.ProcessTap(skiaView.MauiView, 0, 0)) if (GestureManager.ProcessTap(skiaView.MauiView, 0, 0))
{ {
Console.WriteLine("[CollectionViewHandler] Gesture processed successfully"); DiagnosticLog.Debug("CollectionViewHandler", "Gesture processed successfully");
return; return;
} }
} }
@@ -208,7 +209,7 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
{ {
// Set MauiView so gestures can be processed // Set MauiView so gestures can be processed
skiaView.MauiView = view; skiaView.MauiView = view;
Console.WriteLine($"[CollectionViewHandler.ItemViewCreator] Set MauiView={view.GetType().Name} on {skiaView.GetType().Name}, GestureRecognizers={view.GestureRecognizers?.Count ?? 0}"); DiagnosticLog.Debug("CollectionViewHandler", $"ItemViewCreator: Set MauiView={view.GetType().Name} on {skiaView.GetType().Name}, GestureRecognizers={view.GestureRecognizers?.Count ?? 0}");
return skiaView; return skiaView;
} }
} }

View File

@@ -70,13 +70,23 @@ public partial class EntryHandler : ViewHandler<IEntry, SkiaEntry>
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private bool _isUpdatingText;
private void OnTextChanged(object? sender, Platform.TextChangedEventArgs e) private void OnTextChanged(object? sender, Platform.TextChangedEventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (VirtualView is null || PlatformView is null || _isUpdatingText) return;
if (VirtualView.Text != e.NewTextValue) if (VirtualView.Text != e.NewTextValue)
{ {
VirtualView.Text = e.NewTextValue ?? string.Empty; _isUpdatingText = true;
try
{
VirtualView.Text = e.NewTextValue ?? string.Empty;
}
finally
{
_isUpdatingText = false;
}
} }
} }
@@ -87,7 +97,7 @@ public partial class EntryHandler : ViewHandler<IEntry, SkiaEntry>
public static void MapText(EntryHandler handler, IEntry entry) public static void MapText(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (handler.PlatformView is null || handler._isUpdatingText) return;
if (handler.PlatformView.Text != entry.Text) if (handler.PlatformView.Text != entry.Text)
{ {

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -13,6 +14,8 @@ namespace Microsoft.Maui.Platform.Linux.Handlers;
/// </summary> /// </summary>
public static class GestureManager public static class GestureManager
{ {
private const string Tag = "GestureManager";
private class GestureTrackingState private class GestureTrackingState
{ {
public double StartX { get; set; } public double StartX { get; set; }
@@ -35,16 +38,54 @@ public static class GestureManager
Released Released
} }
// Cached reflection MethodInfo for internal MAUI methods
private static MethodInfo? _sendTappedMethod; private static MethodInfo? _sendTappedMethod;
private static MethodInfo? _sendSwipedMethod;
private static MethodInfo? _sendPanMethod;
private static MethodInfo? _sendPinchMethod; private static MethodInfo? _sendPinchMethod;
private static readonly Dictionary<View, (DateTime lastTap, int tapCount)> _tapTracking = new Dictionary<View, (DateTime, int)>(); private static MethodInfo? _sendDragStartingMethod;
private static readonly Dictionary<View, GestureTrackingState> _gestureState = new Dictionary<View, GestureTrackingState>(); private static MethodInfo? _sendDragOverMethod;
private static MethodInfo? _sendDropMethod;
private static readonly Dictionary<PointerEventType, MethodInfo?> _pointerMethodCache = new();
private const double SwipeMinDistance = 50.0; private static readonly Dictionary<View, (DateTime lastTap, int tapCount)> _tapTracking = new();
private const double SwipeMaxTime = 500.0; private static readonly Dictionary<View, GestureTrackingState> _gestureState = new();
private const double SwipeDirectionThreshold = 0.5;
private const double PanMinDistance = 10.0; /// <summary>
private const double PinchScrollScale = 0.1; // Scale factor per scroll unit /// Minimum distance in pixels for a swipe gesture to be recognized.
/// </summary>
public static double SwipeMinDistance { get; set; } = 50.0;
/// <summary>
/// Maximum time in milliseconds for a swipe gesture to be recognized.
/// </summary>
public static double SwipeMaxTime { get; set; } = 500.0;
/// <summary>
/// Ratio threshold for determining swipe direction dominance.
/// </summary>
public static double SwipeDirectionThreshold { get; set; } = 0.5;
/// <summary>
/// Minimum distance in pixels before a pan gesture is recognized.
/// </summary>
public static double PanMinDistance { get; set; } = 10.0;
/// <summary>
/// Scale factor per scroll unit for pinch-via-scroll gestures.
/// </summary>
public static double PinchScrollScale { get; set; } = 0.1;
/// <summary>
/// Removes tracking entries for the specified view, preventing memory leaks
/// when views are disconnected from the visual tree.
/// </summary>
public static void CleanupView(View view)
{
if (view == null) return;
_tapTracking.Remove(view);
_gestureState.Remove(view);
}
/// <summary> /// <summary>
/// Processes a tap gesture on the specified view. /// Processes a tap gesture on the specified view.
@@ -79,12 +120,13 @@ public static class GestureManager
bool result = false; bool result = false;
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var tapRecognizer = (item is TapGestureRecognizer) ? (TapGestureRecognizer)item : null; if (item is not TapGestureRecognizer tapRecognizer)
if (tapRecognizer == null)
{ {
continue; continue;
} }
Console.WriteLine($"[GestureManager] Processing TapGestureRecognizer on {view.GetType().Name}, CommandParameter={tapRecognizer.CommandParameter}, NumberOfTapsRequired={tapRecognizer.NumberOfTapsRequired}"); DiagnosticLog.Debug(Tag,
$"Processing TapGestureRecognizer on {view.GetType().Name}, CommandParameter={tapRecognizer.CommandParameter}, NumberOfTapsRequired={tapRecognizer.NumberOfTapsRequired}");
int numberOfTapsRequired = tapRecognizer.NumberOfTapsRequired; int numberOfTapsRequired = tapRecognizer.NumberOfTapsRequired;
if (numberOfTapsRequired > 1) if (numberOfTapsRequired > 1)
{ {
@@ -92,108 +134,58 @@ public static class GestureManager
if (!_tapTracking.TryGetValue(view, out var tracking)) if (!_tapTracking.TryGetValue(view, out var tracking))
{ {
_tapTracking[view] = (utcNow, 1); _tapTracking[view] = (utcNow, 1);
Console.WriteLine($"[GestureManager] First tap 1/{numberOfTapsRequired}"); DiagnosticLog.Debug(Tag, $"First tap 1/{numberOfTapsRequired}");
continue; continue;
} }
if (!((utcNow - tracking.lastTap).TotalMilliseconds < 300.0)) if (!((utcNow - tracking.lastTap).TotalMilliseconds < 300.0))
{ {
_tapTracking[view] = (utcNow, 1); _tapTracking[view] = (utcNow, 1);
Console.WriteLine($"[GestureManager] Tap timeout, reset to 1/{numberOfTapsRequired}"); DiagnosticLog.Debug(Tag, $"Tap timeout, reset to 1/{numberOfTapsRequired}");
continue; continue;
} }
int tapCount = tracking.tapCount + 1; int tapCount = tracking.tapCount + 1;
if (tapCount < numberOfTapsRequired) if (tapCount < numberOfTapsRequired)
{ {
_tapTracking[view] = (utcNow, tapCount); _tapTracking[view] = (utcNow, tapCount);
Console.WriteLine($"[GestureManager] Tap {tapCount}/{numberOfTapsRequired}, waiting for more taps"); DiagnosticLog.Debug(Tag, $"Tap {tapCount}/{numberOfTapsRequired}, waiting for more taps");
continue; continue;
} }
_tapTracking.Remove(view); _tapTracking.Remove(view);
} }
// Try to raise the Tapped event via cached reflection
bool eventFired = false; bool eventFired = false;
try try
{ {
if (_sendTappedMethod == null) if (_sendTappedMethod == null)
{ {
_sendTappedMethod = typeof(TapGestureRecognizer).GetMethod("SendTapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sendTappedMethod = typeof(TapGestureRecognizer).GetMethod(
"SendTapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
} }
if (_sendTappedMethod != null) if (_sendTappedMethod != null)
{ {
Console.WriteLine($"[GestureManager] Found SendTapped method with {_sendTappedMethod.GetParameters().Length} params");
var args = new TappedEventArgs(tapRecognizer.CommandParameter); var args = new TappedEventArgs(tapRecognizer.CommandParameter);
_sendTappedMethod.Invoke(tapRecognizer, new object[] { view, args }); _sendTappedMethod.Invoke(tapRecognizer, new object[] { view, args });
Console.WriteLine("[GestureManager] SendTapped invoked successfully"); DiagnosticLog.Debug(Tag, "SendTapped invoked successfully");
eventFired = true; eventFired = true;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GestureManager] SendTapped failed: " + ex.Message); DiagnosticLog.Error(Tag, "SendTapped failed", ex);
} }
// Always invoke the Command if available (SendTapped may or may not invoke it internally)
if (!eventFired) if (!eventFired)
{ {
try ICommand? command = tapRecognizer.Command;
if (command != null && command.CanExecute(tapRecognizer.CommandParameter))
{ {
var field = typeof(TapGestureRecognizer).GetField("Tapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) DiagnosticLog.Debug(Tag, "Executing TapGestureRecognizer Command");
?? typeof(TapGestureRecognizer).GetField("_tapped", BindingFlags.Instance | BindingFlags.NonPublic); command.Execute(tapRecognizer.CommandParameter);
if (field != null && field.GetValue(tapRecognizer) is EventHandler<TappedEventArgs> handler)
{
Console.WriteLine("[GestureManager] Invoking Tapped event directly");
var args = new TappedEventArgs(tapRecognizer.CommandParameter);
handler(tapRecognizer, args);
eventFired = true;
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] Direct event invoke failed: " + ex.Message);
} }
} }
if (!eventFired)
{
try
{
string[] fieldNames = new string[] { "TappedEvent", "_TappedHandler", "<Tapped>k__BackingField" };
foreach (string fieldName in fieldNames)
{
var field = typeof(TapGestureRecognizer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
{
Console.WriteLine("[GestureManager] Found field: " + fieldName);
if (field.GetValue(tapRecognizer) is EventHandler<TappedEventArgs> handler)
{
var args = new TappedEventArgs(tapRecognizer.CommandParameter);
handler(tapRecognizer, args);
Console.WriteLine("[GestureManager] Event fired via " + fieldName);
eventFired = true;
break;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] Backing field approach failed: " + ex.Message);
}
}
if (!eventFired)
{
Console.WriteLine("[GestureManager] Could not fire event, dumping type info...");
var methods = typeof(TapGestureRecognizer).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var method in methods)
{
if (method.Name.Contains("Tap", StringComparison.OrdinalIgnoreCase) || method.Name.Contains("Send", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($"[GestureManager] Method: {method.Name}({string.Join(", ", from p in method.GetParameters() select p.ParameterType.Name)})");
}
}
}
ICommand? command = tapRecognizer.Command;
if (command != null && command.CanExecute(tapRecognizer.CommandParameter))
{
Console.WriteLine("[GestureManager] Executing Command");
command.Execute(tapRecognizer.CommandParameter);
}
result = true; result = true;
} }
return result; return result;
@@ -274,7 +266,7 @@ public static class GestureManager
} }
double deltaX = x - state.StartX; double deltaX = x - state.StartX;
double deltaY = y - state.StartY; double deltaY = y - state.StartY;
if (Math.Sqrt(deltaX * deltaX + deltaY * deltaY) >= 10.0) if (Math.Sqrt(deltaX * deltaX + deltaY * deltaY) >= PanMinDistance)
{ {
ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)(state.IsPanning ? 1 : 0)); ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)(state.IsPanning ? 1 : 0));
state.IsPanning = true; state.IsPanning = true;
@@ -299,14 +291,14 @@ public static class GestureManager
double deltaY = y - state.StartY; double deltaY = y - state.StartY;
double distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY); double distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
double elapsed = (DateTime.UtcNow - state.StartTime).TotalMilliseconds; double elapsed = (DateTime.UtcNow - state.StartTime).TotalMilliseconds;
if (distance >= 50.0 && elapsed <= 500.0) if (distance >= SwipeMinDistance && elapsed <= SwipeMaxTime)
{ {
var direction = DetermineSwipeDirection(deltaX, deltaY); var direction = DetermineSwipeDirection(deltaX, deltaY);
if (direction != SwipeDirection.Right) if (direction != SwipeDirection.Right)
{ {
ProcessSwipeGesture(view, direction); ProcessSwipeGesture(view, direction);
} }
else if (Math.Abs(deltaX) > Math.Abs(deltaY) * 0.5) else if (Math.Abs(deltaX) > Math.Abs(deltaY) * SwipeDirectionThreshold)
{ {
ProcessSwipeGesture(view, (deltaX > 0.0) ? SwipeDirection.Right : SwipeDirection.Left); ProcessSwipeGesture(view, (deltaX > 0.0) ? SwipeDirection.Right : SwipeDirection.Left);
} }
@@ -315,9 +307,9 @@ public static class GestureManager
{ {
ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)2); ProcessPanGesture(view, deltaX, deltaY, (GestureStatus)2);
} }
else if (distance < 15.0 && elapsed < 500.0) else if (distance < 15.0 && elapsed < SwipeMaxTime)
{ {
Console.WriteLine($"[GestureManager] Detected tap on {view.GetType().Name} (distance={distance:F1}, elapsed={elapsed:F0}ms)"); DiagnosticLog.Debug(Tag, $"Detected tap on {view.GetType().Name} (distance={distance:F1}, elapsed={elapsed:F0}ms)");
ProcessTap(view, x, y); ProcessTap(view, x, y);
} }
_gestureState.Remove(view); _gestureState.Remove(view);
@@ -351,7 +343,7 @@ public static class GestureManager
{ {
double absX = Math.Abs(deltaX); double absX = Math.Abs(deltaX);
double absY = Math.Abs(deltaY); double absY = Math.Abs(deltaY);
if (absX > absY * 0.5) if (absX > absY * SwipeDirectionThreshold)
{ {
if (deltaX > 0.0) if (deltaX > 0.0)
{ {
@@ -359,7 +351,7 @@ public static class GestureManager
} }
return SwipeDirection.Left; return SwipeDirection.Left;
} }
if (absY > absX * 0.5) if (absY > absX * SwipeDirectionThreshold)
{ {
if (deltaY > 0.0) if (deltaY > 0.0)
{ {
@@ -383,29 +375,34 @@ public static class GestureManager
} }
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var swipeRecognizer = (item is SwipeGestureRecognizer) ? (SwipeGestureRecognizer)item : null; if (item is not SwipeGestureRecognizer swipeRecognizer || !swipeRecognizer.Direction.HasFlag(direction))
if (swipeRecognizer == null || !swipeRecognizer.Direction.HasFlag(direction))
{ {
continue; continue;
} }
Console.WriteLine($"[GestureManager] Swipe detected: {direction}"); DiagnosticLog.Debug(Tag, $"Swipe detected: {direction}");
try try
{ {
var method = typeof(SwipeGestureRecognizer).GetMethod("SendSwiped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_sendSwipedMethod == null)
if (method != null)
{ {
method.Invoke(swipeRecognizer, new object[] { view, direction }); _sendSwipedMethod = typeof(SwipeGestureRecognizer).GetMethod(
Console.WriteLine("[GestureManager] SendSwiped invoked successfully"); "SendSwiped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendSwipedMethod != null)
{
_sendSwipedMethod.Invoke(swipeRecognizer, new object[] { view, direction });
DiagnosticLog.Debug(Tag, "SendSwiped invoked successfully");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GestureManager] SendSwiped failed: " + ex.Message); DiagnosticLog.Error(Tag, "SendSwiped failed", ex);
} }
ICommand? command = swipeRecognizer.Command; ICommand? command = swipeRecognizer.Command;
if (command != null && command.CanExecute(swipeRecognizer.CommandParameter)) if (command != null && command.CanExecute(swipeRecognizer.CommandParameter))
{ {
swipeRecognizer.Command.Execute(swipeRecognizer.CommandParameter); command.Execute(swipeRecognizer.CommandParameter);
} }
} }
} }
@@ -419,18 +416,22 @@ public static class GestureManager
} }
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var panRecognizer = (item is PanGestureRecognizer) ? (PanGestureRecognizer)item : null; if (item is not PanGestureRecognizer panRecognizer)
if (panRecognizer == null)
{ {
continue; continue;
} }
Console.WriteLine($"[GestureManager] Pan gesture: status={status}, totalX={totalX:F1}, totalY={totalY:F1}"); DiagnosticLog.Debug(Tag, $"Pan gesture: status={status}, totalX={totalX:F1}, totalY={totalY:F1}");
try try
{ {
var method = typeof(PanGestureRecognizer).GetMethod("SendPan", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_sendPanMethod == null)
if (method != null)
{ {
method.Invoke(panRecognizer, new object[] _sendPanMethod = typeof(PanGestureRecognizer).GetMethod(
"SendPan", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendPanMethod != null)
{
_sendPanMethod.Invoke(panRecognizer, new object[]
{ {
view, view,
totalX, totalX,
@@ -441,7 +442,7 @@ public static class GestureManager
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GestureManager] SendPan failed: " + ex.Message); DiagnosticLog.Error(Tag, "SendPan failed", ex);
} }
} }
} }
@@ -455,8 +456,7 @@ public static class GestureManager
} }
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var pointerRecognizer = (item is PointerGestureRecognizer) ? (PointerGestureRecognizer)item : null; if (item is not PointerGestureRecognizer pointerRecognizer)
if (pointerRecognizer == null)
{ {
continue; continue;
} }
@@ -471,19 +471,27 @@ public static class GestureManager
PointerEventType.Released => "SendPointerReleased", PointerEventType.Released => "SendPointerReleased",
_ => null, _ => null,
}; };
if (methodName != null) if (methodName == null)
{ {
var method = typeof(PointerGestureRecognizer).GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); continue;
if (method != null) }
{
var args = CreatePointerEventArgs(view, x, y); if (!_pointerMethodCache.TryGetValue(eventType, out var method))
method.Invoke(pointerRecognizer, new object[] { view, args }); {
} method = typeof(PointerGestureRecognizer).GetMethod(
methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_pointerMethodCache[eventType] = method;
}
if (method != null)
{
var args = CreatePointerEventArgs(view, x, y);
method.Invoke(pointerRecognizer, new object[] { view, args });
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GestureManager] Pointer event failed: " + ex.Message); DiagnosticLog.Error(Tag, $"Pointer event {eventType} failed", ex);
} }
} }
} }
@@ -587,26 +595,23 @@ public static class GestureManager
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var pinchRecognizer = item as PinchGestureRecognizer; if (item is not PinchGestureRecognizer pinchRecognizer)
if (pinchRecognizer == null)
{ {
continue; continue;
} }
Console.WriteLine($"[GestureManager] Pinch gesture: status={status}, scale={scale:F2}, origin=({originX:F0},{originY:F0})"); DiagnosticLog.Debug(Tag, $"Pinch gesture: status={status}, scale={scale:F2}, origin=({originX:F0},{originY:F0})");
try try
{ {
// Cache the method lookup
if (_sendPinchMethod == null) if (_sendPinchMethod == null)
{ {
_sendPinchMethod = typeof(PinchGestureRecognizer).GetMethod("SendPinch", _sendPinchMethod = typeof(PinchGestureRecognizer).GetMethod(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); "SendPinch", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
} }
if (_sendPinchMethod != null) if (_sendPinchMethod != null)
{ {
// SendPinch(IView sender, double scale, Point scaleOrigin, GestureStatus status)
var scaleOrigin = new Point(originX / view.Width, originY / view.Height); var scaleOrigin = new Point(originX / view.Width, originY / view.Height);
_sendPinchMethod.Invoke(pinchRecognizer, new object[] _sendPinchMethod.Invoke(pinchRecognizer, new object[]
{ {
@@ -615,12 +620,12 @@ public static class GestureManager
scaleOrigin, scaleOrigin,
status status
}); });
Console.WriteLine("[GestureManager] SendPinch invoked successfully"); DiagnosticLog.Debug(Tag, "SendPinch invoked successfully");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GestureManager] SendPinch failed: {ex.Message}"); DiagnosticLog.Error(Tag, "SendPinch failed", ex);
} }
} }
} }
@@ -751,26 +756,27 @@ public static class GestureManager
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var dragRecognizer = item as DragGestureRecognizer; if (item is not DragGestureRecognizer dragRecognizer) continue;
if (dragRecognizer == null) continue;
Console.WriteLine($"[GestureManager] Starting drag from {view.GetType().Name}"); DiagnosticLog.Debug(Tag, $"Starting drag from {view.GetType().Name}");
try try
{ {
// Create DragStartingEventArgs and invoke SendDragStarting if (_sendDragStartingMethod == null)
var method = typeof(DragGestureRecognizer).GetMethod("SendDragStarting",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{ {
method.Invoke(dragRecognizer, new object[] { view }); _sendDragStartingMethod = typeof(DragGestureRecognizer).GetMethod(
Console.WriteLine("[GestureManager] SendDragStarting invoked successfully"); "SendDragStarting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendDragStartingMethod != null)
{
_sendDragStartingMethod.Invoke(dragRecognizer, new object[] { view });
DiagnosticLog.Debug(Tag, "SendDragStarting invoked successfully");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GestureManager] SendDragStarting failed: {ex.Message}"); DiagnosticLog.Error(Tag, "SendDragStarting failed", ex);
} }
} }
} }
@@ -787,24 +793,26 @@ public static class GestureManager
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var dropRecognizer = item as DropGestureRecognizer; if (item is not DropGestureRecognizer dropRecognizer) continue;
if (dropRecognizer == null) continue;
Console.WriteLine($"[GestureManager] Drag enter on {view.GetType().Name}"); DiagnosticLog.Debug(Tag, $"Drag enter on {view.GetType().Name}");
try try
{ {
var method = typeof(DropGestureRecognizer).GetMethod("SendDragOver", if (_sendDragOverMethod == null)
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{ {
method.Invoke(dropRecognizer, new object[] { view }); _sendDragOverMethod = typeof(DropGestureRecognizer).GetMethod(
"SendDragOver", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendDragOverMethod != null)
{
_sendDragOverMethod.Invoke(dropRecognizer, new object[] { view });
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GestureManager] SendDragOver failed: {ex.Message}"); DiagnosticLog.Error(Tag, "SendDragOver failed", ex);
} }
} }
} }
@@ -821,24 +829,26 @@ public static class GestureManager
foreach (var item in recognizers) foreach (var item in recognizers)
{ {
var dropRecognizer = item as DropGestureRecognizer; if (item is not DropGestureRecognizer dropRecognizer) continue;
if (dropRecognizer == null) continue;
Console.WriteLine($"[GestureManager] Drop on {view.GetType().Name}"); DiagnosticLog.Debug(Tag, $"Drop on {view.GetType().Name}");
try try
{ {
var method = typeof(DropGestureRecognizer).GetMethod("SendDrop", if (_sendDropMethod == null)
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{ {
method.Invoke(dropRecognizer, new object[] { view }); _sendDropMethod = typeof(DropGestureRecognizer).GetMethod(
"SendDrop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendDropMethod != null)
{
_sendDropMethod.Invoke(dropRecognizer, new object[] { view });
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GestureManager] SendDrop failed: {ex.Message}"); DiagnosticLog.Error(Tag, "SendDrop failed", ex);
} }
} }
} }

View File

@@ -55,7 +55,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
_platformWebView.NavigationCompleted += OnNavigationCompleted; _platformWebView.NavigationCompleted += OnNavigationCompleted;
_platformWebView.ScriptDialogRequested += OnScriptDialogRequested; _platformWebView.ScriptDialogRequested += OnScriptDialogRequested;
} }
Console.WriteLine("[GtkWebViewHandler] ConnectHandler - WebView ready"); DiagnosticLog.Debug("GtkWebViewHandler", "ConnectHandler - WebView ready");
} }
protected override void DisconnectHandler(GtkWebViewProxy platformView) protected override void DisconnectHandler(GtkWebViewProxy platformView)
@@ -75,7 +75,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
private async void OnScriptDialogRequested(object? sender, private async void OnScriptDialogRequested(object? sender,
(ScriptDialogType Type, string Message, Action<bool> Callback) e) (ScriptDialogType Type, string Message, Action<bool> Callback) e)
{ {
Console.WriteLine($"[GtkWebViewHandler] Script dialog requested: type={e.Type}, message={e.Message}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Script dialog requested: type={e.Type}, message={e.Message}");
string title = e.Type switch string title = e.Type switch
{ {
@@ -92,18 +92,18 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
{ {
bool result = await LinuxDialogService.ShowAlertAsync(title, e.Message, acceptButton, cancelButton); bool result = await LinuxDialogService.ShowAlertAsync(title, e.Message, acceptButton, cancelButton);
e.Callback(result); e.Callback(result);
Console.WriteLine($"[GtkWebViewHandler] Dialog result: {result}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Dialog result: {result}");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewHandler] Error showing dialog: {ex.Message}"); DiagnosticLog.Error("GtkWebViewHandler", $"Error showing dialog: {ex.Message}", ex);
e.Callback(false); e.Callback(false);
} }
} }
private void OnNavigationStarted(object? sender, string uri) private void OnNavigationStarted(object? sender, string uri)
{ {
Console.WriteLine($"[GtkWebViewHandler] Navigation started: {uri}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Navigation started: {uri}");
try try
{ {
GLibNative.IdleAdd(() => GLibNative.IdleAdd(() =>
@@ -115,25 +115,25 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
var args = new Microsoft.Maui.Controls.WebNavigatingEventArgs( var args = new Microsoft.Maui.Controls.WebNavigatingEventArgs(
WebNavigationEvent.NewPage, null, uri); WebNavigationEvent.NewPage, null, uri);
controller.SendNavigating(args); controller.SendNavigating(args);
Console.WriteLine("[GtkWebViewHandler] Sent Navigating event to VirtualView"); DiagnosticLog.Debug("GtkWebViewHandler", "Sent Navigating event to VirtualView");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewHandler] Error in SendNavigating: {ex.Message}"); DiagnosticLog.Error("GtkWebViewHandler", $"Error in SendNavigating: {ex.Message}", ex);
} }
return false; return false;
}); });
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewHandler] Error dispatching navigation started: {ex.Message}"); DiagnosticLog.Error("GtkWebViewHandler", $"Error dispatching navigation started: {ex.Message}", ex);
} }
} }
private void OnNavigationCompleted(object? sender, (string Url, bool Success) e) private void OnNavigationCompleted(object? sender, (string Url, bool Success) e)
{ {
Console.WriteLine($"[GtkWebViewHandler] Navigation completed: {e.Url} (Success: {e.Success})"); DiagnosticLog.Debug("GtkWebViewHandler", $"Navigation completed: {e.Url} (Success: {e.Success})");
try try
{ {
GLibNative.IdleAdd(() => GLibNative.IdleAdd(() =>
@@ -151,19 +151,19 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
bool canGoForward = _platformWebView?.CanGoForward() ?? false; bool canGoForward = _platformWebView?.CanGoForward() ?? false;
controller.CanGoBack = canGoBack; controller.CanGoBack = canGoBack;
controller.CanGoForward = canGoForward; controller.CanGoForward = canGoForward;
Console.WriteLine($"[GtkWebViewHandler] Sent Navigated, CanGoBack={canGoBack}, CanGoForward={canGoForward}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Sent Navigated, CanGoBack={canGoBack}, CanGoForward={canGoForward}");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewHandler] Error in SendNavigated: {ex.Message}"); DiagnosticLog.Error("GtkWebViewHandler", $"Error in SendNavigated: {ex.Message}", ex);
} }
return false; return false;
}); });
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewHandler] Error dispatching navigation completed: {ex.Message}"); DiagnosticLog.Error("GtkWebViewHandler", $"Error dispatching navigation completed: {ex.Message}", ex);
} }
} }
@@ -175,7 +175,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
var hostService = GtkHostService.Instance; var hostService = GtkHostService.Instance;
if (hostService.HostWindow == null || hostService.WebViewManager == null) if (hostService.HostWindow == null || hostService.WebViewManager == null)
{ {
Console.WriteLine("[GtkWebViewHandler] Warning: GTK host not initialized, cannot register WebView"); DiagnosticLog.Warn("GtkWebViewHandler", "GTK host not initialized, cannot register WebView");
return; return;
} }
@@ -186,7 +186,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0)
{ {
Console.WriteLine($"[GtkWebViewHandler] Skipping invalid bounds: {bounds}"); DiagnosticLog.Warn("GtkWebViewHandler", $"Skipping invalid bounds: {bounds}");
return; return;
} }
@@ -194,12 +194,12 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
{ {
hostService.HostWindow.AddWebView(_platformWebView.Widget, x, y, width, height); hostService.HostWindow.AddWebView(_platformWebView.Widget, x, y, width, height);
_isRegisteredWithHost = true; _isRegisteredWithHost = true;
Console.WriteLine($"[GtkWebViewHandler] Registered WebView at ({x}, {y}) size {width}x{height}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Registered WebView at ({x}, {y}) size {width}x{height}");
} }
else if (bounds != _lastBounds) else if (bounds != _lastBounds)
{ {
hostService.HostWindow.MoveResizeWebView(_platformWebView.Widget, x, y, width, height); hostService.HostWindow.MoveResizeWebView(_platformWebView.Widget, x, y, width, height);
Console.WriteLine($"[GtkWebViewHandler] Updated WebView to ({x}, {y}) size {width}x{height}"); DiagnosticLog.Debug("GtkWebViewHandler", $"Updated WebView to ({x}, {y}) size {width}x{height}");
} }
_lastBounds = bounds; _lastBounds = bounds;
@@ -213,7 +213,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
if (hostService.HostWindow != null) if (hostService.HostWindow != null)
{ {
hostService.HostWindow.RemoveWebView(_platformWebView.Widget); hostService.HostWindow.RemoveWebView(_platformWebView.Widget);
Console.WriteLine("[GtkWebViewHandler] Unregistered WebView from host"); DiagnosticLog.Debug("GtkWebViewHandler", "Unregistered WebView from host");
} }
_isRegisteredWithHost = false; _isRegisteredWithHost = false;
} }
@@ -225,7 +225,7 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
return; return;
var source = webView.Source; var source = webView.Source;
Console.WriteLine($"[GtkWebViewHandler] MapSource: {source?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("GtkWebViewHandler", $"MapSource: {source?.GetType().Name ?? "null"}");
if (source is UrlWebViewSource urlSource) if (source is UrlWebViewSource urlSource)
{ {
@@ -247,19 +247,19 @@ public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
public static void MapGoBack(GtkWebViewHandler handler, IWebView webView, object? args) public static void MapGoBack(GtkWebViewHandler handler, IWebView webView, object? args)
{ {
Console.WriteLine($"[GtkWebViewHandler] MapGoBack called, CanGoBack={handler._platformWebView?.CanGoBack()}"); DiagnosticLog.Debug("GtkWebViewHandler", $"MapGoBack called, CanGoBack={handler._platformWebView?.CanGoBack()}");
handler._platformWebView?.GoBack(); handler._platformWebView?.GoBack();
} }
public static void MapGoForward(GtkWebViewHandler handler, IWebView webView, object? args) public static void MapGoForward(GtkWebViewHandler handler, IWebView webView, object? args)
{ {
Console.WriteLine($"[GtkWebViewHandler] MapGoForward called, CanGoForward={handler._platformWebView?.CanGoForward()}"); DiagnosticLog.Debug("GtkWebViewHandler", $"MapGoForward called, CanGoForward={handler._platformWebView?.CanGoForward()}");
handler._platformWebView?.GoForward(); handler._platformWebView?.GoForward();
} }
public static void MapReload(GtkWebViewHandler handler, IWebView webView, object? args) public static void MapReload(GtkWebViewHandler handler, IWebView webView, object? args)
{ {
Console.WriteLine("[GtkWebViewHandler] MapReload called"); DiagnosticLog.Debug("GtkWebViewHandler", "MapReload called");
handler._platformWebView?.Reload(); handler._platformWebView?.Reload();
} }
} }

View File

@@ -74,7 +74,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
Microsoft.Maui.Controls.Application.Current.RequestedThemeChanged += _themeChangedHandler; Microsoft.Maui.Controls.Application.Current.RequestedThemeChanged += _themeChangedHandler;
} }
Console.WriteLine("[GtkWebViewPlatformView] Created WebKitWebView widget"); DiagnosticLog.Debug("GtkWebViewPlatformView", "Created WebKitWebView widget");
} }
/// <summary> /// <summary>
@@ -105,7 +105,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
var dialogType = (ScriptDialogType)(int)webkitDialogType; var dialogType = (ScriptDialogType)(int)webkitDialogType;
var message = WebKitNative.GetScriptDialogMessage(dialog) ?? ""; var message = WebKitNative.GetScriptDialogMessage(dialog) ?? "";
Console.WriteLine($"[GtkWebViewPlatformView] Script dialog: type={dialogType}, message={message}"); DiagnosticLog.Debug("GtkWebViewPlatformView", $"Script dialog: type={dialogType}, message={message}");
// Get the parent window for proper modal behavior // Get the parent window for proper modal behavior
IntPtr parentWindow = GtkHostService.Instance.HostWindow?.Window ?? IntPtr.Zero; IntPtr parentWindow = GtkHostService.Instance.HostWindow?.Window ?? IntPtr.Zero;
@@ -166,7 +166,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
// Run the dialog synchronously - this blocks until user responds // Run the dialog synchronously - this blocks until user responds
int response = GtkNative.gtk_dialog_run(gtkDialog); int response = GtkNative.gtk_dialog_run(gtkDialog);
Console.WriteLine($"[GtkWebViewPlatformView] Dialog response: {response}"); DiagnosticLog.Debug("GtkWebViewPlatformView", $"Dialog response: {response}");
// Set the confirmed state for confirm dialogs // Set the confirmed state for confirm dialogs
if (dialogType == ScriptDialogType.Confirm || dialogType == ScriptDialogType.BeforeUnloadConfirm) if (dialogType == ScriptDialogType.Confirm || dialogType == ScriptDialogType.BeforeUnloadConfirm)
@@ -184,7 +184,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewPlatformView] Error in OnScriptDialog: {ex.Message}"); DiagnosticLog.Error("GtkWebViewPlatformView", $"Error in OnScriptDialog: {ex.Message}", ex);
// Return false on error to let WebKitGTK try its default handling // Return false on error to let WebKitGTK try its default handling
return false; return false;
} }
@@ -210,7 +210,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
if (gtkDialog == IntPtr.Zero) if (gtkDialog == IntPtr.Zero)
{ {
Console.WriteLine("[GtkWebViewPlatformView] Failed to create prompt dialog"); DiagnosticLog.Error("GtkWebViewPlatformView", "Failed to create prompt dialog");
return false; return false;
} }
@@ -251,7 +251,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
// Run the dialog // Run the dialog
int response = GtkNative.gtk_dialog_run(gtkDialog); int response = GtkNative.gtk_dialog_run(gtkDialog);
Console.WriteLine($"[GtkWebViewPlatformView] Prompt dialog response: {response}"); DiagnosticLog.Debug("GtkWebViewPlatformView", $"Prompt dialog response: {response}");
if (response == GtkNative.GTK_RESPONSE_OK) if (response == GtkNative.GTK_RESPONSE_OK)
{ {
@@ -261,7 +261,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
? System.Runtime.InteropServices.Marshal.PtrToStringUTF8(textPtr) ? System.Runtime.InteropServices.Marshal.PtrToStringUTF8(textPtr)
: ""; : "";
Console.WriteLine($"[GtkWebViewPlatformView] Prompt text: {enteredText}"); DiagnosticLog.Debug("GtkWebViewPlatformView", $"Prompt text: {enteredText}");
// Set the prompt response // Set the prompt response
WebKitNative.SetScriptDialogPromptText(webkitDialog, enteredText ?? ""); WebKitNative.SetScriptDialogPromptText(webkitDialog, enteredText ?? "");
@@ -278,7 +278,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewPlatformView] Error in HandlePromptDialog: {ex.Message}"); DiagnosticLog.Error("GtkWebViewPlatformView", $"Error in HandlePromptDialog: {ex.Message}", ex);
return false; return false;
} }
} }
@@ -299,7 +299,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark;
} }
Console.WriteLine($"[GtkWebViewPlatformView] ApplyDialogTheme: isDark={isDark}, UserAppTheme={Microsoft.Maui.Controls.Application.Current?.UserAppTheme}"); DiagnosticLog.Debug("GtkWebViewPlatformView", $"ApplyDialogTheme: isDark={isDark}, UserAppTheme={Microsoft.Maui.Controls.Application.Current?.UserAppTheme}");
// Create comprehensive CSS based on the theme - targeting all dialog elements // Create comprehensive CSS based on the theme - targeting all dialog elements
string css = isDark string css = isDark
@@ -407,7 +407,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkWebViewPlatformView] Error applying dialog theme: {ex.Message}"); DiagnosticLog.Error("GtkWebViewPlatformView", $"Error applying dialog theme: {ex.Message}", ex);
} }
} }
@@ -419,17 +419,17 @@ public sealed class GtkWebViewPlatformView : IDisposable
switch ((WebKitNative.WebKitLoadEvent)loadEvent) switch ((WebKitNative.WebKitLoadEvent)loadEvent)
{ {
case WebKitNative.WebKitLoadEvent.Started: case WebKitNative.WebKitLoadEvent.Started:
Console.WriteLine("[GtkWebViewPlatformView] Load started: " + uri); DiagnosticLog.Debug("GtkWebViewPlatformView", "Load started: " + uri);
NavigationStarted?.Invoke(this, uri); NavigationStarted?.Invoke(this, uri);
break; break;
case WebKitNative.WebKitLoadEvent.Finished: case WebKitNative.WebKitLoadEvent.Finished:
_currentUri = uri; _currentUri = uri;
Console.WriteLine("[GtkWebViewPlatformView] Load finished: " + uri); DiagnosticLog.Debug("GtkWebViewPlatformView", "Load finished: " + uri);
NavigationCompleted?.Invoke(this, (uri, true)); NavigationCompleted?.Invoke(this, (uri, true));
break; break;
case WebKitNative.WebKitLoadEvent.Committed: case WebKitNative.WebKitLoadEvent.Committed:
_currentUri = uri; _currentUri = uri;
Console.WriteLine("[GtkWebViewPlatformView] Load committed: " + uri); DiagnosticLog.Debug("GtkWebViewPlatformView", "Load committed: " + uri);
break; break;
case WebKitNative.WebKitLoadEvent.Redirected: case WebKitNative.WebKitLoadEvent.Redirected:
break; break;
@@ -437,8 +437,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GtkWebViewPlatformView] Error in OnLoadChanged: " + ex.Message); DiagnosticLog.Error("GtkWebViewPlatformView", "Error in OnLoadChanged: " + ex.Message, ex);
Console.WriteLine("[GtkWebViewPlatformView] Stack trace: " + ex.StackTrace);
} }
} }
@@ -447,7 +446,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
if (_widget != IntPtr.Zero) if (_widget != IntPtr.Zero)
{ {
WebKitNative.LoadUri(_widget, uri); WebKitNative.LoadUri(_widget, uri);
Console.WriteLine("[GtkWebViewPlatformView] Navigate to: " + uri); DiagnosticLog.Debug("GtkWebViewPlatformView", "Navigate to: " + uri);
} }
} }
@@ -456,7 +455,7 @@ public sealed class GtkWebViewPlatformView : IDisposable
if (_widget != IntPtr.Zero) if (_widget != IntPtr.Zero)
{ {
WebKitNative.LoadHtml(_widget, html, baseUri); WebKitNative.LoadHtml(_widget, html, baseUri);
Console.WriteLine("[GtkWebViewPlatformView] Load HTML content"); DiagnosticLog.Debug("GtkWebViewPlatformView", "Load HTML content");
} }
} }

View File

@@ -5,6 +5,7 @@ using System.IO;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -127,7 +128,7 @@ public partial class ImageHandler : ViewHandler<IImage, SkiaImage>
if (image is Image img && img.WidthRequest > 0) if (image is Image img && img.WidthRequest > 0)
{ {
handler.PlatformView.WidthRequest = img.WidthRequest; handler.PlatformView.WidthRequest = img.WidthRequest;
Console.WriteLine($"[ImageHandler] MapWidth: {img.WidthRequest}"); DiagnosticLog.Debug("ImageHandler", $"MapWidth: {img.WidthRequest}");
} }
else if (image.Width > 0) else if (image.Width > 0)
{ {
@@ -142,7 +143,7 @@ public partial class ImageHandler : ViewHandler<IImage, SkiaImage>
if (image is Image img && img.HeightRequest > 0) if (image is Image img && img.HeightRequest > 0)
{ {
handler.PlatformView.HeightRequest = img.HeightRequest; handler.PlatformView.HeightRequest = img.HeightRequest;
Console.WriteLine($"[ImageHandler] MapHeight: {img.HeightRequest}"); DiagnosticLog.Debug("ImageHandler", $"MapHeight: {img.HeightRequest}");
} }
else if (image.Height > 0) else if (image.Height > 0)
{ {

View File

@@ -3,6 +3,7 @@
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -279,13 +280,13 @@ public partial class GridHandler : LayoutHandler
protected override void ConnectHandler(SkiaLayoutView platformView) protected override void ConnectHandler(SkiaLayoutView platformView)
{ {
Console.WriteLine($"[GridHandler.ConnectHandler] Called! VirtualView={VirtualView?.GetType().Name}, PlatformView={platformView?.GetType().Name}, MauiContext={(MauiContext != null ? "set" : "null")}"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Called! VirtualView={VirtualView?.GetType().Name}, PlatformView={platformView?.GetType().Name}, MauiContext={(MauiContext != null ? "set" : "null")}");
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
// Map definitions on connect // Map definitions on connect
if (VirtualView is IGridLayout gridLayout && platformView is SkiaGrid grid && MauiContext != null) if (VirtualView is IGridLayout gridLayout && platformView is SkiaGrid grid && MauiContext != null)
{ {
Console.WriteLine($"[GridHandler.ConnectHandler] Grid has {gridLayout.Count} children, RowDefs={gridLayout.RowDefinitions?.Count ?? 0}"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Grid has {gridLayout.Count} children, RowDefs={gridLayout.RowDefinitions?.Count ?? 0}");
UpdateRowDefinitions(grid, gridLayout); UpdateRowDefinitions(grid, gridLayout);
UpdateColumnDefinitions(grid, gridLayout); UpdateColumnDefinitions(grid, gridLayout);
@@ -295,13 +296,13 @@ public partial class GridHandler : LayoutHandler
var child = gridLayout[i]; var child = gridLayout[i];
if (child == null) continue; if (child == null) continue;
Console.WriteLine($"[GridHandler.ConnectHandler] Child[{i}]: {child.GetType().Name}, Handler={child.Handler?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Child[{i}]: {child.GetType().Name}, Handler={child.Handler?.GetType().Name ?? "null"}");
// Create handler for child if it doesn't exist // Create handler for child if it doesn't exist
if (child.Handler == null) if (child.Handler == null)
{ {
child.Handler = child.ToViewHandler(MauiContext); child.Handler = child.ToViewHandler(MauiContext);
Console.WriteLine($"[GridHandler.ConnectHandler] Created handler for child[{i}]: {child.Handler?.GetType().Name ?? "failed"}"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Created handler for child[{i}]: {child.Handler?.GetType().Name ?? "failed"}");
} }
if (child.Handler?.PlatformView is SkiaView skiaChild) if (child.Handler?.PlatformView is SkiaView skiaChild)
@@ -315,11 +316,11 @@ public partial class GridHandler : LayoutHandler
rowSpan = Microsoft.Maui.Controls.Grid.GetRowSpan(mauiView); rowSpan = Microsoft.Maui.Controls.Grid.GetRowSpan(mauiView);
columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView); columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView);
} }
Console.WriteLine($"[GridHandler.ConnectHandler] Adding child[{i}] at row={row}, col={column}"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Adding child[{i}] at row={row}, col={column}");
grid.AddChild(skiaChild, row, column, rowSpan, columnSpan); grid.AddChild(skiaChild, row, column, rowSpan, columnSpan);
} }
} }
Console.WriteLine($"[GridHandler.ConnectHandler] Grid now has {grid.Children.Count} SkiaView children"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler Grid now has {grid.Children.Count} SkiaView children");
} }
} }

View File

@@ -4,6 +4,7 @@
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -245,7 +246,7 @@ public partial class GridHandler : LayoutHandler
// Don't call base - we handle children specially for Grid // Don't call base - we handle children specially for Grid
if (VirtualView is not IGridLayout gridLayout || MauiContext == null || platformView is not SkiaGrid grid) return; if (VirtualView is not IGridLayout gridLayout || MauiContext == null || platformView is not SkiaGrid grid) return;
Console.WriteLine($"[GridHandler] ConnectHandler: {gridLayout.Count} children, {gridLayout.RowDefinitions.Count} rows, {gridLayout.ColumnDefinitions.Count} cols"); DiagnosticLog.Debug("GridHandler", $"ConnectHandler: {gridLayout.Count} children, {gridLayout.RowDefinitions.Count} rows, {gridLayout.ColumnDefinitions.Count} cols");
// Explicitly map BackgroundColor since it may be set before handler creation // Explicitly map BackgroundColor since it may be set before handler creation
if (VirtualView is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null) if (VirtualView is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
@@ -258,7 +259,7 @@ public partial class GridHandler : LayoutHandler
{ {
var padding = paddable.Padding; var padding = paddable.Padding;
platformView.Padding = padding; platformView.Padding = padding;
Console.WriteLine($"[GridHandler] Applied Padding: L={padding.Left}, T={padding.Top}, R={padding.Right}, B={padding.Bottom}"); DiagnosticLog.Debug("GridHandler", $"Applied Padding: L={padding.Left}, T={padding.Top}, R={padding.Right}, B={padding.Bottom}");
} }
// Map row/column definitions first // Map row/column definitions first
@@ -271,7 +272,7 @@ public partial class GridHandler : LayoutHandler
var child = gridLayout[i]; var child = gridLayout[i];
if (child == null) continue; if (child == null) continue;
Console.WriteLine($"[GridHandler] Processing child {i}: {child.GetType().Name}"); DiagnosticLog.Debug("GridHandler", $"Processing child {i}: {child.GetType().Name}");
// Create handler for child if it doesn't exist // Create handler for child if it doesn't exist
if (child.Handler == null) if (child.Handler == null)
@@ -289,21 +290,20 @@ public partial class GridHandler : LayoutHandler
columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView); columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView);
} }
Console.WriteLine($"[GridHandler] Child {i} at row={row}, col={column}, handler={child.Handler?.GetType().Name}"); DiagnosticLog.Debug("GridHandler", $"Child {i} at row={row}, col={column}, handler={child.Handler?.GetType().Name}");
// Add child's platform view to our grid // Add child's platform view to our grid
if (child.Handler?.PlatformView is SkiaView skiaChild) if (child.Handler?.PlatformView is SkiaView skiaChild)
{ {
grid.AddChild(skiaChild, row, column, rowSpan, columnSpan); grid.AddChild(skiaChild, row, column, rowSpan, columnSpan);
Console.WriteLine($"[GridHandler] Added child {i} to grid"); DiagnosticLog.Debug("GridHandler", $"Added child {i} to grid");
} }
} }
Console.WriteLine($"[GridHandler] ConnectHandler complete"); DiagnosticLog.Debug("GridHandler", "ConnectHandler complete");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GridHandler] EXCEPTION in ConnectHandler: {ex.GetType().Name}: {ex.Message}"); DiagnosticLog.Error("GridHandler", $"EXCEPTION in ConnectHandler: {ex.GetType().Name}: {ex.Message}", ex);
Console.WriteLine($"[GridHandler] Stack trace: {ex.StackTrace}");
throw; throw;
} }
} }

View File

@@ -7,6 +7,7 @@ using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
using Svg.Skia; using Svg.Skia;
using System.Collections.Specialized; using System.Collections.Specialized;
@@ -88,12 +89,12 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
// Get all pages in the navigation stack // Get all pages in the navigation stack
var pages = VirtualView.Navigation.NavigationStack.ToList(); var pages = VirtualView.Navigation.NavigationStack.ToList();
Console.WriteLine($"[NavigationPageHandler] Setting up {pages.Count} pages"); DiagnosticLog.Debug("NavigationPageHandler", $"Setting up {pages.Count} pages");
// If no pages in stack, check CurrentPage // If no pages in stack, check CurrentPage
if (pages.Count == 0 && VirtualView.CurrentPage != null) if (pages.Count == 0 && VirtualView.CurrentPage != null)
{ {
Console.WriteLine($"[NavigationPageHandler] No pages in stack, using CurrentPage: {VirtualView.CurrentPage.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"No pages in stack, using CurrentPage: {VirtualView.CurrentPage.Title}");
pages.Add(VirtualView.CurrentPage); pages.Add(VirtualView.CurrentPage);
} }
@@ -102,12 +103,12 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
// Ensure the page has a handler // Ensure the page has a handler
if (page.Handler == null) if (page.Handler == null)
{ {
Console.WriteLine($"[NavigationPageHandler] Creating handler for: {page.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"Creating handler for: {page.Title}");
page.Handler = page.ToViewHandler(MauiContext); page.Handler = page.ToViewHandler(MauiContext);
} }
Console.WriteLine($"[NavigationPageHandler] Page handler type: {page.Handler?.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Page handler type: {page.Handler?.GetType().Name}");
Console.WriteLine($"[NavigationPageHandler] Page PlatformView type: {page.Handler?.PlatformView?.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Page PlatformView type: {page.Handler?.PlatformView?.GetType().Name}");
if (page.Handler?.PlatformView is SkiaPage skiaPage) if (page.Handler?.PlatformView is SkiaPage skiaPage)
{ {
@@ -117,12 +118,12 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
skiaPage.TitleTextColor = PlatformView.BarTextColor; skiaPage.TitleTextColor = PlatformView.BarTextColor;
skiaPage.Title = page.Title ?? ""; skiaPage.Title = page.Title ?? "";
Console.WriteLine($"[NavigationPageHandler] SkiaPage content: {skiaPage.Content?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("NavigationPageHandler", $"SkiaPage content: {skiaPage.Content?.GetType().Name ?? "null"}");
// If content is null, try to get it from ContentPage // If content is null, try to get it from ContentPage
if (skiaPage.Content == null && page is ContentPage contentPage && contentPage.Content != null) if (skiaPage.Content == null && page is ContentPage contentPage && contentPage.Content != null)
{ {
Console.WriteLine($"[NavigationPageHandler] Content is null, manually creating handler for: {contentPage.Content.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Content is null, manually creating handler for: {contentPage.Content.GetType().Name}");
if (contentPage.Content.Handler == null) if (contentPage.Content.Handler == null)
{ {
contentPage.Content.Handler = contentPage.Content.ToViewHandler(MauiContext); contentPage.Content.Handler = contentPage.Content.ToViewHandler(MauiContext);
@@ -130,7 +131,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent) if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent)
{ {
skiaPage.Content = skiaContent; skiaPage.Content = skiaContent;
Console.WriteLine($"[NavigationPageHandler] Set content to: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Set content to: {skiaContent.GetType().Name}");
} }
} }
@@ -139,18 +140,18 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
if (PlatformView.StackDepth == 0) if (PlatformView.StackDepth == 0)
{ {
Console.WriteLine($"[NavigationPageHandler] Setting root page: {page.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"Setting root page: {page.Title}");
PlatformView.SetRootPage(skiaPage); PlatformView.SetRootPage(skiaPage);
} }
else else
{ {
Console.WriteLine($"[NavigationPageHandler] Pushing page: {page.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"Pushing page: {page.Title}");
PlatformView.Push(skiaPage, false); PlatformView.Push(skiaPage, false);
} }
} }
else else
{ {
Console.WriteLine($"[NavigationPageHandler] Failed to get SkiaPage for: {page.Title}"); DiagnosticLog.Warn("NavigationPageHandler", $"Failed to get SkiaPage for: {page.Title}");
} }
} }
} }
@@ -161,12 +162,12 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
{ {
if (skiaPage is SkiaContentPage contentPage) if (skiaPage is SkiaContentPage contentPage)
{ {
Console.WriteLine($"[NavigationPageHandler] MapToolbarItems for '{page.Title}', count={page.ToolbarItems.Count}"); DiagnosticLog.Debug("NavigationPageHandler", $"MapToolbarItems for '{page.Title}', count={page.ToolbarItems.Count}");
contentPage.ToolbarItems.Clear(); contentPage.ToolbarItems.Clear();
foreach (var item in page.ToolbarItems) foreach (var item in page.ToolbarItems)
{ {
Console.WriteLine($"[NavigationPageHandler] Adding toolbar item: '{item.Text}', IconImageSource={item.IconImageSource}, Order={item.Order}"); DiagnosticLog.Debug("NavigationPageHandler", $"Adding toolbar item: '{item.Text}', IconImageSource={item.IconImageSource}, Order={item.Order}");
// Default and Primary should both be treated as Primary (shown in toolbar) // Default and Primary should both be treated as Primary (shown in toolbar)
// Only Secondary goes to overflow menu // Only Secondary goes to overflow menu
var order = item.Order == ToolbarItemOrder.Secondary var order = item.Order == ToolbarItemOrder.Secondary
@@ -177,7 +178,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
var toolbarItem = item; // Capture for closure var toolbarItem = item; // Capture for closure
var clickCommand = new RelayCommand(() => var clickCommand = new RelayCommand(() =>
{ {
Console.WriteLine($"[NavigationPageHandler] ToolbarItem '{toolbarItem.Text}' clicked, invoking..."); DiagnosticLog.Debug("NavigationPageHandler", $"ToolbarItem '{toolbarItem.Text}' clicked, invoking...");
// Use IMenuItemController to send the click // Use IMenuItemController to send the click
if (toolbarItem is IMenuItemController menuController) if (toolbarItem is IMenuItemController menuController)
{ {
@@ -209,10 +210,10 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
// Subscribe to ToolbarItems changes if not already subscribed // Subscribe to ToolbarItems changes if not already subscribed
if (page.ToolbarItems is INotifyCollectionChanged notifyCollection && !_toolbarSubscriptions.ContainsKey(page)) if (page.ToolbarItems is INotifyCollectionChanged notifyCollection && !_toolbarSubscriptions.ContainsKey(page))
{ {
Console.WriteLine($"[NavigationPageHandler] Subscribing to ToolbarItems changes for '{page.Title}'"); DiagnosticLog.Debug("NavigationPageHandler", $"Subscribing to ToolbarItems changes for '{page.Title}'");
notifyCollection.CollectionChanged += (s, e) => notifyCollection.CollectionChanged += (s, e) =>
{ {
Console.WriteLine($"[NavigationPageHandler] ToolbarItems changed for '{page.Title}', action={e.Action}"); DiagnosticLog.Debug("NavigationPageHandler", $"ToolbarItems changed for '{page.Title}', action={e.Action}");
MapToolbarItems(skiaPage, page); MapToolbarItems(skiaPage, page);
skiaPage.Invalidate(); skiaPage.Invalidate();
}; };
@@ -229,9 +230,9 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
string pngPath = Path.Combine(baseDirectory, fileName); string pngPath = Path.Combine(baseDirectory, fileName);
string svgPath = Path.Combine(baseDirectory, Path.ChangeExtension(fileName, ".svg")); string svgPath = Path.Combine(baseDirectory, Path.ChangeExtension(fileName, ".svg"));
Console.WriteLine($"[NavigationPageHandler] LoadToolbarIcon: Looking for {fileName}"); DiagnosticLog.Debug("NavigationPageHandler", $"LoadToolbarIcon: Looking for {fileName}");
Console.WriteLine($"[NavigationPageHandler] Trying PNG: {pngPath} (exists: {File.Exists(pngPath)})"); DiagnosticLog.Debug("NavigationPageHandler", $" Trying PNG: {pngPath} (exists: {File.Exists(pngPath)})");
Console.WriteLine($"[NavigationPageHandler] Trying SVG: {svgPath} (exists: {File.Exists(svgPath)})"); DiagnosticLog.Debug("NavigationPageHandler", $" Trying SVG: {svgPath} (exists: {File.Exists(svgPath)})");
// Try SVG first // Try SVG first
if (File.Exists(svgPath)) if (File.Exists(svgPath))
@@ -247,7 +248,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
canvas.Clear(SKColors.Transparent); canvas.Clear(SKColors.Transparent);
canvas.Scale(scale); canvas.Scale(scale);
canvas.DrawPicture(svg.Picture, null); canvas.DrawPicture(svg.Picture, null);
Console.WriteLine($"[NavigationPageHandler] Loaded SVG icon: {svgPath}"); DiagnosticLog.Debug("NavigationPageHandler", $"Loaded SVG icon: {svgPath}");
return bitmap; return bitmap;
} }
} }
@@ -257,16 +258,16 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
{ {
using var stream = File.OpenRead(pngPath); using var stream = File.OpenRead(pngPath);
var result = SKBitmap.Decode(stream); var result = SKBitmap.Decode(stream);
Console.WriteLine($"[NavigationPageHandler] Loaded PNG icon: {pngPath}"); DiagnosticLog.Debug("NavigationPageHandler", $"Loaded PNG icon: {pngPath}");
return result; return result;
} }
Console.WriteLine($"[NavigationPageHandler] Icon not found: {fileName}"); DiagnosticLog.Warn("NavigationPageHandler", $"Icon not found: {fileName}");
return null; return null;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[NavigationPageHandler] Error loading icon {fileName}: {ex.Message}"); DiagnosticLog.Error("NavigationPageHandler", $"Error loading icon {fileName}: {ex.Message}", ex);
return null; return null;
} }
} }
@@ -275,20 +276,20 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
{ {
try try
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView Pushed: {e.Page?.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"VirtualView Pushed: {e.Page?.Title}");
if (e.Page == null || PlatformView == null || MauiContext == null) return; if (e.Page == null || PlatformView == null || MauiContext == null) return;
// Ensure the page has a handler // Ensure the page has a handler
if (e.Page.Handler == null) if (e.Page.Handler == null)
{ {
Console.WriteLine($"[NavigationPageHandler] Creating handler for page: {e.Page.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Creating handler for page: {e.Page.GetType().Name}");
e.Page.Handler = e.Page.ToViewHandler(MauiContext); e.Page.Handler = e.Page.ToViewHandler(MauiContext);
Console.WriteLine($"[NavigationPageHandler] Handler created: {e.Page.Handler?.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Handler created: {e.Page.Handler?.GetType().Name}");
} }
if (e.Page.Handler?.PlatformView is SkiaPage skiaPage) if (e.Page.Handler?.PlatformView is SkiaPage skiaPage)
{ {
Console.WriteLine($"[NavigationPageHandler] Setting up skiaPage, content: {skiaPage.Content?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("NavigationPageHandler", $"Setting up skiaPage, content: {skiaPage.Content?.GetType().Name ?? "null"}");
skiaPage.ShowNavigationBar = true; skiaPage.ShowNavigationBar = true;
skiaPage.TitleBarColor = PlatformView.BarBackgroundColor; skiaPage.TitleBarColor = PlatformView.BarBackgroundColor;
skiaPage.TitleTextColor = PlatformView.BarTextColor; skiaPage.TitleTextColor = PlatformView.BarTextColor;
@@ -297,7 +298,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
// Handle content if null // Handle content if null
if (skiaPage.Content == null && e.Page is ContentPage contentPage && contentPage.Content != 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}"); DiagnosticLog.Debug("NavigationPageHandler", $"Content is null, creating handler for: {contentPage.Content.GetType().Name}");
if (contentPage.Content.Handler == null) if (contentPage.Content.Handler == null)
{ {
contentPage.Content.Handler = contentPage.Content.ToViewHandler(MauiContext); contentPage.Content.Handler = contentPage.Content.ToViewHandler(MauiContext);
@@ -305,36 +306,35 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent) if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent)
{ {
skiaPage.Content = skiaContent; skiaPage.Content = skiaContent;
Console.WriteLine($"[NavigationPageHandler] Set content to: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("NavigationPageHandler", $"Set content to: {skiaContent.GetType().Name}");
} }
} }
Console.WriteLine($"[NavigationPageHandler] Mapping toolbar items"); DiagnosticLog.Debug("NavigationPageHandler", "Mapping toolbar items");
MapToolbarItems(skiaPage, e.Page); MapToolbarItems(skiaPage, e.Page);
Console.WriteLine($"[NavigationPageHandler] Pushing page to platform"); DiagnosticLog.Debug("NavigationPageHandler", "Pushing page to platform");
PlatformView.Push(skiaPage, false); PlatformView.Push(skiaPage, false);
Console.WriteLine($"[NavigationPageHandler] Push complete, thread={Environment.CurrentManagedThreadId}"); DiagnosticLog.Debug("NavigationPageHandler", $"Push complete, thread={Environment.CurrentManagedThreadId}");
} }
Console.WriteLine("[NavigationPageHandler] OnVirtualViewPushed returning"); DiagnosticLog.Debug("NavigationPageHandler", "OnVirtualViewPushed returning");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[NavigationPageHandler] EXCEPTION in OnVirtualViewPushed: {ex.GetType().Name}: {ex.Message}"); DiagnosticLog.Error("NavigationPageHandler", $"EXCEPTION in OnVirtualViewPushed: {ex.GetType().Name}: {ex.Message}", ex);
Console.WriteLine($"[NavigationPageHandler] Stack trace: {ex.StackTrace}");
throw; throw;
} }
} }
private void OnVirtualViewPopped(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e) private void OnVirtualViewPopped(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e)
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView Popped: {e.Page?.Title}"); DiagnosticLog.Debug("NavigationPageHandler", $"VirtualView Popped: {e.Page?.Title}");
// Pop on the platform side to sync with MAUI navigation // Pop on the platform side to sync with MAUI navigation
PlatformView?.Pop(); PlatformView?.Pop();
} }
private void OnVirtualViewPoppedToRoot(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e) private void OnVirtualViewPoppedToRoot(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e)
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView PoppedToRoot"); DiagnosticLog.Debug("NavigationPageHandler", "VirtualView PoppedToRoot");
PlatformView?.PopToRoot(); PlatformView?.PopToRoot();
} }
@@ -403,7 +403,7 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
if (handler.PlatformView is null || handler.MauiContext is null || args is not NavigationRequest request) if (handler.PlatformView is null || handler.MauiContext is null || args is not NavigationRequest request)
return; return;
Console.WriteLine($"[NavigationPageHandler] MapRequestNavigation: {request.NavigationStack.Count} pages"); DiagnosticLog.Debug("NavigationPageHandler", $"MapRequestNavigation: {request.NavigationStack.Count} pages");
// Handle navigation request // Handle navigation request
foreach (var view in request.NavigationStack) foreach (var view in request.NavigationStack)

View File

@@ -6,6 +6,7 @@ using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -67,7 +68,7 @@ public partial class PageHandler : ViewHandler<Page, SkiaPage>
private void OnAppearing(object? sender, EventArgs e) private void OnAppearing(object? sender, EventArgs e)
{ {
Console.WriteLine($"[PageHandler] OnAppearing received for: {VirtualView?.Title}"); DiagnosticLog.Debug("PageHandler", $"OnAppearing received for: {VirtualView?.Title}");
(VirtualView as IPageController)?.SendAppearing(); (VirtualView as IPageController)?.SendAppearing();
} }
@@ -118,7 +119,7 @@ public partial class PageHandler : ViewHandler<Page, SkiaPage>
if (backgroundColor != null && backgroundColor != Colors.Transparent) if (backgroundColor != null && backgroundColor != Colors.Transparent)
{ {
handler.PlatformView.BackgroundColor = backgroundColor; handler.PlatformView.BackgroundColor = backgroundColor;
Console.WriteLine($"[PageHandler] MapBackgroundColor: {backgroundColor}"); DiagnosticLog.Debug("PageHandler", $"MapBackgroundColor: {backgroundColor}");
} }
} }
@@ -189,19 +190,19 @@ public partial class ContentPageHandler : PageHandler
// Create handler for content if it doesn't exist // Create handler for content if it doesn't exist
if (content.Handler == null) if (content.Handler == null)
{ {
Console.WriteLine($"[ContentPageHandler] Creating handler for content: {content.GetType().Name}"); DiagnosticLog.Debug("ContentPageHandler", $"Creating handler for content: {content.GetType().Name}");
content.Handler = content.ToViewHandler(handler.MauiContext); content.Handler = content.ToViewHandler(handler.MauiContext);
} }
// The content's handler should provide the platform view // The content's handler should provide the platform view
if (content.Handler?.PlatformView is SkiaView skiaContent) if (content.Handler?.PlatformView is SkiaView skiaContent)
{ {
Console.WriteLine($"[ContentPageHandler] Setting content: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("ContentPageHandler", $"Setting content: {skiaContent.GetType().Name}");
handler.PlatformView.Content = skiaContent; handler.PlatformView.Content = skiaContent;
} }
else else
{ {
Console.WriteLine($"[ContentPageHandler] Content handler PlatformView is not SkiaView: {content.Handler?.PlatformView?.GetType().Name ?? "null"}"); DiagnosticLog.Warn("ContentPageHandler", $"Content handler PlatformView is not SkiaView: {content.Handler?.PlatformView?.GetType().Name ?? "null"}");
} }
} }
else else
@@ -235,7 +236,7 @@ public partial class ContentPageHandler : PageHandler
if (item.IconImageSource is FileImageSource fileSource) if (item.IconImageSource is FileImageSource fileSource)
{ {
// Icon loading would be async - simplified for now // Icon loading would be async - simplified for now
Console.WriteLine($"[ContentPageHandler] Toolbar item icon: {fileSource.File}"); DiagnosticLog.Debug("ContentPageHandler", $"Toolbar item icon: {fileSource.File}");
} }
platformView.ToolbarItems.Add(skiaItem); platformView.ToolbarItems.Add(skiaItem);

View File

@@ -3,6 +3,7 @@
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -48,7 +49,7 @@ public partial class ScrollViewHandler : ViewHandler<IScrollView, SkiaScrollView
var content = scrollView.PresentedContent; var content = scrollView.PresentedContent;
if (content != null) if (content != null)
{ {
Console.WriteLine($"[ScrollViewHandler] MapContent: {content.GetType().Name}"); DiagnosticLog.Debug("ScrollViewHandler", $"MapContent: {content.GetType().Name}");
// Create handler for content if it doesn't exist // Create handler for content if it doesn't exist
if (content.Handler == null) if (content.Handler == null)
@@ -58,7 +59,7 @@ public partial class ScrollViewHandler : ViewHandler<IScrollView, SkiaScrollView
if (content.Handler?.PlatformView is SkiaView skiaContent) if (content.Handler?.PlatformView is SkiaView skiaContent)
{ {
Console.WriteLine($"[ScrollViewHandler] Setting content: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("ScrollViewHandler", $"Setting content: {skiaContent.GetType().Name}");
handler.PlatformView.Content = skiaContent; handler.PlatformView.Content = skiaContent;
} }
} }

View File

@@ -5,6 +5,7 @@ using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Hosting; using Microsoft.Maui.Platform.Linux.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -48,13 +49,13 @@ public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
protected override SkiaShell CreatePlatformView() protected override SkiaShell CreatePlatformView()
{ {
Console.WriteLine("[ShellHandler] CreatePlatformView - creating SkiaShell"); DiagnosticLog.Debug("ShellHandler", "CreatePlatformView - creating SkiaShell");
return new SkiaShell(); return new SkiaShell();
} }
protected override void ConnectHandler(SkiaShell platformView) protected override void ConnectHandler(SkiaShell platformView)
{ {
Console.WriteLine("[ShellHandler] ConnectHandler - connecting to SkiaShell"); DiagnosticLog.Debug("ShellHandler", "ConnectHandler - connecting to SkiaShell");
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.FlyoutIsPresentedChanged += OnFlyoutIsPresentedChanged; platformView.FlyoutIsPresentedChanged += OnFlyoutIsPresentedChanged;
platformView.Navigated += OnNavigated; platformView.Navigated += OnNavigated;
@@ -116,20 +117,20 @@ public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e) private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e)
{ {
Console.WriteLine($"[ShellHandler] Shell Navigating to: {e.Target?.Location}"); DiagnosticLog.Debug("ShellHandler", $"Shell Navigating to: {e.Target?.Location}");
// Route to platform view // Route to platform view
if (PlatformView != null && e.Target?.Location != null) if (PlatformView != null && e.Target?.Location != null)
{ {
var route = e.Target.Location.ToString().TrimStart('/'); var route = e.Target.Location.ToString().TrimStart('/');
Console.WriteLine($"[ShellHandler] Routing to: {route}"); DiagnosticLog.Debug("ShellHandler", $"Routing to: {route}");
PlatformView.GoToAsync(route); PlatformView.GoToAsync(route);
} }
} }
private void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) private void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
{ {
Console.WriteLine($"[ShellHandler] Shell Navigated to: {e.Current?.Location}"); DiagnosticLog.Debug("ShellHandler", $"Shell Navigated to: {e.Current?.Location}");
} }
private void SyncShellItems() private void SyncShellItems()
@@ -230,7 +231,7 @@ public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[ShellHandler] Error rendering content: {ex.Message}"); DiagnosticLog.Error("ShellHandler", $"Error rendering content: {ex.Message}", ex);
} }
return null; return null;

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -47,7 +48,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
protected override LinuxWebView CreatePlatformView() protected override LinuxWebView CreatePlatformView()
{ {
Console.WriteLine("[WebViewHandler] Creating LinuxWebView"); DiagnosticLog.Debug("WebViewHandler", "Creating LinuxWebView");
return new LinuxWebView(); return new LinuxWebView();
} }
@@ -65,7 +66,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
MapUserAgent(this, VirtualView); MapUserAgent(this, VirtualView);
} }
Console.WriteLine("[WebViewHandler] Handler connected"); DiagnosticLog.Debug("WebViewHandler", "Handler connected");
} }
protected override void DisconnectHandler(LinuxWebView platformView) protected override void DisconnectHandler(LinuxWebView platformView)
@@ -74,7 +75,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
platformView.Navigated -= OnNavigated; platformView.Navigated -= OnNavigated;
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
Console.WriteLine("[WebViewHandler] Handler disconnected"); DiagnosticLog.Debug("WebViewHandler", "Handler disconnected");
} }
private void OnNavigating(object? sender, WebViewNavigatingEventArgs e) private void OnNavigating(object? sender, WebViewNavigatingEventArgs e)
@@ -104,7 +105,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
if (source == null) if (source == null)
return; return;
Console.WriteLine($"[WebViewHandler] MapSource: {source.GetType().Name}"); DiagnosticLog.Debug("WebViewHandler", $"MapSource: {source.GetType().Name}");
if (source is IUrlWebViewSource urlSource && !string.IsNullOrEmpty(urlSource.Url)) if (source is IUrlWebViewSource urlSource && !string.IsNullOrEmpty(urlSource.Url))
{ {
@@ -121,7 +122,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
if (handler.PlatformView != null && !string.IsNullOrEmpty(webView.UserAgent)) if (handler.PlatformView != null && !string.IsNullOrEmpty(webView.UserAgent))
{ {
handler.PlatformView.UserAgent = webView.UserAgent; handler.PlatformView.UserAgent = webView.UserAgent;
Console.WriteLine($"[WebViewHandler] MapUserAgent: {webView.UserAgent}"); DiagnosticLog.Debug("WebViewHandler", $"MapUserAgent: {webView.UserAgent}");
} }
} }
@@ -134,7 +135,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
if (handler.PlatformView?.CanGoBack == true) if (handler.PlatformView?.CanGoBack == true)
{ {
handler.PlatformView.GoBack(); handler.PlatformView.GoBack();
Console.WriteLine("[WebViewHandler] GoBack"); DiagnosticLog.Debug("WebViewHandler", "GoBack");
} }
} }
@@ -143,14 +144,14 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
if (handler.PlatformView?.CanGoForward == true) if (handler.PlatformView?.CanGoForward == true)
{ {
handler.PlatformView.GoForward(); handler.PlatformView.GoForward();
Console.WriteLine("[WebViewHandler] GoForward"); DiagnosticLog.Debug("WebViewHandler", "GoForward");
} }
} }
public static void MapReload(WebViewHandler handler, IWebView webView, object? args) public static void MapReload(WebViewHandler handler, IWebView webView, object? args)
{ {
handler.PlatformView?.Reload(); handler.PlatformView?.Reload();
Console.WriteLine("[WebViewHandler] Reload"); DiagnosticLog.Debug("WebViewHandler", "Reload");
} }
public static void MapEval(WebViewHandler handler, IWebView webView, object? args) public static void MapEval(WebViewHandler handler, IWebView webView, object? args)
@@ -158,7 +159,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
if (args is string script) if (args is string script)
{ {
handler.PlatformView?.Eval(script); handler.PlatformView?.Eval(script);
Console.WriteLine($"[WebViewHandler] Eval: {script.Substring(0, Math.Min(50, script.Length))}..."); DiagnosticLog.Debug("WebViewHandler", $"Eval: {script.Substring(0, Math.Min(50, script.Length))}...");
} }
} }
@@ -178,7 +179,7 @@ public partial class WebViewHandler : ViewHandler<IWebView, LinuxWebView>
{ {
request.SetResult(null); request.SetResult(null);
} }
Console.WriteLine($"[WebViewHandler] EvaluateJavaScriptAsync: {request.Script.Substring(0, Math.Min(50, request.Script.Length))}..."); DiagnosticLog.Debug("WebViewHandler", $"EvaluateJavaScriptAsync: {request.Script.Substring(0, Math.Min(50, request.Script.Length))}...");
} }
} }

View File

@@ -4,6 +4,7 @@
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -89,30 +90,30 @@ public partial class WebViewHandler : ViewHandler<IWebView, SkiaWebView>
public static void MapSource(WebViewHandler handler, IWebView webView) public static void MapSource(WebViewHandler handler, IWebView webView)
{ {
Console.WriteLine("[WebViewHandler] MapSource called"); DiagnosticLog.Debug("WebViewHandler", "MapSource called");
if (handler.PlatformView == null) if (handler.PlatformView == null)
{ {
Console.WriteLine("[WebViewHandler] PlatformView is null!"); DiagnosticLog.Warn("WebViewHandler", "PlatformView is null!");
return; return;
} }
var source = webView.Source; var source = webView.Source;
Console.WriteLine($"[WebViewHandler] Source type: {source?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("WebViewHandler", $"Source type: {source?.GetType().Name ?? "null"}");
if (source is UrlWebViewSource urlSource) if (source is UrlWebViewSource urlSource)
{ {
Console.WriteLine($"[WebViewHandler] Loading URL: {urlSource.Url}"); DiagnosticLog.Debug("WebViewHandler", $"Loading URL: {urlSource.Url}");
handler.PlatformView.Source = urlSource.Url ?? ""; handler.PlatformView.Source = urlSource.Url ?? "";
} }
else if (source is HtmlWebViewSource htmlSource) else if (source is HtmlWebViewSource htmlSource)
{ {
Console.WriteLine($"[WebViewHandler] Loading HTML ({htmlSource.Html?.Length ?? 0} chars)"); DiagnosticLog.Debug("WebViewHandler", $"Loading HTML ({htmlSource.Html?.Length ?? 0} chars)");
Console.WriteLine($"[WebViewHandler] HTML preview: {htmlSource.Html?.Substring(0, Math.Min(100, htmlSource.Html?.Length ?? 0))}..."); DiagnosticLog.Debug("WebViewHandler", $"HTML preview: {htmlSource.Html?.Substring(0, Math.Min(100, htmlSource.Html?.Length ?? 0))}...");
handler.PlatformView.Html = htmlSource.Html ?? ""; handler.PlatformView.Html = htmlSource.Html ?? "";
} }
else else
{ {
Console.WriteLine("[WebViewHandler] Unknown source type or null"); DiagnosticLog.Debug("WebViewHandler", "Unknown source type or null");
} }
} }

View File

@@ -5,6 +5,7 @@ using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -81,19 +82,19 @@ public partial class WindowHandler : ElementHandler<IWindow, SkiaWindow>
public static void MapContent(WindowHandler handler, IWindow window) public static void MapContent(WindowHandler handler, IWindow window)
{ {
Console.Error.WriteLine($"[WindowHandler] MapContent - PlatformView={handler.PlatformView != null}"); DiagnosticLog.Debug("WindowHandler", $"MapContent - PlatformView={handler.PlatformView != null}");
if (handler.PlatformView is null) return; if (handler.PlatformView is null) return;
var content = window.Content; var content = window.Content;
Console.Error.WriteLine($"[WindowHandler] MapContent - content type={content?.GetType().Name}, handler={content?.Handler?.GetType().Name}"); DiagnosticLog.Debug("WindowHandler", $"MapContent - content type={content?.GetType().Name}, handler={content?.Handler?.GetType().Name}");
if (content?.Handler?.PlatformView is SkiaView skiaContent) if (content?.Handler?.PlatformView is SkiaView skiaContent)
{ {
Console.Error.WriteLine($"[WindowHandler] MapContent - setting SkiaView content: {skiaContent.GetType().Name}"); DiagnosticLog.Debug("WindowHandler", $"MapContent - setting SkiaView content: {skiaContent.GetType().Name}");
handler.PlatformView.Content = skiaContent; handler.PlatformView.Content = skiaContent;
} }
else else
{ {
Console.Error.WriteLine($"[WindowHandler] MapContent - content has no SkiaView! Handler={content?.Handler}, PlatformView={content?.Handler?.PlatformView}"); DiagnosticLog.Warn("WindowHandler", $"MapContent - content has no SkiaView! Handler={content?.Handler}, PlatformView={content?.Handler?.PlatformView}");
} }
} }

View File

@@ -48,7 +48,7 @@ public static class LinuxProgramHost
// Initialize GTK for WebView support // Initialize GTK for WebView support
GtkHostService.Instance.Initialize(options.Title ?? "MAUI Application", options.Width, options.Height); GtkHostService.Instance.Initialize(options.Title ?? "MAUI Application", options.Width, options.Height);
Console.WriteLine("[LinuxProgramHost] GTK initialized for WebView support"); DiagnosticLog.Debug("LinuxProgramHost", "GTK initialized for WebView support");
// Create Linux application // Create Linux application
using var linuxApp = new LinuxApplication(); using var linuxApp = new LinuxApplication();
@@ -79,7 +79,7 @@ public static class LinuxProgramHost
// Fallback to demo if no application view is available // Fallback to demo if no application view is available
if (rootView == null) if (rootView == null)
{ {
Console.WriteLine("No application page found. Showing demo UI."); DiagnosticLog.Warn("LinuxProgramHost", "No application page found. Showing demo UI.");
rootView = CreateDemoView(); rootView = CreateDemoView();
} }
@@ -140,8 +140,8 @@ public static class LinuxProgramHost
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error rendering application: {ex.Message}"); DiagnosticLog.Error("LinuxProgramHost", $"Error rendering application: {ex.Message}");
Console.WriteLine(ex.StackTrace); DiagnosticLog.Error("LinuxProgramHost", ex.StackTrace ?? "");
return null; return null;
} }
} }

View File

@@ -5,6 +5,7 @@ using System.Reflection;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Hosting; namespace Microsoft.Maui.Platform.Linux.Hosting;
@@ -38,13 +39,13 @@ public class LinuxViewRenderer
{ {
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[NavigateToRoute] CurrentSkiaShell is null"); DiagnosticLog.Warn("LinuxViewRenderer", "CurrentSkiaShell is null");
return false; return false;
} }
// Clean up the route - remove leading // or / // Clean up the route - remove leading // or /
var cleanRoute = route.TrimStart('/'); var cleanRoute = route.TrimStart('/');
Console.WriteLine($"[NavigateToRoute] Navigating to: {cleanRoute}"); DiagnosticLog.Debug("LinuxViewRenderer", $"NavigateToRoute: Navigating to: {cleanRoute}");
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
{ {
@@ -52,13 +53,13 @@ public class LinuxViewRenderer
if (section.Route.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase) || if (section.Route.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase) ||
section.Title.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase)) section.Title.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[NavigateToRoute] Found section {i}: {section.Title}"); DiagnosticLog.Debug("LinuxViewRenderer", $"NavigateToRoute: Found section {i}: {section.Title}");
CurrentSkiaShell.NavigateToSection(i); CurrentSkiaShell.NavigateToSection(i);
return true; return true;
} }
} }
Console.WriteLine($"[NavigateToRoute] Route not found: {cleanRoute}"); DiagnosticLog.Warn("LinuxViewRenderer", $"NavigateToRoute: Route not found: {cleanRoute}");
return false; return false;
} }
@@ -74,17 +75,17 @@ public class LinuxViewRenderer
/// <returns>True if successful</returns> /// <returns>True if successful</returns>
public static bool PushPage(Page page) public static bool PushPage(Page page)
{ {
Console.WriteLine($"[PushPage] Pushing page: {page.GetType().Name}"); DiagnosticLog.Debug("LinuxViewRenderer", $"PushPage: Pushing page: {page.GetType().Name}");
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[PushPage] CurrentSkiaShell is null"); DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: CurrentSkiaShell is null");
return false; return false;
} }
if (CurrentRenderer == null) if (CurrentRenderer == null)
{ {
Console.WriteLine($"[PushPage] CurrentRenderer is null"); DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: CurrentRenderer is null");
return false; return false;
} }
@@ -96,18 +97,18 @@ public class LinuxViewRenderer
if (skiaPage == null) if (skiaPage == null)
{ {
Console.WriteLine($"[PushPage] Failed to render page through handler"); DiagnosticLog.Warn("LinuxViewRenderer", "PushPage: Failed to render page through handler");
return false; return false;
} }
// Push onto SkiaShell's navigation stack // Push onto SkiaShell's navigation stack
CurrentSkiaShell.PushAsync(skiaPage, page.Title ?? "Detail"); CurrentSkiaShell.PushAsync(skiaPage, page.Title ?? "Detail");
Console.WriteLine($"[PushPage] Successfully pushed page via handler system"); DiagnosticLog.Debug("LinuxViewRenderer", "PushPage: Successfully pushed page via handler system");
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[PushPage] Error: {ex.Message}"); DiagnosticLog.Error("LinuxViewRenderer", "PushPage failed", ex);
return false; return false;
} }
} }
@@ -118,11 +119,11 @@ public class LinuxViewRenderer
/// <returns>True if successful</returns> /// <returns>True if successful</returns>
public static bool PopPage() public static bool PopPage()
{ {
Console.WriteLine($"[PopPage] Popping page"); DiagnosticLog.Debug("LinuxViewRenderer", "PopPage: Popping page");
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[PopPage] CurrentSkiaShell is null"); DiagnosticLog.Warn("LinuxViewRenderer", "PopPage: CurrentSkiaShell is null");
return false; return false;
} }
@@ -233,12 +234,12 @@ public class LinuxViewRenderer
// Subscribe to MAUI Shell navigation events to update SkiaShell // Subscribe to MAUI Shell navigation events to update SkiaShell
shell.Navigated += OnShellNavigated; shell.Navigated += OnShellNavigated;
shell.Navigating += (s, e) => Console.WriteLine($"[Navigation] Navigating: {e.Target}"); shell.Navigating += (s, e) => DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Navigating: {e.Target}");
Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}"); DiagnosticLog.Debug("LinuxViewRenderer", $"Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}");
for (int i = 0; i < skiaShell.Sections.Count; i++) for (int i = 0; i < skiaShell.Sections.Count; i++)
{ {
Console.WriteLine($"[Navigation] Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'"); DiagnosticLog.Debug("LinuxViewRenderer", $"Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'");
} }
return skiaShell; return skiaShell;
@@ -250,33 +251,33 @@ public class LinuxViewRenderer
private static void ApplyShellColors(SkiaShell skiaShell, Shell shell) private static void ApplyShellColors(SkiaShell skiaShell, Shell shell)
{ {
bool isDark = Application.Current?.UserAppTheme == AppTheme.Dark; bool isDark = Application.Current?.UserAppTheme == AppTheme.Dark;
Console.WriteLine($"[ApplyShellColors] Theme is: {(isDark ? "Dark" : "Light")}"); DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: Theme is: {(isDark ? "Dark" : "Light")}");
// Flyout background color // Flyout background color
if (shell.FlyoutBackgroundColor != null && shell.FlyoutBackgroundColor != Colors.Transparent) if (shell.FlyoutBackgroundColor != null && shell.FlyoutBackgroundColor != Colors.Transparent)
{ {
skiaShell.FlyoutBackgroundColor = shell.FlyoutBackgroundColor; skiaShell.FlyoutBackgroundColor = shell.FlyoutBackgroundColor;
Console.WriteLine($"[ApplyShellColors] FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}"); DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}");
} }
else else
{ {
skiaShell.FlyoutBackgroundColor = isDark skiaShell.FlyoutBackgroundColor = isDark
? Color.FromRgb(30, 30, 30) ? Color.FromRgb(30, 30, 30)
: Color.FromRgb(255, 255, 255); : Color.FromRgb(255, 255, 255);
Console.WriteLine($"[ApplyShellColors] Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}"); DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}");
} }
// Flyout text color // Flyout text color
skiaShell.FlyoutTextColor = isDark skiaShell.FlyoutTextColor = isDark
? Color.FromRgb(224, 224, 224) ? Color.FromRgb(224, 224, 224)
: Color.FromRgb(33, 33, 33); : Color.FromRgb(33, 33, 33);
Console.WriteLine($"[ApplyShellColors] FlyoutTextColor: {skiaShell.FlyoutTextColor}"); DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: FlyoutTextColor: {skiaShell.FlyoutTextColor}");
// Content background color // Content background color
skiaShell.ContentBackgroundColor = isDark skiaShell.ContentBackgroundColor = isDark
? Color.FromRgb(18, 18, 18) ? Color.FromRgb(18, 18, 18)
: Color.FromRgb(250, 250, 250); : Color.FromRgb(250, 250, 250);
Console.WriteLine($"[ApplyShellColors] ContentBackgroundColor: {skiaShell.ContentBackgroundColor}"); DiagnosticLog.Debug("LinuxViewRenderer", $"ApplyShellColors: ContentBackgroundColor: {skiaShell.ContentBackgroundColor}");
// NavBar background color // NavBar background color
if (shell.BackgroundColor != null && shell.BackgroundColor != Colors.Transparent) if (shell.BackgroundColor != null && shell.BackgroundColor != Colors.Transparent)
@@ -294,27 +295,27 @@ public class LinuxViewRenderer
/// </summary> /// </summary>
private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
{ {
Console.WriteLine($"[Navigation] OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}"); DiagnosticLog.Debug("LinuxViewRenderer", $"OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}");
if (CurrentSkiaShell == null || CurrentMauiShell == null) if (CurrentSkiaShell == null || CurrentMauiShell == null)
{ {
Console.WriteLine($"[Navigation] CurrentSkiaShell or CurrentMauiShell is null"); DiagnosticLog.Warn("LinuxViewRenderer", "CurrentSkiaShell or CurrentMauiShell is null");
return; return;
} }
// Get the current route from the Shell // Get the current route from the Shell
var currentState = CurrentMauiShell.CurrentState; var currentState = CurrentMauiShell.CurrentState;
var location = currentState?.Location?.OriginalString ?? ""; var location = currentState?.Location?.OriginalString ?? "";
Console.WriteLine($"[Navigation] Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}"); DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}");
// Find the matching section in SkiaShell by route // Find the matching section in SkiaShell by route
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
{ {
var section = CurrentSkiaShell.Sections[i]; var section = CurrentSkiaShell.Sections[i];
Console.WriteLine($"[Navigation] Checking section {i}: Route='{section.Route}', Title='{section.Title}'"); DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Checking section {i}: Route='{section.Route}', Title='{section.Title}'");
if (!string.IsNullOrEmpty(section.Route) && location.Contains(section.Route, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(section.Route) && location.Contains(section.Route, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}"); DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Match found by route! Navigating to section {i}");
if (i != CurrentSkiaShell.CurrentSectionIndex) if (i != CurrentSkiaShell.CurrentSectionIndex)
{ {
CurrentSkiaShell.NavigateToSection(i); CurrentSkiaShell.NavigateToSection(i);
@@ -323,7 +324,7 @@ public class LinuxViewRenderer
} }
if (!string.IsNullOrEmpty(section.Title) && location.Contains(section.Title, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(section.Title) && location.Contains(section.Title, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}"); DiagnosticLog.Debug("LinuxViewRenderer", $"Navigation: Match found by title! Navigating to section {i}");
if (i != CurrentSkiaShell.CurrentSectionIndex) if (i != CurrentSkiaShell.CurrentSectionIndex)
{ {
CurrentSkiaShell.NavigateToSection(i); CurrentSkiaShell.NavigateToSection(i);
@@ -331,7 +332,7 @@ public class LinuxViewRenderer
return; return;
} }
} }
Console.WriteLine($"[Navigation] No matching section found for location: {location}"); DiagnosticLog.Warn("LinuxViewRenderer", $"Navigation: No matching section found for location: {location}");
} }
/// <summary> /// <summary>
@@ -476,7 +477,7 @@ public class LinuxViewRenderer
if (cp.BackgroundColor != null && cp.BackgroundColor != Colors.Transparent) if (cp.BackgroundColor != null && cp.BackgroundColor != Colors.Transparent)
{ {
bgColor = cp.BackgroundColor; bgColor = cp.BackgroundColor;
Console.WriteLine($"[CreateShellContentPage] Page BackgroundColor: {bgColor}"); DiagnosticLog.Debug("LinuxViewRenderer", $"CreateShellContentPage: Page BackgroundColor: {bgColor}");
} }
if (contentView is SkiaScrollView scrollView) if (contentView is SkiaScrollView scrollView)

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform.Linux.Handlers; using Microsoft.Maui.Platform.Linux.Handlers;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Hosting; namespace Microsoft.Maui.Platform.Linux.Hosting;
@@ -81,7 +82,7 @@ public static class MauiHandlerExtensions
if (LinuxHandlerMap.TryGetValue(type, out Func<IElementHandler>? factory)) if (LinuxHandlerMap.TryGetValue(type, out Func<IElementHandler>? factory))
{ {
handler = factory(); handler = factory();
Console.WriteLine($"[ToHandler] Using Linux handler for {type.Name}: {handler.GetType().Name}"); DiagnosticLog.Debug("MauiHandlerExtensions", $"Using Linux handler for {type.Name}: {handler.GetType().Name}");
} }
else else
{ {
@@ -101,7 +102,7 @@ public static class MauiHandlerExtensions
if (bestFactory != null) if (bestFactory != null)
{ {
handler = bestFactory(); handler = bestFactory();
Console.WriteLine($"[ToHandler] Using Linux handler (via base {bestMatch!.Name}) for {type.Name}: {handler.GetType().Name}"); DiagnosticLog.Debug("MauiHandlerExtensions", $"Using Linux handler (via base {bestMatch!.Name}) for {type.Name}: {handler.GetType().Name}");
} }
} }
@@ -109,7 +110,7 @@ public static class MauiHandlerExtensions
if (handler == null) if (handler == null)
{ {
handler = mauiContext.Handlers.GetHandler(type); handler = mauiContext.Handlers.GetHandler(type);
Console.WriteLine($"[ToHandler] Using MAUI handler for {type.Name}: {handler?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("MauiHandlerExtensions", $"Using MAUI handler for {type.Name}: {handler?.GetType().Name ?? "null"}");
} }
if (handler != null) if (handler != null)

View File

@@ -65,7 +65,7 @@ public class LinuxApplication : IDisposable
Interlocked.Increment(ref _invalidateCount); Interlocked.Increment(ref _invalidateCount);
if (currentThread != _gtkThreadId && _gtkThreadId != 0) if (currentThread != _gtkThreadId && _gtkThreadId != 0)
{ {
Console.WriteLine($"[DIAG] ⚠️ Invalidate from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}, Source={source}"); DiagnosticLog.Warn("LinuxApplication", $"Invalidate from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}, Source={source}");
} }
} }
@@ -78,23 +78,25 @@ public class LinuxApplication : IDisposable
Interlocked.Increment(ref _requestRedrawCount); Interlocked.Increment(ref _requestRedrawCount);
if (currentThread != _gtkThreadId && _gtkThreadId != 0) if (currentThread != _gtkThreadId && _gtkThreadId != 0)
{ {
Console.WriteLine($"[DIAG] ⚠️ RequestRedraw from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}"); DiagnosticLog.Warn("LinuxApplication", $"RequestRedraw from WRONG THREAD! GTK={_gtkThreadId}, Current={currentThread}");
} }
} }
private static void StartHeartbeat() private static void StartHeartbeat()
{ {
_gtkThreadId = Environment.CurrentManagedThreadId; _gtkThreadId = Environment.CurrentManagedThreadId;
Console.WriteLine($"[DIAG] GTK thread ID: {_gtkThreadId}"); DiagnosticLog.Info("LinuxApplication", $"GTK thread ID: {_gtkThreadId}");
GLibNative.TimeoutAdd(250, () => GLibNative.TimeoutAdd(250, () =>
{ {
if (!DiagnosticLog.IsEnabled)
return true;
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
if ((now - _lastCounterReset).TotalSeconds >= 1.0) if ((now - _lastCounterReset).TotalSeconds >= 1.0)
{ {
int invalidates = Interlocked.Exchange(ref _invalidateCount, 0); int invalidates = Interlocked.Exchange(ref _invalidateCount, 0);
int redraws = Interlocked.Exchange(ref _requestRedrawCount, 0); int redraws = Interlocked.Exchange(ref _requestRedrawCount, 0);
int draws = Interlocked.Exchange(ref _drawCount, 0); int draws = Interlocked.Exchange(ref _drawCount, 0);
Console.WriteLine($"[DIAG] ❤️ Heartbeat | Invalidate={invalidates}/s, RequestRedraw={redraws}/s, Draw={draws}/s"); DiagnosticLog.Debug("LinuxApplication", $"Heartbeat | Invalidate={invalidates}/s, RequestRedraw={redraws}/s, Draw={draws}/s");
_lastCounterReset = now; _lastCounterReset = now;
} }
return true; return true;
@@ -258,11 +260,11 @@ public class LinuxApplication : IDisposable
IntPtr argv = IntPtr.Zero; IntPtr argv = IntPtr.Zero;
if (!GtkNative.gtk_init_check(ref argc, ref argv)) if (!GtkNative.gtk_init_check(ref argc, ref argv))
{ {
Console.WriteLine("[LinuxApplication] Warning: GTK initialization failed - WebView may not work"); DiagnosticLog.Warn("LinuxApplication", "GTK initialization failed - WebView may not work");
} }
else else
{ {
Console.WriteLine("[LinuxApplication] GTK pre-initialized for WebView support"); DiagnosticLog.Debug("LinuxApplication", "GTK pre-initialized for WebView support");
} }
// Set application name for desktop integration (taskbar, etc.) // Set application name for desktop integration (taskbar, etc.)
@@ -275,12 +277,12 @@ public class LinuxApplication : IDisposable
string prgName = appName.Replace(" ", ""); string prgName = appName.Replace(" ", "");
GtkNative.g_set_prgname(prgName); GtkNative.g_set_prgname(prgName);
GtkNative.g_set_application_name(appName); GtkNative.g_set_application_name(appName);
Console.WriteLine($"[LinuxApplication] Set application name: {appName} (prgname: {prgName})"); DiagnosticLog.Debug("LinuxApplication", $"Set application name: {appName} (prgname: {prgName})");
// Initialize dispatcher // Initialize dispatcher
LinuxDispatcher.Initialize(); LinuxDispatcher.Initialize();
DispatcherProvider.SetCurrent(LinuxDispatcherProvider.Instance); DispatcherProvider.SetCurrent(LinuxDispatcherProvider.Instance);
Console.WriteLine("[LinuxApplication] Dispatcher initialized"); DiagnosticLog.Debug("LinuxApplication", "Dispatcher initialized");
var options = app.Services.GetService<LinuxApplicationOptions>() var options = app.Services.GetService<LinuxApplicationOptions>()
?? new LinuxApplicationOptions(); ?? new LinuxApplicationOptions();
@@ -310,16 +312,16 @@ public class LinuxApplication : IDisposable
// Set initial theme based on system theme // Set initial theme based on system theme
var systemTheme = SystemThemeService.Instance.CurrentTheme; var systemTheme = SystemThemeService.Instance.CurrentTheme;
Console.WriteLine($"[LinuxApplication] System theme detected at startup: {systemTheme}"); DiagnosticLog.Debug("LinuxApplication", $"System theme detected at startup: {systemTheme}");
if (systemTheme == SystemTheme.Dark) if (systemTheme == SystemTheme.Dark)
{ {
mauiApplication.UserAppTheme = AppTheme.Dark; mauiApplication.UserAppTheme = AppTheme.Dark;
Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Dark based on system theme"); DiagnosticLog.Debug("LinuxApplication", "Set initial UserAppTheme to Dark based on system theme");
} }
else else
{ {
mauiApplication.UserAppTheme = AppTheme.Light; mauiApplication.UserAppTheme = AppTheme.Light;
Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Light based on system theme"); DiagnosticLog.Debug("LinuxApplication", "Set initial UserAppTheme to Light based on system theme");
} }
// Initialize GTK theme service and apply initial CSS // Initialize GTK theme service and apply initial CSS
@@ -330,7 +332,7 @@ public class LinuxApplication : IDisposable
{ {
if (e.PropertyName == "UserAppTheme") if (e.PropertyName == "UserAppTheme")
{ {
Console.WriteLine($"[LinuxApplication] User theme changed to: {mauiApplication.UserAppTheme}"); DiagnosticLog.Debug("LinuxApplication", $"User theme changed to: {mauiApplication.UserAppTheme}");
// Apply GTK CSS for dialogs, menus, and window decorations // Apply GTK CSS for dialogs, menus, and window decorations
GtkThemeService.ApplyTheme(); GtkThemeService.ApplyTheme();
@@ -355,14 +357,14 @@ public class LinuxApplication : IDisposable
// Handle system theme changes (e.g., GNOME/KDE dark mode toggle) // Handle system theme changes (e.g., GNOME/KDE dark mode toggle)
SystemThemeService.Instance.ThemeChanged += (s, e) => SystemThemeService.Instance.ThemeChanged += (s, e) =>
{ {
Console.WriteLine($"[LinuxApplication] System theme changed to: {e.NewTheme}"); DiagnosticLog.Debug("LinuxApplication", $"System theme changed to: {e.NewTheme}");
// Update MAUI's UserAppTheme to match system theme // Update MAUI's UserAppTheme to match system theme
// This will trigger the PropertyChanged handler which does the refresh // This will trigger the PropertyChanged handler which does the refresh
var newAppTheme = e.NewTheme == SystemTheme.Dark ? AppTheme.Dark : AppTheme.Light; var newAppTheme = e.NewTheme == SystemTheme.Dark ? AppTheme.Dark : AppTheme.Light;
if (mauiApplication.UserAppTheme != newAppTheme) if (mauiApplication.UserAppTheme != newAppTheme)
{ {
Console.WriteLine($"[LinuxApplication] Setting UserAppTheme to {newAppTheme} to match system"); DiagnosticLog.Debug("LinuxApplication", $"Setting UserAppTheme to {newAppTheme} to match system");
mauiApplication.UserAppTheme = newAppTheme; mauiApplication.UserAppTheme = newAppTheme;
} }
else else
@@ -397,9 +399,9 @@ public class LinuxApplication : IDisposable
var mauiWindow = createWindowMethod.Invoke(mauiApplication, new object?[] { null }) as Microsoft.Maui.Controls.Window; var mauiWindow = createWindowMethod.Invoke(mauiApplication, new object?[] { null }) as Microsoft.Maui.Controls.Window;
if (mauiWindow != null) if (mauiWindow != null)
{ {
Console.WriteLine($"[LinuxApplication] Got Window from CreateWindow: {mauiWindow.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Got Window from CreateWindow: {mauiWindow.GetType().Name}");
mainPage = mauiWindow.Page; mainPage = mauiWindow.Page;
Console.WriteLine($"[LinuxApplication] Window.Page: {mainPage?.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Window.Page: {mainPage?.GetType().Name}");
// Add to windows list // Add to windows list
var windowsField = typeof(Application).GetField("_windows", var windowsField = typeof(Application).GetField("_windows",
@@ -415,13 +417,13 @@ public class LinuxApplication : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[LinuxApplication] CreateWindow failed: {ex.Message}"); DiagnosticLog.Error("LinuxApplication", $"CreateWindow failed: {ex.Message}");
} }
// Fall back to deprecated MainPage if CreateWindow didn't work // Fall back to deprecated MainPage if CreateWindow didn't work
if (mainPage == null && mauiApplication.MainPage != null) if (mainPage == null && mauiApplication.MainPage != null)
{ {
Console.WriteLine($"[LinuxApplication] Falling back to MainPage: {mauiApplication.MainPage.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Falling back to MainPage: {mauiApplication.MainPage.GetType().Name}");
mainPage = mauiApplication.MainPage; mainPage = mauiApplication.MainPage;
var windowsField = typeof(Application).GetField("_windows", var windowsField = typeof(Application).GetField("_windows",
@@ -645,13 +647,13 @@ public class LinuxApplication : IDisposable
_mainWindow.Show(); _mainWindow.Show();
Render(); Render();
Console.WriteLine("[LinuxApplication] Starting event loop"); DiagnosticLog.Debug("LinuxApplication", "Starting event loop");
while (_mainWindow.IsRunning) while (_mainWindow.IsRunning)
{ {
_loopCounter++; _loopCounter++;
if (_loopCounter % 1000 == 0) if (_loopCounter % 1000 == 0)
{ {
Console.WriteLine($"[LinuxApplication] Loop iteration {_loopCounter}"); DiagnosticLog.Debug("LinuxApplication", $"Loop iteration {_loopCounter}");
} }
_mainWindow.ProcessEvents(); _mainWindow.ProcessEvents();
@@ -660,7 +662,7 @@ public class LinuxApplication : IDisposable
Render(); Render();
Thread.Sleep(1); Thread.Sleep(1);
} }
Console.WriteLine("[LinuxApplication] Event loop ended"); DiagnosticLog.Debug("LinuxApplication", "Event loop ended");
} }
private void RunGtk() private void RunGtk()
@@ -691,7 +693,7 @@ public class LinuxApplication : IDisposable
/// </summary> /// </summary>
private void RefreshPageForThemeChange() private void RefreshPageForThemeChange()
{ {
Console.WriteLine("[LinuxApplication] RefreshPageForThemeChange - forcing property updates"); DiagnosticLog.Debug("LinuxApplication", "RefreshPageForThemeChange - forcing property updates");
// First, try to trigger MAUI's RequestedThemeChanged event using reflection // First, try to trigger MAUI's RequestedThemeChanged event using reflection
// This ensures AppThemeBinding bindings re-evaluate // This ensures AppThemeBinding bindings re-evaluate
@@ -714,7 +716,7 @@ public class LinuxApplication : IDisposable
var app = Application.Current; var app = Application.Current;
if (app == null) return; if (app == null) return;
Console.WriteLine($"[LinuxApplication] Theme is now: {app.UserAppTheme}, RequestedTheme: {app.RequestedTheme}"); DiagnosticLog.Debug("LinuxApplication", $"Theme is now: {app.UserAppTheme}, RequestedTheme: {app.RequestedTheme}");
} }
private void RefreshViewTheme(SkiaView view) private void RefreshViewTheme(SkiaView view)
@@ -765,7 +767,7 @@ public class LinuxApplication : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[LinuxApplication] Error refreshing theme for {mauiView.GetType().Name}: {ex.Message}"); DiagnosticLog.Error("LinuxApplication", $"Error refreshing theme for {mauiView.GetType().Name}: {ex.Message}");
} }
} }
@@ -792,13 +794,13 @@ public class LinuxApplication : IDisposable
{ {
try try
{ {
Console.WriteLine($"[LinuxApplication] Refreshing page theme: {page.MauiPage?.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Refreshing page theme: {page.MauiPage?.GetType().Name}");
pageHandler.UpdateValue(nameof(IView.Background)); pageHandler.UpdateValue(nameof(IView.Background));
pageHandler.UpdateValue("BackgroundColor"); pageHandler.UpdateValue("BackgroundColor");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[LinuxApplication] Error refreshing page theme: {ex.Message}"); DiagnosticLog.Error("LinuxApplication", $"Error refreshing page theme: {ex.Message}");
} }
} }
@@ -941,7 +943,7 @@ public class LinuxApplication : IDisposable
private void OnPointerPressed(object? sender, PointerEventArgs e) private void OnPointerPressed(object? sender, PointerEventArgs e)
{ {
Console.WriteLine($"[LinuxApplication] OnPointerPressed at ({e.X}, {e.Y}), Button={e.Button}"); DiagnosticLog.Debug("LinuxApplication", $"OnPointerPressed at ({e.X}, {e.Y}), Button={e.Button}");
// Route to context menu if one is active // Route to context menu if one is active
if (LinuxDialogService.HasContextMenu) if (LinuxDialogService.HasContextMenu)
@@ -962,7 +964,7 @@ public class LinuxApplication : IDisposable
// Check for popup overlay first // Check for popup overlay first
var popupOwner = SkiaView.GetPopupOwnerAt(e.X, e.Y); var popupOwner = SkiaView.GetPopupOwnerAt(e.X, e.Y);
var hitView = popupOwner ?? _rootView.HitTest(e.X, e.Y); var hitView = popupOwner ?? _rootView.HitTest(e.X, e.Y);
Console.WriteLine($"[LinuxApplication] HitView: {hitView?.GetType().Name ?? "null"}, rootView: {_rootView.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"HitView: {hitView?.GetType().Name ?? "null"}, rootView: {_rootView.GetType().Name}");
if (hitView != null) if (hitView != null)
{ {
@@ -975,7 +977,7 @@ public class LinuxApplication : IDisposable
FocusedView = hitView; FocusedView = hitView;
} }
Console.WriteLine($"[LinuxApplication] Calling OnPointerPressed on {hitView.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Calling OnPointerPressed on {hitView.GetType().Name}");
hitView.OnPointerPressed(e); hitView.OnPointerPressed(e);
} }
else else
@@ -1018,16 +1020,16 @@ public class LinuxApplication : IDisposable
private void OnScroll(object? sender, ScrollEventArgs e) private void OnScroll(object? sender, ScrollEventArgs e)
{ {
Console.WriteLine($"[LinuxApplication] OnScroll - X={e.X}, Y={e.Y}, DeltaX={e.DeltaX}, DeltaY={e.DeltaY}"); DiagnosticLog.Debug("LinuxApplication", $"OnScroll - X={e.X}, Y={e.Y}, DeltaX={e.DeltaX}, DeltaY={e.DeltaY}");
if (_rootView != null) if (_rootView != null)
{ {
var hitView = _rootView.HitTest(e.X, e.Y); var hitView = _rootView.HitTest(e.X, e.Y);
Console.WriteLine($"[LinuxApplication] HitView: {hitView?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("LinuxApplication", $"HitView: {hitView?.GetType().Name ?? "null"}");
// Bubble scroll events up to find a ScrollView // Bubble scroll events up to find a ScrollView
var view = hitView; var view = hitView;
while (view != null) while (view != null)
{ {
Console.WriteLine($"[LinuxApplication] Bubbling to: {view.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Bubbling to: {view.GetType().Name}");
if (view is SkiaScrollView scrollView) if (view is SkiaScrollView scrollView)
{ {
scrollView.OnScroll(e); scrollView.OnScroll(e);
@@ -1048,7 +1050,7 @@ public class LinuxApplication : IDisposable
// GTK Event Handlers // GTK Event Handlers
private void OnGtkDrawRequested(object? sender, EventArgs e) private void OnGtkDrawRequested(object? sender, EventArgs e)
{ {
Console.WriteLine("[DIAG] >>> OnGtkDrawRequested ENTER"); DiagnosticLog.Debug("LinuxApplication", ">>> OnGtkDrawRequested ENTER");
LogDraw(); LogDraw();
var surface = _gtkWindow?.SkiaSurface; var surface = _gtkWindow?.SkiaSurface;
if (surface?.Canvas != null && _rootView != null) if (surface?.Canvas != null && _rootView != null)
@@ -1057,12 +1059,12 @@ public class LinuxApplication : IDisposable
? new SKColor(32, 33, 36) ? new SKColor(32, 33, 36)
: SKColors.White; : SKColors.White;
surface.Canvas.Clear(bgColor); surface.Canvas.Clear(bgColor);
Console.WriteLine("[DIAG] Drawing rootView..."); DiagnosticLog.Debug("LinuxApplication", "Drawing rootView...");
_rootView.Draw(surface.Canvas); _rootView.Draw(surface.Canvas);
Console.WriteLine("[DIAG] Drawing dialogs..."); DiagnosticLog.Debug("LinuxApplication", "Drawing dialogs...");
var bounds = new SKRect(0, 0, surface.Width, surface.Height); var bounds = new SKRect(0, 0, surface.Width, surface.Height);
LinuxDialogService.DrawDialogs(surface.Canvas, bounds); LinuxDialogService.DrawDialogs(surface.Canvas, bounds);
Console.WriteLine("[DIAG] <<< OnGtkDrawRequested EXIT"); DiagnosticLog.Debug("LinuxApplication", "<<< OnGtkDrawRequested EXIT");
} }
} }
@@ -1075,7 +1077,7 @@ public class LinuxApplication : IDisposable
private void OnGtkPointerPressed(object? sender, (double X, double Y, int Button) e) private void OnGtkPointerPressed(object? sender, (double X, double Y, int Button) e)
{ {
string buttonName = e.Button == 1 ? "Left" : e.Button == 2 ? "Middle" : e.Button == 3 ? "Right" : $"Unknown({e.Button})"; string buttonName = e.Button == 1 ? "Left" : e.Button == 2 ? "Middle" : e.Button == 3 ? "Right" : $"Unknown({e.Button})";
Console.WriteLine($"[LinuxApplication.GTK] PointerPressed at ({e.X:F1}, {e.Y:F1}), Button={e.Button} ({buttonName})"); DiagnosticLog.Debug("LinuxApplication", $"GTK PointerPressed at ({e.X:F1}, {e.Y:F1}), Button={e.Button} ({buttonName})");
// Route to dialog if one is active // Route to dialog if one is active
if (LinuxDialogService.HasActiveDialog) if (LinuxDialogService.HasActiveDialog)
@@ -1098,12 +1100,12 @@ public class LinuxApplication : IDisposable
if (_rootView == null) if (_rootView == null)
{ {
Console.WriteLine("[LinuxApplication.GTK] _rootView is null!"); DiagnosticLog.Warn("LinuxApplication", "GTK _rootView is null!");
return; return;
} }
var hitView = _rootView.HitTest((float)e.X, (float)e.Y); var hitView = _rootView.HitTest((float)e.X, (float)e.Y);
Console.WriteLine($"[LinuxApplication.GTK] HitView: {hitView?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("LinuxApplication", $"GTK HitView: {hitView?.GetType().Name ?? "null"}");
if (hitView != null) if (hitView != null)
{ {
@@ -1116,17 +1118,17 @@ public class LinuxApplication : IDisposable
_capturedView = hitView; _capturedView = hitView;
var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right; var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right;
var args = new PointerEventArgs((float)e.X, (float)e.Y, button); var args = new PointerEventArgs((float)e.X, (float)e.Y, button);
Console.WriteLine("[DIAG] >>> Before OnPointerPressed"); DiagnosticLog.Debug("LinuxApplication", ">>> Before OnPointerPressed");
hitView.OnPointerPressed(args); hitView.OnPointerPressed(args);
Console.WriteLine("[DIAG] <<< After OnPointerPressed, calling RequestRedraw"); DiagnosticLog.Debug("LinuxApplication", "<<< After OnPointerPressed, calling RequestRedraw");
_gtkWindow?.RequestRedraw(); _gtkWindow?.RequestRedraw();
Console.WriteLine("[DIAG] <<< After RequestRedraw, returning from handler"); DiagnosticLog.Debug("LinuxApplication", "<<< After RequestRedraw, returning from handler");
} }
} }
private void OnGtkPointerReleased(object? sender, (double X, double Y, int Button) e) private void OnGtkPointerReleased(object? sender, (double X, double Y, int Button) e)
{ {
Console.WriteLine("[DIAG] >>> OnGtkPointerReleased ENTER"); DiagnosticLog.Debug("LinuxApplication", ">>> OnGtkPointerReleased ENTER");
// Route to dialog if one is active // Route to dialog if one is active
if (LinuxDialogService.HasActiveDialog) if (LinuxDialogService.HasActiveDialog)
@@ -1144,12 +1146,12 @@ public class LinuxApplication : IDisposable
{ {
var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right; var button = e.Button == 1 ? PointerButton.Left : e.Button == 2 ? PointerButton.Middle : PointerButton.Right;
var args = new PointerEventArgs((float)e.X, (float)e.Y, button); var args = new PointerEventArgs((float)e.X, (float)e.Y, button);
Console.WriteLine($"[DIAG] Calling OnPointerReleased on {_capturedView.GetType().Name}"); DiagnosticLog.Debug("LinuxApplication", $"Calling OnPointerReleased on {_capturedView.GetType().Name}");
_capturedView.OnPointerReleased(args); _capturedView.OnPointerReleased(args);
Console.WriteLine("[DIAG] OnPointerReleased returned"); DiagnosticLog.Debug("LinuxApplication", "OnPointerReleased returned");
_capturedView = null; _capturedView = null;
_gtkWindow?.RequestRedraw(); _gtkWindow?.RequestRedraw();
Console.WriteLine("[DIAG] <<< OnGtkPointerReleased EXIT (captured path)"); DiagnosticLog.Debug("LinuxApplication", "<<< OnGtkPointerReleased EXIT (captured path)");
} }
else else
{ {

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Native; namespace Microsoft.Maui.Platform.Linux.Native;
@@ -38,7 +39,7 @@ public static class GLibNative
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GLibNative] Error in idle callback: " + ex.Message); DiagnosticLog.Error("GLibNative", "Error in idle callback", ex);
} }
if (!flag) if (!flag)
{ {
@@ -68,7 +69,7 @@ public static class GLibNative
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GLibNative] Error in timeout callback: " + ex.Message); DiagnosticLog.Error("GLibNative", "Error in timeout callback", ex);
} }
if (!flag) if (!flag)
{ {

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Native; namespace Microsoft.Maui.Platform.Linux.Native;
@@ -131,14 +132,14 @@ internal static class WebKitNative
_handle = dlopen(text, 258); _handle = dlopen(text, 258);
if (_handle != IntPtr.Zero) if (_handle != IntPtr.Zero)
{ {
Console.WriteLine("[WebKitNative] Loaded " + text); DiagnosticLog.Debug("WebKitNative", "Loaded " + text);
break; break;
} }
} }
if (_handle == IntPtr.Zero) if (_handle == IntPtr.Zero)
{ {
Console.WriteLine("[WebKitNative] Failed to load WebKitGTK library"); DiagnosticLog.Warn("WebKitNative", "Failed to load WebKitGTK library");
return false; return false;
} }
@@ -170,7 +171,7 @@ internal static class WebKitNative
if (intPtr != IntPtr.Zero) if (intPtr != IntPtr.Zero)
{ {
_gSignalConnectData = Marshal.GetDelegateForFunctionPointer<GSignalConnectDataDelegate>(intPtr); _gSignalConnectData = Marshal.GetDelegateForFunctionPointer<GSignalConnectDataDelegate>(intPtr);
Console.WriteLine("[WebKitNative] Loaded g_signal_connect_data"); DiagnosticLog.Debug("WebKitNative", "Loaded g_signal_connect_data");
} }
} }
@@ -297,7 +298,7 @@ internal static class WebKitNative
{ {
if (_gSignalConnectData == null || webView == IntPtr.Zero) if (_gSignalConnectData == null || webView == IntPtr.Zero)
{ {
Console.WriteLine("[WebKitNative] Cannot connect load-changed: signal connect not available"); DiagnosticLog.Warn("WebKitNative", "Cannot connect load-changed: signal connect not available");
return 0uL; return 0uL;
} }
_loadChangedCallbacks[webView] = callback; _loadChangedCallbacks[webView] = callback;
@@ -317,7 +318,7 @@ internal static class WebKitNative
{ {
if (_gSignalConnectData == null || webView == IntPtr.Zero) if (_gSignalConnectData == null || webView == IntPtr.Zero)
{ {
Console.WriteLine("[WebKitNative] Cannot connect script-dialog: signal connect not available"); DiagnosticLog.Warn("WebKitNative", "Cannot connect script-dialog: signal connect not available");
return 0uL; return 0uL;
} }
_scriptDialogCallbacks[webView] = callback; _scriptDialogCallbacks[webView] = callback;

View File

@@ -14,18 +14,18 @@
<!-- NuGet Package Properties --> <!-- NuGet Package Properties -->
<PackageId>OpenMaui.Controls.Linux</PackageId> <PackageId>OpenMaui.Controls.Linux</PackageId>
<Version>1.0.0-rc.1</Version> <Version>1.0.0</Version>
<Authors>MarketAlly LLC, David H. Friedel Jr.</Authors> <Authors>MarketAlly LLC, David H. Friedel Jr.</Authors>
<Company>MarketAlly LLC</Company> <Company>MarketAlly LLC</Company>
<Product>OpenMaui Linux Controls</Product> <Product>OpenMaui Linux Controls</Product>
<Description>Linux desktop support for .NET MAUI applications using SkiaSharp rendering. Supports X11 and Wayland display servers with 35+ controls, platform services, and accessibility support.</Description> <Description>Linux desktop support for .NET MAUI applications using SkiaSharp rendering. Supports X11 and Wayland display servers with 35+ controls, platform services, and accessibility support.</Description>
<Copyright>Copyright 2025 MarketAlly LLC</Copyright> <Copyright>Copyright 2025-2026 MarketAlly LLC</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://git.marketally.com/open-maui/maui-linux</PackageProjectUrl> <PackageProjectUrl>https://git.marketally.com/open-maui/maui-linux</PackageProjectUrl>
<RepositoryUrl>https://git.marketally.com/open-maui/maui-linux.git</RepositoryUrl> <RepositoryUrl>https://git.marketally.com/open-maui/maui-linux.git</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>maui;linux;desktop;skia;gui;cross-platform;dotnet;x11;wayland;openmaui</PackageTags> <PackageTags>maui;linux;desktop;skia;gui;cross-platform;dotnet;x11;wayland;openmaui</PackageTags>
<PackageReleaseNotes>RC1: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. 217 passing tests.</PackageReleaseNotes> <PackageReleaseNotes>1.0: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. 217 passing tests.</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>

View File

@@ -2,13 +2,13 @@
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata> <metadata>
<id>OpenMaui.Controls.Linux</id> <id>OpenMaui.Controls.Linux</id>
<version>1.0.0-preview.1</version> <version>1.0.0</version>
<title>OpenMaui Linux Controls</title> <title>OpenMaui Linux Controls</title>
<authors>MarketAlly LLC, David H. Friedel Jr.</authors> <authors>MarketAlly LLC, David H. Friedel Jr.</authors>
<owners>MarketAlly LLC</owners> <owners>MarketAlly LLC</owners>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/open-maui/maui-linux</projectUrl> <projectUrl>https://git.marketally.com/open-maui/maui-linux</projectUrl>
<iconUrl>https://raw.githubusercontent.com/dotnet/maui/main/assets/icon.png</iconUrl> <iconUrl>https://git.marketally.com/open-maui/maui-linux/raw/branch/main/assets/icon.png</iconUrl>
<description> <description>
Linux desktop support for .NET MAUI applications. This package enables running MAUI applications on Linux desktop environments using SkiaSharp for rendering. Linux desktop support for .NET MAUI applications. This package enables running MAUI applications on Linux desktop environments using SkiaSharp for rendering.
@@ -24,22 +24,16 @@ Features:
Developed by MarketAlly LLC. Lead Architect: David H. Friedel Jr. Developed by MarketAlly LLC. Lead Architect: David H. Friedel Jr.
</description> </description>
<releaseNotes> <releaseNotes>
Initial release: 1.0: 100% .NET MAUI API compliance - all public APIs use MAUI types (Color, Rect, Size, Thickness, double). Full XAML support with BindableProperty for all controls, Visual State Manager integration, data binding, and XAML styles. Core SkiaSharp-based rendering engine, X11 window management with full input handling, 35+ control implementations, 18 platform services, accessibility support. 217 passing tests.
- Core SkiaSharp-based rendering engine
- X11 window management with full input handling
- 35+ control implementations
- 18 platform services
- Accessibility support
- 216 unit tests
</releaseNotes> </releaseNotes>
<copyright>Copyright 2025 MarketAlly LLC</copyright> <copyright>Copyright 2025-2026 MarketAlly LLC</copyright>
<tags>maui linux desktop skia gui cross-platform dotnet openmaui</tags> <tags>maui linux desktop skia gui cross-platform dotnet openmaui</tags>
<repository type="git" url="https://github.com/open-maui/maui-linux.git" /> <repository type="git" url="https://git.marketally.com/open-maui/maui-linux.git" />
<dependencies> <dependencies>
<group targetFramework="net9.0"> <group targetFramework="net9.0">
<dependency id="Microsoft.Maui.Controls" version="9.0.0" /> <dependency id="Microsoft.Maui.Controls" version="9.0.40" />
<dependency id="SkiaSharp" version="2.88.8" /> <dependency id="SkiaSharp" version="2.88.9" />
<dependency id="SkiaSharp.NativeAssets.Linux" version="2.88.8" /> <dependency id="SkiaSharp.NativeAssets.Linux" version="2.88.9" />
</group> </group>
</dependencies> </dependencies>
<frameworkAssemblies> <frameworkAssemblies>

View File

@@ -3,6 +3,7 @@
using SkiaSharp; using SkiaSharp;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using Microsoft.Maui.Platform.Linux.Window; using Microsoft.Maui.Platform.Linux.Window;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -58,7 +59,7 @@ public class GpuRenderingEngine : IDisposable
if (!_gpuAvailable) if (!_gpuAvailable)
{ {
Console.WriteLine("[GpuRenderingEngine] GPU not available, using software rendering"); DiagnosticLog.Debug("GpuRenderingEngine", "GPU not available, using software rendering");
InitializeSoftwareRendering(); InitializeSoftwareRendering();
} }
@@ -74,25 +75,25 @@ public class GpuRenderingEngine : IDisposable
var glInterface = GRGlInterface.Create(); var glInterface = GRGlInterface.Create();
if (glInterface == null) if (glInterface == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GL interface"); DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GL interface");
return false; return false;
} }
_grContext = GRContext.CreateGl(glInterface); _grContext = GRContext.CreateGl(glInterface);
if (_grContext == null) if (_grContext == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GR context"); DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GR context");
glInterface.Dispose(); glInterface.Dispose();
return false; return false;
} }
CreateGpuSurface(); CreateGpuSurface();
Console.WriteLine("[GpuRenderingEngine] GPU acceleration enabled"); DiagnosticLog.Debug("GpuRenderingEngine", "GPU acceleration enabled");
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GpuRenderingEngine] GPU initialization failed: {ex.Message}"); DiagnosticLog.Error("GpuRenderingEngine", "GPU initialization failed", ex);
return false; return false;
} }
} }
@@ -124,7 +125,7 @@ public class GpuRenderingEngine : IDisposable
if (_surface == null) if (_surface == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GPU surface, falling back to software"); DiagnosticLog.Warn("GpuRenderingEngine", "Failed to create GPU surface, falling back to software");
_gpuAvailable = false; _gpuAvailable = false;
InitializeSoftwareRendering(); InitializeSoftwareRendering();
return; return;

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Native; using Microsoft.Maui.Platform.Linux.Native;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
@@ -150,7 +151,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable
GtkNative.g_signal_connect_data(_widget, "key-release-event", Marshal.GetFunctionPointerForDelegate(_keyReleaseCallback), IntPtr.Zero, IntPtr.Zero, 0); GtkNative.g_signal_connect_data(_widget, "key-release-event", Marshal.GetFunctionPointerForDelegate(_keyReleaseCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "scroll-event", Marshal.GetFunctionPointerForDelegate(_scrollCallback), IntPtr.Zero, IntPtr.Zero, 0); GtkNative.g_signal_connect_data(_widget, "scroll-event", Marshal.GetFunctionPointerForDelegate(_scrollCallback), IntPtr.Zero, IntPtr.Zero, 0);
Console.WriteLine($"[GtkSkiaSurfaceWidget] Created with size {width}x{height}"); DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"Created with size {width}x{height}");
} }
private void CreateBuffer(int width, int height) private void CreateBuffer(int width, int height)
@@ -179,7 +180,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable
_imageInfo.Height, _imageInfo.Height,
_imageInfo.RowBytes); _imageInfo.RowBytes);
Console.WriteLine($"[GtkSkiaSurfaceWidget] Created buffer {width}x{height}, stride={_imageInfo.RowBytes}"); DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"Created buffer {width}x{height}, stride={_imageInfo.RowBytes}");
} }
public void Resize(int width, int height) public void Resize(int width, int height)
@@ -303,7 +304,7 @@ public sealed class GtkSkiaSurfaceWidget : IDisposable
if (!char.IsControl(c) || c == '\r' || c == '\n' || c == '\t') if (!char.IsControl(c) || c == '\r' || c == '\n' || c == '\t')
{ {
string text = c.ToString(); string text = c.ToString();
Console.WriteLine($"[GtkSkiaSurfaceWidget] TextInput: '{text}' (keyval={keyval}, unicode={unicode})"); DiagnosticLog.Debug("GtkSkiaSurfaceWidget", $"TextInput: '{text}' (keyval={keyval}, unicode={unicode})");
TextInput?.Invoke(this, text); TextInput?.Invoke(this, text);
} }
} }

View File

@@ -5,6 +5,7 @@ using SkiaSharp;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Window; using Microsoft.Maui.Platform.Linux.Window;
using Microsoft.Maui.Platform; using Microsoft.Maui.Platform;
using Microsoft.Maui.Platform.Linux.Services;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
@@ -169,8 +170,16 @@ public class SkiaRenderingEngine : IDisposable
// Measure and arrange // Measure and arrange
var availableSize = new Size(Width, Height); var availableSize = new Size(Width, Height);
rootView.Measure(availableSize); try
rootView.Arrange(new Rect(0, 0, Width, Height)); {
rootView.Measure(availableSize);
rootView.Arrange(new Rect(0, 0, Width, Height));
}
catch (Exception ex)
{
DiagnosticLog.Error("SkiaRenderingEngine", "Exception during Measure/Arrange", ex);
return;
}
// Determine what to redraw // Determine what to redraw
List<SKRect> regionsToRedraw; List<SKRect> regionsToRedraw;
@@ -199,16 +208,37 @@ public class SkiaRenderingEngine : IDisposable
// Render dirty regions // Render dirty regions
foreach (var region in regionsToRedraw) foreach (var region in regionsToRedraw)
{ {
RenderRegion(rootView, region, isFullRedraw); try
{
RenderRegion(rootView, region, isFullRedraw);
}
catch (Exception ex)
{
DiagnosticLog.Error("SkiaRenderingEngine", $"Exception rendering region {region}", ex);
}
} }
// Draw popup overlays (always on top, full redraw) // Draw popup overlays (always on top, full redraw)
SkiaView.DrawPopupOverlays(_canvas); try
{
SkiaView.DrawPopupOverlays(_canvas);
}
catch (Exception ex)
{
DiagnosticLog.Error("SkiaRenderingEngine", "Exception drawing popup overlays", ex);
}
// Draw modal dialogs and context menus on top of everything // Draw modal dialogs and context menus on top of everything
if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu) try
{ {
LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu)
{
LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height));
}
}
catch (Exception ex)
{
DiagnosticLog.Error("SkiaRenderingEngine", "Exception drawing dialogs", ex);
} }
_canvas.Flush(); _canvas.Flush();
@@ -234,7 +264,14 @@ public class SkiaRenderingEngine : IDisposable
_canvas.DrawRect(region, clearPaint); _canvas.DrawRect(region, clearPaint);
// Draw the view tree (views will naturally clip to their bounds) // Draw the view tree (views will naturally clip to their bounds)
rootView.Draw(_canvas); try
{
rootView.Draw(_canvas);
}
catch (Exception ex)
{
DiagnosticLog.Error("SkiaRenderingEngine", "Exception during view Draw", ex);
}
_canvas.Restore(); _canvas.Restore();
} }

View File

@@ -36,7 +36,7 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable
int result = atspi_init(); int result = atspi_init();
if (result != 0) if (result != 0)
{ {
Console.WriteLine("AtSpi2AccessibilityService: Failed to initialize AT-SPI2"); DiagnosticLog.Error("AtSpi2AccessibilityService", "Failed to initialize AT-SPI2");
return; return;
} }
@@ -51,16 +51,16 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable
// Register our application // Register our application
RegisterApplication(); RegisterApplication();
Console.WriteLine("AtSpi2AccessibilityService: Initialized successfully"); DiagnosticLog.Debug("AtSpi2AccessibilityService", "Initialized successfully");
} }
else else
{ {
Console.WriteLine("AtSpi2AccessibilityService: Accessibility is not enabled"); DiagnosticLog.Warn("AtSpi2AccessibilityService", "Accessibility is not enabled");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"AtSpi2AccessibilityService: Initialization failed - {ex.Message}"); DiagnosticLog.Error("AtSpi2AccessibilityService", $"Initialization failed - {ex.Message}");
} }
} }
@@ -168,11 +168,11 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable
// or by emitting "object:announcement" events // or by emitting "object:announcement" events
// For now, use a simpler approach with the event system // For now, use a simpler approach with the event system
Console.WriteLine($"[Accessibility Announcement ({priority})]: {text}"); DiagnosticLog.Debug("AtSpi2AccessibilityService", $"Announcement ({priority}): {text}");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"AtSpi2AccessibilityService: Announcement failed - {ex.Message}"); DiagnosticLog.Error("AtSpi2AccessibilityService", $"Announcement failed - {ex.Message}");
} }
} }
@@ -182,7 +182,7 @@ public class AtSpi2AccessibilityService : IAccessibilityService, IDisposable
// using the org.a11y.atspi.Event interface // using the org.a11y.atspi.Event interface
// For now, log the event for debugging // For now, log the event for debugging
Console.WriteLine($"[AT-SPI2 Event] {eventName}: {accessible.AccessibleName} ({accessible.Role})"); DiagnosticLog.Debug("AtSpi2AccessibilityService", $"Event {eventName}: {accessible.AccessibleName} ({accessible.Role})");
} }
/// <summary> /// <summary>

80
Services/DiagnosticLog.cs Normal file
View File

@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
namespace Microsoft.Maui.Platform.Linux.Services;
/// <summary>
/// Centralized diagnostic logging for the Linux MAUI platform.
/// Logging is enabled only in DEBUG builds by default, or when
/// explicitly enabled via <see cref="IsEnabled"/>.
/// </summary>
public static class DiagnosticLog
{
private static bool? _isEnabled;
/// <summary>
/// Gets or sets whether diagnostic logging is enabled.
/// Defaults to true in DEBUG builds, false in RELEASE builds.
/// </summary>
public static bool IsEnabled
{
get
{
if (_isEnabled.HasValue)
return _isEnabled.Value;
#if DEBUG
return true;
#else
return false;
#endif
}
set => _isEnabled = value;
}
/// <summary>
/// Logs an informational diagnostic message.
/// </summary>
[Conditional("DEBUG")]
public static void Debug(string tag, string message)
{
if (IsEnabled)
System.Console.WriteLine($"[{tag}] {message}");
}
/// <summary>
/// Logs an informational diagnostic message (always writes when enabled, not conditional on DEBUG).
/// Use for important operational messages that should appear in release builds when logging is enabled.
/// </summary>
public static void Info(string tag, string message)
{
if (IsEnabled)
System.Console.WriteLine($"[{tag}] {message}");
}
/// <summary>
/// Logs a warning message. Always writes when logging is enabled.
/// </summary>
public static void Warn(string tag, string message)
{
if (IsEnabled)
System.Console.Error.WriteLine($"[{tag}] WARNING: {message}");
}
/// <summary>
/// Logs an error message. Always writes regardless of IsEnabled.
/// </summary>
public static void Error(string tag, string message)
{
System.Console.Error.WriteLine($"[{tag}] ERROR: {message}");
}
/// <summary>
/// Logs an error message with exception details. Always writes regardless of IsEnabled.
/// </summary>
public static void Error(string tag, string message, Exception ex)
{
System.Console.Error.WriteLine($"[{tag}] ERROR: {message}: {ex.Message}");
}
}

View File

@@ -29,12 +29,12 @@ public static class DisplayServerFactory
if (!string.IsNullOrEmpty(xDisplay) && !string.IsNullOrEmpty(preferX11)) if (!string.IsNullOrEmpty(xDisplay) && !string.IsNullOrEmpty(preferX11))
{ {
Console.WriteLine("[DisplayServer] XWayland detected, using X11 backend (MAUI_PREFER_X11 set)"); DiagnosticLog.Debug("DisplayServerFactory", "XWayland detected, using X11 backend (MAUI_PREFER_X11 set)");
_cachedServerType = DisplayServerType.X11; _cachedServerType = DisplayServerType.X11;
return DisplayServerType.X11; return DisplayServerType.X11;
} }
Console.WriteLine("[DisplayServer] Wayland display detected"); DiagnosticLog.Debug("DisplayServerFactory", "Wayland display detected");
_cachedServerType = DisplayServerType.Wayland; _cachedServerType = DisplayServerType.Wayland;
return DisplayServerType.Wayland; return DisplayServerType.Wayland;
} }
@@ -43,13 +43,13 @@ public static class DisplayServerFactory
var x11Display = Environment.GetEnvironmentVariable("DISPLAY"); var x11Display = Environment.GetEnvironmentVariable("DISPLAY");
if (!string.IsNullOrEmpty(x11Display)) if (!string.IsNullOrEmpty(x11Display))
{ {
Console.WriteLine("[DisplayServer] X11 display detected"); DiagnosticLog.Debug("DisplayServerFactory", "X11 display detected");
_cachedServerType = DisplayServerType.X11; _cachedServerType = DisplayServerType.X11;
return DisplayServerType.X11; return DisplayServerType.X11;
} }
// Default to X11 and let it fail if not available // Default to X11 and let it fail if not available
Console.WriteLine("[DisplayServer] No display server detected, defaulting to X11"); DiagnosticLog.Warn("DisplayServerFactory", "No display server detected, defaulting to X11");
_cachedServerType = DisplayServerType.X11; _cachedServerType = DisplayServerType.X11;
return DisplayServerType.X11; return DisplayServerType.X11;
} }
@@ -76,12 +76,12 @@ public static class DisplayServerFactory
{ {
try try
{ {
Console.WriteLine($"[DisplayServer] Creating X11 window: {title} ({width}x{height})"); DiagnosticLog.Debug("DisplayServerFactory", $"Creating X11 window: {title} ({width}x{height})");
return new X11DisplayWindow(title, width, height); return new X11DisplayWindow(title, width, height);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[DisplayServer] Failed to create X11 window: {ex.Message}"); DiagnosticLog.Error("DisplayServerFactory", $"Failed to create X11 window: {ex.Message}");
throw; throw;
} }
} }
@@ -90,18 +90,18 @@ public static class DisplayServerFactory
{ {
try try
{ {
Console.WriteLine($"[DisplayServer] Creating Wayland window: {title} ({width}x{height})"); DiagnosticLog.Debug("DisplayServerFactory", $"Creating Wayland window: {title} ({width}x{height})");
return new WaylandDisplayWindow(title, width, height); return new WaylandDisplayWindow(title, width, height);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[DisplayServer] Failed to create Wayland window: {ex.Message}"); DiagnosticLog.Error("DisplayServerFactory", $"Failed to create Wayland window: {ex.Message}");
// Try to fall back to X11 via XWayland // Try to fall back to X11 via XWayland
var xDisplay = Environment.GetEnvironmentVariable("DISPLAY"); var xDisplay = Environment.GetEnvironmentVariable("DISPLAY");
if (!string.IsNullOrEmpty(xDisplay)) if (!string.IsNullOrEmpty(xDisplay))
{ {
Console.WriteLine("[DisplayServer] Falling back to X11 (XWayland)"); DiagnosticLog.Warn("DisplayServerFactory", "Falling back to X11 (XWayland)");
return CreateX11Window(title, width, height); return CreateX11Window(title, width, height);
} }

View File

@@ -48,18 +48,18 @@ public class Fcitx5InputMethodService : IInputMethodService, IDisposable
if (start >= 0 && end > start) if (start >= 0 && end > start)
{ {
_inputContextPath = output.Substring(start + 1, end - start - 1); _inputContextPath = output.Substring(start + 1, end - start - 1);
Console.WriteLine($"Fcitx5InputMethodService: Created context at {_inputContextPath}"); DiagnosticLog.Debug("Fcitx5InputMethodService", $"Created context at {_inputContextPath}");
StartMonitoring(); StartMonitoring();
} }
} }
else else
{ {
Console.WriteLine("Fcitx5InputMethodService: Failed to create input context"); DiagnosticLog.Error("Fcitx5InputMethodService", "Failed to create input context");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Fcitx5InputMethodService: Initialization failed - {ex.Message}"); DiagnosticLog.Error("Fcitx5InputMethodService", $"Initialization failed - {ex.Message}");
} }
} }
@@ -102,7 +102,7 @@ public class Fcitx5InputMethodService : IInputMethodService, IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Fcitx5InputMethodService: Monitor error - {ex.Message}"); DiagnosticLog.Error("Fcitx5InputMethodService", $"Monitor error - {ex.Message}");
} }
}); });
} }

View File

@@ -91,7 +91,7 @@ public class FilePickerService : IFilePicker
if (tool == DialogTool.None) if (tool == DialogTool.None)
{ {
// Fall back to console path input // Fall back to console path input
Console.WriteLine("No file dialog available. Please enter file path:"); DiagnosticLog.Warn("FilePickerService", "No file dialog available. Please enter file path:");
var path = Console.ReadLine(); var path = Console.ReadLine();
if (!string.IsNullOrEmpty(path) && File.Exists(path)) if (!string.IsNullOrEmpty(path) && File.Exists(path))
{ {

View File

@@ -76,7 +76,7 @@ public class GlobalHotkeyService : IDisposable
int result = XGrabKey(_display, keyCode, mask, _rootWindow, true, GrabModeAsync, GrabModeAsync); int result = XGrabKey(_display, keyCode, mask, _rootWindow, true, GrabModeAsync, GrabModeAsync);
if (result == 0) if (result == 0)
{ {
Console.WriteLine($"Failed to grab key {key} with modifiers {modifiers}"); DiagnosticLog.Warn("GlobalHotkeyService", $"Failed to grab key {key} with modifiers {modifiers}");
} }
} }
@@ -148,7 +148,7 @@ public class GlobalHotkeyService : IDisposable
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"GlobalHotkeyService error: {ex.Message}"); DiagnosticLog.Error("GlobalHotkeyService", $"Error: {ex.Message}");
} }
} }
} }

View File

@@ -295,17 +295,17 @@ public class Gtk4InteropService : IDisposable
{ {
_useGtk4 = true; _useGtk4 = true;
_initialized = true; _initialized = true;
Console.WriteLine("[GTK4] Initialized GTK4"); DiagnosticLog.Debug("Gtk4InteropService", "Initialized GTK4");
return true; return true;
} }
} }
catch (DllNotFoundException) catch (DllNotFoundException)
{ {
Console.WriteLine("[GTK4] GTK4 not found, trying GTK3"); DiagnosticLog.Warn("Gtk4InteropService", "GTK4 not found, trying GTK3");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GTK4] GTK4 init failed: {ex.Message}"); DiagnosticLog.Error("Gtk4InteropService", $"GTK4 init failed: {ex.Message}");
} }
// Fall back to GTK3 // Fall back to GTK3
@@ -317,17 +317,17 @@ public class Gtk4InteropService : IDisposable
{ {
_useGtk4 = false; _useGtk4 = false;
_initialized = true; _initialized = true;
Console.WriteLine("[GTK4] Initialized GTK3 (fallback)"); DiagnosticLog.Debug("Gtk4InteropService", "Initialized GTK3 (fallback)");
return true; return true;
} }
} }
catch (DllNotFoundException) catch (DllNotFoundException)
{ {
Console.WriteLine("[GTK4] GTK3 not found"); DiagnosticLog.Warn("Gtk4InteropService", "GTK3 not found");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GTK4] GTK3 init failed: {ex.Message}"); DiagnosticLog.Error("Gtk4InteropService", $"GTK3 init failed: {ex.Message}");
} }
return false; return false;

View File

@@ -31,7 +31,7 @@ public static class GtkContextMenuService
IntPtr menu = GtkNative.gtk_menu_new(); IntPtr menu = GtkNative.gtk_menu_new();
if (menu == IntPtr.Zero) if (menu == IntPtr.Zero)
{ {
Console.WriteLine("[GtkContextMenuService] Failed to create GTK menu"); DiagnosticLog.Error("GtkContextMenuService", "Failed to create GTK menu");
return; return;
} }
@@ -56,7 +56,7 @@ public static class GtkContextMenuService
ActivateCallback callback = delegate ActivateCallback callback = delegate
{ {
Console.WriteLine("[GtkContextMenuService] Menu item activated: " + item.Text); DiagnosticLog.Debug("GtkContextMenuService", "Menu item activated: " + item.Text);
_actions[actionIndex]?.Invoke(); _actions[actionIndex]?.Invoke();
}; };
_callbacks.Add(callback); _callbacks.Add(callback);
@@ -88,7 +88,7 @@ public static class GtkContextMenuService
GtkNative.gdk_event_free(currentEvent); GtkNative.gdk_event_free(currentEvent);
} }
Console.WriteLine($"[GtkContextMenuService] Showed GTK menu with {items.Count} items"); DiagnosticLog.Debug("GtkContextMenuService", $"Showed GTK menu with {items.Count} items");
} }
/// <summary> /// <summary>
@@ -107,7 +107,7 @@ public static class GtkContextMenuService
isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark;
} }
Console.WriteLine($"[GtkContextMenuService] ApplyMenuTheme: isDark={isDark}"); DiagnosticLog.Debug("GtkContextMenuService", $"ApplyMenuTheme: isDark={isDark}");
// Create comprehensive CSS based on the theme // Create comprehensive CSS based on the theme
string css = isDark string css = isDark
@@ -164,7 +164,7 @@ public static class GtkContextMenuService
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkContextMenuService] Error applying menu theme: {ex.Message}"); DiagnosticLog.Error("GtkContextMenuService", $"Error applying menu theme: {ex.Message}");
} }
} }
} }

View File

@@ -38,7 +38,7 @@ public static class GtkThemeService
isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark;
} }
Console.WriteLine($"[GtkThemeService] ApplyTheme: isDark={isDark}"); DiagnosticLog.Debug("GtkThemeService", $"ApplyTheme: isDark={isDark}");
// Create comprehensive CSS based on the theme // Create comprehensive CSS based on the theme
string css = isDark ? GetDarkCss() : GetLightCss(); string css = isDark ? GetDarkCss() : GetLightCss();
@@ -47,7 +47,7 @@ public static class GtkThemeService
IntPtr screen = GtkNative.gdk_screen_get_default(); IntPtr screen = GtkNative.gdk_screen_get_default();
if (screen == IntPtr.Zero) if (screen == IntPtr.Zero)
{ {
Console.WriteLine("[GtkThemeService] Failed to get default screen"); DiagnosticLog.Error("GtkThemeService", "Failed to get default screen");
return; return;
} }
@@ -55,14 +55,14 @@ public static class GtkThemeService
IntPtr newProvider = GtkNative.gtk_css_provider_new(); IntPtr newProvider = GtkNative.gtk_css_provider_new();
if (newProvider == IntPtr.Zero) if (newProvider == IntPtr.Zero)
{ {
Console.WriteLine("[GtkThemeService] Failed to create CSS provider"); DiagnosticLog.Error("GtkThemeService", "Failed to create CSS provider");
return; return;
} }
// Load CSS data // Load CSS data
if (!GtkNative.gtk_css_provider_load_from_data(newProvider, css, -1, IntPtr.Zero)) if (!GtkNative.gtk_css_provider_load_from_data(newProvider, css, -1, IntPtr.Zero))
{ {
Console.WriteLine("[GtkThemeService] Failed to load CSS data"); DiagnosticLog.Error("GtkThemeService", "Failed to load CSS data");
return; return;
} }
@@ -72,11 +72,11 @@ public static class GtkThemeService
// Store reference to current provider // Store reference to current provider
_currentCssProvider = newProvider; _currentCssProvider = newProvider;
Console.WriteLine("[GtkThemeService] CSS applied successfully"); DiagnosticLog.Debug("GtkThemeService", "CSS applied successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GtkThemeService] Error applying theme: {ex.Message}"); DiagnosticLog.Error("GtkThemeService", $"Error applying theme: {ex.Message}");
} }
} }

View File

@@ -204,7 +204,7 @@ public class HardwareVideoService : IDisposable
{ {
_currentApi = VideoAccelerationApi.VaApi; _currentApi = VideoAccelerationApi.VaApi;
_initialized = true; _initialized = true;
Console.WriteLine($"[HardwareVideo] Initialized VA-API with {_supportedProfiles.Count} supported profiles"); DiagnosticLog.Debug("HardwareVideoService", $"Initialized VA-API with {_supportedProfiles.Count} supported profiles");
return true; return true;
} }
} }
@@ -216,12 +216,12 @@ public class HardwareVideoService : IDisposable
{ {
_currentApi = VideoAccelerationApi.Vdpau; _currentApi = VideoAccelerationApi.Vdpau;
_initialized = true; _initialized = true;
Console.WriteLine("[HardwareVideo] Initialized VDPAU"); DiagnosticLog.Debug("HardwareVideoService", "Initialized VDPAU");
return true; return true;
} }
} }
Console.WriteLine("[HardwareVideo] No hardware acceleration available, using software"); DiagnosticLog.Warn("HardwareVideoService", "No hardware acceleration available, using software");
_currentApi = VideoAccelerationApi.Software; _currentApi = VideoAccelerationApi.Software;
return false; return false;
} }
@@ -261,12 +261,12 @@ public class HardwareVideoService : IDisposable
} }
catch (DllNotFoundException) catch (DllNotFoundException)
{ {
Console.WriteLine("[HardwareVideo] VA-API libraries not found"); DiagnosticLog.Warn("HardwareVideoService", "VA-API libraries not found");
return false; return false;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[HardwareVideo] VA-API initialization failed: {ex.Message}"); DiagnosticLog.Error("HardwareVideoService", $"VA-API initialization failed: {ex.Message}");
return false; return false;
} }
} }
@@ -276,11 +276,11 @@ public class HardwareVideoService : IDisposable
int status = vaInitialize(_vaDisplay, out int major, out int minor); int status = vaInitialize(_vaDisplay, out int major, out int minor);
if (status != VA_STATUS_SUCCESS) if (status != VA_STATUS_SUCCESS)
{ {
Console.WriteLine($"[HardwareVideo] vaInitialize failed: {GetVaError(status)}"); DiagnosticLog.Error("HardwareVideoService", $"vaInitialize failed: {GetVaError(status)}");
return false; return false;
} }
Console.WriteLine($"[HardwareVideo] VA-API {major}.{minor} initialized"); DiagnosticLog.Debug("HardwareVideoService", $"VA-API {major}.{minor} initialized");
// Query supported profiles // Query supported profiles
int[] profiles = new int[32]; int[] profiles = new int[32];
@@ -331,11 +331,11 @@ public class HardwareVideoService : IDisposable
} }
catch (DllNotFoundException) catch (DllNotFoundException)
{ {
Console.WriteLine("[HardwareVideo] VDPAU libraries not found"); DiagnosticLog.Warn("HardwareVideoService", "VDPAU libraries not found");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[HardwareVideo] VDPAU initialization failed: {ex.Message}"); DiagnosticLog.Error("HardwareVideoService", $"VDPAU initialization failed: {ex.Message}");
} }
return false; return false;
@@ -355,7 +355,7 @@ public class HardwareVideoService : IDisposable
if (!_supportedProfiles.Contains(profile)) if (!_supportedProfiles.Contains(profile))
{ {
Console.WriteLine($"[HardwareVideo] Profile {profile} not supported"); DiagnosticLog.Warn("HardwareVideoService", $"Profile {profile} not supported");
return false; return false;
} }
@@ -383,7 +383,7 @@ public class HardwareVideoService : IDisposable
int status = vaCreateConfig(_vaDisplay, vaProfile, VAEntrypointVLD, IntPtr.Zero, 0, out _vaConfigId); int status = vaCreateConfig(_vaDisplay, vaProfile, VAEntrypointVLD, IntPtr.Zero, 0, out _vaConfigId);
if (status != VA_STATUS_SUCCESS) if (status != VA_STATUS_SUCCESS)
{ {
Console.WriteLine($"[HardwareVideo] vaCreateConfig failed: {GetVaError(status)}"); DiagnosticLog.Error("HardwareVideoService", $"vaCreateConfig failed: {GetVaError(status)}");
return false; return false;
} }
@@ -396,7 +396,7 @@ public class HardwareVideoService : IDisposable
status = vaCreateSurfaces(_vaDisplay, format, (uint)width, (uint)height, _vaSurfaces, 8, IntPtr.Zero, 0); status = vaCreateSurfaces(_vaDisplay, format, (uint)width, (uint)height, _vaSurfaces, 8, IntPtr.Zero, 0);
if (status != VA_STATUS_SUCCESS) if (status != VA_STATUS_SUCCESS)
{ {
Console.WriteLine($"[HardwareVideo] vaCreateSurfaces failed: {GetVaError(status)}"); DiagnosticLog.Error("HardwareVideoService", $"vaCreateSurfaces failed: {GetVaError(status)}");
vaDestroyConfig(_vaDisplay, _vaConfigId); vaDestroyConfig(_vaDisplay, _vaConfigId);
return false; return false;
} }
@@ -405,13 +405,13 @@ public class HardwareVideoService : IDisposable
status = vaCreateContext(_vaDisplay, _vaConfigId, width, height, 0, IntPtr.Zero, 0, out _vaContextId); status = vaCreateContext(_vaDisplay, _vaConfigId, width, height, 0, IntPtr.Zero, 0, out _vaContextId);
if (status != VA_STATUS_SUCCESS) if (status != VA_STATUS_SUCCESS)
{ {
Console.WriteLine($"[HardwareVideo] vaCreateContext failed: {GetVaError(status)}"); DiagnosticLog.Error("HardwareVideoService", $"vaCreateContext failed: {GetVaError(status)}");
vaDestroySurfaces(_vaDisplay, _vaSurfaces, _vaSurfaces.Length); vaDestroySurfaces(_vaDisplay, _vaSurfaces, _vaSurfaces.Length);
vaDestroyConfig(_vaDisplay, _vaConfigId); vaDestroyConfig(_vaDisplay, _vaConfigId);
return false; return false;
} }
Console.WriteLine($"[HardwareVideo] Created decoder: {profile} {width}x{height}"); DiagnosticLog.Debug("HardwareVideoService", $"Created decoder: {profile} {width}x{height}");
return true; return true;
} }

View File

@@ -45,14 +45,14 @@ public class IBusInputMethodService : IInputMethodService, IDisposable
_bus = ibus_bus_new(); _bus = ibus_bus_new();
if (_bus == IntPtr.Zero) if (_bus == IntPtr.Zero)
{ {
Console.WriteLine("IBusInputMethodService: Failed to connect to IBus"); DiagnosticLog.Error("IBusInputMethodService", "Failed to connect to IBus");
return; return;
} }
// Check if IBus is connected // Check if IBus is connected
if (!ibus_bus_is_connected(_bus)) if (!ibus_bus_is_connected(_bus))
{ {
Console.WriteLine("IBusInputMethodService: IBus not connected"); DiagnosticLog.Error("IBusInputMethodService", "IBus not connected");
return; return;
} }
@@ -60,7 +60,7 @@ public class IBusInputMethodService : IInputMethodService, IDisposable
_context = ibus_bus_create_input_context(_bus, "maui-linux"); _context = ibus_bus_create_input_context(_bus, "maui-linux");
if (_context == IntPtr.Zero) if (_context == IntPtr.Zero)
{ {
Console.WriteLine("IBusInputMethodService: Failed to create input context"); DiagnosticLog.Error("IBusInputMethodService", "Failed to create input context");
return; return;
} }
@@ -71,11 +71,11 @@ public class IBusInputMethodService : IInputMethodService, IDisposable
// Connect signals // Connect signals
ConnectSignals(); ConnectSignals();
Console.WriteLine("IBusInputMethodService: Initialized successfully"); DiagnosticLog.Debug("IBusInputMethodService", "Initialized successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"IBusInputMethodService: Initialization failed - {ex.Message}"); DiagnosticLog.Error("IBusInputMethodService", $"Initialization failed - {ex.Message}");
} }
} }

View File

@@ -63,33 +63,33 @@ public static class InputMethodServiceFactory
// Try Fcitx5 first if it's the configured IM // Try Fcitx5 first if it's the configured IM
if (imModule?.Contains("fcitx") == true && Fcitx5InputMethodService.IsAvailable()) if (imModule?.Contains("fcitx") == true && Fcitx5InputMethodService.IsAvailable())
{ {
Console.WriteLine("InputMethodServiceFactory: Using Fcitx5"); DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5");
return CreateFcitx5Service(); return CreateFcitx5Service();
} }
// Try IBus (most common on modern Linux) // Try IBus (most common on modern Linux)
if (IsIBusAvailable()) if (IsIBusAvailable())
{ {
Console.WriteLine("InputMethodServiceFactory: Using IBus"); DiagnosticLog.Debug("InputMethodServiceFactory", "Using IBus");
return CreateIBusService(); return CreateIBusService();
} }
// Try Fcitx5 as fallback // Try Fcitx5 as fallback
if (Fcitx5InputMethodService.IsAvailable()) if (Fcitx5InputMethodService.IsAvailable())
{ {
Console.WriteLine("InputMethodServiceFactory: Using Fcitx5"); DiagnosticLog.Debug("InputMethodServiceFactory", "Using Fcitx5");
return CreateFcitx5Service(); return CreateFcitx5Service();
} }
// Fall back to XIM // Fall back to XIM
if (IsXIMAvailable()) if (IsXIMAvailable())
{ {
Console.WriteLine("InputMethodServiceFactory: Using XIM"); DiagnosticLog.Debug("InputMethodServiceFactory", "Using XIM");
return CreateXIMService(); return CreateXIMService();
} }
// No IME available // No IME available
Console.WriteLine("InputMethodServiceFactory: No IME available, using null service"); DiagnosticLog.Warn("InputMethodServiceFactory", "No IME available, using null service");
return new NullInputMethodService(); return new NullInputMethodService();
} }
@@ -101,7 +101,7 @@ public static class InputMethodServiceFactory
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"InputMethodServiceFactory: Failed to create IBus service - {ex.Message}"); DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create IBus service - {ex.Message}");
return new NullInputMethodService(); return new NullInputMethodService();
} }
} }
@@ -114,7 +114,7 @@ public static class InputMethodServiceFactory
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"InputMethodServiceFactory: Failed to create Fcitx5 service - {ex.Message}"); DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create Fcitx5 service - {ex.Message}");
return new NullInputMethodService(); return new NullInputMethodService();
} }
} }
@@ -127,7 +127,7 @@ public static class InputMethodServiceFactory
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"InputMethodServiceFactory: Failed to create XIM service - {ex.Message}"); DiagnosticLog.Error("InputMethodServiceFactory", $"Failed to create XIM service - {ex.Message}");
return new NullInputMethodService(); return new NullInputMethodService();
} }
} }

View File

@@ -19,7 +19,7 @@ public static class MauiIconGenerator
{ {
if (!File.Exists(metaFilePath)) if (!File.Exists(metaFilePath))
{ {
Console.WriteLine("[MauiIconGenerator] Metadata file not found: " + metaFilePath); DiagnosticLog.Error("MauiIconGenerator", "Metadata file not found: " + metaFilePath);
return null; return null;
} }
@@ -48,9 +48,9 @@ public static class MauiIconGenerator
? scaleVal ? scaleVal
: 0.65f; : 0.65f;
Console.WriteLine($"[MauiIconGenerator] Generating {size}x{size} icon"); DiagnosticLog.Debug("MauiIconGenerator", $"Generating {size}x{size} icon");
Console.WriteLine($"[MauiIconGenerator] Color: {color}"); DiagnosticLog.Debug("MauiIconGenerator", $" Color: {color}");
Console.WriteLine($"[MauiIconGenerator] Scale: {scale}"); DiagnosticLog.Debug("MauiIconGenerator", $" Scale: {scale}");
using var surface = SKSurface.Create(new SKImageInfo(size, size, SKColorType.Bgra8888, SKAlphaType.Premul)); using var surface = SKSurface.Create(new SKImageInfo(size, size, SKColorType.Bgra8888, SKAlphaType.Premul));
var canvas = surface.Canvas; var canvas = surface.Canvas;
@@ -82,12 +82,12 @@ public static class MauiIconGenerator
using var fileStream = File.OpenWrite(outputPath); using var fileStream = File.OpenWrite(outputPath);
data.SaveTo(fileStream); data.SaveTo(fileStream);
Console.WriteLine("[MauiIconGenerator] Generated: " + outputPath); DiagnosticLog.Debug("MauiIconGenerator", "Generated: " + outputPath);
return outputPath; return outputPath;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[MauiIconGenerator] Error: " + ex.Message); DiagnosticLog.Error("MauiIconGenerator", "Error: " + ex.Message);
return null; return null;
} }
} }

View File

@@ -106,7 +106,7 @@ public class MonitorService : IDisposable
_display = X11.XOpenDisplay(IntPtr.Zero); _display = X11.XOpenDisplay(IntPtr.Zero);
if (_display == IntPtr.Zero) if (_display == IntPtr.Zero)
{ {
Console.WriteLine("[MonitorService] Failed to open X11 display"); DiagnosticLog.Error("MonitorService", "Failed to open X11 display");
_initialized = true; _initialized = true;
return; return;
} }
@@ -117,26 +117,26 @@ public class MonitorService : IDisposable
// Check if XRandR is available // Check if XRandR is available
if (XRandR.XRRQueryExtension(_display, out _eventBase, out _errorBase) == 0) if (XRandR.XRRQueryExtension(_display, out _eventBase, out _errorBase) == 0)
{ {
Console.WriteLine("[MonitorService] XRandR extension not available"); DiagnosticLog.Warn("MonitorService", "XRandR extension not available");
_initialized = true; _initialized = true;
return; return;
} }
if (XRandR.XRRQueryVersion(_display, out int major, out int minor) == 0) if (XRandR.XRRQueryVersion(_display, out int major, out int minor) == 0)
{ {
Console.WriteLine("[MonitorService] Failed to query XRandR version"); DiagnosticLog.Error("MonitorService", "Failed to query XRandR version");
_initialized = true; _initialized = true;
return; return;
} }
Console.WriteLine($"[MonitorService] XRandR {major}.{minor} available"); DiagnosticLog.Debug("MonitorService", $"XRandR {major}.{minor} available");
RefreshMonitors(); RefreshMonitors();
_initialized = true; _initialized = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[MonitorService] Initialization failed: {ex.Message}"); DiagnosticLog.Error("MonitorService", $"Initialization failed: {ex.Message}");
_initialized = true; _initialized = true;
} }
} }
@@ -157,7 +157,7 @@ public class MonitorService : IDisposable
resources = XRandR.XRRGetScreenResourcesCurrent(_display, _rootWindow); resources = XRandR.XRRGetScreenResourcesCurrent(_display, _rootWindow);
if (resources == IntPtr.Zero) if (resources == IntPtr.Zero)
{ {
Console.WriteLine("[MonitorService] Failed to get screen resources"); DiagnosticLog.Error("MonitorService", "Failed to get screen resources");
return; return;
} }
@@ -252,10 +252,10 @@ public class MonitorService : IDisposable
_monitors = newMonitors; _monitors = newMonitors;
// Log detected monitors // Log detected monitors
Console.WriteLine($"[MonitorService] Detected {_monitors.Count} monitor(s):"); DiagnosticLog.Debug("MonitorService", $"Detected {_monitors.Count} monitor(s):");
foreach (var monitor in _monitors) foreach (var monitor in _monitors)
{ {
Console.WriteLine($" {monitor}"); DiagnosticLog.Debug("MonitorService", $" {monitor}");
} }
// Notify if configuration changed // Notify if configuration changed

View File

@@ -104,7 +104,7 @@ public class NotificationService
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[NotificationService] D-Bus monitor error: {ex.Message}"); DiagnosticLog.Error("NotificationService", $"D-Bus monitor error: {ex.Message}");
} }
} }

View File

@@ -91,7 +91,7 @@ public class PortalFilePickerService : IFilePicker
else else
{ {
// No file picker available // No file picker available
Console.WriteLine("[FilePickerService] No file picker available (install xdg-desktop-portal, zenity, or kdialog)"); DiagnosticLog.Warn("PortalFilePickerService", "No file picker available (install xdg-desktop-portal, zenity, or kdialog)");
return Enumerable.Empty<FileResult>(); return Enumerable.Empty<FileResult>();
} }
} }
@@ -146,7 +146,7 @@ public class PortalFilePickerService : IFilePicker
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[FilePickerService] Portal error: {ex.Message}"); DiagnosticLog.Error("PortalFilePickerService", $"Portal error: {ex.Message}");
// Fall back to zenity/kdialog // Fall back to zenity/kdialog
if (_fallbackTool != null) if (_fallbackTool != null)
{ {
@@ -358,7 +358,7 @@ public class PortalFilePickerService : IFilePicker
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[FilePickerService] Command error: {ex.Message}"); DiagnosticLog.Error("PortalFilePickerService", $"Command error: {ex.Message}");
return ""; return "";
} }
} }

View File

@@ -392,14 +392,14 @@ public class SystemThemeService
if (oldTheme != CurrentTheme) if (oldTheme != CurrentTheme)
{ {
Console.WriteLine($"[SystemThemeService] Theme change detected via polling: {oldTheme} -> {CurrentTheme}"); DiagnosticLog.Debug("SystemThemeService", $"Theme change detected via polling: {oldTheme} -> {CurrentTheme}");
UpdateColors(); UpdateColors();
ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(CurrentTheme)); ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(CurrentTheme));
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[SystemThemeService] Error in poll timer: {ex.Message}"); DiagnosticLog.Error("SystemThemeService", $"Error in poll timer: {ex.Message}");
} }
} }

View File

@@ -44,7 +44,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable
_display = XOpenDisplay(IntPtr.Zero); _display = XOpenDisplay(IntPtr.Zero);
if (_display == IntPtr.Zero) if (_display == IntPtr.Zero)
{ {
Console.WriteLine("X11InputMethodService: Failed to open display"); DiagnosticLog.Error("X11InputMethodService", "Failed to open display");
return; return;
} }
@@ -58,7 +58,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable
_xim = XOpenIM(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); _xim = XOpenIM(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (_xim == IntPtr.Zero) if (_xim == IntPtr.Zero)
{ {
Console.WriteLine("X11InputMethodService: No input method available, trying IBus..."); DiagnosticLog.Warn("X11InputMethodService", "No input method available, trying IBus...");
TryIBusFallback(); TryIBusFallback();
return; return;
} }
@@ -97,7 +97,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable
if (_xic != IntPtr.Zero) if (_xic != IntPtr.Zero)
{ {
Console.WriteLine("X11InputMethodService: Input context created successfully"); DiagnosticLog.Debug("X11InputMethodService", "Input context created successfully");
} }
} }
@@ -153,7 +153,7 @@ public class X11InputMethodService : IInputMethodService, IDisposable
{ {
// Try to connect to IBus via D-Bus // Try to connect to IBus via D-Bus
// This provides a more modern IME interface // This provides a more modern IME interface
Console.WriteLine("X11InputMethodService: IBus fallback not yet implemented"); DiagnosticLog.Warn("X11InputMethodService", "IBus fallback not yet implemented");
} }
public void SetFocus(IInputContext? context) public void SetFocus(IInputContext? context)

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -63,10 +64,10 @@ public static class LinuxDialogService
public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds) public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds)
{ {
Console.WriteLine($"[LinuxDialogService] DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}"); DiagnosticLog.Debug("LinuxDialogService", $"DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}");
foreach (var dialog in _activeDialogs) foreach (var dialog in _activeDialogs)
{ {
Console.WriteLine($"[LinuxDialogService] Drawing dialog: IsVisible={dialog.IsVisible}, Opacity={dialog.Opacity}"); DiagnosticLog.Debug("LinuxDialogService", $"Drawing dialog: IsVisible={dialog.IsVisible}, Opacity={dialog.Opacity}");
dialog.Measure(new Size(bounds.Width, bounds.Height)); dialog.Measure(new Size(bounds.Width, bounds.Height));
dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height)); dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height));
dialog.Draw(canvas); dialog.Draw(canvas);
@@ -89,7 +90,7 @@ public static class LinuxDialogService
public static void ShowContextMenu(SkiaContextMenu menu) public static void ShowContextMenu(SkiaContextMenu menu)
{ {
Console.WriteLine("[LinuxDialogService] ShowContextMenu called"); DiagnosticLog.Debug("LinuxDialogService", "ShowContextMenu called");
_activeContextMenu = menu; _activeContextMenu = menu;
_showPopupCallback?.Invoke(); _showPopupCallback?.Invoke();
_invalidateCallback?.Invoke(); _invalidateCallback?.Invoke();

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Platform.Linux.Interop; using Microsoft.Maui.Platform.Linux.Interop;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -111,7 +112,7 @@ public class LinuxWebView : SkiaView
_gtkWindow = WebKitGtk.gtk_window_new(0); _gtkWindow = WebKitGtk.gtk_window_new(0);
if (_gtkWindow == IntPtr.Zero) if (_gtkWindow == IntPtr.Zero)
{ {
Console.WriteLine("[LinuxWebView] Failed to create GTK window"); DiagnosticLog.Error("LinuxWebView", "Failed to create GTK window");
return; return;
} }
@@ -123,7 +124,7 @@ public class LinuxWebView : SkiaView
_webView = WebKitGtk.webkit_web_view_new(); _webView = WebKitGtk.webkit_web_view_new();
if (_webView == IntPtr.Zero) if (_webView == IntPtr.Zero)
{ {
Console.WriteLine("[LinuxWebView] Failed to create WebKit WebView"); DiagnosticLog.Error("LinuxWebView", "Failed to create WebKit WebView");
WebKitGtk.gtk_widget_destroy(_gtkWindow); WebKitGtk.gtk_widget_destroy(_gtkWindow);
_gtkWindow = IntPtr.Zero; _gtkWindow = IntPtr.Zero;
return; return;
@@ -148,12 +149,12 @@ public class LinuxWebView : SkiaView
WebKitGtk.gtk_container_add(_gtkWindow, _webView); WebKitGtk.gtk_container_add(_gtkWindow, _webView);
_initialized = true; _initialized = true;
Console.WriteLine("[LinuxWebView] WebKitGTK WebView initialized successfully"); DiagnosticLog.Debug("LinuxWebView", "WebKitGTK WebView initialized successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[LinuxWebView] Initialization failed: {ex.Message}"); DiagnosticLog.Error("LinuxWebView", $"Initialization failed: {ex.Message}", ex);
Console.WriteLine($"[LinuxWebView] Make sure WebKitGTK is installed: sudo apt install libwebkit2gtk-4.1-0"); DiagnosticLog.Warn("LinuxWebView", "Make sure WebKitGTK is installed: sudo apt install libwebkit2gtk-4.1-0");
} }
} }

View File

@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -62,7 +63,7 @@ public class SkiaAlertDialog : SkiaView
protected override void OnDraw(SKCanvas canvas, SKRect bounds) protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{ {
var app = Application.Current; var app = Application.Current;
Console.WriteLine($"[SkiaAlertDialog] OnDraw: app={app != null}, UserAppTheme={app?.UserAppTheme}, RequestedTheme={app?.RequestedTheme}, IsDarkMode={SkiaTheme.IsDarkMode}, DialogBg={DialogBackground}"); DiagnosticLog.Debug("SkiaAlertDialog", $"OnDraw: app={app != null}, UserAppTheme={app?.UserAppTheme}, RequestedTheme={app?.RequestedTheme}, IsDarkMode={SkiaTheme.IsDarkMode}, DialogBg={DialogBackground}");
// Draw semi-transparent overlay covering entire screen // Draw semi-transparent overlay covering entire screen
using var overlayPaint = new SKPaint using var overlayPaint = new SKPaint

View File

@@ -6,6 +6,7 @@ using System.Windows.Input;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Rendering; using Microsoft.Maui.Platform.Linux.Rendering;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -1096,7 +1097,7 @@ public class SkiaButton : SkiaView, IButtonController
var result = new Size(Math.Max(width, 44f), Math.Max(height, 36f)); var result = new Size(Math.Max(width, 44f), Math.Max(height, 36f));
if (Text == "Round") if (Text == "Round")
Console.WriteLine($"[SkiaButton.Measure] Text='Round' WReq={WidthRequest} HReq={HeightRequest} width={width:F1} height={height:F1} result={result.Width:F0}x{result.Height:F0}"); DiagnosticLog.Debug("SkiaButton", $"Measure Text='Round' WReq={WidthRequest} HReq={HeightRequest} width={width:F1} height={height:F1} result={result.Width:F0}x{result.Height:F0}");
return result; return result;
} }

View File

@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -460,7 +461,7 @@ public class SkiaCollectionView : SkiaItemsView
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[SkiaCollectionView.DrawItem] EXCEPTION: " + ex.Message + "\n" + ex.StackTrace); DiagnosticLog.Error("SkiaCollectionView", "DrawItem EXCEPTION: " + ex.Message + "\n" + ex.StackTrace, ex);
return; return;
} }
} }

View File

@@ -1087,13 +1087,13 @@ public class SkiaEditor : SkiaView, IInputContext
public override void OnPointerPressed(PointerEventArgs e) public override void OnPointerPressed(PointerEventArgs e)
{ {
Console.WriteLine($"[SkiaEditor] OnPointerPressed: Button={e.Button}, IsEnabled={IsEnabled}"); DiagnosticLog.Debug("SkiaEditor", $"OnPointerPressed: Button={e.Button}, IsEnabled={IsEnabled}");
if (!IsEnabled) return; if (!IsEnabled) return;
// Handle right-click context menu // Handle right-click context menu
if (e.Button == PointerButton.Right) if (e.Button == PointerButton.Right)
{ {
Console.WriteLine("[SkiaEditor] Right-click detected, showing context menu"); DiagnosticLog.Debug("SkiaEditor", "Right-click detected, showing context menu");
ShowContextMenu(e.X, e.Y); ShowContextMenu(e.X, e.Y);
return; return;
} }
@@ -1532,7 +1532,7 @@ public class SkiaEditor : SkiaView, IInputContext
private void ShowContextMenu(float x, float y) private void ShowContextMenu(float x, float y)
{ {
Console.WriteLine($"[SkiaEditor] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); DiagnosticLog.Debug("SkiaEditor", $"ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}");
bool hasSelection = _selectionLength != 0; bool hasSelection = _selectionLength != 0;
bool hasText = !string.IsNullOrEmpty(Text); bool hasText = !string.IsNullOrEmpty(Text);
bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText());

View File

@@ -1644,7 +1644,7 @@ public class SkiaEntry : SkiaView, IInputContext
private void ShowContextMenu(float x, float y) private void ShowContextMenu(float x, float y)
{ {
Console.WriteLine($"[SkiaEntry] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); DiagnosticLog.Debug("SkiaEntry", $"ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}");
bool hasSelection = _selectionLength != 0; bool hasSelection = _selectionLength != 0;
bool hasText = !string.IsNullOrEmpty(Text); bool hasText = !string.IsNullOrEmpty(Text);
bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText());

View File

@@ -11,6 +11,7 @@ using System.Threading.Tasks;
using System.Timers; using System.Timers;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
using Svg.Skia; using Svg.Skia;
@@ -513,7 +514,7 @@ public class SkiaImage : SkiaView
_isSvg = false; _isSvg = false;
_currentFilePath = null; _currentFilePath = null;
_cacheKey = null; _cacheKey = null;
Console.WriteLine($"[SkiaImage] File not found: {filePath}"); DiagnosticLog.Warn("SkiaImage", $"File not found: {filePath}");
ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath))); ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath)));
return; return;
} }
@@ -1210,7 +1211,7 @@ public class SkiaImage : SkiaView
var stream = assembly.GetManifestResourceStream(resourceName); var stream = assembly.GetManifestResourceStream(resourceName);
if (stream != null) if (stream != null)
{ {
Console.WriteLine($"[SkiaImage] Loaded embedded resource: {resourceName}"); DiagnosticLog.Debug("SkiaImage", $"Loaded embedded resource: {resourceName}");
return (stream, requestedExt); return (stream, requestedExt);
} }
} }
@@ -1227,7 +1228,7 @@ public class SkiaImage : SkiaView
var stream = assembly.GetManifestResourceStream(resourceName); var stream = assembly.GetManifestResourceStream(resourceName);
if (stream != null) if (stream != null)
{ {
Console.WriteLine($"[SkiaImage] Loaded SVG as PNG substitute: {resourceName}"); DiagnosticLog.Debug("SkiaImage", $"Loaded SVG as PNG substitute: {resourceName}");
return (stream, ".svg"); return (stream, ".svg");
} }
} }

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
using Svg.Skia; using Svg.Skia;
@@ -422,7 +423,7 @@ public class SkiaImageButton : SkiaView
{ {
_isLoading = true; _isLoading = true;
Invalidate(); Invalidate();
Console.WriteLine("[SkiaImageButton] LoadFromFileAsync: " + filePath); DiagnosticLog.Debug("SkiaImageButton", "LoadFromFileAsync: " + filePath);
try try
{ {
@@ -450,15 +451,15 @@ public class SkiaImageButton : SkiaView
if (File.Exists(path)) if (File.Exists(path))
{ {
foundPath = path; foundPath = path;
Console.WriteLine("[SkiaImageButton] Found file at: " + path); DiagnosticLog.Debug("SkiaImageButton", "Found file at: " + path);
break; break;
} }
} }
if (foundPath == null) if (foundPath == null)
{ {
Console.WriteLine("[SkiaImageButton] File not found: " + filePath); DiagnosticLog.Warn("SkiaImageButton", "File not found: " + filePath);
Console.WriteLine("[SkiaImageButton] Searched paths: " + string.Join(", ", searchPaths)); DiagnosticLog.Debug("SkiaImageButton", "Searched paths: " + string.Join(", ", searchPaths));
_isLoading = false; _isLoading = false;
ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath))); ImageLoadingError?.Invoke(this, new ImageLoadingErrorEventArgs(new FileNotFoundException(filePath)));
return; return;
@@ -498,7 +499,7 @@ public class SkiaImageButton : SkiaView
canvas.Translate(-cullRect.Left, -cullRect.Top); canvas.Translate(-cullRect.Left, -cullRect.Top);
canvas.DrawPicture(svg.Picture); canvas.DrawPicture(svg.Picture);
Bitmap = bitmap; Bitmap = bitmap;
Console.WriteLine($"[SkiaImageButton] Loaded SVG: {foundPath} ({width}x{height}), cullRect={cullRect}"); DiagnosticLog.Debug("SkiaImageButton", $"Loaded SVG: {foundPath} ({width}x{height}), cullRect={cullRect}");
} }
} }
else else
@@ -508,7 +509,7 @@ public class SkiaImageButton : SkiaView
if (bitmap != null) if (bitmap != null)
{ {
Bitmap = bitmap; Bitmap = bitmap;
Console.WriteLine("[SkiaImageButton] Loaded image: " + foundPath); DiagnosticLog.Debug("SkiaImageButton", "Loaded image: " + foundPath);
} }
} }
}); });

View File

@@ -5,6 +5,7 @@ using SkiaSharp;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -114,7 +115,7 @@ public class SkiaItemsView : SkiaView
protected virtual void RefreshItems() protected virtual void RefreshItems()
{ {
Console.WriteLine($"[SkiaItemsView] RefreshItems called, clearing {_items.Count} items and {_itemViewCache.Count} cached views"); DiagnosticLog.Debug("SkiaItemsView", $"RefreshItems called, clearing {_items.Count} items and {_itemViewCache.Count} cached views");
_items.Clear(); _items.Clear();
_itemViewCache.Clear(); // Clear cached views when items change _itemViewCache.Clear(); // Clear cached views when items change
_itemHeights.Clear(); // Clear cached heights _itemHeights.Clear(); // Clear cached heights
@@ -125,7 +126,7 @@ public class SkiaItemsView : SkiaView
_items.Add(item); _items.Add(item);
} }
} }
Console.WriteLine($"[SkiaItemsView] RefreshItems done, now have {_items.Count} items"); DiagnosticLog.Debug("SkiaItemsView", $"RefreshItems done, now have {_items.Count} items");
_scrollOffset = 0; _scrollOffset = 0;
} }
@@ -194,7 +195,7 @@ public class SkiaItemsView : SkiaView
protected override void OnDraw(SKCanvas canvas, SKRect bounds) protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{ {
Console.WriteLine($"[SkiaItemsView] OnDraw - bounds={bounds}, items={_items.Count}, ItemViewCreator={(ItemViewCreator != null ? "set" : "null")}"); DiagnosticLog.Debug("SkiaItemsView", $"OnDraw - bounds={bounds}, items={_items.Count}, ItemViewCreator={(ItemViewCreator != null ? "set" : "null")}");
// Draw background // Draw background
if (BackgroundColor != null && BackgroundColor != Colors.Transparent) if (BackgroundColor != null && BackgroundColor != Colors.Transparent)
@@ -283,7 +284,7 @@ public class SkiaItemsView : SkiaView
// Try to use ItemViewCreator for templated rendering // Try to use ItemViewCreator for templated rendering
if (ItemViewCreator != null) if (ItemViewCreator != null)
{ {
Console.WriteLine($"[SkiaItemsView] DrawItem {index} - ItemViewCreator exists, item: {item}"); DiagnosticLog.Debug("SkiaItemsView", $"DrawItem {index} - ItemViewCreator exists, item: {item}");
// Get or create cached view for this index // Get or create cached view for this index
if (!_itemViewCache.TryGetValue(index, out var itemView) || itemView == null) if (!_itemViewCache.TryGetValue(index, out var itemView) || itemView == null)
{ {
@@ -322,7 +323,7 @@ public class SkiaItemsView : SkiaView
} }
else else
{ {
Console.WriteLine($"[SkiaItemsView] DrawItem {index} - ItemViewCreator is NULL, falling back to ToString"); DiagnosticLog.Debug("SkiaItemsView", $"DrawItem {index} - ItemViewCreator is NULL, falling back to ToString");
} }
// Draw separator // Draw separator
@@ -424,7 +425,7 @@ public class SkiaItemsView : SkiaView
public override void OnPointerPressed(PointerEventArgs e) public override void OnPointerPressed(PointerEventArgs e)
{ {
Console.WriteLine($"[SkiaItemsView] OnPointerPressed - x={e.X}, y={e.Y}, Bounds={Bounds}, ScreenBounds={ScreenBounds}, ItemCount={_items.Count}"); DiagnosticLog.Debug("SkiaItemsView", $"OnPointerPressed - x={e.X}, y={e.Y}, Bounds={Bounds}, ScreenBounds={ScreenBounds}, ItemCount={_items.Count}");
if (!IsEnabled) return; if (!IsEnabled) return;
// Check if clicking on scrollbar thumb // Check if clicking on scrollbar thumb
@@ -537,7 +538,7 @@ public class SkiaItemsView : SkiaView
cumulativeY += itemH + _itemSpacing; cumulativeY += itemH + _itemSpacing;
} }
Console.WriteLine($"[SkiaItemsView] Tap at Y={e.Y}, screenBounds.Top={screenBounds.Top}, scrollOffset={_scrollOffset}, localY={localY}, index={tappedIndex}"); DiagnosticLog.Debug("SkiaItemsView", $"Tap at Y={e.Y}, screenBounds.Top={screenBounds.Top}, scrollOffset={_scrollOffset}, localY={localY}, index={tappedIndex}");
if (tappedIndex >= 0 && tappedIndex < _items.Count) if (tappedIndex >= 0 && tappedIndex < _items.Count)
{ {

View File

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
using Microsoft.Maui; using Microsoft.Maui;
@@ -227,10 +228,10 @@ public abstract class SkiaLayoutView : SkiaView
} }
if (hasCV) if (hasCV)
{ {
Console.WriteLine($"[SkiaStackLayout+CV] OnDraw - bounds={bounds}, children={_children.Count}"); DiagnosticLog.Debug("SkiaLayoutView", $"[SkiaStackLayout+CV] OnDraw - bounds={bounds}, children={_children.Count}");
foreach (var c in _children) foreach (var c in _children)
{ {
Console.WriteLine($"[SkiaStackLayout+CV] Child: {c.GetType().Name}, IsVisible={c.IsVisible}, Bounds={c.Bounds}"); DiagnosticLog.Debug("SkiaLayoutView", $"[SkiaStackLayout+CV] Child: {c.GetType().Name}, IsVisible={c.IsVisible}, Bounds={c.Bounds}");
} }
} }
} }
@@ -959,9 +960,9 @@ public class SkiaGrid : SkiaLayoutView
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[SkiaGrid] EXCEPTION in ArrangeOverride: {ex.GetType().Name}: {ex.Message}"); DiagnosticLog.Error("SkiaGrid", $"EXCEPTION in ArrangeOverride: {ex.GetType().Name}: {ex.Message}", ex);
Console.WriteLine($"[SkiaGrid] Bounds: {bounds}, RowHeights: {_rowHeights.Length}, RowDefs: {_rowDefinitions.Count}, Children: {Children.Count}"); DiagnosticLog.Error("SkiaGrid", $"Bounds: {bounds}, RowHeights: {_rowHeights.Length}, RowDefs: {_rowDefinitions.Count}, Children: {Children.Count}");
Console.WriteLine($"[SkiaGrid] Stack trace: {ex.StackTrace}"); DiagnosticLog.Error("SkiaGrid", $"Stack trace: {ex.StackTrace}");
throw; throw;
} }
} }

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux; using Microsoft.Maui.Platform.Linux;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -123,12 +124,12 @@ public class SkiaNavigationPage : SkiaView
} }
else else
{ {
Console.WriteLine("[SkiaNavigationPage] Push (no animation): setting _currentPage to " + page.Title); DiagnosticLog.Debug("SkiaNavigationPage", "Push (no animation): setting _currentPage to " + page.Title);
_currentPage = page; _currentPage = page;
_currentPage.OnAppearing(); _currentPage.OnAppearing();
Console.WriteLine("[SkiaNavigationPage] Push: calling Invalidate"); DiagnosticLog.Debug("SkiaNavigationPage", "Push: calling Invalidate");
Invalidate(); Invalidate();
Console.WriteLine("[SkiaNavigationPage] Push: Invalidate called, _currentPage is now " + _currentPage?.Title); DiagnosticLog.Debug("SkiaNavigationPage", "Push: Invalidate called, _currentPage is now " + _currentPage?.Title);
} }
Pushed?.Invoke(this, new NavigationEventArgs(page)); Pushed?.Invoke(this, new NavigationEventArgs(page));
@@ -326,7 +327,7 @@ public class SkiaNavigationPage : SkiaView
else if (_currentPage != null) else if (_currentPage != null)
{ {
// Draw current page normally // Draw current page normally
Console.WriteLine("[SkiaNavigationPage] OnDraw: drawing _currentPage=" + _currentPage.Title); DiagnosticLog.Debug("SkiaNavigationPage", "OnDraw: drawing _currentPage=" + _currentPage.Title);
_currentPage.Bounds = new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height); _currentPage.Bounds = new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
_currentPage.Draw(canvas); _currentPage.Draw(canvas);
@@ -375,7 +376,7 @@ public class SkiaNavigationPage : SkiaView
public override void OnPointerPressed(PointerEventArgs e) public override void OnPointerPressed(PointerEventArgs e)
{ {
Console.WriteLine($"[SkiaNavigationPage] OnPointerPressed at ({e.X}, {e.Y}), _isAnimating={_isAnimating}"); DiagnosticLog.Debug("SkiaNavigationPage", $"OnPointerPressed at ({e.X}, {e.Y}), _isAnimating={_isAnimating}");
if (_isAnimating) return; if (_isAnimating) return;
// Check for back button click // Check for back button click
@@ -383,13 +384,13 @@ public class SkiaNavigationPage : SkiaView
{ {
if (e.X < 56 && e.Y < _navigationBarHeight) if (e.X < 56 && e.Y < _navigationBarHeight)
{ {
Console.WriteLine($"[SkiaNavigationPage] Back button clicked"); DiagnosticLog.Debug("SkiaNavigationPage", "Back button clicked");
Pop(); Pop();
return; return;
} }
} }
Console.WriteLine($"[SkiaNavigationPage] Forwarding to _currentPage: {_currentPage?.GetType().Name}"); DiagnosticLog.Debug("SkiaNavigationPage", $"Forwarding to _currentPage: {_currentPage?.GetType().Name}");
_currentPage?.OnPointerPressed(e); _currentPage?.OnPointerPressed(e);
} }
@@ -454,7 +455,7 @@ public class SkiaNavigationPage : SkiaView
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[SkiaNavigationPage] HitTest error: {ex.Message}"); DiagnosticLog.Error("SkiaNavigationPage", $"HitTest error: {ex.Message}", ex);
} }
} }

View File

@@ -3,6 +3,7 @@
using SkiaSharp; using SkiaSharp;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -204,7 +205,7 @@ public class SkiaPage : SkiaView
var availableSize = new Size(adjustedBounds.Width, adjustedBounds.Height); var availableSize = new Size(adjustedBounds.Width, adjustedBounds.Height);
_content.Measure(availableSize); _content.Measure(availableSize);
_content.Arrange(new Rect(adjustedBounds.Left, adjustedBounds.Top, adjustedBounds.Width, adjustedBounds.Height)); _content.Arrange(new Rect(adjustedBounds.Left, adjustedBounds.Top, adjustedBounds.Width, adjustedBounds.Height));
Console.WriteLine($"[SkiaPage] Drawing content: {_content.GetType().Name}, Bounds={_content.Bounds}, IsVisible={_content.IsVisible}"); DiagnosticLog.Debug("SkiaPage", $"Drawing content: {_content.GetType().Name}, Bounds={_content.Bounds}, IsVisible={_content.IsVisible}");
_content.Draw(canvas); _content.Draw(canvas);
} }
@@ -284,7 +285,7 @@ public class SkiaPage : SkiaView
public void OnAppearing() public void OnAppearing()
{ {
Console.WriteLine($"[SkiaPage] OnAppearing called for: {Title}, HasListeners={Appearing != null}"); DiagnosticLog.Debug("SkiaPage", $"OnAppearing called for: {Title}, HasListeners={Appearing != null}");
Appearing?.Invoke(this, EventArgs.Empty); Appearing?.Invoke(this, EventArgs.Empty);
} }
@@ -436,7 +437,7 @@ public class SkiaContentPage : SkiaPage
private void DrawToolbarItems(SKCanvas canvas, SKRect navBarBounds) private void DrawToolbarItems(SKCanvas canvas, SKRect navBarBounds)
{ {
var primaryItems = _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary).ToList(); var primaryItems = _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary).ToList();
Console.WriteLine($"[SkiaContentPage] DrawToolbarItems: {primaryItems.Count} primary items, navBarBounds={navBarBounds}"); DiagnosticLog.Debug("SkiaContentPage", $"DrawToolbarItems: {primaryItems.Count} primary items, navBarBounds={navBarBounds}");
if (primaryItems.Count == 0) return; if (primaryItems.Count == 0) return;
using var font = new SKFont(SKTypeface.Default, 14); using var font = new SKFont(SKTypeface.Default, 14);
@@ -470,7 +471,7 @@ public class SkiaContentPage : SkiaPage
var destRect = new SKRect(iconX, iconY, iconX + iconSize, iconY + iconSize); var destRect = new SKRect(iconX, iconY, iconX + iconSize, iconY + iconSize);
canvas.DrawBitmap(item.Icon, destRect); canvas.DrawBitmap(item.Icon, destRect);
Console.WriteLine($"[SkiaContentPage] Drew toolbar icon '{item.Text}' at ({iconX}, {iconY})"); DiagnosticLog.Debug("SkiaContentPage", $"Drew toolbar icon '{item.Text}' at ({iconX}, {iconY})");
} }
else else
{ {
@@ -490,33 +491,33 @@ public class SkiaContentPage : SkiaPage
canvas.DrawText(item.Text, x, y, textPaint); canvas.DrawText(item.Text, x, y, textPaint);
} }
Console.WriteLine($"[SkiaContentPage] Toolbar item '{item.Text}' HitBounds set to {item.HitBounds}"); DiagnosticLog.Debug("SkiaContentPage", $"Toolbar item '{item.Text}' HitBounds set to {item.HitBounds}");
rightEdge = itemLeft - 8; // Gap between items rightEdge = itemLeft - 8; // Gap between items
} }
} }
public override void OnPointerPressed(PointerEventArgs e) public override void OnPointerPressed(PointerEventArgs e)
{ {
Console.WriteLine($"[SkiaContentPage] OnPointerPressed at ({e.X}, {e.Y}), ShowNavigationBar={ShowNavigationBar}, NavigationBarHeight={NavigationBarHeight}"); DiagnosticLog.Debug("SkiaContentPage", $"OnPointerPressed at ({e.X}, {e.Y}), ShowNavigationBar={ShowNavigationBar}, NavigationBarHeight={NavigationBarHeight}");
Console.WriteLine($"[SkiaContentPage] ToolbarItems count: {_toolbarItems.Count}"); DiagnosticLog.Debug("SkiaContentPage", $"ToolbarItems count: {_toolbarItems.Count}");
// Check toolbar item clicks // Check toolbar item clicks
if (ShowNavigationBar && e.Y < NavigationBarHeight) if (ShowNavigationBar && e.Y < NavigationBarHeight)
{ {
Console.WriteLine($"[SkiaContentPage] In navigation bar area, checking toolbar items"); DiagnosticLog.Debug("SkiaContentPage", "In navigation bar area, checking toolbar items");
foreach (var item in _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary)) foreach (var item in _toolbarItems.Where(t => t.Order == SkiaToolbarItemOrder.Primary))
{ {
var bounds = item.HitBounds; var bounds = item.HitBounds;
var contains = bounds.Contains(e.X, e.Y); var contains = bounds.Contains(e.X, e.Y);
Console.WriteLine($"[SkiaContentPage] Checking item '{item.Text}', HitBounds=({bounds.Left},{bounds.Top},{bounds.Right},{bounds.Bottom}), Click=({e.X},{e.Y}), Contains={contains}, Command={item.Command != null}"); DiagnosticLog.Debug("SkiaContentPage", $"Checking item '{item.Text}', HitBounds=({bounds.Left},{bounds.Top},{bounds.Right},{bounds.Bottom}), Click=({e.X},{e.Y}), Contains={contains}, Command={item.Command != null}");
if (contains) if (contains)
{ {
Console.WriteLine($"[SkiaContentPage] Toolbar item clicked: {item.Text}"); DiagnosticLog.Debug("SkiaContentPage", $"Toolbar item clicked: {item.Text}");
item.Command?.Execute(null); item.Command?.Execute(null);
return; return;
} }
} }
Console.WriteLine($"[SkiaContentPage] No toolbar item hit"); DiagnosticLog.Debug("SkiaContentPage", "No toolbar item hit");
} }
base.OnPointerPressed(e); base.OnPointerPressed(e);

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -428,7 +429,7 @@ public class SkiaScrollView : SkiaView
public override void OnScroll(ScrollEventArgs e) public override void OnScroll(ScrollEventArgs e)
{ {
Console.WriteLine($"[SkiaScrollView] OnScroll - DeltaY={e.DeltaY}, ScrollableHeight={ScrollableHeight}, ContentSize={ContentSize}, Bounds={Bounds}"); DiagnosticLog.Debug("SkiaScrollView", $"OnScroll - DeltaY={e.DeltaY}, ScrollableHeight={ScrollableHeight}, ContentSize={ContentSize}, Bounds={Bounds}");
// Handle mouse wheel scrolling // Handle mouse wheel scrolling
var deltaMultiplier = 40f; // Scroll speed var deltaMultiplier = 40f; // Scroll speed
@@ -438,7 +439,7 @@ public class SkiaScrollView : SkiaView
{ {
var oldScrollY = _scrollY; var oldScrollY = _scrollY;
ScrollY += e.DeltaY * deltaMultiplier; ScrollY += e.DeltaY * deltaMultiplier;
Console.WriteLine($"[SkiaScrollView] ScrollY changed: {oldScrollY} -> {_scrollY}"); DiagnosticLog.Debug("SkiaScrollView", $"ScrollY changed: {oldScrollY} -> {_scrollY}");
if (_scrollY != oldScrollY) if (_scrollY != oldScrollY)
scrolled = true; scrolled = true;
} }
@@ -876,7 +877,7 @@ public class SkiaScrollView : SkiaView
var actualBounds = bounds; var actualBounds = bounds;
if (double.IsInfinity(bounds.Height) || double.IsNaN(bounds.Height)) if (double.IsInfinity(bounds.Height) || double.IsNaN(bounds.Height))
{ {
Console.WriteLine($"[SkiaScrollView] WARNING: Infinite/NaN height, using default viewport={DefaultViewportHeight}"); DiagnosticLog.Warn("SkiaScrollView", $"Infinite/NaN height, using default viewport={DefaultViewportHeight}");
actualBounds = new Rect(bounds.Left, bounds.Top, bounds.Width, DefaultViewportHeight); actualBounds = new Rect(bounds.Left, bounds.Top, bounds.Width, DefaultViewportHeight);
} }

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -481,10 +482,10 @@ public class SkiaShell : SkiaLayoutView
/// </summary> /// </summary>
public void RefreshTheme() public void RefreshTheme()
{ {
Console.WriteLine("[SkiaShell] RefreshTheme called - refreshing all pages"); DiagnosticLog.Debug("SkiaShell", "RefreshTheme called - refreshing all pages");
if (MauiShell != null && ColorRefresher != null) if (MauiShell != null && ColorRefresher != null)
{ {
Console.WriteLine("[SkiaShell] Refreshing shell colors"); DiagnosticLog.Debug("SkiaShell", "Refreshing shell colors");
ColorRefresher(this, MauiShell); ColorRefresher(this, MauiShell);
} }
if (ContentRenderer != null) if (ContentRenderer != null)
@@ -495,7 +496,7 @@ public class SkiaShell : SkiaLayoutView
{ {
if (item.MauiShellContent != null) if (item.MauiShellContent != null)
{ {
Console.WriteLine("[SkiaShell] Re-rendering: " + item.Title); DiagnosticLog.Debug("SkiaShell", "Re-rendering: " + item.Title);
var skiaView = ContentRenderer(item.MauiShellContent); var skiaView = ContentRenderer(item.MauiShellContent);
if (skiaView != null) if (skiaView != null)
{ {
@@ -775,7 +776,7 @@ public class SkiaShell : SkiaLayoutView
protected override Rect ArrangeOverride(Rect bounds) protected override Rect ArrangeOverride(Rect bounds)
{ {
Console.WriteLine($"[SkiaShell] ArrangeOverride - bounds={bounds}"); DiagnosticLog.Debug("SkiaShell", $"ArrangeOverride - bounds={bounds}");
// Arrange current content with padding // Arrange current content with padding
if (_currentContent != null) if (_currentContent != null)
@@ -787,7 +788,7 @@ public class SkiaShell : SkiaLayoutView
contentTop, contentTop,
bounds.Width - ContentPadding * 2, bounds.Width - ContentPadding * 2,
contentBottom - contentTop); contentBottom - contentTop);
Console.WriteLine($"[SkiaShell] Arranging content with bounds={contentBounds}, padding={ContentPadding}"); DiagnosticLog.Debug("SkiaShell", $"Arranging content with bounds={contentBounds}, padding={ContentPadding}");
_currentContent.Arrange(contentBounds); _currentContent.Arrange(contentBounds);
} }

View File

@@ -1682,7 +1682,7 @@ public abstract class SkiaView : BindableObject, IDisposable, IAccessible
public virtual void OnPointerReleased(PointerEventArgs e) public virtual void OnPointerReleased(PointerEventArgs e)
{ {
Console.WriteLine($"[SkiaView] OnPointerReleased on {GetType().Name}, MauiView={MauiView?.GetType().Name ?? "null"}"); DiagnosticLog.Debug("SkiaView", $"OnPointerReleased on {GetType().Name}, MauiView={MauiView?.GetType().Name ?? "null"}");
if (MauiView != null) if (MauiView != null)
{ {
GestureManager.ProcessPointerUp(MauiView, e.X, e.Y); GestureManager.ProcessPointerUp(MauiView, e.X, e.Y);

View File

@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
@@ -507,7 +508,7 @@ public class SkiaWebView : SkiaView
{ {
_mainDisplay = display; _mainDisplay = display;
_mainWindow = window; _mainWindow = window;
Console.WriteLine($"[WebView] Main window set: display={display}, window={window}"); DiagnosticLog.Debug("SkiaWebView", $"Main window set: display={display}, window={window}");
} }
public static void ProcessGtkEvents() public static void ProcessGtkEvents()
@@ -548,14 +549,14 @@ public class SkiaWebView : SkiaView
{ {
_useGtk4 = true; _useGtk4 = true;
_webkitLib = "libwebkitgtk-6.0.so.4"; _webkitLib = "libwebkitgtk-6.0.so.4";
Console.WriteLine("[WebView] Warning: Using GTK4 WebKitGTK - embedding may be limited"); DiagnosticLog.Warn("SkiaWebView", "Using GTK4 WebKitGTK - embedding may be limited");
} }
} }
} }
if (_webkitHandle == IntPtr.Zero) if (_webkitHandle == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] WebKitGTK not found. Install with: sudo apt install libwebkit2gtk-4.1-0"); DiagnosticLog.Error("SkiaWebView", "WebKitGTK not found. Install with: sudo apt install libwebkit2gtk-4.1-0");
return false; return false;
} }
@@ -579,7 +580,7 @@ public class SkiaWebView : SkiaView
_webkitGetUserAgent = LoadFunction<WebKitSettingsGetUserAgentDelegate>("webkit_settings_get_user_agent"); _webkitGetUserAgent = LoadFunction<WebKitSettingsGetUserAgentDelegate>("webkit_settings_get_user_agent");
_webkitRunJavascript = LoadFunction<WebKitWebViewRunJavascriptDelegate>("webkit_web_view_run_javascript"); _webkitRunJavascript = LoadFunction<WebKitWebViewRunJavascriptDelegate>("webkit_web_view_run_javascript");
Console.WriteLine($"[WebView] Using {_webkitLib}"); DiagnosticLog.Debug("SkiaWebView", $"Using {_webkitLib}");
return _webkitWebViewNew != null; return _webkitWebViewNew != null;
} }
@@ -593,7 +594,7 @@ public class SkiaWebView : SkiaView
{ {
string[] events = { "STARTED", "REDIRECTED", "COMMITTED", "FINISHED" }; string[] events = { "STARTED", "REDIRECTED", "COMMITTED", "FINISHED" };
string eventName = loadEvent >= 0 && loadEvent < events.Length ? events[loadEvent] : loadEvent.ToString(); string eventName = loadEvent >= 0 && loadEvent < events.Length ? events[loadEvent] : loadEvent.ToString();
Console.WriteLine($"[WebView] Load event: {eventName}"); DiagnosticLog.Debug("SkiaWebView", $"Load event: {eventName}");
if (!_webViewInstances.TryGetValue(webView, out var instance)) return; if (!_webViewInstances.TryGetValue(webView, out var instance)) return;
@@ -644,11 +645,11 @@ public class SkiaWebView : SkiaView
Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
Environment.SetEnvironmentVariable("LIBGL_ALWAYS_SOFTWARE", "1"); Environment.SetEnvironmentVariable("LIBGL_ALWAYS_SOFTWARE", "1");
Environment.SetEnvironmentVariable("WEBKIT_DISABLE_COMPOSITING_MODE", "1"); Environment.SetEnvironmentVariable("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
Console.WriteLine("[WebView] Using X11 backend with software rendering for proper positioning"); DiagnosticLog.Debug("SkiaWebView", "Using X11 backend with software rendering for proper positioning");
var waylandDisplay = Environment.GetEnvironmentVariable("WAYLAND_DISPLAY"); var waylandDisplay = Environment.GetEnvironmentVariable("WAYLAND_DISPLAY");
Console.WriteLine($"[WebView] XDG_RUNTIME_DIR: {Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR")}"); DiagnosticLog.Debug("SkiaWebView", $"XDG_RUNTIME_DIR: {Environment.GetEnvironmentVariable("XDG_RUNTIME_DIR")}");
Console.WriteLine($"[WebView] Forcing X11: GDK_BACKEND=x11, WAYLAND_DISPLAY={waylandDisplay}, XDG_SESSION_TYPE=x11"); DiagnosticLog.Debug("SkiaWebView", $"Forcing X11: GDK_BACKEND=x11, WAYLAND_DISPLAY={waylandDisplay}, XDG_SESSION_TYPE=x11");
if (_useGtk4) if (_useGtk4)
{ {
@@ -660,19 +661,19 @@ public class SkiaWebView : SkiaView
IntPtr argv = IntPtr.Zero; IntPtr argv = IntPtr.Zero;
if (!gtk3_init_check(ref argc, ref argv)) if (!gtk3_init_check(ref argc, ref argv))
{ {
Console.WriteLine("[WebView] gtk3_init_check failed!"); DiagnosticLog.Error("SkiaWebView", "gtk3_init_check failed!");
} }
} }
_gtkInitialized = true; _gtkInitialized = true;
var gdkDisplay = gdk3_display_get_default(); var gdkDisplay = gdk3_display_get_default();
Console.WriteLine($"[WebView] GDK display: {gdkDisplay}"); DiagnosticLog.Debug("SkiaWebView", $"GDK display: {gdkDisplay}");
} }
_webView = _webkitWebViewNew!(); _webView = _webkitWebViewNew!();
if (_webView == IntPtr.Zero) if (_webView == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Failed to create WebKit view"); DiagnosticLog.Error("SkiaWebView", "Failed to create WebKit view");
return; return;
} }
@@ -680,7 +681,7 @@ public class SkiaWebView : SkiaView
_loadChangedCallback = OnLoadChanged; _loadChangedCallback = OnLoadChanged;
var callbackPtr = Marshal.GetFunctionPointerForDelegate(_loadChangedCallback); var callbackPtr = Marshal.GetFunctionPointerForDelegate(_loadChangedCallback);
g_signal_connect_data(_webView, "load-changed", callbackPtr, IntPtr.Zero, IntPtr.Zero, 0); g_signal_connect_data(_webView, "load-changed", callbackPtr, IntPtr.Zero, IntPtr.Zero, 0);
Console.WriteLine("[WebView] Connected to load-changed signal"); DiagnosticLog.Debug("SkiaWebView", "Connected to load-changed signal");
int width = Math.Max(800, (int)RequestedWidth); int width = Math.Max(800, (int)RequestedWidth);
int height = Math.Max(600, (int)RequestedHeight); int height = Math.Max(600, (int)RequestedHeight);
@@ -691,7 +692,7 @@ public class SkiaWebView : SkiaView
gtk4_window_set_title(_gtkWindow, "OpenMaui WebView"); gtk4_window_set_title(_gtkWindow, "OpenMaui WebView");
gtk4_window_set_default_size(_gtkWindow, width, height); gtk4_window_set_default_size(_gtkWindow, width, height);
gtk4_window_set_child(_gtkWindow, _webView); gtk4_window_set_child(_gtkWindow, _webView);
Console.WriteLine($"[WebView] GTK4 window created: {width}x{height}"); DiagnosticLog.Debug("SkiaWebView", $"GTK4 window created: {width}x{height}");
} }
else else
{ {
@@ -702,7 +703,7 @@ public class SkiaWebView : SkiaView
gtk3_widget_set_vexpand(_webView, true); gtk3_widget_set_vexpand(_webView, true);
gtk3_widget_set_size_request(_webView, width, height); gtk3_widget_set_size_request(_webView, width, height);
gtk3_container_add(_gtkWindow, _webView); gtk3_container_add(_gtkWindow, _webView);
Console.WriteLine($"[WebView] GTK3 TOPLEVEL window created: {width}x{height}"); DiagnosticLog.Debug("SkiaWebView", $"GTK3 TOPLEVEL window created: {width}x{height}");
} }
ConfigureWebKitSettings(); ConfigureWebKitSettings();
@@ -727,11 +728,11 @@ public class SkiaWebView : SkiaView
LoadHtml(_html); LoadHtml(_html);
} }
Console.WriteLine("[WebView] Initialized successfully"); DiagnosticLog.Debug("SkiaWebView", "Initialized successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[WebView] Initialization failed: {ex.Message}"); DiagnosticLog.Error("SkiaWebView", $"Initialization failed: {ex.Message}", ex);
} }
} }
@@ -746,31 +747,31 @@ public class SkiaWebView : SkiaView
var settings = _webkitGetSettings(_webView); var settings = _webkitGetSettings(_webView);
if (settings == IntPtr.Zero) if (settings == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Could not get WebKit settings"); DiagnosticLog.Warn("SkiaWebView", "Could not get WebKit settings");
return; return;
} }
if (_webkitSetHardwareAcceleration != null) if (_webkitSetHardwareAcceleration != null)
{ {
_webkitSetHardwareAcceleration(settings, 2); // NEVER _webkitSetHardwareAcceleration(settings, 2); // NEVER
Console.WriteLine("[WebView] Set hardware acceleration to NEVER (software rendering)"); DiagnosticLog.Debug("SkiaWebView", "Set hardware acceleration to NEVER (software rendering)");
} }
else else
{ {
Console.WriteLine("[WebView] Warning: Could not set hardware acceleration policy"); DiagnosticLog.Warn("SkiaWebView", "Could not set hardware acceleration policy");
} }
if (_webkitSetWebgl != null) if (_webkitSetWebgl != null)
{ {
_webkitSetWebgl(settings, false); _webkitSetWebgl(settings, false);
Console.WriteLine("[WebView] Disabled WebGL"); DiagnosticLog.Debug("SkiaWebView", "Disabled WebGL");
} }
Console.WriteLine("[WebView] WebKit settings configured successfully"); DiagnosticLog.Debug("SkiaWebView", "WebKit settings configured successfully");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[WebView] Failed to configure settings: {ex.Message}"); DiagnosticLog.Error("SkiaWebView", $"Failed to configure settings: {ex.Message}", ex);
} }
} }
@@ -808,28 +809,28 @@ public class SkiaWebView : SkiaView
{ {
Navigating?.Invoke(this, new WebNavigatingEventArgs(url)); Navigating?.Invoke(this, new WebNavigatingEventArgs(url));
_webkitLoadUri(_webView, url); _webkitLoadUri(_webView, url);
Console.WriteLine($"[WebView] URL loaded: {url}"); DiagnosticLog.Debug("SkiaWebView", $"URL loaded: {url}");
ShowNativeWindow(); ShowNativeWindow();
} }
} }
public void LoadHtml(string html, string? baseUrl = null) public void LoadHtml(string html, string? baseUrl = null)
{ {
Console.WriteLine($"[WebView] LoadHtml called, html length: {html?.Length ?? 0}"); DiagnosticLog.Debug("SkiaWebView", $"LoadHtml called, html length: {html?.Length ?? 0}");
if (string.IsNullOrEmpty(html)) if (string.IsNullOrEmpty(html))
{ {
Console.WriteLine("[WebView] Cannot load HTML - html is null or empty"); DiagnosticLog.Warn("SkiaWebView", "Cannot load HTML - html is null or empty");
return; return;
} }
if (!_isInitialized) Initialize(); if (!_isInitialized) Initialize();
if (_webView == IntPtr.Zero || _webkitLoadHtml == null) if (_webView == IntPtr.Zero || _webkitLoadHtml == null)
{ {
Console.WriteLine("[WebView] Cannot load HTML - not initialized or no webkit function"); DiagnosticLog.Warn("SkiaWebView", "Cannot load HTML - not initialized or no webkit function");
return; return;
} }
Console.WriteLine("[WebView] Calling webkit_web_view_load_html..."); DiagnosticLog.Debug("SkiaWebView", "Calling webkit_web_view_load_html...");
_webkitLoadHtml(_webView, html, baseUrl); _webkitLoadHtml(_webView, html, baseUrl);
Console.WriteLine("[WebView] HTML loaded to WebKit"); DiagnosticLog.Debug("SkiaWebView", "HTML loaded to WebKit");
ShowNativeWindow(); ShowNativeWindow();
} }
@@ -919,13 +920,13 @@ public class SkiaWebView : SkiaView
{ {
if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Cannot create X11 container - main window not set"); DiagnosticLog.Warn("SkiaWebView", "Cannot create X11 container - main window not set");
return false; return false;
} }
if (_x11Container != IntPtr.Zero) if (_x11Container != IntPtr.Zero)
{ {
Console.WriteLine("[WebView] X11 container already exists"); DiagnosticLog.Debug("SkiaWebView", "X11 container already exists");
return true; return true;
} }
@@ -939,23 +940,23 @@ public class SkiaWebView : SkiaView
if (width < 100) width = 780; if (width < 100) width = 780;
if (height < 100) height = 300; if (height < 100) height = 300;
Console.WriteLine($"[WebView] Creating X11 container at ({x}, {y}), size ({width}x{height})"); DiagnosticLog.Debug("SkiaWebView", $"Creating X11 container at ({x}, {y}), size ({width}x{height})");
_x11Container = XCreateSimpleWindow(_mainDisplay, _mainWindow, x, y, width, height, 0, 0, 0xFFFFFF); _x11Container = XCreateSimpleWindow(_mainDisplay, _mainWindow, x, y, width, height, 0, 0, 0xFFFFFF);
if (_x11Container == IntPtr.Zero) if (_x11Container == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Failed to create X11 container window"); DiagnosticLog.Error("SkiaWebView", "Failed to create X11 container window");
return false; return false;
} }
Console.WriteLine($"[WebView] Created X11 container: {_x11Container.ToInt64()}"); DiagnosticLog.Debug("SkiaWebView", $"Created X11 container: {_x11Container.ToInt64()}");
XMapWindow(_mainDisplay, _x11Container); XMapWindow(_mainDisplay, _x11Container);
XFlush(_mainDisplay); XFlush(_mainDisplay);
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[WebView] Error creating X11 container: {ex.Message}"); DiagnosticLog.Error("SkiaWebView", $"Error creating X11 container: {ex.Message}", ex);
return false; return false;
} }
} }
@@ -966,7 +967,7 @@ public class SkiaWebView : SkiaView
if (!_isInitialized) Initialize(); if (!_isInitialized) Initialize();
if (_gtkWindow == IntPtr.Zero) return; if (_gtkWindow == IntPtr.Zero) return;
Console.WriteLine("[WebView] Showing native GTK window..."); DiagnosticLog.Debug("SkiaWebView", "Showing native GTK window...");
if (!_useGtk4) if (!_useGtk4)
{ {
@@ -997,14 +998,14 @@ public class SkiaWebView : SkiaView
TryReparentIntoMainWindow(); TryReparentIntoMainWindow();
_isEmbedded = true; _isEmbedded = true;
Console.WriteLine("[WebView] Native window shown"); DiagnosticLog.Debug("SkiaWebView", "Native window shown");
} }
private void TryReparentIntoMainWindow() private void TryReparentIntoMainWindow()
{ {
if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Cannot setup - main window not set"); DiagnosticLog.Warn("SkiaWebView", "Cannot setup - main window not set");
return; return;
} }
@@ -1015,11 +1016,11 @@ public class SkiaWebView : SkiaView
if (_gtkX11Window != IntPtr.Zero) if (_gtkX11Window != IntPtr.Zero)
{ {
_isProperlyReparented = true; _isProperlyReparented = true;
Console.WriteLine($"[WebView] GTK X11 window: {_gtkX11Window} (reparented successfully)"); DiagnosticLog.Debug("SkiaWebView", $"GTK X11 window: {_gtkX11Window} (reparented successfully)");
} }
else else
{ {
Console.WriteLine($"[WebView] GTK X11 window: failed to get XID"); DiagnosticLog.Warn("SkiaWebView", "GTK X11 window: failed to get XID");
} }
} }
@@ -1047,7 +1048,7 @@ public class SkiaWebView : SkiaView
int width = Math.Max(100, (int)Bounds.Width); int width = Math.Max(100, (int)Bounds.Width);
int height = Math.Max(100, (int)Bounds.Height); int height = Math.Max(100, (int)Bounds.Height);
Console.WriteLine($"[WebView] Position: screen=({screenX}, {screenY}), size ({width}x{height}), bounds=({Bounds.Left},{Bounds.Top})"); DiagnosticLog.Debug("SkiaWebView", $"Position: screen=({screenX}, {screenY}), size ({width}x{height}), bounds=({Bounds.Left},{Bounds.Top})");
if (!_useGtk4) if (!_useGtk4)
{ {
@@ -1126,11 +1127,11 @@ public class SkiaWebView : SkiaView
gtk3_window_set_skip_pager_hint(_gtkWindow, true); gtk3_window_set_skip_pager_hint(_gtkWindow, true);
gtk3_window_set_keep_above(_gtkWindow, true); gtk3_window_set_keep_above(_gtkWindow, true);
gtk3_window_set_decorated(_gtkWindow, false); gtk3_window_set_decorated(_gtkWindow, false);
Console.WriteLine("[WebView] Overlay mode enabled with UTILITY hint"); DiagnosticLog.Debug("SkiaWebView", "Overlay mode enabled with UTILITY hint");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[WebView] Failed to enable overlay mode: {ex.Message}"); DiagnosticLog.Error("SkiaWebView", $"Failed to enable overlay mode: {ex.Message}", ex);
} }
} }
@@ -1138,7 +1139,7 @@ public class SkiaWebView : SkiaView
{ {
if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero) if (_mainDisplay == IntPtr.Zero || _mainWindow == IntPtr.Zero)
{ {
Console.WriteLine("[WebView] Cannot setup embedding - main window not set"); DiagnosticLog.Warn("SkiaWebView", "Cannot setup embedding - main window not set");
return; return;
} }
@@ -1148,7 +1149,7 @@ public class SkiaWebView : SkiaView
int width = Math.Max(100, (int)Bounds.Width); int width = Math.Max(100, (int)Bounds.Width);
int height = Math.Max(100, (int)Bounds.Height); int height = Math.Max(100, (int)Bounds.Height);
Console.WriteLine($"[WebView] Initial position: ({screenX}, {screenY}), size ({width}x{height})"); DiagnosticLog.Debug("SkiaWebView", $"Initial position: ({screenX}, {screenY}), size ({width}x{height})");
if (!_useGtk4) if (!_useGtk4)
{ {
@@ -1191,7 +1192,7 @@ public class SkiaWebView : SkiaView
if (mainWindowMoved || Math.Abs(screenX - _lastPosX) > 2 || Math.Abs(screenY - _lastPosY) > 2 || if (mainWindowMoved || Math.Abs(screenX - _lastPosX) > 2 || Math.Abs(screenY - _lastPosY) > 2 ||
Math.Abs(width - _lastWidth) > 2 || Math.Abs(height - _lastHeight) > 2) Math.Abs(width - _lastWidth) > 2 || Math.Abs(height - _lastHeight) > 2)
{ {
Console.WriteLine($"[WebView] Move to ({screenX}, {screenY}), size ({width}x{height}), mainWin=({destX},{destY}), bounds=({Bounds.Left},{Bounds.Top})"); DiagnosticLog.Debug("SkiaWebView", $"Move to ({screenX}, {screenY}), size ({width}x{height}), mainWin=({destX},{destY}), bounds=({Bounds.Left},{Bounds.Top})");
_lastPosX = screenX; _lastPosX = screenX;
_lastPosY = screenY; _lastPosY = screenY;
_lastWidth = width; _lastWidth = width;
@@ -1266,12 +1267,12 @@ public class SkiaWebView : SkiaView
var root = XDefaultRootWindow(display); var root = XDefaultRootWindow(display);
if (XTranslateCoordinates(display, window, root, 0, 0, out x, out y, out _)) if (XTranslateCoordinates(display, window, root, 0, 0, out x, out y, out _))
{ {
Console.WriteLine($"[WebView] Main window at screen ({x}, {y})"); DiagnosticLog.Debug("SkiaWebView", $"Main window at screen ({x}, {y})");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[WebView] Failed to get window position: {ex.Message}"); DiagnosticLog.Error("SkiaWebView", $"Failed to get window position: {ex.Message}", ex);
} }
} }
@@ -1295,13 +1296,13 @@ public class SkiaWebView : SkiaView
if (_isProperlyReparented && _gtkX11Window != IntPtr.Zero) if (_isProperlyReparented && _gtkX11Window != IntPtr.Zero)
{ {
Console.WriteLine($"[WebView] UpdateEmbedded (reparented): ({x}, {y}), size ({width}x{height})"); DiagnosticLog.Debug("SkiaWebView", $"UpdateEmbedded (reparented): ({x}, {y}), size ({width}x{height})");
XMoveResizeWindow(_mainDisplay, _gtkX11Window, x, y, width, height); XMoveResizeWindow(_mainDisplay, _gtkX11Window, x, y, width, height);
XFlush(_mainDisplay); XFlush(_mainDisplay);
} }
else if (_x11Container != IntPtr.Zero) else if (_x11Container != IntPtr.Zero)
{ {
Console.WriteLine($"[WebView] UpdateEmbedded (container): ({x}, {y}), size ({width}x{height})"); DiagnosticLog.Debug("SkiaWebView", $"UpdateEmbedded (container): ({x}, {y}), size ({width}x{height})");
XMoveResizeWindow(_mainDisplay, _x11Container, x, y, width, height); XMoveResizeWindow(_mainDisplay, _x11Container, x, y, width, height);
if (_gtkX11Window != IntPtr.Zero && _isProperlyReparented) if (_gtkX11Window != IntPtr.Zero && _isProperlyReparented)
{ {

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Native; using Microsoft.Maui.Platform.Linux.Native;
using Microsoft.Maui.Platform.Linux.Rendering; using Microsoft.Maui.Platform.Linux.Rendering;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Window; namespace Microsoft.Maui.Platform.Linux.Window;
@@ -163,7 +164,7 @@ public sealed class GtkHostWindow : IDisposable
ConnectSignal(_window, "button-release-event", Marshal.GetFunctionPointerForDelegate(_buttonReleaseHandler)); ConnectSignal(_window, "button-release-event", Marshal.GetFunctionPointerForDelegate(_buttonReleaseHandler));
ConnectSignal(_window, "motion-notify-event", Marshal.GetFunctionPointerForDelegate(_motionHandler)); ConnectSignal(_window, "motion-notify-event", Marshal.GetFunctionPointerForDelegate(_motionHandler));
Console.WriteLine($"[GtkHostWindow] Created GTK window on X11: {width}x{height}"); DiagnosticLog.Debug("GtkHostWindow", $"Created GTK window on X11: {width}x{height}");
} }
private void ConnectSignal(IntPtr widget, string signal, IntPtr handler) private void ConnectSignal(IntPtr widget, string signal, IntPtr handler)
@@ -202,7 +203,7 @@ public sealed class GtkHostWindow : IDisposable
1 => "Left", 1 => "Left",
_ => $"Other({button})", _ => $"Other({button})",
}; };
Console.WriteLine($"[GtkHostWindow] ButtonPress at ({x:F1}, {y:F1}), button={button} ({buttonName})"); DiagnosticLog.Debug("GtkHostWindow", $"ButtonPress at ({x:F1}, {y:F1}), button={button} ({buttonName})");
PointerPressed?.Invoke(this, (x, y, button)); PointerPressed?.Invoke(this, (x, y, button));
_skiaSurface?.RaisePointerPressed(x, y, button); _skiaSurface?.RaisePointerPressed(x, y, button);
return false; return false;
@@ -256,7 +257,7 @@ public sealed class GtkHostWindow : IDisposable
{ {
if (string.IsNullOrEmpty(iconPath) || !File.Exists(iconPath)) if (string.IsNullOrEmpty(iconPath) || !File.Exists(iconPath))
{ {
Console.WriteLine("[GtkHostWindow] Icon file not found: " + iconPath); DiagnosticLog.Warn("GtkHostWindow", "Icon file not found: " + iconPath);
return; return;
} }
try try
@@ -266,12 +267,12 @@ public sealed class GtkHostWindow : IDisposable
{ {
GtkNative.gtk_window_set_icon(_window, pixbuf); GtkNative.gtk_window_set_icon(_window, pixbuf);
GtkNative.g_object_unref(pixbuf); GtkNative.g_object_unref(pixbuf);
Console.WriteLine("[GtkHostWindow] Set window icon: " + iconPath); DiagnosticLog.Debug("GtkHostWindow", "Set window icon: " + iconPath);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[GtkHostWindow] Failed to set icon: " + ex.Message); DiagnosticLog.Error("GtkHostWindow", "Failed to set icon", ex);
} }
} }
@@ -285,7 +286,7 @@ public sealed class GtkHostWindow : IDisposable
GtkNative.gtk_widget_set_size_request(webViewWidget, width, height); GtkNative.gtk_widget_set_size_request(webViewWidget, width, height);
GtkNative.gtk_fixed_put(_webViewLayer, webViewWidget, x, y); GtkNative.gtk_fixed_put(_webViewLayer, webViewWidget, x, y);
GtkNative.gtk_widget_show(webViewWidget); GtkNative.gtk_widget_show(webViewWidget);
Console.WriteLine($"[GtkHostWindow] Added WebView at ({x}, {y}) size {width}x{height}"); DiagnosticLog.Debug("GtkHostWindow", $"Added WebView at ({x}, {y}) size {width}x{height}");
} }
public void MoveResizeWebView(IntPtr webViewWidget, int x, int y, int width, int height) public void MoveResizeWebView(IntPtr webViewWidget, int x, int y, int width, int height)

View File

@@ -3,6 +3,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Input; using Microsoft.Maui.Platform.Linux.Input;
using Microsoft.Maui.Platform.Linux.Services;
namespace Microsoft.Maui.Platform.Linux.Window; namespace Microsoft.Maui.Platform.Linux.Window;
@@ -789,7 +790,7 @@ public class WaylandWindow : IDisposable
// Create shared memory buffer // Create shared memory buffer
CreateShmBuffer(); CreateShmBuffer();
Console.WriteLine($"[Wayland] Window created: {_width}x{_height}"); DiagnosticLog.Debug("WaylandWindow", $"Window created: {_width}x{_height}");
} }
private void CreateShmBuffer() private void CreateShmBuffer()
@@ -910,7 +911,7 @@ public class WaylandWindow : IDisposable
var window = (WaylandWindow)handle.Target!; var window = (WaylandWindow)handle.Target!;
var interfaceName = Marshal.PtrToStringAnsi(iface); var interfaceName = Marshal.PtrToStringAnsi(iface);
Console.WriteLine($"[Wayland] Global: {interfaceName} v{version}"); DiagnosticLog.Debug("WaylandWindow", $"Global: {interfaceName} v{version}");
switch (interfaceName) switch (interfaceName)
{ {

View File

@@ -3,6 +3,7 @@
using Microsoft.Maui.Platform.Linux.Interop; using Microsoft.Maui.Platform.Linux.Interop;
using Microsoft.Maui.Platform.Linux.Input; using Microsoft.Maui.Platform.Linux.Input;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp; using SkiaSharp;
using Svg.Skia; using Svg.Skia;
@@ -226,7 +227,7 @@ public class X11Window : IDisposable
}; };
X11.XSetClassHint(_display, _window, ref classHint); X11.XSetClassHint(_display, _window, ref classHint);
Console.WriteLine($"[X11Window] Set WM_CLASS: {resName}, {resClass}"); DiagnosticLog.Debug("X11Window", $"Set WM_CLASS: {resName}, {resClass}");
} }
finally finally
{ {
@@ -244,10 +245,10 @@ public class X11Window : IDisposable
{ {
if (string.IsNullOrEmpty(iconPath) || !System.IO.File.Exists(iconPath)) if (string.IsNullOrEmpty(iconPath) || !System.IO.File.Exists(iconPath))
{ {
Console.WriteLine("[X11Window] Icon file not found: " + iconPath); DiagnosticLog.Warn("X11Window", "Icon file not found: " + iconPath);
return; return;
} }
Console.WriteLine("[X11Window] SetIcon called: " + iconPath); DiagnosticLog.Debug("X11Window", "SetIcon called: " + iconPath);
try try
{ {
SKBitmap? bitmap = null; SKBitmap? bitmap = null;
@@ -255,7 +256,7 @@ public class X11Window : IDisposable
// Handle SVG icons // Handle SVG icons
if (iconPath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) if (iconPath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine("[X11Window] Loading SVG icon"); DiagnosticLog.Debug("X11Window", "Loading SVG icon");
using var svg = new SKSvg(); using var svg = new SKSvg();
svg.Load(iconPath); svg.Load(iconPath);
if (svg.Picture != null) if (svg.Picture != null)
@@ -273,16 +274,16 @@ public class X11Window : IDisposable
} }
else else
{ {
Console.WriteLine("[X11Window] Loading raster icon"); DiagnosticLog.Debug("X11Window", "Loading raster icon");
bitmap = SKBitmap.Decode(iconPath); bitmap = SKBitmap.Decode(iconPath);
} }
if (bitmap == null) if (bitmap == null)
{ {
Console.WriteLine("[X11Window] Failed to load icon: " + iconPath); DiagnosticLog.Warn("X11Window", "Failed to load icon: " + iconPath);
return; return;
} }
Console.WriteLine($"[X11Window] Loaded bitmap: {bitmap.Width}x{bitmap.Height}"); DiagnosticLog.Debug("X11Window", $"Loaded bitmap: {bitmap.Width}x{bitmap.Height}");
// Scale to 64x64 if needed // Scale to 64x64 if needed
int targetSize = 64; int targetSize = 64;
@@ -318,11 +319,11 @@ public class X11Window : IDisposable
X11.XChangeProperty(_display, _window, property, type, 32, 0, (nint)data, dataSize); X11.XChangeProperty(_display, _window, property, type, 32, 0, (nint)data, dataSize);
} }
X11.XFlush(_display); X11.XFlush(_display);
Console.WriteLine($"[X11Window] Set window icon: {width}x{height}"); DiagnosticLog.Debug("X11Window", $"Set window icon: {width}x{height}");
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[X11Window] Failed to set icon: " + ex.Message); DiagnosticLog.Error("X11Window", "Failed to set icon", ex);
} }
} }
@@ -450,7 +451,7 @@ public class X11Window : IDisposable
{ {
if (_eventCounter % 100 == 0) if (_eventCounter % 100 == 0)
{ {
Console.WriteLine($"[X11Window] ProcessEvents: {pending} pending events"); DiagnosticLog.Debug("X11Window", $"ProcessEvents: {pending} pending events");
} }
_eventCounter++; _eventCounter++;
while (X11.XPending(_display) > 0) while (X11.XPending(_display) > 0)