web-view icon indicating copy to clipboard operation
web-view copied to clipboard

Frameless Mode has some minor problems.

Open FrankenApps opened this issue 4 years ago • 25 comments

Thanks for adding frameless mode. It is what I need in order to use my own custom title bar. Unfortunately for that use case the window should also be resizeable. I tested some configurations on Windows 10 and only found the EDGE (non-resizable) version to provide the expected result. I used the following code for testing:

use web_view::*;

fn main() {
    web_view::builder()
        .content(Content::Html(HTML))
        .size(150, 150)
        .frameless(true)
        .debug(true)
        .resizable(false)
        .user_data("")
        .invoke_handler(|webview, arg| {
            match arg {
                "exit" => webview.exit(),
                _ => (),
            }
            Ok(())
        })
        .run()
        .unwrap();
}

const HTML: &str = r#"
<!doctype html>
<html>
	<body style="background-color:#00dd00">
        <button onclick="external.invoke('exit')" style="position:absolute;top:0px;left:0px;height:30px;width:100%">exit</button>
	</body>
</html>
"#;

These are the results: bug_report

FrankenApps avatar Mar 15 '20 11:03 FrankenApps

Thanks for the report and provided pictures, I am able to reproduce this. Will look into this. cc @FreeMasen might know more

richardhozak avatar Mar 15 '20 11:03 richardhozak

Thanks for looking into that. While starting to build a custom title bar, I stumbled over another problem. It is totally possible to maximize and restore the window with webview.set_fullscreen(true) and webview.set_fullscreen(false), but I have not found a way, to minimize the window. Maybe, this could also be implemented if it isn't yet.

FrankenApps avatar Mar 22 '20 10:03 FrankenApps

I have implemented maximize and minimize(cocoa pending), and also got drag-able window working on edge/mshtml.

Currently I call drag_intent on mousedown event which moves the window according to the amount the mouse has moved and would do nothing if drag_intent is called when mouse is not held, but a better way would be to expose methods to modify hittest region values for caption bar(Titlebar), and resize frames. This would allow the ui to handle the regions for window borders.

Blakeinstein avatar Jul 14 '20 13:07 Blakeinstein

Yes, draggable areas would definitely be needed, because there might be other drag behaviour inside the window (for example panning an embedded map). However I am not so sure, if the user should implement their own window borders for resize. I think these should be provided by the API, when frameless and resizable are set to true. I think Electron did a great job with their frameless window, so that might be something to look into. One can specify the drag region very conveniently like this

.titlebar {
  -webkit-user-select: none;
  -webkit-app-region: drag;
}

and the resize borders are native in Electron.

FrankenApps avatar Jul 15 '20 05:07 FrankenApps

Well, winapi has nchhittest result that defines what section is what, and supports doing math on window size.

I am unsure if its possible to maintain this capability cross platform.

Blakeinstein avatar Jul 15 '20 13:07 Blakeinstein

The above problem is caused most likely because to achieve frameless window we are still using WS_OVERLAPPEDWINDOW. Instead we should use WS_POPUP, as demonstrated here https://github.com/melak47/BorderlessWindow/blob/master/BorderlessWindow/src/BorderlessWindow.cpp

edit: This works!

if (frameless) {
    style &= WS_POPUP;
}

Now all you need is a NChittest in wndproc to return the box borders for resizing. This will also enable a user to define custom titlebar.

I got this effect by adding a dlgframe flag as,

if (frameless) {
	style &= (WS_POPUP | WS_DLGFRAME);
}

Then from the reference, adding a similar hit test function as part of browser_window

auto hit_test(POINT cursor, HWND hwnd) const -> LRESULT {
        const POINT border{
            ::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER),
            ::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)
        };
        RECT window;
        if (!::GetWindowRect(hwnd, &window)) {
            return HTNOWHERE;
        }

        const auto drag = true ? HTCAPTION : HTCLIENT;

        enum region_mask {
            client = 0b0000,
            left   = 0b0001,
            right  = 0b0010,
            top    = 0b0100,
            bottom = 0b1000,
        };

        const auto result =
            left    * (cursor.x <  (window.left   + border.x)) |
            right   * (cursor.x >= (window.right  - border.x)) |
            top     * (cursor.y <  (window.top    + border.y)) |
            bottom  * (cursor.y >= (window.bottom - border.y));

        switch (result) {
            case left          : return HTLEFT       ;
            case right         : return HTRIGHT      ;
            case top           : return HTTOP        ;
            case bottom        : return HTBOTTOM     ;
            case top | left    : return HTTOPLEFT    ;
            case top | right   : return HTTOPRIGHT   ;
            case bottom | left : return HTBOTTOMLEFT ;
            case bottom | right: return HTBOTTOMRIGHT;
            case client        : return drag;
            default            : return HTNOWHERE;
        }
    }

then updating WebviewWndProc as,

LRESULT return_val;
    auto w = reinterpret_cast<browser_window*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
    auto& window = *w;
    ...
    case WM_NCHITTEST:
        return window.hit_test(POINT{
                        LOWORD(lp),
                        HIWORD(lp)
                    }, hwnd);
	...

The results should be live at my fork https://github.com/Blakeinstein/web-view/ and can be tested by running the project I am working on, which is now finally public, https://github.com/Blakeinstein/Bloop. Ignore missing fonts.

Edit: One final note, I forgot to mention returning HTCAPTION does nothing apparently.

Blakeinstein avatar Jul 16 '20 07:07 Blakeinstein

Since I am very interested in that feature, I went ahead and tested your linked project.

I am impressed, how you managed to handle the drag event in the title bar.

However I still experience the white border on top of the edge window... sc

Btw. when I maximize the window, it is not fully maximized (e.g. there is some space on the edges, where the window underneath is visible) and it is possible to drag the window in maximized mode, without the window collapsing...

I have played around with WS_POPUP in the past, but wasn't able to get rid of the white border on top. According to this post that is not a bug. But I do personally think, that it is rather distracting for the user, especially when the windows' background is black or dark.

FrankenApps avatar Jul 16 '20 10:07 FrankenApps

I apologize, due to broken web technologies the editor is not fully functional yet, but as far as that white border is concerned, one is stuck with it in windows 10. As for the maximize, I am not quite sure how it is supposed to be achieved in winapi tbh. I try to set the window to size of the available screen. Moreover I think maximized windows should still be able to move. I can set a call to unmaximize? once a drag intent is initiated....

I have been trying to debug that white border for the past hour or so.... and ran through quite a few questions on stackoverflow....

Blakeinstein avatar Jul 16 '20 10:07 Blakeinstein

I think it would be interesting to know, how for example Electron handles the white border + their window actually animates when maximizing and minimizing (which is of less concern in my opinion, but cool to have in the long run). I have tried to look at their code in the past, but was not really able to figure it out.

One option would be to try to extend the client area in such a way, that it covers the top border, but I think, then it would become impossible to resize the window on the top border.

This sole issue is a stopper for me at the moment to switch to webview...

FrankenApps avatar Jul 16 '20 10:07 FrankenApps

Can anyone look into https://github.com/melak47/BorderlessWindow ? I am learning cpp and winapi as I go, and this is a bit too confusing code for me. The frameless and border resize come from here but they got rid of the white border box apparently using WM_Create, activate and NCCalcSize messages.

Blakeinstein avatar Jul 16 '20 17:07 Blakeinstein

Manged to fix this by returning 0 in case when the window is frame-less

case WM_NCCALCSIZE: {
    if (window->is_borderless) {
        return 0;
    } else {
        return DefWindowProc(hwnd, msg, wparam, lparam);
    }
}

Thanks to melak47/BorderlessWindow#6, and the corresponding stackoverflow question

I will update my repo asap. edit: Totally forgot resize still doesnt work.

Blakeinstein avatar Jul 17 '20 02:07 Blakeinstein

After a shit ton of research, I have to conclude that I have reached a dead end. I will most likely undergo the same "hack" I went through with my drag window approach. Add an event listener then, scale window to mouse position.

Blakeinstein avatar Jul 18 '20 09:07 Blakeinstein

The hit test function in my BorderlessWindow returns HT_CAPTION for the client area. Not sure if that's what you want, since that's where the web view content is.

melak47 avatar Jul 18 '20 13:07 melak47

I am pretty sure we can add an additional flag to toggle moveable window. That way as long as mouse is over an area that requests drag, we can set the flag to return HT_CAPTION instead.

Edit: We can just default to HT_CLIENT for now and handle window drag, separately. Implementing NCHITTEST never worked for me however, as the results for some reason were always ignored.

Blakeinstein avatar Jul 18 '20 14:07 Blakeinstein

I'll be honest, I have no idea how this web view thing works, or at which level it may be hijacking the input

melak47 avatar Jul 18 '20 14:07 melak47

You can take a look at webview-sys/webview_edge.cpp , that file contains two classes, a browser_window, that creates the window and the webview which creates a thread, and a process and attaches a webview async control on that window. I myself do not understand the internals.

Blakeinstein avatar Jul 18 '20 14:07 Blakeinstein

It seems the webview swallows nearly all the input when the mouse is over it. Only clicks seem to pass through, so there never are any WM_NCHITTEST messages to handle :/

melak47 avatar Jul 18 '20 14:07 melak47

Ah that makes sense, no wonder none of my hit test messages were working. I had to do a hack for getting the window to drag over the custom titlebar, wherein I just move the window to the mouse pos as long as the mouse is held. I am thinking of using the same hack for resizing, even though it wouldn't be native at all.

Blakeinstein avatar Jul 18 '20 14:07 Blakeinstein

An interesting find, @Codeusa implemented a hittest hack for HT_CAPTION here RainwayApp@07653fc

Blakeinstein avatar Jul 19 '20 13:07 Blakeinstein

There is probably a way to leverage the -webkit-app-region so that it's not hardcoded. Our app is a fixed size however so I didn't bother to implement it.

andrewmd5 avatar Jul 25 '20 05:07 andrewmd5

Yes but even then the hits on those regions would need to be translated to NCHITTEST messages that return the corresponding region for it to work.

On Sat, Jul 25, 2020, 10:56 AM Andrew Sampson [email protected] wrote:

There is probably a way to leverage the -webkit-app-region so that it's not hardcoded.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Boscop/web-view/issues/154#issuecomment-663812354, or unsubscribe https://github.com/notifications/unsubscribe-auth/AKCCJRSEW5IAJA2AWUBITRDR5JUHNANCNFSM4LK27U4Q .

Blakeinstein avatar Jul 25 '20 06:07 Blakeinstein

While researching this, I discovered that those css attributes are actually css extensions implemented in webkit itself. Source : https://developer.mozilla.org/en-US/docs/Web/CSS/WebKit_Extensions

I dont think we can add this feature to edge or mshtml, edge2 might work however.

edit: Could some test out reducing the winrt webview control's bounding rect to be like 5px less? Then use that 5px to handle resizing on the underlying window? One might need to set the background color transparent.

Blakeinstein avatar Jul 26 '20 07:07 Blakeinstein

I ended up getting resize to work without any visible borders, by allowing a 2px edge to pass to the WM_NCHITTEST message. You can test it at https://github.com/Blakeinstein/Bloop

Blakeinstein avatar Aug 03 '20 11:08 Blakeinstein

Hi, was a solution ever found for the white edges on the right and bottom for the MSHTML (non-resizable) case? I don't particularly need to be able to grab window borders, just trying to get it to render without that issue

Airyzz avatar Aug 30 '24 12:08 Airyzz

Hi, was a solution ever found for the white edges on the right and bottom for the MSHTML (non-resizable) case? I don't particularly need to be able to grab window borders, just trying to get it to render without that issue

nevermind, I was able to solve it here

Airyzz avatar Sep 01 '24 09:09 Airyzz