CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2
  1. #1
    Join Date
    Oct 2013
    Posts
    2

    Question WM_NCHITTEST and WM_NCCALCSIZE on Awesomium UI

    Hello. I'm currently working on a custom interface utilizing the Awesomium library (Chromium rendering engine for use of HTML, CSS and Javascript for UI). I've read through the tutorial on how to "extend" the windows frame into the client area to hide it behind what is drawn in the client area (http://msdn.microsoft.com/en-us/libr...v=vs.85).aspx). I have successfully accomplished this task, but NCA hit-testing that is supposed to be happen by using the DwmDefWindowProc or the custom hit-test method does not occur. When I first launch the WM_NCHITTEST and WM_NCCALCSIZE messages are dispatched, but never again after the awesomium window area covers the frame. Thus it seems like the mouse cannot be captured in these areas (borders and caption).

    So the gist of it is: I need to retain the move/resize/snap-to-size functionality of the windows frame while it is hidden.

    Here is the code I have so far:

    Code:
    #include "view.h"
    #include <Awesomium/WebCore.h>
    #include <Awesomium/STLHelpers.h>
    #include <Uxtheme.h>
    #include <dwmapi.h>
    #include <gdiplus.h>
    #include <vector>
    
    #include <iostream>
    
    
    class ViewWin;
    
    static bool g_is_initialized = false;
    static std::vector<ViewWin*> g_active_views_;
    const wchar_t szWindowClass[] = L"ViewWinClass";
    const wchar_t szTitle[] = L"Application";
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    using namespace Awesomium;
    
    /*
    * MACROS
    */
    
    #define RECTWIDTH(rc)			(rc.right - rc.left)
    #define RECTHEIGHT(rc)			(rc.bottom - rc.top)
    
    #define LEFTEXTENDWIDTH			8
    #define RIGHTEXTENDWIDTH		8
    #define BOTTOMEXTENDWIDTH		20
    #define TOPEXTENDWIDTH			33
    #define TOPEXTENDWIDTH_ZOOM		33
    
    #define BIT_COUNT				32
    
    #ifndef GET_X_LPARAM
    #define GET_X_LPARAM(lp)                        ((int)(short)LOWORD(lp))
    #endif
    #ifndef GET_Y_LPARAM
    #define GET_Y_LPARAM(lp)                        ((int)(short)HIWORD(lp))
    #endif
    
    #define TMT_CAPTIONFONT			1
    
    class ViewWin : public View, public WebViewListener::View 
    {
    public:
    	ViewWin(int width, int height) 
    	{
    		PlatformInit();
    
    		web_view_ = WebCore::instance()->CreateWebView(width,
    			height,
    			0,
    			Awesomium::kWebViewType_Window);
    
    		web_view_->set_view_listener(this);
    
    		// Create our WinAPI Window
    		HINSTANCE hInstance = GetModuleHandle(0);
    
    		hwnd_ = CreateWindow(szWindowClass,
    			szTitle,
    			WS_OVERLAPPEDWINDOW,
    			CW_USEDEFAULT,
    			CW_USEDEFAULT,
    			width,
    			height, 
    			NULL,
    			NULL,
    			hInstance,
    			NULL);
    
    		if (!hwnd_)
    			exit(-1);
    
    		web_view_->set_parent_window(hwnd_);
    		
    		ShowWindow(hwnd_, SW_SHOWNORMAL);
    		UpdateWindow(hwnd_);
    
    		SetTimer (hwnd_, 0, 15, NULL );
    
    		g_active_views_.push_back(this);
    	}
    	virtual ~ViewWin() {
    		for (std::vector<ViewWin*>::iterator i = g_active_views_.begin();
    			i != g_active_views_.end(); i++) {
    				if (*i == this) {
    					g_active_views_.erase(i);
    					break;
    				}
    		}
    
    		web_view_->Destroy();
    	}
    
    	HWND hwnd() { return hwnd_; }
    
    	static void PlatformInit() {
    		if (g_is_initialized)
    			return;
    
    		WNDCLASSEX wc;
    
    		wc.cbSize        = sizeof(WNDCLASSEX);
    		wc.style         = 0;
    		wc.lpfnWndProc   = WndProc;
    		wc.cbClsExtra    = 0;
    		wc.cbWndExtra    = 0;
    		wc.hInstance     = GetModuleHandle(0);
    		wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    		wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    		wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    		wc.lpszMenuName  = NULL;
    		wc.lpszClassName = szWindowClass;
    		wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
    
    		if(!RegisterClassEx(&wc)) {
    			exit(-1);
    		}
    
    		g_is_initialized = true;
    	}
    	static ViewWin* GetFromHandle(HWND handle) {
    		for (std::vector<ViewWin*>::iterator i = g_active_views_.begin();
    			i != g_active_views_.end(); i++) {
    				if ((*i)->hwnd() == handle) {
    					return *i;
    				}
    		}
    
    		return NULL;
    	}
    
    	// Following methods are inherited from WebViewListener::View
    
    	virtual void OnChangeTitle(Awesomium::WebView* caller, const Awesomium::WebString& title) 
    	{
    			std::string title_utf8(ToString(title));
    			std::wstring title_wide(title_utf8.begin(), title_utf8.end());
    
    			SetWindowText(hwnd_, title_wide.c_str());
    	}
    	virtual void OnChangeAddressBar(Awesomium::WebView* caller, const Awesomium::WebURL& url) { }
    	virtual void OnChangeTooltip(Awesomium::WebView* caller, const Awesomium::WebString& tooltip) { }
    	virtual void OnChangeTargetURL(Awesomium::WebView* caller, const Awesomium::WebURL& url) { }
    	virtual void OnChangeCursor(Awesomium::WebView* caller, Awesomium::Cursor cursor) { }
    	virtual void OnChangeFocus(Awesomium::WebView* caller, Awesomium::FocusedElementType focused_type) { }
    	virtual void OnShowCreatedWebView(Awesomium::WebView* caller, Awesomium::WebView* new_view, const Awesomium::WebURL& opener_url, const Awesomium::WebURL& target_url, const Awesomium::Rect& initial_pos, bool is_popup) { }
    	virtual void OnAddConsoleMessage(Awesomium::WebView* caller, const Awesomium::WebString& message, int line_number, const Awesomium::WebString& source) { }
    
    protected:
    	HWND hwnd_;
    };
    
    /**---------------------------------
    * WndProc
    -----------------------------------*/
    
    // Paint the title on the custom frame.
    void PaintCustomCaption(HWND hWnd, HDC hdc)
    {
        RECT rcClient;
        GetClientRect(hWnd, &rcClient);
    
        HTHEME hTheme = OpenThemeData(NULL, L"CompositedWindow::Window");
        if (hTheme)
        {
            HDC hdcPaint = CreateCompatibleDC(hdc);
            if (hdcPaint)
            {
                int cx = RECTWIDTH(rcClient);
                int cy = RECTHEIGHT(rcClient);
    
                // Define the BITMAPINFO structure used to draw text.
                // Note that biHeight is negative. This is done because
                // DrawThemeTextEx() needs the bitmap to be in top-to-bottom
                // order.
                BITMAPINFO dib = { 0 };
                dib.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
                dib.bmiHeader.biWidth           = cx;
                dib.bmiHeader.biHeight          = -cy;
                dib.bmiHeader.biPlanes          = 1;
                dib.bmiHeader.biBitCount        = BIT_COUNT;
                dib.bmiHeader.biCompression     = BI_RGB;
    
                HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
                if (hbm)
                {
                    HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
    
                    // Setup the theme drawing options.
                    DTTOPTS DttOpts = {sizeof(DTTOPTS)};
                    DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE;
                    DttOpts.iGlowSize = 15;
    
                    // Select a font.
                    LOGFONT lgFont;
                    HFONT hFontOld = NULL;
                    if (SUCCEEDED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &lgFont)))
                    {
                        HFONT hFont = CreateFontIndirect(&lgFont);
                        hFontOld = (HFONT) SelectObject(hdcPaint, hFont);
                    }
    
                    // Draw the title.
                    RECT rcPaint = rcClient;
                    rcPaint.top += 8;
                    rcPaint.right -= 125;
                    rcPaint.left += 8;
                    rcPaint.bottom = 50;
                    DrawThemeTextEx(hTheme, 
                                    hdcPaint, 
                                    0, 0, 
                                    szTitle, 
                                    -1, 
                                    DT_LEFT | DT_WORD_ELLIPSIS, 
                                    &rcPaint, 
                                    &DttOpts);
    
                    // Blit text to the frame.
                    BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
    
                    SelectObject(hdcPaint, hbmOld);
                    if (hFontOld)
                    {
                        SelectObject(hdcPaint, hFontOld);
                    }
                    DeleteObject(hbm);
                }
                DeleteDC(hdcPaint);
            }
            CloseThemeData(hTheme);
        }
    }
    
    // Hit test the frame for resizing and moving.
    LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
    {
        // Get the point coordinates for the hit test.
        POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
    
        // Get the window rectangle.
        RECT rcWindow;
        GetWindowRect(hWnd, &rcWindow);
    
    	std::cout << "Left: " << rcWindow.left << "Top: " << rcWindow.top << "Right: " << rcWindow.right << "Bottom: " << rcWindow.bottom << std::endl;
    
        // Get the frame rectangle, adjusted for the style without a caption.
        RECT rcFrame = { 0 };
        AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
    
        // Determine if the hit test is for resizing. Default middle (1,1).
        USHORT uRow = 1;
        USHORT uCol = 1;
        bool fOnResizeBorder = false;
    
        // Determine if the point is at the top or bottom of the window.
        if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH)
        {
            fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
            uRow = 0;
        }
        else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH)
        {
            uRow = 2;
        }
    
        // Determine if the point is at the left or right of the window.
        if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH)
        {
            uCol = 0; // left side
        }
        else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH)
        {
            uCol = 2; // right side
        }
    
        // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
        LRESULT hitTests[3][3] = 
        {
            { HTTOPLEFT,    fOnResizeBorder ? HTTOP : HTCAPTION,    HTTOPRIGHT },
            { HTLEFT,       HTNOWHERE,     HTRIGHT },
            { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
        };
    
        return hitTests[uRow][uCol];
    }
    
    LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
    {
        LRESULT lRet = 0;
        HRESULT hr = S_OK;
        bool fCallDWP = true; // Pass on to DefWindowProc?
    
        fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
    
    	
        // Handle window creation.
        if (message == WM_CREATE)
        {
    		
            RECT rcClient;
            GetWindowRect(hWnd, &rcClient);
    
            // Inform application of the frame change.
            SetWindowPos(hWnd, 
                         NULL, 
                         rcClient.left, rcClient.top,
                         RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
                         SWP_FRAMECHANGED);
    
            fCallDWP = true;
            lRet = 0;
        }
    
        // Handle window activation.
        if (message == WM_ACTIVATE)
        {
            // Extend the frame into the client area.
    		MARGINS margins;
    
            margins.cxLeftWidth = LEFTEXTENDWIDTH;      // 8
            margins.cxRightWidth = RIGHTEXTENDWIDTH;    // 8
            margins.cyBottomHeight = BOTTOMEXTENDWIDTH; // 20
            margins.cyTopHeight = TOPEXTENDWIDTH;       // 27
    
    		
            hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
    
            if (!SUCCEEDED(hr))
            {
                // Handle error.
            }
    
            fCallDWP = true;
            lRet = 0;
        }
    
        if (message == WM_PAINT)
        {
    		
    		PAINTSTRUCT ps;
            HDC hdc;
            {
                hdc = BeginPaint(hWnd, &ps);
                PaintCustomCaption(hWnd, hdc);
                EndPaint(hWnd, &ps);
            }
    
            fCallDWP = true;
            lRet = 0;
        }
    
        // Handle the non-client size message.
        if ((message == WM_NCCALCSIZE) && (wParam == TRUE) )
        {
    		
            // Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
            NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
    		
            pncsp->rgrc[0].left   = pncsp->rgrc[0].left   + 0;
            pncsp->rgrc[0].top    = pncsp->rgrc[0].top    + 0;
            pncsp->rgrc[0].right  = pncsp->rgrc[0].right  - 0;
            pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 0;
    
            lRet = 0;
            
            // No need to pass the message on to the DefWindowProc.
            fCallDWP = false;
        }
    
        // Handle hit testing in the NCA if not handled by DwmDefWindowProc.
        if ((message == WM_NCHITTEST) && (lRet == 0))
        {
            lRet = HitTestNCA(hWnd, wParam, lParam);
    
            if (lRet != HTNOWHERE)
            {
                fCallDWP = false;
            }
        }
    
        *pfCallDWP = fCallDWP;
    
    
        return lRet;
    }
    
    LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	ViewWin* view = ViewWin::GetFromHandle(hWnd);
        int wmId, wmEvent;
        PAINTSTRUCT ps;
        HDC hdc;
        HRESULT hr; 
        LRESULT result = 0;
    
    	
        switch (message)
        {
            case WM_CREATE:
                {
    			}
                break;
    
    		case WM_SIZE:
    			view->web_view()->Resize(LOWORD(lParam), HIWORD(lParam));
    		break;
    
            case WM_COMMAND:
                wmId    = LOWORD(wParam);
                wmEvent = HIWORD(wParam);
    
                // Parse the menu selections:
                switch (wmId)
                {
                    default:
                        return DefWindowProc(hWnd, message, wParam, lParam);
                }
                break;
            case WM_PAINT:
                {
    				
                    hdc = BeginPaint(hWnd, &ps);
                    PaintCustomCaption(hWnd, hdc);
                    
                    // Add any drawing code here...
        
                    EndPaint(hWnd, &ps);
                }
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        bool fCallDWP = true;
        BOOL fDwmEnabled = FALSE;
        LRESULT lRet = 0;
        HRESULT hr = S_OK;
    
        // Winproc worker for custom frame issues.
        hr = DwmIsCompositionEnabled(&fDwmEnabled);
    
    	//std::cout << "Is DWM comp enabled?: " << hr << std::endl;
        if (SUCCEEDED(hr))
        {
            lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
        }
    
        // Winproc worker for the rest of the application.
        if (fCallDWP)
        {
            lRet = AppWinProc(hWnd, message, wParam, lParam);
        }
    
        return lRet;
    }
    
    View* View::Create(int width, int height) {
    	return new ViewWin(width, height);
    }
    Any help with this would be greatly appreciated!

    Thank you, and best regards,
    Oyvind

  2. #2
    Join Date
    Oct 2013
    Posts
    2

    Re: WM_NCHITTEST and WM_NCCALCSIZE on Awesomium UI

    Hello again. I see there have been quite a few views, with no pointer/answer/thought.

    I could also say that if you might just have an idea, or a notion of what it MIGHT be, I would take that over anything else. Meanwhile I'm trying to figure it out by contacting the Awesomium API guys (since they do not release the actual source of their code, I cannot determine if the reason is their LIB or not).

    Would it help if I provided the entire solution to the project? If anyone would care to tinker with it as well..?

    Thanks again.
    Best regards,
    Oyvind

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured