天天看點

CEF學習小記(二)-如何使浏覽器視窗随對話框一起縮放

通過上一篇文章,已經可以浏覽網頁了,但有一個問題,就是那最大化對話框後,網頁的浏覽區域并沒有變,如何讓浏覽區域變化呢?

我參考了兩篇文章,一篇是(http://blog.csdn.net/cyloser/article/details/49734559),另一篇是(http://www.heycode.com/a13696.html)。感謝這兩篇文章的作者,沒有這兩篇文章,我估計還在糾結為會麼CefSize::Set(…)函數為什麼不能改變視窗大小呢!當然如果有人知道如何使用Set()函數,請不吝賜教。

解決方法

我們知道,視窗大小的改變通常會産生WM_SIZE消息。那麼首先應該在對話框中添加OnSize函數。此外,要控制浏覽器視窗,必須要獲得其句柄或指針,如何獲得呢?這就是本文主要講解的内容。

在(一)中搭建CEF用到了cefsimple示例中的五個檔案,這五個檔案可以實作最基本的功能。為了探究cefsimple如何完成視窗縮放的功能,我嘗試在整個cefsimple中搜尋WM_SIZE,然而并沒有搜到。由于時間問題 ,我沒有去分析cefsimple的源碼,而是去cefclient項目的去找答案。在cefclient項目,很容易就在cefclient_win.cpp中搜到了,通過代碼分析,将主要代碼寫在下方:

if (!g_handler.get())
        break;
  CefRefPtr<CefBrowser> browser=g_handler->GetBrowser();
  if (browser/*g_handler->GetBrowser()*/) 
  {
        // Retrieve the window handle (parent window with off-screen rendering).
        CefWindowHandle hwnd =g_handler->GetBrowser()->GetHost()->GetWindowHandle();
        if (hwnd) 
        {
            // Resize the window and address bar to match the new frame size.
            RECT rect;
            GetClientRect(hWnd, &rect);
            rect.top += URLBAR_HEIGHT;
            ......
          }
    }
           

上面這段代碼說明了什麼呢?首先檢查g_handler.get()是否為0,為0就直接退出了WM_SIZE的消息處理了。在為非0的情況下,獲得浏覽器的智能指針(CefRefPtr),并對其進行判斷。在browser非零的情況下才能獲得浏覽視窗的句柄,然後根據客戶的界面進行更改。

可能你會問:g_handler是什麼變量?對g_handler進行追蹤,發現這是一個在cefclient.cpp中定義的全局變量,CefRefPtr g_handler;。是以在完全按照前一篇文章是不行的,應将app,handler的聲明為全局變量,并分别在構造函數和OnInitDialog中進行初始化,模闆分别為SimpleApp、SimpleHandler。然而就這樣還是不能使用,因為g_handler并沒有GetBrowser() 這樣的成員函數,怎麼辦?還得回到cefclient項目中尋找答案。

在client_handler.h和client_handler.cpp中找到了GetBrowser()的聲明和定義。

.h 
// Lock used to protect members accessed on multiple threads. Make it mutable
// so that it can be used from const methods.
mutable base::Lock lock_;
// The child browser window.
CefRefPtr<CefBrowser> browser_;
CefRefPtr<CefBrowser> GetBrowser() const;


----------


.cpp 
CefRefPtr<CefBrowser> ClientHandler::GetBrowser() const 
{
  base::AutoLock lock_scope(lock_);
  return browser_;
}
           

得到了GetBrowser的聲明和定義,問題又來了,lock_和browser_是什麼鬼?然而就這麼用到你的工程中還是不是的,否則就會出現如下錯誤提示:

CEF學習小記(二)-如何使浏覽器視窗随對話框一起縮放

通過對cefclient中lock_的類型base::Lock分析,發現這是在include/base/cef_lock.h中定義的,是以在handler.h中務必要加上#include “include/base/cef_lock.h”。請注意,不要不加思考地對代碼進行複制!!,ClientApp,ClientHandler這些類名一定要改成自已項目中定義的。

現在完成對OnSize()函數的編寫,代碼如下:

void CceftestDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if(g_handler!=NULL)
    {
        CefRefPtr<CefBrowser> browser=g_handler->GetBrowser();
        if(browser)
        {
            CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();
            ::MoveWindow(hwnd,,,cx,cy,TRUE);  //因為浏覽器對于對話框是子視窗,是以浏覽器的左上角坐标是相于父視窗的客戶區的左上角而言的
        }
    }
}
           

編譯一下,通過了。運作,發現居然還是老樣子。怎麼回事?!隻好對OnSize進行斷點調試,發現if(browser)的判斷體始終沒有執行。不信邪的我回到cefclient項目進行WM_SIZE處進行斷點調試,發現browser_非0。于是我意識到我一定在SimpleHandler.cpp中漏掉了對browser_的一些操作。在cefclient項目的cef_handler.cpp中搜尋對browser_的操作,找到了兩處,分别是在OnAfterCreated和OnBeforeClose,一個是在建立之後,一個是在關閉之前,剛後形成互補,代碼如下:

void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
    ......
     if (!GetBrowser())   {
    base::AutoLock lock_scope(lock_);
    // We need to keep the main child window, but not popup windows
    browser_ = browser;
    browser_id_ = browser->GetIdentifier();
  } 
  ......
}
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) 
{
......
 if (GetBrowserId() == browser->GetIdentifier()) {
    {
      base::AutoLock lock_scope(lock_);
      // Free the browser pointer so that the browser can be destroyed
      browser_ = NULL;
    }
    ......
}
           

由于隻有一個浏覽器視窗,是以關于視窗ID的操作就可以無視了。簡化代碼後如下:

void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) 
{
......
    if (!GetBrowser())
    {
      base::AutoLock lock_scope(lock_);
      // We need to keep the main child window, but not popup windows
      browser_ = browser;
    }
    ......
}
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) 
{
......
    base::AutoLock lock_scope(lock_);   //這一句包括下一句一定要有,否則就會在退出程式時報錯,程序無法釋放
    // Free the browser pointer so that the browser can be destroyed
    browser_ = NULL;
......
}
           

好了,現在再對項目進行編譯運作。效果如下。

CEF學習小記(二)-如何使浏覽器視窗随對話框一起縮放

後記:

我自己在處理對browser_的一些補全操作時,第一次隻在OnAfterCreated中加了相關代碼,沒有意識到要在OnBeforeClosed中加,結果也成功達到了目的,但是會出現一個奇怪的問題,就是在退出時會報錯。花了好長時間才明白browser_出現在OnAfterCreated和OnBeforeClosed中是有原因的,一個初始化,一個銷毀,官方在注釋中也說明了這一點。