分享个Duilib中基于wke的浏览器控件

概述

wke是基于谷歌chrome浏览器源代码的裁剪版本,大小仅仅只有10M左右,无需依赖其他的扩展库(跟CEF的一大堆大约40M的DLL来比简直爽呆了),就可以在本地使用谷歌内核快速加载网页。网上也有基于wke在Duilib 上扩展的控件代码,其实wke头文件挺清楚的了,接口一目了然,特别是JS与C++交互的函数更是容易看懂,也没什么难的,你也可以做到的。

代码

毕竟是裁剪库,有的功能还是没有接口来处理的(比如网页加载前、页面跳转、菜单消息……),头文件代码:

#ifndef __UIWKEWEBKIT_H_
#define __UIWKEWEBKIT_H_
#pragma once
#include "wke.h"
#include <string>
using std::wstring;

namespace DuiLib
{
	///////////////////////////////////////////
	//网页加载状态改变的回调
	class CWkeWebkitLoadCallback
	{
	public:
		virtual void	OnLoadFailed()=0;
		virtual void	OnLoadComplete()=0;
		virtual void	OnDocumentReady()=0;
	};
	///////////////////////////////////////////
	//网页标题、地址改变的回调
	class CWkeWebkitClientCallback
	{
	public:
		virtual void	OnTitleChange(const CDuiString& strTitle)=0;
		virtual void	OnUrlChange(const CDuiString& strUrl)=0;
	};

	class CWkeWebkitUI :
		public CControlUI,
		public wkeBufHandler
	{
	public:
		CWkeWebkitUI(void);
		~CWkeWebkitUI(void);
		virtual void	onBufUpdated (const HDC hdc,int x, int y, int cx, int cy);
		virtual LPCTSTR	GetClass()const;
		virtual LPVOID	GetInterface(LPCTSTR pstrName);
		virtual void	DoEvent(TEventUI& event);
		virtual void	DoPaint(HDC hDC, const RECT& rcPaint);
		virtual void	SetPos(RECT rc);
		virtual void	DoInit();
		virtual void	SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
		//////////////////////////////////////
		const	wstring& GetUrl()const ;
		bool	CanGoBack() const;
		bool	GoBack();
		bool	CanGoForward() const;
		bool	GoForward();
		void	StopLoad();
		void	Refresh();
		wkeWebView	GetWebView();
		void	SetLoadCallback(CWkeWebkitLoadCallback* pCallback);
		CWkeWebkitLoadCallback* GetLoadCallback();
		void	Navigate(LPCTSTR lpUrl);
		void	LoadFile(LPCTSTR lpFile);
		void	LoadHtml(LPCTSTR lpHtml);
	protected:
		void	StartCheckThread();
		void	StopCheckThread();
		static	void OnTitleChange(const struct _wkeClientHandler* clientHandler, const wkeString title);
		static  void OnUrlChange(const struct _wkeClientHandler* clientHandler, const wkeString url);
	private:
		static int	m_bWebkitCount;
		HANDLE		m_hCheckThread;
		wstring		m_strUrl;
		wkeWebView	m_pWebView;
		wkeClientHandler	m_ClientHandler;
		CWkeWebkitLoadCallback*		m_pLoadCallback;
		CWkeWebkitClientCallback*	m_pClientCallback;
	};
}

#endif//__UIWKEWEBKIT_H_

实现部分代码:

#include "StdAfx.h"
#include "UIWkeWebkit.h"
#pragma comment(lib, "DuiEx/wke/wke")

namespace DuiLib{
///////////////////////////////////////////////////
//网页加载状态监测线程
DWORD WINAPI CheckThreadFun(LPVOID lpParam)
{
	CWkeWebkitUI* pWebkitUI=(CWkeWebkitUI*)lpParam;
	wkeWebView	pWebView=pWebkitUI->GetWebView();
	if ( NULL == pWebView )
		return 1;
	CWkeWebkitLoadCallback* pCallback=pWebkitUI->GetLoadCallback();
	if ( NULL == pCallback )
		return 1;
	bool bOver=false;
	while( !pWebView->isLoaded() )
	{
		if ( !bOver && pWebView->isDocumentReady() )
		{
			pCallback->OnDocumentReady();
			bOver=true;
		}
		if ( pWebView->isLoadFailed() )
		{
			pCallback->OnLoadFailed();
			return 1;
		}
		::Sleep(30);
	}
	if ( pWebView->isLoadComplete() )
		pCallback->OnLoadComplete();
	return 0;
}

//////////////////////////////////////////////////////
int CWkeWebkitUI::m_bWebkitCount=0;
CWkeWebkitUI::CWkeWebkitUI(void)
:m_pWebView(NULL)
,m_hCheckThread(NULL)
,m_pLoadCallback(NULL)
,m_pClientCallback(NULL)
{
	if ( 0 == m_bWebkitCount )
		wkeInit();
	m_pWebView=wkeCreateWebView();
	m_pWebView->setBufHandler(this);
	m_ClientHandler.onTitleChanged	=&CWkeWebkitUI::OnTitleChange;
	m_ClientHandler.onURLChanged	=&CWkeWebkitUI::OnUrlChange;
	m_bWebkitCount++;
}

CWkeWebkitUI::~CWkeWebkitUI(void)
{
	StopCheckThread();
	m_pManager->KillTimer(this);
	wkeDestroyWebView(m_pWebView);
	m_bWebkitCount--;
	if ( 0 == m_bWebkitCount )
		wkeShutdown();
}

LPCTSTR CWkeWebkitUI::GetClass() const
{
	return L"WkeWebkitUI";
}

LPVOID CWkeWebkitUI::GetInterface( LPCTSTR pstrName )
{
	if( _tcscmp(pstrName, _T("WkeWebkit")) == 0 ) 
		return static_cast<CWkeWebkitUI*>(this);
	return CControlUI::GetInterface(pstrName);
}

void CWkeWebkitUI::DoEvent( TEventUI& event )
{
	switch( event.Type )
	{
	case UIEVENT_SETFOCUS:
		if ( m_pWebView ) m_pWebView->focus(); break;
	case UIEVENT_KILLFOCUS:
		if ( m_pWebView ) m_pWebView->unfocus(); break;
	case UIEVENT_WINDOWSIZE:
		if ( m_pWebView ) m_pWebView->resize(GET_X_LPARAM(event.lParam), GET_Y_LPARAM(event.lParam)); break;
	case UIEVENT_CHAR:
		{
			if ( NULL == m_pWebView ) break;
			unsigned int charCode = event.wParam;
			unsigned int flags = 0;
			if (HIWORD(event.lParam) & KF_REPEAT)
				flags |= WKE_REPEAT;
			if (HIWORD(event.lParam) & KF_EXTENDED)
				flags |= WKE_EXTENDED;
			bool bHandled=m_pWebView->keyPress(charCode, flags, false);
			if ( bHandled )
				return ;
			break;
		}
	case UIEVENT_KEYDOWN:
		{
			if ( NULL == m_pWebView ) break;
			unsigned int flags = 0;
			if (HIWORD(event.lParam) & KF_REPEAT)
				flags |= WKE_REPEAT;
			if (HIWORD(event.lParam) & KF_EXTENDED)
				flags |= WKE_EXTENDED;
			bool bHandled=m_pWebView->keyDown(event.wParam, flags, false);
			if ( event.wParam == VK_F5 )
				Refresh();
			if ( bHandled )
				return ;
			break;
		}
	case UIEVENT_KEYUP:
		{
			if ( NULL == m_pWebView ) break;
			unsigned int flags = 0;
			if (HIWORD(event.lParam) & KF_REPEAT)
				flags |= WKE_REPEAT;
			if (HIWORD(event.lParam) & KF_EXTENDED)
				flags |= WKE_EXTENDED;
			bool bHandled=m_pWebView->keyUp(event.wParam, flags, false);
			if ( bHandled )
				return ;
			break;
		}
	case UIEVENT_CONTEXTMENU:
		{
			if ( NULL == m_pWebView ) break;
			unsigned int flags = 0;
			if (event.wParam & MK_CONTROL)
				flags |= WKE_CONTROL;
			if (event.wParam & MK_SHIFT)
				flags |= WKE_SHIFT;
			if (event.wParam & MK_LBUTTON)
				flags |= WKE_LBUTTON;
			if (event.wParam & MK_MBUTTON)
				flags |= WKE_MBUTTON;
			if (event.wParam & MK_RBUTTON)
				flags |= WKE_RBUTTON;
			bool handled = m_pWebView->contextMenuEvent(event.ptMouse.x, event.ptMouse.y, flags);
			if ( handled )
				return ;
			break;
		}
	case UIEVENT_MOUSEMOVE:
	case UIEVENT_BUTTONDOWN:
	case UIEVENT_BUTTONUP:
	case UIEVENT_RBUTTONDOWN:
	case UIEVENT_DBLCLICK:
		{
			HWND hWnd=m_pManager->GetPaintWindow();
			if ( event.Type == UIEVENT_BUTTONDOWN )
			{
				::SetFocus(hWnd);
				SetCapture(hWnd);
			}
			else if ( event.Type == UIEVENT_BUTTONUP )
				ReleaseCapture();
			unsigned int flags = 0;
			if (event.wParam & MK_CONTROL)
				flags |= WKE_CONTROL;
			if (event.wParam & MK_SHIFT)
				flags |= WKE_SHIFT;

			if (event.wParam & MK_LBUTTON)
				flags |= WKE_LBUTTON;
			if (event.wParam & MK_MBUTTON)
				flags |= WKE_MBUTTON;
			if (event.wParam & MK_RBUTTON)
				flags |= WKE_RBUTTON;
			UINT uMsg=0;
			switch ( event.Type )
			{
			case UIEVENT_BUTTONDOWN:	uMsg=WM_LBUTTONDOWN; break;
			case UIEVENT_BUTTONUP:		uMsg=WM_LBUTTONUP; break;
			case UIEVENT_RBUTTONDOWN:	uMsg=WM_RBUTTONDOWN; break;
			case UIEVENT_DBLCLICK:		uMsg=WM_LBUTTONDBLCLK; break;
			case UIEVENT_MOUSEMOVE:		uMsg=WM_MOUSEMOVE; break;
			}
			bool bHandled = m_pWebView->mouseEvent(uMsg, event.ptMouse.x-m_rcItem.left, \
				event.ptMouse.y-m_rcItem.top, flags);
			if ( bHandled )
				return ;
			break;
		}
	case UIEVENT_TIMER:
		if ( m_pWebView )
			m_pWebView->tick();
		break;
	case UIEVENT_SCROLLWHEEL:
		{
			POINT pt;
			pt.x = LOWORD(event.lParam);
			pt.y = HIWORD(event.lParam);
			int nFlag=GET_X_LPARAM(event.wParam);
			int delta = (nFlag==SB_LINEDOWN)?-120:120;
			unsigned int flags = 0;
			if (event.wParam & MK_CONTROL)
				flags |= WKE_CONTROL;
			if (event.wParam & MK_SHIFT)
				flags |= WKE_SHIFT;
			if (event.wParam & MK_LBUTTON)
				flags |= WKE_LBUTTON;
			if (event.wParam & MK_MBUTTON)
				flags |= WKE_MBUTTON;
			if (event.wParam & MK_RBUTTON)
				flags |= WKE_RBUTTON;
			bool handled = m_pWebView->mouseWheel(pt.x, pt.y, delta, flags);
			if ( handled )
				return ;
			break;
		}
	default:
		CControlUI::DoEvent(event); break;
	}
}

void CWkeWebkitUI::DoPaint( HDC hDC, const RECT& rcPaint )
{
	if ( m_pWebView )
	{
		RECT rcInsert;
		IntersectRect(&rcInsert, &m_rcItem, &rcPaint);
		m_pWebView->paint(hDC, rcInsert.left, rcInsert.top, \
			rcInsert.right-rcInsert.left, rcInsert.bottom-rcInsert.top, \
			rcInsert.left-m_rcItem.left, rcInsert.top-m_rcItem.top, true);
	}
}

void CWkeWebkitUI::onBufUpdated( const HDC hdc,int x, int y, int cx, int cy )
{
	RECT rcValide={ x, y, x+cx, y+cy };
	::OffsetRect(&rcValide, m_rcItem.left, m_rcItem.top);
	HWND hWnd=m_pManager->GetPaintWindow();
	::InvalidateRect(hWnd, &rcValide, TRUE);
}

void CWkeWebkitUI::Navigate( LPCTSTR lpUrl )
{
	if ( m_pWebView )
	{
		m_pWebView->loadURL(lpUrl);
		StartCheckThread();
	}
}

void CWkeWebkitUI::SetPos( RECT rc )
{
	CControlUI::SetPos(rc);
	if ( m_pWebView )
		m_pWebView->resize(rc.right-rc.left, rc.bottom-rc.top);
}

void CWkeWebkitUI::DoInit()
{
	if ( !m_strUrl.empty() )
		Navigate(m_strUrl.c_str());
	m_pManager->SetTimer(this, 100, 100);
}

void CWkeWebkitUI::StartCheckThread()
{
	StopCheckThread();
	m_hCheckThread=::CreateThread(NULL, 0, CheckThreadFun, this, 0, NULL);
}

void CWkeWebkitUI::StopCheckThread()
{
	if ( m_hCheckThread )
	{
		if ( ::WaitForSingleObject(m_hCheckThread, 10) != WAIT_OBJECT_0 )
			::TerminateThread(m_hCheckThread, 0);
		::CloseHandle(m_hCheckThread);
		m_hCheckThread = NULL;
	}
}

bool CWkeWebkitUI::CanGoBack() const
{
	return m_pWebView?m_pWebView->canGoBack():false;
}

bool CWkeWebkitUI::GoBack()
{
	return m_pWebView?m_pWebView->goBack():false;
}

bool CWkeWebkitUI::CanGoForward() const
{
	return m_pWebView?m_pWebView->canGoForward():false;
}

bool CWkeWebkitUI::GoForward()
{
	return m_pWebView?m_pWebView->goForward():false;
}

void CWkeWebkitUI::StopLoad()
{
	if ( m_pWebView )
		m_pWebView->stopLoading();
}

void CWkeWebkitUI::Refresh()
{
	if ( m_pWebView )
	{
		StopCheckThread();
		m_pWebView->reload();
		StartCheckThread();
	}
}

wkeWebView CWkeWebkitUI::GetWebView()
{
	return m_pWebView;
}

void CWkeWebkitUI::SetLoadCallback( CWkeWebkitLoadCallback* pCallback )
{
	m_pLoadCallback=pCallback;
}

CWkeWebkitLoadCallback* CWkeWebkitUI::GetLoadCallback()
{
	return m_pLoadCallback;
}

void CWkeWebkitUI::OnTitleChange( const struct _wkeClientHandler* clientHandler, const wkeString title )
{

}

void CWkeWebkitUI::OnUrlChange( const struct _wkeClientHandler* clientHandler, const wkeString url )
{

}

void CWkeWebkitUI::LoadFile( LPCTSTR lpFile )
{
	if ( m_pWebView )
		m_pWebView->loadFile(lpFile);
}

void CWkeWebkitUI::LoadHtml( LPCTSTR lpHtml )
{
	if ( m_pWebView )
		m_pWebView->loadHTML(lpHtml);
}

const wstring& CWkeWebkitUI::GetUrl() const
{
	return m_strUrl;
}

void CWkeWebkitUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue )
{
	if ( _tcscmp(pstrName, _T("url")) == 0 )
		m_strUrl = pstrValue;
	else
		CControlUI::SetAttribute(pstrName, pstrValue);
}

}

解析:

主要处理的就是消息部分,把这个区域的那种消息分发给wke的接口去处理。另外就是加了个线程,检测网页加载状态,通过回调通知网页加载完成、失败、DOC完成(需要用户先初始化回调指针才会通知用户)。

控件定义完成后,我们需要来配置XML了:

<WkeWebkit name="webkit1" url="http://192.168.1.20:92/?mainctl=chrome&version=1.2.3"/>

放在一个布局里面,指定URL即可,你也可以加上其他属性然后在SetAttribute中对这些属性进行初始化。

运行程序,一个无窗口的chrome内核网页控件就展示出来了:

分享个Duilib中基于wke的浏览器控件_第1张图片

后记

wke加载网页只能用于展示以及JS交互处理,毕竟裁剪后还是要失去很多其他功能的,对于chrome内核10M的大小已经是相当的不错了,另外还有一个EaWebkit也是基于chrome的扩展,编译成DLL后只有6M左右,也是开源的,大家可以网上看看。


你可能感兴趣的:(分享个Duilib中基于wke的浏览器控件)