SteveSandersonMS / WebWindow

.NET Core library to open native OS windows containing web UI on Windows, Mac, and Linux. Experimental.
Apache License 2.0
1.99k stars 215 forks source link

Full screen and maximize support #73

Open westbrma opened 4 years ago

Jinjinov commented 4 years ago

WebWindow.h

WebWindow(AutoString title, WebWindow* parent, WebMessageReceivedCallback webMessageReceivedCallback, bool fullscreen, int x, int y, int width, int height);

WebWindow.Windows.cpp

WebWindow::WebWindow(AutoString title, WebWindow* parent, WebMessageReceivedCallback webMessageReceivedCallback, bool fullscreen, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, int width = CW_USEDEFAULT, int height = CW_USEDEFAULT)
{
    // Create the window
    _webMessageReceivedCallback = webMessageReceivedCallback;
    _parent = parent;

    if (fullscreen)
    {
        x = 0;
        y = 0;
        width = GetSystemMetrics(SM_CXSCREEN);
        height = GetSystemMetrics(SM_CYSCREEN);
    }

    _hWnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        title,                          // Window text
        fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW,    // Window style

        // Size and position
        x, y, width, height,

        parent ? parent->_hWnd : NULL,       // Parent window
        NULL,       // Menu
        _hInstance, // Instance handle
        this        // Additional application data
    );
    hwndToWebWindow[_hWnd] = this;
}

WebWindow.Linux.cpp

WebWindow::WebWindow(AutoString title, WebWindow* parent, WebMessageReceivedCallback webMessageReceivedCallback, bool fullscreen, int x, int y, int width, int height) : _webview(nullptr)
{
    _webMessageReceivedCallback = webMessageReceivedCallback;

    // It makes xlib thread safe.
    // Needed for get_position.
    XInitThreads();

    gtk_init(0, NULL);
    _window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    if (fullscreen)
    {
        GdkRectangle geometry = { 0 };
        gdk_monitor_get_geometry(gdk_display_get_primary_monitor(gdk_display_get_default()), &geometry);

        x = 0;
        y = 0;
        width = geometry.width;
        height = geometry.height;

        gtk_window_fullscreen(GTK_WINDOW(_window));
    }

    gtk_window_move(GTK_WINDOW(_window), x, y);
    gtk_window_set_default_size(GTK_WINDOW(_window), width, height);
    SetTitle(title);

    if (parent == NULL)
    {
        g_signal_connect(G_OBJECT(_window), "destroy",
            G_CALLBACK(+[](GtkWidget* w, gpointer arg) { gtk_main_quit(); }),
            this);
        g_signal_connect(G_OBJECT(_window), "size-allocate",
            G_CALLBACK(on_size_allocate),
            this);
        g_signal_connect(G_OBJECT(_window), "configure-event",
            G_CALLBACK(on_configure_event),
            this);
    }
}

Exports.cpp

EXPORTED WebWindow* WebWindow_ctor(AutoString title, WebWindow* parent, WebMessageReceivedCallback webMessageReceivedCallback, bool fullscreen, int x, int y, int width, int height)
{
    return new WebWindow(title, parent, webMessageReceivedCallback, fullscreen, x, y, width, height);
}

WebWindow.cs

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)] static extern IntPtr WebWindow_ctor(string title, IntPtr parentWebWindow, OnWebMessageReceivedCallback webMessageReceivedCallback, bool fullscreen, int x, int y, int width, int height);

    public WebWindow(string title, Action<WebWindowOptions> configure, bool fullscreen = false, int x = 0, int y = 0, int width = 800, int height = 600)
    {
        _ownerThreadId = Thread.CurrentThread.ManagedThreadId;

        if (configure is null)
        {
            throw new ArgumentNullException(nameof(configure));
        }

        var options = new WebWindowOptions();
        configure.Invoke(options);

        WriteTitleField(title);

        var onWebMessageReceivedDelegate = (OnWebMessageReceivedCallback)ReceiveWebMessage;
        _gcHandlesToFree.Add(GCHandle.Alloc(onWebMessageReceivedDelegate));

        var parentPtr = options.Parent?._nativeWebWindow ?? default;
        _nativeWebWindow = WebWindow_ctor(_title, parentPtr, onWebMessageReceivedDelegate, fullscreen, x, y, width, height);

        foreach (var (schemeName, handler) in options.SchemeHandlers)
        {
            AddCustomScheme(schemeName, handler);
        }

        var onResizedDelegate = (ResizedCallback)OnResized;
        _gcHandlesToFree.Add(GCHandle.Alloc(onResizedDelegate));
        WebWindow_SetResizedCallback(_nativeWebWindow, onResizedDelegate);

        var onMovedDelegate = (MovedCallback)OnMoved;
        _gcHandlesToFree.Add(GCHandle.Alloc(onMovedDelegate));
        WebWindow_SetMovedCallback(_nativeWebWindow, onMovedDelegate);

        // Auto-show to simplify the API, but more importantly because you can't
        // do things like navigate until it has been shown
        Show();
    }

ComponentsDesktop.cs

    public static void Run<TStartup>(string windowTitle, string hostHtmlPath, bool fullscreen = false, int x = 0, int y = 0, int width = 800, int height = 600)
    {
        DesktopSynchronizationContext.UnhandledException += (sender, exception) =>
        {
            UnhandledException(exception);
        };

        WebWindow = new WebWindow(windowTitle, options =>
        {
            var contentRootAbsolute = Path.GetDirectoryName(Path.GetFullPath(hostHtmlPath));

            options.SchemeHandlers.Add(BlazorAppScheme, (string url, out string contentType) =>
            {
                // TODO: Only intercept for the hostname 'app' and passthrough for others
                // TODO: Prevent directory traversal?
                var appFile = Path.Combine(contentRootAbsolute, new Uri(url).AbsolutePath.Substring(1));
                if (appFile == contentRootAbsolute)
                {
                    appFile = hostHtmlPath;
                }

                contentType = GetContentType(appFile);
                return File.Exists(appFile) ? File.OpenRead(appFile) : null;
            });

            // framework:// is resolved as embedded resources
            options.SchemeHandlers.Add("framework", (string url, out string contentType) =>
            {
                contentType = GetContentType(url);
                return SupplyFrameworkFile(url);
            });
        }, fullscreen, x, y, width, height);

        CancellationTokenSource appLifetimeCts = new CancellationTokenSource();
        Task.Factory.StartNew(async () =>
        {
            try
            {
                var ipc = new IPC(WebWindow);
                await RunAsync<TStartup>(ipc, appLifetimeCts.Token);
            }
            catch (Exception ex)
            {
                UnhandledException(ex);
                throw;
            }
        });

        try
        {
            WebWindow.NavigateToUrl(BlazorAppScheme + "://app/");
            WebWindow.WaitForExit();
        }
        finally
        {
            appLifetimeCts.Cancel();
        }
    }
dima117 commented 3 years ago

What is the status of this issue?