CEF多标签浏览器

由于工程仅仅提供学习用,怎么简单怎么做。标签关闭通过菜单关闭。通过前面几篇文章学习我们已经可以粗略的实现一个简单的浏览器。如果需要实现多标签浏览器我们需要解决如下几个问题:(Note:Win32的UI线程和Cef的UI线程不是同一个线程,所以在操作的时候需要注意多线程的问题

  1. 如何让新打开的网页在标签页下显示?
  2. 如何在切换标签页的时候切换网页?
  3. 如何通过关闭标签页的时候关闭网页?
  4. 如何通过关键字搜索?
  5. 如何显示devTool?
  6. 如何新建标签页?
1.如何让新打开的网页在标签页下显示?

通过网页点击打开的新网页是也许POPUP模式,但是我们希望作为标签页窗口。所以需要将窗口属相修改为WS_CHILD并且把父窗口设置为tab窗口。源码如下:

void CBrowserHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) 
{
	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_parent_hwnd);
	// Add to the list of existing browsers.
	m_browser_list.push_back(browser);
	::PostMessage(g_hMainWnd, UM_NEWPAGE, (WPARAM)browser->GetHost()->GetWindowHandle(), m_browser_list.size() - 1);
}
2.如何在切换标签页的时候切换网页?

通过上面修改属性作为子窗口,我们知道每个CefBrowser都有一个窗口句柄,所以我们可以通过切换标签的时候隐藏没有选中的窗口,显示已经选中的窗口。源码如下:

void CBrowserHandler::SetVisible(bool bVisible, HWND hWnd)
{
	if (hWnd == NULL) return;

	//Cef的UI线程和对话框的UI线程不在同一个
	if (!CefCurrentlyOn(TID_UI))
	{
		// Execute on the UI thread.
		// bind ref cef_closure_task
		CefPostTask(TID_UI, base::Bind(&CBrowserHandler::SetVisible, this, bVisible, hWnd));
		return;
	}

	for (int i = 0; i < m_browser_list.size();++i)
	{
		bool bVisible = (m_browser_list[i]->GetHost()->GetWindowHandle() == hWnd);
		::ShowWindow(m_browser_list[i]->GetHost()->GetWindowHandle(), bVisible ? SW_SHOWNORMAL : SW_HIDE);
	}
}
3.如何通过关闭标签页的时候关闭网页?

关闭标签页的时候,如果按照之前文章介绍的时候发现关闭某一个网页的时候,这个浏览器进程都关闭了,这不是我们期望的。那么怎么解决呢?通过不断重试发现如果作为POPUP窗口的时候是可以选择性关闭某个网页的。结合TryCloseBrowser关闭机制:关闭的时候会投递WM_CLOSE消息。那么如果我们试着在关闭的时候篡改父窗口,那么是否可以成功呢?结果OK。源码如下:

bool CBrowserApp::TryClose(HWND hWnd)
{
	if (hWnd != NULL)
	{
		//必须更改窗口父窗口属性,否则会退出浏览器进程
		::SetParent(hWnd, GetDesktopWindow());
		if (m_simple_handler)
			m_simple_handler->CloseBrowsers(hWnd);
		return true;
	}
	else
	{
		if (m_simple_handler && m_simple_handler.get() &&
			!m_simple_handler->IsClosing())
		{
			m_simple_handler->CloseAllBrowsers();
			return true;
		}
	}

	return false;
}

void CBrowserHandler::CloseBrowsers(HWND hWnd)
{
	if (!CefCurrentlyOn(TID_UI))
	{
		// Execute on the UI thread.
		// bind ref cef_closure_task
		CefPostTask(TID_UI, base::Bind(&CBrowserHandler::CloseBrowsers, this, hWnd));
		return;
	}

	if (m_browser_list.empty())
		return;

	for (int i = 0; i < m_browser_list.size(); ++i)
	{
		if (m_browser_list[i] && m_browser_list[i]->GetHost()->GetWindowHandle() == hWnd)
		{
			m_browser_list[i]->GetHost()->TryCloseBrowser();
			break;
		}
	}
}
4.如何通过关键字搜索?

这个我们以百度搜索为例讲解,我们在浏览器输入百度云盘:
在这里插入图片描述
所以搜索就很简单了。如果url是以https://或者http://开头我们则直接跳转到该url,否则我们将url转换为utf-8,然后附加在https://www.baidu.com/s?ie=UTF-8&wd=xxxx。源码如下:

LRESULT CCefDemoDlg::OnGoSearch(WPARAM wParam, LPARAM lParam)
{
	HWND hNowWnd = GetCurselWnd();
	if (hNowWnd && m_browser_app)
	{
		CString strurl;
		m_url.GetWindowTextW(strurl);
		//如果是以http或者https开始则直接跳转,否则以百度搜索引擎搜索
		//以百度为搜索引擎
		//https://www.baidu.com/s?ie=UTF-8&wd=%E7%99%BE%E5%BA%A6
		std::wstring wurl = strurl.GetBuffer(0);
		if (wurl.substr(0,8) == L"https://" || wurl.substr(0,7) == L"http://")
			m_browser_app->LoadUrl(hNowWnd, strurl.GetBuffer(0));
		else
		{
			std::string str = "https://www.baidu.com/s?ie=UTF-8&wd=";
			std::string utf8_keywors = "";
			UnicodeToUtf8(strurl.GetBuffer(0), utf8_keywors);
			str += utf8_keywors;
			m_browser_app->LoadUrl(hNowWnd, str);
		}
	}
	return TRUE;
}

void CBrowserHandler::LoadUrl(HWND hWnd, CefString url)
{
	if (!CefCurrentlyOn(TID_UI))
	{
		// Execute on the UI thread.
		// bind ref cef_closure_task
		CefPostTask(TID_UI, base::Bind(&CBrowserHandler::LoadUrl, this, hWnd, url));
		return;
	}

	if (m_browser_list.empty())
		return;

	for (int i = 0; i < m_browser_list.size(); ++i)
	{
		if (m_browser_list[i] && m_browser_list[i]->GetHost()->GetWindowHandle() == hWnd)
		{
			m_browser_list[i]->GetMainFrame()->LoadURL(url);
			break;
		}
	}
}
5.如何显示devTool?

Cef本身支持直接显示devTool,所以我们只需要选定需要显示的CefBrowser就行(devTool本身也是CefBrowser)。源码如下:

void CBrowserHandler::ShowDevTool(HWND hWnd)
{
	if (!CefCurrentlyOn(TID_UI))
	{
		// Execute on the UI thread.
		// bind ref cef_closure_task
		CefPostTask(TID_UI, base::Bind(&CBrowserHandler::ShowDevTool, this, hWnd));
		return;
	}

	if (m_browser_list.empty())
		return;

	for (int i = 0; i < m_browser_list.size(); ++i)
	{
		if (m_browser_list[i] && m_browser_list[i]->GetHost()->GetWindowHandle() == hWnd)
		{
			CefWindowInfo windowInfo;
			CefRefPtr<CefClient> client;
			CefBrowserSettings settings;
			if (!m_browser_list[i]->GetHost()->HasDevTools())
				m_browser_list[i]->GetHost()->ShowDevTools(windowInfo, this, settings, CefPoint());
			break;
		}
	}
}
6.如何新建标签页?

首先新建标签页分为两类:1.指定主页,2.空白页
我们先说空白页,新建空白页的时候并没有创建CefBrowser,我们只是单纯的建立了一个标签。然后在地址栏输入地址,进行搜索的时候把以该地址新建一个CefBrowser绑定到该标签页下即可。指定主页就类似于空白页的下半部分。所以我们只讲解指定主页的,因为关联到指定标签属于UI层的工作,该工作略,源码如下:

void CBrowserApp::NewTabPage(std::string url)
{
	RECT rtClient;
	::GetClientRect(m_parent_hwnd, &rtClient);
	rtClient.top += 40;
	CefWindowInfo wnd_info;
	wnd_info.SetAsChild(m_parent_hwnd, rtClient);

	CefBrowserSettings browser_settings;
	CefBrowserHost::CreateBrowser(wnd_info, m_simple_handler, url, browser_settings, NULL);
}

如果上述功能实现了,我们结合之前的文章说明,那么一个多标签浏览器就成功实现了。

Note:在实践过程中我们发现在Edit里面Enter的时候Cef会触发断言,所以我们需要在PreTranslateMessage拦截Enter消息。

BOOL CCefDemoDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO:  在此添加专用代码和/或调用基类
	if (pMsg->message == WM_KEYDOWN)
	{
		if (pMsg->wParam == VK_RETURN)
		{
			if (&m_url == GetFocus())
			{
				SetFocus();
				PostMessage(UM_GOSEARCH, 0, 0);
				return TRUE;
			}
		}
	}
	return CDialogEx::PreTranslateMessage(pMsg);
}

整体效果展示:
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/CAir2/article/details/86080918
今日推荐