using Hi.Disp;
using Hi.Native;
using Hi.PanelModels;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Collections.Generic;
using Hi.Geom;
namespace Hi.Wpf.Disp
{
#region WPF Rendering Canvas
public class RenderingCanvas : UserControl, IDisposable
{
#region Core_Properties
///
/// The DispEngine instance that handles rendering and user interactions
///
public DispEngine DispEngine { get; } = new DispEngine();
///
/// Internal container for rendering content
///
private UserControl DisplayerPane { get; }
///
/// Dictionary to store touch point information
///
private Dictionary TouchingPointsMap { get; } = new Dictionary();
///
/// Dictionary to store previous positions of touch points
///
private Dictionary PreviousTouchingPointsMap { get; } = new Dictionary();
#endregion
#region Initialization
///
/// Initializes a new instance of the RenderingCanvas
///
public RenderingCanvas()
{
DispEngine.BackgroundColor = new Vec3d(0.1, 0.1, 0.5);
DispEngine.BackgroundOpacity = 0.1;
// Configure the main control properties
HorizontalAlignment = HorizontalAlignment.Stretch;
VerticalAlignment = VerticalAlignment.Stretch;
Focusable = true;
KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Cycle);
DataContextChanged += CanvasDataContextChanged;
// Create and configure the display pane
DisplayerPane = new UserControl();
DisplayerPane.HorizontalAlignment = HorizontalAlignment.Stretch;
DisplayerPane.VerticalAlignment = VerticalAlignment.Stretch;
DisplayerPane.Focusable = true;
DisplayerPane.IsTabStop = true;
// Connect event handlers for user input and window events
DisplayerPane.SizeChanged += RenderingCanvas_SizeChanged;
DisplayerPane.MouseMove += RenderingCanvas_MouseMove;
DisplayerPane.MouseDown += RenderingCanvas_MouseDown;
DisplayerPane.MouseUp += RenderingCanvas_MouseUp;
DisplayerPane.MouseWheel += RenderingCanvas_MouseWheel;
DisplayerPane.KeyDown += RenderingCanvas_KeyDown;
DisplayerPane.KeyUp += RenderingCanvas_KeyUp;
DisplayerPane.Loaded += RenderingCanvas_Loaded;
DisplayerPane.Unloaded += RenderingCanvas_Unloaded;
DisplayerPane.IsVisibleChanged += DisplayerPane_IsVisibleChanged;
// Add touch event handlers
DisplayerPane.TouchDown += RenderingCanvas_TouchDown;
DisplayerPane.TouchMove += RenderingCanvas_TouchMove;
DisplayerPane.TouchUp += RenderingCanvas_TouchUp;
// Enable touch support
this.IsManipulationEnabled = true;
// Add the display pane to this control's content
Content = DisplayerPane;
}
#endregion
#region Touch_Events
///
/// Handles the touch down event
///
private void RenderingCanvas_TouchDown(object sender, TouchEventArgs e)
{
// Add touch point to dictionary
Point touchPoint = e.GetTouchPoint(DisplayerPane).Position;
DispEngine.TouchDown(e.TouchDevice.Id,
(int)touchPoint.X, (int)touchPoint.Y);
// Ensure control gets focus
DisplayerPane.Focus();
e.TouchDevice.Capture(DisplayerPane);
e.Handled = true;
}
///
/// Handles the touch move event
///
private void RenderingCanvas_TouchMove(object sender, TouchEventArgs e)
{
Point touchPoint = e.GetTouchPoint(DisplayerPane).Position;
DispEngine.TouchMove(e.TouchDevice.Id,
(int)touchPoint.X, (int)touchPoint.Y);
e.Handled = true;
}
///
/// Handles the touch up event
///
private void RenderingCanvas_TouchUp(object sender, TouchEventArgs e)
{
DispEngine.TouchUp(e.TouchDevice.Id);
e.Handled = true;
}
#endregion
#region Window_Events
///
/// Handles window state changes (maximize, minimize, etc.)
///
private unsafe void RenderingCanvas_StateChanged(object sender, EventArgs e)
{
switch ((sender as Window).WindowState)
{
case WindowState.Maximized:
DispEngine.IsVisible = true;
break;
case WindowState.Minimized:
DispEngine.IsVisible = false;
break;
case WindowState.Normal:
DispEngine.IsVisible = true;
break;
}
}
///
/// Handles data context changes
///
private unsafe void CanvasDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
DispEngine pre = e.OldValue as DispEngine;
DispEngine cur = e.NewValue as DispEngine;
//child's binding event is triggered after IsVisible event and Load event.
if (pre != null) //this section will never occur if the datacontext not set twice.
{
pre.Terminate();
pre.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped;
}
if (cur != null)
{
cur.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped;
cur.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height);
cur.IsVisible = IsVisible;
}
}
///
/// Reference to the current window containing this control
///
private Window currentWindow;
///
/// Gets or sets the current window, connecting or disconnecting state change events
///
Window CurrentWindow
{
get => currentWindow; set
{
if (currentWindow != null)
currentWindow.StateChanged -= RenderingCanvas_StateChanged;
currentWindow = value;
if (currentWindow != null)
currentWindow.StateChanged += RenderingCanvas_StateChanged;
}
}
///
/// Handles the loaded event
///
private unsafe void RenderingCanvas_Loaded(object sender, RoutedEventArgs e)
{
// Get the window containing this control
CurrentWindow = Window.GetWindow(this);
// Set up DispEngine rendering
DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped;
DispEngine.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped;
DispEngine.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height);
DispEngine.IsVisible = IsVisible;
}
///
/// Handles the unloaded event
///
private unsafe void RenderingCanvas_Unloaded(object sender, RoutedEventArgs e)
{
DispEngine.IsVisible = IsVisible;
DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped;
CurrentWindow = null;
}
#endregion
#region DispEngine_Rendering
///
/// Handles the buffer swapped event from DispEngine
///
private unsafe void RenderingCanvas_BufferSwapped(byte* data, int w, int h)
{
if (data == null)
return;
// Copy pixel data from DispEngine
int n = w * h * 4;
byte[] arr = new byte[n];
for (int i = 0; i < n; i++)
arr[i] = data[i];
// Update UI on the UI thread
DisplayerPane.Dispatcher.InvokeAsync(() =>
{
BitmapSource bitmap = BitmapSource.Create(w, h, 1, 1, PixelFormats.Bgra32, null, arr, w * 4);
DisplayerPane.Background = new ImageBrush(bitmap);
});
}
///
/// Handles the size changed event
///
private void RenderingCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Notify DispEngine of size changes
DispEngine.Resize((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height);
}
///
/// Handles visibility changes
///
private unsafe void DisplayerPane_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Update visibility state in DispEngine
DispEngine.IsVisible = IsVisible;
}
#endregion
#region Keyboard_Events
///
/// Handles the key up event
///
private void RenderingCanvas_KeyUp(object sender, KeyEventArgs e)
{
DispEngine.KeyUp((long)e.Key);
}
///
/// Handles the key down event
///
private void RenderingCanvas_KeyDown(object sender, KeyEventArgs e)
{
DispEngine.KeyDown((long)e.Key);
// Map specific keys for view transformation
long key = (long)e.Key;
if (key == (long)Key.RightShift)
key = (long)Key.LeftShift;
DispEngine.KeyDownTransform(key, new key_table__transform_view_by_key_pressing_t()
{
HOME = (long)Key.Home,
PAGE_UP = (long)Key.PageUp,
PAGE_DOWN = (long)Key.PageDown,
F1 = (long)Key.F1,
F2 = (long)Key.F2,
F3 = (long)Key.F3,
F4 = (long)Key.F4,
SHIFT = (long)Key.LeftShift,
ARROW_LEFT = (long)Key.Left,
ARROW_RIGHT = (long)Key.Right,
ARROW_DOWN = (long)Key.Down,
ARROW_UP = (long)Key.Up
});
}
#endregion
#region Mouse_Events
///
/// Helper method to get mouse button mask
///
internal static HiMouseButtonMask GetMouseButtonMask(MouseDevice device)
{
HiMouseButtonMask mouseButtonMask = 0;
mouseButtonMask.SetLeftPressed(device.LeftButton == MouseButtonState.Pressed);
mouseButtonMask.SetMiddlePressed(device.MiddleButton == MouseButtonState.Pressed);
mouseButtonMask.SetRightPressed(device.RightButton == MouseButtonState.Pressed);
mouseButtonMask.SetXButton1Pressed(device.XButton1 == MouseButtonState.Pressed);
mouseButtonMask.SetXButton2Pressed(device.XButton2 == MouseButtonState.Pressed);
return mouseButtonMask;
}
///
/// Handles the mouse wheel event
///
private void RenderingCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
// Handle mouse wheel for zoom operations
DispEngine.MouseWheel(0, e.Delta / 120);
DispEngine.MouseWheelTransform(0, e.Delta / 120);
}
///
/// Handles the mouse up event
///
private void RenderingCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
// Handle mouse button release
DispEngine.MouseButtonUp((long)e.ChangedButton);
(sender as UIElement)?.ReleaseMouseCapture();
}
///
/// Handles the mouse down event
///
private void RenderingCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
// Handle mouse button press
DispEngine.MouseButtonDown((long)e.ChangedButton);
DisplayerPane.Focus();
(sender as UIElement)?.CaptureMouse();
}
///
/// Handles the mouse move event
///
private void RenderingCanvas_MouseMove(object sender, MouseEventArgs e)
{
// Update mouse position and handle drag transforms
Point p = e.GetPosition(DisplayerPane);
DispEngine.MouseMove((int)p.X, (int)p.Y);
DispEngine.MouseDragTransform((int)p.X, (int)p.Y,
new mouse_button_table__transform_view_by_mouse_drag_t()
{
LEFT_BUTTON = (long)MouseButton.Left,
RIGHT_BUTTON = (long)MouseButton.Right
});
}
#endregion
#region Cleanup
///
/// Flag to track disposed state
///
private bool disposedValue;
///
/// Disposes managed resources
///
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// Dispose the DispEngine to free resources
DispEngine.Dispose();
}
disposedValue = true;
}
}
///
/// Public dispose method to free resources
///
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
#endregion
}