MewUI Documentation

repository·main·Indexed 20 days ago

https://github.com/aprillz/mewui

A cross-platform, lightweight, code-first .NET GUI framework designed for NativeAOT and trimming-friendly desktop applications. Includes MewCharts for data visualization with Cartesian, Pie, and Polar charts, and MewDock for managing complex docking layouts with support for document panes, tool panes, and FlexLayout-compatible JSON persistence.

Tokens
86.1K
Snippets
207
Records
360
Agent score
67%

What's inside MewUI

  1. Overview of Aprillz.MewUI.Svg

    main

    The Aprillz.MewUI.Svg extension provides SVG rendering for MewUI via the IGraphicsContext.

    Key features:

    • Pure managed: Does not depend on System.Drawing.
    • Backend Agnostic: Built on SVG.NET with a MewUI rendering backend, making it compatible with all MewUI backends (Direct2D, GDI, MewVG/OpenGL).
    • AOT/Trim Friendly: Designed to be compatible with NativeAOT and trimming.

    Dependencies:

    • Aprillz.MewUI
    • ExCSS
    • Bundles SVG.NET (Ms-PL license)
  2. Overview of Aprillz.MewUI.Analyzers

    main

    Aprillz.MewUI.Analyzers provides Roslyn analyzers and refactorings specifically designed for MewUI fluent markup. It is distributed as a NuGet analyzer (analyzers/dotnet/cs), making it compatible with Visual Studio, VS Code (C# Dev Kit), Rider, and CI environments.

    Key Characteristics:

    • Build-time only: The analyzers do not add any code to your runtime or NativeAOT output.
    • Automatic Discovery: The analyzer uses the existing extension methods in your project as the source of truth for fluent setters.
    • Status: Early experimental version (as of 2026-06-15). Diagnostic IDs and behaviors may change.

    Supported Diagnostic IDs:

    • MEW1101: Convert object initializer to fluent chain.
    • MEW1102: Fluent chain expand / collapse.
    • MEW1103: Merge statements into a fluent chain.
    • MEW1104: Property assignment to fluent call.
  3. Overview of the Aprillz.MewUI.Skia package family

    main

    The Skia integration is split into several packages to allow for modularity and platform-specific optimizations:

    • Aprillz.MewUI.Skia: The core package containing SkiaCanvasView, the ISkiaInteropProvider registry, and the default CPU upload fallback.
    • Aprillz.MewUI.Skia.Interop.*: Specialized packages providing zero-copy GPU bridges for specific backends.
    • Aprillz.MewUI.Skia.{Windows,Linux,MacOS,All}: Metapackages that bundle the core package with all relevant interop packages for a specific platform.
  4. How Animations interact with the Property System

    main

    Animations in MewUI do not exist in a separate layer; instead, they use a wrapper mechanism.

    When an animation starts, the property value is wrapped in an AnimatedEntry which preserves the original base value and its source. Every frame, the interpolated value is updated via an internal SetAnimatedValue call.

    Key Behaviors:

    • Completion: When the animation finishes, ClearAnimatedValue is called, restoring the preserved base value and source.
    • Interruption: If SetValue is called on the property from any source while an animation is running, the animation is immediately stopped and the new value is stored.
    • Priority: From a value resolution perspective, Animated values have a lower priority than Local values, but a higher priority than Trigger or Style values.
  5. Use platform-abstracted dialogs and services

    main

    MewUI abstracts window management and message loops across Windows, Linux/X11, and macOS. This abstraction extends to prompts and file/folder services.

    Message Boxes

    • Managed MessageBox: The recommended choice for cross-platform prompts. It supports both synchronous and asynchronous modes.
    • NativeMessageBox: Use this if you specifically require the OS-provided prompt. It falls back to the managed version if native integration is unavailable.

    File and Folder Dialogs

    MewUI provides cross-platform managed dialogs. By default, they attempt to use native integration (PreferNative = true) and fall back to managed dialogs if native integration fails or is unavailable.

    Native Integration Behavior:

    • Windows: Uses Win32 file/folder dialogs.
    • macOS: Uses AppKit file/folder dialogs.
    • Linux/X11: Uses XDG Desktop Portal. If the portal is missing or fails, it falls back to managed dialogs.

    To force the use of managed dialogs immediately, set PreferNative = false.

  6. Navigate nested sources with BindingPath

    main

    A BindingPath<TRoot, TValue> allows you to describe a reusable path to a nested property without using strings or reflection. It is built using a chain of .Then() calls.

    How BindingPath works

    • From<TRoot>(): Starts the path definition.
    • .Then(...): Navigates to the next segment. The behavior of Then depends on the argument:
      • Func<TCurrent, TNext>: A simple getter. Does not observe changes.
      • Func<TCurrent, ObservableValue<TNext>>: Returns an observable. Observes changes.
      • MewProperty<TNext>: A property on a MewObject. Observes changes.

    Null Handling and Fallbacks

    • If an intermediate segment in the path is null, the path becomes unavailable and the control uses the provided fallbackValue.
    • When an intermediate segment becomes non-null again, the path automatically reconnects.
    • A null final leaf is treated as a valid source value and does not trigger the fallback.
    • Note: When using nullable intermediates, use the null-forgiving operator (!) in your Then calls (e.g., customer!.City) to satisfy the compiler; BindingPath performs its own runtime null checks.
    sealed class OrderViewModel
    {
        public ObservableValue<CustomerViewModel?> Customer { get; } = new();
    }
    
    sealed class CustomerViewModel
    {
        public ObservableValue<string> City { get; } = new();
    }
    
    // Define the reusable path
    static readonly BindingPath<OrderViewModel, string> CityPath = BindingPath
        .From<OrderViewModel>()
        .Then(static order => order.Customer)
        .Then(static customer => customer!.City);
    
    // Use the path in a binding
    var city = new TextBlock().Bind(
        TextBlock.TextProperty,
        orderInstance,
        CityPath,
        mode: BindingMode.OneWay,
        fallbackValue: "-");
  7. Coordinate Space: Using Window-Absolute Bounds

    main

    In MewUI, Element.Bounds is expressed in window-absolute coordinates, not relative to the parent.

    When building custom panels, you must arrange children by offsetting from your own absolute Bounds. For example: child.Arrange(new Rect(Bounds.X + offsetX, Bounds.Y + offsetY, ...)).

    Accessing Parent-Relative Coordinates: If you need coordinates relative to a parent, do not use Bounds. Instead:

    • Use RenderSize to get the element's size without its origin.
    • Use TranslatePoint, TranslateRect, TransformToAncestor, or TransformToDescendant to convert between different coordinate spaces.
    // Incorrect: assuming Bounds is local
    // child.Arrange(new Rect(0, 0, width, height)); 
    
    // Correct: offsetting from window-absolute Bounds
    child.Arrange(new Rect(Bounds.X + offsetX, Bounds.Y + offsetY, childWidth, childHeight));
  8. How animation integration works with the property system

    main

    Animations in MewUI do not create a separate storage tier. Instead, they use a wrapper mechanism called AnimatedEntry.

    Lifecycle of an Animation

    1. Start: When an animation begins, the current value and its source are wrapped in an AnimatedEntry to preserve the base state.
    2. Update: Each frame updates the interpolated value via internal calls to SetAnimatedValue(propertyId, value).
    3. Completion: Once the animation finishes, ClearAnimatedValue(propertyId) is called, which restores the original preserved base value and source.
    4. Interruption: If a SetValue call is received from any source while an animation is currently running, the animation is immediately stopped, and the new value is stored.

    Value Resolution Priority

    In the hierarchy of value resolution, Animated values resolve below Local values but above Trigger or Style values.

  9. Arrange Internal Layout with ArrangeContent

    main

    The ArrangeContent phase is where you determine the final positions of child elements within the space allocated to the control.

    Key Concepts

    • Child Rects: For controls with children, use this phase to calculate child rectangles and call Arrange on them.
    • Bounds vs DesiredSize: You must assume that the DesiredSize calculated during Measure may differ from the actual Bounds assigned to the control. Always layout children based on the actual Bounds received.
  10. The Change Notification Pipeline

    main

    When a property value is set, the following pipeline is executed in order:

    1. Priority Check: Reject if a higher-priority source already owns the value.
    2. Coerce: Apply coerce callback (skipped for null).
    3. Equality Check: If the coerced value equals the current value, stop here (skips notification).
    4. Validate: Run validate callback (pre-commit veto; throwing rejects the change).
    5. Animation Stop: Stop any running animation for this property.
    6. Storage: Store the value, then trigger:
      • OnMewPropertyChanged(property) (virtual handler).
      • Automatic Invalidation: Based on flags (AffectsLayout $\rightarrow$ InvalidateMeasure(), etc.).
      • Inheritance Propagation: If Inherits is set, propagate to descendants.
      • Property Forwards: Notify weak-reference targets.
      • Binding Callbacks: Synchronize ObservableValue.
      • Changed Callback: Execute the changed callback registered via Register().
  11. How C# Markup works in MewUI

    main

    MewUI's C# Markup is a Fluent API that allows you to declare UI using pure C# code instead of XAML. It is designed for high performance and modern deployment scenarios:

    • Native AOT Compatible: Everything is determined at compile time without using Reflection.
    • Type Safety: Errors are caught by the compiler.
    • IntelliSense: Full IDE auto-completion support.
    • Code Reusability: You can extract UI components into standard C# methods.

    All extension methods return this, enabling a method-chaining pattern.

    new Button()
        .Content("Click Me")
        .Width(100)
        .OnClick(() => Console.WriteLine("Clicked!"))
  12. Implement the CanExecute pattern for Buttons

    main

    You can control the enabled/disabled state of a Button based on a condition using OnCanClick. This is similar to the WPF ICommand.CanExecute pattern.

    Automatic Re-evaluation: CanClick is automatically re-evaluated during:

    • Focus changes
    • MouseUp events
    • KeyUp events

    Manual Re-evaluation: If your state changes outside of these events (e.g., in a background task or custom logic), call window.RequerySuggested() to force the UI to re-evaluate the CanClick condition.

    var text = new ObservableValue<string>("");
    
    new TextBox()
        .BindText(text)
        .OnTextChanged(_ => window.RequerySuggested()),
    
    new Button()
        .Content("Submit")
        .OnCanClick(() => !string.IsNullOrWhiteSpace(text.Value))
        .OnClick(() => Submit(text.Value))