Files
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

23 KiB

Cross-Platform UI Considerations

**Referenced Files in This Document** - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) - [App.axaml.cs](file://ClassicAssist.Avalonia/App.axaml.cs) - [Program.cs](file://ClassicAssist.Avalonia/Program.cs) - [MainWindow.axaml.cs](file://ClassicAssist.Avalonia/Views/MainWindow.axaml.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)

Table of Contents

  1. Introduction
  2. Project Structure
  3. Core Components
  4. Architecture Overview
  5. Detailed Component Analysis
  6. Platform-Specific Behaviors
  7. Dispatcher System and Thread Marshaling
  8. Keyboard Handling Differences
  9. File System Access Patterns
  10. Native Integration Points
  11. Testing Strategies
  12. Debugging Techniques
  13. Best Practices for Platform-Agnostic Code
  14. Conclusion

Introduction

This document provides comprehensive guidance for developing cross-platform user interfaces in ClassicAssist.Avalonia, focusing on platform-specific behaviors, rendering differences, and compatibility considerations across Windows, macOS, and Linux platforms. It covers the dispatcher system for thread marshaling, UI invoker patterns, platform abstraction layers, keyboard handling differences, file system access patterns, and native integration points.

The ClassicAssist.Avalonia project implements a sophisticated cross-platform architecture that abstracts platform-specific details while maintaining optimal performance and native feel across different operating systems. Understanding these patterns is crucial for developers working on UI components that need to function consistently across all supported platforms.

Project Structure

The ClassicAssist.Avalonia project follows a modular architecture that separates platform-specific implementations from shared logic:

graph TB
subgraph "Shared Layer"
Shared["ClassicAssist.Shared"]
IDispatcher["IDispatcher Interface"]
IUIInvoker["IUIInvoker Interface"]
PlatformDetector["PlatformDetector"]
end
subgraph "Avalonia Implementation"
Avalonia["ClassicAssist.Avalonia"]
AvaloniaDispatcher["AvaloniaDispatcher"]
AvaloniaUIInvoker["AvaloniaUIInvoker"]
App["App.axaml.cs"]
Program["Program.cs"]
end
subgraph "WPF Legacy"
WPF["ClassicAssist"]
WPFDispatcher["WPFDispatcher"]
WPFUIInvoker["WPFUIInvoker"]
end
Shared --> Avalonia
Shared --> WPF
IDispatcher --> AvaloniaDispatcher
IUIInvoker --> AvaloniaUIInvoker
PlatformDetector --> Avalonia
App --> AvaloniaDispatcher
Program --> App

Diagram sources

Section sources

Core Components

The cross-platform UI architecture is built around several core components that provide abstraction over platform-specific implementations:

Dispatcher Abstraction

The IDispatcher interface defines a contract for thread marshaling operations, allowing UI updates to be safely executed on the appropriate thread regardless of the underlying platform.

UI Invoker Pattern

The IUIInvoker interface provides a consistent API for invoking UI operations across different platforms, abstracting away the complexities of platform-specific threading models.

Platform Detection

The PlatformDetector class provides utilities for detecting the current operating system and making platform-specific decisions at runtime.

Section sources

Architecture Overview

The ClassicAssist.Avalonia architecture implements a layered approach to cross-platform UI development:

sequenceDiagram
participant ViewModel as "ViewModel"
participant Dispatcher as "IDispatcher"
participant AvaloniaDisp as "AvaloniaDispatcher"
participant UIThread as "UI Thread"
ViewModel->>Dispatcher : InvokeOnUiThread(action)
Dispatcher->>AvaloniaDisp : Dispatch action
AvaloniaDisp->>UIThread : Marshal to UI thread
UIThread-->>AvaloniaDisp : Execute action
AvaloniaDisp-->>Dispatcher : Complete
Dispatcher-->>ViewModel : Action completed

Diagram sources

The architecture ensures that UI updates are always performed on the correct thread while providing a consistent API across different platforms. This design pattern prevents common threading issues and ensures smooth operation across Windows, macOS, and Linux environments.

Detailed Component Analysis

Dispatcher System Implementation

The dispatcher system provides thread-safe UI updates through a well-defined abstraction layer:

classDiagram
class IDispatcher {
<<interface>>
+InvokeOnUiThread(action) void
+BeginInvokeOnUiThread(action) void
+IsOnUIThread() bool
}
class AvaloniaDispatcher {
-_dispatcher Avalonia.Threading.Dispatcher
+InvokeOnUiThread(action) void
+BeginInvokeOnUiThread(action) void
+IsOnUIThread() bool
-EnsureDispatcherInitialized() void
}
class WPFDispatcher {
-_dispatcher System.Windows.Threading.Dispatcher
+InvokeOnUiThread(action) void
+BeginInvokeOnUiThread(action) void
+IsOnUIThread() bool
}
IDispatcher <|.. AvaloniaDispatcher
IDispatcher <|.. WPFDispatcher
AvaloniaDispatcher --> Avalonia.Threading.Dispatcher : "uses"
WPFDispatcher --> System.Windows.Threading.Dispatcher : "uses"

Diagram sources

The dispatcher implementation handles platform-specific threading concerns while providing a unified interface for UI thread marshaling. This approach allows the same business logic to work seamlessly across different UI frameworks.

UI Invoker Pattern

The UI invoker pattern provides a higher-level abstraction for common UI operations:

flowchart TD
Start([UI Operation Request]) --> CheckThread{"On UI Thread?"}
CheckThread --> |Yes| ExecuteDirect["Execute Directly"]
CheckThread --> |No| UseDispatcher["Use Dispatcher"]
UseDispatcher --> MarshalToUI["Marshal to UI Thread"]
MarshalToUI --> ExecuteOnUI["Execute on UI Thread"]
ExecuteDirect --> Complete([Operation Complete])
ExecuteOnUI --> Complete

Diagram sources

This pattern ensures that UI operations are always executed on the appropriate thread, preventing race conditions and ensuring thread safety across all platforms.

Section sources

Platform-Specific Behaviors

Window Management Differences

Each platform has distinct window management characteristics that affect how windows are created, displayed, and managed:

Windows Platform

  • Uses Win32 API for advanced window features
  • Supports hardware acceleration by default
  • Handles DPI scaling automatically
  • Provides native context menus and tooltips

macOS Platform

  • Integrates with Cocoa framework
  • Supports native menu bars and dock integration
  • Implements proper window lifecycle management
  • Handles Retina display scaling

Linux Platform

  • Depends on X11 or Wayland backend
  • May require additional configuration for hardware acceleration
  • Varies significantly between distributions
  • Limited support for some advanced features

Rendering Differences

Avalonia provides consistent rendering across platforms, but certain visual elements may render differently:

graph LR
subgraph "Rendering Pipeline"
XAML["XAML/UI Definition"]
AvaloniaRenderer["Avalonia Renderer"]
PlatformBackend["Platform Backend"]
end
subgraph "Windows"
WinRT["WinRT/DirectX"]
WGL["OpenGL"]
end
subgraph "macOS"
Metal["Metal"]
OpenGL["OpenGL"]
end
subgraph "Linux"
X11["X11"]
Wayland["Wayland"]
Vulkan["Vulkan"]
end
AvaloniaRenderer --> PlatformBackend
PlatformBackend --> WinRT
PlatformBackend --> Metal
PlatformBackend --> X11

Diagram sources

Font and Text Rendering

Font rendering varies across platforms due to different text measurement engines:

  • Windows: Uses ClearType for enhanced text clarity
  • macOS: Leverages Core Text for native font rendering
  • Linux: Depends on FreeType and system font configuration

Section sources

Dispatcher System and Thread Marshaling

Thread Safety Considerations

Cross-platform applications must handle threading carefully, as each platform has different threading models and constraints:

stateDiagram-v2
[*] --> BackgroundThread
BackgroundThread --> UIUpdateRequired : "Need to update UI"
UIUpdateRequired --> DispatcherQueue : "Queue UI operation"
DispatcherQueue --> UIThread : "Dispatch to UI thread"
UIThread --> UIUpdate : "Execute UI update"
UIUpdate --> [*]
BackgroundThread --> ContinueProcessing : "Continue background work"
ContinueProcessing --> [*]

Diagram sources

Dispatcher Implementation Details

The Avalonia dispatcher implementation leverages Avalonia's built-in threading infrastructure:

  • Automatic Thread Detection: Detects whether code is running on the UI thread
  • Synchronous and Asynchronous Operations: Supports both immediate and deferred UI updates
  • Exception Handling: Properly propagates exceptions from UI thread operations
  • Performance Optimization: Minimizes overhead for frequent UI updates

Best Practices for Thread Marshaling

  1. Always use the dispatcher for UI updates from background threads
  2. Avoid blocking the UI thread with long-running operations
  3. Handle exceptions properly in dispatched operations
  4. Consider performance implications of frequent UI updates
  5. Test thoroughly on all target platforms

Section sources

Keyboard Handling Differences

Key Event Processing

Keyboard input handling varies significantly across platforms:

Windows Platform

  • Uses Win32 key codes and virtual key codes
  • Supports advanced input features like raw input
  • Handles modifier keys consistently
  • Provides detailed key state information

macOS Platform

  • Uses Carbon/Cocoa key event system
  • Different modifier key behavior (Command vs Control)
  • International keyboard layout support
  • Special handling for function keys

Linux Platform

  • Depends on X11 or Wayland input system
  • Variable support for special keys
  • Distribution-dependent behavior
  • May require additional configuration for full functionality

Platform-Specific Key Mappings

flowchart TD
KeyPress["Key Press Event"] --> PlatformCheck{"Platform Detection"}
PlatformCheck --> |Windows| WinKeys["Windows Key Codes"]
PlatformCheck --> |macOS| MacKeys["macOS Key Codes"]
PlatformCheck --> |Linux| LinKeys["Linux Key Codes"]
WinKeys --> Normalize["Normalize to Common Format"]
MacKeys --> Normalize
LinKeys --> Normalize
Normalize --> ApplicationLogic["Application Logic"]
ApplicationLogic --> Response["UI Response"]

Diagram sources

Best Practices for Keyboard Input

  1. Abstract key handling behind platform-independent interfaces
  2. Test keyboard shortcuts on all target platforms
  3. Handle international keyboards appropriately
  4. Provide fallback mechanisms for unsupported keys
  5. Document platform-specific behaviors clearly

File System Access Patterns

Cross-Platform File Operations

File system access requires careful consideration of platform differences:

graph TB
subgraph "Application Layer"
FileSystemAPI["FileSystem API"]
PathUtilities["Path Utilities"]
end
subgraph "Platform Abstraction"
PathSeparator["Path Separator Handler"]
PermissionHandler["Permission Handler"]
EncodingHandler["Encoding Handler"]
end
subgraph "Platform Implementations"
WinFS["Windows File System"]
MacFS["macOS File System"]
LinuxFS["Linux File System"]
end
FileSystemAPI --> PathUtilities
PathUtilities --> PathSeparator
PathUtilities --> PermissionHandler
PathUtilities --> EncodingHandler
PathSeparator --> WinFS
PathSeparator --> MacFS
PathSeparator --> LinuxFS
PermissionHandler --> WinFS
PermissionHandler --> MacFS
PermissionHandler --> LinuxFS
EncodingHandler --> WinFS
EncodingHandler --> MacFS
EncodingHandler --> LinuxFS

Diagram sources

Path Handling Differences

  • Windows: Uses backslashes (\) as path separators
  • macOS/Linux: Uses forward slashes (/) as path separators
  • Case Sensitivity: Linux/macOS are case-sensitive; Windows is not
  • Drive Letters: Only Windows uses drive letters (C:, D:)

File Permissions and Security

Each platform has different permission models:

  • Windows: ACL-based permissions with user/group model
  • macOS: Unix-style permissions with extended attributes
  • Linux: Traditional Unix permissions with SELinux/AppArmor support

Section sources

Native Integration Points

Platform-Specific APIs

The application integrates with platform-specific APIs for enhanced functionality:

Windows Integration

  • Registry access for settings storage
  • Windows Forms interop for legacy components
  • DirectX integration for graphics acceleration
  • Windows notification system integration

macOS Integration

  • Cocoa framework integration
  • NSUserDefaults for preferences
  • Spotlight indexing support
  • Notification center integration

Linux Integration

  • Desktop environment integration (GNOME, KDE)
  • System tray integration
  • MIME type handling
  • File association management

Native Method Calls

sequenceDiagram
participant App as "Application"
participant NativeBridge as "Native Bridge"
participant OS as "Operating System"
App->>NativeBridge : Call Native Function
NativeBridge->>OS : Platform-Specific API Call
OS-->>NativeBridge : Result Data
NativeBridge-->>App : Processed Result
Note over App,OS : Platform abstraction layer handles<br/>different calling conventions and data types

Diagram sources

Memory Management Considerations

Different platforms have varying memory management requirements:

  • Windows: COM objects require proper cleanup
  • macOS: ARC-managed objects with manual bridging
  • Linux: Manual memory management for native libraries

Section sources

Testing Strategies

Cross-Platform Testing Approach

Testing cross-platform UI components requires a multi-faceted approach:

flowchart TD
UnitTests["Unit Tests"] --> MockPlatform["Mock Platform Services"]
UnitTests --> BusinessLogic["Business Logic Validation"]
IntegrationTests["Integration Tests"] --> TestEnvironment["Test Environment Setup"]
IntegrationTests --> PlatformAbstractions["Platform Abstractions"]
UIAutomation["UI Automation"] --> HeadlessBrowser["Headless Browser Testing"]
UIAutomation --> ScreenCapture["Screen Capture Analysis"]
ManualTesting["Manual Testing"] --> AllPlatforms["All Target Platforms"]
ManualTesting --> UserScenarios["Real User Scenarios"]
AllPlatforms --> Windows["Windows Testing"]
AllPlatforms --> macOS["macOS Testing"]
AllPlatforms --> Linux["Linux Testing"]

Diagram sources

Platform-Specific Test Cases

Windows-Specific Tests

  • Registry access and modification
  • Windows Forms interoperability
  • Hardware acceleration verification
  • DPI scaling validation

macOS-Specific Tests

  • Cocoa framework integration
  • Menu bar functionality
  • Dock integration
  • Retina display rendering

Linux-Specific Tests

  • Desktop environment compatibility
  • File manager integration
  • System theme adherence
  • Package manager integration

Continuous Integration Setup

graph LR
subgraph "CI Pipeline"
Build["Build Stage"]
UnitTest["Unit Tests"]
IntegrationTest["Integration Tests"]
PlatformTest["Platform-Specific Tests"]
Deployment["Deployment"]
end
subgraph "Platforms"
Win["Windows VM"]
Mac["macOS VM"]
Lin["Linux VM"]
end
Build --> UnitTest
UnitTest --> IntegrationTest
IntegrationTest --> PlatformTest
PlatformTest --> Win
PlatformTest --> Mac
PlatformTest --> Lin
Win --> Deployment
Mac --> Deployment
Lin --> Deployment

Diagram sources

Debugging Techniques

Platform-Specific Debugging Tools

Windows Debugging

  • Visual Studio debugger with .NET debugging
  • WinDbg for low-level analysis
  • Performance Profiler for resource usage
  • Network traffic monitoring

macOS Debugging

  • Xcode debugger with LLDB
  • Instruments for performance analysis
  • Console.app for system logs
  • Activity Monitor for resource usage

Linux Debugging

  • GDB for source-level debugging
  • Valgrind for memory leak detection
  • strace for system call tracing
  • dmesg for kernel messages

Common Cross-Platform Issues

flowchart TD
IssueDetected["Issue Detected"] --> PlatformIdentify{"Identify Platform"}
PlatformIdentify --> WinIssues["Windows-Specific Issues"]
PlatformIdentify --> MacIssues["macOS-Specific Issues"]
PlatformIdentify --> LinIssues["Linux-Specific Issues"]
WinIssues --> WinTools["Windows Debug Tools"]
MacIssues --> MacTools["macOS Debug Tools"]
LinIssues --> LinTools["Linux Debug Tools"]
WinTools --> Resolution["Resolution Strategy"]
MacTools --> Resolution
LinTools --> Resolution
Resolution --> VerifyFix["Verify Fix on All Platforms"]
VerifyFix --> DeployFix["Deploy Fix"]

Diagram sources

Logging and Diagnostics

Implement comprehensive logging that works across all platforms:

  • Structured Logging: Use JSON-formatted logs for easy parsing
  • Platform Context: Include platform information in log entries
  • Performance Metrics: Track performance indicators per platform
  • Error Aggregation: Centralize error reporting across platforms

Section sources

Best Practices for Platform-Agnostic Code

Design Principles

  1. Interface-Based Design: Define clear interfaces for platform-specific functionality
  2. Dependency Injection: Use DI containers to inject platform-specific implementations
  3. Configuration-Driven Behavior: Make platform-specific behavior configurable
  4. Graceful Degradation: Provide fallbacks when platform features are unavailable

Code Organization

graph TB
subgraph "Shared Code"
Interfaces["Interfaces & Contracts"]
Models["Data Models"]
Algorithms["Platform-Independent Algorithms"]
end
subgraph "Platform Implementations"
WinImpl["Windows Implementation"]
MacImpl["macOS Implementation"]
LinImpl["Linux Implementation"]
end
subgraph "UI Layer"
Views["Platform-Agnostic Views"]
ViewModels["Shared ViewModels"]
end
Interfaces --> WinImpl
Interfaces --> MacImpl
Interfaces --> LinImpl
Models --> Views
Models --> ViewModels
Algorithms --> ViewModels
Algorithms --> Views

Diagram sources

Conditional Compilation vs Runtime Detection

Choose the appropriate approach based on complexity:

  • Conditional Compilation: For significant architectural differences
  • Runtime Detection: For minor behavioral variations
  • Feature Flags: For optional platform-specific features

Error Handling Strategies

Implement robust error handling that accounts for platform differences:

  • Platform-Specific Error Messages: Provide meaningful error messages for each platform
  • Fallback Mechanisms: Gracefully handle missing platform features
  • Diagnostic Information: Collect platform-specific diagnostic data
  • User-Friendly Recovery: Guide users through platform-specific recovery steps

Conclusion

Developing cross-platform UI applications with ClassicAssist.Avalonia requires careful consideration of platform-specific behaviors, threading models, and system integrations. The architecture presented in this document provides a solid foundation for building maintainable and performant cross-platform applications.

Key takeaways include:

  1. Abstraction Layers: Use well-defined interfaces to abstract platform-specific details
  2. Thread Safety: Always use the dispatcher system for UI updates from background threads
  3. Platform Detection: Implement robust platform detection for conditional behavior
  4. Comprehensive Testing: Test thoroughly on all target platforms
  5. Graceful Degradation: Handle missing platform features gracefully
  6. Consistent User Experience: Maintain consistent behavior across platforms while respecting platform conventions

By following these guidelines and leveraging the provided abstractions, developers can create high-quality cross-platform applications that provide excellent user experiences on Windows, macOS, and Linux platforms.