libcef多标签浏览器实现
作者:互联网
我怒啊,TMD为了研究libcef浏览器,我已经写了几篇关于如何使用libcef的文章,包括嵌入、基本使用,然后顺着这道路一直调研下去,当然在这个过程中我遇到的最大问题也是我花了一天时间搜索的问题就是新建的浏览器标签窗口在不点击页面的时候滚动条是可以使用的,就是在网页内容较多的时候可以通过滚轮上下滚动,但是当点击页面里面之后,鼠标滚轮就失效了,一开始认为最大的原因就是新建的窗口被我捕获到之后将对应窗口属性修改了导致了我的上述问题,但是所有的窗口属性都试了遍都没有发现问题所在。
在我解决鼠标滚动的时候,找了半天,终于找到一篇文章,也是关于实现多标签的cef浏览器,而且它的实现的标签里没有鼠标滚轮问题,但是最恶心的就是TMD喜欢炫耀,没有为广大网友做一丁点贡献,我就纳闷了,NTMD没有贡献精神就别TM写相关的技术资料,写了又不将关键技术或Demon提供为广大网友,最恶心的事提供的Demon只给一个exe,我在想,你有病吗?谁要你的exe有毛用?你是在炫耀你的实力吗?你以为我搞定不这小小的东西?看了看这哥们的CSDN排名,还是在6000多,真是有点怀疑这类人了,在这里我多说几句,没有奉献精神的技术“牛人”,即使再怎么牛,在我眼里就是一坨屎!顺便对所有人都说一下,知道感恩,回报社会!
我是一位毫无保留的人,因为我知道我懂得技术来源于网络,那么我有责任为网络或技术的进步做出我应有的贡献,当然也有很多从网络上学到知识的但并未付出的,这也屡屡不见,但是,如果每一个人都这么看,技术如何发扬,科技如何进步?就废话说道这里吧,下面就不如正题,这仅仅是我的一个开始,后续关于cef嵌入的文章我会继续写下去:
前一篇文章中,我已经大致描述了如何在MFC中简单实用CEF内核,现在增加的功能就是:
(1)如何在浏览器中实现多标签功能
(2)多标签切换显示
(3)新建浏览器鼠标滚轮失效的问题
后续文章中我会实现如下功能
(1)关闭对应标签浏览器
(2)查找功能
(3)C++与JS交互
首先,我说明一下今天这篇文章实现的功能
(1)如何在浏览器中实现多标签功能
这里,我用的duilib(自己的duilib,在项目实践中修改了很多bug的库),多选项卡实现就是使用了OPtion的group属性,另外每一个标签的切换使用了Tablayout布局,这里的xml代码简单一览如下:
因为所有标签横线排列,所以使用了水平布局,并且使用了相对布局,让控件自适应布局的宽度;
(2)多标签切换显示
浏览器事件是我们创建窗口的源头,浏览器是如何创建的?标题变化如何捕获?URL地址发生变化了怎么知道?是否可以在当前浏览器前进后退?这里就涉及到我们在创建浏览器的时候传递进去的事件处理器(浏览器客户端client,包括浏览器的导航、浏览器框架的菜单等操作、载入状态、显示状态、生命周期等)
首先看如何创建浏览器的,这里我们就是在窗口初始化完成后:
[cpp] view plain copy
- DUI_BEGIN_MESSAGE_MAP(CBrowserWnd,WindowImplBase)
- DUI_ON_MSGTYPE(DUI_MSGTYPE_WINDOWINIT, OnWindowInit)
- DUI_ON_MSGTYPE(DUI_MSGTYPE_SELECTCHANGED, OnSelectChanged)
- DUI_END_MESSAGE_MAP();
捕获到WINDOWINIT消息,这个消息就是窗口所有控件位置都初始化完成(位置正确到位了,在虚函数InitWindow中所有控件位置你是无法获取的)。在这里我们创建一个默认浏览器,设置不需要设置子窗口,因为为了统一性,我们在捕获每一个新窗口时候都会自动设置为子窗口:
[cpp] view plain copy
- void CBrowserWnd::OnWindowInit(TNotifyUI& msg)
- {
- CHorizontalLayoutUI* pBody = static_cast<CHorizontalLayoutUI*>(m_PaintManager.FindControl(_T("body")));
- if (NULL != pBody)
- {
- RECT rtPositon = pBody->GetPos();
- InitBrower(rtPositon);
- }
- m_bInit = true;
- }
- void CBrowserWnd::InitBrower(RECT rtPositon)
- {
- CefWindowInfo cefWindowInfo;
- //cefWindowInfo.SetAsChild(m_PaintManager.GetPaintWindow(), rtPositon);
- CefBrowserSettings browserSetting;
- m_objBrowserEvent = CefRefPtr<CCefBrowserEventHandler>(new CCefBrowserEventHandler(this));
- CefBrowserHost::CreateBrowser(cefWindowInfo, m_objBrowserEvent, (LPCTSTR)m_strUrl.GetData(), browserSetting, NULL);
- }
在浏览器事件处理器中,我们如何处理的:
[html] view plain copy
- #include "StdAfx.h"
- #include "CefBrowserEventHandler.h"
- #include <sstream>
- #include <string>
- #include "include/base/cef_bind.h"
- #include "include/views/cef_browser_view.h"
- #include "include/views/cef_window.h"
- #include "include/wrapper/cef_closure_task.h"
- #include "include/wrapper/cef_helpers.h"
- #include "BrowserWnd.h"
- CCefBrowserEventHandler::CCefBrowserEventHandler(CBrowserWnd* pMainFrame)
- {
- m_pMainFrame = pMainFrame;
- }
- CCefBrowserEventHandler::~CCefBrowserEventHandler(void)
- {
- }
- CefRefPtr<CefDisplayHandler> CCefBrowserEventHandler::GetDisplayHandler() OVERRIDE
- {
- return this;
- }
- CefRefPtr<CefLifeSpanHandler> CCefBrowserEventHandler::GetLifeSpanHandler() OVERRIDE
- {
- return this;
- }
- CefRefPtr<CefLoadHandler> CCefBrowserEventHandler::GetLoadHandler() OVERRIDE
- {
- return this;
- }
- void CCefBrowserEventHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE
- {
- ::SendMessage(m_pMainFrame->GetHWND(), WM_TITLE_CHANGED, (WPARAM)browser->GetHost()->GetWindowHandle(), (LPARAM)std::wstring(title).c_str());
- }
- void CCefBrowserEventHandler::OnAddressChange(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,const CefString& url) OVERRIDE
- {
- ::SendMessage(m_pMainFrame->GetHWND(), WM_URL_CHANGED, (WPARAM)browser->GetHost()->GetWindowHandle(), (LPARAM)(LPCTSTR)url.c_str());
- }
- void CCefBrowserEventHandler::OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, ErrorCode errorCode,
- const CefString& errorText, const CefString& failedUrl) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- if (ERR_ABORTED == errorCode)
- return ;
- std::stringstream ss;
- ss << "<html><body bgcolor=\"white\">"
- "<h2>Failed to load URL " << std::string(failedUrl) <<
- " with error " << std::string(errorText) << " (" << errorCode <<
- ").</h2></body></html>";
- frame->LoadString(ss.str(), failedUrl);
- }
- void CCefBrowserEventHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward) OVERRIDE
- {
- ::PostMessage(m_pMainFrame->GetHWND(), WM_LOAD_CHANGED, (WPARAM)browser->GetHost()->GetWindowHandle(), MAKELPARAM(canGoForward, canGoBack));
- }
- void CCefBrowserEventHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- HWND hWnd = browser->GetHost()->GetWindowHandle();
- DWORD dwNewStyle = (::GetWindowLong(hWnd, GWL_STYLE)&~(WS_POPUP|WS_CAPTION|WS_BORDER|WS_SIZEBOX|WS_SYSMENU))|WS_CHILD;
- ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
- SetParent(hWnd, m_pMainFrame->GetHWND());
- m_browser_list.push_back(browser);
- ::PostMessage(m_pMainFrame->GetHWND(), WM_CREATE_NEW_PAGE, (WPARAM)browser->GetHost()->GetWindowHandle(), 0);
- }
- BrowserPtr CCefBrowserEventHandler::GetBrowser(HWND hWnd)
- {
- BrowserPtr pBrowser = NULL;
- for(size_t i = 0; i < m_browser_list.size(); ++i)
- {
- if (m_browser_list[i] && m_browser_list[i]->GetHost()->GetWindowHandle() == hWnd)
- {
- pBrowser = m_browser_list[i];
- break;
- }
- }
- return pBrowser;
- }
- bool CCefBrowserEventHandler::DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- if (1 == m_browser_list.size())
- {
- }
- return false;
- }
- void CCefBrowserEventHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- for (BrowserList::iterator bit = m_browser_list.begin(); bit != m_browser_list.end(); ++bit)
- {
- if ((*bit)->IsSame(browser)) {
- m_browser_list.erase(bit);
- break;
- }
- }
- if (m_browser_list.empty())
- {
- CefQuitMessageLoop();
- }
- }
- void CCefBrowserEventHandler::CloseAllBrowser(bool force_close)
- {
- if (!CefCurrentlyOn(TID_UI))
- {
- CefPostTask(TID_UI,base::Bind(&CCefBrowserEventHandler::CloseAllBrowser, this, force_close));
- return;
- }
- if (m_browser_list.empty())
- return;
- BrowserList::const_iterator it = m_browser_list.begin();
- for (; it != m_browser_list.end(); ++it)
- {
- (*it)->GetHost()->CloseBrowser(force_close);
- }
- }
根据对应新建的浏览器动态创建对应的浏览器选项卡:
[cpp] view plain copy
- void CBrowserWnd::OnNewPage(HWND hWnd)
- {
- COptionUI* pOption = new COptionUI;
- m_pOptions->Add(pOption);
- CDuiString strAttr;
- strAttr.Format(_T("width=\"%d\" group=\"tab_group\" normalimage=\"选项卡1.png\" selectedimage=\"选项卡2.png\" endellipsis=\"true\""), m_nOptionWidth);
- pOption->ApplyAttributeList(strAttr);
- m_objHwndMap[pOption] = hWnd;
- m_pOptions->SetFixedWidth(m_nOptionWidth*m_objHwndMap.size());
- pOption->Selected(true);
- CHorizontalLayoutUI* pBody = static_cast<CHorizontalLayoutUI*>(m_PaintManager.FindControl(_T("body")));
- if (NULL != pBody)
- {
- RECT rtPositon = pBody->GetPos();
- ::MoveWindow(hWnd, rtPositon.left, rtPositon.top, rtPositon.right-rtPositon.left, rtPositon.bottom-rtPositon.top, TRUE);
- }
- ShowPage(hWnd);
- }
选择不同选项卡进行切换:
[cpp] view plain copy
- void CBrowserWnd::OnSelectChanged(TNotifyUI& msg)
- {
- COptionUI* pOption = (COptionUI*)msg.pSender;
- if(NULL == pOption)
- return ;
- OnUrlChanged(m_objHwndMap[pOption], m_objBrowserEvent->GetBrowser(m_objHwndMap[pOption])->GetMainFrame()->GetURL().c_str());
- OnTitleChanged(m_objHwndMap[pOption], pOption->GetText().GetData());
- OnLoadChanged(m_objHwndMap[pOption], m_objBrowserEvent->GetBrowser(m_objHwndMap[pOption])->CanGoBack(),
- m_objBrowserEvent->GetBrowser(m_objHwndMap[pOption])->CanGoForward());
- ShowPage((COptionUI*)msg.pSender);
- }
[cpp] view plain copy
- // 加载进度变化
- void BrowserWnd::OnLoadChanged(HWND hWnd, BOOL bCanBack, BOOL bCanForword)
- {
- COptionUI* pOption = GetOption(hWnd);
- if (NULL == pOption)
- return ;
- if (!pOption->IsSelected())
- return ;
- if (m_pBackbtn)
- {
- m_pBackbtn->SetEnabled(bCanBack != FALSE);
- }
- if (m_pForwordbtn)
- {
- m_pForwordbtn->SetEnabled(bCanForword != FALSE);
- }
- }
[cpp] view plain copy
- // URL变化
- void CBrowserWnd::OnUrlChanged(HWND hWnd, LPCTSTR lpLoadUrl)
- {
- COptionUI* pOption = GetOption(hWnd);
- if (NULL == pOption)
- return ;
- if (!pOption->IsSelected())
- return ;
- if (lpLoadUrl && m_pUrlEdit)
- {
- m_strUrl = lpLoadUrl;
- m_pUrlEdit->SetText(lpLoadUrl);
- }
- }
[cpp] view plain copy
- // 标题变化
- void CBrowserWnd::OnTitleChanged(HWND hWnd, LPCTSTR lpTitle)
- {
- COptionUI* pOption = GetOption(hWnd);
- if (NULL == pOption)
- return ;
- pOption->SetText(lpTitle);
- if (pOption->IsSelected() && m_pTitile)
- {
- CString strTitile = lpTitle;
- strTitile += _T(" ");
- m_pTitile->SetText(strTitile);
- }
- }
以上代码中,在切换的时候,将对应的浏览器显示出来(对应浏览器的HWND我们是可以获取到并存下来的,这里如何存取到要看我的上一篇文章:Browser->GetHost->GetWindowHandle获取到对应的窗口句柄);
接下来就是相应不同浏览器的标题变化、加载变化以及URL状态变化等事件,根据不同的事件丰富我们的内容;
(3)新建浏览器鼠标滚轮失效的问题
说道这个问题,其实我之前也用过也遇到过,不过这个小问题编码的时候没太注意到,我原来的代码是这样的:
[cpp] view plain copy
- void CCefBrowserEventHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- HWND hWnd = browser->GetHost()->GetWindowHandle();
- SetParent(hWnd, m_pMainFrame->GetHWND());
- DWORD dwNewStyle = (::GetWindowLong(hWnd, GWL_STYLE)&~(WS_POPUP|WS_CAPTION|WS_BORDER|WS_SIZEBOX|WS_SYSMENU))|WS_CHILD;
- ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
- m_browser_list.push_back(browser);
- ::PostMessage(m_pMainFrame->GetHWND(), WM_CREATE_NEW_PAGE, (WPARAM)browser->GetHost()->GetWindowHandle(), 0);
- }
纠正之后的代码:[cpp] view plain copy
- void CCefBrowserEventHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE
- {
- CEF_REQUIRE_UI_THREAD();
- HWND hWnd = browser->GetHost()->GetWindowHandle();
- DWORD dwNewStyle = (::GetWindowLong(hWnd, GWL_STYLE)&~(WS_POPUP|WS_CAPTION|WS_BORDER|WS_SIZEBOX|WS_SYSMENU))|WS_CHILD;
- ::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
- SetParent(hWnd, m_pMainFrame->GetHWND());
- m_browser_list.push_back(browser);
- ::PostMessage(m_pMainFrame->GetHWND(), WM_CREATE_NEW_PAGE, (WPARAM)browser->GetHost()->GetWindowHandle(), 0);
- }
是否发现了不同之处?是的就是在设置浏览器窗口属性的时候顺序导致的,有Popup窗口更改为子窗口的时候,必须有子窗口属性之后设置父窗口才奏效,由子窗口变为popup窗口的时候必须先去掉父窗口,也就是setparent为null后在修改为popup,这是一个顺序问题,其中的理解意味深长,这里我就不多说了!很重要!
好了给大家看看效果,所有图片都是我在easyicon上随便找的,比较难看,我这里只是做demon,所以,你么可以自己处理UI:
千言万语,抵不过一例源代码:http://download.csdn.net/detail/lixiang987654321/9600129
标签:libcef,浏览器,hWnd,CCefBrowserEventHandler,pOption,标签,WS,browser 来源: https://blog.csdn.net/qq_39538699/article/details/88355719