using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Photino.NET;
public partial class PhotinoWindow
{
//PRIVATE FIELDS
///
/// Parameters sent to Photino.Native to start a new instance of a Photino.Native window.
///
/// Indicates whether the window is resizable.
/// Specifies whether the context menu is enabled.
/// An array of strings representing custom scheme names.
/// Specifies whether developer tools are enabled.
/// Indicates whether browser permissions are granted.
/// Defines the path for temporary files.
/// Sets the title of the window.
/// Specifies whether the window should use the OS default location.
/// Indicates whether the window should use the OS default size.
/// Sets the zoom level for the window.
private PhotinoNativeParameters _startupParameters = new()
{
Resizable = true, //These values can't be initialized within the struct itself. Set required defaults.
ContextMenuEnabled = true,
CustomSchemeNames = new string[16],
DevToolsEnabled = true,
GrantBrowserPermissions = true,
UserAgent = "Photino WebView",
MediaAutoplayEnabled = true,
FileSystemAccessEnabled = true,
WebSecurityEnabled = true,
JavascriptClipboardAccessEnabled = true,
MediaStreamEnabled = true,
SmoothScrollingEnabled = true,
IgnoreCertificateErrorsEnabled = false,
NotificationsEnabled = true,
TemporaryFilesPath = IsWindowsPlatform
? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Photino")
: null,
Title = "Photino",
UseOsDefaultLocation = true,
UseOsDefaultSize = true,
Zoom = 100,
MaxHeight = int.MaxValue,
MaxWidth = int.MaxValue,
};
//Pointers to the type and instance.
private static IntPtr _nativeType = IntPtr.Zero;
private IntPtr _nativeInstance;
private readonly int _managedThreadId;
//There can only be 1 message loop for all windows.
private static bool _messageLoopIsStarted = false;
//READ ONLY PROPERTIES
///
/// Indicates whether the current platform is Windows.
///
///
/// true if the current platform is Windows; otherwise, false.
///
public static bool IsWindowsPlatform => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
///
/// Indicates whether the current platform is MacOS.
///
///
/// true if the current platform is MacOS; otherwise, false.
///
public static bool IsMacOsPlatform => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
///
/// Indicates the version of MacOS
///
public static Version MacOsVersion => IsMacOsPlatform ? Version.Parse(RuntimeInformation.OSDescription.Split(' ')[1]) : null;
///
/// Indicates whether the current platform is Linux.
///
///
/// true if the current platform is Linux; otherwise, false.
///
public static bool IsLinuxPlatform => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
///
/// Represents a property that gets the handle of the native window on a Windows platform.
///
///
/// Only available on the Windows platform.
/// If this property is accessed from a non-Windows platform, a PlatformNotSupportedException will be thrown.
/// If this property is accessed before the window is initialized, an ApplicationException will be thrown.
///
///
/// The handle of the native window. The value is of type .
///
/// Thrown when the window is not initialized yet.
/// Thrown when accessed from a non-Windows platform.
public IntPtr WindowHandle
{
get
{
if (IsWindowsPlatform)
{
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("The Photino window is not initialized yet");
var handle = IntPtr.Zero;
Invoke(() => handle = Photino_getHwnd_win32(_nativeInstance));
return handle;
}
else
throw new PlatformNotSupportedException($"{nameof(WindowHandle)} is only supported on Windows.");
}
}
///
/// Gets list of information for each monitor from the native window.
/// This property represents a list of Monitor objects associated to each display monitor.
///
///
/// If called when the native instance of the window is not initialized, it will throw an ApplicationException.
///
/// Thrown when the native instance of the window is not initialized.
///
/// A read-only list of Monitor objects representing information about each display monitor.
///
public IReadOnlyList Monitors
{
get
{
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("The Photino window hasn't been initialized yet.");
List monitors = new();
int callback(in NativeMonitor monitor)
{
monitors.Add(new Monitor(monitor));
return 1;
}
Invoke(() => Photino_GetAllMonitors(_nativeInstance, callback));
return monitors;
}
}
///
/// Retrieves the primary monitor information from the native window instance.
///
/// Thrown when the window hasn't been initialized yet.
///
/// Returns a Monitor object representing the main monitor. The main monitor is the first monitor in the list of available monitors.
///
public Monitor MainMonitor
{
get
{
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("The Photino window hasn't been initialized yet.");
return Monitors[0];
}
}
///
/// Gets the dots per inch (DPI) for the primary display from the native window.
///
///
/// An ApplicationException is thrown if the window hasn't been initialized yet.
///
public uint ScreenDpi
{
get
{
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("The Photino window hasn't been initialized yet.");
uint dpi = 0;
Invoke(() => dpi = Photino_GetScreenDpi(_nativeInstance));
return dpi;
}
}
///
/// Gets a unique GUID to identify the native window.
///
///
/// This property is not currently utilized by the Photino framework.
///
public Guid Id { get; } = Guid.NewGuid();
//READ-WRITE PROPERTIES
///
/// When true, the native window will appear centered on the screen. By default, this is set to false.
///
///
/// Thrown if trying to set value after native window is initalized.
///
public bool Centered
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.CenterOnInitialize;
return false;
}
set
{
if (_nativeInstance == IntPtr.Zero)
{
if (_startupParameters.CenterOnInitialize != value)
_startupParameters.CenterOnInitialize = value;
}
else
Invoke(() => Photino_Center(_nativeInstance));
}
}
///
/// Gets or sets a value indicating whether the native window should be chromeless.
/// When true, the native window will appear without a title bar or border.
/// By default, this is set to false.
///
///
/// Thrown if trying to set value after native window is initalized.
///
///
/// The user has to supply titlebar, border, dragging and resizing manually.
///
public bool Chromeless
{
get
{
return _startupParameters.Chromeless;
}
set
{
if (_nativeInstance == IntPtr.Zero)
{
if (_startupParameters.Chromeless != value)
_startupParameters.Chromeless = value;
}
else
throw new ApplicationException("Chromeless can only be set before the native window is instantiated.");
}
}
///
/// When true, the native window and browser control can be displayed with transparent background.
/// Html document's body background must have alpha-based value.
/// WebView2 on Windows can only be fully transparent or fully opaque.
/// By default, this is set to false.
///
///
/// On Windows, thrown if trying to set value after native window is initalized.
///
public bool Transparent
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Transparent;
var enabled = false;
Invoke(() => Photino_GetTransparentEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (Transparent != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Transparent = value;
else
{
if (IsWindowsPlatform)
throw new ApplicationException("Transparent can only be set on Windows before the native window is instantiated.");
else
{
Log($"Invoking Photino_SetTransparentEnabled({value})");
Invoke(() => Photino_SetTransparentEnabled(_nativeInstance, value));
}
}
}
}
}
///
/// When true, the user can access the browser control's context menu.
/// By default, this is set to true.
///
public bool ContextMenuEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.ContextMenuEnabled;
var enabled = false;
Invoke(() => Photino_GetContextMenuEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (ContextMenuEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.ContextMenuEnabled = value;
else
Invoke(() => Photino_SetContextMenuEnabled(_nativeInstance, value));
}
}
}
///
/// When true, the user can access the browser control's developer tools.
/// By default, this is set to true.
///
public bool DevToolsEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.DevToolsEnabled;
var enabled = false;
Invoke(() => Photino_GetDevToolsEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (DevToolsEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.DevToolsEnabled = value;
else
Invoke(() => Photino_SetDevToolsEnabled(_nativeInstance, value));
}
}
}
public bool MediaAutoplayEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.MediaAutoplayEnabled;
var enabled = false;
Invoke(() => Photino_GetMediaAutoplayEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (MediaAutoplayEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.MediaAutoplayEnabled = value;
else
throw new ApplicationException("MediaAutoplayEnabled can only be set before the native window is instantiated.");
}
}
}
public string UserAgent
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.UserAgent;
var userAgent = string.Empty;
Invoke(() =>
{
var ptr = Photino_GetUserAgent(_nativeInstance);
userAgent = Marshal.PtrToStringAuto(ptr);
});
return userAgent;
}
set
{
if (UserAgent != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.UserAgent = value;
else
throw new ApplicationException("UserAgent can only be set before the native window is instantiated.");
}
}
}
public bool FileSystemAccessEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.FileSystemAccessEnabled;
var enabled = false;
Invoke(() => Photino_GetFileSystemAccessEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (FileSystemAccessEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.FileSystemAccessEnabled = value;
else
throw new ApplicationException("FileSystemAccessEnabled can only be set before the native window is instantiated.");
}
}
}
public bool WebSecurityEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.WebSecurityEnabled;
var enabled = true;
Invoke(() => Photino_GetWebSecurityEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (WebSecurityEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.WebSecurityEnabled = value;
else
throw new ApplicationException("WebSecurityEnabled can only be set before the native window is instantiated.");
}
}
}
public bool JavascriptClipboardAccessEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.JavascriptClipboardAccessEnabled;
var enabled = true;
Invoke(() => Photino_GetJavascriptClipboardAccessEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (JavascriptClipboardAccessEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.JavascriptClipboardAccessEnabled = value;
else
throw new ApplicationException("JavascriptClipboardAccessEnabled can only be set before the native window is instantiated.");
}
}
}
public bool MediaStreamEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.MediaStreamEnabled;
var enabled = true;
Invoke(() => Photino_GetMediaStreamEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (MediaStreamEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.MediaStreamEnabled = value;
else
throw new ApplicationException("MediaStreamEnabled can only be set before the native window is instantiated.");
}
}
}
public bool SmoothScrollingEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.SmoothScrollingEnabled;
var enabled = false;
Invoke(() => Photino_GetSmoothScrollingEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (SmoothScrollingEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.SmoothScrollingEnabled = value;
else
throw new ApplicationException("SmoothScrollingEnabled can only be set before the native window is instantiated.");
}
}
}
public bool IgnoreCertificateErrorsEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.IgnoreCertificateErrorsEnabled;
var enabled = false;
Invoke(() => Photino_GetIgnoreCertificateErrorsEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (IgnoreCertificateErrorsEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.IgnoreCertificateErrorsEnabled = value;
else
throw new ApplicationException("IgnoreCertificateErrorsEnabled can only be set before the native window is instantiated.");
}
}
}
public bool NotificationsEnabled
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.NotificationsEnabled;
var enabled = false;
Invoke(() => Photino_GetNotificationsEnabled(_nativeInstance, out enabled));
return enabled;
}
set
{
if (NotificationsEnabled != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.NotificationsEnabled = value;
else
throw new ApplicationException("NotificationsEnabled can only be set before the native window is instantiated.");
}
}
}
///
/// This property returns or sets the fullscreen status of the window.
/// When set to true, the native window will cover the entire screen, similar to kiosk mode.
/// By default, this is set to false.
///
public bool FullScreen
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.FullScreen;
var fullScreen = false;
Invoke(() => Photino_GetFullScreen(_nativeInstance, out fullScreen));
return fullScreen;
}
set
{
if (FullScreen != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.FullScreen = value;
else
Invoke(() => Photino_SetFullScreen(_nativeInstance, value));
}
}
}
///
/// Gets or Sets whether the native browser control grants all requests for access to local resources
/// such as the users camera and microphone. By default, this is set to true.
///
///
/// This only works on Windows.
///
public bool GrantBrowserPermissions
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.GrantBrowserPermissions;
var grant = false;
Invoke(() => Photino_GetGrantBrowserPermissions(_nativeInstance, out grant));
return grant;
}
set
{
if (GrantBrowserPermissions != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.GrantBrowserPermissions = value;
else
throw new ApplicationException("GrantBrowserPermissions can only be set before the native window is instantiated.");
}
}
}
/// ///
/// Gets or Sets the Height property of the native window in pixels.
/// Default value is 0.
///
///
public int Height
{
get => Size.Height;
set
{
var currentSize = Size;
if (currentSize.Height != value)
Size = new Size(currentSize.Width, value);
}
}
private string _iconFile;
///
/// Gets or sets the icon file for the native window title bar.
/// The file must be located on the local machine and cannot be a URL. The default is none.
///
///
/// This only works on Windows and Linux.
///
///
/// The file path to the icon.
///
/// Icon file: {value} does not exist.
public string IconFile
{
get => _iconFile;
set
{
if (_iconFile != value)
{
if (!File.Exists(value))
{
var absolutePath = $"{System.AppContext.BaseDirectory}{value}";
if (!File.Exists(absolutePath))
throw new ArgumentException($"Icon file: {value} does not exist.");
}
_iconFile = value;
if (_nativeInstance == IntPtr.Zero)
_startupParameters.WindowIconFile = _iconFile;
else
Invoke(() => Photino_SetIconFile(_nativeInstance, _iconFile));
}
}
}
///
/// Gets or sets the native window Left (X) and Top coordinates (Y) in pixels.
/// Default is 0,0 which means the window will be aligned to the top left edge of the screen.
///
///
public Point Location
{
get
{
if (_nativeInstance == IntPtr.Zero)
return new Point(_startupParameters.Left, _startupParameters.Top);
var left = 0;
var top = 0;
Invoke(() => Photino_GetPosition(_nativeInstance, out left, out top));
return new Point(left, top);
}
set
{
if (Location.X != value.X || Location.Y != value.Y)
{
if (_nativeInstance == IntPtr.Zero)
{
_startupParameters.Left = value.X;
_startupParameters.Top = value.Y;
}
else
Invoke(() => Photino_SetPosition(_nativeInstance, value.X, value.Y));
}
}
}
///
/// Gets or sets the native window Left (X) coordinate in pixels.
/// This represents the horizontal position of the window relative to the screen.
/// Default value is 0 which means the window will be aligned to the left edge of the screen.
///
///
public int Left
{
get => Location.X;
set
{
if (Location.X != value)
Location = new Point(value, Location.Y);
}
}
///
/// Gets or sets whether the native window is maximized.
/// Default is false.
///
public bool Maximized
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Maximized;
bool maximized = false;
Invoke(() => Photino_GetMaximized(_nativeInstance, out maximized));
return maximized;
}
set
{
if (Maximized != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Maximized = value;
else
Invoke(() => Photino_SetMaximized(_nativeInstance, value));
}
}
}
///Gets or set the maximum size of the native window in pixels.
public Point MaxSize
{
get => new Point(MaxWidth, MaxHeight);
set
{
if (MaxWidth != value.X || MaxHeight != value.Y)
{
if (_nativeInstance == IntPtr.Zero)
{
_startupParameters.MaxWidth = value.X;
_startupParameters.MaxHeight = value.Y;
}
else
Invoke(() => Photino_SetMaxSize(_nativeInstance, value.X, value.Y));
}
}
}
///Gets or sets the native window maximum height in pixels.
private int _maxHeight = int.MaxValue;
public int MaxHeight
{
get => _maxHeight;
set
{
if (_maxHeight != value)
{
MaxSize = new Point(MaxSize.X, value);
_maxHeight = value;
}
}
}
///Gets or sets the native window maximum height in pixels.
private int _maxWidth = int.MaxValue;
public int MaxWidth
{
get => _maxWidth;
set
{
if (_maxWidth != value)
{
MaxSize = new Point(value, MaxSize.Y);
_maxWidth = value;
}
}
}
///
/// Gets or sets whether the native window is minimized (hidden).
/// Default is false.
///
public bool Minimized
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Minimized;
bool minimized = false;
Invoke(() => Photino_GetMinimized(_nativeInstance, out minimized));
return minimized;
}
set
{
if (Minimized != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Minimized = value;
else
Invoke(() => Photino_SetMinimized(_nativeInstance, value));
}
}
}
///Gets or set the minimum size of the native window in pixels.
public Point MinSize
{
get => new Point(MinWidth, MinHeight);
set
{
if (MinWidth != value.X || MinHeight != value.Y)
{
if (_nativeInstance == IntPtr.Zero)
{
_startupParameters.MinWidth = value.X;
_startupParameters.MinHeight = value.Y;
}
else
Invoke(() => Photino_SetMinSize(_nativeInstance, value.X, value.Y));
}
}
}
///Gets or sets the native window minimum height in pixels.
private int _minHeight = 0;
public int MinHeight
{
get => _minHeight;
set
{
if (_minHeight != value)
{
MinSize = new Point(MinSize.X, value);
_minHeight = value;
}
}
}
///Gets or sets the native window minimum height in pixels.
private int _minWidth = 0;
public int MinWidth
{
get => _minWidth;
set
{
if (_minWidth != value)
{
MinSize = new Point(value, MinSize.Y);
_minWidth = value;
}
}
}
private readonly PhotinoWindow _dotNetParent;
///
/// Gets the reference to parent PhotinoWindow instance.
/// This property can only be set in the constructor and it is optional.
///
public PhotinoWindow Parent { get { return _dotNetParent; } }
///
/// Gets or sets whether the native window can be resized by the user.
/// Default is true.
///
public bool Resizable
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Resizable;
var resizable = false;
Invoke(() => Photino_GetResizable(_nativeInstance, out resizable));
return resizable;
}
set
{
if (Resizable != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Resizable = value;
else
Invoke(() => Photino_SetResizable(_nativeInstance, value));
}
}
}
///
/// Gets or sets the native window Size. This represents the width and the height of the window in pixels.
/// The default Size is 0,0.
///
///
public Size Size
{
get
{
if (_nativeInstance == IntPtr.Zero)
return new Size(_startupParameters.Width, _startupParameters.Height);
var width = 0;
var height = 0;
Invoke(() => Photino_GetSize(_nativeInstance, out width, out height));
return new Size(width, height);
}
set
{
if (Size.Width != value.Width || Size.Height != value.Height)
{
if (_nativeInstance == IntPtr.Zero)
{
_startupParameters.Height = value.Height;
_startupParameters.Width = value.Width;
}
else
Invoke(() => Photino_SetSize(_nativeInstance, value.Width, value.Height));
}
}
}
///
/// Gets or sets platform specific initialization parameters for the native browser control on startup.
/// Default is none.
///WINDOWS: WebView2 specific string. Space separated.
///https://peter.sh/experiments/chromium-command-line-switches/
///https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2environmentoptions.additionalbrowserarguments?view=webview2-dotnet-1.0.1938.49&viewFallbackFrom=webview2-dotnet-1.0.1901.177view%3Dwebview2-1.0.1901.177
///https://www.chromium.org/developers/how-tos/run-chromium-with-flags/
///LINUX: Webkit2Gtk specific string. Enter parameter names and values as JSON string.
///e.g. { "set_enable_encrypted_media": true }
///https://webkitgtk.org/reference/webkit2gtk/2.5.1/WebKitSettings.html
///https://lazka.github.io/pgi-docs/WebKit2-4.0/classes/Settings.html
///MAC: Webkit specific string. Enter parameter names and values as JSON string.
///e.g. { "minimumFontSize": 8 }
///https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc
///https://developer.apple.com/documentation/webkit/wkpreferences?language=objc
///
public string BrowserControlInitParameters
{
get
{
return _startupParameters.BrowserControlInitParameters;
}
set
{
var ss = _startupParameters.BrowserControlInitParameters;
if (string.Compare(ss, value, true) != 0)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.BrowserControlInitParameters = value;
else
throw new ApplicationException($"{nameof(ss)} cannot be changed after Photino Window is initialized");
}
}
}
///
/// Gets or sets an HTML string that the browser control will render when initialized.
/// Default is none.
///
///
/// Either StartString or StartUrl must be specified.
///
///
///
/// Thrown if trying to set value after native window is initalized.
///
public string StartString
{
get
{
return _startupParameters.StartString;
}
set
{
var ss = _startupParameters.StartString;
if (string.Compare(ss, value, true) != 0)
{
if (_nativeInstance != IntPtr.Zero)
throw new ApplicationException($"{nameof(ss)} cannot be changed after Photino Window is initialized");
LoadRawString(value);
}
}
}
///
/// Gets or sets an URL that the browser control will navigate to when initialized.
/// Default is none.
///
///
/// Either StartString or StartUrl must be specified.
///
///
///
/// Thrown if trying to set value after native window is initalized.
///
public string StartUrl
{
get
{
return _startupParameters.StartUrl;
}
set
{
var su = _startupParameters.StartUrl;
if (string.Compare(su, value, true) != 0)
{
if (_nativeInstance != IntPtr.Zero)
throw new ApplicationException($"{nameof(su)} cannot be changed after Photino Window is initialized");
Load(value);
}
}
}
///
/// Gets or sets the local path to store temp files for browser control.
/// Default is the user's AppDataLocal folder.
///
///
/// Only available on Windows.
///
///
/// Thrown if platform is not Windows.
///
public string TemporaryFilesPath
{
get
{
return _startupParameters.TemporaryFilesPath;
}
set
{
var tfp = _startupParameters.TemporaryFilesPath;
if (tfp != value)
{
if (_nativeInstance != IntPtr.Zero)
throw new ApplicationException($"{nameof(tfp)} cannot be changed after Photino Window is initialized");
_startupParameters.TemporaryFilesPath = value;
}
}
}
///
/// Gets or sets the registration Id for doing toast notifications.
/// Default is to use the window title.
///
///
/// Only available on Windows.
///
///
/// Thrown if platform is not Windows.
///
public string NotificationRegistrationId
{
get
{
return _startupParameters.NotificationRegistrationId;
}
set
{
var nri = _startupParameters.NotificationRegistrationId;
if (nri != value)
{
if (_nativeInstance != IntPtr.Zero)
throw new ApplicationException($"{nameof(nri)} cannot be changed after Photino Window is initialized");
_startupParameters.NotificationRegistrationId = value;
}
}
}
///
/// Gets or sets the native window title.
/// Default is "Photino".
///
public string Title
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Title;
var title = string.Empty;
Invoke(() =>
{
var ptr = Photino_GetTitle(_nativeInstance);
title = Marshal.PtrToStringAuto(ptr);
});
return title;
}
set
{
if (Title != value)
{
// Due to Linux/Gtk platform limitations, the window title has to be no more than 31 chars
if (value.Length > 31 && IsLinuxPlatform)
value = value[..31];
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Title = value;
else
Invoke(() => Photino_SetTitle(_nativeInstance, value));
}
}
}
///
/// Gets or sets the native window Top (Y) coordinate in pixels.
/// Default is 0.
///
///
public int Top
{
get => Location.Y;
set
{
if (Location.Y != value)
Location = new Point(Location.X, value);
}
}
///
/// Gets or sets whether the native window is always at the top of the z-order.
/// Default is false.
///
public bool Topmost
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Topmost;
var topmost = false;
Invoke(() => Photino_GetTopmost(_nativeInstance, out topmost));
return topmost;
}
set
{
if (Topmost != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Topmost = value;
else
Invoke(() => Photino_SetTopmost(_nativeInstance, value));
}
}
}
///
/// When true the native window starts up at the OS Default location.
/// Default is true.
///
///
/// Overrides Left (X) and Top (Y) properties.
///
///
/// Thrown if trying to set value after native window is initalized.
///
public bool UseOsDefaultLocation
{
get
{
return _startupParameters.UseOsDefaultLocation;
}
set
{
if (_nativeInstance == IntPtr.Zero)
{
if (UseOsDefaultLocation != value)
_startupParameters.UseOsDefaultLocation = value;
}
else
throw new ApplicationException("UseOsDefaultLocation can only be set before the native window is instantiated.");
}
}
///
/// When true the native window starts at the OS Default size.
/// Default is true.
///
///
/// Overrides Height and Width properties.
///
///
/// Thrown if trying to set value after native window is initalized.
///
public bool UseOsDefaultSize
{
get
{
return _startupParameters.UseOsDefaultSize;
}
set
{
if (_nativeInstance == IntPtr.Zero)
{
if (UseOsDefaultSize != value)
_startupParameters.UseOsDefaultSize = value;
}
else
throw new ApplicationException("UseOsDefaultSize can only be set before the native window is instantiated.");
}
}
///
/// Gets or sets handlers for WebMessageReceived event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WebMessageReceivedHandler
{
get
{
return WebMessageReceived;
}
set
{
WebMessageReceived += value;
}
}
///
/// Gets or Sets the native window width in pixels.
/// Default is 0.
///
///
public int Width
{
get => Size.Width;
set
{
var currentSize = Size;
if (currentSize.Width != value)
Size = new Size(value, currentSize.Height);
}
}
///
/// Gets or sets the handlers for WindowClosing event.
/// Set assigns a new handler to the event.
///
///
public NetClosingDelegate WindowClosingHandler
{
get
{
return WindowClosing;
}
set
{
WindowClosing += value;
}
}
///
/// Gets or sets handlers for WindowCreating event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowCreatingHandler
{
get
{
return WindowCreating;
}
set
{
WindowCreating += value;
}
}
///
/// Gets or sets handlers for WindowCreated event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowCreatedHandler
{
get
{
return WindowCreated;
}
set
{
WindowCreated += value;
}
}
///
/// Gets or sets handlers for WindowLocationChanged event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowLocationChangedHandler
{
get
{
return WindowLocationChanged;
}
set
{
WindowLocationChanged += value;
}
}
///
/// Gets or sets handlers for WindowSizeChanged event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowSizeChangedHandler
{
get
{
return WindowSizeChanged;
}
set
{
WindowSizeChanged += value;
}
}
///
/// Gets or sets handlers for WindowFocusIn event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowFocusInHandler
{
get
{
return WindowFocusIn;
}
set
{
WindowFocusIn += value;
}
}
///
/// Gets or sets handlers for WindowFocusOut event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowFocusOutHandler
{
get
{
return WindowFocusOut;
}
set
{
WindowFocusOut += value;
}
}
///
/// Gets or sets handlers for WindowMaximized event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowMaximizedHandler
{
get
{
return WindowMaximized;
}
set
{
WindowMaximized += value;
}
}
///
/// Gets or sets handlers for WindowRestored event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowRestoredHandler
{
get
{
return WindowRestored;
}
set
{
WindowRestored += value;
}
}
///
/// Gets or sets handlers for WindowMinimized event.
/// Set assigns a new handler to the event.
///
///
public EventHandler WindowMinimizedHandler
{
get
{
return WindowMinimized;
}
set
{
WindowMinimized += value;
}
}
///
/// Gets or sets the native browser control .
/// Default is 100.
///
/// 100 = 100%, 50 = 50%
public int Zoom
{
get
{
if (_nativeInstance == IntPtr.Zero)
return _startupParameters.Zoom;
var zoom = 0;
Invoke(() => Photino_GetZoom(_nativeInstance, out zoom));
return zoom;
}
set
{
if (Zoom != value)
{
if (_nativeInstance == IntPtr.Zero)
_startupParameters.Zoom = value;
else
Invoke(() => Photino_SetZoom(_nativeInstance, value));
}
}
}
///
/// Gets or sets the logging verbosity to standard output (Console/Terminal).
/// 0 = Critical Only
/// 1 = Critical and Warning
/// 2 = Verbose
/// >2 = All Details
/// Default is 2.
///
public int LogVerbosity { get; set; } = 2;
//CONSTRUCTOR
///
/// Initializes a new instance of the PhotinoWindow class.
///
///
/// This class represents a native window with a native browser control taking up the entire client area.
/// If a parent window is specified, this window will be created as a child of the specified parent window.
///
/// The parent PhotinoWindow. This is optional and defaults to null.
public PhotinoWindow(PhotinoWindow parent = null)
{
_dotNetParent = parent;
_managedThreadId = Environment.CurrentManagedThreadId;
//This only has to be done once
if (_nativeType == IntPtr.Zero)
_nativeType = NativeLibrary.GetMainProgramHandle();
//Wire up handlers from C++ to C#
_startupParameters.ClosingHandler = OnWindowClosing;
_startupParameters.ResizedHandler = OnSizeChanged;
_startupParameters.MaximizedHandler = OnMaximized;
_startupParameters.RestoredHandler = OnRestored;
_startupParameters.MinimizedHandler = OnMinimized;
_startupParameters.MovedHandler = OnLocationChanged;
_startupParameters.FocusInHandler = OnFocusIn;
_startupParameters.FocusOutHandler = OnFocusOut;
_startupParameters.WebMessageReceivedHandler = OnWebMessageReceived;
_startupParameters.CustomSchemeHandler = OnCustomScheme;
}
//FLUENT METHODS FOR INITIALIZING STARTUP PARAMETERS FOR NEW WINDOWS
//CAN ALSO BE CALLED AFTER INITIALIZATION TO SET VALUES
//ONE OF THESE 3 METHODS *MUST* BE CALLED PRIOR TO CALLING WAITFORCLOSE() OR CREATECHILDWINDOW()
///
/// Dispatches an Action to the UI thread if called from another thread.
///
///
/// Returns the current instance.
///
/// The delegate encapsulating a method / action to be executed in the UI thread.
public PhotinoWindow Invoke(Action workItem)
{
// If we're already on the UI thread, no need to dispatch
if (Environment.CurrentManagedThreadId == _managedThreadId)
workItem();
else
Photino_Invoke(_nativeInstance, workItem.Invoke);
return this;
}
///
/// Loads a specified into the browser control.
///
///
/// Returns the current instance.
///
///
/// Load() or LoadString() must be called before native window is initialized.
///
/// A Uri pointing to the file or the URL to load.
public PhotinoWindow Load(Uri uri)
{
Log($".Load({uri})");
if (_nativeInstance == IntPtr.Zero)
_startupParameters.StartUrl = uri.ToString();
else
Invoke(() => Photino_NavigateToUrl(_nativeInstance, uri.ToString()));
return this;
}
///
/// Loads a specified path into the browser control.
///
///
/// Returns the current instance.
///
///
/// Load() or LoadString() must be called before native window is initialized.
///
/// A path pointing to the ressource to load.
public PhotinoWindow Load(string path)
{
Log($".Load({path})");
// ––––––––––––––––––––––
// SECURITY RISK!
// This needs validation!
// ––––––––––––––––––––––
// Open a web URL string path
if (path.Contains("http://") || path.Contains("https://"))
return Load(new Uri(path));
// Open a file resource string path
string absolutePath = Path.GetFullPath(path);
// For bundled app it can be necessary to consider
// the app context base directory. Check there too.
if (File.Exists(absolutePath) == false)
{
absolutePath = $"{System.AppContext.BaseDirectory}/{path}";
if (File.Exists(absolutePath) == false)
{
Log($" ** File \"{path}\" could not be found.");
return this;
}
}
return Load(new Uri(absolutePath, UriKind.Absolute));
}
///
/// Loads a raw string into the browser control.
///
///
/// Returns the current instance.
///
///
/// Used to load HTML into the browser control directly.
/// Load() or LoadString() must be called before native window is initialized.
///
/// Raw content (such as HTML)
public PhotinoWindow LoadRawString(string content)
{
var shortContent = content.Length > 50 ? string.Concat(content.AsSpan(0, 50), "...") : content;
Log($".LoadRawString({shortContent})");
if (_nativeInstance == IntPtr.Zero)
_startupParameters.StartString = content;
else
Invoke(() => Photino_NavigateToString(_nativeInstance, content));
return this;
}
///
/// Centers the native window on the primary display.
///
///
/// If called prior to window initialization, overrides Left (X) and Top (Y) properties.
///
///
/// Returns the current instance.
///
///
public PhotinoWindow Center()
{
Log(".Center()");
Centered = true;
return this;
}
///
/// Moves the native window to the specified location on the screen in pixels using a Point.
///
///
/// Returns the current instance.
///
/// Position as
/// Whether the window can go off-screen (work area)
public PhotinoWindow MoveTo(Point location, bool allowOutsideWorkArea = false)
{
Log($".MoveTo({location}, {allowOutsideWorkArea})");
if (LogVerbosity > 2)
{
Log($" Current location: {Location}");
Log($" New location: {location}");
}
// If the window is outside of the work area,
// recalculate the position and continue.
//When window isn't initialized yet, cannot determine screen size.
if (allowOutsideWorkArea == false && _nativeInstance != IntPtr.Zero)
{
int horizontalWindowEdge = location.X + Width;
int verticalWindowEdge = location.Y + Height;
int horizontalWorkAreaEdge = MainMonitor.WorkArea.Width;
int verticalWorkAreaEdge = MainMonitor.WorkArea.Height;
bool isOutsideHorizontalWorkArea = horizontalWindowEdge > horizontalWorkAreaEdge;
bool isOutsideVerticalWorkArea = verticalWindowEdge > verticalWorkAreaEdge;
var locationInsideWorkArea = new Point(
isOutsideHorizontalWorkArea ? horizontalWorkAreaEdge - Width : location.X,
isOutsideVerticalWorkArea ? verticalWorkAreaEdge - Height : location.Y
);
location = locationInsideWorkArea;
}
// Bug:
// For some reason the vertical position is not handled correctly.
// Whenever a positive value is set, the window appears at the
// very bottom of the screen and the only visible thing is the
// application window title bar. As a workaround we make a
// negative value out of the vertical position to "pull" the window up.
// Note:
// This behavior seems to be a macOS thing. In the Photino.Native
// project files it is commented to be expected behavior for macOS.
// There is some code trying to mitigate this problem but it might
// not work as expected. Further investigation is necessary.
// Update:
// This behavior seems to have changed with macOS Sonoma.
// Therefore we determine the version of macOS and only apply the
// workaround for older versions.
if (IsMacOsPlatform && MacOsVersion.Major < 23)
{
var workArea = MainMonitor.WorkArea.Size;
location.Y = location.Y >= 0
? location.Y - workArea.Height
: location.Y;
}
Location = location;
return this;
}
///
/// Moves the native window to the specified location on the screen in pixels
/// using (X) and (Y) properties.
///
///
/// Returns the current instance.
///
/// Position from left in pixels
/// Position from top in pixels
/// Whether the window can go off-screen (work area)
public PhotinoWindow MoveTo(int left, int top, bool allowOutsideWorkArea = false)
{
Log($".MoveTo({left}, {top}, {allowOutsideWorkArea})");
return MoveTo(new Point(left, top), allowOutsideWorkArea);
}
///
/// Moves the native window relative to its current location on the screen
/// using a .
///
///
/// Returns the current instance.
///
/// Relative offset
public PhotinoWindow Offset(Point offset)
{
Log($".Offset({offset})");
var location = Location;
int left = location.X + offset.X;
int top = location.Y + offset.Y;
return MoveTo(left, top);
}
///
/// Moves the native window relative to its current location on the screen in pixels
/// using (X) and (Y) properties.
///
///
/// Returns the current instance.
///
/// Relative offset from left in pixels
/// Relative offset from top in pixels
public PhotinoWindow Offset(int left, int top)
{
Log($".Offset({left}, {top})");
return Offset(new Point(left, top));
}
///
/// When true, the native window will appear without a title bar or border.
/// By default, this is set to false.
///
///
/// The user has to supply titlebar, border, dragging and resizing manually.
///
///
/// Returns the current instance.
///
/// Whether the window should be chromeless
public PhotinoWindow SetChromeless(bool chromeless)
{
Log($".SetChromeless({chromeless})");
if (_nativeInstance != IntPtr.Zero)
throw new ApplicationException("Chromeless can only be set before the native window is instantiated.");
_startupParameters.Chromeless = chromeless;
return this;
}
///
/// When true, the native window can be displayed with transparent background.
/// Chromeless must be set to true. Html document's body background must have alpha-based value.
/// By default, this is set to false.
///
public PhotinoWindow SetTransparent(bool enabled)
{
Log($".SetTransparent({enabled})");
Transparent = enabled;
return this;
}
///
/// When true, the user can access the browser control's context menu.
/// By default, this is set to true.
///
///
/// Returns the current instance.
///
/// Whether the context menu should be available
public PhotinoWindow SetContextMenuEnabled(bool enabled)
{
Log($".SetContextMenuEnabled({enabled})");
ContextMenuEnabled = enabled;
return this;
}
///
/// When true, the user can access the browser control's developer tools.
/// By default, this is set to true.
///
///
/// Returns the current instance.
///
/// Whether developer tools should be available
public PhotinoWindow SetDevToolsEnabled(bool enabled)
{
Log($".SetDevTools({enabled})");
DevToolsEnabled = enabled;
return this;
}
///
/// When set to true, the native window will cover the entire screen, similar to kiosk mode.
/// By default, this is set to false.
///
///
/// Returns the current instance.
///
/// Whether the window should be fullscreen
public PhotinoWindow SetFullScreen(bool fullScreen)
{
Log($".SetFullScreen({fullScreen})");
FullScreen = fullScreen;
return this;
}
///
/// When set to true, the native browser control grants all requests for access to local resources
/// such as the users camera and microphone. By default, this is set to true.
///
///
/// This only works on Windows.
///
///
/// Returns the current instance.
///
/// Whether permissions should be automatically granted.
public PhotinoWindow SetGrantBrowserPermissions(bool grant)
{
Log($".SetGrantBrowserPermission({grant})");
GrantBrowserPermissions = grant;
return this;
}
///
/// Sets . Sets the user agent on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetUserAgent(string userAgent)
{
Log($".SetUserAgent({userAgent})");
UserAgent = userAgent;
return this;
}
///
/// Sets platform specific initialization parameters for the native browser control on startup.
/// Default is none.
///
/// WINDOWS: WebView2 specific string. Space separated.
/// https://peter.sh/experiments/chromium-command-line-switches/
/// https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2environmentoptions.additionalbrowserarguments?view=webview2-dotnet-1.0.1938.49&viewFallbackFrom=webview2-dotnet-1.0.1901.177view%3Dwebview2-1.0.1901.177
/// https://www.chromium.org/developers/how-tos/run-chromium-with-flags/
/// LINUX: Webkit2Gtk specific string. Enter parameter names and values as JSON string.
/// e.g. { "set_enable_encrypted_media": true }
/// https://webkitgtk.org/reference/webkit2gtk/2.5.1/WebKitSettings.html
/// https://lazka.github.io/pgi-docs/WebKit2-4.0/classes/Settings.html
/// MAC: Webkit specific string. Enter parameter names and values as JSON string.
/// e.g. { "minimumFontSize": 8 }
/// https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc
/// https://developer.apple.com/documentation/webkit/wkpreferences?language=objc
///
///
/// Returns the current instance.
///
public PhotinoWindow SetBrowserControlInitParameters(string parameters)
{
Log($".SetBrowserControlInitParameters({parameters})");
BrowserControlInitParameters = parameters;
return this;
}
///
/// Sets the registration id for toast notifications.
///
///
/// Only available on Windows.
/// Defaults to window title if not specified.
///
///
/// Thrown if platform is not Windows.
///
///
/// Returns the current instance.
public PhotinoWindow SetNotificationRegistrationId(string notificationRegistrationId)
{
Log($".SetNotificationRegistrationId({notificationRegistrationId})");
NotificationRegistrationId = notificationRegistrationId;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetMediaAutoplayEnabled(bool enable)
{
Log($".SetMediaAutoplayEnabled({enable})");
MediaAutoplayEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetFileSystemAccessEnabled(bool enable)
{
Log($".SetFileSystemAccessEnabled({enable})");
FileSystemAccessEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetWebSecurityEnabled(bool enable)
{
Log($".SetWebSecurityEnabled({enable})");
WebSecurityEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetJavascriptClipboardAccessEnabled(bool enable)
{
Log($".SetJavascriptClipboardAccessEnabled({enable})");
JavascriptClipboardAccessEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetMediaStreamEnabled(bool enable)
{
Log($".SetMediaStreamEnabled({enable})");
MediaStreamEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetSmoothScrollingEnabled(bool enable)
{
Log($".SetSmoothScrollingEnabled({enable})");
SmoothScrollingEnabled = enable;
return this;
}
///
/// Sets on the browser control at initialization.
///
///
/// Returns the current instance.
public PhotinoWindow SetIgnoreCertificateErrorsEnabled(bool enable)
{
Log($".SetIgnoreCertificateErrorsEnabled({enable})");
IgnoreCertificateErrorsEnabled = enable;
return this;
}
///
/// Sets whether ShowNotification() can be called.
///
///
/// Only available on Windows.
///
///
/// Thrown if platform is not Windows.
///
///
/// Returns the current instance.
public PhotinoWindow SetNotificationsEnabled(bool enable)
{
Log($".SetNotificationsEnabled({enable})");
NotificationsEnabled = enable;
return this;
}
///
/// Sets the native window in pixels.
/// Default is 0.
///
///
/// Returns the current instance.
///
///
/// Height in pixels
public PhotinoWindow SetHeight(int height)
{
Log($".SetHeight({height})");
Height = height;
return this;
}
///
/// Sets the icon file for the native window title bar.
/// The file must be located on the local machine and cannot be a URL. The default is none.
///
///
/// This only works on Windows and Linux.
///
///
/// Returns the current instance.
///
/// Icon file: {value} does not exist.
/// The file path to the icon.
public PhotinoWindow SetIconFile(string iconFile)
{
Log($".SetIconFile({iconFile})");
IconFile = iconFile;
return this;
}
///
/// Sets the native window to a new (X) coordinate in pixels.
/// Default is 0.
///
///
///
/// Returns the current instance.
///
/// Position in pixels from the left (X).
public PhotinoWindow SetLeft(int left)
{
Log($".SetLeft({Left})");
Left = left;
return this;
}
///
/// Sets whether the native window can be resized by the user.
/// Default is true.
///
///
/// Returns the current instance.
///
/// Whether the window is resizable
public PhotinoWindow SetResizable(bool resizable)
{
Log($".SetResizable({resizable})");
Resizable = resizable;
return this;
}
///
/// Sets the native window Size. This represents the and the of the window in pixels.
/// The default Size is 0,0.
///
///
///
/// Returns the current instance.
///
/// Width & Height
public PhotinoWindow SetSize(Size size)
{
Log($".SetSize({size})");
Size = size;
return this;
}
///
/// Sets the native window Size. This represents the and the of the window in pixels.
/// The default Size is 0,0.
///
///
///
/// Returns the current instance.
///
/// Width in pixels
/// Height in pixels
public PhotinoWindow SetSize(int width, int height)
{
Log($".SetSize({width}, {height})");
Size = new Size(width, height);
return this;
}
///
/// Sets the native window (X) and coordinates (Y) in pixels.
/// Default is 0,0 which means the window will be aligned to the top left edge of the screen.
///
///
///
/// Returns the current instance.
///
/// Location as a
public PhotinoWindow SetLocation(Point location)
{
Log($".SetLocation({location})");
Location = location;
return this;
}
///
/// Sets the logging verbosity to standard output (Console/Terminal).
/// 0 = Critical Only
/// 1 = Critical and Warning
/// 2 = Verbose
/// >2 = All Details
/// Default is 2.
///
///
/// Returns the current instance.
///
/// Verbosity as integer
public PhotinoWindow SetLogVerbosity(int verbosity)
{
Log($".SetLogVerbosity({verbosity})");
LogVerbosity = verbosity;
return this;
}
///
/// Sets whether the native window is maximized.
/// Default is false.
///
///
/// Returns the current instance.
///
/// Whether the window should be maximized.
public PhotinoWindow SetMaximized(bool maximized)
{
Log($".SetMaximized({maximized})");
Maximized = maximized;
return this;
}
///Native window maximum Width and Height in pixels.
public PhotinoWindow SetMaxSize(int maxWidth, int maxHeight)
{
Log($".SetMaxSize({maxWidth}, {maxHeight})");
MaxSize = new Point(maxWidth, maxHeight);
return this;
}
///Native window maximum Height in pixels.
public PhotinoWindow SetMaxHeight(int maxHeight)
{
Log($".SetMaxHeight({maxHeight})");
MaxHeight = maxHeight;
return this;
}
///Native window maximum Width in pixels.
public PhotinoWindow SetMaxWidth(int maxWidth)
{
Log($".SetMaxWidth({maxWidth})");
MaxWidth = maxWidth;
return this;
}
///
/// Sets whether the native window is minimized (hidden).
/// Default is false.
///
///
/// Returns the current instance.
///
/// Whether the window should be minimized.
public PhotinoWindow SetMinimized(bool minimized)
{
Log($".SetMinimized({minimized})");
Minimized = minimized;
return this;
}
///Native window maximum Width and Height in pixels.
public PhotinoWindow SetMinSize(int minWidth, int minHeight)
{
Log($".SetMinSize({minWidth}, {minHeight})");
MinSize = new Point(minWidth, minHeight);
return this;
}
///Native window maximum Height in pixels.
public PhotinoWindow SetMinHeight(int minHeight)
{
Log($".SetMinHeight({minHeight})");
MinHeight = minHeight;
return this;
}
///Native window maximum Width in pixels.
public PhotinoWindow SetMinWidth(int minWidth)
{
Log($".SetMinWidth({minWidth})");
MinWidth = minWidth;
return this;
}
///
/// Sets the local path to store temp files for browser control.
/// Default is the user's AppDataLocal folder.
///
///
/// Only available on Windows.
///
///
/// Thrown if platform is not Windows.
///
///
/// Returns the current instance.
///
/// Path to temp files directory.
public PhotinoWindow SetTemporaryFilesPath(string tempFilesPath)
{
Log($".SetTemporaryFilesPath({tempFilesPath})");
TemporaryFilesPath = tempFilesPath;
return this;
}
///
/// Sets the native window .
/// Default is "Photino".
///
///
/// Returns the current instance.
///
/// Window title
public PhotinoWindow SetTitle(string title)
{
Log($".SetTitle({title})");
Title = title;
return this;
}
///
/// Sets the native window (Y) coordinate in pixels.
/// Default is 0.
///
///
/// Returns the current instance.
///
///
/// Position in pixels from the top (Y).
public PhotinoWindow SetTop(int top)
{
Log($".SetTop({top})");
Top = top;
return this;
}
///
/// Sets whether the native window is always at the top of the z-order.
/// Default is false.
///
///
/// Returns the current instance.
///
/// Whether the window is at the top
public PhotinoWindow SetTopMost(bool topMost)
{
Log($".SetTopMost({topMost})");
Topmost = topMost;
return this;
}
///
/// Sets the native window width in pixels.
/// Default is 0.
///
///
/// Returns the current instance.
///
///
/// Width in pixels
public PhotinoWindow SetWidth(int width)
{
Log($".SetWidth({width})");
Width = width;
return this;
}
///
/// Sets the native browser control .
/// Default is 100.
///
///
/// Returns the current instance.
///
/// Zoomlevel as integer
/// 100 = 100%, 50 = 50%
public PhotinoWindow SetZoom(int zoom)
{
Log($".SetZoom({zoom})");
Zoom = zoom;
return this;
}
///
/// When true the native window starts up at the OS Default location.
/// Default is true.
///
///
/// Overrides (X) and (Y) properties.
///
///
/// Returns the current instance.
///
/// Whether the OS Default should be used.
public PhotinoWindow SetUseOsDefaultLocation(bool useOsDefault)
{
Log($".SetUseOsDefaultLocation({useOsDefault})");
UseOsDefaultLocation = useOsDefault;
return this;
}
///
/// When true the native window starts at the OS Default size.
/// Default is true.
///
///
/// Overrides and properties.
///
///
/// Returns the current instance.
///
/// Whether the OS Default should be used.
public PhotinoWindow SetUseOsDefaultSize(bool useOsDefault)
{
Log($".SetUseOsDefaultSize({useOsDefault})");
UseOsDefaultSize = useOsDefault;
return this;
}
///
/// Set runtime path for WebView2 so that developers can use Photino on Windows using the "Fixed Version" deployment module of the WebView2 runtime.
///
///
/// This only works on Windows.
///
///
/// Returns the current instance.
///
///
/// Runtime path for WebView2
public PhotinoWindow Win32SetWebView2Path(string data)
{
if (IsWindowsPlatform)
Invoke(() => Photino_setWebView2RuntimePath_win32(_nativeType, data));
else
Log("Win32SetWebView2Path is only supported on the Windows platform");
return this;
}
///
/// Clears the auto-fill data in the browser control.
///
///
/// This method is only supported on the Windows platform.
///
///
/// Returns the current instance.
///
public PhotinoWindow ClearBrowserAutoFill()
{
if (IsWindowsPlatform)
Invoke(() => Photino_ClearBrowserAutoFill(_nativeInstance));
else
Log("ClearBrowserAutoFill is only supported on the Windows platform");
return this;
}
//NON-FLUENT METHODS - CAN ONLY BE CALLED AFTER WINDOW IS INITIALIZED
//ONE OF THESE 2 METHODS *MUST* BE CALLED TO CREATE THE WINDOW
///
/// Responsible for the initialization of the primary native window and remains in operation until the window is closed.
/// This method is also applicable for initializing child windows, but in this case, it does not inhibit operation.
///
///
/// The operation of the message loop is exclusive to the main native window only.
///
public void WaitForClose()
{
//fill in the fixed size array of custom scheme names
var i = 0;
foreach (var name in CustomSchemes.Take(16))
{
_startupParameters.CustomSchemeNames[i] = name.Key;
i++;
}
_startupParameters.NativeParent = _dotNetParent == null
? IntPtr.Zero
: _dotNetParent._nativeInstance;
var errors = _startupParameters.GetParamErrors();
if (errors.Count == 0)
{
OnWindowCreating();
try //All C++ exceptions will bubble up to here.
{
_nativeType = NativeLibrary.GetMainProgramHandle();
if (IsWindowsPlatform)
Invoke(() => Photino_register_win32(_nativeType));
else if (IsMacOsPlatform)
Invoke(() => Photino_register_mac());
Invoke(() => _nativeInstance = Photino_ctor(ref _startupParameters));
}
catch (Exception ex)
{
int lastError = 0;
if (IsWindowsPlatform)
lastError = Marshal.GetLastWin32Error();
Log($"***\n{ex.Message}\n{ex.StackTrace}\nError #{lastError}");
throw new ApplicationException($"Native code exception. Error # {lastError} See inner exception for details.", ex);
}
OnWindowCreated();
if (!_messageLoopIsStarted)
{
_messageLoopIsStarted = true;
try
{
Invoke(() => Photino_WaitForExit(_nativeInstance)); //start the message loop. there can only be 1 message loop for all windows.
}
catch (Exception ex)
{
int lastError = 0;
if (IsWindowsPlatform)
lastError = Marshal.GetLastWin32Error();
Log($"***\n{ex.Message}\n{ex.StackTrace}\nError #{lastError}");
throw new ApplicationException($"Native code exception. Error # {lastError} See inner exception for details.", ex);
}
}
}
else
{
var formattedErrors = "\n";
foreach (var error in errors)
formattedErrors += error + "\n";
throw new ArgumentException($"Startup Parameters Are Not Valid: {formattedErrors}");
}
}
///
/// Closes the native window.
///
///
/// Thrown when the window is not initialized.
///
public void Close()
{
Log(".Close()");
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("Close cannot be called until after the Photino window is initialized.");
Invoke(() => Photino_Close(_nativeInstance));
}
///
/// Send a message to the native window's native browser control's JavaScript context.
///
///
/// In JavaScript, messages can be received via window.external.receiveMessage(message)
///
///
/// Thrown when the window is not initialized.
///
/// Message as string
public void SendWebMessage(string message)
{
Log($".SendWebMessage({message})");
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("SendWebMessage cannot be called until after the Photino window is initialized.");
Invoke(() => Photino_SendWebMessage(_nativeInstance, message));
}
public async Task SendWebMessageAsync(string message)
{
await Task.Run(() =>
{
Log($".SendWebMessage({message})");
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("SendWebMessage cannot be called until after the Photino window is initialized.");
Invoke(() => Photino_SendWebMessage(_nativeInstance, message));
});
}
///
/// Sends a native notification to the OS.
/// Sometimes referred to as Toast notifications.
///
///
/// Thrown when the window is not initialized.
///
/// The title of the notification
/// The text of the notification
public void SendNotification(string title, string body)
{
Log($".SendNotification({title}, {body})");
if (_nativeInstance == IntPtr.Zero)
throw new ApplicationException("SendNotification cannot be called until after the Photino window is initialized.");
Invoke(() => Photino_ShowNotification(_nativeInstance, title, body));
}
///
/// Show an open file dialog native to the OS.
///
///
/// Filter names are not used on macOS. Use async version for Photino.Blazor as syncronous version crashes.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Whether multiple selections are allowed
/// Array of for filtering.
/// Array of file paths as strings
public string[] ShowOpenFile(string title = "Choose file", string defaultPath = null, bool multiSelect = false, (string Name, string[] Extensions)[] filters = null) => ShowOpenDialog(false, title, defaultPath, multiSelect, filters);
///
/// Async version is required for Photino.Blazor
///
///
/// Filter names are not used on macOS. Use async version for Photino.Blazor as syncronous version crashes.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Whether multiple selections are allowed
/// Array of for filtering.
/// Array of file paths as strings
public async Task ShowOpenFileAsync(string title = "Choose file", string defaultPath = null, bool multiSelect = false, (string Name, string[] Extensions)[] filters = null)
{
return await Task.Run(() => ShowOpenFile(title, defaultPath, multiSelect, filters));
}
///
/// Show an open folder dialog native to the OS.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Whether multiple selections are allowed
/// Array of folder paths as strings
public string[] ShowOpenFolder(string title = "Select folder", string defaultPath = null, bool multiSelect = false) => ShowOpenDialog(true, title, defaultPath, multiSelect, null);
///
/// Async version is required for Photino.Blazor
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Whether multiple selections are allowed
/// Array of folder paths as strings
public async Task ShowOpenFolderAsync(string title = "Choose file", string defaultPath = null, bool multiSelect = false)
{
return await Task.Run(() => ShowOpenFolder(title, defaultPath, multiSelect));
}
///
/// Show an save folder dialog native to the OS.
///
///
/// Filter names are not used on macOS.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Array of for filtering.
///
public string ShowSaveFile(string title = "Save file", string defaultPath = null, (string Name, string[] Extensions)[] filters = null)
{
defaultPath ??= Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
filters ??= Array.Empty<(string, string[])>();
string result = null;
var nativeFilters = GetNativeFilters(filters);
Invoke(() =>
{
var ptrResult = Photino_ShowSaveFile(_nativeInstance, title, defaultPath, nativeFilters, filters.Length);
result = Marshal.PtrToStringAuto(ptrResult);
});
return result;
}
///
/// Async version is required for Photino.Blazor
///
///
/// Filter names are not used on macOS.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Default path. Defaults to
/// Array of for filtering.
///
public async Task ShowSaveFileAsync(string title = "Choose file", string defaultPath = null, (string Name, string[] Extensions)[] filters = null)
{
return await Task.Run(() => ShowSaveFile(title, defaultPath, filters));
}
///
/// Show a message dialog native to the OS.
///
///
/// Thrown when the window is not initialized.
///
/// Title of the dialog
/// Text of the dialog
/// Available interaction buttons
/// Icon of the dialog
///
public PhotinoDialogResult ShowMessage(string title, string text, PhotinoDialogButtons buttons = PhotinoDialogButtons.Ok, PhotinoDialogIcon icon = PhotinoDialogIcon.Info)
{
var result = PhotinoDialogResult.Cancel;
Invoke(() => result = Photino_ShowMessage(_nativeInstance, title, text, buttons, icon));
return result;
}
///
/// Show a native open dialog.
///
/// Whether files are hidden
/// Title of the dialog
/// Default path. Defaults to
/// Whether multiple selections are allowed
/// Array of for filtering.
/// Array of paths
private string[] ShowOpenDialog(bool foldersOnly, string title, string defaultPath, bool multiSelect, (string Name, string[] Extensions)[] filters)
{
defaultPath ??= Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
filters ??= Array.Empty<(string, string[])>();
var results = Array.Empty();
var nativeFilters = GetNativeFilters(filters, foldersOnly);
Invoke(() =>
{
var ptrResults = foldersOnly ?
Photino_ShowOpenFolder(_nativeInstance, title, defaultPath, multiSelect, out var resultCount) :
Photino_ShowOpenFile(_nativeInstance, title, defaultPath, multiSelect, nativeFilters, nativeFilters.Length, out resultCount);
if (resultCount == 0) return;
var ptrArray = new IntPtr[resultCount];
results = new string[resultCount];
Marshal.Copy(ptrResults, ptrArray, 0, resultCount);
for (var i = 0; i < resultCount; i++)
{
results[i] = Marshal.PtrToStringAuto(ptrArray[i]);
}
});
return results;
}
///
/// Logs a message.
///
/// Log message
private void Log(string message)
{
if (LogVerbosity < 1) return;
Console.WriteLine($"Photino.NET: \"{Title ?? "PhotinoWindow"}\"{message}");
}
///
/// Returns an array of strings for native filters
///
///
///
/// String array of filters
private static string[] GetNativeFilters((string Name, string[] Extensions)[] filters, bool empty = false)
{
var nativeFilters = Array.Empty();
if (!empty && filters is { Length: > 0 })
{
nativeFilters = IsMacOsPlatform ?
filters.SelectMany(t => t.Extensions.Select(s => s == "*" ? s : s.TrimStart('*', '.'))).ToArray() :
filters.Select(t => $"{t.Name}|{t.Extensions.Select(s => s.StartsWith('.') ? $"*{s}" : !s.StartsWith("*.") ? $"*.{s}" : s).Aggregate((e1, e2) => $"{e1};{e2}")}").ToArray();
}
return nativeFilters;
}
}