diff --git a/Handlers/GtkWebViewProxy.cs b/Handlers/GtkWebViewProxy.cs index 8a9dcf6..ace5afd 100644 --- a/Handlers/GtkWebViewProxy.cs +++ b/Handlers/GtkWebViewProxy.cs @@ -27,22 +27,9 @@ public class GtkWebViewProxy : SkiaView public override void Arrange(SKRect bounds) { base.Arrange(bounds); - var windowBounds = TransformToWindow(bounds); - _handler.RegisterWithHost(windowBounds); - } - - private SKRect TransformToWindow(SKRect localBounds) - { - float x = localBounds.Left; - float y = localBounds.Top; - - for (var parent = Parent; parent != null; parent = parent.Parent) - { - x += parent.Bounds.Left; - y += parent.Bounds.Top; - } - - return new SKRect(x, y, x + localBounds.Width, y + localBounds.Height); + // Bounds are already in absolute window coordinates - use them directly + // The Skia layout system uses absolute coordinates throughout + _handler.RegisterWithHost(Bounds); } public override void Draw(SKCanvas canvas) diff --git a/Handlers/ImageButtonHandler.cs b/Handlers/ImageButtonHandler.cs index f5991f5..68ad2da 100644 --- a/Handlers/ImageButtonHandler.cs +++ b/Handlers/ImageButtonHandler.cs @@ -25,6 +25,8 @@ public partial class ImageButtonHandler : ViewHandler CommandMapper = new(ViewHandler.ViewCommandMapper) @@ -165,6 +167,28 @@ public partial class ImageButtonHandler : ViewHandler 0) + { + handler.PlatformView.WidthRequest = imgBtn.WidthRequest; + } + } + + public static void MapHeight(ImageButtonHandler handler, IImageButton imageButton) + { + if (handler.PlatformView is null) return; + + // Map HeightRequest from the MAUI ImageButton to the platform view + if (imageButton is Microsoft.Maui.Controls.ImageButton imgBtn && imgBtn.HeightRequest > 0) + { + handler.PlatformView.HeightRequest = imgBtn.HeightRequest; + } + } + // Image source loading helper private ImageSourceServiceResultManager _sourceLoader = null!; diff --git a/LinuxApplication.cs b/LinuxApplication.cs index 30a0fd7..95f4912 100644 --- a/LinuxApplication.cs +++ b/LinuxApplication.cs @@ -219,6 +219,21 @@ public class LinuxApplication : IDisposable /// Optional configuration action. public static void Run(MauiApp app, string[] args, Action? configure) { + // Force X11 backend for GTK/WebKitGTK - MUST be set before any GTK code runs + Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); + + // Pre-initialize GTK for WebView compatibility (even when using X11 mode) + int argc = 0; + IntPtr argv = IntPtr.Zero; + if (!GtkNative.gtk_init_check(ref argc, ref argv)) + { + Console.WriteLine("[LinuxApplication] Warning: GTK initialization failed - WebView may not work"); + } + else + { + Console.WriteLine("[LinuxApplication] GTK pre-initialized for WebView support"); + } + // Initialize dispatcher LinuxDispatcher.Initialize(); DispatcherProvider.SetCurrent(LinuxDispatcherProvider.Instance); diff --git a/Views/SkiaImage.cs b/Views/SkiaImage.cs index 101b957..b2f1d47 100644 --- a/Views/SkiaImage.cs +++ b/Views/SkiaImage.cs @@ -433,6 +433,51 @@ public class SkiaImage : SkiaView } } + protected override SKRect ArrangeOverride(SKRect bounds) + { + // If we have explicit size requests, constrain to desired size + // This follows MAUI standard behavior - controls respect WidthRequest/HeightRequest + var desiredWidth = DesiredSize.Width; + var desiredHeight = DesiredSize.Height; + + // If desired size is smaller than available bounds, align within bounds + if (desiredWidth > 0 && desiredHeight > 0 && + (desiredWidth < bounds.Width || desiredHeight < bounds.Height)) + { + float finalWidth = Math.Min(desiredWidth, bounds.Width); + float finalHeight = Math.Min(desiredHeight, bounds.Height); + + // Calculate position based on HorizontalOptions + // LayoutAlignment: Start=0, Center=1, End=2, Fill=3 + float x = bounds.Left; + var hAlignValue = (int)HorizontalOptions.Alignment; + if (hAlignValue == 1) // Center + { + x = bounds.Left + (bounds.Width - finalWidth) / 2; + } + else if (hAlignValue == 2) // End + { + x = bounds.Right - finalWidth; + } + + // Calculate position based on VerticalOptions + float y = bounds.Top; + var vAlignValue = (int)VerticalOptions.Alignment; + if (vAlignValue == 1) // Center + { + y = bounds.Top + (bounds.Height - finalHeight) / 2; + } + else if (vAlignValue == 2) // End + { + y = bounds.Bottom - finalHeight; + } + + return new SKRect(x, y, x + finalWidth, y + finalHeight); + } + + return bounds; + } + protected override SKSize MeasureOverride(SKSize availableSize) { double widthRequest = base.WidthRequest; diff --git a/Views/SkiaImageButton.cs b/Views/SkiaImageButton.cs index bacfcb4..5f2d0ec 100644 --- a/Views/SkiaImageButton.cs +++ b/Views/SkiaImageButton.cs @@ -269,12 +269,14 @@ public class SkiaImageButton : SkiaView bool hasWidth = WidthRequest > 0; bool hasHeight = HeightRequest > 0; + // Default to 24x24 for icons when no size specified + const float DefaultIconSize = 24f; float targetWidth = hasWidth ? (float)(WidthRequest - PaddingLeft - PaddingRight) - : cullRect.Width; + : DefaultIconSize; float targetHeight = hasHeight ? (float)(HeightRequest - PaddingTop - PaddingBottom) - : cullRect.Height; + : DefaultIconSize; float scale = Math.Min(targetWidth / cullRect.Width, targetHeight / cullRect.Height); int width = Math.Max(1, (int)(cullRect.Width * scale)); @@ -472,6 +474,29 @@ public class SkiaImageButton : SkiaView protected override SKSize MeasureOverride(SKSize availableSize) { + // Respect explicit WidthRequest/HeightRequest first (MAUI standard behavior) + if (WidthRequest > 0 && HeightRequest > 0) + { + return new SKSize((float)WidthRequest, (float)HeightRequest); + } + if (WidthRequest > 0) + { + // Fixed width, calculate height from aspect ratio or use width + float height = HeightRequest > 0 ? (float)HeightRequest + : _image != null ? (float)WidthRequest * _image.Height / _image.Width + : (float)WidthRequest; + return new SKSize((float)WidthRequest, height); + } + if (HeightRequest > 0) + { + // Fixed height, calculate width from aspect ratio or use height + float width = WidthRequest > 0 ? (float)WidthRequest + : _image != null ? (float)HeightRequest * _image.Width / _image.Height + : (float)HeightRequest; + return new SKSize(width, (float)HeightRequest); + } + + // No explicit size - calculate from content var padding = new SKSize(PaddingLeft + PaddingRight, PaddingTop + PaddingBottom); if (_image == null) @@ -504,6 +529,53 @@ public class SkiaImageButton : SkiaView return new SKSize(imageWidth + padding.Width, imageHeight + padding.Height); } + protected override SKRect ArrangeOverride(SKRect bounds) + { + // If we have explicit size requests, constrain to desired size + // This follows MAUI standard behavior - controls respect WidthRequest/HeightRequest + var desiredWidth = DesiredSize.Width; + var desiredHeight = DesiredSize.Height; + + // If desired size is smaller than available bounds, align within bounds + if (desiredWidth > 0 && desiredHeight > 0 && + (desiredWidth < bounds.Width || desiredHeight < bounds.Height)) + { + float finalWidth = Math.Min(desiredWidth, bounds.Width); + float finalHeight = Math.Min(desiredHeight, bounds.Height); + + // Calculate position based on HorizontalOptions + // LayoutAlignment: Start=0, Center=1, End=2, Fill=3 + float x = bounds.Left; + var hAlignValue = (int)HorizontalOptions.Alignment; + if (hAlignValue == 1) // Center + { + x = bounds.Left + (bounds.Width - finalWidth) / 2; + } + else if (hAlignValue == 2) // End + { + x = bounds.Right - finalWidth; + } + // Fill (3) and Start (0) both use x = bounds.Left + + // Calculate position based on VerticalOptions + float y = bounds.Top; + var vAlignValue = (int)VerticalOptions.Alignment; + if (vAlignValue == 1) // Center + { + y = bounds.Top + (bounds.Height - finalHeight) / 2; + } + else if (vAlignValue == 2) // End + { + y = bounds.Bottom - finalHeight; + } + // Fill (3) and Start (0) both use y = bounds.Top + + return new SKRect(x, y, x + finalWidth, y + finalHeight); + } + + return bounds; + } + protected override void Dispose(bool disposing) { if (disposing)