mirror of
https://github.com/vczh-libraries/Release.git
synced 2026-02-05 19:40:03 +08:00
13485 lines
360 KiB
C++
13485 lines
360 KiB
C++
/***********************************************************************
|
|
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
|
|
DEVELOPER: Zihan Chen(vczh)
|
|
***********************************************************************/
|
|
#include "GacUIWindows.h"
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\WINNATIVEWINDOW.CPP
|
|
***********************************************************************/
|
|
|
|
#pragma comment(lib, "Imm32.lib")
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace collections;
|
|
|
|
HWND GetHWNDFromNativeWindowHandle(INativeWindow* window)
|
|
{
|
|
if(!window) return NULL;
|
|
IWindowsForm* form=GetWindowsForm(window);
|
|
if(!form) return NULL;
|
|
return form->GetWindowHandle();
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsClass
|
|
***********************************************************************/
|
|
|
|
class WinClass : public Object
|
|
{
|
|
protected:
|
|
WString name;
|
|
WNDCLASSEX windowClass;
|
|
ATOM windowAtom;
|
|
|
|
public:
|
|
WinClass(WString _name, bool shadow, bool ownDC, WNDPROC procedure, HINSTANCE hInstance)
|
|
{
|
|
name=_name;
|
|
windowClass.cbSize=sizeof(windowClass);
|
|
windowClass.style=CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | (shadow?CS_DROPSHADOW:0) | (ownDC?CS_OWNDC:0);
|
|
windowClass.lpfnWndProc=procedure;
|
|
windowClass.cbClsExtra=0;
|
|
windowClass.cbWndExtra=0;
|
|
windowClass.hInstance=hInstance;
|
|
windowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
|
|
windowClass.hCursor=NULL;//LoadCursor(NULL,IDC_ARROW);
|
|
windowClass.hbrBackground=GetSysColorBrush(COLOR_BTNFACE);
|
|
windowClass.lpszMenuName=NULL;
|
|
windowClass.lpszClassName=name.Buffer();
|
|
windowClass.hIconSm=NULL;
|
|
windowAtom=RegisterClassEx(&windowClass);
|
|
}
|
|
|
|
bool IsAvailable()
|
|
{
|
|
return windowAtom!=0;
|
|
}
|
|
|
|
WString GetName()
|
|
{
|
|
return name;
|
|
}
|
|
|
|
ATOM GetClassAtom()
|
|
{
|
|
return windowAtom;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsForm
|
|
***********************************************************************/
|
|
|
|
class WindowsForm : public Object, public INativeWindow, public IWindowsForm
|
|
{
|
|
protected:
|
|
|
|
LONG_PTR InternalGetExStyle()
|
|
{
|
|
return GetWindowLongPtr(handle, GWL_EXSTYLE);
|
|
}
|
|
|
|
void InternalSetExStyle(LONG_PTR exStyle)
|
|
{
|
|
LONG_PTR result = SetWindowLongPtr(handle, GWL_EXSTYLE, exStyle);
|
|
SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
|
|
bool GetExStyle(LONG_PTR exStyle)
|
|
{
|
|
LONG_PTR Long=InternalGetExStyle();
|
|
return (Long & exStyle) != 0;
|
|
}
|
|
|
|
void SetExStyle(LONG_PTR exStyle, bool available)
|
|
{
|
|
LONG_PTR Long = InternalGetExStyle();
|
|
if(available)
|
|
{
|
|
Long |= exStyle;
|
|
}
|
|
else
|
|
{
|
|
Long &= ~exStyle;
|
|
}
|
|
InternalSetExStyle(Long);
|
|
}
|
|
|
|
bool GetStyle(LONG_PTR style)
|
|
{
|
|
LONG_PTR Long = GetWindowLongPtr(handle, GWL_STYLE);
|
|
return (Long & style) != 0;
|
|
}
|
|
|
|
void SetStyle(LONG_PTR style, bool available)
|
|
{
|
|
LONG_PTR Long = GetWindowLongPtr(handle, GWL_STYLE);
|
|
if(available)
|
|
{
|
|
Long |= style;
|
|
}
|
|
else
|
|
{
|
|
Long &= ~style;
|
|
}
|
|
SetWindowLongPtr(handle, GWL_STYLE, Long);
|
|
SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
|
|
NativeWindowMouseInfo ConvertMouse(WPARAM wParam, LPARAM lParam, bool wheelMessage, bool nonClient)
|
|
{
|
|
NativeWindowMouseInfo info;
|
|
|
|
info.nonClient = false;
|
|
if (nonClient)
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case HTMINBUTTON:
|
|
case HTMAXBUTTON:
|
|
case HTCLOSE:
|
|
break;
|
|
default:
|
|
info.nonClient = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(wheelMessage)
|
|
{
|
|
info.wheel=GET_WHEEL_DELTA_WPARAM(wParam);
|
|
wParam=GET_KEYSTATE_WPARAM(wParam);
|
|
}
|
|
else
|
|
{
|
|
info.wheel=0;
|
|
}
|
|
|
|
if (nonClient)
|
|
{
|
|
info.ctrl = WinIsKeyPressing(VK_CONTROL);
|
|
info.shift = WinIsKeyPressing(VK_SHIFT);
|
|
info.left= WinIsKeyPressing(MK_LBUTTON);
|
|
info.middle= WinIsKeyPressing(MK_MBUTTON);
|
|
info.right = WinIsKeyPressing(MK_RBUTTON);
|
|
|
|
POINTS point = MAKEPOINTS(lParam);
|
|
Point offset = GetClientBoundsInScreen().LeftTop();
|
|
info.x = point.x - offset.x;
|
|
info.y = point.y - offset.y;
|
|
}
|
|
else
|
|
{
|
|
info.ctrl=(wParam & MK_CONTROL)!=0;
|
|
info.shift=(wParam & MK_SHIFT)!=0;
|
|
info.left=(wParam & MK_LBUTTON)!=0;
|
|
info.middle=(wParam & MK_MBUTTON)!=0;
|
|
info.right=(wParam & MK_RBUTTON)!=0;
|
|
|
|
POINTS point = MAKEPOINTS(lParam);
|
|
|
|
if (wheelMessage)
|
|
{
|
|
Point offset = GetClientBoundsInScreen().LeftTop();
|
|
info.x = point.x - offset.x;
|
|
info.y = point.y - offset.y;
|
|
}
|
|
else
|
|
{
|
|
info.x = point.x;
|
|
info.y = point.y;
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
NativeWindowKeyInfo ConvertKey(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
NativeWindowKeyInfo info;
|
|
info.code=wParam;
|
|
info.ctrl=WinIsKeyPressing(VK_CONTROL);
|
|
info.shift=WinIsKeyPressing(VK_SHIFT);
|
|
info.alt=WinIsKeyPressing(VK_MENU);
|
|
info.capslock=WinIsKeyToggled(VK_CAPITAL);
|
|
return info;
|
|
}
|
|
|
|
NativeWindowCharInfo ConvertChar(WPARAM wParam)
|
|
{
|
|
NativeWindowCharInfo info;
|
|
info.code=(wchar_t)wParam;
|
|
info.ctrl=WinIsKeyPressing(VK_CONTROL);
|
|
info.shift=WinIsKeyPressing(VK_SHIFT);
|
|
info.alt=WinIsKeyPressing(VK_MENU);
|
|
info.capslock=WinIsKeyToggled(VK_CAPITAL);
|
|
return info;
|
|
}
|
|
|
|
void TrackMouse(bool enable)
|
|
{
|
|
TRACKMOUSEEVENT trackMouseEvent;
|
|
trackMouseEvent.cbSize=sizeof(trackMouseEvent);
|
|
trackMouseEvent.hwndTrack=handle;
|
|
trackMouseEvent.dwFlags=(enable?0:TME_CANCEL) | TME_HOVER | TME_LEAVE;
|
|
trackMouseEvent.dwHoverTime=HOVER_DEFAULT;
|
|
TrackMouseEvent(&trackMouseEvent);
|
|
}
|
|
|
|
void UpdateCompositionForContent()
|
|
{
|
|
HIMC imc = ImmGetContext(handle);
|
|
COMPOSITIONFORM cf;
|
|
cf.dwStyle = CFS_POINT;
|
|
cf.ptCurrentPos.x = (int)caretPoint.x;
|
|
cf.ptCurrentPos.y = (int)caretPoint.y;
|
|
ImmSetCompositionWindow(imc, &cf);
|
|
ImmReleaseContext(handle, imc);
|
|
}
|
|
|
|
bool HandleMessageInternal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
|
|
{
|
|
bool transferFocusEvent = false;
|
|
bool nonClient = false;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_RBUTTONDBLCLK:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_MBUTTONDBLCLK:
|
|
transferFocusEvent=true;
|
|
}
|
|
switch(uMsg)
|
|
{
|
|
// ************************************** moving and sizing
|
|
case WM_MOVING:case WM_SIZING:
|
|
{
|
|
LPRECT rawBounds=(LPRECT)lParam;
|
|
Rect bounds(rawBounds->left, rawBounds->top, rawBounds->right, rawBounds->bottom);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Moving(bounds, false);
|
|
}
|
|
if( rawBounds->left!=bounds.Left()
|
|
|| rawBounds->top!=bounds.Top()
|
|
|| rawBounds->right!=bounds.Right()
|
|
|| rawBounds->bottom!=bounds.Bottom())
|
|
{
|
|
rawBounds->left=(int)bounds.Left();
|
|
rawBounds->top=(int)bounds.Top();
|
|
rawBounds->right=(int)bounds.Right();
|
|
rawBounds->bottom=(int)bounds.Bottom();
|
|
result=TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case WM_MOVE:case WM_SIZE:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Moved();
|
|
}
|
|
}
|
|
break;
|
|
// ************************************** state
|
|
case WM_ENABLE:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
if(wParam==TRUE)
|
|
{
|
|
listeners[i]->Enabled();
|
|
}
|
|
else
|
|
{
|
|
listeners[i]->Disabled();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_SETFOCUS:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->GotFocus();
|
|
}
|
|
}
|
|
break;
|
|
case WM_KILLFOCUS:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LostFocus();
|
|
}
|
|
}
|
|
break;
|
|
case WM_ACTIVATE:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
if(wParam==WA_ACTIVE || wParam==WA_CLICKACTIVE)
|
|
{
|
|
listeners[i]->Activated();
|
|
}
|
|
else
|
|
{
|
|
listeners[i]->Deactivated();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_SHOWWINDOW:
|
|
{
|
|
if(wParam==TRUE)
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Opened();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Closed();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_CLOSE:
|
|
{
|
|
bool cancel=false;
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Closing(cancel);
|
|
}
|
|
return cancel;
|
|
}
|
|
break;
|
|
// ************************************** mouse
|
|
case WM_NCLBUTTONDOWN:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LeftButtonDown(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCLBUTTONUP:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_LBUTTONUP:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LeftButtonUp(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCLBUTTONDBLCLK:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LeftButtonDoubleClick(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCRBUTTONDOWN:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->RightButtonDown(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCRBUTTONUP:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_RBUTTONUP:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->RightButtonUp(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCRBUTTONDBLCLK:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_RBUTTONDBLCLK:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->RightButtonDoubleClick(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCMBUTTONDOWN:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_MBUTTONDOWN:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MiddleButtonDown(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCMBUTTONUP:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_MBUTTONUP:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MiddleButtonUp(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCMBUTTONDBLCLK:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_MBUTTONDBLCLK:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MiddleButtonDoubleClick(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCMOUSEMOVE:
|
|
if (!customFrameMode) break;
|
|
nonClient = true;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
|
|
if(info.x!=mouseLastX || info.y!=mouseLastY)
|
|
{
|
|
if(!mouseHoving)
|
|
{
|
|
mouseHoving=true;
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MouseEntered();
|
|
}
|
|
TrackMouse(true);
|
|
}
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MouseMoving(info);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
// ************************************** wheel
|
|
case WM_MOUSEHWHEEL:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->HorizontalWheel(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->VerticalWheel(info);
|
|
}
|
|
}
|
|
break;
|
|
// ************************************** mouse state
|
|
case WM_NCMOUSELEAVE:
|
|
nonClient = true;
|
|
case WM_MOUSELEAVE:
|
|
if (customFrameMode == nonClient)
|
|
{
|
|
mouseLastX=-1;
|
|
mouseLastY=-1;
|
|
mouseHoving=false;
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MouseLeaved();
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCMOUSEHOVER:
|
|
case WM_MOUSEHOVER:
|
|
{
|
|
TrackMouse(true);
|
|
}
|
|
break;
|
|
// ************************************** key
|
|
case WM_KEYUP:
|
|
{
|
|
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->KeyUp(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_KEYDOWN:
|
|
{
|
|
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->KeyDown(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_SYSKEYUP:
|
|
{
|
|
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
|
|
if (supressingAlt && !info.ctrl && !info.shift && info.code == VK_MENU)
|
|
{
|
|
supressingAlt = false;
|
|
break;
|
|
}
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->SysKeyUp(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
|
|
if (supressingAlt && !info.ctrl && !info.shift && info.code == VK_MENU)
|
|
{
|
|
break;
|
|
}
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->SysKeyDown(info);
|
|
}
|
|
}
|
|
break;
|
|
case WM_CHAR:
|
|
{
|
|
NativeWindowCharInfo info=ConvertChar(wParam);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Char(info);
|
|
}
|
|
}
|
|
break;
|
|
// ************************************** painting
|
|
case WM_PAINT:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Paint();
|
|
}
|
|
}
|
|
break;
|
|
case WM_ERASEBKGND:
|
|
result = 0;
|
|
return true;
|
|
case WM_NCPAINT:
|
|
case WM_SYNCPAINT:
|
|
if(customFrameMode)
|
|
{
|
|
result=0;
|
|
return true;
|
|
}
|
|
break;
|
|
// ************************************** IME
|
|
case WM_IME_SETCONTEXT:
|
|
if(wParam==TRUE)
|
|
{
|
|
HIMC imc = ImmGetContext(handle);
|
|
ImmAssociateContext(hwnd, imc);
|
|
ImmReleaseContext(handle, imc);
|
|
}
|
|
break;
|
|
case WM_IME_STARTCOMPOSITION:
|
|
UpdateCompositionForContent();
|
|
break;
|
|
// ************************************** hit test
|
|
case WM_NCHITTEST:
|
|
{
|
|
POINTS location=MAKEPOINTS(lParam);
|
|
Point windowLocation=GetBounds().LeftTop();
|
|
location.x-=(SHORT)windowLocation.x;
|
|
location.y-=(SHORT)windowLocation.y;
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
switch(listeners[i]->HitTest(Point(location.x, location.y)))
|
|
{
|
|
case INativeWindowListener::BorderNoSizing:
|
|
result=HTBORDER;
|
|
return true;
|
|
case INativeWindowListener::BorderLeft:
|
|
result=HTLEFT;
|
|
return true;
|
|
case INativeWindowListener::BorderRight:
|
|
result=HTRIGHT;
|
|
return true;
|
|
case INativeWindowListener::BorderTop:
|
|
result=HTTOP;
|
|
return true;
|
|
case INativeWindowListener::BorderBottom:
|
|
result=HTBOTTOM;
|
|
return true;
|
|
case INativeWindowListener::BorderLeftTop:
|
|
result=HTTOPLEFT;
|
|
return true;
|
|
case INativeWindowListener::BorderRightTop:
|
|
result=HTTOPRIGHT;
|
|
return true;
|
|
case INativeWindowListener::BorderLeftBottom:
|
|
result=HTBOTTOMLEFT;
|
|
return true;
|
|
case INativeWindowListener::BorderRightBottom:
|
|
result=HTBOTTOMRIGHT;
|
|
return true;
|
|
case INativeWindowListener::Title:
|
|
result=HTCAPTION;
|
|
return true;
|
|
case INativeWindowListener::ButtonMinimum:
|
|
result=HTMINBUTTON;
|
|
return true;
|
|
case INativeWindowListener::ButtonMaximum:
|
|
result=HTMAXBUTTON;
|
|
return true;
|
|
case INativeWindowListener::ButtonClose:
|
|
result=HTCLOSE;
|
|
return true;
|
|
case INativeWindowListener::Client:
|
|
result=HTCLIENT;
|
|
return true;
|
|
case INativeWindowListener::Icon:
|
|
result=HTSYSMENU;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
// ************************************** MISC
|
|
case WM_SETCURSOR:
|
|
{
|
|
DWORD hitTestResult=LOWORD(lParam);
|
|
if(hitTestResult==HTCLIENT)
|
|
{
|
|
HCURSOR cursorHandle=cursor->GetCursorHandle();
|
|
if(GetCursor()!=cursorHandle)
|
|
{
|
|
SetCursor(cursorHandle);
|
|
}
|
|
result=TRUE;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case WM_NCCALCSIZE:
|
|
if((BOOL)wParam && customFrameMode)
|
|
{
|
|
result=0;
|
|
return true;
|
|
}
|
|
break;
|
|
case WM_NCACTIVATE:
|
|
if(customFrameMode)
|
|
{
|
|
if(wParam==TRUE)
|
|
{
|
|
result=FALSE;
|
|
}
|
|
else
|
|
{
|
|
result=TRUE;
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(IsWindow(hwnd)!=0)
|
|
{
|
|
if(transferFocusEvent && IsFocused())
|
|
{
|
|
WindowsForm* window=this;
|
|
while(window->parentWindow && window->alwaysPassFocusToParent)
|
|
{
|
|
window=window->parentWindow;
|
|
}
|
|
if(window!=this)
|
|
{
|
|
window->SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (customFrameMode)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCLBUTTONDOWN:
|
|
switch (wParam)
|
|
{
|
|
case HTMINBUTTON:
|
|
case HTMAXBUTTON:
|
|
case HTCLOSE:
|
|
result = 0;
|
|
return true;
|
|
}
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
{
|
|
POINTS location = MAKEPOINTS(lParam);
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
switch(listeners[i]->HitTest(Point(location.x, location.y)))
|
|
{
|
|
case INativeWindowListener::ButtonMinimum:
|
|
ShowMinimized();
|
|
return false;
|
|
case INativeWindowListener::ButtonMaximum:
|
|
if (GetSizeState() == INativeWindow::Maximized)
|
|
{
|
|
ShowRestored();
|
|
}
|
|
else
|
|
{
|
|
ShowMaximized();
|
|
}
|
|
return false;
|
|
case INativeWindowListener::ButtonClose:
|
|
Hide();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
protected:
|
|
HWND handle;
|
|
WString title;
|
|
WindowsCursor* cursor;
|
|
Point caretPoint;
|
|
WindowsForm* parentWindow;
|
|
bool alwaysPassFocusToParent;
|
|
List<INativeWindowListener*> listeners;
|
|
vint mouseLastX;
|
|
vint mouseLastY;
|
|
vint mouseHoving;
|
|
Interface* graphicsHandler;
|
|
bool customFrameMode;
|
|
List<Ptr<INativeMessageHandler>> messageHandlers;
|
|
bool supressingAlt;
|
|
|
|
public:
|
|
WindowsForm(HWND parent, WString className, HINSTANCE hInstance)
|
|
:cursor(0)
|
|
,parentWindow(0)
|
|
,alwaysPassFocusToParent(false)
|
|
,mouseLastX(-1)
|
|
,mouseLastY(-1)
|
|
,mouseHoving(false)
|
|
,graphicsHandler(0)
|
|
,customFrameMode(false)
|
|
,supressingAlt(false)
|
|
{
|
|
DWORD exStyle = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT;
|
|
DWORD style = WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
|
|
handle=CreateWindowEx(exStyle, className.Buffer(), L"", style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parent, NULL, hInstance, NULL);
|
|
}
|
|
|
|
~WindowsForm()
|
|
{
|
|
List<INativeWindowListener*> copiedListeners;
|
|
CopyFrom(copiedListeners, listeners);
|
|
for(vint i=0;i<copiedListeners.Count();i++)
|
|
{
|
|
INativeWindowListener* listener=copiedListeners[i];
|
|
if(listeners.Contains(listener))
|
|
{
|
|
listener->Destroyed();
|
|
}
|
|
}
|
|
DestroyWindow(handle);
|
|
}
|
|
|
|
void InvokeDestroying()
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Destroying();
|
|
}
|
|
}
|
|
|
|
bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
|
|
{
|
|
bool skip = false;
|
|
{
|
|
FOREACH(Ptr<INativeMessageHandler>, handler, messageHandlers)
|
|
{
|
|
handler->BeforeHandle(hwnd, uMsg, wParam, lParam, skip);
|
|
}
|
|
if (skip)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
skip = HandleMessageInternal(hwnd, uMsg, wParam, lParam, result);
|
|
if (GetWindowsFormFromHandle(hwnd))
|
|
{
|
|
FOREACH(Ptr<INativeMessageHandler>, handler, messageHandlers)
|
|
{
|
|
handler->AfterHandle(hwnd, uMsg, wParam, lParam, skip, result);
|
|
}
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
HWND GetWindowHandle()override
|
|
{
|
|
return handle;
|
|
}
|
|
|
|
Interface* GetGraphicsHandler()override
|
|
{
|
|
return graphicsHandler;
|
|
}
|
|
|
|
void SetGraphicsHandler(Interface* handler)override
|
|
{
|
|
graphicsHandler=handler;
|
|
}
|
|
|
|
bool InstallMessageHandler(Ptr<INativeMessageHandler> handler)override
|
|
{
|
|
if (messageHandlers.Contains(handler.Obj()))
|
|
{
|
|
return false;
|
|
}
|
|
messageHandlers.Add(handler);
|
|
return true;
|
|
}
|
|
|
|
bool UninstallMessageHandler(Ptr<INativeMessageHandler> handler)override
|
|
{
|
|
vint index = messageHandlers.IndexOf(handler.Obj());
|
|
if (index == -1)return false;
|
|
messageHandlers.RemoveAt(handler);
|
|
return true;
|
|
}
|
|
|
|
Rect GetBounds()
|
|
{
|
|
RECT rect;
|
|
GetWindowRect(handle, &rect);
|
|
return Rect(rect.left, rect.top, rect.right, rect.bottom);
|
|
}
|
|
|
|
void SetBounds(const Rect& bounds)
|
|
{
|
|
Rect newBounds=bounds;
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->Moving(newBounds, true);
|
|
}
|
|
MoveWindow(handle, (int)newBounds.Left(), (int)newBounds.Top(), (int)newBounds.Width(), (int)newBounds.Height(), FALSE);
|
|
}
|
|
|
|
Size GetClientSize()
|
|
{
|
|
return GetClientBoundsInScreen().GetSize();
|
|
}
|
|
|
|
void SetClientSize(Size size)
|
|
{
|
|
RECT required={0,0,(int)size.x,(int)size.y};
|
|
RECT bounds;
|
|
GetWindowRect(handle, &bounds);
|
|
AdjustWindowRect(&required, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE);
|
|
SetBounds(Rect(Point(bounds.left, bounds.top), Size(required.right-required.left, required.bottom-required.top)));
|
|
}
|
|
|
|
Rect GetClientBoundsInScreen()
|
|
{
|
|
if(customFrameMode)
|
|
{
|
|
return GetBounds();
|
|
}
|
|
else
|
|
{
|
|
RECT required={0,0,0,0};
|
|
RECT bounds;
|
|
GetWindowRect(handle, &bounds);
|
|
AdjustWindowRect(&required, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE);
|
|
return Rect(
|
|
Point(
|
|
(bounds.left-required.left),
|
|
(bounds.top-required.top)
|
|
),
|
|
Size(
|
|
(bounds.right-bounds.left)-(required.right-required.left),
|
|
(bounds.bottom-bounds.top)-(required.bottom-required.top)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
WString GetTitle()
|
|
{
|
|
return title;
|
|
}
|
|
|
|
void SetTitle(WString _title)
|
|
{
|
|
title=_title;
|
|
SetWindowText(handle, title.Buffer());
|
|
}
|
|
|
|
INativeCursor* GetWindowCursor()
|
|
{
|
|
return cursor;
|
|
}
|
|
|
|
void SetWindowCursor(INativeCursor* _cursor)
|
|
{
|
|
WindowsCursor* newCursor=dynamic_cast<WindowsCursor*>(_cursor);
|
|
if(newCursor && cursor!=newCursor)
|
|
{
|
|
cursor=newCursor;
|
|
if(mouseHoving && IsVisible())
|
|
{
|
|
SetCursor(cursor->GetCursorHandle());
|
|
}
|
|
}
|
|
}
|
|
|
|
Point GetCaretPoint()
|
|
{
|
|
return caretPoint;
|
|
}
|
|
|
|
void SetCaretPoint(Point point)
|
|
{
|
|
caretPoint=point;
|
|
UpdateCompositionForContent();
|
|
}
|
|
|
|
INativeWindow* GetParent()
|
|
{
|
|
return parentWindow;
|
|
}
|
|
|
|
void SetParent(INativeWindow* parent)
|
|
{
|
|
parentWindow=dynamic_cast<WindowsForm*>(parent);
|
|
if(parentWindow)
|
|
{
|
|
SetWindowLongPtr(handle, GWLP_HWNDPARENT, (LONG_PTR)parentWindow->handle);
|
|
}
|
|
else
|
|
{
|
|
SetWindowLongPtr(handle, GWLP_HWNDPARENT, NULL);
|
|
}
|
|
}
|
|
|
|
bool GetAlwaysPassFocusToParent()
|
|
{
|
|
return alwaysPassFocusToParent;
|
|
}
|
|
|
|
void SetAlwaysPassFocusToParent(bool value)
|
|
{
|
|
alwaysPassFocusToParent=value;
|
|
}
|
|
|
|
void EnableCustomFrameMode()
|
|
{
|
|
customFrameMode=true;
|
|
}
|
|
|
|
void DisableCustomFrameMode()
|
|
{
|
|
customFrameMode=false;
|
|
}
|
|
|
|
bool IsCustomFrameModeEnabled()
|
|
{
|
|
return customFrameMode;
|
|
}
|
|
|
|
WindowSizeState GetSizeState()
|
|
{
|
|
if(IsIconic(handle))
|
|
{
|
|
return INativeWindow::Minimized;
|
|
}
|
|
else if(IsZoomed(handle))
|
|
{
|
|
return INativeWindow::Maximized;
|
|
}
|
|
else
|
|
{
|
|
return INativeWindow::Restored;
|
|
}
|
|
}
|
|
|
|
void Show()
|
|
{
|
|
ShowWindow(handle, SW_SHOWNORMAL);
|
|
}
|
|
|
|
void ShowDeactivated()
|
|
{
|
|
ShowWindow(handle, SW_SHOWNOACTIVATE);
|
|
SetWindowPos(handle,HWND_TOP,0,0,0,0,SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
void ShowRestored()
|
|
{
|
|
ShowWindow(handle, SW_RESTORE);
|
|
}
|
|
|
|
void ShowMaximized()
|
|
{
|
|
ShowWindow(handle, SW_SHOWMAXIMIZED);
|
|
}
|
|
|
|
void ShowMinimized()
|
|
{
|
|
ShowWindow(handle, SW_SHOWMINIMIZED);
|
|
}
|
|
|
|
void Hide()
|
|
{
|
|
SendMessage(handle, WM_CLOSE, NULL, NULL);
|
|
}
|
|
|
|
bool IsVisible()
|
|
{
|
|
return IsWindowVisible(handle)!=0;
|
|
}
|
|
|
|
void Enable()
|
|
{
|
|
EnableWindow(handle, TRUE);
|
|
}
|
|
|
|
void Disable()
|
|
{
|
|
EnableWindow(handle, FALSE);
|
|
}
|
|
|
|
bool IsEnabled()
|
|
{
|
|
return IsWindowEnabled(handle)!=0;
|
|
}
|
|
|
|
void SetFocus()
|
|
{
|
|
::SetFocus(handle);
|
|
}
|
|
|
|
bool IsFocused()
|
|
{
|
|
return GetFocus()==handle;
|
|
}
|
|
|
|
void SetActivate()
|
|
{
|
|
SetActiveWindow(handle);
|
|
}
|
|
|
|
bool IsActivated()
|
|
{
|
|
return GetActiveWindow()==handle;
|
|
}
|
|
|
|
void ShowInTaskBar()
|
|
{
|
|
SetExStyle(WS_EX_APPWINDOW, true);
|
|
}
|
|
|
|
void HideInTaskBar()
|
|
{
|
|
SetExStyle(WS_EX_APPWINDOW, false);
|
|
}
|
|
|
|
bool IsAppearedInTaskBar()
|
|
{
|
|
return GetExStyle(WS_EX_APPWINDOW);
|
|
}
|
|
|
|
void EnableActivate()
|
|
{
|
|
SetExStyle(WS_EX_NOACTIVATE, false);
|
|
}
|
|
|
|
void DisableActivate()
|
|
{
|
|
SetExStyle(WS_EX_NOACTIVATE, true);
|
|
}
|
|
|
|
bool IsEnabledActivate()
|
|
{
|
|
return !GetExStyle(WS_EX_NOACTIVATE);
|
|
}
|
|
|
|
bool RequireCapture()
|
|
{
|
|
SetCapture(handle);
|
|
return true;
|
|
}
|
|
|
|
bool ReleaseCapture()
|
|
{
|
|
::ReleaseCapture();
|
|
return true;
|
|
}
|
|
|
|
bool IsCapturing()
|
|
{
|
|
return GetCapture()==handle;
|
|
}
|
|
|
|
bool GetMaximizedBox()
|
|
{
|
|
return GetStyle(WS_MAXIMIZEBOX);
|
|
}
|
|
|
|
void SetMaximizedBox(bool visible)
|
|
{
|
|
SetStyle(WS_MAXIMIZEBOX, visible);
|
|
}
|
|
|
|
bool GetMinimizedBox()
|
|
{
|
|
return GetStyle(WS_MINIMIZEBOX);
|
|
}
|
|
|
|
void SetMinimizedBox(bool visible)
|
|
{
|
|
SetStyle(WS_MINIMIZEBOX, visible);
|
|
}
|
|
|
|
bool GetBorder()
|
|
{
|
|
return GetStyle(WS_BORDER);
|
|
}
|
|
|
|
void SetBorder(bool visible)
|
|
{
|
|
SetStyle(WS_BORDER, visible);
|
|
}
|
|
|
|
bool GetSizeBox()
|
|
{
|
|
return GetStyle(WS_SIZEBOX);
|
|
}
|
|
|
|
void SetSizeBox(bool visible)
|
|
{
|
|
SetStyle(WS_SIZEBOX, visible);
|
|
}
|
|
|
|
bool GetIconVisible()
|
|
{
|
|
return GetStyle(WS_SYSMENU);
|
|
}
|
|
|
|
void SetIconVisible(bool visible)
|
|
{
|
|
SetStyle(WS_SYSMENU, visible);
|
|
}
|
|
|
|
bool GetTitleBar()
|
|
{
|
|
return GetStyle(WS_CAPTION);
|
|
}
|
|
|
|
void SetTitleBar(bool visible)
|
|
{
|
|
SetStyle(WS_CAPTION, visible);
|
|
}
|
|
|
|
bool GetTopMost()
|
|
{
|
|
return GetExStyle(WS_EX_TOPMOST);
|
|
}
|
|
|
|
void SetTopMost(bool topmost)
|
|
{
|
|
SetWindowPos(handle, (topmost ? HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
|
|
}
|
|
|
|
void SupressAlt()
|
|
{
|
|
if (!supressingAlt)
|
|
{
|
|
supressingAlt = true;
|
|
PostMessage(handle, WM_SYSKEYDOWN, VK_MENU, 0);
|
|
PostMessage(handle, WM_SYSKEYUP, VK_MENU, 0);
|
|
}
|
|
}
|
|
|
|
bool InstallListener(INativeWindowListener* listener)
|
|
{
|
|
if(listeners.Contains(listener))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
listeners.Add(listener);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool UninstallListener(INativeWindowListener* listener)
|
|
{
|
|
if(listeners.Contains(listener))
|
|
{
|
|
listeners.Remove(listener);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RedrawContent()
|
|
{
|
|
if(graphicsHandler)
|
|
{
|
|
SendMessage(this->handle, WM_PAINT, NULL, NULL);
|
|
}
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsController
|
|
***********************************************************************/
|
|
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
LRESULT CALLBACK MouseProc(int nCode , WPARAM wParam , LPARAM lParam);
|
|
|
|
class WindowsController : public Object, public virtual INativeController, public virtual INativeWindowService
|
|
{
|
|
protected:
|
|
WinClass windowClass;
|
|
WinClass godClass;
|
|
HINSTANCE hInstance;
|
|
HWND godWindow;
|
|
Dictionary<HWND, WindowsForm*> windows;
|
|
INativeWindow* mainWindow;
|
|
HWND mainWindowHandle;
|
|
|
|
WindowsCallbackService callbackService;
|
|
WindowsResourceService resourceService;
|
|
WindowsAsyncService asyncService;
|
|
WindowsClipboardService clipboardService;
|
|
WindowsImageService imageService;
|
|
WindowsScreenService screenService;
|
|
WindowsInputService inputService;
|
|
WindowsDialogService dialogService;
|
|
|
|
public:
|
|
WindowsController(HINSTANCE _hInstance)
|
|
:hInstance(_hInstance)
|
|
,windowClass(L"VczhWindow", false, false, WndProc, _hInstance)
|
|
,godClass(L"GodWindow", false, false, GodProc, _hInstance)
|
|
,mainWindow(0)
|
|
,mainWindowHandle(0)
|
|
,screenService(&GetHWNDFromNativeWindowHandle)
|
|
,inputService(&MouseProc)
|
|
,dialogService(&GetHWNDFromNativeWindowHandle)
|
|
{
|
|
godWindow=CreateWindowEx(WS_EX_CONTROLPARENT, godClass.GetName().Buffer(), L"GodWindow", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
|
|
clipboardService.SetOwnerHandle(godWindow);
|
|
inputService.SetOwnerHandle(godWindow);
|
|
}
|
|
|
|
~WindowsController()
|
|
{
|
|
inputService.StopTimer();
|
|
inputService.StopHookMouse();
|
|
clipboardService.SetOwnerHandle(NULL);
|
|
DestroyWindow(godWindow);
|
|
}
|
|
|
|
WindowsForm* GetWindowsFormFromHandle(HWND hwnd)
|
|
{
|
|
vint index = windows.Keys().IndexOf(hwnd);
|
|
if (index == -1)return 0;
|
|
return windows.Values()[index];
|
|
}
|
|
|
|
bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
|
|
{
|
|
bool skipDefaultProcedure=false;
|
|
vint index=windows.Keys().IndexOf(hwnd);
|
|
if(index!=-1)
|
|
{
|
|
WindowsForm* window=windows.Values().Get(index);
|
|
skipDefaultProcedure=window->HandleMessage(hwnd, uMsg, wParam, lParam, result);
|
|
switch(uMsg)
|
|
{
|
|
case WM_CLOSE:
|
|
if(!skipDefaultProcedure)
|
|
{
|
|
ShowWindow(window->GetWindowHandle(), SW_HIDE);
|
|
if(window!=mainWindow)
|
|
{
|
|
skipDefaultProcedure=true;
|
|
}
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
DestroyNativeWindow(window);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(hwnd==mainWindowHandle && uMsg==WM_DESTROY)
|
|
{
|
|
for(vint i=0;i<windows.Count();i++)
|
|
{
|
|
if(windows.Values().Get(i)->IsVisible())
|
|
{
|
|
windows.Values().Get(i)->Hide();
|
|
}
|
|
}
|
|
while(windows.Count())
|
|
{
|
|
DestroyNativeWindow(windows.Values().Get(0));
|
|
}
|
|
PostQuitMessage(0);
|
|
}
|
|
return skipDefaultProcedure;
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
INativeWindow* CreateNativeWindow()
|
|
{
|
|
WindowsForm* window=new WindowsForm(godWindow, windowClass.GetName(), hInstance);
|
|
windows.Add(window->GetWindowHandle(), window);
|
|
callbackService.InvokeNativeWindowCreated(window);
|
|
window->SetWindowCursor(resourceService.GetDefaultSystemCursor());
|
|
return window;
|
|
}
|
|
|
|
void DestroyNativeWindow(INativeWindow* window)
|
|
{
|
|
WindowsForm* windowsForm=dynamic_cast<WindowsForm*>(window);
|
|
windowsForm->InvokeDestroying();
|
|
if(windowsForm!=0 && windows.Keys().Contains(windowsForm->GetWindowHandle()))
|
|
{
|
|
callbackService.InvokeNativeWindowDestroyed(window);
|
|
windows.Remove(windowsForm->GetWindowHandle());
|
|
delete windowsForm;
|
|
}
|
|
}
|
|
|
|
INativeWindow* GetMainWindow()
|
|
{
|
|
return mainWindow;
|
|
}
|
|
|
|
void Run(INativeWindow* window)
|
|
{
|
|
mainWindow=window;
|
|
mainWindowHandle=GetWindowsForm(window)->GetWindowHandle();
|
|
mainWindow->Show();
|
|
MSG message;
|
|
while(GetMessage(&message, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&message);
|
|
DispatchMessage(&message);
|
|
asyncService.ExecuteAsyncTasks();
|
|
}
|
|
}
|
|
|
|
INativeWindow* GetWindow(Point location)
|
|
{
|
|
POINT p;
|
|
p.x=(int)location.x;
|
|
p.y=(int)location.y;
|
|
HWND handle=WindowFromPoint(p);
|
|
vint index=windows.Keys().IndexOf(handle);
|
|
if(index==-1)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return windows.Values().Get(index);
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
INativeCallbackService* CallbackService()
|
|
{
|
|
return &callbackService;
|
|
}
|
|
|
|
INativeResourceService* ResourceService()
|
|
{
|
|
return &resourceService;
|
|
}
|
|
|
|
INativeAsyncService* AsyncService()
|
|
{
|
|
return &asyncService;
|
|
}
|
|
|
|
INativeClipboardService* ClipboardService()
|
|
{
|
|
return &clipboardService;
|
|
}
|
|
|
|
INativeImageService* ImageService()
|
|
{
|
|
return &imageService;
|
|
}
|
|
|
|
INativeScreenService* ScreenService()
|
|
{
|
|
return &screenService;
|
|
}
|
|
|
|
INativeWindowService* WindowService()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
INativeInputService* InputService()
|
|
{
|
|
return &inputService;
|
|
}
|
|
|
|
INativeDialogService* DialogService()
|
|
{
|
|
return &dialogService;
|
|
}
|
|
|
|
bool IsWindowsVersionEqualOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
|
|
{
|
|
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
|
|
DWORDLONG const dwlConditionMask =
|
|
VerSetConditionMask(
|
|
VerSetConditionMask(
|
|
VerSetConditionMask(
|
|
0,
|
|
VER_MAJORVERSION,
|
|
VER_GREATER_EQUAL
|
|
),
|
|
VER_MINORVERSION,
|
|
VER_GREATER_EQUAL
|
|
),
|
|
VER_SERVICEPACKMAJOR,
|
|
VER_GREATER_EQUAL
|
|
);
|
|
|
|
osvi.dwMajorVersion = wMajorVersion;
|
|
osvi.dwMinorVersion = wMinorVersion;
|
|
osvi.wServicePackMajor = wServicePackMajor;
|
|
|
|
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
|
|
}
|
|
|
|
bool IsWindowsServer()
|
|
{
|
|
OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION };
|
|
DWORDLONG const dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL );
|
|
|
|
return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
|
|
}
|
|
|
|
WString GetOSVersion()
|
|
{
|
|
const vint SystemCount = 5;
|
|
DWORD SystemVersions[SystemCount] =
|
|
{
|
|
_WIN32_WINNT_WIN2K,
|
|
_WIN32_WINNT_WINXP,
|
|
_WIN32_WINNT_VISTA,
|
|
_WIN32_WINNT_WIN7,
|
|
_WIN32_WINNT_WIN8
|
|
};
|
|
vint SystemMaxServerPacks[SystemCount] =
|
|
{
|
|
4,
|
|
3,
|
|
2,
|
|
1,
|
|
0,
|
|
};
|
|
const wchar_t* SystemClientNames[SystemCount] =
|
|
{
|
|
L"Windows 2000",
|
|
L"Windows XP",
|
|
L"Windows Vista",
|
|
L"Windows 7",
|
|
L"Windows 8",
|
|
};
|
|
const wchar_t* SystemServerNames[SystemCount] =
|
|
{
|
|
L"Windows Server 2003",
|
|
L"Windows Server 2003 R2",
|
|
L"Windows Server 2008",
|
|
L"Windows Server 2008 R2",
|
|
L"Windows Server 2012",
|
|
};
|
|
|
|
bool isWindowsServer = IsWindowsServer();
|
|
for(vint systemIndex = SystemCount-1; systemIndex >=0; systemIndex--)
|
|
{
|
|
DWORD systemVersion = SystemVersions[systemIndex];
|
|
vint maxSp = SystemMaxServerPacks[systemIndex];
|
|
for(vint sp = maxSp; sp>=0; sp--)
|
|
{
|
|
if(IsWindowsVersionEqualOrGreater(HIBYTE(systemVersion), LOBYTE(systemVersion), (WORD)sp))
|
|
{
|
|
WString systemName = isWindowsServer?SystemServerNames[systemIndex]:SystemClientNames[systemIndex];
|
|
if(sp==0)
|
|
{
|
|
return systemName+L";";
|
|
}
|
|
else
|
|
{
|
|
return systemName+L";SP"+itow(sp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return L"Windows <Unknown Version>";
|
|
}
|
|
|
|
WString GetExecutablePath()
|
|
{
|
|
Array<wchar_t> buffer(65536);
|
|
GetModuleFileName(NULL, &buffer[0], (DWORD)buffer.Count());
|
|
return &buffer[0];
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
void InvokeMouseHook(WPARAM message, Point location)
|
|
{
|
|
callbackService.InvokeMouseHook(message, location);
|
|
}
|
|
|
|
void InvokeGlobalTimer()
|
|
{
|
|
callbackService.InvokeGlobalTimer();
|
|
}
|
|
|
|
void InvokeClipboardUpdated()
|
|
{
|
|
callbackService.InvokeClipboardUpdated();
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
Windows Procedure
|
|
***********************************************************************/
|
|
|
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
WindowsController* controller=dynamic_cast<WindowsController*>(GetCurrentController());
|
|
if(controller)
|
|
{
|
|
LRESULT result=0;
|
|
if(controller->HandleMessage(hwnd, uMsg, wParam, lParam, result))
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
WindowsController* controller=dynamic_cast<WindowsController*>(GetCurrentController());
|
|
if(controller)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
case WM_TIMER:
|
|
controller->InvokeGlobalTimer();
|
|
break;
|
|
case WM_CLIPBOARDUPDATE:
|
|
controller->InvokeClipboardUpdated();
|
|
break;
|
|
}
|
|
}
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CALLBACK MouseProc(int nCode , WPARAM wParam , LPARAM lParam)
|
|
{
|
|
WindowsController* controller=dynamic_cast<WindowsController*>(GetCurrentController());
|
|
if(controller)
|
|
{
|
|
MSLLHOOKSTRUCT* mouseHookStruct=(MSLLHOOKSTRUCT*)lParam;
|
|
Point location(mouseHookStruct->pt.x, mouseHookStruct->pt.y);
|
|
controller->InvokeMouseHook(wParam, location);
|
|
}
|
|
return CallNextHookEx(NULL,nCode,wParam,lParam);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Windows Platform Native Controller
|
|
***********************************************************************/
|
|
|
|
INativeController* CreateWindowsNativeController(HINSTANCE hInstance)
|
|
{
|
|
return new WindowsController(hInstance);
|
|
}
|
|
|
|
IWindowsForm* GetWindowsFormFromHandle(HWND hwnd)
|
|
{
|
|
auto controller = dynamic_cast<WindowsController*>(GetCurrentController());
|
|
if (controller)
|
|
{
|
|
return controller->GetWindowsFormFromHandle(hwnd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
IWindowsForm* GetWindowsForm(INativeWindow* window)
|
|
{
|
|
return dynamic_cast<WindowsForm*>(window);
|
|
}
|
|
|
|
void DestroyWindowsNativeController(INativeController* controller)
|
|
{
|
|
delete controller;
|
|
}
|
|
|
|
void EnableCrossKernelCrashing()
|
|
{
|
|
typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
|
|
typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
|
|
const DWORD EXCEPTION_SWALLOWING = 0x1;
|
|
|
|
HMODULE kernel32 = LoadLibrary(L"kernel32.dll");
|
|
tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
|
|
tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
|
|
if (pGetPolicy && pSetPolicy)
|
|
{
|
|
DWORD dwFlags;
|
|
if (pGetPolicy(&dwFlags))
|
|
{
|
|
// Turn off the filter
|
|
pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\DIRECT2D\WINDIRECT2DAPPLICATION.CPP
|
|
***********************************************************************/
|
|
#pragma comment(lib, "d2d1.lib")
|
|
#pragma comment(lib, "dwrite.lib")
|
|
#pragma comment(lib, "d3d11.lib")
|
|
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace vl::collections;
|
|
|
|
/***********************************************************************
|
|
WindowListener
|
|
***********************************************************************/
|
|
|
|
class Direct2DWindowsNativeWindowListener : public Object, public INativeWindowListener
|
|
{
|
|
protected:
|
|
ID2D1Factory* d2dFactory;
|
|
INativeWindow* window;
|
|
|
|
virtual void RebuildCanvas(Size size) = 0;
|
|
public:
|
|
Direct2DWindowsNativeWindowListener(INativeWindow* _window, ID2D1Factory* _d2dFactory)
|
|
:window(_window)
|
|
,d2dFactory(_d2dFactory)
|
|
{
|
|
}
|
|
|
|
void Moved()
|
|
{
|
|
RebuildCanvas(window->GetClientSize());
|
|
}
|
|
|
|
virtual ID2D1RenderTarget* GetDirect2DRenderTarget() = 0;
|
|
virtual void RecreateRenderTarget() = 0;
|
|
virtual bool PresentRenderTarget() = 0;
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowListener 1.0
|
|
***********************************************************************/
|
|
|
|
class Direct2DWindowsNativeWindowListener_1_0 : public Direct2DWindowsNativeWindowListener
|
|
{
|
|
protected:
|
|
ComPtr<ID2D1HwndRenderTarget> d2dRenderTarget;
|
|
Size previousSize;
|
|
|
|
void RebuildCanvas(Size size)override
|
|
{
|
|
if (size.x <= 1) size.x = 1;
|
|
if (size.y <= 1) size.y = 1;
|
|
if(!d2dRenderTarget)
|
|
{
|
|
ID2D1HwndRenderTarget* renderTarget=0;
|
|
IWindowsForm* form=GetWindowsForm(window);
|
|
D2D1_RENDER_TARGET_PROPERTIES tp=D2D1::RenderTargetProperties();
|
|
tp.dpiX=96;
|
|
tp.dpiY=96;
|
|
HRESULT hr=d2dFactory->CreateHwndRenderTarget(
|
|
tp,
|
|
D2D1::HwndRenderTargetProperties(
|
|
form->GetWindowHandle(),
|
|
D2D1::SizeU((int)size.x, (int)size.y)
|
|
),
|
|
&renderTarget
|
|
);
|
|
if(!FAILED(hr))
|
|
{
|
|
d2dRenderTarget=renderTarget;
|
|
d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
|
}
|
|
}
|
|
else if(previousSize!=size)
|
|
{
|
|
d2dRenderTarget->Resize(D2D1::SizeU((int)size.x, (int)size.y));
|
|
}
|
|
previousSize=size;
|
|
}
|
|
public:
|
|
Direct2DWindowsNativeWindowListener_1_0(INativeWindow* _window, ID2D1Factory* _d2dFactory)
|
|
:Direct2DWindowsNativeWindowListener(_window, _d2dFactory)
|
|
{
|
|
}
|
|
|
|
ID2D1RenderTarget* GetDirect2DRenderTarget()override
|
|
{
|
|
if(!d2dRenderTarget) Moved();
|
|
return d2dRenderTarget.Obj();
|
|
}
|
|
|
|
void RecreateRenderTarget()override
|
|
{
|
|
if (d2dRenderTarget)
|
|
{
|
|
d2dRenderTarget = 0;
|
|
}
|
|
}
|
|
|
|
bool PresentRenderTarget()override
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowListener 1.1
|
|
***********************************************************************/
|
|
|
|
class Direct2DWindowsNativeWindowListener_1_1 : public Direct2DWindowsNativeWindowListener
|
|
{
|
|
protected:
|
|
ComPtr<ID2D1Factory1> d2dFactory1;
|
|
ID3D11Device* d3d11Device;
|
|
ComPtr<IDXGIDevice> dxgiDevice;
|
|
ComPtr<IDXGISwapChain1> dxgiSwapChain;
|
|
ComPtr<ID2D1DeviceContext> d2dDeviceContext;
|
|
Size previousSize;
|
|
|
|
ComPtr<IDXGIDevice> GetDXGIDevice()
|
|
{
|
|
IDXGIDevice* device = nullptr;
|
|
HRESULT hr = d3d11Device->QueryInterface(&device);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
return device;
|
|
}
|
|
|
|
ComPtr<IDXGISwapChain1> CreateSwapChain(IDXGIDevice* dxgiDevice)
|
|
{
|
|
ComPtr<IDXGIAdapter> dxgiAdapter;
|
|
{
|
|
IDXGIAdapter* adapter = nullptr;
|
|
HRESULT hr = dxgiDevice->GetAdapter(&adapter);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
dxgiAdapter = adapter;
|
|
}
|
|
|
|
ComPtr<IDXGIFactory2> dxgiFactory;
|
|
{
|
|
IDXGIFactory2* factory = nullptr;
|
|
HRESULT hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&factory);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
dxgiFactory = factory;
|
|
}
|
|
|
|
IWindowsForm* form = GetWindowsForm(window);
|
|
ComPtr<IDXGISwapChain1> dxgiSwapChain;
|
|
{
|
|
DXGI_SWAP_CHAIN_DESC1 props = {};
|
|
props.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
props.SampleDesc.Count = 1;
|
|
props.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
props.BufferCount = 2;
|
|
|
|
IDXGISwapChain1* swapChain = nullptr;
|
|
HRESULT hr = dxgiFactory->CreateSwapChainForHwnd(d3d11Device, form->GetWindowHandle(), &props, nullptr, nullptr, &swapChain);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
dxgiSwapChain = swapChain;
|
|
}
|
|
|
|
return dxgiSwapChain;
|
|
}
|
|
|
|
ComPtr<ID2D1DeviceContext> CreateDeviceContext(IDXGIDevice* dxgiDevice)
|
|
{
|
|
ComPtr<ID2D1Device> d2d1Device;
|
|
{
|
|
ID2D1Device* device = nullptr;
|
|
HRESULT hr = d2dFactory1->CreateDevice(dxgiDevice, &device);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
d2d1Device = device;
|
|
}
|
|
|
|
ComPtr<ID2D1DeviceContext> d2dDeviceContext;
|
|
{
|
|
ID2D1DeviceContext* deviceContext = nullptr;
|
|
HRESULT hr = d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &deviceContext);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
d2dDeviceContext = deviceContext;
|
|
}
|
|
|
|
return d2dDeviceContext;
|
|
}
|
|
|
|
ComPtr<ID2D1Bitmap1> CreateBitmap(IDXGISwapChain1* swapChain, ID2D1DeviceContext* deviceContext)
|
|
{
|
|
ComPtr<IDXGISurface> dxgiSurface;
|
|
{
|
|
IDXGISurface* surface = nullptr;
|
|
HRESULT hr = swapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&surface);
|
|
if (!SUCCEEDED(hr))return 0;
|
|
dxgiSurface = surface;
|
|
}
|
|
|
|
ComPtr<ID2D1Bitmap1> d2dBitmap;
|
|
{
|
|
auto props = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));
|
|
|
|
ID2D1Bitmap1* bitmap = nullptr;
|
|
HRESULT hr = deviceContext->CreateBitmapFromDxgiSurface(dxgiSurface.Obj(), props, &bitmap);
|
|
if (!SUCCEEDED(hr)) return 0;
|
|
d2dBitmap = bitmap;
|
|
}
|
|
|
|
return d2dBitmap;
|
|
}
|
|
|
|
void RebuildCanvas(Size size)override
|
|
{
|
|
if (size.x <= 1) size.x = 1;
|
|
if (size.y <= 1) size.y = 1;
|
|
|
|
if(!d2dDeviceContext)
|
|
{
|
|
if (!dxgiDevice)
|
|
{
|
|
dxgiDevice = GetDXGIDevice();
|
|
}
|
|
|
|
if (!dxgiSwapChain)
|
|
{
|
|
dxgiSwapChain = CreateSwapChain(dxgiDevice.Obj());
|
|
}
|
|
|
|
d2dDeviceContext = CreateDeviceContext(dxgiDevice.Obj());
|
|
auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj());
|
|
d2dDeviceContext->SetTarget(d2dBitmap.Obj());
|
|
d2dDeviceContext->SetDpi(96, 96);
|
|
}
|
|
else if(previousSize!=size)
|
|
{
|
|
d2dDeviceContext->SetTarget(nullptr);
|
|
HRESULT hr = dxgiSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj());
|
|
d2dDeviceContext->SetTarget(d2dBitmap.Obj());
|
|
}
|
|
}
|
|
previousSize=size;
|
|
}
|
|
public:
|
|
Direct2DWindowsNativeWindowListener_1_1(INativeWindow* _window, ComPtr<ID2D1Factory1> _d2dFactory1, ID3D11Device* _d3d11Device)
|
|
:Direct2DWindowsNativeWindowListener(_window, _d2dFactory1.Obj())
|
|
,d2dFactory1(_d2dFactory1)
|
|
,d3d11Device(_d3d11Device)
|
|
{
|
|
}
|
|
|
|
ID2D1RenderTarget* GetDirect2DRenderTarget()override
|
|
{
|
|
if(!d2dDeviceContext) Moved();
|
|
return d2dDeviceContext.Obj();
|
|
}
|
|
|
|
void RecreateRenderTarget()override
|
|
{
|
|
if (d2dDeviceContext)
|
|
{
|
|
d2dDeviceContext = 0;
|
|
dxgiSwapChain = 0;
|
|
}
|
|
}
|
|
|
|
bool PresentRenderTarget()override
|
|
{
|
|
if (d2dDeviceContext)
|
|
{
|
|
if (dxgiSwapChain)
|
|
{
|
|
DXGI_PRESENT_PARAMETERS parameters = {0};
|
|
HRESULT hr = dxgiSwapChain->Present1(1, 0, ¶meters);
|
|
return hr == S_OK || hr == DXGI_STATUS_OCCLUDED;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
ControllerListener
|
|
***********************************************************************/
|
|
|
|
class Direct2DWindowsNativeControllerListener : public Object, public INativeControllerListener
|
|
{
|
|
public:
|
|
Dictionary<INativeWindow*, Ptr<Direct2DWindowsNativeWindowListener>> nativeWindowListeners;
|
|
ComPtr<ID2D1Factory> d2dFactory;
|
|
ComPtr<IDWriteFactory> dwrite;
|
|
ComPtr<ID3D11Device> d3d11Device;
|
|
|
|
Direct2DWindowsNativeControllerListener()
|
|
{
|
|
{
|
|
D2D1_FACTORY_OPTIONS fo = {};
|
|
#ifdef _DEBUG
|
|
fo.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
|
|
#endif
|
|
|
|
ID2D1Factory* factory=0;
|
|
HRESULT hr=D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, fo, &factory);
|
|
if(!FAILED(hr))
|
|
{
|
|
d2dFactory=factory;
|
|
}
|
|
}
|
|
{
|
|
IDWriteFactory* factory=0;
|
|
HRESULT hr=DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&factory));
|
|
if(!FAILED(hr))
|
|
{
|
|
dwrite=factory;
|
|
}
|
|
}
|
|
}
|
|
|
|
ComPtr<ID3D11Device> CreateD3D11Device(D3D_DRIVER_TYPE driverType)
|
|
{
|
|
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
|
#ifdef _DEBUG
|
|
flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
#endif
|
|
|
|
D3D_FEATURE_LEVEL featureLevels[] =
|
|
{
|
|
D3D_FEATURE_LEVEL_11_1,
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
D3D_FEATURE_LEVEL_10_1,
|
|
D3D_FEATURE_LEVEL_10_0,
|
|
D3D_FEATURE_LEVEL_9_3,
|
|
D3D_FEATURE_LEVEL_9_2,
|
|
D3D_FEATURE_LEVEL_9_1
|
|
};
|
|
|
|
|
|
ID3D11Device* device = nullptr;
|
|
HRESULT hr = D3D11CreateDevice(
|
|
nullptr,
|
|
driverType,
|
|
nullptr,
|
|
flags,
|
|
featureLevels,
|
|
sizeof(featureLevels) / sizeof(*featureLevels),
|
|
D3D11_SDK_VERSION,
|
|
&device,
|
|
nullptr,
|
|
nullptr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
return device;
|
|
}
|
|
else if (device)
|
|
{
|
|
device->Release();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void NativeWindowCreated(INativeWindow* window)
|
|
{
|
|
ComPtr<ID2D1Factory1> d2dfactory1;
|
|
{
|
|
ID2D1Factory1* factory = nullptr;
|
|
HRESULT hr = windows::GetDirect2DFactory()->QueryInterface(&factory);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
d2dfactory1 = factory;
|
|
}
|
|
}
|
|
|
|
Ptr<Direct2DWindowsNativeWindowListener> listener;
|
|
if (d2dfactory1)
|
|
{
|
|
if (!d3d11Device)
|
|
{
|
|
d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_HARDWARE);
|
|
if (!d3d11Device)
|
|
{
|
|
d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_WARP);
|
|
}
|
|
}
|
|
#if _DEBUG
|
|
CHECK_ERROR(d3d11Device,
|
|
L"Direct2DWindowsNativeControllerListener::NativeWindowCreated(INativeWindow*)#"
|
|
L"Failed to create Direct3D 11 Device. "
|
|
L"If you are running in Debug mode on Windows 10, please ensure the optional feature [Graphics Tools] is correctly installed. "
|
|
L"This error will be skipped in Release mode and GacUI will fallback to use Direct2D 1.0, but you still need to check your Windows SDK Installation."
|
|
);
|
|
#endif
|
|
}
|
|
|
|
if (d2dfactory1 && d3d11Device)
|
|
{
|
|
listener = new Direct2DWindowsNativeWindowListener_1_1(window, d2dfactory1, d3d11Device.Obj());
|
|
}
|
|
else
|
|
{
|
|
listener = new Direct2DWindowsNativeWindowListener_1_0(window, d2dFactory.Obj());
|
|
}
|
|
window->InstallListener(listener.Obj());
|
|
nativeWindowListeners.Add(window, listener);
|
|
}
|
|
|
|
void NativeWindowDestroying(INativeWindow* window)
|
|
{
|
|
Ptr<Direct2DWindowsNativeWindowListener> listener=nativeWindowListeners[window];
|
|
nativeWindowListeners.Remove(window);
|
|
window->UninstallListener(listener.Obj());
|
|
}
|
|
};
|
|
|
|
Direct2DWindowsNativeControllerListener* direct2DListener=0;
|
|
|
|
Direct2DWindowsNativeWindowListener* GetNativeWindowListener(INativeWindow* window)
|
|
{
|
|
vint index = direct2DListener->nativeWindowListeners.Keys().IndexOf(window);
|
|
return index == -1
|
|
? 0
|
|
: direct2DListener->nativeWindowListeners.Values().Get(index).Obj();
|
|
}
|
|
|
|
ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window)
|
|
{
|
|
if (auto listener = GetNativeWindowListener(window))
|
|
{
|
|
return listener->GetDirect2DRenderTarget();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RecreateNativeWindowDirect2DRenderTarget(INativeWindow* window)
|
|
{
|
|
if (auto listener = GetNativeWindowListener(window))
|
|
{
|
|
return listener->RecreateRenderTarget();
|
|
}
|
|
}
|
|
|
|
bool PresentNativeWindowDirect2DRenderTarget(INativeWindow* window)
|
|
{
|
|
if (auto listener = GetNativeWindowListener(window))
|
|
{
|
|
return listener->PresentRenderTarget();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ID2D1Factory* GetDirect2DFactory()
|
|
{
|
|
return direct2DListener->d2dFactory.Obj();
|
|
}
|
|
|
|
IDWriteFactory* GetDirectWriteFactory()
|
|
{
|
|
return direct2DListener->dwrite.Obj();
|
|
}
|
|
|
|
ID3D11Device* GetD3D11Device()
|
|
{
|
|
return direct2DListener->d3d11Device.Obj();
|
|
}
|
|
}
|
|
|
|
namespace elements_windows_d2d
|
|
{
|
|
/***********************************************************************
|
|
OS Supporting
|
|
***********************************************************************/
|
|
|
|
class WinDirect2DApplicationDirect2DObjectProvider : public IWindowsDirect2DObjectProvider
|
|
{
|
|
public:
|
|
void RecreateRenderTarget(INativeWindow* window)
|
|
{
|
|
vl::presentation::windows::RecreateNativeWindowDirect2DRenderTarget(window);
|
|
}
|
|
|
|
bool PresentRenderTarget(INativeWindow* window)
|
|
{
|
|
return vl::presentation::windows::PresentNativeWindowDirect2DRenderTarget(window);
|
|
}
|
|
|
|
ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window)
|
|
{
|
|
return vl::presentation::windows::GetNativeWindowDirect2DRenderTarget(window);
|
|
}
|
|
|
|
ID2D1Factory* GetDirect2DFactory()
|
|
{
|
|
return vl::presentation::windows::GetDirect2DFactory();
|
|
}
|
|
|
|
IDWriteFactory* GetDirectWriteFactory()
|
|
{
|
|
return vl::presentation::windows::GetDirectWriteFactory();
|
|
}
|
|
|
|
IWindowsDirect2DRenderTarget* GetBindedRenderTarget(INativeWindow* window)
|
|
{
|
|
return dynamic_cast<IWindowsDirect2DRenderTarget*>(vl::presentation::windows::GetWindowsForm(window)->GetGraphicsHandler());
|
|
}
|
|
|
|
void SetBindedRenderTarget(INativeWindow* window, IWindowsDirect2DRenderTarget* renderTarget)
|
|
{
|
|
vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget);
|
|
}
|
|
|
|
IWICImagingFactory* GetWICImagingFactory()
|
|
{
|
|
return vl::presentation::windows::GetWICImagingFactory();
|
|
}
|
|
|
|
IWICBitmap* GetWICBitmap(INativeImageFrame* frame)
|
|
{
|
|
return vl::presentation::windows::GetWICBitmap(frame);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
using namespace vl;
|
|
using namespace vl::presentation;
|
|
using namespace vl::presentation::windows;
|
|
using namespace vl::presentation::elements_windows_d2d;
|
|
|
|
int WinMainDirect2D(HINSTANCE hInstance, void(*RendererMain)())
|
|
{
|
|
EnableCrossKernelCrashing();
|
|
// create controller
|
|
INativeController* controller=CreateWindowsNativeController(hInstance);
|
|
SetCurrentController(controller);
|
|
{
|
|
// install listener
|
|
Direct2DWindowsNativeControllerListener listener;
|
|
controller->CallbackService()->InstallListener(&listener);
|
|
direct2DListener=&listener;
|
|
// main
|
|
RendererMain();
|
|
// uninstall listener
|
|
direct2DListener=0;
|
|
controller->CallbackService()->UninstallListener(&listener);
|
|
}
|
|
// destroy controller
|
|
DestroyWindowsNativeController(controller);
|
|
return 0;
|
|
}
|
|
|
|
int SetupWindowsDirect2DRenderer()
|
|
{
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
HINSTANCE hInstance=(HINSTANCE)GetModuleHandle(NULL);
|
|
WinDirect2DApplicationDirect2DObjectProvider objectProvider;
|
|
SetWindowsDirect2DObjectProvider(&objectProvider);
|
|
return WinMainDirect2D(hInstance, &RendererMainDirect2D);
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\GDI\WINGDI.CPP
|
|
***********************************************************************/
|
|
#include <math.h>
|
|
|
|
#pragma comment(lib, "Msimg32.lib")
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
|
|
/*********************************************************************************************************
|
|
WinRegion
|
|
*********************************************************************************************************/
|
|
|
|
bool IsEqual(WinRegion::Ptr Region1, WinRegion::Ptr Region2)
|
|
{
|
|
return EqualRgn(Region1->GetHandle(), Region2->GetHandle())!=0;
|
|
}
|
|
|
|
WinRegion::WinRegion(vint Left, vint Top, vint Right, vint Bottom, bool Rectangle)
|
|
{
|
|
if(Rectangle)
|
|
{
|
|
FHandle=CreateRectRgn((int)Left, (int)Top, (int)Right, (int)Bottom);
|
|
}
|
|
else
|
|
{
|
|
FHandle=CreateEllipticRgn((int)Left, (int)Top, (int)Right, (int)Bottom);
|
|
}
|
|
}
|
|
|
|
WinRegion::WinRegion(RECT Rect, bool Rectangle)
|
|
{
|
|
if(Rectangle)
|
|
{
|
|
FHandle=CreateRectRgnIndirect(&Rect);
|
|
}
|
|
else
|
|
{
|
|
FHandle=CreateEllipticRgnIndirect(&Rect);
|
|
}
|
|
}
|
|
|
|
WinRegion::WinRegion(vint Left, vint Top, vint Right, vint Bottom, vint EllipseWidth, vint EllipseHeight)
|
|
{
|
|
FHandle=CreateRoundRectRgn((int)Left, (int)Top, (int)Right, (int)Bottom, (int)EllipseWidth, (int)EllipseHeight);
|
|
}
|
|
|
|
WinRegion::WinRegion(POINT* Points, vint Count, bool Alternate)
|
|
{
|
|
FHandle=CreatePolygonRgn(Points, (int)Count, Alternate?ALTERNATE:WINDING);
|
|
}
|
|
|
|
WinRegion::WinRegion(WinRegion::Ptr Region)
|
|
{
|
|
FHandle=CreateRectRgn(0, 0, 1, 1);
|
|
CombineRgn(FHandle, Region->GetHandle(), Region->GetHandle(), RGN_COPY);
|
|
}
|
|
|
|
WinRegion::WinRegion(WinRegion::Ptr Region1, WinRegion::Ptr Region2, vint CombineMode)
|
|
{
|
|
FHandle=CreateRectRgn(0, 0, 1, 1);
|
|
CombineRgn(FHandle, Region1->GetHandle(), Region2->GetHandle(), (int)CombineMode);
|
|
}
|
|
|
|
WinRegion::WinRegion(HRGN RegionHandle)
|
|
{
|
|
FHandle=RegionHandle;
|
|
}
|
|
|
|
WinRegion::~WinRegion()
|
|
{
|
|
DeleteObject(FHandle);
|
|
}
|
|
|
|
HRGN WinRegion::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
bool WinRegion::ContainPoint(POINT Point)
|
|
{
|
|
return PtInRegion(FHandle, Point.x, Point.y)!=0;
|
|
}
|
|
|
|
bool WinRegion::ContainRect(RECT Rect)
|
|
{
|
|
return RectInRegion(FHandle, &Rect)!=0;
|
|
}
|
|
|
|
RECT WinRegion::GetBoundRect()
|
|
{
|
|
RECT Rect={0, 0, 0, 0};
|
|
GetRgnBox(FHandle, &Rect);
|
|
return Rect;
|
|
}
|
|
|
|
void WinRegion::Move(vint OffsetX, vint OffsetY)
|
|
{
|
|
OffsetRgn(FHandle, (int)OffsetX, (int)OffsetY);
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinTransform
|
|
*********************************************************************************************************/
|
|
|
|
WinTransform::WinTransform(XFORM Transform)
|
|
{
|
|
FTransform=Transform;
|
|
}
|
|
|
|
WinTransform::WinTransform(const WinTransform& Transform)
|
|
{
|
|
FTransform=Transform.FTransform;
|
|
}
|
|
|
|
WinTransform& WinTransform::operator=(const WinTransform& Transform)
|
|
{
|
|
FTransform=Transform.FTransform;
|
|
return *this;
|
|
}
|
|
|
|
WinTransform WinTransform::operator*(const WinTransform& Transform)
|
|
{
|
|
XFORM Result;
|
|
CombineTransform(&Result, GetHandle(), Transform.GetHandle());
|
|
return Result;
|
|
}
|
|
|
|
const XFORM* WinTransform::GetHandle()const
|
|
{
|
|
return &FTransform;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
WinTransform WinTransform::Translate(float OffsetX, float OffsetY)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=1.0f;
|
|
Transform.eM12=0.0f;
|
|
Transform.eM21=0.0f;
|
|
Transform.eM22=1.0f;
|
|
Transform.eDx=OffsetX;
|
|
Transform.eDy=OffsetY;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::Scale(float ScaleX, float ScaleY)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=ScaleX;
|
|
Transform.eM12=0.0f;
|
|
Transform.eM21=0.0f;
|
|
Transform.eM22=ScaleY;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::Rotate(float Angle)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=(FLOAT)cos(Angle);
|
|
Transform.eM12= (FLOAT)sin(Angle);
|
|
Transform.eM21= (FLOAT)-sin(Angle);
|
|
Transform.eM22= (FLOAT)cos(Angle);
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::Rotate(float Cos, float Sin)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=Cos;
|
|
Transform.eM12=Sin;
|
|
Transform.eM21=-Sin;
|
|
Transform.eM22=Cos;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::ReflectX()
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=1.0f;
|
|
Transform.eM12=0.0f;
|
|
Transform.eM21=0.0f;
|
|
Transform.eM22=-1.0f;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::ReflectY()
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=-1.0f;
|
|
Transform.eM12=0.0f;
|
|
Transform.eM21=0.0f;
|
|
Transform.eM22=1.0f;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::Reflect(float VectorX, float VectorY)
|
|
{
|
|
float Len= (FLOAT)sqrt(VectorX*VectorX+VectorY*VectorY);
|
|
float Cos=VectorX/Len;
|
|
float Sin=VectorY/Len;
|
|
|
|
return Rotate(Cos, -Sin)*ReflectX()*Rotate(Cos, Sin);
|
|
}
|
|
|
|
WinTransform WinTransform::Reflect(float OriginX, float OriginY, float VectorX, float VectorY)
|
|
{
|
|
float Len= (FLOAT)sqrt(VectorX*VectorX+VectorY*VectorY);
|
|
float Cos=VectorX/Len;
|
|
float Sin=VectorY/Len;
|
|
|
|
return Translate(-OriginX, -OriginY)*Rotate(Cos, -Sin)*ReflectX()*Rotate(Cos, Sin)*Translate(OriginX, OriginY);
|
|
}
|
|
|
|
WinTransform WinTransform::AxisV(float Xx, float Xy, float Yx, float Yy)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=Xx;
|
|
Transform.eM12=Xy;
|
|
Transform.eM21=Yx;
|
|
Transform.eM22=Yy;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
WinTransform WinTransform::AxisA(float AngleX, float LenX, float AngleY, float LenY)
|
|
{
|
|
XFORM Transform;
|
|
Transform.eM11=(FLOAT)cos(AngleX)*LenX;
|
|
Transform.eM12=(FLOAT)sin(AngleX)*LenX;
|
|
Transform.eM21=(FLOAT)cos(AngleY)*LenY;
|
|
Transform.eM22=(FLOAT)sin(AngleY)*LenY;
|
|
Transform.eDx=0.0f;
|
|
Transform.eDy=0.0f;
|
|
return Transform;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinMetaFileBuilder
|
|
*********************************************************************************************************/
|
|
|
|
void WinMetaFileBuilder::Create(vint Width, vint Height)
|
|
{
|
|
HDC hdcRef=GetDC(NULL);
|
|
vint iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);
|
|
vint iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);
|
|
vint iWidthPels = GetDeviceCaps(hdcRef, HORZRES);
|
|
vint iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
|
|
ReleaseDC(NULL, hdcRef);
|
|
|
|
RECT Rect;
|
|
Rect.left=0;
|
|
Rect.top=0;
|
|
Rect.right = (int)((Width*iWidthMM*100)/iWidthPels);
|
|
Rect.bottom = (int)((Height*iHeightMM*100)/iHeightPels);
|
|
|
|
HDC Handle=CreateEnhMetaFile(NULL, NULL, &Rect, L"VczhLibrary++GDI\0Enhanced Meta File\0");
|
|
FDC->Initialize(Handle);
|
|
|
|
FWidth=Width;
|
|
FHeight=Height;
|
|
}
|
|
|
|
void WinMetaFileBuilder::Draw(HENHMETAFILE Handle)
|
|
{
|
|
RECT Rect;
|
|
Rect.left=0;
|
|
Rect.top=0;
|
|
Rect.right=(int)FWidth;
|
|
Rect.bottom=(int)FHeight;
|
|
PlayEnhMetaFile(FDC->GetHandle(), Handle, &Rect);
|
|
}
|
|
|
|
void WinMetaFileBuilder::Destroy()
|
|
{
|
|
DeleteEnhMetaFile(CloseEnhMetaFile(FDC->GetHandle()));
|
|
}
|
|
|
|
WinMetaFileBuilder::WinMetaFileBuilder(vint Width, vint Height)
|
|
{
|
|
FDC=new WinProxyDC();
|
|
Create(Width, Height);
|
|
}
|
|
|
|
WinMetaFileBuilder::~WinMetaFileBuilder()
|
|
{
|
|
Destroy();
|
|
delete FDC;
|
|
}
|
|
|
|
void WinMetaFileBuilder::LoadFrom(WinMetaFile* File)
|
|
{
|
|
Destroy();
|
|
Create(File->GetWidth(), File->GetHeight());
|
|
Draw(File->GetHandle());
|
|
}
|
|
|
|
void WinMetaFileBuilder::SaveTo(WinMetaFile* File)
|
|
{
|
|
HENHMETAFILE Handle=CloseEnhMetaFile(FDC->GetHandle());
|
|
if(File->FHandle)
|
|
{
|
|
DeleteEnhMetaFile(File->FHandle);
|
|
}
|
|
File->FHandle=Handle;
|
|
File->FWidth=FWidth;
|
|
File->FHeight=FHeight;
|
|
Create(FWidth, FHeight);
|
|
Draw(Handle);
|
|
}
|
|
|
|
void WinMetaFileBuilder::LoadFrom(WString FileName)
|
|
{
|
|
WinMetaFile File(FileName);
|
|
Destroy();
|
|
Create(File.GetWidth(), File.GetHeight());
|
|
Draw(File.GetHandle());
|
|
}
|
|
|
|
void WinMetaFileBuilder::SaveTo(WString FileName)
|
|
{
|
|
HENHMETAFILE Handle=CloseEnhMetaFile(FDC->GetHandle());
|
|
HENHMETAFILE NewHandle=CopyEnhMetaFile(Handle, FileName.Buffer());
|
|
DeleteEnhMetaFile(NewHandle);
|
|
Create(FWidth, FHeight);
|
|
Draw(Handle);
|
|
DeleteEnhMetaFile(Handle);
|
|
}
|
|
|
|
WinDC* WinMetaFileBuilder::GetWinDC()
|
|
{
|
|
return FDC;
|
|
}
|
|
|
|
vint WinMetaFileBuilder::GetWidth()
|
|
{
|
|
return FWidth;
|
|
}
|
|
|
|
vint WinMetaFileBuilder::GetHeight()
|
|
{
|
|
return FHeight;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinMetaFile
|
|
*********************************************************************************************************/
|
|
|
|
WinMetaFile::WinMetaFile(WString FileName)
|
|
{
|
|
FHandle=GetEnhMetaFile(FileName.Buffer());
|
|
ENHMETAHEADER Header;
|
|
GetEnhMetaFileHeader(FHandle, sizeof(Header), &Header);
|
|
FWidth=(Header.rclFrame.right-Header.rclFrame.left)*Header.szlDevice.cx/(Header.szlMillimeters.cx*100);
|
|
FHeight=(Header.rclFrame.bottom-Header.rclFrame.top)*Header.szlDevice.cy/(Header.szlMillimeters.cy*100);
|
|
}
|
|
|
|
WinMetaFile::WinMetaFile(WinMetaFileBuilder* Builder)
|
|
{
|
|
FHandle=NULL;
|
|
Builder->SaveTo(this);
|
|
}
|
|
|
|
WinMetaFile::~WinMetaFile()
|
|
{
|
|
DeleteEnhMetaFile(FHandle);
|
|
}
|
|
|
|
HENHMETAFILE WinMetaFile::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
vint WinMetaFile::GetWidth()
|
|
{
|
|
return FWidth;
|
|
}
|
|
|
|
vint WinMetaFile::GetHeight()
|
|
{
|
|
return FHeight;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinBitmap
|
|
*********************************************************************************************************/
|
|
|
|
vint WinBitmap::GetBitsFromBB(BitmapBits BB)
|
|
{
|
|
switch(BB)
|
|
{
|
|
case vbb32Bits:
|
|
return 32;
|
|
case vbb24Bits:
|
|
return 24;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
vint WinBitmap::GetLineBytes(vint Width, BitmapBits BB)
|
|
{
|
|
vint Bits=GetBitsFromBB(BB);
|
|
vint LineBits=Width*Bits;
|
|
vint AlignBits=sizeof(DWORD)*8;
|
|
LineBits+=(AlignBits-LineBits%AlignBits)%AlignBits;
|
|
return LineBits/8;
|
|
}
|
|
|
|
void WinBitmap::FillBitmapInfoHeader(vint Width, vint Height, BitmapBits Bits, BITMAPINFOHEADER* Header)
|
|
{
|
|
Header->biSize=sizeof(BITMAPINFOHEADER);
|
|
Header->biWidth=(int)Width;
|
|
Header->biHeight=-(int)Height;
|
|
Header->biPlanes=1;
|
|
Header->biBitCount=(int)GetBitsFromBB(Bits);
|
|
Header->biCompression=BI_RGB;
|
|
Header->biSizeImage=0;
|
|
Header->biXPelsPerMeter=0;
|
|
Header->biYPelsPerMeter=0;
|
|
Header->biClrUsed=(Bits==vbb2Bits?2:0);
|
|
Header->biClrImportant=0;
|
|
}
|
|
|
|
HBITMAP WinBitmap::CreateDDB(vint Width, vint Height, BitmapBits Bits)
|
|
{
|
|
if(Bits==vbb2Bits)
|
|
{
|
|
return CreateBitmap((int)Width, (int)Height, 2, (int)GetBitsFromBB(Bits), NULL);
|
|
}
|
|
else
|
|
{
|
|
WinBitmap Bitmap(1, 1, Bits, true);
|
|
return CreateCompatibleBitmap(Bitmap.GetWinDC()->GetHandle(), (int)Width, (int)Height);
|
|
}
|
|
}
|
|
|
|
HBITMAP WinBitmap::CreateDIB(vint Width, vint Height, BitmapBits Bits, BYTE**& ScanLines)
|
|
{
|
|
BITMAPINFO* Info=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER)+2*sizeof(RGBQUAD));
|
|
FillBitmapInfoHeader(Width, Height, Bits, &Info->bmiHeader);
|
|
Info->bmiColors[0].rgbBlue=0;
|
|
Info->bmiColors[0].rgbGreen=0;
|
|
Info->bmiColors[0].rgbRed=0;
|
|
Info->bmiColors[0].rgbReserved=0;
|
|
Info->bmiColors[1].rgbBlue=255;
|
|
Info->bmiColors[1].rgbGreen=255;
|
|
Info->bmiColors[1].rgbRed=255;
|
|
Info->bmiColors[1].rgbReserved=255;
|
|
|
|
BYTE* FirstLine=0;
|
|
HBITMAP Handle=CreateDIBSection(FDC->GetHandle(), Info, DIB_RGB_COLORS, (void**)&FirstLine, NULL, 0);
|
|
ScanLines=new BYTE*[Height];
|
|
vint LineBytes=GetLineBytes(Width, Bits);
|
|
for(vint i=0;i<Height;i++)
|
|
{
|
|
ScanLines[i]=FirstLine+LineBytes*i;
|
|
}
|
|
free(Info);
|
|
return Handle;
|
|
}
|
|
|
|
void WinBitmap::Constructor(vint Width, vint Height, BitmapBits Bits, bool DIBSections)
|
|
{
|
|
FDC=new WinImageDC();
|
|
if(DIBSections)
|
|
{
|
|
FHandle=CreateDIB(Width, Height, Bits, FScanLines);
|
|
}
|
|
else
|
|
{
|
|
FHandle=CreateDDB(Width, Height, Bits);
|
|
FScanLines=0;
|
|
}
|
|
FWidth=Width;
|
|
FHeight=Height;
|
|
FBits=Bits;
|
|
FAlphaChannelBuilt=false;
|
|
HGDIOBJ Object=SelectObject(FDC->GetHandle(), FHandle);
|
|
if(Object)
|
|
{
|
|
DeleteObject(Object);
|
|
}
|
|
}
|
|
|
|
WinBitmap::WinBitmap(vint Width, vint Height, BitmapBits Bits, bool DIBSections)
|
|
{
|
|
Constructor(Width, Height, Bits, DIBSections);
|
|
}
|
|
|
|
WinBitmap::WinBitmap(WString FileName, bool Use32Bits, bool DIBSections)
|
|
{
|
|
FBits=Use32Bits?vbb32Bits:vbb24Bits;
|
|
|
|
HBITMAP TempBmp=(HBITMAP)LoadImage(NULL, FileName.Buffer(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
|
|
HDC TempDC=CreateCompatibleDC(NULL);
|
|
BITMAP TempRec;
|
|
|
|
GetObject(TempBmp, sizeof(BITMAP), &TempRec);
|
|
DeleteObject(SelectObject(TempDC, TempBmp));
|
|
|
|
Constructor(TempRec.bmWidth, TempRec.bmHeight, FBits, DIBSections);
|
|
BitBlt(FDC->GetHandle(), 0, 0, TempRec.bmWidth, TempRec.bmHeight, TempDC, 0, 0, SRCCOPY);
|
|
|
|
DeleteObject(TempDC);
|
|
DeleteObject(TempBmp);
|
|
}
|
|
|
|
WinBitmap::~WinBitmap()
|
|
{
|
|
if(FScanLines)
|
|
{
|
|
delete[] FScanLines;
|
|
}
|
|
if(FHandle)
|
|
{
|
|
DeleteObject(FHandle);
|
|
}
|
|
delete FDC;
|
|
}
|
|
|
|
void WinBitmap::SaveToFile(WString FileName)
|
|
{
|
|
if(FScanLines)
|
|
{
|
|
BITMAPFILEHEADER Header1;
|
|
BITMAPV5HEADER Header2;
|
|
{
|
|
Header1.bfType='M'*256+'B';
|
|
Header1.bfSize=(int)(sizeof(Header1)+sizeof(Header2)+GetLineBytes()*FHeight);
|
|
Header1.bfReserved1=0;
|
|
Header1.bfReserved2=0;
|
|
Header1.bfOffBits=sizeof(Header2)+sizeof(Header1);
|
|
}
|
|
{
|
|
memset(&Header2, 0, sizeof(Header2));
|
|
Header2.bV5Size=sizeof(Header2);
|
|
Header2.bV5Width=(int)FWidth;
|
|
Header2.bV5Height=-(int)FHeight;
|
|
Header2.bV5Planes=1;
|
|
Header2.bV5BitCount=(int)GetBitsFromBB(FBits);
|
|
Header2.bV5Compression=BI_RGB;
|
|
Header2.bV5CSType=LCS_sRGB;
|
|
Header2.bV5Intent=LCS_GM_GRAPHICS;
|
|
}
|
|
stream::FileStream Output(FileName, stream::FileStream::WriteOnly);
|
|
Output.Write(&Header1, sizeof(Header1));
|
|
Output.Write(&Header2, sizeof(Header2));
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
Output.Write(FScanLines[i], GetLineBytes());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WinBitmap Temp(FWidth, FHeight, FBits, true);
|
|
Temp.GetWinDC()->Copy(0, 0, FWidth, FHeight, FDC, 0, 0);
|
|
Temp.SaveToFile(FileName);
|
|
}
|
|
}
|
|
|
|
WinDC* WinBitmap::GetWinDC()
|
|
{
|
|
return FDC;
|
|
}
|
|
|
|
vint WinBitmap::GetWidth()
|
|
{
|
|
return FWidth;
|
|
}
|
|
|
|
vint WinBitmap::GetHeight()
|
|
{
|
|
return FHeight;
|
|
}
|
|
|
|
vint WinBitmap::GetLineBytes()
|
|
{
|
|
return GetLineBytes(FWidth, FBits);
|
|
}
|
|
|
|
BYTE** WinBitmap::GetScanLines()
|
|
{
|
|
return FScanLines;
|
|
}
|
|
|
|
HBITMAP WinBitmap::GetBitmap()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
WinBitmap::BitmapBits WinBitmap::GetBitmapBits()
|
|
{
|
|
return FBits;
|
|
}
|
|
|
|
void WinBitmap::FillCompatibleHeader(BITMAPINFOHEADER* Header)
|
|
{
|
|
FillBitmapInfoHeader(FWidth, FHeight, FBits, Header);
|
|
}
|
|
|
|
bool WinBitmap::CanBuildAlphaChannel()
|
|
{
|
|
return FScanLines!=0 && FBits==vbb32Bits;
|
|
}
|
|
|
|
bool WinBitmap::IsAlphaChannelBuilt()
|
|
{
|
|
return FAlphaChannelBuilt;
|
|
}
|
|
|
|
void WinBitmap::BuildAlphaChannel(bool autoPremultiply)
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
FAlphaChannelBuilt=true;
|
|
if(autoPremultiply)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
BYTE* Colors=FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
BYTE Alpha=Colors[3];
|
|
Colors[0]=Colors[0]*Alpha/255;
|
|
Colors[1]=Colors[1]*Alpha/255;
|
|
Colors[2]=Colors[2]*Alpha/255;
|
|
Colors+=4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::GenerateTrans(COLORREF Color)
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
COLORREF* Colors=(COLORREF*)FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
COLORREF Dest=*Colors & 0x00FFFFFF;
|
|
*Colors = Dest | (0xFF000000 * (Dest!=Color));
|
|
Colors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::GenerateAlpha(BYTE Alpha)
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
BYTE* Colors=FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
Colors[3]=Alpha;
|
|
Colors+=4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::GenerateTransAlpha(COLORREF Color, BYTE Alpha)
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
COLORREF A=Alpha<<24;
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
COLORREF* Colors=(COLORREF*)FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
COLORREF Dest=*Colors & 0x00FFFFFF;
|
|
*Colors = Dest | (A * (Dest!=Color));
|
|
Colors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::GenerateLuminance()
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
COLORREF* Colors=(COLORREF*)FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
COLORREF Dest=*Colors & 0x00FFFFFF;
|
|
*Colors = Dest | ((GetRValue(Dest)*77 + GetGValue(Dest)*151 + GetBValue(Dest)*28) & 0x0000FF00)<<16;
|
|
Colors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::GenerateGrayLevel()
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
COLORREF* Colors=(COLORREF*)FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
COLORREF Dest=*Colors & 0x00FFFFFF;
|
|
*Colors = Dest | ((GetRValue(Dest)+GetGValue(Dest)+GetBValue(Dest))/3)<<24;
|
|
Colors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinBitmap::Generate(BYTE(*Function)(COLORREF))
|
|
{
|
|
if(CanBuildAlphaChannel() && !FAlphaChannelBuilt)
|
|
{
|
|
for(vint i=0;i<FHeight;i++)
|
|
{
|
|
COLORREF* Colors=(COLORREF*)FScanLines[i];
|
|
vint j=FWidth;
|
|
while(j--)
|
|
{
|
|
COLORREF Dest= *Colors & 0x00FFFFFF;
|
|
*Colors = Dest | Function(Dest)<<24;
|
|
Colors++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinBrush
|
|
*********************************************************************************************************/
|
|
|
|
WinBrush::WinBrush()
|
|
{
|
|
FDIBMemory=0;
|
|
LOGBRUSH lb;
|
|
lb.lbColor=0;
|
|
lb.lbStyle=BS_NULL;
|
|
lb.lbHatch=0;
|
|
FHandle=CreateBrushIndirect(&lb);
|
|
}
|
|
|
|
WinBrush::WinBrush(COLORREF Color)
|
|
{
|
|
FDIBMemory=0;
|
|
FHandle=CreateSolidBrush(Color);
|
|
}
|
|
|
|
WinBrush::WinBrush(vint Hatch, COLORREF Color)
|
|
{
|
|
FDIBMemory=0;
|
|
FHandle=CreateHatchBrush((int)Hatch, Color);
|
|
}
|
|
|
|
WinBrush::WinBrush(WinBitmap::Ptr DIB)
|
|
{
|
|
WinBitmap Temp(DIB->GetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true);
|
|
Temp.GetWinDC()->Draw(0, 0, DIB);
|
|
vint HeaderSize=sizeof(BITMAPINFOHEADER);
|
|
FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()];
|
|
Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory);
|
|
memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes());
|
|
|
|
FHandle=CreateDIBPatternBrushPt(FDIBMemory, DIB_RGB_COLORS);
|
|
DWORD Error=GetLastError();
|
|
}
|
|
|
|
WinBrush::~WinBrush()
|
|
{
|
|
DeleteObject(FHandle);
|
|
if(FDIBMemory)
|
|
{
|
|
delete[] FDIBMemory;
|
|
}
|
|
}
|
|
|
|
HBRUSH WinBrush::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinPen
|
|
*********************************************************************************************************/
|
|
|
|
WinPen::WinPen(vint Style, vint Width, COLORREF Color)
|
|
{
|
|
FDIBMemory=0;
|
|
FHandle=CreatePen((int)Style, (int)Width, (int)Color);
|
|
}
|
|
|
|
WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Width, COLORREF Color)
|
|
{
|
|
FDIBMemory=0;
|
|
LOGBRUSH Brush;
|
|
Brush.lbColor=Color;
|
|
Brush.lbStyle=BS_SOLID;
|
|
Brush.lbHatch=0;
|
|
FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0);
|
|
}
|
|
|
|
WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Hatch, vint Width, COLORREF Color)
|
|
{
|
|
FDIBMemory=0;
|
|
LOGBRUSH Brush;
|
|
Brush.lbColor=Color;
|
|
Brush.lbStyle=BS_HATCHED;
|
|
Brush.lbHatch=Hatch;
|
|
FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0);
|
|
}
|
|
|
|
WinPen::WinPen(WinBitmap::Ptr DIB, vint Style, vint EndCap, vint Join, vint Width)
|
|
{
|
|
WinBitmap Temp(DIB->GetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true);
|
|
Temp.GetWinDC()->Draw(0, 0, DIB);
|
|
vint HeaderSize=sizeof(BITMAPINFOHEADER);
|
|
FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()];
|
|
Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory);
|
|
memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes());
|
|
|
|
LOGBRUSH Brush;
|
|
Brush.lbColor=RGB(0, 0, 0);
|
|
Brush.lbStyle=BS_DIBPATTERNPT;
|
|
Brush.lbHatch=(LONG)FDIBMemory;
|
|
FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0);
|
|
}
|
|
|
|
WinPen::~WinPen()
|
|
{
|
|
DeleteObject(FHandle);
|
|
if(FDIBMemory)
|
|
{
|
|
delete[] FDIBMemory;
|
|
}
|
|
}
|
|
|
|
HPEN WinPen::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinFont
|
|
*********************************************************************************************************/
|
|
|
|
WinFont::WinFont(WString Name, vint Height, vint Width, vint Escapement, vint Orientation, vint Weight, bool Italic, bool Underline, bool StrikeOut, bool Antialise)
|
|
{
|
|
FFontInfo.lfHeight=(int)Height;
|
|
FFontInfo.lfWidth=(int)Width;
|
|
FFontInfo.lfEscapement=(int)Escapement;
|
|
FFontInfo.lfOrientation=(int)Orientation;
|
|
FFontInfo.lfWeight=(int)Weight;
|
|
FFontInfo.lfItalic=Italic?TRUE:FALSE;
|
|
FFontInfo.lfUnderline=Underline?TRUE:FALSE;
|
|
FFontInfo.lfStrikeOut=StrikeOut?TRUE:FALSE;
|
|
FFontInfo.lfCharSet=DEFAULT_CHARSET;
|
|
FFontInfo.lfOutPrecision=OUT_DEFAULT_PRECIS;
|
|
FFontInfo.lfClipPrecision=CLIP_DEFAULT_PRECIS;
|
|
FFontInfo.lfQuality=Antialise?CLEARTYPE_QUALITY:NONANTIALIASED_QUALITY;
|
|
FFontInfo.lfPitchAndFamily=DEFAULT_PITCH | FF_DONTCARE;
|
|
wcsncpy_s(FFontInfo.lfFaceName, sizeof(FFontInfo.lfFaceName)/sizeof(*FFontInfo.lfFaceName), Name.Buffer(), LF_FACESIZE-1);
|
|
FHandle=CreateFontIndirect(&FFontInfo);
|
|
}
|
|
|
|
WinFont::WinFont(LOGFONT* FontInfo)
|
|
{
|
|
FFontInfo=*FontInfo;
|
|
FHandle=CreateFontIndirect(&FFontInfo);
|
|
}
|
|
|
|
WinFont::~WinFont()
|
|
{
|
|
DeleteObject(FHandle);
|
|
}
|
|
|
|
HFONT WinFont::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
LOGFONT* WinFont::GetInfo()
|
|
{
|
|
return &FFontInfo;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
IWinResourceService
|
|
*********************************************************************************************************/
|
|
|
|
WinBrush::Ptr CreateDefaultBrush()
|
|
{
|
|
return new WinBrush(RGB(255, 255, 255));
|
|
}
|
|
|
|
WinPen::Ptr CreateDefaultPen()
|
|
{
|
|
return new WinPen(PS_SOLID, 0, RGB(0, 0, 0));
|
|
}
|
|
|
|
WinFont::Ptr CreateDefaultFont()
|
|
{
|
|
NONCLIENTMETRICS NonClientMetrics;
|
|
NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0);
|
|
if(!*NonClientMetrics.lfMessageFont.lfFaceName)
|
|
{
|
|
NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS)-sizeof(NonClientMetrics.iPaddedBorderWidth);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0);
|
|
}
|
|
return new WinFont(&NonClientMetrics.lfMessageFont);
|
|
}
|
|
|
|
class DefaultResourceService : public Object, public IWinResourceService
|
|
{
|
|
public:
|
|
static IWinResourceService* _DefaultResourceService;
|
|
|
|
WinPen::Ptr GetDefaultPen()
|
|
{
|
|
return CreateDefaultPen();
|
|
}
|
|
|
|
WinBrush::Ptr GetDefaultBrush()
|
|
{
|
|
return CreateDefaultBrush();
|
|
}
|
|
|
|
WinFont::Ptr GetDefaultFont()
|
|
{
|
|
return CreateDefaultFont();
|
|
}
|
|
} _DRS;
|
|
IWinResourceService* DefaultResourceService::_DefaultResourceService=&_DRS;
|
|
|
|
IWinResourceService* GetDefaultResourceService()
|
|
{
|
|
return DefaultResourceService::_DefaultResourceService;
|
|
}
|
|
|
|
void SetDefaultResourceService(IWinResourceService* Service)
|
|
{
|
|
DefaultResourceService::_DefaultResourceService=Service;
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinDC
|
|
*********************************************************************************************************/
|
|
|
|
void WinDC::Init()
|
|
{
|
|
FPen=GetDefaultResourceService()->GetDefaultPen();
|
|
FOldPen=(HPEN)SelectObject(FHandle, FPen->GetHandle());
|
|
|
|
FBrush=GetDefaultResourceService()->GetDefaultBrush();
|
|
FOldBrush=(HBRUSH)SelectObject(FHandle, FBrush->GetHandle());
|
|
|
|
FFont=GetDefaultResourceService()->GetDefaultFont();
|
|
FOldFont=(HFONT)SelectObject(FHandle, FFont->GetHandle());
|
|
|
|
SetGraphicsMode(FHandle, GM_ADVANCED);
|
|
}
|
|
|
|
WinDC::WinDC()
|
|
{
|
|
FHandle=0;
|
|
FOldPen=0;
|
|
FOldBrush=0;
|
|
FOldFont=0;
|
|
}
|
|
|
|
WinDC::~WinDC()
|
|
{
|
|
SelectObject(FHandle, FOldFont);
|
|
SelectObject(FHandle, FOldBrush);
|
|
SelectObject(FHandle, FOldPen);
|
|
}
|
|
|
|
HDC WinDC::GetHandle()
|
|
{
|
|
return FHandle;
|
|
}
|
|
|
|
WinPen::Ptr WinDC::GetPen()
|
|
{
|
|
return FPen;
|
|
}
|
|
|
|
WinBrush::Ptr WinDC::GetBrush()
|
|
{
|
|
return FBrush;
|
|
}
|
|
|
|
WinFont::Ptr WinDC::GetFont()
|
|
{
|
|
return FFont;
|
|
}
|
|
|
|
void WinDC::SetPen(WinPen::Ptr Pen)
|
|
{
|
|
SelectObject(FHandle, Pen->GetHandle());
|
|
FPen=Pen;
|
|
}
|
|
|
|
void WinDC::SetBrush(WinBrush::Ptr Brush)
|
|
{
|
|
SelectObject(FHandle, Brush->GetHandle());
|
|
FBrush=Brush;
|
|
}
|
|
|
|
void WinDC::SetFont(WinFont::Ptr Font)
|
|
{
|
|
SelectObject(FHandle, Font->GetHandle());
|
|
FFont=Font;
|
|
}
|
|
|
|
COLORREF WinDC::GetBackColor()
|
|
{
|
|
return GetBkColor(FHandle);
|
|
}
|
|
|
|
void WinDC::SetBackColor(COLORREF Color)
|
|
{
|
|
SetBkColor(FHandle, Color);
|
|
}
|
|
|
|
COLORREF WinDC::GetTextColor()
|
|
{
|
|
return ::GetTextColor(FHandle);
|
|
}
|
|
|
|
void WinDC::SetTextColor(COLORREF Color)
|
|
{
|
|
::SetTextColor(FHandle, Color);
|
|
}
|
|
|
|
bool WinDC::GetBackTransparent()
|
|
{
|
|
return GetBkMode(FHandle)==TRANSPARENT;
|
|
}
|
|
|
|
void WinDC::SetBackTransparent(bool Transparent)
|
|
{
|
|
SetBkMode(FHandle, Transparent?TRANSPARENT:OPAQUE);
|
|
}
|
|
|
|
POINT WinDC::GetBrushOrigin()
|
|
{
|
|
POINT Point;
|
|
GetBrushOrgEx(FHandle, &Point);
|
|
return Point;
|
|
}
|
|
|
|
void WinDC::SetBrushOrigin(POINT Point)
|
|
{
|
|
SetBrushOrgEx(FHandle, Point.x, Point.y, NULL);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount)
|
|
{
|
|
TextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount);
|
|
}
|
|
|
|
void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount, vint TabWidth, vint TabOriginX)
|
|
{
|
|
int realTabWidth=(int)TabWidth;
|
|
TabbedTextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount, 1, &realTabWidth, (int)TabOriginX);
|
|
}
|
|
|
|
void WinDC::DrawBuffer(RECT Rect, const wchar_t* Text, vint CharCount, UINT Format)
|
|
{
|
|
DrawText(FHandle, Text, (int)CharCount, &Rect, Format);
|
|
}
|
|
|
|
void WinDC::DrawString(vint X, vint Y, WString Text)
|
|
{
|
|
DrawBuffer(X, Y, Text.Buffer(), Text.Length());
|
|
}
|
|
|
|
void WinDC::DrawString(vint X, vint Y, WString Text, vint TabWidth, vint TabOriginX)
|
|
{
|
|
DrawBuffer(X, Y, Text.Buffer(), Text.Length(), TabWidth, TabOriginX);
|
|
}
|
|
|
|
void WinDC::DrawString(RECT Rect, WString Text, UINT Format)
|
|
{
|
|
DrawBuffer(Rect, Text.Buffer(), Text.Length(), Format);
|
|
}
|
|
|
|
SIZE WinDC::MeasureString(WString Text, vint TabSize)
|
|
{
|
|
return MeasureBuffer(Text.Buffer(), Text.Length(), TabSize);
|
|
}
|
|
|
|
SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint CharCount, vint TabSize)
|
|
{
|
|
SIZE Size;
|
|
if(TabSize==-1)
|
|
{
|
|
GetTextExtentPoint32(FHandle, Text, (int)CharCount, &Size);
|
|
}
|
|
else
|
|
{
|
|
int realTabSize=(int)TabSize;
|
|
DWORD Result=GetTabbedTextExtent(FHandle, Text, (int)CharCount, 1, &realTabSize);
|
|
Size.cx=LOWORD(Result);
|
|
Size.cy=HIWORD(Result);
|
|
}
|
|
return Size;
|
|
}
|
|
|
|
SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint TabSize)
|
|
{
|
|
return MeasureBuffer(Text, wcslen(Text), TabSize);
|
|
}
|
|
|
|
SIZE WinDC::MeasureWrapLineString(WString Text, vint MaxWidth)
|
|
{
|
|
return MeasureWrapLineBuffer(Text.Buffer(), Text.Length(), MaxWidth);
|
|
}
|
|
|
|
SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint CharCount, vint MaxWidth)
|
|
{
|
|
SIZE size = {0};
|
|
vint lineCount=0;
|
|
const wchar_t* reading=Text;
|
|
INT* dx=new INT[CharCount];
|
|
while(*reading)
|
|
{
|
|
INT fit=0;
|
|
GetTextExtentExPoint(FHandle, reading, (int)(CharCount-(reading-Text)), (int)MaxWidth, &fit, dx, &size);
|
|
reading+=fit;
|
|
lineCount++;
|
|
}
|
|
delete dx;
|
|
size.cx=0;
|
|
size.cy*=(int)lineCount;
|
|
return size;
|
|
}
|
|
|
|
SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint MaxWidth)
|
|
{
|
|
return MeasureWrapLineBuffer(Text, wcslen(Text), MaxWidth);
|
|
}
|
|
|
|
void WinDC::FillRegion(WinRegion::Ptr Region)
|
|
{
|
|
FillRgn(FHandle, Region->GetHandle(), FBrush->GetHandle());
|
|
}
|
|
|
|
void WinDC::FrameRegion(WinRegion::Ptr Region, vint BlockWidth, vint BlockHeight)
|
|
{
|
|
FrameRgn(FHandle, Region->GetHandle(), FBrush->GetHandle(), (int)BlockWidth, (int)BlockHeight);
|
|
}
|
|
|
|
void WinDC::MoveTo(vint X, vint Y)
|
|
{
|
|
::MoveToEx(FHandle, (int)X, (int)Y, NULL);
|
|
}
|
|
|
|
void WinDC::LineTo(vint X, vint Y)
|
|
{
|
|
::LineTo(FHandle, (int)X, (int)Y);
|
|
}
|
|
|
|
void WinDC::Rectangle(vint Left, vint Top, vint Right, vint Bottom)
|
|
{
|
|
::Rectangle(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom);
|
|
}
|
|
|
|
void WinDC::Rectangle(RECT Rect)
|
|
{
|
|
::Rectangle(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
}
|
|
|
|
void WinDC::FocusRectangle(vint Left, vint Top, vint Right, vint Bottom)
|
|
{
|
|
RECT Rect;
|
|
Rect.left=(int)Left;
|
|
Rect.top=(int)Top;
|
|
Rect.right=(int)Right;
|
|
Rect.bottom=(int)Bottom;
|
|
::DrawFocusRect(FHandle, &Rect);
|
|
}
|
|
|
|
void WinDC::FocusRectangle(RECT Rect)
|
|
{
|
|
::DrawFocusRect(FHandle, &Rect);
|
|
}
|
|
|
|
void WinDC::FillRect(vint Left, vint Top, vint Right, vint Bottom)
|
|
{
|
|
RECT Rect;
|
|
Rect.left=(int)Left;
|
|
Rect.top=(int)Top;
|
|
Rect.right=(int)Right;
|
|
Rect.bottom=(int)Bottom;
|
|
::FillRect(FHandle, &Rect, FBrush->GetHandle());
|
|
}
|
|
|
|
void WinDC::FillRect(RECT Rect)
|
|
{
|
|
::FillRect(FHandle, &Rect, FBrush->GetHandle());
|
|
}
|
|
|
|
void WinDC::Ellipse(vint Left, vint Top, vint Right, vint Bottom)
|
|
{
|
|
::Ellipse(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom);
|
|
}
|
|
|
|
void WinDC::Ellipse(RECT Rect)
|
|
{
|
|
::Ellipse(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
}
|
|
|
|
void WinDC::RoundRect(vint Left, vint Top, vint Right, vint Bottom, vint EllipseWidth, vint EllipseHeight)
|
|
{
|
|
::RoundRect(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)EllipseWidth, (int)EllipseHeight);
|
|
}
|
|
|
|
void WinDC::RoundRect(RECT Rect, vint EllipseWidth, vint EllipseHeight)
|
|
{
|
|
::RoundRect(FHandle, (int)Rect.left, (int)Rect.top, (int)Rect.right, (int)Rect.bottom, (int)EllipseWidth, (int)EllipseHeight);
|
|
}
|
|
|
|
void WinDC::PolyLine(const POINT* Points, vint Count)
|
|
{
|
|
::Polyline(FHandle, Points, (int)Count);
|
|
}
|
|
|
|
void WinDC::PolyLineTo(const POINT* Points, vint Count)
|
|
{
|
|
::PolylineTo(FHandle, Points, (int)Count);
|
|
}
|
|
|
|
void WinDC::PolyGon(const POINT* Points, vint Count)
|
|
{
|
|
::Polygon(FHandle, Points, (int)Count);
|
|
}
|
|
|
|
void WinDC::PolyBezier(const POINT* Points, vint Count)
|
|
{
|
|
::PolyBezier(FHandle, Points, (int)Count);
|
|
}
|
|
|
|
void WinDC::PolyBezierTo(const POINT* Points, vint Count)
|
|
{
|
|
::PolyBezierTo(FHandle, Points, (int)Count);
|
|
}
|
|
|
|
void WinDC::PolyDraw(const POINT* Points, const BYTE* Actions, vint PointCount)
|
|
{
|
|
::PolyDraw(FHandle, Points, Actions, (int)PointCount);
|
|
}
|
|
|
|
void WinDC::Arc(RECT Bound, POINT Start, POINT End)
|
|
{
|
|
::Arc(FHandle, Bound.left, Bound.top, Bound.right, Bound.bottom, Start.x, Start.y, End.x, End.y);
|
|
}
|
|
|
|
void WinDC::Arc(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
|
|
{
|
|
::Arc(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
|
|
}
|
|
|
|
void WinDC::ArcTo(RECT Bound, POINT Start, POINT End)
|
|
{
|
|
::ArcTo(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
|
|
}
|
|
|
|
void WinDC::ArcTo(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
|
|
{
|
|
::ArcTo(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
|
|
}
|
|
|
|
void WinDC::AngleArc(vint X, vint Y, vint Radius, float StartAngle, float SweepAngle)
|
|
{
|
|
::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, StartAngle, SweepAngle);
|
|
}
|
|
|
|
void WinDC::AngleArc(vint X, vint Y, vint Radius, double StartAngle, double SweepAngle)
|
|
{
|
|
::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, (float)StartAngle, (float)SweepAngle);
|
|
}
|
|
|
|
void WinDC::Chord(RECT Bound, POINT Start, POINT End)
|
|
{
|
|
::Chord(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
|
|
}
|
|
|
|
void WinDC::Chord(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
|
|
{
|
|
::Chord(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
|
|
}
|
|
|
|
void WinDC::Pie(RECT Bound, POINT Start, POINT End)
|
|
{
|
|
::Pie(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y);
|
|
}
|
|
|
|
void WinDC::Pie(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY)
|
|
{
|
|
::Pie(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY);
|
|
}
|
|
|
|
void WinDC::GradientTriangle(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_TRIANGLE* Triangles, vint TriangleCount)
|
|
{
|
|
GradientFill(FHandle, Vertices, (int)VerticesCount, Triangles, (int)TriangleCount, GRADIENT_FILL_TRIANGLE);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::BeginPath()
|
|
{
|
|
::BeginPath(FHandle);
|
|
}
|
|
|
|
void WinDC::EndPath()
|
|
{
|
|
::EndPath(FHandle);
|
|
}
|
|
|
|
void WinDC::ClosePath()
|
|
{
|
|
::CloseFigure(FHandle);
|
|
}
|
|
|
|
void WinDC::WidenPath()
|
|
{
|
|
::WidenPath(FHandle);
|
|
}
|
|
|
|
void WinDC::DiscardPath()
|
|
{
|
|
::AbortPath(FHandle);
|
|
}
|
|
|
|
void WinDC::DrawPath()
|
|
{
|
|
::StrokePath(FHandle);
|
|
}
|
|
|
|
void WinDC::FillPath()
|
|
{
|
|
::FillPath(FHandle);
|
|
}
|
|
|
|
void WinDC::DrawAndFillPath()
|
|
{
|
|
::StrokeAndFillPath(FHandle);
|
|
}
|
|
|
|
WinRegion::Ptr WinDC::RegionFromPath()
|
|
{
|
|
return new WinRegion(::PathToRegion(FHandle));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
bool WinDC::PointInClip(POINT Point)
|
|
{
|
|
return PtVisible(FHandle, Point.x, Point.y)==TRUE;
|
|
}
|
|
|
|
bool WinDC::RectInClip(RECT Rect)
|
|
{
|
|
return RectVisible(FHandle, &Rect)==TRUE;
|
|
}
|
|
|
|
void WinDC::ClipPath(vint CombineMode)
|
|
{
|
|
SelectClipPath(FHandle, (int)CombineMode);
|
|
}
|
|
|
|
void WinDC::ClipRegion(WinRegion::Ptr Region)
|
|
{
|
|
SelectClipRgn(FHandle, Region->GetHandle());
|
|
}
|
|
|
|
void WinDC::RemoveClip()
|
|
{
|
|
SelectClipRgn(FHandle, NULL);
|
|
}
|
|
|
|
void WinDC::MoveClip(vint OffsetX, vint OffsetY)
|
|
{
|
|
OffsetClipRgn(FHandle, (int)OffsetX, (int)OffsetY);
|
|
}
|
|
|
|
void WinDC::CombineClip(WinRegion::Ptr Region, vint CombineMode)
|
|
{
|
|
ExtSelectClipRgn(FHandle, Region->GetHandle(), (int)CombineMode);
|
|
}
|
|
|
|
void WinDC::IntersetClipRect(RECT Rect)
|
|
{
|
|
::IntersectClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
}
|
|
|
|
void WinDC::ExcludeClipRect(RECT Rect)
|
|
{
|
|
::ExcludeClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom);
|
|
}
|
|
|
|
WinRegion::Ptr WinDC::GetClipRegion()
|
|
{
|
|
HRGN Handle=CreateRectRgn(0, 0, 1, 1);
|
|
GetClipRgn(FHandle, Handle);
|
|
return new WinRegion(Handle);
|
|
}
|
|
|
|
RECT WinDC::GetClipBoundRect()
|
|
{
|
|
RECT Rect;
|
|
GetClipBox(FHandle, &Rect);
|
|
return Rect;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
WinTransform WinDC::GetTransform()
|
|
{
|
|
XFORM Transform;
|
|
GetWorldTransform(FHandle, &Transform);
|
|
return Transform;
|
|
}
|
|
|
|
void WinDC::SetTransform(const WinTransform& Transform)
|
|
{
|
|
SetWorldTransform(FHandle, Transform.GetHandle());
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY, DWORD DrawROP)
|
|
{
|
|
HDC SourceHandle=Source?Source->GetHandle():0;
|
|
BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)DrawROP);
|
|
}
|
|
|
|
void WinDC::Copy(RECT dstRect, WinDC* Source, POINT srcPos, DWORD DrawROP)
|
|
{
|
|
HDC SourceHandle=Source?Source->GetHandle():0;
|
|
BitBlt(FHandle, dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, SourceHandle, srcPos.x, srcPos.y, DrawROP);
|
|
}
|
|
|
|
void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, DWORD DrawROP)
|
|
{
|
|
HDC SourceHandle=Source?Source->GetHandle():0;
|
|
StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)srcW, (int)srcH, (int)DrawROP);
|
|
}
|
|
|
|
void WinDC::Copy(RECT dstRect, WinDC* Source, RECT srcRect, DWORD DrawROP)
|
|
{
|
|
HDC SourceHandle=Source?Source->GetHandle():0;
|
|
StretchBlt( FHandle , dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top,
|
|
SourceHandle, srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top,
|
|
DrawROP);
|
|
}
|
|
|
|
void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC* Source, vint srcX, vint srcY, vint srcW, vint srcH)
|
|
{
|
|
POINT Pt[3];
|
|
Pt[0]=UpperLeft;
|
|
Pt[1]=UpperRight;
|
|
Pt[2]=LowerLeft;
|
|
PlgBlt(FHandle, Pt, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, 0, 0, 0);
|
|
}
|
|
|
|
void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC*Source, RECT srcRect)
|
|
{
|
|
POINT Pt[3];
|
|
Pt[0]=UpperLeft;
|
|
Pt[1]=UpperRight;
|
|
Pt[2]=LowerLeft;
|
|
PlgBlt(FHandle, Pt, Source->GetHandle(), srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, 0, 0, 0);
|
|
}
|
|
|
|
void WinDC::CopyTrans(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, COLORREF Color)
|
|
{
|
|
TransparentBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Color);
|
|
}
|
|
|
|
void WinDC::CopyTrans(RECT dstRect, WinDC* Source, RECT srcRect, COLORREF Color)
|
|
{
|
|
TransparentBlt( FHandle , dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top,
|
|
Source->GetHandle() , srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top,
|
|
Color);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, WinMetaFile* MetaFile)
|
|
{
|
|
Draw(dstX, dstY, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile);
|
|
}
|
|
|
|
void WinDC::Draw(POINT Pos, WinMetaFile* MetaFile)
|
|
{
|
|
Draw(Pos.x, Pos.y, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile);
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinMetaFile* MetaFile)
|
|
{
|
|
RECT Rect;
|
|
Rect.left=(int)dstX;
|
|
Rect.top=(int)dstY;
|
|
Rect.right=(int)(dstX+dstW);
|
|
Rect.bottom=(int)(dstY+dstH);
|
|
Draw(Rect, MetaFile);
|
|
}
|
|
|
|
void WinDC::Draw(RECT Rect, WinMetaFile* MetaFile)
|
|
{
|
|
PlayEnhMetaFile(FHandle, MetaFile->GetHandle(), &Rect);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap)
|
|
{
|
|
vint dstW=Bitmap->GetWidth();
|
|
vint dstH=Bitmap->GetHeight();
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap)
|
|
{
|
|
vint dstX=Pos.x;
|
|
vint dstY=Pos.y;
|
|
vint dstW=Bitmap->GetWidth();
|
|
vint dstH=Bitmap->GetHeight();
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap)
|
|
{
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=Bitmap->GetWidth();
|
|
vint srcH=Bitmap->GetHeight();
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap)
|
|
{
|
|
vint dstX=Rect.left;
|
|
vint dstY=Rect.top;
|
|
vint dstW=Rect.right-Rect.left;
|
|
vint dstH=Rect.bottom-Rect.top;
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=Bitmap->GetWidth();
|
|
vint srcH=Bitmap->GetHeight();
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY)
|
|
{
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos)
|
|
{
|
|
vint dstX=Rect.left;
|
|
vint dstY=Rect.top;
|
|
vint dstW=Rect.right-Rect.left;
|
|
vint dstH=Rect.bottom-Rect.top;
|
|
vint srcX=Pos.x;
|
|
vint srcY=Pos.y;
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH)
|
|
{
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect)
|
|
{
|
|
vint dstX=dstRect.left;
|
|
vint dstY=dstRect.top;
|
|
vint dstW=dstRect.right-dstRect.left;
|
|
vint dstH=dstRect.bottom-dstRect.top;
|
|
vint srcX=srcRect.left;
|
|
vint srcY=srcRect.top;
|
|
vint srcW=srcRect.right-srcRect.left;
|
|
vint srcH=srcRect.bottom-srcRect.top;
|
|
if(!Bitmap->IsAlphaChannelBuilt())
|
|
{
|
|
StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY);
|
|
}
|
|
else
|
|
{
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=255;
|
|
Blend.AlphaFormat=AC_SRC_ALPHA;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap, unsigned char Alpha)
|
|
{
|
|
vint dstW=Bitmap->GetWidth();
|
|
vint dstH=Bitmap->GetHeight();
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap, unsigned char Alpha)
|
|
{
|
|
vint dstX=Pos.x;
|
|
vint dstY=Pos.y;
|
|
vint dstW=Bitmap->GetWidth();
|
|
vint dstH=Bitmap->GetHeight();
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, unsigned char Alpha)
|
|
{
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=Bitmap->GetWidth();
|
|
vint srcH=Bitmap->GetHeight();
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, unsigned char Alpha)
|
|
{
|
|
vint dstX=Rect.left;
|
|
vint dstY=Rect.top;
|
|
vint dstW=Rect.right-Rect.left;
|
|
vint dstH=Rect.bottom-Rect.top;
|
|
vint srcX=0;
|
|
vint srcY=0;
|
|
vint srcW=Bitmap->GetWidth();
|
|
vint srcH=Bitmap->GetHeight();
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, unsigned char Alpha)
|
|
{
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos, unsigned char Alpha)
|
|
{
|
|
vint dstX=Rect.left;
|
|
vint dstY=Rect.top;
|
|
vint dstW=Rect.right-Rect.left;
|
|
vint dstH=Rect.bottom-Rect.top;
|
|
vint srcX=Pos.x;
|
|
vint srcY=Pos.y;
|
|
vint srcW=dstW;
|
|
vint srcH=dstH;
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH, unsigned char Alpha)
|
|
{
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect, unsigned char Alpha)
|
|
{
|
|
vint dstX=dstRect.left;
|
|
vint dstY=dstRect.top;
|
|
vint dstW=dstRect.right-dstRect.left;
|
|
vint dstH=dstRect.bottom-dstRect.top;
|
|
vint srcX=srcRect.left;
|
|
vint srcY=srcRect.top;
|
|
vint srcW=srcRect.right-srcRect.left;
|
|
vint srcH=srcRect.bottom-srcRect.top;
|
|
|
|
BLENDFUNCTION Blend;
|
|
Blend.BlendOp=AC_SRC_OVER;
|
|
Blend.BlendFlags=0;
|
|
Blend.SourceConstantAlpha=Alpha;
|
|
Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0;
|
|
AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend);
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinControlDC
|
|
*********************************************************************************************************/
|
|
|
|
WinControlDC::WinControlDC(HWND Handle)
|
|
{
|
|
FControlHandle=Handle;
|
|
FHandle=GetDC(FControlHandle);
|
|
Init();
|
|
}
|
|
|
|
WinControlDC::~WinControlDC()
|
|
{
|
|
ReleaseDC(FControlHandle, FHandle);
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinProxyDC
|
|
*********************************************************************************************************/
|
|
|
|
WinProxyDC::WinProxyDC()
|
|
{
|
|
FHandle=NULL;
|
|
}
|
|
|
|
WinProxyDC::~WinProxyDC()
|
|
{
|
|
}
|
|
|
|
void WinProxyDC::Initialize(HDC Handle)
|
|
{
|
|
FHandle=Handle;
|
|
Init();
|
|
}
|
|
|
|
/*********************************************************************************************************
|
|
WinImageDC
|
|
*********************************************************************************************************/
|
|
|
|
WinImageDC::WinImageDC()
|
|
{
|
|
FHandle=CreateCompatibleDC(NULL);
|
|
Init();
|
|
}
|
|
|
|
WinImageDC::~WinImageDC()
|
|
{
|
|
DeleteDC(FHandle);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\GDI\WINGDIAPPLICATION.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace vl::collections;
|
|
|
|
class GdiWindowsNativeWindowListener : public Object, public INativeWindowListener
|
|
{
|
|
protected:
|
|
Ptr<WinBitmap> buffer;
|
|
INativeWindow* window;
|
|
|
|
vint DetermineBufferLength(vint minSize, vint minBound, vint maxBound, vint currentSize)
|
|
{
|
|
if(currentSize<minSize || currentSize>maxBound)
|
|
{
|
|
return minBound;
|
|
}
|
|
else
|
|
{
|
|
return currentSize;
|
|
}
|
|
}
|
|
|
|
Size CalculateBufferSize()
|
|
{
|
|
Size windowSize=window->GetClientSize();
|
|
Size minBounds(windowSize.x*5/4, windowSize.y*5/4);
|
|
Size maxBounds(windowSize.x*3/2, windowSize.y*3/2);
|
|
Size currentSize=buffer?Size(buffer->GetWidth(), buffer->GetHeight()):Size(0, 0);
|
|
vint newWidth=DetermineBufferLength(windowSize.x, minBounds.x, maxBounds.x, currentSize.x);
|
|
vint newHeight=DetermineBufferLength(windowSize.y, minBounds.y, maxBounds.y, currentSize.y);
|
|
return Size(newWidth, newHeight);
|
|
}
|
|
|
|
void RebuildCanvas(Size size)
|
|
{
|
|
if(size.x<256)size.x=256;
|
|
if(size.y<256)size.y=256;
|
|
if(buffer)
|
|
{
|
|
if(buffer->GetWidth()!=size.x || buffer->GetHeight()!=size.y)
|
|
{
|
|
buffer=0;
|
|
}
|
|
}
|
|
if(!buffer)
|
|
{
|
|
buffer=new WinBitmap(size.x, size.y, WinBitmap::vbb32Bits, true);
|
|
buffer->GetWinDC()->SetBackTransparent(true);
|
|
}
|
|
}
|
|
public:
|
|
GdiWindowsNativeWindowListener(INativeWindow* _window)
|
|
:window(_window)
|
|
{
|
|
}
|
|
|
|
void Moved()
|
|
{
|
|
RebuildCanvas(CalculateBufferSize());
|
|
}
|
|
|
|
void Paint()
|
|
{
|
|
IWindowsForm* form=GetWindowsForm(window);
|
|
WinControlDC controlDC(form->GetWindowHandle());
|
|
controlDC.Draw(0, 0, buffer);
|
|
}
|
|
|
|
WinDC* GetWinDC()
|
|
{
|
|
if(!buffer) Moved();
|
|
return buffer->GetWinDC();
|
|
}
|
|
};
|
|
|
|
class GdiWindowsNativeControllerListener : public Object, public INativeControllerListener
|
|
{
|
|
public:
|
|
Dictionary<INativeWindow*, Ptr<GdiWindowsNativeWindowListener>> nativeWindowListeners;
|
|
|
|
void NativeWindowCreated(INativeWindow* window)
|
|
{
|
|
Ptr<GdiWindowsNativeWindowListener> listener=new GdiWindowsNativeWindowListener(window);
|
|
window->InstallListener(listener.Obj());
|
|
nativeWindowListeners.Add(window, listener);
|
|
}
|
|
|
|
void NativeWindowDestroying(INativeWindow* window)
|
|
{
|
|
Ptr<GdiWindowsNativeWindowListener> listener=nativeWindowListeners[window];
|
|
nativeWindowListeners.Remove(window);
|
|
window->UninstallListener(listener.Obj());
|
|
}
|
|
};
|
|
|
|
GdiWindowsNativeControllerListener* gdiListener=0;
|
|
|
|
WinDC* GetNativeWindowDC(INativeWindow* window)
|
|
{
|
|
vint index=gdiListener->nativeWindowListeners.Keys().IndexOf(window);
|
|
return index==-1?0:gdiListener->nativeWindowListeners.Values().Get(index)->GetWinDC();
|
|
}
|
|
|
|
HDC GetNativeWindowHDC(INativeWindow* window)
|
|
{
|
|
WinDC* dc=GetNativeWindowDC(window);
|
|
return dc?dc->GetHandle():NULL;
|
|
}
|
|
}
|
|
|
|
namespace elements_windows_gdi
|
|
{
|
|
/***********************************************************************
|
|
OS Supporting
|
|
***********************************************************************/
|
|
|
|
class WinGDIApplicationGDIObjectProvider : public IWindowsGDIObjectProvider
|
|
{
|
|
protected:
|
|
IMLangFontLink2* mLangFontLink;
|
|
|
|
public:
|
|
WinGDIApplicationGDIObjectProvider()
|
|
:mLangFontLink(0)
|
|
{
|
|
CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, (void**)&mLangFontLink);
|
|
}
|
|
|
|
~WinGDIApplicationGDIObjectProvider()
|
|
{
|
|
mLangFontLink->Release();
|
|
}
|
|
|
|
windows::WinDC* GetNativeWindowDC(INativeWindow* window)override
|
|
{
|
|
return vl::presentation::windows::GetNativeWindowDC(window);
|
|
}
|
|
|
|
IWindowsGDIRenderTarget* GetBindedRenderTarget(INativeWindow* window)override
|
|
{
|
|
return dynamic_cast<IWindowsGDIRenderTarget*>(vl::presentation::windows::GetWindowsForm(window)->GetGraphicsHandler());
|
|
}
|
|
|
|
void SetBindedRenderTarget(INativeWindow* window, IWindowsGDIRenderTarget* renderTarget)override
|
|
{
|
|
vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget);
|
|
}
|
|
|
|
IWICImagingFactory* GetWICImagingFactory()override
|
|
{
|
|
return vl::presentation::windows::GetWICImagingFactory();
|
|
}
|
|
|
|
IWICBitmap* GetWICBitmap(INativeImageFrame* frame)override
|
|
{
|
|
return vl::presentation::windows::GetWICBitmap(frame);
|
|
}
|
|
|
|
IMLangFontLink2* GetMLangFontLink()override
|
|
{
|
|
return mLangFontLink;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
using namespace vl;
|
|
using namespace vl::presentation;
|
|
using namespace vl::presentation::windows;
|
|
using namespace vl::presentation::elements_windows_gdi;
|
|
|
|
int WinMainGDI(HINSTANCE hInstance, void(*RendererMain)())
|
|
{
|
|
EnableCrossKernelCrashing();
|
|
// create controller
|
|
INativeController* controller=CreateWindowsNativeController(hInstance);
|
|
SetCurrentController(controller);
|
|
{
|
|
// install listener
|
|
GdiWindowsNativeControllerListener listener;
|
|
controller->CallbackService()->InstallListener(&listener);
|
|
gdiListener=&listener;
|
|
// main
|
|
RendererMain();
|
|
// uninstall listener
|
|
gdiListener=0;
|
|
controller->CallbackService()->UninstallListener(&listener);
|
|
}
|
|
// destroy controller
|
|
DestroyWindowsNativeController(controller);
|
|
return 0;
|
|
}
|
|
|
|
int SetupWindowsGDIRenderer()
|
|
{
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
HINSTANCE hInstance=(HINSTANCE)GetModuleHandle(NULL);
|
|
WinGDIApplicationGDIObjectProvider objectProvider;
|
|
SetWindowsGDIObjectProvider(&objectProvider);
|
|
return WinMainGDI(hInstance, &RendererMainGDI);
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSASYNCSERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
WindowsAsyncService::TaskItem
|
|
***********************************************************************/
|
|
|
|
WindowsAsyncService::TaskItem::TaskItem()
|
|
:semaphore(0)
|
|
{
|
|
}
|
|
|
|
WindowsAsyncService::TaskItem::TaskItem(Semaphore* _semaphore, const Func<void()>& _proc)
|
|
:semaphore(_semaphore)
|
|
,proc(_proc)
|
|
{
|
|
}
|
|
|
|
WindowsAsyncService::TaskItem::~TaskItem()
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsAsyncService::DelayItem
|
|
***********************************************************************/
|
|
|
|
WindowsAsyncService::DelayItem::DelayItem(WindowsAsyncService* _service, const Func<void()>& _proc, bool _executeInMainThread, vint milliseconds)
|
|
:service(_service)
|
|
,proc(_proc)
|
|
,status(INativeDelay::Pending)
|
|
,executeTime(DateTime::LocalTime().Forward(milliseconds))
|
|
,executeInMainThread(_executeInMainThread)
|
|
{
|
|
}
|
|
|
|
WindowsAsyncService::DelayItem::~DelayItem()
|
|
{
|
|
}
|
|
|
|
INativeDelay::ExecuteStatus WindowsAsyncService::DelayItem::GetStatus()
|
|
{
|
|
return status;
|
|
}
|
|
|
|
bool WindowsAsyncService::DelayItem::Delay(vint milliseconds)
|
|
{
|
|
SPIN_LOCK(service->taskListLock)
|
|
{
|
|
if(status==INativeDelay::Pending)
|
|
{
|
|
executeTime=DateTime::LocalTime().Forward(milliseconds);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WindowsAsyncService::DelayItem::Cancel()
|
|
{
|
|
SPIN_LOCK(service->taskListLock)
|
|
{
|
|
if(status==INativeDelay::Pending)
|
|
{
|
|
if(service->delayItems.Remove(this))
|
|
{
|
|
status=INativeDelay::Canceled;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsAsyncService
|
|
***********************************************************************/
|
|
|
|
WindowsAsyncService::WindowsAsyncService()
|
|
:mainThreadId(Thread::GetCurrentThreadId())
|
|
{
|
|
}
|
|
|
|
WindowsAsyncService::~WindowsAsyncService()
|
|
{
|
|
}
|
|
|
|
void WindowsAsyncService::ExecuteAsyncTasks()
|
|
{
|
|
DateTime now=DateTime::LocalTime();
|
|
Array<TaskItem> items;
|
|
List<Ptr<DelayItem>> executableDelayItems;
|
|
|
|
SPIN_LOCK(taskListLock)
|
|
{
|
|
CopyFrom(items, taskItems);
|
|
taskItems.RemoveRange(0, items.Count());
|
|
for(vint i=delayItems.Count()-1;i>=0;i--)
|
|
{
|
|
Ptr<DelayItem> item=delayItems[i];
|
|
if(now.filetime>=item->executeTime.filetime)
|
|
{
|
|
item->status=INativeDelay::Executing;
|
|
executableDelayItems.Add(item);
|
|
delayItems.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
FOREACH(TaskItem, item, items)
|
|
{
|
|
item.proc();
|
|
if(item.semaphore)
|
|
{
|
|
item.semaphore->Release();
|
|
}
|
|
}
|
|
FOREACH(Ptr<DelayItem>, item, executableDelayItems)
|
|
{
|
|
if(item->executeInMainThread)
|
|
{
|
|
item->proc();
|
|
item->status=INativeDelay::Executed;
|
|
}
|
|
else
|
|
{
|
|
InvokeAsync([=]()
|
|
{
|
|
item->proc();
|
|
item->status=INativeDelay::Executed;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
bool WindowsAsyncService::IsInMainThread()
|
|
{
|
|
return Thread::GetCurrentThreadId()==mainThreadId;
|
|
}
|
|
|
|
void WindowsAsyncService::InvokeAsync(const Func<void()>& proc)
|
|
{
|
|
ThreadPoolLite::Queue(proc);
|
|
}
|
|
|
|
void WindowsAsyncService::InvokeInMainThread(const Func<void()>& proc)
|
|
{
|
|
SPIN_LOCK(taskListLock)
|
|
{
|
|
TaskItem item(0, proc);
|
|
taskItems.Add(item);
|
|
}
|
|
}
|
|
|
|
bool WindowsAsyncService::InvokeInMainThreadAndWait(const Func<void()>& proc, vint milliseconds)
|
|
{
|
|
Semaphore semaphore;
|
|
semaphore.Create(0, 1);
|
|
|
|
SPIN_LOCK(taskListLock)
|
|
{
|
|
TaskItem item(&semaphore, proc);
|
|
taskItems.Add(item);
|
|
}
|
|
|
|
if(milliseconds<0)
|
|
{
|
|
return semaphore.Wait();
|
|
}
|
|
else
|
|
{
|
|
return semaphore.WaitForTime(milliseconds);
|
|
}
|
|
}
|
|
|
|
Ptr<INativeDelay> WindowsAsyncService::DelayExecute(const Func<void()>& proc, vint milliseconds)
|
|
{
|
|
Ptr<DelayItem> delay;
|
|
SPIN_LOCK(taskListLock)
|
|
{
|
|
delay=new DelayItem(this, proc, false, milliseconds);
|
|
delayItems.Add(delay);
|
|
}
|
|
return delay;
|
|
}
|
|
|
|
Ptr<INativeDelay> WindowsAsyncService::DelayExecuteInMainThread(const Func<void()>& proc, vint milliseconds)
|
|
{
|
|
Ptr<DelayItem> delay;
|
|
SPIN_LOCK(taskListLock)
|
|
{
|
|
delay=new DelayItem(this, proc, true, milliseconds);
|
|
delayItems.Add(delay);
|
|
}
|
|
return delay;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSCALLBACKSERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
|
|
/***********************************************************************
|
|
WindowsCallbackService
|
|
***********************************************************************/
|
|
|
|
WindowsCallbackService::WindowsCallbackService()
|
|
{
|
|
}
|
|
|
|
bool WindowsCallbackService::InstallListener(INativeControllerListener* listener)
|
|
{
|
|
if(listeners.Contains(listener))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
listeners.Add(listener);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool WindowsCallbackService::UninstallListener(INativeControllerListener* listener)
|
|
{
|
|
if(listeners.Contains(listener))
|
|
{
|
|
listeners.Remove(listener);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void WindowsCallbackService::InvokeMouseHook(WPARAM message, Point location)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LeftButtonDown(location);
|
|
}
|
|
}
|
|
break;
|
|
case WM_LBUTTONUP:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->LeftButtonUp(location);
|
|
}
|
|
}
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->RightButtonDown(location);
|
|
}
|
|
}
|
|
break;
|
|
case WM_RBUTTONUP:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->RightButtonUp(location);
|
|
}
|
|
}
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->MouseMoving(location);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WindowsCallbackService::InvokeGlobalTimer()
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->GlobalTimer();
|
|
}
|
|
}
|
|
|
|
void WindowsCallbackService::InvokeClipboardUpdated()
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->ClipboardUpdated();
|
|
}
|
|
}
|
|
|
|
void WindowsCallbackService::InvokeNativeWindowCreated(INativeWindow* window)
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->NativeWindowCreated(window);
|
|
}
|
|
}
|
|
|
|
void WindowsCallbackService::InvokeNativeWindowDestroyed(INativeWindow* window)
|
|
{
|
|
for(vint i=0;i<listeners.Count();i++)
|
|
{
|
|
listeners[i]->NativeWindowDestroying(window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSCLIPBOARDSERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
|
|
/***********************************************************************
|
|
WindowsClipboardService
|
|
***********************************************************************/
|
|
|
|
WindowsClipboardService::WindowsClipboardService()
|
|
:ownerHandle(NULL)
|
|
{
|
|
}
|
|
|
|
void WindowsClipboardService::SetOwnerHandle(HWND handle)
|
|
{
|
|
HWND oldHandle=ownerHandle;
|
|
ownerHandle=handle;
|
|
if(handle==NULL)
|
|
{
|
|
RemoveClipboardFormatListener(oldHandle);
|
|
}
|
|
else
|
|
{
|
|
AddClipboardFormatListener(ownerHandle);
|
|
}
|
|
}
|
|
|
|
bool WindowsClipboardService::ContainsText()
|
|
{
|
|
if(OpenClipboard(ownerHandle))
|
|
{
|
|
UINT format=0;
|
|
bool contains=false;
|
|
while(format=EnumClipboardFormats(format))
|
|
{
|
|
if(format==CF_TEXT || format==CF_UNICODETEXT)
|
|
{
|
|
contains=true;
|
|
break;
|
|
}
|
|
}
|
|
CloseClipboard();
|
|
return contains;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
WString WindowsClipboardService::GetText()
|
|
{
|
|
if(OpenClipboard(ownerHandle))
|
|
{
|
|
WString result;
|
|
HANDLE handle=GetClipboardData(CF_UNICODETEXT);
|
|
if(handle!=0)
|
|
{
|
|
wchar_t* buffer=(wchar_t*)GlobalLock(handle);
|
|
result=buffer;
|
|
GlobalUnlock(handle);
|
|
}
|
|
CloseClipboard();
|
|
return result;
|
|
}
|
|
return L"";
|
|
}
|
|
|
|
bool WindowsClipboardService::SetText(const WString& value)
|
|
{
|
|
if(OpenClipboard(ownerHandle))
|
|
{
|
|
EmptyClipboard();
|
|
vint size=(value.Length()+1)*sizeof(wchar_t);
|
|
HGLOBAL data=GlobalAlloc(GMEM_MOVEABLE, size);
|
|
wchar_t* buffer=(wchar_t*)GlobalLock(data);
|
|
memcpy(buffer, value.Buffer(), size);
|
|
GlobalUnlock(data);
|
|
SetClipboardData(CF_UNICODETEXT, data);
|
|
CloseClipboard();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSDIALOGSERVICE.CPP
|
|
***********************************************************************/
|
|
#include <Vfw.h>
|
|
|
|
#pragma comment(lib, "Vfw32.lib")
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
WindowsDialogService
|
|
***********************************************************************/
|
|
|
|
WindowsDialogService::WindowsDialogService(HandleRetriver _handleRetriver)
|
|
:handleRetriver(_handleRetriver)
|
|
{
|
|
}
|
|
|
|
INativeDialogService::MessageBoxButtonsOutput WindowsDialogService::ShowMessageBox(
|
|
INativeWindow* window,
|
|
const WString& text,
|
|
const WString& title,
|
|
MessageBoxButtonsInput buttons,
|
|
MessageBoxDefaultButton defaultButton,
|
|
MessageBoxIcons icon,
|
|
MessageBoxModalOptions modal)
|
|
{
|
|
WString realTitle=title;
|
|
if(title==L"" && window!=0)
|
|
{
|
|
realTitle=window->GetTitle();
|
|
}
|
|
HWND hWnd=handleRetriver(window);
|
|
LPCTSTR lpText=text.Buffer();
|
|
LPCTSTR lpCaption=realTitle.Buffer();
|
|
UINT uType=0;
|
|
|
|
#define MAP(A, B) case A: uType|=(B); break
|
|
switch(buttons)
|
|
{
|
|
MAP(DisplayOK, MB_OK);
|
|
MAP(DisplayOKCancel, MB_OKCANCEL);
|
|
MAP(DisplayYesNo, MB_YESNO);
|
|
MAP(DisplayYesNoCancel, MB_YESNOCANCEL);
|
|
MAP(DisplayRetryCancel, MB_RETRYCANCEL);
|
|
MAP(DisplayAbortRetryIgnore, MB_ABORTRETRYIGNORE);
|
|
MAP(DisplayCancelTryAgainContinue, MB_CANCELTRYCONTINUE);
|
|
}
|
|
switch(defaultButton)
|
|
{
|
|
MAP(DefaultFirst, MB_DEFBUTTON1);
|
|
MAP(DefaultSecond, MB_DEFBUTTON2);
|
|
MAP(DefaultThird, MB_DEFBUTTON3);
|
|
}
|
|
switch(icon)
|
|
{
|
|
MAP(IconError, MB_ICONERROR);
|
|
MAP(IconQuestion, MB_ICONQUESTION);
|
|
MAP(IconWarning, MB_ICONWARNING);
|
|
MAP(IconInformation, MB_ICONINFORMATION);
|
|
}
|
|
switch(modal)
|
|
{
|
|
MAP(ModalWindow, MB_APPLMODAL);
|
|
MAP(ModalSystem, MB_SYSTEMMODAL);
|
|
MAP(ModalTask, MB_TASKMODAL);
|
|
}
|
|
#undef MAP
|
|
|
|
vint result=MessageBox(hWnd, lpText, lpCaption, uType);
|
|
switch(result)
|
|
{
|
|
case IDABORT: return SelectAbort;
|
|
case IDCANCEL: return SelectCancel;
|
|
case IDCONTINUE: return SelectContinue;
|
|
case IDIGNORE: return SelectIgnore;
|
|
case IDNO: return SelectNo;
|
|
case IDOK: return SelectOK;
|
|
case IDRETRY: return SelectRetry;
|
|
case IDTRYAGAIN: return SelectTryAgain;
|
|
case IDYES: return SelectYes;
|
|
default: return SelectOK;
|
|
}
|
|
}
|
|
|
|
bool WindowsDialogService::ShowColorDialog(INativeWindow* window, Color& selection, bool selected, ColorDialogCustomColorOptions customColorOptions, Color* customColors)
|
|
{
|
|
CHOOSECOLOR chooseColor;
|
|
ZeroMemory(&chooseColor, sizeof(chooseColor));
|
|
COLORREF customColorsBuffer[16]={0};
|
|
if(customColors)
|
|
{
|
|
for(vint i=0;i<sizeof(customColorsBuffer)/sizeof(*customColorsBuffer);i++)
|
|
{
|
|
customColorsBuffer[i]=RGB(customColors[i].r, customColors[i].g, customColors[i].b);
|
|
}
|
|
}
|
|
|
|
chooseColor.lStructSize=sizeof(chooseColor);
|
|
chooseColor.hwndOwner=handleRetriver(window);
|
|
chooseColor.rgbResult=RGB(selection.r, selection.g, selection.b);
|
|
chooseColor.lpCustColors=customColorsBuffer;
|
|
chooseColor.Flags=CC_ANYCOLOR;
|
|
|
|
#define MAP(A, B) case A: chooseColor.Flags|=(B); break
|
|
switch(customColorOptions)
|
|
{
|
|
MAP(CustomColorDisabled, CC_PREVENTFULLOPEN);
|
|
MAP(CustomColorOpened, CC_FULLOPEN);
|
|
}
|
|
#undef MAP
|
|
if(selected)
|
|
{
|
|
chooseColor.Flags|=CC_RGBINIT;
|
|
}
|
|
|
|
BOOL result=ChooseColor(&chooseColor);
|
|
if(result)
|
|
{
|
|
selection=Color(GetRValue(chooseColor.rgbResult), GetGValue(chooseColor.rgbResult), GetBValue(chooseColor.rgbResult));
|
|
if(customColors)
|
|
{
|
|
for(vint i=0;i<sizeof(customColorsBuffer)/sizeof(*customColorsBuffer);i++)
|
|
{
|
|
COLORREF color=customColorsBuffer[i];
|
|
customColors[i]=Color(GetRValue(color), GetGValue(color), GetBValue(color));
|
|
}
|
|
}
|
|
}
|
|
return result!=FALSE;
|
|
}
|
|
|
|
bool WindowsDialogService::ShowFontDialog(INativeWindow* window, FontProperties& selectionFont, Color& selectionColor, bool selected, bool showEffect, bool forceFontExist)
|
|
{
|
|
LOGFONT logFont;
|
|
ZeroMemory(&logFont, sizeof(logFont));
|
|
logFont.lfHeight=-(int)selectionFont.size;
|
|
logFont.lfWeight=selectionFont.bold?FW_BOLD:FW_REGULAR;
|
|
logFont.lfItalic=selectionFont.italic?TRUE:FALSE;
|
|
logFont.lfUnderline=selectionFont.underline?TRUE:FALSE;
|
|
logFont.lfStrikeOut=selectionFont.strikeline?TRUE:FALSE;
|
|
wcscpy_s(logFont.lfFaceName, selectionFont.fontFamily.Buffer());
|
|
|
|
CHOOSEFONT chooseFont;
|
|
ZeroMemory(&chooseFont, sizeof(chooseFont));
|
|
chooseFont.lStructSize=sizeof(chooseFont);
|
|
chooseFont.hwndOwner=handleRetriver(window);
|
|
chooseFont.lpLogFont=&logFont;
|
|
chooseFont.iPointSize=0;
|
|
chooseFont.Flags=(showEffect?CF_EFFECTS:0) | (forceFontExist?CF_FORCEFONTEXIST:0) | (selected?CF_INITTOLOGFONTSTRUCT:0);
|
|
chooseFont.rgbColors=RGB(selectionColor.r, selectionColor.g, selectionColor.b);
|
|
|
|
BOOL result=ChooseFont(&chooseFont);
|
|
if(result)
|
|
{
|
|
selectionFont.fontFamily=logFont.lfFaceName;
|
|
selectionFont.bold=logFont.lfWeight>FW_REGULAR;
|
|
selectionFont.italic=logFont.lfItalic!=FALSE;
|
|
selectionFont.underline=logFont.lfUnderline!=FALSE;
|
|
selectionFont.strikeline=logFont.lfStrikeOut!=FALSE;
|
|
selectionFont.size=-logFont.lfHeight;
|
|
|
|
selectionColor=Color(GetRValue(chooseFont.rgbColors), GetGValue(chooseFont.rgbColors), GetBValue(chooseFont.rgbColors));
|
|
}
|
|
return result!=FALSE;
|
|
}
|
|
|
|
bool WindowsDialogService::ShowFileDialog(INativeWindow* window, collections::List<WString>& selectionFileNames, vint& selectionFilterIndex, FileDialogTypes dialogType, const WString& title, const WString& initialFileName, const WString& initialDirectory, const WString& defaultExtension, const WString& filter, FileDialogOptions options)
|
|
{
|
|
Array<wchar_t> fileNamesBuffer(65536>initialFileName.Length()+1?65536:initialFileName.Length()+1);
|
|
wcscpy_s(&fileNamesBuffer[0], fileNamesBuffer.Count(), initialFileName.Buffer());
|
|
|
|
OPENFILENAME ofn;
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
ofn.lStructSize=sizeof(ofn);
|
|
ofn.hwndOwner=handleRetriver(window);
|
|
ofn.hInstance=NULL;
|
|
ofn.lpstrCustomFilter=NULL;
|
|
ofn.nMaxCustFilter=0;
|
|
ofn.nFilterIndex=(int)selectionFilterIndex+1;
|
|
ofn.lpstrFile=&fileNamesBuffer[0];
|
|
ofn.nMaxFile=(int)fileNamesBuffer.Count();
|
|
ofn.lpstrFileTitle=NULL;
|
|
ofn.nMaxFileTitle=0;
|
|
ofn.lpstrInitialDir=initialDirectory==L""?NULL:initialDirectory.Buffer();
|
|
ofn.lpstrTitle=title==L""?NULL:title.Buffer();
|
|
ofn.lpstrDefExt=defaultExtension==L""?NULL:defaultExtension.Buffer();
|
|
|
|
List<vint> filterSeparators;
|
|
for(vint i=0;i<filter.Length();i++)
|
|
{
|
|
if(filter[i]==L'|')
|
|
{
|
|
filterSeparators.Add(i);
|
|
}
|
|
}
|
|
if(filterSeparators.Count()%2==1)
|
|
{
|
|
filterSeparators.Add(filter.Length());
|
|
}
|
|
|
|
Array<wchar_t> filterBuffer(filter.Length()+2);
|
|
vint index=0;
|
|
for(vint i=0;i<filterSeparators.Count();i++)
|
|
{
|
|
vint end=filterSeparators[i];
|
|
wcsncpy_s(&filterBuffer[index], filterBuffer.Count()-index, filter.Buffer()+index, end-index);
|
|
filterBuffer[end]=0;
|
|
index=end+1;
|
|
}
|
|
filterBuffer[index]=0;
|
|
ofn.lpstrFilter=&filterBuffer[0];
|
|
|
|
ofn.Flags=OFN_ENABLESIZING | OFN_EXPLORER | OFN_LONGNAMES;
|
|
if(options&FileDialogAllowMultipleSelection) ofn.Flags|=OFN_ALLOWMULTISELECT;
|
|
if(options&FileDialogFileMustExist) ofn.Flags|=OFN_FILEMUSTEXIST;
|
|
if(!(options&FileDialogShowReadOnlyCheckBox)) ofn.Flags|=OFN_HIDEREADONLY;
|
|
if(!(options&FileDialogDereferenceLinks)) ofn.Flags|=OFN_NODEREFERENCELINKS;
|
|
if(!(options&FileDialogShowNetworkButton)) ofn.Flags|=OFN_NONETWORKBUTTON;
|
|
if(options&FileDialogPromptCreateFile) ofn.Flags|=OFN_CREATEPROMPT;
|
|
if(options&FileDialogPromptOverwriteFile) ofn.Flags|=OFN_OVERWRITEPROMPT;
|
|
if(options&FileDialogDirectoryMustExist) ofn.Flags|=OFN_PATHMUSTEXIST;
|
|
if(!(options&FileDialogAddToRecent)) ofn.Flags|=OFN_DONTADDTORECENT;
|
|
|
|
BOOL result=FALSE;
|
|
switch(dialogType)
|
|
{
|
|
case FileDialogOpen:
|
|
result=GetOpenFileName(&ofn);
|
|
break;
|
|
case FileDialogOpenPreview:
|
|
result=GetOpenFileNamePreview(&ofn);
|
|
break;
|
|
case FileDialogSave:
|
|
result=GetSaveFileName(&ofn);
|
|
break;
|
|
case FileDialogSavePreview:
|
|
result=GetSaveFileNamePreview(&ofn);
|
|
break;
|
|
}
|
|
if(result)
|
|
{
|
|
selectionFilterIndex=ofn.nFilterIndex-1;
|
|
selectionFileNames.Clear();
|
|
if(options&FileDialogAllowMultipleSelection)
|
|
{
|
|
const wchar_t* reading=&fileNamesBuffer[0];
|
|
WString directory=reading;
|
|
reading+=directory.Length()+1;
|
|
while(*reading)
|
|
{
|
|
WString fileName=reading;
|
|
reading+=fileName.Length()+1;
|
|
selectionFileNames.Add(directory+L"\\"+fileName);
|
|
}
|
|
if(selectionFileNames.Count()==0)
|
|
{
|
|
selectionFileNames.Add(directory);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
selectionFileNames.Add(&fileNamesBuffer[0]);
|
|
}
|
|
}
|
|
return result!=FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSIMAGESERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
#include <Shlwapi.h>
|
|
|
|
#pragma comment(lib, "WindowsCodecs.lib")
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
WindowsImageFrame
|
|
***********************************************************************/
|
|
|
|
void WindowsImageFrame::Initialize(IWICBitmapSource* bitmapSource)
|
|
{
|
|
IWICImagingFactory* factory=GetWICImagingFactory();
|
|
ComPtr<IWICFormatConverter> converter;
|
|
{
|
|
IWICFormatConverter* formatConverter=0;
|
|
HRESULT hr=factory->CreateFormatConverter(&formatConverter);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
converter=formatConverter;
|
|
converter->Initialize(
|
|
bitmapSource,
|
|
GUID_WICPixelFormat32bppPBGRA,
|
|
WICBitmapDitherTypeNone,
|
|
NULL,
|
|
0.0f,
|
|
WICBitmapPaletteTypeCustom);
|
|
}
|
|
}
|
|
|
|
IWICBitmap* bitmap=0;
|
|
IWICBitmapSource* convertedBitmapSource=0;
|
|
if(converter)
|
|
{
|
|
convertedBitmapSource=converter.Obj();
|
|
}
|
|
else
|
|
{
|
|
convertedBitmapSource=bitmapSource;
|
|
}
|
|
HRESULT hr=factory->CreateBitmapFromSource(convertedBitmapSource, WICBitmapCacheOnLoad, &bitmap);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
frameBitmap=bitmap;
|
|
}
|
|
}
|
|
|
|
WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmapFrameDecode* frameDecode)
|
|
:image(_image)
|
|
{
|
|
Initialize(frameDecode);
|
|
}
|
|
|
|
WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmap* sourceBitmap)
|
|
:image(_image)
|
|
{
|
|
Initialize(sourceBitmap);
|
|
}
|
|
|
|
WindowsImageFrame::~WindowsImageFrame()
|
|
{
|
|
for(vint i=0;i<caches.Count();i++)
|
|
{
|
|
caches.Values().Get(i)->OnDetach(this);
|
|
}
|
|
}
|
|
|
|
INativeImage* WindowsImageFrame::GetImage()
|
|
{
|
|
return image;
|
|
}
|
|
|
|
Size WindowsImageFrame::GetSize()
|
|
{
|
|
UINT width=0;
|
|
UINT height=0;
|
|
frameBitmap->GetSize(&width, &height);
|
|
return Size(width, height);
|
|
}
|
|
|
|
bool WindowsImageFrame::SetCache(void* key, Ptr<INativeImageFrameCache> cache)
|
|
{
|
|
vint index=caches.Keys().IndexOf(key);
|
|
if(index!=-1)
|
|
{
|
|
return false;
|
|
}
|
|
caches.Add(key, cache);
|
|
cache->OnAttach(this);
|
|
return true;
|
|
}
|
|
|
|
Ptr<INativeImageFrameCache> WindowsImageFrame::GetCache(void* key)
|
|
{
|
|
vint index=caches.Keys().IndexOf(key);
|
|
return index==-1?0:caches.Values().Get(index);
|
|
}
|
|
|
|
Ptr<INativeImageFrameCache> WindowsImageFrame::RemoveCache(void* key)
|
|
{
|
|
vint index=caches.Keys().IndexOf(key);
|
|
if(index==-1)
|
|
{
|
|
return 0;
|
|
}
|
|
Ptr<INativeImageFrameCache> cache=caches.Values().Get(index);
|
|
cache->OnDetach(this);
|
|
caches.Remove(key);
|
|
return cache;
|
|
}
|
|
|
|
IWICBitmap* WindowsImageFrame::GetFrameBitmap()
|
|
{
|
|
return frameBitmap.Obj();
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsImage
|
|
***********************************************************************/
|
|
|
|
WindowsImage::WindowsImage(INativeImageService* _imageService, IWICBitmapDecoder* _bitmapDecoder)
|
|
:imageService(_imageService)
|
|
,bitmapDecoder(_bitmapDecoder)
|
|
{
|
|
UINT count=0;
|
|
bitmapDecoder->GetFrameCount(&count);
|
|
frames.Resize(count);
|
|
}
|
|
|
|
WindowsImage::~WindowsImage()
|
|
{
|
|
}
|
|
|
|
INativeImageService* WindowsImage::GetImageService()
|
|
{
|
|
return imageService;
|
|
}
|
|
|
|
INativeImage::FormatType WindowsImage::GetFormat()
|
|
{
|
|
GUID formatGUID;
|
|
HRESULT hr=bitmapDecoder->GetContainerFormat(&formatGUID);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(formatGUID==GUID_ContainerFormatBmp)
|
|
{
|
|
return INativeImage::Bmp;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatPng)
|
|
{
|
|
return INativeImage::Png;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatGif)
|
|
{
|
|
return INativeImage::Gif;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatJpeg)
|
|
{
|
|
return INativeImage::Jpeg;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatIco)
|
|
{
|
|
return INativeImage::Icon;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatTiff)
|
|
{
|
|
return INativeImage::Tiff;
|
|
}
|
|
else if(formatGUID==GUID_ContainerFormatWmp)
|
|
{
|
|
return INativeImage::Wmp;
|
|
}
|
|
}
|
|
return INativeImage::Unknown;
|
|
}
|
|
|
|
vint WindowsImage::GetFrameCount()
|
|
{
|
|
return frames.Count();
|
|
}
|
|
|
|
INativeImageFrame* WindowsImage::GetFrame(vint index)
|
|
{
|
|
if(0<=index && index<GetFrameCount())
|
|
{
|
|
Ptr<WindowsImageFrame>& frame=frames[index];
|
|
if(!frame)
|
|
{
|
|
IWICBitmapFrameDecode* frameDecode=0;
|
|
HRESULT hr=bitmapDecoder->GetFrame((int)index, &frameDecode);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
frame=new WindowsImageFrame(this, frameDecode);
|
|
frameDecode->Release();
|
|
}
|
|
}
|
|
return frame.Obj();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsBitmapImage
|
|
***********************************************************************/
|
|
|
|
WindowsBitmapImage::WindowsBitmapImage(INativeImageService* _imageService, IWICBitmap* sourceBitmap, FormatType _formatType)
|
|
:imageService(_imageService)
|
|
,formatType(_formatType)
|
|
{
|
|
frame = new WindowsImageFrame(this, sourceBitmap);
|
|
}
|
|
|
|
WindowsBitmapImage::~WindowsBitmapImage()
|
|
{
|
|
}
|
|
|
|
INativeImageService* WindowsBitmapImage::GetImageService()
|
|
{
|
|
return imageService;
|
|
}
|
|
|
|
INativeImage::FormatType WindowsBitmapImage::GetFormat()
|
|
{
|
|
return formatType;
|
|
}
|
|
|
|
vint WindowsBitmapImage::GetFrameCount()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
INativeImageFrame* WindowsBitmapImage::GetFrame(vint index)
|
|
{
|
|
return index==0?frame.Obj():0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsImageService
|
|
***********************************************************************/
|
|
|
|
WindowsImageService::WindowsImageService()
|
|
{
|
|
IWICImagingFactory* factory=0;
|
|
HRESULT hr = CoCreateInstance(
|
|
#if defined(WINCODEC_SDK_VERSION2)
|
|
CLSID_WICImagingFactory1,
|
|
#else
|
|
CLSID_WICImagingFactory,
|
|
#endif
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IWICImagingFactory,
|
|
(LPVOID*)&factory
|
|
);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
imagingFactory=factory;
|
|
}
|
|
}
|
|
|
|
WindowsImageService::~WindowsImageService()
|
|
{
|
|
}
|
|
|
|
Ptr<INativeImage> WindowsImageService::CreateImageFromFile(const WString& path)
|
|
{
|
|
IWICBitmapDecoder* bitmapDecoder=0;
|
|
HRESULT hr=imagingFactory->CreateDecoderFromFilename(
|
|
path.Buffer(),
|
|
NULL,
|
|
GENERIC_READ,
|
|
WICDecodeMetadataCacheOnDemand,
|
|
&bitmapDecoder);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
return new WindowsImage(this, bitmapDecoder);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Ptr<INativeImage> WindowsImageService::CreateImageFromMemory(void* buffer, vint length)
|
|
{
|
|
Ptr<INativeImage> result;
|
|
::IStream* stream=SHCreateMemStream((const BYTE*)buffer, (int)length);
|
|
if(stream)
|
|
{
|
|
IWICBitmapDecoder* bitmapDecoder=0;
|
|
HRESULT hr=imagingFactory->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnDemand, &bitmapDecoder);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
result=new WindowsImage(this, bitmapDecoder);
|
|
}
|
|
stream->Release();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Ptr<INativeImage> WindowsImageService::CreateImageFromStream(stream::IStream& stream)
|
|
{
|
|
stream::MemoryStream memoryStream;
|
|
char buffer[65536];
|
|
while(true)
|
|
{
|
|
vint length=stream.Read(buffer, sizeof(buffer));
|
|
memoryStream.Write(buffer, length);
|
|
if(length!=sizeof(buffer))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return CreateImageFromMemory(memoryStream.GetInternalBuffer(), (vint)memoryStream.Size());
|
|
}
|
|
|
|
Ptr<INativeImage> WindowsImageService::CreateImageFromHBITMAP(HBITMAP handle)
|
|
{
|
|
IWICBitmap* bitmap=0;
|
|
HRESULT hr=imagingFactory->CreateBitmapFromHBITMAP(handle, NULL, WICBitmapUseAlpha, &bitmap);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
Ptr<INativeImage> image=new WindowsBitmapImage(this, bitmap, INativeImage::Bmp);
|
|
bitmap->Release();
|
|
return image;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Ptr<INativeImage> WindowsImageService::CreateImageFromHICON(HICON handle)
|
|
{
|
|
IWICBitmap* bitmap=0;
|
|
HRESULT hr=imagingFactory->CreateBitmapFromHICON(handle, &bitmap);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
Ptr<INativeImage> image=new WindowsBitmapImage(this, bitmap, INativeImage::Icon);
|
|
bitmap->Release();
|
|
return image;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
IWICImagingFactory* WindowsImageService::GetImagingFactory()
|
|
{
|
|
return imagingFactory.Obj();
|
|
}
|
|
|
|
/***********************************************************************
|
|
Helper Functions
|
|
***********************************************************************/
|
|
|
|
IWICImagingFactory* GetWICImagingFactory()
|
|
{
|
|
return dynamic_cast<WindowsImageService*>(GetCurrentController()->ImageService())->GetImagingFactory();
|
|
}
|
|
|
|
IWICBitmap* GetWICBitmap(INativeImageFrame* frame)
|
|
{
|
|
return dynamic_cast<WindowsImageFrame*>(frame)->GetFrameBitmap();
|
|
}
|
|
|
|
Ptr<INativeImage> CreateImageFromHBITMAP(HBITMAP handle)
|
|
{
|
|
return dynamic_cast<WindowsImageService*>(GetCurrentController()->ImageService())->CreateImageFromHBITMAP(handle);
|
|
}
|
|
|
|
Ptr<INativeImage> CreateImageFromHICON(HICON handle)
|
|
{
|
|
return dynamic_cast<WindowsImageService*>(GetCurrentController()->ImageService())->CreateImageFromHICON(handle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSINPUTSERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
bool WinIsKeyPressing(vint code)
|
|
{
|
|
return (GetKeyState((int)code)&0xF0)!=0;
|
|
}
|
|
|
|
bool WinIsKeyToggled(vint code)
|
|
{
|
|
return (GetKeyState((int)code)&0x0F)!=0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsInputService
|
|
***********************************************************************/
|
|
|
|
WString WindowsInputService::GetKeyNameInternal(vint code)
|
|
{
|
|
if (code < 8) return L"?";
|
|
wchar_t name[256]={0};
|
|
vint scanCode=MapVirtualKey((int)code, MAPVK_VK_TO_VSC)<<16;
|
|
switch(code)
|
|
{
|
|
case VK_INSERT:
|
|
case VK_DELETE:
|
|
case VK_HOME:
|
|
case VK_END:
|
|
case VK_PRIOR:
|
|
case VK_NEXT:
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
scanCode|=1<<24;
|
|
break;
|
|
case VK_CLEAR:
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
return L"?";
|
|
}
|
|
GetKeyNameText((int)scanCode, name, sizeof(name)/sizeof(*name));
|
|
return name[0]?name:L"?";
|
|
}
|
|
|
|
void WindowsInputService::InitializeKeyNames()
|
|
{
|
|
for (vint i = 0; i < keyNames.Count(); i++)
|
|
{
|
|
keyNames[i] = GetKeyNameInternal(i);
|
|
if (keyNames[i] != L"?")
|
|
{
|
|
keys.Set(keyNames[i], i);
|
|
}
|
|
}
|
|
}
|
|
|
|
WindowsInputService::WindowsInputService(HOOKPROC _mouseProc)
|
|
:ownerHandle(NULL)
|
|
,mouseHook(NULL)
|
|
,isTimerEnabled(false)
|
|
,mouseProc(_mouseProc)
|
|
,keyNames(146)
|
|
{
|
|
InitializeKeyNames();
|
|
}
|
|
|
|
void WindowsInputService::SetOwnerHandle(HWND handle)
|
|
{
|
|
ownerHandle=handle;
|
|
}
|
|
|
|
void WindowsInputService::StartHookMouse()
|
|
{
|
|
if(!IsHookingMouse())
|
|
{
|
|
mouseHook=SetWindowsHookEx(WH_MOUSE_LL, mouseProc, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
void WindowsInputService::StopHookMouse()
|
|
{
|
|
if(IsHookingMouse())
|
|
{
|
|
UnhookWindowsHookEx(mouseHook);
|
|
mouseHook=NULL;
|
|
}
|
|
}
|
|
|
|
bool WindowsInputService::IsHookingMouse()
|
|
{
|
|
return mouseHook!=NULL;
|
|
}
|
|
|
|
void WindowsInputService::StartTimer()
|
|
{
|
|
if(!IsTimerEnabled())
|
|
{
|
|
SetTimer(ownerHandle, 1, 16, NULL);
|
|
isTimerEnabled=true;
|
|
}
|
|
}
|
|
|
|
void WindowsInputService::StopTimer()
|
|
{
|
|
if(IsTimerEnabled())
|
|
{
|
|
KillTimer(ownerHandle, 1);
|
|
isTimerEnabled=false;
|
|
}
|
|
}
|
|
|
|
bool WindowsInputService::IsTimerEnabled()
|
|
{
|
|
return isTimerEnabled;
|
|
}
|
|
|
|
bool WindowsInputService::IsKeyPressing(vint code)
|
|
{
|
|
return WinIsKeyPressing(code);
|
|
}
|
|
|
|
bool WindowsInputService::IsKeyToggled(vint code)
|
|
{
|
|
return WinIsKeyToggled(code);
|
|
}
|
|
|
|
WString WindowsInputService::GetKeyName(vint code)
|
|
{
|
|
if (0 <= code && 0 < keyNames.Count())
|
|
{
|
|
return keyNames[code];
|
|
}
|
|
else
|
|
{
|
|
return L"?";
|
|
}
|
|
}
|
|
|
|
vint WindowsInputService::GetKey(const WString& name)
|
|
{
|
|
vint index = keys.Keys().IndexOf(name);
|
|
return index == -1 ? -1 : keys.Values()[index];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSRESOURCESERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
|
|
/***********************************************************************
|
|
WindowsCursor
|
|
***********************************************************************/
|
|
|
|
WindowsCursor::WindowsCursor(HCURSOR _handle)
|
|
:handle(_handle)
|
|
,isSystemCursor(false)
|
|
,systemCursorType(INativeCursor::Arrow)
|
|
{
|
|
}
|
|
|
|
WindowsCursor::WindowsCursor(SystemCursorType type)
|
|
:handle(NULL)
|
|
,isSystemCursor(true)
|
|
,systemCursorType(type)
|
|
{
|
|
LPWSTR id=NULL;
|
|
switch(type)
|
|
{
|
|
case SmallWaiting:
|
|
id=IDC_APPSTARTING;
|
|
break;
|
|
case LargeWaiting:
|
|
id=IDC_WAIT;
|
|
break;
|
|
case Arrow:
|
|
id=IDC_ARROW;
|
|
break;
|
|
case Cross:
|
|
id=IDC_CROSS;
|
|
break;
|
|
case Hand:
|
|
id=IDC_HAND;
|
|
break;
|
|
case Help:
|
|
id=IDC_HELP;
|
|
break;
|
|
case IBeam:
|
|
id=IDC_IBEAM;
|
|
break;
|
|
case SizeAll:
|
|
id=IDC_SIZEALL;
|
|
break;
|
|
case SizeNESW:
|
|
id=IDC_SIZENESW;
|
|
break;
|
|
case SizeNS:
|
|
id=IDC_SIZENS;
|
|
break;
|
|
case SizeNWSE:
|
|
id=IDC_SIZENWSE;
|
|
break;
|
|
case SizeWE:
|
|
id=IDC_SIZEWE;
|
|
break;
|
|
}
|
|
handle=(HCURSOR)LoadImage(NULL, id, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED);
|
|
}
|
|
|
|
bool WindowsCursor::IsSystemCursor()
|
|
{
|
|
return isSystemCursor;
|
|
}
|
|
|
|
INativeCursor::SystemCursorType WindowsCursor::GetSystemCursorType()
|
|
{
|
|
return systemCursorType;
|
|
}
|
|
|
|
HCURSOR WindowsCursor::GetCursorHandle()
|
|
{
|
|
return handle;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsResourceService
|
|
***********************************************************************/
|
|
|
|
WindowsResourceService::WindowsResourceService()
|
|
{
|
|
{
|
|
systemCursors.Resize(INativeCursor::SystemCursorCount);
|
|
for(vint i=0;i<systemCursors.Count();i++)
|
|
{
|
|
systemCursors[i]=new WindowsCursor((INativeCursor::SystemCursorType)i);
|
|
}
|
|
}
|
|
{
|
|
NONCLIENTMETRICS metrics;
|
|
metrics.cbSize=sizeof(NONCLIENTMETRICS);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
|
|
if(!*metrics.lfMessageFont.lfFaceName)
|
|
{
|
|
metrics.cbSize=sizeof(NONCLIENTMETRICS)-sizeof(metrics.iPaddedBorderWidth);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
|
|
}
|
|
defaultFont.fontFamily=metrics.lfMessageFont.lfFaceName;
|
|
defaultFont.size=metrics.lfMessageFont.lfHeight;
|
|
if(defaultFont.size<0)
|
|
{
|
|
defaultFont.size=-defaultFont.size;
|
|
}
|
|
}
|
|
}
|
|
|
|
INativeCursor* WindowsResourceService::GetSystemCursor(INativeCursor::SystemCursorType type)
|
|
{
|
|
vint index=(vint)type;
|
|
if(0<=index && index<systemCursors.Count())
|
|
{
|
|
return systemCursors[index].Obj();
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
INativeCursor* WindowsResourceService::GetDefaultSystemCursor()
|
|
{
|
|
return GetSystemCursor(INativeCursor::Arrow);
|
|
}
|
|
|
|
FontProperties WindowsResourceService::GetDefaultFont()
|
|
{
|
|
return defaultFont;
|
|
}
|
|
|
|
void WindowsResourceService::SetDefaultFont(const FontProperties& value)
|
|
{
|
|
defaultFont=value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSSCREENSERVICE.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace windows
|
|
{
|
|
|
|
/***********************************************************************
|
|
WindowsScreen
|
|
***********************************************************************/
|
|
|
|
WindowsScreen::WindowsScreen()
|
|
{
|
|
monitor=NULL;
|
|
}
|
|
|
|
Rect WindowsScreen::GetBounds()
|
|
{
|
|
MONITORINFOEX info;
|
|
info.cbSize=sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(monitor, &info);
|
|
return Rect(info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom);
|
|
}
|
|
|
|
Rect WindowsScreen::GetClientBounds()
|
|
{
|
|
MONITORINFOEX info;
|
|
info.cbSize=sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(monitor, &info);
|
|
return Rect(info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom);
|
|
}
|
|
|
|
WString WindowsScreen::GetName()
|
|
{
|
|
MONITORINFOEX info;
|
|
info.cbSize=sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(monitor, &info);
|
|
|
|
wchar_t buffer[sizeof(info.szDevice)/sizeof(*info.szDevice)+1];
|
|
memset(buffer, 0, sizeof(buffer));
|
|
memcpy(buffer, info.szDevice, sizeof(info.szDevice));
|
|
return buffer;
|
|
}
|
|
|
|
bool WindowsScreen::IsPrimary()
|
|
{
|
|
MONITORINFOEX info;
|
|
info.cbSize=sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(monitor, &info);
|
|
return info.dwFlags==MONITORINFOF_PRIMARY;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsScreenService
|
|
***********************************************************************/
|
|
|
|
WindowsScreenService::WindowsScreenService(HandleRetriver _handleRetriver)
|
|
:handleRetriver(_handleRetriver)
|
|
{
|
|
}
|
|
|
|
BOOL CALLBACK WindowsScreenService::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
|
|
{
|
|
MonitorEnumProcData* data=(MonitorEnumProcData*)dwData;
|
|
if(data->currentScreen==data->screenService->screens.Count())
|
|
{
|
|
data->screenService->screens.Add(new WindowsScreen());
|
|
}
|
|
data->screenService->screens[data->currentScreen]->monitor=hMonitor;
|
|
data->currentScreen++;
|
|
return TRUE;
|
|
}
|
|
|
|
void WindowsScreenService::RefreshScreenInformation()
|
|
{
|
|
for(vint i=0;i<screens.Count();i++)
|
|
{
|
|
screens[i]->monitor=NULL;
|
|
}
|
|
MonitorEnumProcData data;
|
|
data.screenService=this;
|
|
data.currentScreen=0;
|
|
EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)(&data));
|
|
}
|
|
|
|
vint WindowsScreenService::GetScreenCount()
|
|
{
|
|
RefreshScreenInformation();
|
|
return GetSystemMetrics(SM_CMONITORS);
|
|
}
|
|
|
|
INativeScreen* WindowsScreenService::GetScreen(vint index)
|
|
{
|
|
RefreshScreenInformation();
|
|
return screens[index].Obj();
|
|
}
|
|
|
|
INativeScreen* WindowsScreenService::GetScreen(INativeWindow* window)
|
|
{
|
|
RefreshScreenInformation();
|
|
HWND hwnd=handleRetriver(window);
|
|
if(hwnd)
|
|
{
|
|
HMONITOR monitor=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
|
|
if(monitor!=NULL)
|
|
{
|
|
for(vint i=0;i<screens.Count();i++)
|
|
{
|
|
if(screens[i]->monitor==monitor)
|
|
{
|
|
return screens[i].Obj();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSLAYOUTPROVIDERWINDOWSGDI.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements_windows_gdi
|
|
{
|
|
using namespace elements;
|
|
using namespace collections;
|
|
using namespace windows;
|
|
|
|
/***********************************************************************
|
|
WindowsGDIParagraph
|
|
***********************************************************************/
|
|
|
|
class WindowsGDIParagraph : public Object, public IGuiGraphicsParagraph, protected UniscribeRun::IRendererCallback
|
|
{
|
|
protected:
|
|
IGuiGraphicsLayoutProvider* provider;
|
|
Ptr<UniscribeParagraph> paragraph;
|
|
WString text;
|
|
IWindowsGDIRenderTarget* renderTarget;
|
|
|
|
vint caret;
|
|
Color caretColor;
|
|
bool caretFrontSide;
|
|
Ptr<WinPen> caretPen;
|
|
|
|
WinDC* paragraphDC;
|
|
Point paragraphOffset;
|
|
IGuiGraphicsParagraphCallback* paragraphCallback;
|
|
|
|
void PrepareUniscribeData()
|
|
{
|
|
if(paragraph->BuildUniscribeData(renderTarget->GetDC()))
|
|
{
|
|
vint width=paragraph->lastAvailableWidth==-1?65536:paragraph->lastAvailableWidth;
|
|
paragraph->Layout(width, paragraph->paragraphAlignment);
|
|
}
|
|
}
|
|
|
|
WinDC* GetWinDC()
|
|
{
|
|
return paragraphDC;
|
|
}
|
|
|
|
Point GetParagraphOffset()
|
|
{
|
|
return paragraphOffset;
|
|
}
|
|
|
|
IGuiGraphicsParagraphCallback* GetParagraphCallback()
|
|
{
|
|
return paragraphCallback;
|
|
}
|
|
public:
|
|
WindowsGDIParagraph(IGuiGraphicsLayoutProvider* _provider, const WString& _text, IGuiGraphicsRenderTarget* _renderTarget, IGuiGraphicsParagraphCallback* _paragraphCallback)
|
|
:provider(_provider)
|
|
,text(_text)
|
|
,renderTarget(dynamic_cast<IWindowsGDIRenderTarget*>(_renderTarget))
|
|
,caret(-1)
|
|
,caretFrontSide(false)
|
|
,paragraphDC(nullptr)
|
|
,paragraphCallback(_paragraphCallback)
|
|
{
|
|
paragraph=new UniscribeParagraph;
|
|
paragraph->paragraphText=text;
|
|
|
|
Ptr<UniscribeFragment> fragment=new UniscribeFragment(_text);
|
|
fragment->fontStyle=GetCurrentController()->ResourceService()->GetDefaultFont();
|
|
paragraph->documentFragments.Add(fragment);
|
|
}
|
|
|
|
~WindowsGDIParagraph()
|
|
{
|
|
CloseCaret();
|
|
}
|
|
|
|
IGuiGraphicsLayoutProvider* GetProvider()override
|
|
{
|
|
return provider;
|
|
}
|
|
|
|
IGuiGraphicsRenderTarget* GetRenderTarget()override
|
|
{
|
|
return renderTarget;
|
|
}
|
|
|
|
bool GetWrapLine()override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void SetWrapLine(bool value)override
|
|
{
|
|
}
|
|
|
|
vint GetMaxWidth()override
|
|
{
|
|
return paragraph->lastAvailableWidth;
|
|
}
|
|
|
|
void SetMaxWidth(vint value)override
|
|
{
|
|
paragraph->BuildUniscribeData(renderTarget->GetDC());
|
|
paragraph->Layout(value, paragraph->paragraphAlignment);
|
|
}
|
|
|
|
Alignment GetParagraphAlignment()override
|
|
{
|
|
return paragraph->paragraphAlignment;
|
|
}
|
|
|
|
void SetParagraphAlignment(Alignment value)override
|
|
{
|
|
paragraph->BuildUniscribeData(renderTarget->GetDC());
|
|
paragraph->Layout(paragraph->lastAvailableWidth, value);
|
|
}
|
|
|
|
bool SetFont(vint start, vint length, const WString& value)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
return paragraph->SetFont(start, length, value);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetSize(vint start, vint length, vint value)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
return paragraph->SetSize(start, length, value);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetStyle(vint start, vint length, TextStyle value)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
return paragraph->SetStyle(start, length, (value&Bold)!=0, (value&Italic)!=0, (value&Underline)!=0, (value&Strikeline)!=0);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetColor(vint start, vint length, Color value)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
return paragraph->SetColor(start, length, value);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetBackgroundColor(vint start, vint length, Color value)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
return paragraph->SetBackgroundColor(start, length, value);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SetInlineObject(vint start, vint length, const InlineObjectProperties& properties)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
if(paragraph->SetInlineObject(start, length, properties))
|
|
{
|
|
if (properties.backgroundImage)
|
|
{
|
|
IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer();
|
|
if(renderer)
|
|
{
|
|
renderer->SetRenderTarget(renderTarget);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ResetInlineObject(vint start, vint length)override
|
|
{
|
|
if(length==0) return true;
|
|
if(0<=start && start<text.Length() && length>=0 && 0<=start+length && start+length<=text.Length())
|
|
{
|
|
if (auto inlineObject = paragraph->ResetInlineObject(start, length))
|
|
{
|
|
if (auto element = inlineObject.Value().backgroundImage)
|
|
{
|
|
auto renderer=element->GetRenderer();
|
|
if(renderer)
|
|
{
|
|
renderer->SetRenderTarget(0);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vint GetHeight()override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->bounds.Height();
|
|
}
|
|
|
|
bool OpenCaret(vint _caret, Color _color, bool _frontSide)override
|
|
{
|
|
if(!IsValidCaret(_caret)) return false;
|
|
if(caret!=-1) CloseCaret();
|
|
caret=_caret;
|
|
caretColor=_color;
|
|
caretFrontSide=_frontSide;
|
|
caretPen=GetWindowsGDIResourceManager()->CreateGdiPen(caretColor);
|
|
return true;
|
|
}
|
|
|
|
bool CloseCaret()override
|
|
{
|
|
if(caret==-1) return false;
|
|
caret=-1;
|
|
GetWindowsGDIResourceManager()->DestroyGdiPen(caretColor);
|
|
caretPen=0;
|
|
return true;
|
|
}
|
|
|
|
void Render(Rect bounds)override
|
|
{
|
|
PrepareUniscribeData();
|
|
|
|
paragraphDC = renderTarget->GetDC();
|
|
paragraphOffset = bounds.LeftTop();
|
|
paragraph->Render(this, true);
|
|
paragraph->Render(this, false);
|
|
paragraphDC = 0;
|
|
|
|
if(caret!=-1)
|
|
{
|
|
Rect caretBounds=GetCaretBounds(caret, caretFrontSide);
|
|
vint x=caretBounds.x1+bounds.x1;
|
|
vint y1=caretBounds.y1+bounds.y1;
|
|
vint y2=y1+caretBounds.Height();
|
|
|
|
WinDC* dc=renderTarget->GetDC();
|
|
dc->SetPen(caretPen);
|
|
dc->MoveTo(x-1, y1);
|
|
dc->LineTo(x-1, y2);
|
|
dc->MoveTo(x, y1);
|
|
dc->LineTo(x, y2);
|
|
}
|
|
}
|
|
|
|
vint GetCaret(vint comparingCaret, CaretRelativePosition position, bool& preferFrontSide)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->GetCaret(comparingCaret, position, preferFrontSide);
|
|
}
|
|
|
|
Rect GetCaretBounds(vint caret, bool frontSide)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->GetCaretBounds(caret, frontSide);
|
|
}
|
|
|
|
vint GetCaretFromPoint(Point point)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->GetCaretFromPoint(point);
|
|
}
|
|
|
|
Nullable<InlineObjectProperties> GetInlineObjectFromPoint(Point point, vint& start, vint& length)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->GetInlineObjectFromPoint(point, start, length);
|
|
}
|
|
|
|
vint GetNearestCaretFromTextPos(vint textPos, bool frontSide)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->GetNearestCaretFromTextPos(textPos, frontSide);
|
|
}
|
|
|
|
bool IsValidCaret(vint caret)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->IsValidCaret(caret);
|
|
}
|
|
|
|
bool IsValidTextPos(vint textPos)override
|
|
{
|
|
PrepareUniscribeData();
|
|
return paragraph->IsValidTextPos(textPos);
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsGDILayoutProvider
|
|
***********************************************************************/
|
|
|
|
Ptr<IGuiGraphicsParagraph> WindowsGDILayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback)
|
|
{
|
|
return new WindowsGDIParagraph(this, text, renderTarget, callback);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSRENDERERSWINDOWSGDI.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements_windows_gdi
|
|
{
|
|
using namespace windows;
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
GuiSolidBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiSolidBorderElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldColor=element->GetColor();
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
|
|
}
|
|
|
|
void GuiSolidBorderElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
resourceManager->DestroyGdiBrush(Color(0, 0, 0, 0));
|
|
}
|
|
|
|
void GuiSolidBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void GuiSolidBorderElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(oldColor.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetBrush(brush);
|
|
renderTarget->GetDC()->SetPen(pen);
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiSolidBorderElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color color=element->GetColor();
|
|
if(oldColor!=color)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
oldColor=color;
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiRoundBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiRoundBorderElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldColor=element->GetColor();
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
|
|
}
|
|
|
|
void GuiRoundBorderElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
resourceManager->DestroyGdiBrush(Color(0, 0, 0, 0));
|
|
}
|
|
|
|
void GuiRoundBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void GuiRoundBorderElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(oldColor.a>0)
|
|
{
|
|
vint ellipse=element->GetRadius()*2;
|
|
renderTarget->GetDC()->SetBrush(brush);
|
|
renderTarget->GetDC()->SetPen(pen);
|
|
renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1, ellipse, ellipse);
|
|
}
|
|
}
|
|
|
|
void GuiRoundBorderElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color color=element->GetColor();
|
|
if(oldColor!=color)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
oldColor=color;
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
Gui3DBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
void Gui3DBorderElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldColor1=element->GetColor1();
|
|
oldColor2=element->GetColor2();
|
|
pen1=resourceManager->CreateGdiPen(oldColor1);
|
|
pen2=resourceManager->CreateGdiPen(oldColor2);
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor1);
|
|
resourceManager->DestroyGdiPen(oldColor2);
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(oldColor1.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetPen(pen1);
|
|
renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1);
|
|
renderTarget->GetDC()->LineTo(bounds.x2, bounds.y1);
|
|
renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1);
|
|
renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2);
|
|
}
|
|
if(oldColor2.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetPen(pen2);
|
|
renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1);
|
|
renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2-1);
|
|
renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1);
|
|
renderTarget->GetDC()->LineTo(bounds.x2-1, bounds.y1);
|
|
}
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color color1=element->GetColor1();
|
|
if(oldColor1!=color1)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor1);
|
|
oldColor1=color1;
|
|
pen1=resourceManager->CreateGdiPen(oldColor1);
|
|
}
|
|
|
|
Color color2=element->GetColor2();
|
|
if(oldColor2!=color2)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor2);
|
|
oldColor2=color2;
|
|
pen2=resourceManager->CreateGdiPen(oldColor2);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
Gui3DSplitterElementRenderer
|
|
***********************************************************************/
|
|
|
|
void Gui3DSplitterElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldColor1=element->GetColor1();
|
|
oldColor2=element->GetColor2();
|
|
pen1=resourceManager->CreateGdiPen(oldColor1);
|
|
pen2=resourceManager->CreateGdiPen(oldColor2);
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor1);
|
|
resourceManager->DestroyGdiPen(oldColor2);
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::Render(Rect bounds)
|
|
{
|
|
Point p11, p12, p21, p22;
|
|
switch(element->GetDirection())
|
|
{
|
|
case Gui3DSplitterElement::Horizontal:
|
|
{
|
|
vint y=bounds.y1+bounds.Height()/2-1;
|
|
p11=Point(bounds.x1, y);
|
|
p12=Point(bounds.x2, y);
|
|
p21=Point(bounds.x1, y+1);
|
|
p22=Point(bounds.x2, y+1);
|
|
}
|
|
break;
|
|
case Gui3DSplitterElement::Vertical:
|
|
{
|
|
vint x=bounds.x1+bounds.Width()/2-1;
|
|
p11=Point(x, bounds.y1);
|
|
p12=Point(x, bounds.y2);
|
|
p21=Point(x+1, bounds.y1);
|
|
p22=Point(x+1, bounds.y2);
|
|
}
|
|
break;
|
|
}
|
|
if(oldColor1.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetPen(pen1);
|
|
renderTarget->GetDC()->MoveTo(p11.x, p11.y);
|
|
renderTarget->GetDC()->LineTo(p12.x, p12.y);
|
|
}
|
|
if(oldColor2.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetPen(pen2);
|
|
renderTarget->GetDC()->MoveTo(p21.x, p21.y);
|
|
renderTarget->GetDC()->LineTo(p22.x, p22.y);
|
|
}
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color color1=element->GetColor1();
|
|
if(oldColor1!=color1)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor1);
|
|
oldColor1=color1;
|
|
pen1=resourceManager->CreateGdiPen(oldColor1);
|
|
}
|
|
|
|
Color color2=element->GetColor2();
|
|
if(oldColor2!=color2)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor2);
|
|
oldColor2=color2;
|
|
pen2=resourceManager->CreateGdiPen(oldColor2);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiSolidBackgroundElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiSolidBackgroundElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldColor=element->GetColor();
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
brush=resourceManager->CreateGdiBrush(oldColor);
|
|
}
|
|
|
|
void GuiSolidBackgroundElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
resourceManager->DestroyGdiBrush(oldColor);
|
|
}
|
|
|
|
void GuiSolidBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void GuiSolidBackgroundElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(oldColor.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetPen(pen);
|
|
renderTarget->GetDC()->SetBrush(brush);
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
renderTarget->GetDC()->FillRect(bounds.Left(), bounds.Top(), bounds.Right(), bounds.Bottom());
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiSolidBackgroundElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color color=element->GetColor();
|
|
if(oldColor!=color)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldColor);
|
|
resourceManager->DestroyGdiBrush(oldColor);
|
|
oldColor=color;
|
|
pen=resourceManager->CreateGdiPen(oldColor);
|
|
brush=resourceManager->CreateGdiBrush(oldColor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiGradientBackgroundElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiGradientBackgroundElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiGradientBackgroundElementRenderer::FinalizeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiGradientBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
void GuiGradientBackgroundElementRenderer::Render(Rect bounds)
|
|
{
|
|
Color color1=element->GetColor1();
|
|
Color color2=element->GetColor2();
|
|
if(color1.a>0 || color2.a>0)
|
|
{
|
|
TRIVERTEX vertices[4];
|
|
GRADIENT_TRIANGLE triangles[2];
|
|
|
|
vertices[0].x=(int)bounds.x1;
|
|
vertices[0].y=(int)bounds.y1;
|
|
vertices[1].x=(int)bounds.x1;
|
|
vertices[1].y=(int)bounds.y2;
|
|
vertices[2].x=(int)bounds.x2;
|
|
vertices[2].y=(int)bounds.y2;
|
|
vertices[3].x=(int)bounds.x2;
|
|
vertices[3].y=(int)bounds.y1;
|
|
|
|
triangles[0].Vertex1=0;
|
|
triangles[0].Vertex2=1;
|
|
triangles[0].Vertex3=2;
|
|
triangles[1].Vertex1=0;
|
|
triangles[1].Vertex2=2;
|
|
triangles[1].Vertex3=3;
|
|
|
|
if(element->GetDirection()==GuiGradientBackgroundElement::Horizontal)
|
|
{
|
|
vertices[0].Red=color1.r<<8;
|
|
vertices[0].Green=color1.g<<8;
|
|
vertices[0].Blue=color1.b<<8;
|
|
vertices[0].Alpha=0xFF00;
|
|
|
|
vertices[1].Red=color1.r<<8;
|
|
vertices[1].Green=color1.g<<8;
|
|
vertices[1].Blue=color1.b<<8;
|
|
vertices[1].Alpha=0xFF00;
|
|
|
|
vertices[2].Red=color2.r<<8;
|
|
vertices[2].Green=color2.g<<8;
|
|
vertices[2].Blue=color2.b<<8;
|
|
vertices[2].Alpha=0xFF00;
|
|
|
|
vertices[3].Red=color2.r<<8;
|
|
vertices[3].Green=color2.g<<8;
|
|
vertices[3].Blue=color2.b<<8;
|
|
vertices[3].Alpha=0xFF00;
|
|
}
|
|
else
|
|
{
|
|
vertices[0].Red=color1.r<<8;
|
|
vertices[0].Green=color1.g<<8;
|
|
vertices[0].Blue=color1.b<<8;
|
|
vertices[0].Alpha=0xFF00;
|
|
|
|
vertices[1].Red=color2.r<<8;
|
|
vertices[1].Green=color2.g<<8;
|
|
vertices[1].Blue=color2.b<<8;
|
|
vertices[1].Alpha=0xFF00;
|
|
|
|
vertices[2].Red=color2.r<<8;
|
|
vertices[2].Green=color2.g<<8;
|
|
vertices[2].Blue=color2.b<<8;
|
|
vertices[2].Alpha=0xFF00;
|
|
|
|
vertices[3].Red=color1.r<<8;
|
|
vertices[3].Green=color1.g<<8;
|
|
vertices[3].Blue=color1.b<<8;
|
|
vertices[3].Alpha=0xFF00;
|
|
}
|
|
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
{
|
|
renderTarget->GetDC()->GradientTriangle(vertices, 6, triangles, 2);
|
|
}
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
{
|
|
Ptr<WinRegion> ellipseRegion=new WinRegion(bounds.x1, bounds.y1, bounds.x2+1, bounds.y2+1, false);
|
|
Ptr<WinRegion> oldRegion=renderTarget->GetDC()->GetClipRegion();
|
|
Ptr<WinRegion> newRegion=new WinRegion(oldRegion, ellipseRegion, RGN_AND);
|
|
renderTarget->GetDC()->ClipRegion(newRegion);
|
|
renderTarget->GetDC()->GradientTriangle(vertices, 6, triangles, 2);
|
|
renderTarget->GetDC()->ClipRegion(oldRegion);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiGradientBackgroundElementRenderer::OnElementStateChanged()
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiSolidLabelElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiSolidLabelElementRenderer::UpdateMinSize()
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
renderTarget->GetDC()->SetFont(font);
|
|
SIZE size={0};
|
|
const WString& text=element->GetText();
|
|
if(element->GetWrapLine())
|
|
{
|
|
if(element->GetWrapLineHeightCalculation())
|
|
{
|
|
if(oldMaxWidth==-1 || text.Length()==0)
|
|
{
|
|
size=renderTarget->GetDC()->MeasureBuffer(L" ");
|
|
}
|
|
else
|
|
{
|
|
size=renderTarget->GetDC()->MeasureWrapLineString(text, oldMaxWidth);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size=text.Length()==0
|
|
?renderTarget->GetDC()->MeasureBuffer(L" ")
|
|
:renderTarget->GetDC()->MeasureString(text)
|
|
;
|
|
}
|
|
minSize=Size((element->GetEllipse()?0:size.cx), size.cy);
|
|
}
|
|
else
|
|
{
|
|
minSize=Size();
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
oldFont=element->GetFont();
|
|
font=resourceManager->CreateGdiFont(oldFont);
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiFont(oldFont);
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
UpdateMinSize();
|
|
}
|
|
|
|
GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer()
|
|
:oldMaxWidth(-1)
|
|
{
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::Render(Rect bounds)
|
|
{
|
|
Color color=element->GetColor();
|
|
if(color.a>0)
|
|
{
|
|
renderTarget->GetDC()->SetFont(font);
|
|
renderTarget->GetDC()->SetTextColor(RGB(color.r, color.g, color.b));
|
|
|
|
UINT format=DT_NOPREFIX;
|
|
RECT rect;
|
|
rect.left=(int)bounds.Left();
|
|
rect.top=(int)bounds.Top();
|
|
rect.right=(int)bounds.Right();
|
|
rect.bottom=(int)bounds.Bottom();
|
|
|
|
if(element->GetMultiline() || element->GetWrapLine())
|
|
{
|
|
format|=DT_EDITCONTROL;
|
|
}
|
|
else
|
|
{
|
|
format|=DT_SINGLELINE;
|
|
switch(element->GetVerticalAlignment())
|
|
{
|
|
case Alignment::Top:
|
|
format|=DT_TOP;
|
|
break;
|
|
case Alignment::Center:
|
|
format|=DT_VCENTER;
|
|
break;
|
|
case Alignment::Bottom:
|
|
format|=DT_BOTTOM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(element->GetHorizontalAlignment())
|
|
{
|
|
case Alignment::Left:
|
|
format|=DT_LEFT;
|
|
break;
|
|
case Alignment::Center:
|
|
format|=DT_CENTER;
|
|
break;
|
|
case Alignment::Right:
|
|
format|=DT_RIGHT;
|
|
break;
|
|
}
|
|
|
|
if(element->GetWrapLine())
|
|
{
|
|
format|=DT_WORDBREAK;
|
|
}
|
|
if(element->GetEllipse())
|
|
{
|
|
format|=DT_END_ELLIPSIS;
|
|
}
|
|
renderTarget->GetDC()->DrawString(rect, element->GetText(), format);
|
|
if(oldMaxWidth!=bounds.Width())
|
|
{
|
|
oldMaxWidth=bounds.Width();
|
|
UpdateMinSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::OnElementStateChanged()
|
|
{
|
|
FontProperties fontProperties=element->GetFont();
|
|
if(oldFont!=fontProperties)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiFont(oldFont);
|
|
oldFont=fontProperties;
|
|
font=resourceManager->CreateGdiFont(oldFont);
|
|
}
|
|
UpdateMinSize();
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiImageFrameElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiImageFrameElementRenderer::UpdateBitmap()
|
|
{
|
|
if(element->GetImage())
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
|
|
bitmap=resourceManager->GetBitmap(frame, element->GetEnabled());
|
|
|
|
if (element->GetStretch())
|
|
{
|
|
minSize=Size(0,0);
|
|
}
|
|
else
|
|
{
|
|
minSize=frame->GetSize();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bitmap=0;
|
|
minSize=Size(0, 0);
|
|
}
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::InitializeInternal()
|
|
{
|
|
UpdateBitmap();
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::FinalizeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
GuiImageFrameElementRenderer::GuiImageFrameElementRenderer()
|
|
{
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(bitmap)
|
|
{
|
|
WinDC* dc=renderTarget->GetDC();
|
|
Rect source(0, 0, minSize.x, minSize.y);
|
|
Rect destination;
|
|
if(element->GetStretch())
|
|
{
|
|
INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
|
|
source = Rect(Point(0, 0), frame->GetSize());
|
|
destination=Rect(bounds.x1, bounds.y1, bounds.x2, bounds.y2);
|
|
}
|
|
else
|
|
{
|
|
vint x=0;
|
|
vint y=0;
|
|
switch(element->GetHorizontalAlignment())
|
|
{
|
|
case Alignment::Left:
|
|
x=bounds.Left();
|
|
break;
|
|
case Alignment::Center:
|
|
x=bounds.Left()+(bounds.Width()-minSize.x)/2;
|
|
break;
|
|
case Alignment::Right:
|
|
x=bounds.Right()-minSize.x;
|
|
break;
|
|
}
|
|
switch(element->GetVerticalAlignment())
|
|
{
|
|
case Alignment::Top:
|
|
y=bounds.Top();
|
|
break;
|
|
case Alignment::Center:
|
|
y=bounds.Top()+(bounds.Height()-minSize.y)/2;
|
|
break;
|
|
case Alignment::Bottom:
|
|
y=bounds.Bottom()-minSize.y;
|
|
break;
|
|
}
|
|
destination=Rect(x, y, x+minSize.x, y+minSize.y);
|
|
}
|
|
if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
vint max=element->GetFrameIndex();
|
|
for(vint i=0;i<=max;i++)
|
|
{
|
|
Ptr<WinBitmap> frameBitmap=resourceManager->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled());
|
|
dc->Draw(
|
|
destination.Left(), destination.Top(), destination.Width(), destination.Height(),
|
|
frameBitmap,
|
|
source.Left(), source.Top(), source.Width(), source.Height()
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dc->Draw(
|
|
destination.Left(), destination.Top(), destination.Width(), destination.Height(),
|
|
bitmap,
|
|
source.Left(), source.Top(), source.Width(), source.Height()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::OnElementStateChanged()
|
|
{
|
|
UpdateBitmap();
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiPolygonElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiPolygonElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
pen=resourceManager->CreateGdiPen(oldPenColor);
|
|
brush=resourceManager->CreateGdiBrush(oldBrushColor);
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldPenColor);
|
|
resourceManager->DestroyGdiBrush(oldBrushColor);
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
GuiPolygonElementRenderer::GuiPolygonElementRenderer()
|
|
:points(0)
|
|
,pointCount(0)
|
|
,oldPenColor(0, 0, 0, 0)
|
|
,oldBrushColor(0, 0, 0, 0)
|
|
{
|
|
}
|
|
|
|
GuiPolygonElementRenderer::~GuiPolygonElementRenderer()
|
|
{
|
|
if(points) delete[] points;
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(pointCount>=3 && (oldPenColor.a || oldBrushColor.a))
|
|
{
|
|
vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1;
|
|
vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1;
|
|
for(vint i=0;i<pointCount;i++)
|
|
{
|
|
points[i].x+=(int)offsetX;
|
|
points[i].y+=(int)offsetY;
|
|
}
|
|
renderTarget->GetDC()->SetPen(pen);
|
|
renderTarget->GetDC()->SetBrush(brush);
|
|
renderTarget->GetDC()->PolyGon(points, pointCount);
|
|
for(vint i=0;i<pointCount;i++)
|
|
{
|
|
points[i].x-=(int)offsetX;
|
|
points[i].y-=(int)offsetY;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::OnElementStateChanged()
|
|
{
|
|
minSize=element->GetSize();
|
|
{
|
|
if(points)
|
|
{
|
|
delete[] points;
|
|
points=0;
|
|
}
|
|
pointCount=element->GetPointCount();
|
|
if(pointCount>0)
|
|
{
|
|
points=new POINT[pointCount];
|
|
for(vint i=0;i<pointCount;i++)
|
|
{
|
|
Point p=element->GetPoint(i);
|
|
points[i].x=(int)p.x;
|
|
points[i].y=(int)p.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
if(oldPenColor!=element->GetBorderColor() || !pen)
|
|
{
|
|
resourceManager->DestroyGdiPen(oldPenColor);
|
|
oldPenColor=element->GetBorderColor();
|
|
pen=resourceManager->CreateGdiPen(oldPenColor);
|
|
}
|
|
if(oldBrushColor!=element->GetBackgroundColor() || !brush)
|
|
{
|
|
resourceManager->DestroyGdiPen(oldBrushColor);
|
|
oldBrushColor=element->GetBackgroundColor();
|
|
brush=resourceManager->CreateGdiBrush(oldBrushColor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiColorizedTextElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiColorizedTextElementRenderer::DestroyColors()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
for(vint i=0;i<colors.Count();i++)
|
|
{
|
|
resourceManager->DestroyGdiBrush(colors[i].normal.background);
|
|
resourceManager->DestroyGdiBrush(colors[i].selectedFocused.background);
|
|
resourceManager->DestroyGdiBrush(colors[i].selectedUnfocused.background);
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::ColorChanged()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
ColorArray newColors;
|
|
newColors.Resize(element->GetColors().Count());
|
|
for(vint i=0;i<newColors.Count();i++)
|
|
{
|
|
text::ColorEntry entry=element->GetColors().Get(i);
|
|
ColorEntryResource newEntry;
|
|
|
|
newEntry.normal.text=entry.normal.text;
|
|
newEntry.normal.background=entry.normal.background;
|
|
newEntry.normal.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.normal.background);
|
|
newEntry.selectedFocused.text=entry.selectedFocused.text;
|
|
newEntry.selectedFocused.background=entry.selectedFocused.background;
|
|
newEntry.selectedFocused.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.selectedFocused.background);
|
|
newEntry.selectedUnfocused.text=entry.selectedUnfocused.text;
|
|
newEntry.selectedUnfocused.background=entry.selectedUnfocused.background;
|
|
newEntry.selectedUnfocused.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.selectedUnfocused.background);
|
|
newColors[i]=newEntry;
|
|
}
|
|
|
|
DestroyColors();
|
|
CopyFrom(colors, newColors);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::FontChanged()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
if(font)
|
|
{
|
|
element->GetLines().SetCharMeasurer(0);
|
|
resourceManager->DestroyGdiFont(oldFont);
|
|
resourceManager->DestroyCharMeasurer(oldFont);
|
|
font=0;
|
|
}
|
|
oldFont=element->GetFont();
|
|
font=resourceManager->CreateGdiFont(oldFont);
|
|
element->GetLines().SetCharMeasurer(resourceManager->CreateCharMeasurer(oldFont).Obj());
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::InitializeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
element->SetCallback(this);
|
|
oldCaretColor=element->GetCaretColor();
|
|
caretPen=resourceManager->CreateGdiPen(oldCaretColor);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::FinalizeInternal()
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
if(font)
|
|
{
|
|
resourceManager->DestroyGdiFont(oldFont);
|
|
resourceManager->DestroyCharMeasurer(oldFont);
|
|
}
|
|
resourceManager->DestroyGdiPen(oldCaretColor);
|
|
DestroyColors();
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
element->GetLines().SetRenderTarget(newRenderTarget);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
WinDC* dc=renderTarget->GetDC();
|
|
dc->SetFont(font);
|
|
|
|
wchar_t passwordChar=element->GetPasswordChar();
|
|
Point viewPosition=element->GetViewPosition();
|
|
Rect viewBounds(viewPosition, bounds.GetSize());
|
|
vint startRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, viewBounds.y1)).row;
|
|
vint endRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, viewBounds.y2)).row;
|
|
TextPos selectionBegin=element->GetCaretBegin()<element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd();
|
|
TextPos selectionEnd=element->GetCaretBegin()>element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd();
|
|
bool focused=element->GetFocused();
|
|
Ptr<windows::WinBrush> lastBrush=0;
|
|
|
|
for(vint row=startRow;row<=endRow;row++)
|
|
{
|
|
Rect startRect=element->GetLines().GetRectFromTextPos(TextPos(row, 0));
|
|
Point startPoint=startRect.LeftTop();
|
|
vint startColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, startPoint.y)).column;
|
|
vint endColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, startPoint.y)).column;
|
|
text::TextLine& line=element->GetLines().GetLine(row);
|
|
|
|
vint x=startColumn==0?0:line.att[startColumn-1].rightOffset;
|
|
for(vint column=startColumn; column<=endColumn; column++)
|
|
{
|
|
bool inSelection=false;
|
|
if(selectionBegin.row==selectionEnd.row)
|
|
{
|
|
inSelection=(row==selectionBegin.row && selectionBegin.column<=column && column<selectionEnd.column);
|
|
}
|
|
else if(row==selectionBegin.row)
|
|
{
|
|
inSelection=selectionBegin.column<=column;
|
|
}
|
|
else if(row==selectionEnd.row)
|
|
{
|
|
inSelection=column<selectionEnd.column;
|
|
}
|
|
else
|
|
{
|
|
inSelection=selectionBegin.row<row && row<selectionEnd.row;
|
|
}
|
|
|
|
bool crlf=column==line.dataLength;
|
|
vint colorIndex=crlf?0:line.att[column].colorIndex;
|
|
if(colorIndex>=colors.Count())
|
|
{
|
|
colorIndex=0;
|
|
}
|
|
ColorItemResource& color=
|
|
!inSelection?colors[colorIndex].normal:
|
|
focused?colors[colorIndex].selectedFocused:
|
|
colors[colorIndex].selectedUnfocused;
|
|
vint x2=crlf?x+startRect.Height()/2:line.att[column].rightOffset;
|
|
vint tx=x-viewPosition.x+bounds.x1;
|
|
vint ty=startPoint.y-viewPosition.y+bounds.y1;
|
|
|
|
if(color.background.a)
|
|
{
|
|
if(lastBrush!=color.backgroundBrush)
|
|
{
|
|
lastBrush=color.backgroundBrush;
|
|
dc->SetBrush(lastBrush);
|
|
}
|
|
dc->FillRect(tx, ty, tx+(x2-x), ty+startRect.Height());
|
|
}
|
|
if(!crlf)
|
|
{
|
|
if(color.text.a)
|
|
{
|
|
dc->SetTextColor(RGB(color.text.r, color.text.g, color.text.b));
|
|
dc->DrawBuffer(tx, ty, (passwordChar?&passwordChar:&line.text[column]), 1);
|
|
}
|
|
}
|
|
x=x2;
|
|
}
|
|
}
|
|
|
|
if(element->GetCaretVisible() && element->GetLines().IsAvailable(element->GetCaretEnd()))
|
|
{
|
|
Point caretPoint=element->GetLines().GetPointFromTextPos(element->GetCaretEnd());
|
|
vint height=element->GetLines().GetRowHeight();
|
|
dc->SetPen(caretPen);
|
|
dc->MoveTo(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1);
|
|
dc->LineTo(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1);
|
|
dc->MoveTo(caretPoint.x-1-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1);
|
|
dc->LineTo(caretPoint.x-1-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::OnElementStateChanged()
|
|
{
|
|
Color caretColor=element->GetCaretColor();
|
|
if(oldCaretColor!=caretColor)
|
|
{
|
|
IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager();
|
|
resourceManager->DestroyGdiPen(oldCaretColor);
|
|
oldCaretColor=caretColor;
|
|
caretPen=resourceManager->CreateGdiPen(oldCaretColor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiGDIElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiGDIElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiGDIElementRenderer::FinalizeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiGDIElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
|
|
{
|
|
}
|
|
|
|
GuiGDIElementRenderer::GuiGDIElementRenderer()
|
|
{
|
|
}
|
|
|
|
GuiGDIElementRenderer::~GuiGDIElementRenderer()
|
|
{
|
|
}
|
|
|
|
void GuiGDIElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
renderTarget->PushClipper(bounds);
|
|
if(!renderTarget->IsClipperCoverWholeTarget())
|
|
{
|
|
WinDC* dc=renderTarget->GetDC();
|
|
GuiGDIElementEventArgs arguments(element, dc, bounds);
|
|
element->Rendering.Execute(arguments);
|
|
}
|
|
renderTarget->PopClipper();
|
|
}
|
|
}
|
|
|
|
void GuiGDIElementRenderer::OnElementStateChanged()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSUNISCRIBE.CPP
|
|
***********************************************************************/
|
|
|
|
#pragma comment(lib, "usp10.lib")
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements_windows_gdi
|
|
{
|
|
using namespace regex;
|
|
|
|
/***********************************************************************
|
|
UniscribeFragment
|
|
***********************************************************************/
|
|
|
|
UniscribeFragment::UniscribeFragment(const WString& _text)
|
|
:text(_text)
|
|
{
|
|
colors.Add(UniscribeColorRange(0, text.Length()), UniscribeColor(Color(0, 0, 0), Color(0, 0, 0, 0)));
|
|
}
|
|
|
|
WString UniscribeFragment::GetFingerprint()
|
|
{
|
|
return fontStyle.fontFamily+L"#"
|
|
+itow(fontStyle.size)+L"#"
|
|
+(fontStyle.bold?L"B":L"N")+L"#"
|
|
+(fontStyle.italic?L"I":L"N")+L"#"
|
|
+(fontStyle.underline?L"U":L"N")+L"#"
|
|
+(fontStyle.strikeline?L"S":L"N")+L"#"
|
|
;
|
|
}
|
|
|
|
void UniscribeFragment::CutColors(vint start, vint length)
|
|
{
|
|
vint end=start+length;
|
|
for(vint i=colors.Count()-1;i>=0;i--)
|
|
{
|
|
UniscribeColorRange key=colors.Keys()[i];
|
|
if(key.start<end && start<key.end)
|
|
{
|
|
UniscribeColor value=colors.Values()[i];
|
|
|
|
vint s1=key.start;
|
|
vint s2=key.start>start?key.start:start;
|
|
vint s3=key.end<end?key.end:end;
|
|
vint s4=key.end;
|
|
|
|
colors.Remove(key);
|
|
if(s1<s2)
|
|
{
|
|
colors.Add(UniscribeColorRange(s1, s2), value);
|
|
}
|
|
if(s2<s3)
|
|
{
|
|
colors.Add(UniscribeColorRange(s2, s3), value);
|
|
}
|
|
if(s3<s4)
|
|
{
|
|
colors.Add(UniscribeColorRange(s3, s4), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UniscribeFragment::UpdateOverlappedColors(vint start, vint length, Color UniscribeColor::* colorField, Color color)
|
|
{
|
|
vint end=start+length;
|
|
for(vint i=colors.Count()-1;i>=0;i--)
|
|
{
|
|
UniscribeColorRange key=colors.Keys()[i];
|
|
if(key.start<end && start<key.end)
|
|
{
|
|
UniscribeColor value=colors.Values()[i];
|
|
value.*colorField=color;
|
|
colors.Set(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UniscribeFragment::DefragmentColors()
|
|
{
|
|
vint lastIndex=-1;
|
|
UniscribeColor lastColor;
|
|
for(vint i=colors.Count()-1;i>=-1;i--)
|
|
{
|
|
if(lastIndex==-1)
|
|
{
|
|
lastIndex=i;
|
|
if(i!=-1)
|
|
{
|
|
lastColor=colors.Values()[i];
|
|
}
|
|
}
|
|
else if(i==-1 || colors.Values()[i]!=lastColor)
|
|
{
|
|
if(lastIndex-i>0)
|
|
{
|
|
vint start=colors.Keys()[i+1].start;
|
|
vint end=colors.Keys()[lastIndex].end;
|
|
UniscribeColorRange key(start, end);
|
|
|
|
for(vint j=lastIndex;j>i;j--)
|
|
{
|
|
colors.Remove(colors.Keys()[j]);
|
|
}
|
|
colors.Add(key, lastColor);
|
|
}
|
|
lastIndex=i;
|
|
if(i!=-1)
|
|
{
|
|
lastColor=colors.Values()[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UniscribeColor UniscribeFragment::GetCharColor(vint charIndex)
|
|
{
|
|
vint start=0;
|
|
vint end=colors.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
UniscribeColorRange key=colors.Keys()[middle];
|
|
if(charIndex<key.start)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(charIndex>=key.end)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else
|
|
{
|
|
return colors.Values()[middle];
|
|
}
|
|
}
|
|
return UniscribeColor();
|
|
}
|
|
|
|
Ptr<UniscribeFragment> UniscribeFragment::Copy(vint start, vint length)
|
|
{
|
|
vint end=start+length;
|
|
Ptr<UniscribeFragment> fragment=new UniscribeFragment(length==0?L"":text.Sub(start, length));
|
|
fragment->fontStyle=fontStyle;
|
|
fragment->fontObject=fontObject;
|
|
|
|
if(length!=0)
|
|
{
|
|
fragment->colors.Clear();
|
|
CutColors(start, length);
|
|
for(vint i=0;i<colors.Count();i++)
|
|
{
|
|
UniscribeColorRange key=colors.Keys()[i];
|
|
if(key.start<end && start<key.end)
|
|
{
|
|
UniscribeColor value=colors.Values()[i];
|
|
fragment->colors.Add(UniscribeColorRange(key.start-start, key.end-start), value);
|
|
}
|
|
}
|
|
}
|
|
return fragment;
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeGlyphData
|
|
***********************************************************************/
|
|
|
|
UniscribeGlyphData::UniscribeGlyphData()
|
|
{
|
|
ClearUniscribeData(0, 0);
|
|
}
|
|
|
|
void UniscribeGlyphData::ClearUniscribeData(vint glyphCount, vint length)
|
|
{
|
|
glyphs.Resize(glyphCount);
|
|
glyphVisattrs.Resize(glyphCount);
|
|
glyphAdvances.Resize(glyphCount);
|
|
glyphOffsets.Resize(glyphCount);
|
|
charCluster.Resize(length);
|
|
memset(&runAbc, 0, sizeof(runAbc));
|
|
memset(&sa, 0, sizeof(sa));
|
|
}
|
|
|
|
bool UniscribeGlyphData::BuildUniscribeData(WinDC* dc, SCRIPT_ITEM* scriptItem, SCRIPT_CACHE& scriptCache, const wchar_t* runText, vint length, List<vint>& breakings, List<bool>& breakingAvailabilities)
|
|
{
|
|
vint glyphCount=glyphs.Count();
|
|
bool resizeGlyphData=false;
|
|
if(glyphCount==0)
|
|
{
|
|
glyphCount=(vint)(1.5*length+16);
|
|
resizeGlyphData=true;
|
|
}
|
|
sa=scriptItem->a;
|
|
WinDC* dcParameter=0;
|
|
{
|
|
// generate shape information
|
|
if(resizeGlyphData)
|
|
{
|
|
glyphs.Resize(glyphCount);
|
|
glyphVisattrs.Resize(glyphCount);
|
|
charCluster.Resize(length);
|
|
}
|
|
|
|
while(true)
|
|
{
|
|
int availableGlyphCount=0;
|
|
HRESULT hr=ScriptShape(
|
|
(dcParameter?dcParameter->GetHandle():NULL),
|
|
&scriptCache,
|
|
runText,
|
|
(int)length,
|
|
(int)glyphCount,
|
|
&sa,
|
|
&glyphs[0],
|
|
&charCluster[0],
|
|
&glyphVisattrs[0],
|
|
&availableGlyphCount
|
|
);
|
|
if(hr==0)
|
|
{
|
|
glyphCount=availableGlyphCount;
|
|
break;
|
|
}
|
|
else if(hr==E_PENDING)
|
|
{
|
|
dcParameter=dc;
|
|
}
|
|
else if(hr==E_OUTOFMEMORY)
|
|
{
|
|
if(resizeGlyphData)
|
|
{
|
|
glyphCount+=length;
|
|
}
|
|
else
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
}
|
|
else if(hr==USP_E_SCRIPT_NOT_IN_FONT)
|
|
{
|
|
if(sa.eScript==SCRIPT_UNDEFINED)
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
else
|
|
{
|
|
sa.eScript=SCRIPT_UNDEFINED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
}
|
|
if(resizeGlyphData)
|
|
{
|
|
glyphs.Resize(glyphCount);
|
|
glyphVisattrs.Resize(glyphCount);
|
|
}
|
|
}
|
|
|
|
{
|
|
SCRIPT_FONTPROPERTIES fp;
|
|
memset(&fp, 0, sizeof(fp));
|
|
fp.cBytes=sizeof(fp);
|
|
HRESULT hr=ScriptGetFontProperties(
|
|
(dcParameter?dcParameter->GetHandle():NULL),
|
|
&scriptCache,
|
|
&fp
|
|
);
|
|
WORD invalidGlyph=fp.wgDefault;
|
|
if(hr!=S_OK)
|
|
{
|
|
invalidGlyph=0;
|
|
}
|
|
|
|
// generate breaking information
|
|
breakings.Add(0);
|
|
vint charIndex=0;
|
|
bool lastGlyphAvailable=false;
|
|
while(charIndex<length)
|
|
{
|
|
vint glyphIndex=charCluster[charIndex];
|
|
vint nextCharIndex=charIndex;
|
|
while(nextCharIndex<length && charCluster[nextCharIndex]==glyphIndex)
|
|
{
|
|
nextCharIndex++;
|
|
}
|
|
|
|
vint glyphCount=0;
|
|
if(nextCharIndex==length)
|
|
{
|
|
glyphCount=glyphs.Count()-glyphIndex;
|
|
}
|
|
else
|
|
{
|
|
glyphCount=charCluster[nextCharIndex]-glyphIndex;
|
|
}
|
|
|
|
if(scriptItem->a.fRTL)
|
|
{
|
|
glyphCount=-glyphCount;
|
|
glyphIndex-=glyphCount-1;
|
|
}
|
|
|
|
bool available=true;
|
|
for(vint i=0;i<glyphCount;i++)
|
|
{
|
|
if(glyphs[i+glyphIndex]==invalidGlyph)
|
|
{
|
|
available=false;
|
|
}
|
|
}
|
|
|
|
if(charIndex==0)
|
|
{
|
|
lastGlyphAvailable=available;
|
|
breakingAvailabilities.Add(available);
|
|
}
|
|
else if(lastGlyphAvailable!=available)
|
|
{
|
|
breakings.Add(charIndex);
|
|
lastGlyphAvailable=available;
|
|
breakingAvailabilities.Add(available);
|
|
}
|
|
|
|
charIndex=nextCharIndex;
|
|
}
|
|
}
|
|
|
|
if(breakings.Count()==1)
|
|
{
|
|
// generate place information
|
|
WinDC* dcParameter=0;
|
|
if(resizeGlyphData)
|
|
{
|
|
glyphAdvances.Resize(glyphCount);
|
|
glyphOffsets.Resize(glyphCount);
|
|
}
|
|
while(true)
|
|
{
|
|
HRESULT hr=ScriptPlace(
|
|
(dcParameter?dcParameter->GetHandle():NULL),
|
|
&scriptCache,
|
|
&glyphs[0],
|
|
(int)glyphCount,
|
|
&glyphVisattrs[0],
|
|
&sa,
|
|
&glyphAdvances[0],
|
|
&glyphOffsets[0],
|
|
&runAbc
|
|
);
|
|
if(hr==0)
|
|
{
|
|
break;
|
|
}
|
|
else if(hr==E_PENDING)
|
|
{
|
|
dcParameter=dc;
|
|
}
|
|
else
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
BUILD_UNISCRIBE_DATA_FAILED:
|
|
return false;
|
|
}
|
|
|
|
void UniscribeGlyphData::BuildUniscribeData(WinDC* dc, SCRIPT_ITEM* scriptItem, SCRIPT_LOGATTR* charLogattrs, const wchar_t* runText, vint length)
|
|
{
|
|
vint glyphCount=0;
|
|
for(vint i=0;i<length;i++)
|
|
{
|
|
if(i==0 || charLogattrs[i].fCharStop)
|
|
{
|
|
glyphCount++;
|
|
}
|
|
}
|
|
|
|
ClearUniscribeData(glyphCount, length);
|
|
sa=scriptItem->a;
|
|
memset(&glyphs[0], 0, sizeof(glyphs[0])*glyphs.Count());
|
|
memset(&glyphVisattrs[0], 0, sizeof(glyphVisattrs[0])*glyphVisattrs.Count());
|
|
memset(&glyphAdvances[0], 0, sizeof(glyphAdvances[0])*glyphAdvances.Count());
|
|
memset(&glyphOffsets[0], 0, sizeof(glyphOffsets[0])*glyphOffsets.Count());
|
|
memset(&charCluster[0], 0, sizeof(charCluster[0])*charCluster.Count());
|
|
|
|
for(vint i=0;i<glyphCount;i++)
|
|
{
|
|
glyphs[i]=(WORD)i;
|
|
}
|
|
|
|
if(sa.fRTL)
|
|
{
|
|
vint currentGlyphCount=0;
|
|
for(vint i=0;i<length;i++)
|
|
{
|
|
if(i==0 || charLogattrs[i].fCharStop)
|
|
{
|
|
currentGlyphCount++;
|
|
}
|
|
charCluster[i]=(WORD)(glyphCount-currentGlyphCount);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vint currentGlyphCount=0;
|
|
for(vint i=0;i<length;i++)
|
|
{
|
|
if(i==0 || charLogattrs[i].fCharStop)
|
|
{
|
|
currentGlyphCount++;
|
|
}
|
|
charCluster[i]=(WORD)(currentGlyphCount-1);
|
|
}
|
|
}
|
|
|
|
{
|
|
vint lastCharIndex=0;
|
|
vint lastGlyphIndex=0;
|
|
for(vint i=1;i<=length;i++)
|
|
{
|
|
if(i==0 || charLogattrs[i].fCharStop)
|
|
{
|
|
vint glyphLength=i-lastCharIndex;
|
|
const wchar_t* glyphText=sa.fRTL?runText+length-lastCharIndex-glyphLength:runText+lastCharIndex;
|
|
SIZE size=dc->MeasureBuffer(runText, glyphLength, -1);
|
|
glyphAdvances[lastGlyphIndex]=size.cx;
|
|
lastCharIndex=i;
|
|
lastGlyphIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(vint i=0;i<glyphCount;i++)
|
|
{
|
|
runAbc.abcB+=glyphAdvances[i];
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeItem
|
|
***********************************************************************/
|
|
|
|
UniscribeItem::UniscribeItem()
|
|
:startFromLine(0)
|
|
,length(0)
|
|
,itemText(0)
|
|
{
|
|
}
|
|
|
|
UniscribeItem::~UniscribeItem()
|
|
{
|
|
}
|
|
|
|
void UniscribeItem::ClearUniscribeData()
|
|
{
|
|
charLogattrs.Resize(0);
|
|
}
|
|
|
|
bool UniscribeItem::BuildUniscribeData()
|
|
{
|
|
// generate break information
|
|
charLogattrs.Resize(length);
|
|
|
|
HRESULT hr=ScriptBreak(
|
|
itemText,
|
|
(int)length,
|
|
&scriptItem.a,
|
|
&charLogattrs[0]
|
|
);
|
|
if(hr!=0)
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
|
|
return true;
|
|
BUILD_UNISCRIBE_DATA_FAILED:
|
|
ClearUniscribeData();
|
|
return false;
|
|
}
|
|
|
|
bool UniscribeItem::IsRightToLeft()
|
|
{
|
|
return scriptItem.a.fRTL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeRun
|
|
***********************************************************************/
|
|
|
|
UniscribeRun::UniscribeRun()
|
|
:documentFragment(0)
|
|
,scriptItem(0)
|
|
,startFromLine(0)
|
|
,startFromFragment(0)
|
|
,length(0)
|
|
,runText(0)
|
|
{
|
|
}
|
|
|
|
UniscribeRun::~UniscribeRun()
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeTextRun
|
|
***********************************************************************/
|
|
|
|
UniscribeTextRun::UniscribeTextRun()
|
|
:scriptCache(0)
|
|
,advance(0)
|
|
,needFontFallback(false)
|
|
{
|
|
}
|
|
|
|
UniscribeTextRun::~UniscribeTextRun()
|
|
{
|
|
ClearUniscribeData();
|
|
}
|
|
|
|
void UniscribeTextRun::ClearUniscribeData()
|
|
{
|
|
if(scriptCache)
|
|
{
|
|
ScriptFreeCache(&scriptCache);
|
|
scriptCache=0;
|
|
}
|
|
advance=0;
|
|
needFontFallback=false;
|
|
wholeGlyph.ClearUniscribeData(0, 0);
|
|
}
|
|
|
|
void UniscribeTextRun::SearchSingleGlyphCluster(vint charStart, vint& charLength, vint& cluster, vint& nextCluster)
|
|
{
|
|
cluster=wholeGlyph.charCluster[charStart];
|
|
vint nextChar=charStart;
|
|
while(nextChar<wholeGlyph.charCluster.Count())
|
|
{
|
|
if(wholeGlyph.charCluster[nextChar]!=cluster)
|
|
{
|
|
break;
|
|
}
|
|
nextChar++;
|
|
}
|
|
charLength=nextChar-charStart;
|
|
SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
|
|
}
|
|
|
|
void UniscribeTextRun::SearchSingleChar(vint charStart, vint& charLength, vint& cluster, vint& nextCluster)
|
|
{
|
|
charLength=0;
|
|
vint startFromItem=charStart+startFromLine-scriptItem->startFromLine;
|
|
vint currentFromItem=startFromItem;
|
|
while(++currentFromItem<scriptItem->length)
|
|
{
|
|
if(scriptItem->charLogattrs[currentFromItem].fCharStop)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
charLength=currentFromItem-startFromItem;
|
|
SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
|
|
}
|
|
|
|
void UniscribeTextRun::SearchGlyphCluster(vint charStart, vint charLength, vint& cluster, vint& nextCluster)
|
|
{
|
|
cluster=wholeGlyph.charCluster[charStart];
|
|
if(charStart+charLength>length)
|
|
{
|
|
charLength=length-charStart;
|
|
}
|
|
if(scriptItem->IsRightToLeft())
|
|
{
|
|
nextCluster
|
|
=charStart+charLength==length
|
|
?-1
|
|
:wholeGlyph.charCluster[charStart+charLength];
|
|
}
|
|
else
|
|
{
|
|
nextCluster
|
|
=charStart+charLength==length
|
|
?wholeGlyph.glyphs.Count()
|
|
:wholeGlyph.charCluster[charStart+charLength];
|
|
}
|
|
}
|
|
|
|
bool UniscribeTextRun::BuildUniscribeData(WinDC* dc, List<vint>& breakings)
|
|
{
|
|
ClearUniscribeData();
|
|
|
|
dc->SetFont(documentFragment->fontObject);
|
|
List<bool> breakingAvailabilities;
|
|
if(!wholeGlyph.BuildUniscribeData(dc, &scriptItem->scriptItem, scriptCache, runText, length, breakings, breakingAvailabilities))
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
|
|
if(breakings.Count()==1 && !breakingAvailabilities[0])
|
|
{
|
|
SCRIPT_LOGATTR* charLogattrs=&scriptItem->charLogattrs[0]+startFromLine-scriptItem->startFromLine;
|
|
wholeGlyph.BuildUniscribeData(dc, &scriptItem->scriptItem, charLogattrs, runText, length);
|
|
needFontFallback=true;
|
|
}
|
|
advance=wholeGlyph.runAbc.abcA+wholeGlyph.runAbc.abcB+wholeGlyph.runAbc.abcC;
|
|
|
|
return true;
|
|
BUILD_UNISCRIBE_DATA_FAILED:
|
|
ClearUniscribeData();
|
|
return false;
|
|
}
|
|
|
|
vint UniscribeTextRun::SumWidth(vint charStart, vint charLength)
|
|
{
|
|
vint cluster=0;
|
|
vint nextCluster=0;
|
|
SearchGlyphCluster(charStart, charLength, cluster, nextCluster);
|
|
vint width=0;
|
|
if(scriptItem->IsRightToLeft())
|
|
{
|
|
for(vint i=cluster;i>nextCluster;i--)
|
|
{
|
|
width+=wholeGlyph.glyphAdvances[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(vint i=cluster;i<nextCluster;i++)
|
|
{
|
|
width+=wholeGlyph.glyphAdvances[i];
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
|
|
vint UniscribeTextRun::SumHeight()
|
|
{
|
|
return documentFragment->fontStyle.size;
|
|
}
|
|
|
|
vint UniscribeTextRun::SumTextHeight()
|
|
{
|
|
return SumHeight();
|
|
}
|
|
|
|
void UniscribeTextRun::SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)
|
|
{
|
|
vint width=0;
|
|
charLength=0;
|
|
charAdvances=0;
|
|
for(vint i=tempStart;i<=length;)
|
|
{
|
|
if(i==length || scriptItem->charLogattrs[i+(startFromLine-scriptItem->startFromLine)].fSoftBreak==TRUE)
|
|
{
|
|
if(width<=maxWidth || (firstRun && charLength==0))
|
|
{
|
|
charLength=i-tempStart;
|
|
charAdvances=width;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if(i==length) break;
|
|
|
|
vint cluster=wholeGlyph.charCluster[i];
|
|
vint clusterLength=1;
|
|
while(i+clusterLength<length)
|
|
{
|
|
if(wholeGlyph.charCluster[i+clusterLength]==cluster)
|
|
{
|
|
clusterLength++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(scriptItem->IsRightToLeft())
|
|
{
|
|
vint nextCluster
|
|
=i+clusterLength==length
|
|
?-1
|
|
:wholeGlyph.charCluster[i+clusterLength];
|
|
for(vint j=cluster;j>nextCluster;j--)
|
|
{
|
|
width+=wholeGlyph.glyphAdvances[j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vint nextCluster
|
|
=i+clusterLength==length
|
|
?wholeGlyph.glyphs.Count()
|
|
:wholeGlyph.charCluster[i+clusterLength];
|
|
for(vint j=cluster;j<nextCluster;j++)
|
|
{
|
|
width+=wholeGlyph.glyphAdvances[j];
|
|
}
|
|
}
|
|
i+=clusterLength;
|
|
}
|
|
}
|
|
|
|
void UniscribeTextRun::Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)
|
|
{
|
|
auto dc = callback->GetWinDC();
|
|
RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex];
|
|
if(fragment.length==0) return;
|
|
|
|
vint startFromFragmentBounds=0;
|
|
vint accumulatedWidth=0;
|
|
while(startFromFragmentBounds<fragment.length)
|
|
{
|
|
vint charIndex=fragment.startFromRun+startFromFragmentBounds;
|
|
vint charLength=0;
|
|
vint cluster=0;
|
|
vint nextCluster=0;
|
|
SearchSingleGlyphCluster(charIndex, charLength, cluster, nextCluster);
|
|
|
|
vint clusterStart=0;
|
|
vint clusterCount=0;
|
|
if(scriptItem->IsRightToLeft())
|
|
{
|
|
clusterStart=nextCluster+1;
|
|
clusterCount=cluster-nextCluster;
|
|
}
|
|
else
|
|
{
|
|
clusterStart=cluster;
|
|
clusterCount=nextCluster-cluster;
|
|
}
|
|
|
|
vint clusterWidth=0;
|
|
for(vint i=0;i<clusterCount;i++)
|
|
{
|
|
clusterWidth+=wholeGlyph.glyphAdvances[i+clusterStart];
|
|
}
|
|
|
|
vint x=0;
|
|
if(scriptItem->IsRightToLeft())
|
|
{
|
|
x=fragment.bounds.x2-accumulatedWidth-clusterWidth;
|
|
}
|
|
else
|
|
{
|
|
x=fragment.bounds.x1+accumulatedWidth;
|
|
}
|
|
RECT rect;
|
|
rect.left=(int)(x+offsetX);
|
|
rect.top=(int)(fragment.bounds.Top()+offsetY);
|
|
rect.right=(int)(rect.left+clusterWidth);
|
|
rect.bottom=(int)(rect.top+fragment.bounds.Height()*1.5);
|
|
|
|
UniscribeColor color=documentFragment->GetCharColor(charIndex+startFromFragment);
|
|
if(renderBackground)
|
|
{
|
|
Color backgroundColor=color.backgroundColor;
|
|
|
|
if(backgroundColor.a>0)
|
|
{
|
|
Ptr<WinBrush> brush=new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b));
|
|
dc->SetBrush(brush);
|
|
dc->FillRect(rect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Color fontColor=color.fontColor;
|
|
dc->SetFont(documentFragment->fontObject);
|
|
dc->SetTextColor(RGB(fontColor.r, fontColor.g, fontColor.b));
|
|
|
|
if(needFontFallback)
|
|
{
|
|
dc->DrawBuffer(rect.left, rect.top, runText+charIndex, charLength);
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr=ScriptTextOut(
|
|
dc->GetHandle(),
|
|
&scriptCache,
|
|
rect.left,
|
|
rect.top,
|
|
0,
|
|
&rect,
|
|
&wholeGlyph.sa,
|
|
NULL,
|
|
0,
|
|
&wholeGlyph.glyphs[clusterStart],
|
|
(int)(clusterCount),
|
|
&wholeGlyph.glyphAdvances[clusterStart],
|
|
NULL,
|
|
&wholeGlyph.glyphOffsets[clusterStart]
|
|
);
|
|
}
|
|
}
|
|
|
|
startFromFragmentBounds+=charLength;
|
|
accumulatedWidth+=clusterWidth;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeEmbeddedObjectRun
|
|
***********************************************************************/
|
|
|
|
UniscribeEmbeddedObjectRun::UniscribeEmbeddedObjectRun()
|
|
{
|
|
}
|
|
|
|
UniscribeEmbeddedObjectRun::~UniscribeEmbeddedObjectRun()
|
|
{
|
|
}
|
|
|
|
bool UniscribeEmbeddedObjectRun::BuildUniscribeData(WinDC* dc, List<vint>& breakings)
|
|
{
|
|
breakings.Add(0);
|
|
return true;
|
|
}
|
|
|
|
vint UniscribeEmbeddedObjectRun::SumWidth(vint charStart, vint charLength)
|
|
{
|
|
return properties.size.x;
|
|
}
|
|
|
|
vint UniscribeEmbeddedObjectRun::SumHeight()
|
|
{
|
|
return properties.size.y;
|
|
}
|
|
|
|
vint UniscribeEmbeddedObjectRun::SumTextHeight()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void UniscribeEmbeddedObjectRun::SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances)
|
|
{
|
|
if (firstRun || properties.size.x <= maxWidth)
|
|
{
|
|
charLength = length - tempStart;
|
|
charAdvances = properties.size.x;
|
|
}
|
|
else
|
|
{
|
|
charLength = 0;
|
|
charAdvances = 0;
|
|
}
|
|
}
|
|
|
|
void UniscribeEmbeddedObjectRun::Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground)
|
|
{
|
|
auto dc = callback->GetWinDC();
|
|
RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex];
|
|
if(renderBackground)
|
|
{
|
|
RECT rect;
|
|
rect.left=(int)(fragment.bounds.Left()+offsetX)-2;
|
|
rect.top=(int)(fragment.bounds.Top()+offsetY)-2;
|
|
rect.right=(int)(fragment.bounds.Right()+offsetX)+2;
|
|
rect.bottom=(int)(fragment.bounds.Bottom()+offsetY)+2;
|
|
|
|
Color backgroundColor=documentFragment->colors.Values()[0].backgroundColor;
|
|
|
|
if(backgroundColor.a>0)
|
|
{
|
|
Ptr<WinBrush> brush=new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b));
|
|
dc->SetBrush(brush);
|
|
dc->FillRect(rect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (properties.backgroundImage)
|
|
{
|
|
Rect bounds=fragment.bounds;
|
|
bounds.x1+=offsetX;
|
|
bounds.x2+=offsetX;
|
|
bounds.y1+=offsetY;
|
|
bounds.y2+=offsetY;
|
|
IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer();
|
|
if(renderer)
|
|
{
|
|
renderer->Render(bounds);
|
|
}
|
|
}
|
|
|
|
if (properties.callbackId != -1)
|
|
{
|
|
if (auto paragraphCallback = callback->GetParagraphCallback())
|
|
{
|
|
auto offset = callback->GetParagraphOffset();
|
|
vint x = fragment.bounds.x1 + offsetX - offset.x;
|
|
vint y = fragment.bounds.y1 + offsetY - offset.y;
|
|
auto size = paragraphCallback->OnRenderInlineObject(properties.callbackId, Rect(Point(x, y), fragment.bounds.GetSize()));
|
|
properties.size = size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeVirtualLine
|
|
***********************************************************************/
|
|
|
|
UniscribeVirtualLine::UniscribeVirtualLine()
|
|
:startFromLine(0)
|
|
,length(0)
|
|
,runText(0)
|
|
,firstRunIndex(-1)
|
|
,firstRunBoundsIndex(-1)
|
|
,lastRunIndex(-1)
|
|
,lastRunBoundsIndex(-1)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeLine
|
|
***********************************************************************/
|
|
|
|
UniscribeLine::UniscribeLine()
|
|
:startFromParagraph(0)
|
|
{
|
|
}
|
|
|
|
void UniscribeLine::ClearUniscribeData()
|
|
{
|
|
scriptItems.Clear();
|
|
scriptRuns.Clear();
|
|
virtualLines.Clear();
|
|
}
|
|
|
|
bool UniscribeLine::BuildUniscribeData(WinDC* dc)
|
|
{
|
|
lineText=L"";
|
|
ClearUniscribeData();
|
|
vint current=0;
|
|
List<vint> fragmentStarts;
|
|
FOREACH(Ptr<UniscribeFragment>, fragment, documentFragments)
|
|
{
|
|
fragmentStarts.Add(current);
|
|
lineText+=fragment->text;
|
|
current+=fragment->text.Length();
|
|
}
|
|
|
|
if(lineText!=L"")
|
|
{
|
|
{
|
|
SCRIPT_DIGITSUBSTITUTE sds={0};
|
|
ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &sds);
|
|
SCRIPT_CONTROL sc={0};
|
|
SCRIPT_STATE ss={0};
|
|
ScriptApplyDigitSubstitution(&sds, &sc, &ss);
|
|
|
|
// itemize a line
|
|
Array<SCRIPT_ITEM> items(lineText.Length()+2);
|
|
int scriptItemCount=0;
|
|
HRESULT hr=ScriptItemize(
|
|
lineText.Buffer(),
|
|
(int)lineText.Length(),
|
|
(int)(items.Count()-1),
|
|
&sc,
|
|
&ss,
|
|
&items[0],
|
|
&scriptItemCount
|
|
);
|
|
if(hr!=0)
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
|
|
items.Resize(scriptItemCount+1);
|
|
for(vint i=0;i<scriptItemCount;i++)
|
|
{
|
|
SCRIPT_ITEM item=items[i];
|
|
Ptr<UniscribeItem> scriptItem=new UniscribeItem;
|
|
scriptItem->startFromLine=item.iCharPos;
|
|
scriptItem->length=items[i+1].iCharPos-item.iCharPos;
|
|
scriptItem->itemText=lineText.Buffer()+item.iCharPos;
|
|
scriptItem->scriptItem=item;
|
|
|
|
if(!scriptItem->BuildUniscribeData())
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
scriptItems.Add(scriptItem);
|
|
}
|
|
}
|
|
{
|
|
// use item and document fragment information to produce runs
|
|
// one item is constructed by one or more runs
|
|
// characters in each run contains the same style
|
|
vint fragmentIndex=0;
|
|
vint fragmentStart=0;
|
|
for(vint i=0;i<scriptItems.Count();i++)
|
|
{
|
|
Ptr<UniscribeItem> scriptItem=scriptItems[i];
|
|
vint currentStart=scriptItem->startFromLine;
|
|
|
|
while(currentStart<scriptItem->startFromLine+scriptItem->length)
|
|
{
|
|
UniscribeFragment* fragment=0;
|
|
vint itemRemainLength=scriptItem->length-(currentStart-scriptItem->startFromLine);
|
|
vint fragmentRemainLength=0;
|
|
while(true)
|
|
{
|
|
fragment=documentFragments[fragmentIndex].Obj();
|
|
fragmentRemainLength=fragment->text.Length()-(currentStart-fragmentStart);
|
|
if(fragmentRemainLength<=0)
|
|
{
|
|
fragmentStart+=fragment->text.Length();
|
|
fragmentIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
vint shortLength=itemRemainLength<fragmentRemainLength?itemRemainLength:fragmentRemainLength;
|
|
bool skip=false;
|
|
{
|
|
vint elementCurrent=0;
|
|
FOREACH(Ptr<UniscribeFragment>, elementFragment, documentFragments)
|
|
{
|
|
vint elementLength=elementFragment->text.Length();
|
|
if(elementFragment->inlineObjectProperties)
|
|
{
|
|
if(elementCurrent<=currentStart && currentStart+shortLength<=elementCurrent+elementLength)
|
|
{
|
|
if(elementCurrent==currentStart)
|
|
{
|
|
auto run=MakePtr<UniscribeEmbeddedObjectRun>();
|
|
run->documentFragment=fragment;
|
|
run->scriptItem=scriptItem.Obj();
|
|
run->startFromLine=currentStart;
|
|
run->startFromFragment=currentStart-fragmentStarts[fragmentIndex];
|
|
run->length=elementLength;
|
|
run->runText=lineText.Buffer()+currentStart;
|
|
run->properties=elementFragment->inlineObjectProperties.Value();
|
|
scriptRuns.Add(run);
|
|
}
|
|
skip=true;
|
|
break;
|
|
}
|
|
}
|
|
elementCurrent+=elementLength;
|
|
}
|
|
}
|
|
if(!skip)
|
|
{
|
|
Ptr<UniscribeTextRun> run=new UniscribeTextRun;
|
|
run->documentFragment=fragment;
|
|
run->scriptItem=scriptItem.Obj();
|
|
run->startFromLine=currentStart;
|
|
run->startFromFragment=currentStart-fragmentStarts[fragmentIndex];
|
|
run->length=shortLength;
|
|
run->runText=lineText.Buffer()+currentStart;
|
|
scriptRuns.Add(run);
|
|
}
|
|
currentStart+=shortLength;
|
|
}
|
|
}
|
|
|
|
// for each run, generate shape information
|
|
vint runIndex=0;
|
|
while(runIndex<scriptRuns.Count())
|
|
{
|
|
Ptr<UniscribeRun> run=scriptRuns[runIndex];
|
|
List<vint> breakings;
|
|
if(!run->BuildUniscribeData(dc, breakings))
|
|
{
|
|
goto BUILD_UNISCRIBE_DATA_FAILED;
|
|
}
|
|
else if(breakings.Count()>1)
|
|
{
|
|
if(Ptr<UniscribeTextRun> textRun=run.Cast<UniscribeTextRun>())
|
|
{
|
|
scriptRuns.RemoveAt(runIndex);
|
|
for(vint i=0;i<breakings.Count();i++)
|
|
{
|
|
vint start=breakings[i];
|
|
vint length=i==breakings.Count()-1?textRun->length-start:breakings[i+1]-start;
|
|
|
|
Ptr<UniscribeTextRun> newRun=new UniscribeTextRun;
|
|
newRun->documentFragment=run->documentFragment;
|
|
newRun->scriptItem=run->scriptItem;
|
|
newRun->startFromLine=start+run->startFromLine;
|
|
newRun->startFromFragment=start+run->startFromFragment;
|
|
newRun->length=length;
|
|
newRun->runText=run->runText+newRun->startFromLine-run->startFromLine;
|
|
scriptRuns.Insert(runIndex+i, newRun);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
runIndex++;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
BUILD_UNISCRIBE_DATA_FAILED:
|
|
ClearUniscribeData();
|
|
return false;
|
|
}
|
|
|
|
void UniscribeLine::Layout(vint availableWidth, Alignment alignment, vint top, vint& totalHeight)
|
|
{
|
|
vint cx=0;
|
|
vint cy=top;
|
|
virtualLines.Clear();
|
|
if(scriptRuns.Count()==0)
|
|
{
|
|
// if this line doesn't contains any run, skip and render a blank line
|
|
vint height=(vint)(documentFragments[0]->fontStyle.size*1.5);
|
|
bounds=Rect(Point(cx, cy), Size(0, height));
|
|
cy+=height;
|
|
}
|
|
else
|
|
{
|
|
FOREACH(Ptr<UniscribeRun>, run, scriptRuns)
|
|
{
|
|
run->fragmentBounds.Clear();
|
|
}
|
|
|
|
// render this line into lines with auto line wrapping
|
|
vint startRun=0;
|
|
vint startRunOffset=0;
|
|
vint lastRun=0;
|
|
vint lastRunOffset=0;
|
|
vint currentWidth=0;
|
|
|
|
while(startRun<scriptRuns.Count())
|
|
{
|
|
vint currentWidth=0;
|
|
bool firstRun=true;
|
|
// search for a range to fit in the given width
|
|
for(vint i=startRun;i<scriptRuns.Count();i++)
|
|
{
|
|
vint charLength=0;
|
|
vint charAdvances=0;
|
|
UniscribeRun* run=scriptRuns[i].Obj();
|
|
run->SearchForLineBreak(lastRunOffset, availableWidth-currentWidth, firstRun, charLength, charAdvances);
|
|
firstRun=false;
|
|
|
|
if(charLength==run->length-lastRunOffset)
|
|
{
|
|
lastRun=i+1;
|
|
lastRunOffset=0;
|
|
currentWidth+=charAdvances;
|
|
}
|
|
else
|
|
{
|
|
lastRun=i;
|
|
lastRunOffset=lastRunOffset+charLength;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the range is empty, than this should be the end of line, ignore it
|
|
if(startRun<lastRun || (startRun==lastRun && startRunOffset<lastRunOffset))
|
|
{
|
|
// calculate the max line height in this range;
|
|
vint availableLastRun=lastRun<scriptRuns.Count()-1?lastRun:scriptRuns.Count()-1;
|
|
vint maxHeight=0;
|
|
vint maxTextHeight=0;
|
|
for(vint i=startRun;i<=availableLastRun;i++)
|
|
{
|
|
if(i==lastRun && lastRunOffset==0)
|
|
{
|
|
break;
|
|
}
|
|
{
|
|
vint size=scriptRuns[i]->SumHeight();
|
|
if(maxHeight<size) maxHeight=size;
|
|
}
|
|
{
|
|
vint size=scriptRuns[i]->SumTextHeight();
|
|
if(maxTextHeight<size) maxTextHeight=size;
|
|
}
|
|
}
|
|
|
|
// determine the rendering order for all runs inside this range
|
|
Array<BYTE> levels(availableLastRun-startRun+1);
|
|
Array<int> runVisualToLogical(levels.Count());
|
|
Array<int> runLogicalToVisual(levels.Count());
|
|
for(vint i=startRun;i<=availableLastRun;i++)
|
|
{
|
|
levels[i-startRun]=scriptRuns[i]->scriptItem->scriptItem.a.s.uBidiLevel;
|
|
}
|
|
ScriptLayout((int)levels.Count(), &levels[0], &runVisualToLogical[0], &runLogicalToVisual[0]);
|
|
|
|
// render all runs inside this range
|
|
vint startRunFragmentCount=-1;
|
|
for(vint i=startRun;i<=availableLastRun;i++)
|
|
{
|
|
vint runIndex=runVisualToLogical[i-startRun]+startRun;
|
|
UniscribeRun* run=scriptRuns[runIndex].Obj();
|
|
vint start=runIndex==startRun?startRunOffset:0;
|
|
vint end=runIndex==lastRun?lastRunOffset:run->length;
|
|
vint length=end-start;
|
|
|
|
if(runIndex==startRun)
|
|
{
|
|
startRunFragmentCount=run->fragmentBounds.Count();
|
|
}
|
|
|
|
UniscribeRun::RunFragmentBounds fragmentBounds;
|
|
fragmentBounds.startFromRun=start;
|
|
fragmentBounds.length=length;
|
|
fragmentBounds.bounds=Rect(
|
|
Point(cx, cy+maxHeight-run->SumHeight()),
|
|
Size(run->SumWidth(start, length), run->SumHeight())
|
|
);
|
|
run->fragmentBounds.Add(fragmentBounds);
|
|
|
|
cx+=run->SumWidth(start, length);
|
|
}
|
|
|
|
// adjust alignment
|
|
vint cxOffset=0;
|
|
switch(alignment)
|
|
{
|
|
case Alignment::Center:
|
|
cxOffset=(availableWidth-cx)/2;
|
|
break;
|
|
case Alignment::Right:
|
|
cxOffset=availableWidth-cx;
|
|
break;
|
|
}
|
|
|
|
// shift all bounds using alignment
|
|
if(cxOffset!=0)
|
|
{
|
|
for(vint i=startRun;i<=availableLastRun;i++)
|
|
{
|
|
UniscribeRun* run=scriptRuns[i].Obj();
|
|
for(vint j=(i==startRun?startRunFragmentCount:0);j<run->fragmentBounds.Count();j++)
|
|
{
|
|
UniscribeRun::RunFragmentBounds& fragmentBounds=run->fragmentBounds[j];
|
|
fragmentBounds.bounds.x1+=cxOffset;
|
|
fragmentBounds.bounds.x2+=cxOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
// create a virtual line
|
|
{
|
|
Ptr<UniscribeVirtualLine> virtualLine=new UniscribeVirtualLine;
|
|
virtualLine->firstRunIndex=startRun;
|
|
virtualLine->firstRunBoundsIndex=startRunFragmentCount;
|
|
virtualLine->lastRunIndex=availableLastRun;
|
|
virtualLine->lastRunBoundsIndex=scriptRuns[availableLastRun]->fragmentBounds.Count()-1;
|
|
|
|
UniscribeRun* firstRun=scriptRuns[virtualLine->firstRunIndex].Obj();
|
|
UniscribeRun* lastRun=scriptRuns[virtualLine->lastRunIndex].Obj();
|
|
UniscribeRun::RunFragmentBounds& firstBounds=firstRun->fragmentBounds[virtualLine->firstRunBoundsIndex];
|
|
UniscribeRun::RunFragmentBounds& lastBounds=lastRun->fragmentBounds[virtualLine->lastRunBoundsIndex];
|
|
|
|
virtualLine->startFromLine=firstRun->startFromLine+firstBounds.startFromRun;
|
|
virtualLine->length=lastRun->startFromLine+lastBounds.startFromRun+lastBounds.length-virtualLine->startFromLine;
|
|
virtualLine->runText=lineText.Buffer()+virtualLine->startFromLine;
|
|
|
|
bool updateBounds=false;
|
|
for(vint i=startRun;i<=availableLastRun;i++)
|
|
{
|
|
UniscribeRun* run=scriptRuns[i].Obj();
|
|
for(vint j=(i==startRun?startRunFragmentCount:0);j<run->fragmentBounds.Count();j++)
|
|
{
|
|
UniscribeRun::RunFragmentBounds& fragmentBounds=run->fragmentBounds[j];
|
|
if(updateBounds)
|
|
{
|
|
if(virtualLine->bounds.x1>fragmentBounds.bounds.x1) virtualLine->bounds.x1=fragmentBounds.bounds.x1;
|
|
if(virtualLine->bounds.x2<fragmentBounds.bounds.x2) virtualLine->bounds.x2=fragmentBounds.bounds.x2;
|
|
if(virtualLine->bounds.y1>fragmentBounds.bounds.y1) virtualLine->bounds.y1=fragmentBounds.bounds.y1;
|
|
if(virtualLine->bounds.y2<fragmentBounds.bounds.y2) virtualLine->bounds.y2=fragmentBounds.bounds.y2;
|
|
}
|
|
else
|
|
{
|
|
virtualLine->bounds=fragmentBounds.bounds;
|
|
updateBounds=true;
|
|
}
|
|
}
|
|
}
|
|
virtualLines.Add(virtualLine);
|
|
}
|
|
|
|
cx=0;
|
|
cy+=(vint)(maxHeight + maxTextHeight*0.5);
|
|
}
|
|
|
|
startRun=lastRun;
|
|
startRunOffset=lastRunOffset;
|
|
}
|
|
|
|
// calculate line bounds
|
|
vint minX=0;
|
|
vint minY=top;
|
|
vint maxX=0;
|
|
vint maxY=top;
|
|
FOREACH(Ptr<UniscribeRun>, run, scriptRuns)
|
|
{
|
|
FOREACH(UniscribeRun::RunFragmentBounds, fragmentBounds, run->fragmentBounds)
|
|
{
|
|
Rect bounds=fragmentBounds.bounds;
|
|
if(minX>bounds.Left()) minX=bounds.Left();
|
|
if(minY>bounds.Top()) minX=bounds.Top();
|
|
if(maxX<bounds.Right()) maxX=bounds.Right();
|
|
if(maxY<bounds.Bottom()) maxY=bounds.Bottom();
|
|
}
|
|
}
|
|
bounds=Rect(minX, minY, maxX, maxY);
|
|
}
|
|
totalHeight=cy;
|
|
}
|
|
|
|
void UniscribeLine::Render(UniscribeRun::IRendererCallback* callback, vint offsetX, vint offsetY, bool renderBackground)
|
|
{
|
|
FOREACH(Ptr<UniscribeRun>, run, scriptRuns)
|
|
{
|
|
for(vint i=0;i<run->fragmentBounds.Count();i++)
|
|
{
|
|
run->Render(callback, i, offsetX, offsetY, renderBackground);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph
|
|
***********************************************************************/
|
|
|
|
UniscribeParagraph::UniscribeParagraph()
|
|
:lastAvailableWidth(-1)
|
|
,paragraphAlignment(Alignment::Left)
|
|
,built(false)
|
|
{
|
|
}
|
|
|
|
UniscribeParagraph::~UniscribeParagraph()
|
|
{
|
|
ClearUniscribeData();
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph (Initialization)
|
|
***********************************************************************/
|
|
|
|
void UniscribeParagraph::ClearUniscribeData()
|
|
{
|
|
FOREACH(Ptr<UniscribeFragment>, fragment, documentFragments)
|
|
{
|
|
GetWindowsGDIResourceManager()->DestroyGdiFont(fragment->fontStyle);
|
|
fragment->fontObject=0;
|
|
}
|
|
lines.Clear();
|
|
lastAvailableWidth=-1;
|
|
}
|
|
|
|
bool UniscribeParagraph::BuildUniscribeData(WinDC* dc)
|
|
{
|
|
if(built) return false;
|
|
|
|
built=true;
|
|
ClearUniscribeData();
|
|
Dictionary<WString, Ptr<WinFont>> fonts;
|
|
FOREACH(Ptr<UniscribeFragment>, fragment, documentFragments)
|
|
{
|
|
if(!fragment->fontObject)
|
|
{
|
|
WString fragmentFingerPrint=fragment->GetFingerprint();
|
|
vint index=fonts.Keys().IndexOf(fragmentFingerPrint);
|
|
if(index==-1)
|
|
{
|
|
fragment->fontObject=GetWindowsGDIResourceManager()->CreateGdiFont(fragment->fontStyle);
|
|
fonts.Add(fragmentFingerPrint, fragment->fontObject);
|
|
}
|
|
else
|
|
{
|
|
fragment->fontObject=fonts.Values().Get(index);
|
|
}
|
|
}
|
|
}
|
|
{
|
|
Regex regexLine(L"\r\n");
|
|
Ptr<UniscribeLine> line;
|
|
FOREACH(Ptr<UniscribeFragment>, fragment, documentFragments)
|
|
{
|
|
if(fragment->inlineObjectProperties)
|
|
{
|
|
if(!line)
|
|
{
|
|
line=new UniscribeLine;
|
|
lines.Add(line);
|
|
}
|
|
line->documentFragments.Add(fragment);
|
|
}
|
|
else
|
|
{
|
|
RegexMatch::List textLines;
|
|
regexLine.Split(fragment->text, true, textLines);
|
|
|
|
for(vint i=0;i<textLines.Count();i++)
|
|
{
|
|
RegexString rs=textLines[i]->Result();
|
|
WString text=rs.Value();
|
|
if(i>0)
|
|
{
|
|
line=0;
|
|
}
|
|
if(!line)
|
|
{
|
|
line=new UniscribeLine;
|
|
lines.Add(line);
|
|
}
|
|
|
|
if(textLines.Count()==1)
|
|
{
|
|
line->documentFragments.Add(fragment);
|
|
}
|
|
else
|
|
{
|
|
Ptr<UniscribeFragment> runFragment=fragment->Copy(rs.Start(), rs.Length());
|
|
line->documentFragments.Add(runFragment);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
line->BuildUniscribeData(dc);
|
|
}
|
|
|
|
vint lineStart=0;
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
line->startFromParagraph=lineStart;
|
|
lineStart+=line->lineText.Length()+2;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void UniscribeParagraph::Layout(vint availableWidth, Alignment alignment)
|
|
{
|
|
if(lastAvailableWidth==availableWidth && paragraphAlignment==alignment)
|
|
{
|
|
return;
|
|
}
|
|
lastAvailableWidth=availableWidth;
|
|
paragraphAlignment=alignment;
|
|
|
|
vint cy=0;
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
line->Layout(availableWidth, alignment, cy, cy);
|
|
}
|
|
|
|
// calculate paragraph bounds
|
|
vint minX=0;
|
|
vint minY=0;
|
|
vint maxX=0;
|
|
vint maxY=0;
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
Rect bounds=line->bounds;
|
|
if(minX>bounds.Left()) minX=bounds.Left();
|
|
if(minY>bounds.Top()) minX=bounds.Top();
|
|
if(maxX<bounds.Right()) maxX=bounds.Right();
|
|
if(maxY<bounds.Bottom()) maxY=bounds.Bottom();
|
|
}
|
|
|
|
vint offsetY=0;
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
FOREACH(Ptr<UniscribeFragment>, fragment, line->documentFragments)
|
|
{
|
|
vint size=fragment->fontStyle.size/3;
|
|
if(size>offsetY)
|
|
{
|
|
offsetY=size;
|
|
}
|
|
}
|
|
}
|
|
bounds=Rect(minX, minY, maxX, maxY+offsetY);
|
|
}
|
|
|
|
void UniscribeParagraph::Render(UniscribeRun::IRendererCallback* callback, bool renderBackground)
|
|
{
|
|
auto offset = callback->GetParagraphOffset();
|
|
FOREACH(Ptr<UniscribeLine>, line, lines)
|
|
{
|
|
line->Render(callback, offset.x, offset.y, renderBackground);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph (Formatting Helper)
|
|
***********************************************************************/
|
|
|
|
void UniscribeParagraph::SearchFragment(vint start, vint length, vint& fs, vint& ss, vint& fe, vint& se)
|
|
{
|
|
fs=-1;
|
|
ss=-1;
|
|
fe=-1;
|
|
se=-1;
|
|
vint current=0;
|
|
for(vint i=0;i<documentFragments.Count();i++)
|
|
{
|
|
vint fragmentLength=documentFragments[i]->text.Length();
|
|
if(current<=start && start<current+fragmentLength)
|
|
{
|
|
fs=i;
|
|
ss=start-current;
|
|
}
|
|
if(current<=start+length && start+length<=current+fragmentLength)
|
|
{
|
|
fe=i;
|
|
se=start+length-current;
|
|
}
|
|
if(fs!=-1 && fe!=-1)
|
|
{
|
|
break;
|
|
}
|
|
current+=fragmentLength;
|
|
}
|
|
}
|
|
|
|
bool UniscribeParagraph::CutFragment(vint fs, vint ss, vint fe, vint se, vint& f1, vint& f2)
|
|
{
|
|
f1=-1;
|
|
f2=-1;
|
|
if(fs==fe)
|
|
{
|
|
Ptr<UniscribeFragment> fragment=documentFragments[fs];
|
|
if(fragment->inlineObjectProperties)
|
|
{
|
|
if(ss==0 && se==fragment->text.Length())
|
|
{
|
|
f1=f2=fs;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
for(vint i=fs;i<=fe;i++)
|
|
{
|
|
if(documentFragments[i]->inlineObjectProperties)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
if(fs==fe)
|
|
{
|
|
Ptr<UniscribeFragment> fragment=documentFragments[fs];
|
|
vint length=fragment->text.Length();
|
|
if(ss==0)
|
|
{
|
|
if(se==length)
|
|
{
|
|
f1=f2=fs;
|
|
}
|
|
else
|
|
{
|
|
f1=f2=fs;
|
|
Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, se);
|
|
Ptr<UniscribeFragment> rightFragment=fragment->Copy(se, length-se);
|
|
documentFragments.RemoveAt(fs);
|
|
documentFragments.Insert(fs, leftFragment);
|
|
documentFragments.Insert(fs+1, rightFragment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(se==length)
|
|
{
|
|
f1=fs+1;
|
|
f2=fs+1;
|
|
Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, ss);
|
|
Ptr<UniscribeFragment> rightFragment=fragment->Copy(ss, length-ss);
|
|
documentFragments.RemoveAt(fs);
|
|
documentFragments.Insert(fs, leftFragment);
|
|
documentFragments.Insert(fs+1, rightFragment);
|
|
}
|
|
else
|
|
{
|
|
f1=fs+1;
|
|
f2=fs+1;
|
|
Ptr<UniscribeFragment> leftFragment=fragment->Copy(0, ss);
|
|
Ptr<UniscribeFragment> middleFragment=fragment->Copy(ss, se-ss);
|
|
Ptr<UniscribeFragment> rightFragment=fragment->Copy(se, length-se);
|
|
documentFragments.RemoveAt(fs);
|
|
documentFragments.Insert(fs, leftFragment);
|
|
documentFragments.Insert(fs+1, middleFragment);
|
|
documentFragments.Insert(fs+2, rightFragment);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ptr<UniscribeFragment> fragmentStart=documentFragments[fs];
|
|
Ptr<UniscribeFragment> fragmentEnd=documentFragments[fe];
|
|
if(ss==0)
|
|
{
|
|
f1=fs;
|
|
}
|
|
else
|
|
{
|
|
f1=fs+1;
|
|
fe++;
|
|
vint length=fragmentStart->text.Length();
|
|
Ptr<UniscribeFragment> leftFragment=fragmentStart->Copy(0, ss);
|
|
Ptr<UniscribeFragment> rightFragment=fragmentStart->Copy(ss, length-ss);
|
|
documentFragments.RemoveAt(fs);
|
|
documentFragments.Insert(fs, leftFragment);
|
|
documentFragments.Insert(fs+1, rightFragment);
|
|
}
|
|
if(se==fragmentEnd->text.Length())
|
|
{
|
|
f2=fe;
|
|
}
|
|
else
|
|
{
|
|
f2=fe;
|
|
fe++;
|
|
vint length=fragmentEnd->text.Length();
|
|
Ptr<UniscribeFragment> leftFragment=fragmentStart->Copy(0, se);
|
|
Ptr<UniscribeFragment> rightFragment=fragmentEnd->Copy(se, length-se);
|
|
documentFragments.RemoveAt(fe);
|
|
documentFragments.Insert(fe, leftFragment);
|
|
documentFragments.Insert(fe+1, rightFragment);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void UniscribeParagraph::CutFragmentColors(vint fs, vint ss, vint fe, vint se, Color UniscribeColor::* colorField, Color color)
|
|
{
|
|
for(vint f=fs;f<=fe;f++)
|
|
{
|
|
Ptr<UniscribeFragment> fragment=documentFragments[f];
|
|
vint start=f==fs?ss:0;
|
|
vint end=f==fe?se:fragment->text.Length();
|
|
|
|
fragment->CutColors(start, end-start);
|
|
fragment->UpdateOverlappedColors(start, end-start, colorField, color);
|
|
fragment->DefragmentColors();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph (Formatting)
|
|
***********************************************************************/
|
|
|
|
bool UniscribeParagraph::SetFont(vint start, vint length, const WString& value)
|
|
{
|
|
vint fs, ss, fe, se, f1, f2;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(CutFragment(fs, ss, fe, se, f1, f2))
|
|
{
|
|
for(vint i=f1;i<=f2;i++)
|
|
{
|
|
documentFragments[i]->fontStyle.fontFamily=value;
|
|
}
|
|
built=false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool UniscribeParagraph::SetSize(vint start, vint length, vint value)
|
|
{
|
|
vint fs, ss, fe, se, f1, f2;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(CutFragment(fs, ss, fe, se, f1, f2))
|
|
{
|
|
for(vint i=f1;i<=f2;i++)
|
|
{
|
|
documentFragments[i]->fontStyle.size=value;
|
|
}
|
|
built=false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool UniscribeParagraph::SetStyle(vint start, vint length, bool bold, bool italic, bool underline, bool strikeline)
|
|
{
|
|
vint fs, ss, fe, se, f1, f2;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(CutFragment(fs, ss, fe, se, f1, f2))
|
|
{
|
|
for(vint i=f1;i<=f2;i++)
|
|
{
|
|
documentFragments[i]->fontStyle.bold=bold;
|
|
documentFragments[i]->fontStyle.italic=italic;
|
|
documentFragments[i]->fontStyle.underline=underline;
|
|
documentFragments[i]->fontStyle.strikeline=strikeline;
|
|
}
|
|
built=false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool UniscribeParagraph::SetColor(vint start, vint length, Color value)
|
|
{
|
|
vint fs, ss, fe, se;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false;
|
|
|
|
CutFragmentColors(fs, ss, fe, se, &UniscribeColor::fontColor, value);
|
|
return true;
|
|
}
|
|
|
|
bool UniscribeParagraph::SetBackgroundColor(vint start, vint length, Color value)
|
|
{
|
|
vint fs, ss, fe, se;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false;
|
|
|
|
CutFragmentColors(fs, ss, fe, se, &UniscribeColor::backgroundColor, value);
|
|
return true;
|
|
}
|
|
|
|
bool UniscribeParagraph::SetInlineObject(vint start, vint length, const IGuiGraphicsParagraph::InlineObjectProperties& properties)
|
|
{
|
|
vint fs, ss, fe, se, f1, f2;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
if(CutFragment(fs, ss, fe, se, f1, f2))
|
|
{
|
|
WString text;
|
|
for(vint i=f1;i<=f2;i++)
|
|
{
|
|
text+=documentFragments[f1]->text;
|
|
}
|
|
Ptr<UniscribeFragment> elementFragment=new UniscribeFragment(text);
|
|
|
|
for(vint i=f1;i<=f2;i++)
|
|
{
|
|
elementFragment->cachedTextFragment.Add(documentFragments[f1]);
|
|
documentFragments.RemoveAt(f1);
|
|
}
|
|
elementFragment->inlineObjectProperties=properties;
|
|
documentFragments.Insert(f1, elementFragment);
|
|
built=false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
InlineObject UniscribeParagraph::ResetInlineObject(vint start, vint length)
|
|
{
|
|
vint fs, ss, fe, se;
|
|
SearchFragment(start, length, fs, ss, fe, se);
|
|
Ptr<UniscribeFragment> fragment = documentFragments[fs];
|
|
if(fs==fe && ss==0 && se==fragment->text.Length() && fragment->inlineObjectProperties)
|
|
{
|
|
documentFragments.RemoveAt(fs);
|
|
for(vint i=0;i<fragment->cachedTextFragment.Count();i++)
|
|
{
|
|
documentFragments.Insert(fs+i, fragment->cachedTextFragment[i]);
|
|
}
|
|
built=false;
|
|
return fragment->inlineObjectProperties;
|
|
}
|
|
return InlineObject();
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph (Caret Helper)
|
|
***********************************************************************/
|
|
|
|
void UniscribeParagraph::GetLineIndexFromTextPos(vint textPos, vint& frontLine, vint& backLine)
|
|
{
|
|
frontLine=-1;
|
|
backLine=-1;
|
|
if(!IsValidTextPos(textPos)) return;
|
|
|
|
vint start=0;
|
|
vint end=lines.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
Ptr<UniscribeLine> line=lines[middle];
|
|
vint lineStart=line->startFromParagraph;
|
|
vint lineEnd=line->startFromParagraph+line->lineText.Length();
|
|
if(textPos<lineStart)
|
|
{
|
|
if(textPos==lineStart-1)
|
|
{
|
|
frontLine=middle-1;
|
|
backLine=middle;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
end=middle-1;
|
|
}
|
|
}
|
|
else if(textPos>lineEnd)
|
|
{
|
|
if(textPos==lineEnd+1)
|
|
{
|
|
frontLine=middle;
|
|
backLine=middle+1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
start=middle+1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
frontLine=middle;
|
|
backLine=middle;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UniscribeParagraph::GetVirtualLineIndexFromTextPos(vint textPos, vint lineIndex, vint& frontLine, vint& backLine)
|
|
{
|
|
frontLine=-1;
|
|
backLine=-1;
|
|
if(!IsValidTextPos(textPos)) return;
|
|
if(lineIndex<0 || lineIndex>=lines.Count()) return;
|
|
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
vint start=0;
|
|
vint end=line->virtualLines.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
Ptr<UniscribeVirtualLine> vline=line->virtualLines[middle];
|
|
vint lineStart=line->startFromParagraph+vline->startFromLine;
|
|
vint lineEnd=line->startFromParagraph+vline->startFromLine+vline->length;
|
|
if(textPos<lineStart)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(textPos>lineEnd)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else if(textPos==lineStart)
|
|
{
|
|
frontLine=middle==0?0:middle-1;
|
|
backLine=middle;
|
|
return;
|
|
}
|
|
else if(textPos==lineEnd)
|
|
{
|
|
frontLine=middle;
|
|
backLine=middle==line->virtualLines.Count()-1?middle:middle+1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
frontLine=middle;
|
|
backLine=middle;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UniscribeParagraph::GetItemIndexFromTextPos(vint textPos, vint lineIndex, vint& frontItem, vint& backItem)
|
|
{
|
|
frontItem=-1;
|
|
backItem=-1;
|
|
if(!IsValidTextPos(textPos)) return;
|
|
if(lineIndex<0 || lineIndex>=lines.Count()) return;
|
|
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
vint start=0;
|
|
vint end=line->scriptItems.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
Ptr<UniscribeItem> item=line->scriptItems[middle];
|
|
vint lineStart=line->startFromParagraph+item->startFromLine;
|
|
vint lineEnd=line->startFromParagraph+item->startFromLine+item->length;
|
|
if(textPos<lineStart)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(textPos>lineEnd)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else if(textPos==lineStart)
|
|
{
|
|
frontItem=middle==0?0:middle-1;
|
|
backItem=middle;
|
|
return;
|
|
}
|
|
else if(textPos==lineEnd)
|
|
{
|
|
frontItem=middle;
|
|
backItem=middle==line->scriptItems.Count()-1?middle:middle+1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
frontItem=middle;
|
|
backItem=middle;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Rect UniscribeParagraph::GetCaretBoundsWithLine(vint caret, vint lineIndex, vint virtualLineIndex, bool frontSide)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(line->startFromParagraph<=caret && caret<=line->startFromParagraph+line->lineText.Length())
|
|
{
|
|
if(line->lineText==L"") return line->bounds;
|
|
Ptr<UniscribeVirtualLine> virtualLine=line->virtualLines[virtualLineIndex];
|
|
|
|
for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++)
|
|
{
|
|
Ptr<UniscribeRun> run=line->scriptRuns[i];
|
|
if(Ptr<UniscribeTextRun> textRun=run.Cast<UniscribeTextRun>())
|
|
{
|
|
vint firstBounds=i==virtualLine->firstRunIndex?virtualLine->firstRunBoundsIndex:0;
|
|
vint lastBounds=i==virtualLine->lastRunIndex?virtualLine->lastRunBoundsIndex:run->fragmentBounds.Count()-1;
|
|
|
|
for(vint j=firstBounds;j<=lastBounds;j++)
|
|
{
|
|
UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[j];
|
|
vint boundsStart=line->startFromParagraph+run->startFromLine+bounds.startFromRun;
|
|
if(boundsStart==caret)
|
|
{
|
|
if(!frontSide || i==virtualLine->firstRunIndex && j==virtualLine->firstRunBoundsIndex)
|
|
{
|
|
if(run->scriptItem->scriptItem.a.fRTL)
|
|
{
|
|
return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2);
|
|
}
|
|
else
|
|
{
|
|
return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2);
|
|
}
|
|
}
|
|
}
|
|
else if(caret==boundsStart+bounds.length)
|
|
{
|
|
if(frontSide || i==virtualLine->lastRunIndex && j==virtualLine->lastRunBoundsIndex)
|
|
{
|
|
if(run->scriptItem->scriptItem.a.fRTL)
|
|
{
|
|
return Rect(bounds.bounds.x1, bounds.bounds.y1, bounds.bounds.x1, bounds.bounds.y2);
|
|
}
|
|
else
|
|
{
|
|
return Rect(bounds.bounds.x2, bounds.bounds.y1, bounds.bounds.x2, bounds.bounds.y2);
|
|
}
|
|
}
|
|
}
|
|
else if(boundsStart<caret && caret<boundsStart+bounds.length)
|
|
{
|
|
vint accumulatedWidth=0;
|
|
vint lastRunChar=bounds.startFromRun;
|
|
for(vint i=0;i<=bounds.length;i++)
|
|
{
|
|
vint charIndex=bounds.startFromRun+i;
|
|
vint newLastRunChar=lastRunChar;
|
|
if(i>0)
|
|
{
|
|
if(i==bounds.length)
|
|
{
|
|
newLastRunChar=charIndex;
|
|
}
|
|
else
|
|
{
|
|
WORD cluster1=textRun->wholeGlyph.charCluster[charIndex-1];
|
|
WORD cluster2=textRun->wholeGlyph.charCluster[charIndex];
|
|
if(cluster1!=cluster2)
|
|
{
|
|
newLastRunChar=charIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(newLastRunChar!=lastRunChar)
|
|
{
|
|
WORD glyph1=0;
|
|
WORD glyph2=0;
|
|
if(run->scriptItem->scriptItem.a.fRTL)
|
|
{
|
|
glyph2=textRun->wholeGlyph.charCluster[lastRunChar]+1;
|
|
glyph1=newLastRunChar==run->length?0:textRun->wholeGlyph.charCluster[newLastRunChar]+1;
|
|
}
|
|
else
|
|
{
|
|
glyph1=textRun->wholeGlyph.charCluster[lastRunChar];
|
|
glyph2=newLastRunChar==run->length?(WORD)textRun->wholeGlyph.glyphs.Count():textRun->wholeGlyph.charCluster[newLastRunChar];
|
|
}
|
|
|
|
vint glyphWidth=0;
|
|
for(WORD g=glyph1;g<glyph2;g++)
|
|
{
|
|
glyphWidth+=textRun->wholeGlyph.glyphAdvances[g];
|
|
}
|
|
accumulatedWidth+=glyphWidth;
|
|
lastRunChar=newLastRunChar;
|
|
|
|
if(line->startFromParagraph+run->startFromLine+lastRunChar==caret)
|
|
{
|
|
vint x=0;
|
|
if(run->scriptItem->scriptItem.a.fRTL)
|
|
{
|
|
x=bounds.bounds.x2-accumulatedWidth;
|
|
}
|
|
else
|
|
{
|
|
x=bounds.bounds.x1+accumulatedWidth;
|
|
}
|
|
return Rect(x, bounds.bounds.y1, x, bounds.bounds.y2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Rect();
|
|
}
|
|
|
|
vint UniscribeParagraph::GetCaretFromXWithTextRunBounds(vint x, vint lineIndex, vint runIndex, vint runBoundsIndex)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(line->lineText==L"") return line->startFromParagraph;
|
|
Ptr<UniscribeTextRun> run=line->scriptRuns[runIndex].Cast<UniscribeTextRun>();
|
|
UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[runBoundsIndex];
|
|
|
|
vint startFromFragmentBounds=0;
|
|
vint accumulatedWidth=0;
|
|
while(startFromFragmentBounds<bounds.length)
|
|
{
|
|
vint charIndex=bounds.startFromRun+startFromFragmentBounds;
|
|
vint charLength=0;
|
|
vint cluster=0;
|
|
vint nextCluster=0;
|
|
run->SearchSingleChar(charIndex, charLength, cluster, nextCluster);
|
|
|
|
vint clusterStart=0;
|
|
vint clusterCount=0;
|
|
if(run->scriptItem->IsRightToLeft())
|
|
{
|
|
clusterStart=nextCluster+1;
|
|
clusterCount=cluster-nextCluster;
|
|
}
|
|
else
|
|
{
|
|
clusterStart=cluster;
|
|
clusterCount=nextCluster-cluster;
|
|
}
|
|
|
|
vint clusterWidth=0;
|
|
for(vint i=0;i<clusterCount;i++)
|
|
{
|
|
clusterWidth+=run->wholeGlyph.glyphAdvances[i+clusterStart];
|
|
}
|
|
|
|
if(run->scriptItem->scriptItem.a.fRTL)
|
|
{
|
|
vint x2=bounds.bounds.x2-accumulatedWidth;
|
|
vint x1=x2-clusterWidth;
|
|
if(x1<=x && x<x2)
|
|
{
|
|
vint d1=x-x1;
|
|
vint d2=x2-x;
|
|
if(d1<=d2)
|
|
{
|
|
return line->startFromParagraph+run->startFromLine+charIndex+charLength;
|
|
}
|
|
else
|
|
{
|
|
return line->startFromParagraph+run->startFromLine+charIndex;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vint x1=bounds.bounds.x1+accumulatedWidth;
|
|
vint x2=x1+clusterWidth;
|
|
if(x1<=x && x<x2)
|
|
{
|
|
vint d1=x-x1;
|
|
vint d2=x2-x;
|
|
if(d1<=d2)
|
|
{
|
|
return line->startFromParagraph+run->startFromLine+charIndex;
|
|
}
|
|
else
|
|
{
|
|
return line->startFromParagraph+run->startFromLine+charIndex+charLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
startFromFragmentBounds+=charLength;
|
|
accumulatedWidth+=clusterWidth;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
vint UniscribeParagraph::GetCaretFromXWithLine(vint x, vint lineIndex, vint virtualLineIndex)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(line->virtualLines.Count()==0) return line->startFromParagraph;
|
|
Ptr<UniscribeVirtualLine> virtualLine=line->virtualLines[virtualLineIndex];
|
|
if(x<virtualLine->bounds.x1) return line->startFromParagraph+virtualLine->startFromLine;
|
|
if(x>=virtualLine->bounds.x2) return line->startFromParagraph+virtualLine->startFromLine+virtualLine->length;
|
|
|
|
for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++)
|
|
{
|
|
Ptr<UniscribeRun> run=line->scriptRuns[i];
|
|
if(Ptr<UniscribeTextRun> textRun=run.Cast<UniscribeTextRun>())
|
|
{
|
|
vint firstBounds=i==virtualLine->firstRunIndex?virtualLine->firstRunBoundsIndex:0;
|
|
vint lastBounds=i==virtualLine->lastRunIndex?virtualLine->lastRunBoundsIndex:run->fragmentBounds.Count()-1;
|
|
|
|
for(vint j=firstBounds;j<=lastBounds;j++)
|
|
{
|
|
UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[j];
|
|
if(bounds.bounds.x1<=x && x<bounds.bounds.x2)
|
|
{
|
|
return GetCaretFromXWithTextRunBounds(x, lineIndex, i, j);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Rect bounds=run->fragmentBounds[0].bounds;
|
|
if(bounds.x1<=x && x<bounds.x2)
|
|
{
|
|
vint d1=x-bounds.x1;
|
|
vint d2=bounds.x2-x;
|
|
if(d1<=d2)
|
|
{
|
|
return line->startFromParagraph+run->startFromLine;
|
|
}
|
|
else
|
|
{
|
|
return line->startFromParagraph+run->startFromLine+run->length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
InlineObject UniscribeParagraph::GetInlineObjectFromXWithLine(vint x, vint lineIndex, vint virtualLineIndex, vint& start, vint& length)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(line->virtualLines.Count()==0) return InlineObject();
|
|
Ptr<UniscribeVirtualLine> virtualLine=line->virtualLines[virtualLineIndex];
|
|
if(x<virtualLine->bounds.x1) return InlineObject();
|
|
if(x>=virtualLine->bounds.x2) return InlineObject();
|
|
|
|
for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++)
|
|
{
|
|
Ptr<UniscribeRun> run=line->scriptRuns[i];
|
|
if(auto elementRun=run.Cast<UniscribeEmbeddedObjectRun>())
|
|
{
|
|
Rect bounds=run->fragmentBounds[0].bounds;
|
|
if(bounds.x1<=x && x<bounds.x2)
|
|
{
|
|
start=line->startFromParagraph+elementRun->startFromLine;
|
|
length=elementRun->length;
|
|
return elementRun->properties;
|
|
}
|
|
}
|
|
}
|
|
return InlineObject();
|
|
}
|
|
|
|
vint UniscribeParagraph::GetLineY(vint lineIndex)
|
|
{
|
|
if(lineIndex==lines.Count())
|
|
{
|
|
return bounds.Height();
|
|
}
|
|
else
|
|
{
|
|
return lines[lineIndex]->bounds.y1;
|
|
}
|
|
}
|
|
|
|
vint UniscribeParagraph::GetVirtualLineY(vint lineIndex, vint virtualLineIndex)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(virtualLineIndex==line->virtualLines.Count())
|
|
{
|
|
return GetLineY(lineIndex+1);
|
|
}
|
|
else
|
|
{
|
|
return line->virtualLines[virtualLineIndex]->bounds.y1;
|
|
}
|
|
}
|
|
|
|
vint UniscribeParagraph::GetLineIndexFromY(vint y)
|
|
{
|
|
if(y<lines[0]->bounds.y1) return 0;
|
|
if(y>=lines[lines.Count()-1]->bounds.y2) return lines.Count()-1;
|
|
|
|
vint start=0;
|
|
vint end=lines.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
vint minY=GetLineY(middle);
|
|
vint maxY=GetLineY(middle+1);
|
|
if(y<minY)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(y>=maxY)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else
|
|
{
|
|
return middle;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
vint UniscribeParagraph::GetVirtualLineIndexFromY(vint y, vint lineIndex)
|
|
{
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(y<line->bounds.y1) return 0;
|
|
if(y>=line->bounds.y2) return line->virtualLines.Count()-1;
|
|
|
|
vint start=0;
|
|
vint end=line->virtualLines.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
vint minY=GetVirtualLineY(lineIndex, middle);
|
|
vint maxY=GetVirtualLineY(lineIndex, middle+1);
|
|
if(y<minY)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(y>=maxY)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else
|
|
{
|
|
return middle;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
UniscribeParagraph (Caret)
|
|
***********************************************************************/
|
|
|
|
vint UniscribeParagraph::GetCaret(vint comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide)
|
|
{
|
|
if(position==IGuiGraphicsParagraph::CaretFirst) return 0;
|
|
if(position==IGuiGraphicsParagraph::CaretLast) return paragraphText.Length();
|
|
if(!IsValidCaret(comparingCaret)) return -1;
|
|
|
|
if(position==IGuiGraphicsParagraph::CaretMoveLeft)
|
|
{
|
|
return comparingCaret==0?0:GetNearestCaretFromTextPos(comparingCaret-1, true);
|
|
}
|
|
if(position==IGuiGraphicsParagraph::CaretMoveRight)
|
|
{
|
|
return comparingCaret==paragraphText.Length()?paragraphText.Length():GetNearestCaretFromTextPos(comparingCaret+1, false);
|
|
}
|
|
|
|
vint frontLine=0;
|
|
vint backLine=0;
|
|
GetLineIndexFromTextPos(comparingCaret, frontLine, backLine);
|
|
|
|
Ptr<UniscribeLine> line=lines[frontLine];
|
|
if(line->virtualLines.Count()==0)
|
|
{
|
|
switch(position)
|
|
{
|
|
case IGuiGraphicsParagraph::CaretLineFirst:
|
|
case IGuiGraphicsParagraph::CaretLineLast:
|
|
return line->startFromParagraph;
|
|
}
|
|
}
|
|
|
|
vint frontVirtualLine=0;
|
|
vint backVirtualLine=0;
|
|
GetVirtualLineIndexFromTextPos(comparingCaret, frontLine, frontVirtualLine, backVirtualLine);
|
|
vint virtualLineIndex=preferFrontSide?frontVirtualLine:backVirtualLine;
|
|
|
|
Ptr<UniscribeVirtualLine> virtualLine=virtualLineIndex==-1?0:line->virtualLines[virtualLineIndex];
|
|
|
|
switch(position)
|
|
{
|
|
case IGuiGraphicsParagraph::CaretLineFirst:
|
|
return line->startFromParagraph+virtualLine->startFromLine;
|
|
case IGuiGraphicsParagraph::CaretLineLast:
|
|
return line->startFromParagraph+virtualLine->startFromLine+virtualLine->length;
|
|
case IGuiGraphicsParagraph::CaretMoveUp:
|
|
{
|
|
if(frontLine==0 && virtualLineIndex<=0) return comparingCaret;
|
|
Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide);
|
|
if(bounds.Height()!=0)
|
|
{
|
|
if(virtualLineIndex<=0)
|
|
{
|
|
frontLine--;
|
|
virtualLineIndex=lines[frontLine]->virtualLines.Count()-1;
|
|
}
|
|
else
|
|
{
|
|
virtualLineIndex--;
|
|
}
|
|
preferFrontSide=true;
|
|
return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex);
|
|
}
|
|
}
|
|
break;
|
|
case IGuiGraphicsParagraph::CaretMoveDown:
|
|
{
|
|
if(frontLine==lines.Count()-1 && virtualLineIndex==line->virtualLines.Count()-1) return comparingCaret;
|
|
Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide);
|
|
if(bounds.Height()!=0)
|
|
{
|
|
if(virtualLineIndex==line->virtualLines.Count()-1)
|
|
{
|
|
frontLine++;
|
|
virtualLineIndex=0;
|
|
}
|
|
else
|
|
{
|
|
virtualLineIndex++;
|
|
}
|
|
preferFrontSide=false;
|
|
return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Rect UniscribeParagraph::GetCaretBounds(vint caret, bool frontSide)
|
|
{
|
|
if(!IsValidCaret(caret)) return Rect();
|
|
|
|
vint frontLine=0;
|
|
vint backLine=0;
|
|
GetLineIndexFromTextPos(caret, frontLine, backLine);
|
|
|
|
vint frontVirtualLine=0;
|
|
vint backVirtualLine=0;
|
|
GetVirtualLineIndexFromTextPos(caret, frontLine, frontVirtualLine, backVirtualLine);
|
|
vint virtualLineIndex=frontSide?frontVirtualLine:backVirtualLine;
|
|
|
|
return GetCaretBoundsWithLine(caret, frontLine, virtualLineIndex, frontSide);
|
|
}
|
|
|
|
vint UniscribeParagraph::GetCaretFromPoint(Point point)
|
|
{
|
|
vint lineIndex=GetLineIndexFromY(point.y);
|
|
if(lineIndex==-1) return -1;
|
|
|
|
Ptr<UniscribeLine> line=lines[lineIndex];
|
|
if(line->virtualLines.Count()==0) return line->startFromParagraph;
|
|
|
|
vint virtualLineIndex=GetVirtualLineIndexFromY(point.y, lineIndex);
|
|
if(virtualLineIndex==-1) return -1;
|
|
|
|
return GetCaretFromXWithLine(point.x, lineIndex, virtualLineIndex);
|
|
}
|
|
|
|
InlineObject UniscribeParagraph::GetInlineObjectFromPoint(Point point, vint& start, vint& length)
|
|
{
|
|
start = -1;
|
|
length = 0;
|
|
vint lineIndex = GetLineIndexFromY(point.y);
|
|
if (lineIndex == -1) return InlineObject();
|
|
|
|
Ptr<UniscribeLine> line = lines[lineIndex];
|
|
if (line->virtualLines.Count() == 0) return InlineObject();
|
|
|
|
vint virtualLineIndex = GetVirtualLineIndexFromY(point.y, lineIndex);
|
|
if (virtualLineIndex == -1) return InlineObject();
|
|
|
|
return GetInlineObjectFromXWithLine(point.x, lineIndex, virtualLineIndex, start, length);
|
|
}
|
|
|
|
vint UniscribeParagraph::GetNearestCaretFromTextPos(vint textPos, bool frontSide)
|
|
{
|
|
if(!IsValidTextPos(textPos)) return -1;
|
|
vint frontLine=0;
|
|
vint backLine=0;
|
|
GetLineIndexFromTextPos(textPos, frontLine, backLine);
|
|
if(frontLine==-1 || backLine==-1) return -1;
|
|
if(frontLine!=backLine)
|
|
{
|
|
return frontSide?textPos-1:textPos+1;
|
|
}
|
|
|
|
Ptr<UniscribeLine> line=lines[frontLine];
|
|
if(textPos==line->startFromParagraph || textPos==line->startFromParagraph+line->lineText.Length())
|
|
{
|
|
return textPos;
|
|
}
|
|
|
|
vint frontItem=-1;
|
|
vint backItem=-1;
|
|
GetItemIndexFromTextPos(textPos, frontLine, frontItem, backItem);
|
|
if(frontItem==-1 || backItem==-1) return -1;
|
|
if(frontItem!=backItem) return textPos;
|
|
|
|
Ptr<UniscribeItem> item=line->scriptItems[frontItem];
|
|
vint lineTextPos=textPos-line->startFromParagraph;
|
|
if(lineTextPos==item->startFromLine) return textPos;
|
|
if(lineTextPos==item->startFromLine+item->length) return textPos;
|
|
|
|
vint itemTextPos=lineTextPos-item->startFromLine;
|
|
if(item->charLogattrs[itemTextPos].fCharStop) return textPos;
|
|
|
|
if(frontSide)
|
|
{
|
|
for(vint i=itemTextPos-1;i>=0;i--)
|
|
{
|
|
if(item->charLogattrs[i].fCharStop) return i+line->startFromParagraph+item->startFromLine;
|
|
}
|
|
return line->startFromParagraph+item->startFromLine;
|
|
}
|
|
else
|
|
{
|
|
for(vint i=itemTextPos+1;i<item->length;i++)
|
|
{
|
|
if(item->charLogattrs[i].fCharStop) return i+line->startFromParagraph+item->startFromLine;
|
|
}
|
|
return line->startFromParagraph+item->startFromLine+item->length;
|
|
}
|
|
}
|
|
|
|
bool UniscribeParagraph::IsValidCaret(vint caret)
|
|
{
|
|
if(!IsValidTextPos(caret)) return false;
|
|
return GetNearestCaretFromTextPos(caret, true)==caret;
|
|
}
|
|
|
|
bool UniscribeParagraph::IsValidTextPos(vint textPos)
|
|
{
|
|
return 0<=textPos && textPos<=paragraphText.Length();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSWINDOWSGDI.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements
|
|
{
|
|
|
|
/***********************************************************************
|
|
GuiGDIElement
|
|
***********************************************************************/
|
|
|
|
GuiGDIElement::GuiGDIElement()
|
|
{
|
|
}
|
|
|
|
GuiGDIElement::~GuiGDIElement()
|
|
{
|
|
}
|
|
}
|
|
|
|
namespace elements_windows_gdi
|
|
{
|
|
using namespace windows;
|
|
using namespace elements;
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
WindowsGDIRenderTarget
|
|
***********************************************************************/
|
|
|
|
class WindowsGDIRenderTarget : public Object, public IWindowsGDIRenderTarget
|
|
{
|
|
protected:
|
|
INativeWindow* window;
|
|
WinDC* dc;
|
|
List<Rect> clippers;
|
|
vint clipperCoverWholeTargetCounter;
|
|
|
|
void ApplyClipper()
|
|
{
|
|
if(clipperCoverWholeTargetCounter==0)
|
|
{
|
|
if(clippers.Count()==0)
|
|
{
|
|
dc->RemoveClip();
|
|
}
|
|
else
|
|
{
|
|
Rect clipper=GetClipper();
|
|
dc->ClipRegion(new WinRegion(clipper.Left(), clipper.Top(), clipper.Right(), clipper.Bottom(), true));
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
WindowsGDIRenderTarget(INativeWindow* _window)
|
|
:window(_window)
|
|
,dc(0)
|
|
,clipperCoverWholeTargetCounter(0)
|
|
{
|
|
}
|
|
|
|
WinDC* GetDC()override
|
|
{
|
|
return dc?dc:GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
|
|
}
|
|
|
|
void StartRendering()override
|
|
{
|
|
dc=GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
|
|
}
|
|
|
|
bool StopRendering()override
|
|
{
|
|
dc = 0;
|
|
return true;
|
|
}
|
|
|
|
void PushClipper(Rect clipper)override
|
|
{
|
|
if(clipperCoverWholeTargetCounter>0)
|
|
{
|
|
clipperCoverWholeTargetCounter++;
|
|
}
|
|
else
|
|
{
|
|
Rect previousClipper=GetClipper();
|
|
Rect currentClipper;
|
|
|
|
currentClipper.x1=(previousClipper.x1>clipper.x1?previousClipper.x1:clipper.x1);
|
|
currentClipper.y1=(previousClipper.y1>clipper.y1?previousClipper.y1:clipper.y1);
|
|
currentClipper.x2=(previousClipper.x2<clipper.x2?previousClipper.x2:clipper.x2);
|
|
currentClipper.y2=(previousClipper.y2<clipper.y2?previousClipper.y2:clipper.y2);
|
|
|
|
if(currentClipper.x1<currentClipper.x2 && currentClipper.y1<currentClipper.y2)
|
|
{
|
|
clippers.Add(currentClipper);
|
|
}
|
|
else
|
|
{
|
|
clipperCoverWholeTargetCounter++;
|
|
}
|
|
}
|
|
ApplyClipper();
|
|
}
|
|
|
|
void PopClipper()override
|
|
{
|
|
if(clippers.Count()>0)
|
|
{
|
|
if(clipperCoverWholeTargetCounter>0)
|
|
{
|
|
clipperCoverWholeTargetCounter--;
|
|
}
|
|
else
|
|
{
|
|
clippers.RemoveAt(clippers.Count()-1);
|
|
}
|
|
ApplyClipper();
|
|
}
|
|
}
|
|
|
|
Rect GetClipper()override
|
|
{
|
|
if(clippers.Count()==0)
|
|
{
|
|
return Rect(Point(0, 0), window->GetClientSize());
|
|
}
|
|
else
|
|
{
|
|
return clippers[clippers.Count()-1];
|
|
}
|
|
}
|
|
|
|
bool IsClipperCoverWholeTarget()override
|
|
{
|
|
return clipperCoverWholeTargetCounter>0;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
CachedResourceAllocator
|
|
***********************************************************************/
|
|
|
|
class CachedPenAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, Ptr<WinPen>)
|
|
public:
|
|
Ptr<WinPen> CreateInternal(Color color)
|
|
{
|
|
return new WinPen(PS_SOLID, 1, RGB(color.r, color.g, color.b));
|
|
}
|
|
};
|
|
|
|
class CachedBrushAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, Ptr<WinBrush>)
|
|
public:
|
|
Ptr<WinBrush> CreateInternal(Color color)
|
|
{
|
|
return color.a==0?new WinBrush:new WinBrush(RGB(color.r, color.g, color.b));
|
|
}
|
|
};
|
|
|
|
class CachedFontAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr<WinFont>)
|
|
public:
|
|
static Ptr<WinFont> CreateGdiFont(const FontProperties& value)
|
|
{
|
|
vint size=value.size<0?value.size:-value.size;
|
|
return new WinFont(value.fontFamily, size, 0, 0, 0, (value.bold?FW_BOLD:FW_NORMAL), value.italic, value.underline, value.strikeline, value.antialias);
|
|
}
|
|
|
|
Ptr<WinFont> CreateInternal(const FontProperties& value)
|
|
{
|
|
return CreateGdiFont(value);
|
|
}
|
|
};
|
|
|
|
class CachedCharMeasurerAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr<text::CharMeasurer>)
|
|
|
|
protected:
|
|
class GdiCharMeasurer : public text::CharMeasurer
|
|
{
|
|
protected:
|
|
Ptr<WinFont> font;
|
|
vint size;
|
|
|
|
Size MeasureInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
WindowsGDIRenderTarget* gdiRenderTarget=dynamic_cast<WindowsGDIRenderTarget*>(renderTarget);
|
|
WinDC* dc=gdiRenderTarget->GetDC();
|
|
dc->SetFont(font);
|
|
SIZE size=dc->MeasureBuffer(&character, 1, -1);
|
|
return Size(size.cx, size.cy);
|
|
}
|
|
else
|
|
{
|
|
return Size(0, 0);
|
|
}
|
|
}
|
|
|
|
vint MeasureWidthInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
return MeasureInternal(character, renderTarget).x;
|
|
}
|
|
|
|
vint GetRowHeightInternal(IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
return MeasureInternal(L' ', renderTarget).y;
|
|
}
|
|
else
|
|
{
|
|
return size;
|
|
}
|
|
}
|
|
public:
|
|
GdiCharMeasurer(Ptr<WinFont> _font, vint _size)
|
|
:text::CharMeasurer(_size)
|
|
,size(_size)
|
|
,font(_font)
|
|
{
|
|
}
|
|
};
|
|
public:
|
|
Ptr<text::CharMeasurer> CreateInternal(const FontProperties& value)
|
|
{
|
|
return new GdiCharMeasurer(CachedFontAllocator::CreateGdiFont(value), value.size);
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsGDIResourceManager
|
|
***********************************************************************/
|
|
|
|
class WindowsGDIImageFrameCache : public Object, public INativeImageFrameCache
|
|
{
|
|
protected:
|
|
IWindowsGDIResourceManager* resourceManager;
|
|
INativeImageFrame* cachedFrame;
|
|
Ptr<WinBitmap> bitmap;
|
|
Ptr<WinBitmap> disabledBitmap;
|
|
public:
|
|
WindowsGDIImageFrameCache(IWindowsGDIResourceManager* _resourceManager)
|
|
:resourceManager(_resourceManager)
|
|
{
|
|
}
|
|
|
|
~WindowsGDIImageFrameCache()
|
|
{
|
|
}
|
|
|
|
void OnAttach(INativeImageFrame* frame)override
|
|
{
|
|
cachedFrame=frame;
|
|
Size size=frame->GetSize();
|
|
bitmap=new WinBitmap(size.x, size.y, WinBitmap::vbb32Bits, true);
|
|
|
|
IWICBitmap* wicBitmap=GetWindowsGDIObjectProvider()->GetWICBitmap(frame);
|
|
WICRect rect;
|
|
rect.X=0;
|
|
rect.Y=0;
|
|
rect.Width=(int)size.x;
|
|
rect.Height=(int)size.y;
|
|
wicBitmap->CopyPixels(&rect, (int)bitmap->GetLineBytes(), (int)(bitmap->GetLineBytes()*size.y), (BYTE*)bitmap->GetScanLines()[0]);
|
|
|
|
bitmap->BuildAlphaChannel(false);
|
|
}
|
|
|
|
void OnDetach(INativeImageFrame* frame)override
|
|
{
|
|
resourceManager->DestroyBitmapCache(cachedFrame);
|
|
}
|
|
|
|
INativeImageFrame* GetFrame()
|
|
{
|
|
return cachedFrame;
|
|
}
|
|
|
|
Ptr<WinBitmap> GetBitmap(bool enabled)
|
|
{
|
|
if(enabled)
|
|
{
|
|
return bitmap;
|
|
}
|
|
else
|
|
{
|
|
if(!disabledBitmap)
|
|
{
|
|
vint w=bitmap->GetWidth();
|
|
vint h=bitmap->GetHeight();
|
|
disabledBitmap=new WinBitmap(w, h, WinBitmap::vbb32Bits, true);
|
|
for(vint y=0;y<h;y++)
|
|
{
|
|
BYTE* read=bitmap->GetScanLines()[y];
|
|
BYTE* write=disabledBitmap->GetScanLines()[y];
|
|
for(vint x=0;x<w;x++)
|
|
{
|
|
BYTE g=(read[0]+read[1]+read[2])/6+read[3]/2;
|
|
write[0]=g;
|
|
write[1]=g;
|
|
write[2]=g;
|
|
write[3]=read[3];
|
|
read+=4;
|
|
write+=4;
|
|
}
|
|
}
|
|
disabledBitmap->BuildAlphaChannel(false);
|
|
}
|
|
return disabledBitmap;
|
|
}
|
|
}
|
|
};
|
|
|
|
class WindowsGDIResourceManager : public GuiGraphicsResourceManager, public IWindowsGDIResourceManager, public INativeControllerListener
|
|
{
|
|
typedef SortedList<Ptr<WindowsGDIImageFrameCache>> ImageCacheList;
|
|
protected:
|
|
SortedList<Ptr<WindowsGDIRenderTarget>> renderTargets;
|
|
Ptr<WindowsGDILayoutProvider> layoutProvider;
|
|
CachedPenAllocator pens;
|
|
CachedBrushAllocator brushes;
|
|
CachedFontAllocator fonts;
|
|
CachedCharMeasurerAllocator charMeasurers;
|
|
ImageCacheList imageCaches;
|
|
public:
|
|
WindowsGDIResourceManager()
|
|
{
|
|
layoutProvider=new WindowsGDILayoutProvider;
|
|
}
|
|
|
|
IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
|
|
{
|
|
return GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window);
|
|
}
|
|
|
|
void RecreateRenderTarget(INativeWindow* window)override
|
|
{
|
|
}
|
|
|
|
IGuiGraphicsLayoutProvider* GetLayoutProvider()override
|
|
{
|
|
return layoutProvider.Obj();
|
|
}
|
|
|
|
void NativeWindowCreated(INativeWindow* window)override
|
|
{
|
|
WindowsGDIRenderTarget* renderTarget=new WindowsGDIRenderTarget(window);
|
|
renderTargets.Add(renderTarget);
|
|
GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, renderTarget);
|
|
}
|
|
|
|
void NativeWindowDestroying(INativeWindow* window)override
|
|
{
|
|
WindowsGDIRenderTarget* renderTarget=dynamic_cast<WindowsGDIRenderTarget*>(GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window));
|
|
GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, 0);
|
|
renderTargets.Remove(renderTarget);
|
|
}
|
|
|
|
Ptr<windows::WinPen> CreateGdiPen(Color color)override
|
|
{
|
|
return pens.Create(color);
|
|
}
|
|
|
|
void DestroyGdiPen(Color color)override
|
|
{
|
|
pens.Destroy(color);
|
|
}
|
|
|
|
Ptr<windows::WinBrush> CreateGdiBrush(Color color)override
|
|
{
|
|
return brushes.Create(color);
|
|
}
|
|
|
|
void DestroyGdiBrush(Color color)override
|
|
{
|
|
brushes.Destroy(color);
|
|
}
|
|
|
|
Ptr<windows::WinFont> CreateGdiFont(const FontProperties& fontProperties)override
|
|
{
|
|
return fonts.Create(fontProperties);
|
|
}
|
|
|
|
void DestroyGdiFont(const FontProperties& fontProperties)override
|
|
{
|
|
fonts.Destroy(fontProperties);
|
|
}
|
|
|
|
Ptr<elements::text::CharMeasurer> CreateCharMeasurer(const FontProperties& fontProperties)override
|
|
{
|
|
return charMeasurers.Create(fontProperties);
|
|
}
|
|
|
|
void DestroyCharMeasurer(const FontProperties& fontProperties)override
|
|
{
|
|
charMeasurers.Destroy(fontProperties);
|
|
}
|
|
|
|
Ptr<windows::WinBitmap> GetBitmap(INativeImageFrame* frame, bool enabled)override
|
|
{
|
|
Ptr<INativeImageFrameCache> cache=frame->GetCache(this);
|
|
if(cache)
|
|
{
|
|
return cache.Cast<WindowsGDIImageFrameCache>()->GetBitmap(enabled);
|
|
}
|
|
else
|
|
{
|
|
WindowsGDIImageFrameCache* gdiCache=new WindowsGDIImageFrameCache(this);
|
|
if(frame->SetCache(this, gdiCache))
|
|
{
|
|
return gdiCache->GetBitmap(enabled);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DestroyBitmapCache(INativeImageFrame* frame)override
|
|
{
|
|
WindowsGDIImageFrameCache* cache=frame->GetCache(this).Cast<WindowsGDIImageFrameCache>().Obj();
|
|
imageCaches.Remove(cache);
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace elements_windows_gdi
|
|
{
|
|
IWindowsGDIResourceManager* windowsGDIResourceManager=0;
|
|
|
|
IWindowsGDIResourceManager* GetWindowsGDIResourceManager()
|
|
{
|
|
return windowsGDIResourceManager;
|
|
}
|
|
|
|
void SetWindowsGDIResourceManager(IWindowsGDIResourceManager* resourceManager)
|
|
{
|
|
windowsGDIResourceManager=resourceManager;
|
|
}
|
|
|
|
/***********************************************************************
|
|
OS Supporting
|
|
***********************************************************************/
|
|
|
|
IWindowsGDIObjectProvider* windowsGDIObjectProvider=0;
|
|
|
|
IWindowsGDIObjectProvider* GetWindowsGDIObjectProvider()
|
|
{
|
|
return windowsGDIObjectProvider;
|
|
}
|
|
|
|
void SetWindowsGDIObjectProvider(IWindowsGDIObjectProvider* provider)
|
|
{
|
|
windowsGDIObjectProvider=provider;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NativeMain
|
|
***********************************************************************/
|
|
|
|
using namespace vl::presentation;
|
|
using namespace vl::presentation::elements;
|
|
|
|
void RendererMainGDI()
|
|
{
|
|
elements_windows_gdi::WindowsGDIResourceManager resourceManager;
|
|
SetGuiGraphicsResourceManager(&resourceManager);
|
|
elements_windows_gdi::SetWindowsGDIResourceManager(&resourceManager);
|
|
GetCurrentController()->CallbackService()->InstallListener(&resourceManager);
|
|
|
|
elements_windows_gdi::GuiSolidBorderElementRenderer::Register();
|
|
elements_windows_gdi::GuiRoundBorderElementRenderer::Register();
|
|
elements_windows_gdi::Gui3DBorderElementRenderer::Register();
|
|
elements_windows_gdi::Gui3DSplitterElementRenderer::Register();
|
|
elements_windows_gdi::GuiSolidBackgroundElementRenderer::Register();
|
|
elements_windows_gdi::GuiGradientBackgroundElementRenderer::Register();
|
|
elements_windows_gdi::GuiSolidLabelElementRenderer::Register();
|
|
elements_windows_gdi::GuiImageFrameElementRenderer::Register();
|
|
elements_windows_gdi::GuiPolygonElementRenderer::Register();
|
|
elements_windows_gdi::GuiColorizedTextElementRenderer::Register();
|
|
elements_windows_gdi::GuiGDIElementRenderer::Register();
|
|
elements::GuiDocumentElement::GuiDocumentElementRenderer::Register();
|
|
|
|
GuiApplicationMain();
|
|
elements_windows_gdi::SetWindowsGDIResourceManager(0);
|
|
SetGuiGraphicsResourceManager(0);
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSLAYOUTPROVIDERWINDOWSDIRECT2D.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
using namespace elements;
|
|
using namespace collections;
|
|
|
|
namespace elements_windows_d2d
|
|
{
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DElementInlineObject
|
|
***********************************************************************/
|
|
|
|
class WindowsDirect2DElementInlineObject : public IDWriteInlineObject
|
|
{
|
|
public:
|
|
class IRendererCallback : public Interface
|
|
{
|
|
public:
|
|
virtual Color GetBackgroundColor(vint textPosition) = 0;
|
|
virtual IWindowsDirect2DRenderTarget* GetDirect2DRenderTarget() = 0;
|
|
virtual Point GetParagraphOffset() = 0;
|
|
virtual IGuiGraphicsParagraphCallback* GetParagraphCallback() = 0;
|
|
};
|
|
|
|
protected:
|
|
vint counter;
|
|
IGuiGraphicsParagraph::InlineObjectProperties properties;
|
|
IRendererCallback* rendererCallback;
|
|
vint start;
|
|
vint length;
|
|
|
|
public:
|
|
WindowsDirect2DElementInlineObject(
|
|
const IGuiGraphicsParagraph::InlineObjectProperties& _properties,
|
|
IRendererCallback* _rendererCallback,
|
|
vint _start,
|
|
vint _length
|
|
)
|
|
:counter(1)
|
|
,properties(_properties)
|
|
,rendererCallback(_rendererCallback)
|
|
,start(_start)
|
|
,length(_length)
|
|
{
|
|
}
|
|
|
|
~WindowsDirect2DElementInlineObject()
|
|
{
|
|
if (properties.backgroundImage)
|
|
{
|
|
IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer();
|
|
if(graphicsRenderer)
|
|
{
|
|
graphicsRenderer->SetRenderTarget(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
vint GetStart()
|
|
{
|
|
return start;
|
|
}
|
|
|
|
vint GetLength()
|
|
{
|
|
return length;
|
|
}
|
|
|
|
const IGuiGraphicsParagraph::InlineObjectProperties& GetProperties()
|
|
{
|
|
return properties;
|
|
}
|
|
|
|
Ptr<IGuiGraphicsElement> GetElement()
|
|
{
|
|
return properties.backgroundImage;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(
|
|
REFIID riid,
|
|
void __RPC_FAR *__RPC_FAR *ppvObject
|
|
)
|
|
{
|
|
if(ppvObject)
|
|
{
|
|
*ppvObject=NULL;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE AddRef(void)
|
|
{
|
|
++counter;
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG STDMETHODCALLTYPE Release(void)
|
|
{
|
|
if(--counter==0)
|
|
{
|
|
delete this;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Draw)(
|
|
void* clientDrawingContext,
|
|
IDWriteTextRenderer* renderer,
|
|
FLOAT originX,
|
|
FLOAT originY,
|
|
BOOL isSideways,
|
|
BOOL isRightToLeft,
|
|
IUnknown* clientDrawingEffect
|
|
)override
|
|
{
|
|
Rect bounds(Point((vint)originX, (vint)originY), properties.size);
|
|
if (properties.backgroundImage)
|
|
{
|
|
IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer();
|
|
if(graphicsRenderer)
|
|
{
|
|
graphicsRenderer->Render(bounds);
|
|
}
|
|
}
|
|
|
|
Color color=rendererCallback->GetBackgroundColor(start);
|
|
if(color.a!=0)
|
|
{
|
|
color.a/=2;
|
|
if(IWindowsDirect2DRenderTarget* renderTarget=rendererCallback->GetDirect2DRenderTarget())
|
|
{
|
|
ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(color);
|
|
|
|
renderTarget->GetDirect2DRenderTarget()->FillRectangle(
|
|
D2D1::RectF(bounds.x1-0.5f, bounds.y1-0.5f, bounds.x2+0.5f, bounds.y2+0.5f),
|
|
brush
|
|
);
|
|
|
|
renderTarget->DestroyDirect2DBrush(color);
|
|
}
|
|
}
|
|
|
|
if (properties.callbackId != -1)
|
|
{
|
|
if (auto callback = rendererCallback->GetParagraphCallback())
|
|
{
|
|
auto offset = rendererCallback->GetParagraphOffset();
|
|
auto size = callback->OnRenderInlineObject(properties.callbackId, Rect(Point(bounds.x1 - offset.x, bounds.y1 - offset.y), bounds.GetSize()));
|
|
properties.size = size;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(GetMetrics)(
|
|
DWRITE_INLINE_OBJECT_METRICS* metrics
|
|
)override
|
|
{
|
|
metrics->width=(FLOAT)properties.size.x;
|
|
metrics->height=(FLOAT)properties.size.y;
|
|
metrics->baseline=(FLOAT)(properties.baseline==-1?properties.size.y:properties.baseline);
|
|
metrics->supportsSideways=TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(GetOverhangMetrics)(
|
|
DWRITE_OVERHANG_METRICS* overhangs
|
|
)override
|
|
{
|
|
overhangs->left=0;
|
|
overhangs->right=0;
|
|
overhangs->top=0;
|
|
overhangs->bottom=0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(GetBreakConditions)(
|
|
DWRITE_BREAK_CONDITION* breakConditionBefore,
|
|
DWRITE_BREAK_CONDITION* breakConditionAfter
|
|
)override
|
|
{
|
|
switch(properties.breakCondition)
|
|
{
|
|
case IGuiGraphicsParagraph::StickToPreviousRun:
|
|
*breakConditionBefore=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
*breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK;
|
|
break;
|
|
case IGuiGraphicsParagraph::StickToNextRun:
|
|
*breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK;
|
|
*breakConditionAfter=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
break;
|
|
default:
|
|
*breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK;
|
|
*breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK;
|
|
}
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph
|
|
***********************************************************************/
|
|
|
|
class WindowsDirect2DParagraph : public Object, public IGuiGraphicsParagraph, public WindowsDirect2DElementInlineObject::IRendererCallback
|
|
{
|
|
protected:
|
|
struct TextRange
|
|
{
|
|
vint start;
|
|
vint end;
|
|
|
|
TextRange(){}
|
|
TextRange(vint _start, vint _end):start(_start),end(_end){}
|
|
|
|
bool operator==(const TextRange& range) const { return start==range.start; }
|
|
bool operator!=(const TextRange& range) const { return start!=range.start; }
|
|
bool operator<(const TextRange& range) const { return start<range.start; }
|
|
bool operator<=(const TextRange& range) const { return start<=range.start; }
|
|
bool operator>(const TextRange& range) const { return start>range.start; }
|
|
bool operator>=(const TextRange& range) const { return start>=range.start; }
|
|
};
|
|
|
|
typedef Dictionary<IGuiGraphicsElement*, ComPtr<WindowsDirect2DElementInlineObject>> InlineElementMap;
|
|
typedef Dictionary<TextRange, Color> ColorMap;
|
|
typedef Dictionary<TextRange, IGuiGraphicsElement*> GraphicsElementMap;
|
|
protected:
|
|
IGuiGraphicsLayoutProvider* provider;
|
|
ID2D1SolidColorBrush* defaultTextColor;
|
|
IDWriteFactory* dwriteFactory;
|
|
IWindowsDirect2DRenderTarget* renderTarget;
|
|
WString paragraphText;
|
|
ComPtr<IDWriteTextLayout> textLayout;
|
|
bool wrapLine;
|
|
vint maxWidth;
|
|
List<Color> usedColors;
|
|
|
|
InlineElementMap inlineElements;
|
|
GraphicsElementMap graphicsElements;
|
|
ColorMap backgroundColors;
|
|
|
|
vint caret;
|
|
Color caretColor;
|
|
bool caretFrontSide;
|
|
ID2D1SolidColorBrush* caretBrush;
|
|
|
|
bool formatDataAvailable;
|
|
Array<DWRITE_LINE_METRICS> lineMetrics;
|
|
Array<vint> lineStarts;
|
|
Array<FLOAT> lineTops;
|
|
Array<DWRITE_CLUSTER_METRICS> clusterMetrics;
|
|
Array<DWRITE_HIT_TEST_METRICS> hitTestMetrics;
|
|
Array<vint> charHitTestMap;
|
|
|
|
Point paragraphOffset;
|
|
IGuiGraphicsParagraphCallback* paragraphCallback;
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Ranges)
|
|
***********************************************************************/
|
|
|
|
template<typename T>
|
|
void CutMap(Dictionary<TextRange, T>& map, vint start, vint length)
|
|
{
|
|
vint end=start+length;
|
|
for(vint i=map.Count()-1;i>=0;i--)
|
|
{
|
|
TextRange key=map.Keys()[i];
|
|
if(key.start<end && start<key.end)
|
|
{
|
|
T value=map.Values()[i];
|
|
|
|
vint s1=key.start;
|
|
vint s2=key.start>start?key.start:start;
|
|
vint s3=key.end<end?key.end:end;
|
|
vint s4=key.end;
|
|
|
|
map.Remove(key);
|
|
if(s1<s2)
|
|
{
|
|
map.Add(TextRange(s1, s2), value);
|
|
}
|
|
if(s2<s3)
|
|
{
|
|
map.Add(TextRange(s2, s3), value);
|
|
}
|
|
if(s3<s4)
|
|
{
|
|
map.Add(TextRange(s3, s4), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void UpdateOverlappedMap(Dictionary<TextRange, T>& map, vint start, vint length, const T& value)
|
|
{
|
|
vint end=start+length;
|
|
for(vint i=map.Count()-1;i>=0;i--)
|
|
{
|
|
TextRange key=map.Keys()[i];
|
|
if(key.start<end && start<key.end)
|
|
{
|
|
map.Set(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void DefragmentMap(Dictionary<TextRange, T>& map)
|
|
{
|
|
vint lastIndex=map.Count()-1;
|
|
T lastValue=map.Values()[lastIndex];
|
|
for(vint i=map.Count()-2;i>=-1;i--)
|
|
{
|
|
if(i==-1 || map.Values()[i]!=lastValue)
|
|
{
|
|
if(lastIndex-i>0)
|
|
{
|
|
vint start=map.Keys()[i+1].start;
|
|
vint end=map.Keys()[lastIndex].end;
|
|
TextRange key(start, end);
|
|
|
|
for(vint j=lastIndex;j>i;j--)
|
|
{
|
|
map.Remove(map.Keys()[j]);
|
|
}
|
|
map.Add(key, lastValue);
|
|
}
|
|
lastIndex=i;
|
|
if(i!=-1)
|
|
{
|
|
lastValue=map.Values()[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void SetMap(Dictionary<TextRange, T>& map, vint start, vint length, const T& value)
|
|
{
|
|
CutMap(map, start, length);
|
|
UpdateOverlappedMap(map, start, length, value);
|
|
DefragmentMap(map);
|
|
}
|
|
|
|
template<typename T>
|
|
bool GetMap(Dictionary<TextRange, T>& map, vint textPosition, T& value)
|
|
{
|
|
vint start=0;
|
|
vint end=map.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
TextRange key=map.Keys()[middle];
|
|
if(textPosition<key.start)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(textPosition>=key.end)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else
|
|
{
|
|
value=map.Values()[middle];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Layout Retriving)
|
|
***********************************************************************/
|
|
|
|
void PrepareFormatData()
|
|
{
|
|
if(!formatDataAvailable)
|
|
{
|
|
formatDataAvailable=true;
|
|
{
|
|
UINT32 lineCount=0;
|
|
textLayout->GetLineMetrics(NULL, 0, &lineCount);
|
|
lineMetrics.Resize(lineCount);
|
|
if(lineCount>0)
|
|
{
|
|
textLayout->GetLineMetrics(&lineMetrics[0], lineCount, &lineCount);
|
|
}
|
|
|
|
lineStarts.Resize(lineCount);
|
|
lineTops.Resize(lineCount);
|
|
vint start=0;
|
|
FLOAT top=0;
|
|
for(vint i=0;i<lineMetrics.Count();i++)
|
|
{
|
|
DWRITE_LINE_METRICS& metrics=lineMetrics[i];
|
|
|
|
lineStarts[i]=start;
|
|
start+=metrics.length;
|
|
|
|
lineTops[i]=top;
|
|
top+=metrics.height;
|
|
}
|
|
}
|
|
{
|
|
UINT32 clusterCount=0;
|
|
textLayout->GetClusterMetrics(NULL, 0, &clusterCount);
|
|
clusterMetrics.Resize(clusterCount);
|
|
if(clusterCount>0)
|
|
{
|
|
textLayout->GetClusterMetrics(&clusterMetrics[0], clusterCount, &clusterCount);
|
|
}
|
|
}
|
|
{
|
|
vint textPos=0;
|
|
hitTestMetrics.Resize(clusterMetrics.Count());
|
|
for(vint i=0;i<hitTestMetrics.Count();i++)
|
|
{
|
|
FLOAT x=0;
|
|
FLOAT y=0;
|
|
DWRITE_HIT_TEST_METRICS& metrics=hitTestMetrics[i];
|
|
textLayout->HitTestTextPosition((UINT32)textPos, FALSE, &x, &y, &metrics);
|
|
textPos+=metrics.length;
|
|
}
|
|
}
|
|
{
|
|
charHitTestMap.Resize(paragraphText.Length());
|
|
for(vint i=0;i<hitTestMetrics.Count();i++)
|
|
{
|
|
DWRITE_HIT_TEST_METRICS& metrics=hitTestMetrics[i];
|
|
for(UINT32 j=0;j<metrics.length;j++)
|
|
{
|
|
charHitTestMap[metrics.textPosition+j]=i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Initialization)
|
|
***********************************************************************/
|
|
|
|
WindowsDirect2DParagraph(IGuiGraphicsLayoutProvider* _provider, const WString& _text, IGuiGraphicsRenderTarget* _renderTarget, IGuiGraphicsParagraphCallback* _paragraphCallback)
|
|
:provider(_provider)
|
|
,dwriteFactory(GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory())
|
|
,renderTarget(dynamic_cast<IWindowsDirect2DRenderTarget*>(_renderTarget))
|
|
,paragraphText(_text)
|
|
,textLayout(0)
|
|
,wrapLine(true)
|
|
,maxWidth(-1)
|
|
,caret(-1)
|
|
,caretFrontSide(false)
|
|
,caretBrush(0)
|
|
,formatDataAvailable(false)
|
|
,paragraphCallback(_paragraphCallback)
|
|
{
|
|
FontProperties defaultFont=GetCurrentController()->ResourceService()->GetDefaultFont();
|
|
Direct2DTextFormatPackage* package=GetWindowsDirect2DResourceManager()->CreateDirect2DTextFormat(defaultFont);
|
|
defaultTextColor=renderTarget->CreateDirect2DBrush(Color(0, 0, 0));
|
|
usedColors.Add(Color(0, 0, 0));
|
|
|
|
IDWriteTextLayout* rawTextLayout=0;
|
|
HRESULT hr=dwriteFactory->CreateTextLayout(
|
|
_text.Buffer(),
|
|
(int)_text.Length(),
|
|
package->textFormat.Obj(),
|
|
0,
|
|
0,
|
|
&rawTextLayout);
|
|
if(!FAILED(hr))
|
|
{
|
|
textLayout=rawTextLayout;
|
|
textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
|
}
|
|
graphicsElements.Add(TextRange(0, _text.Length()), 0);
|
|
backgroundColors.Add(TextRange(0, _text.Length()), Color(0, 0, 0, 0));
|
|
|
|
GetWindowsDirect2DResourceManager()->DestroyDirect2DTextFormat(defaultFont);
|
|
}
|
|
|
|
~WindowsDirect2DParagraph()
|
|
{
|
|
CloseCaret();
|
|
FOREACH(Color, color, usedColors)
|
|
{
|
|
renderTarget->DestroyDirect2DBrush(color);
|
|
}
|
|
}
|
|
|
|
IGuiGraphicsLayoutProvider* GetProvider()override
|
|
{
|
|
return provider;
|
|
}
|
|
|
|
IGuiGraphicsRenderTarget* GetRenderTarget()override
|
|
{
|
|
return renderTarget;
|
|
}
|
|
|
|
Point GetParagraphOffset()override
|
|
{
|
|
return paragraphOffset;
|
|
}
|
|
|
|
IGuiGraphicsParagraphCallback* GetParagraphCallback()override
|
|
{
|
|
return paragraphCallback;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Formatting)
|
|
***********************************************************************/
|
|
|
|
bool GetWrapLine()override
|
|
{
|
|
return wrapLine;
|
|
}
|
|
|
|
void SetWrapLine(bool value)override
|
|
{
|
|
if(wrapLine!=value)
|
|
{
|
|
wrapLine=value;
|
|
textLayout->SetWordWrapping(value?DWRITE_WORD_WRAPPING_WRAP:DWRITE_WORD_WRAPPING_NO_WRAP);
|
|
formatDataAvailable=false;
|
|
}
|
|
}
|
|
|
|
vint GetMaxWidth()override
|
|
{
|
|
return maxWidth;
|
|
}
|
|
|
|
void SetMaxWidth(vint value)override
|
|
{
|
|
if(maxWidth!=value)
|
|
{
|
|
maxWidth=value;
|
|
textLayout->SetMaxWidth(value==-1?65536:(FLOAT)value);
|
|
formatDataAvailable=false;
|
|
}
|
|
}
|
|
|
|
Alignment GetParagraphAlignment()override
|
|
{
|
|
switch(textLayout->GetTextAlignment())
|
|
{
|
|
case DWRITE_TEXT_ALIGNMENT_LEADING:
|
|
return Alignment::Left;
|
|
case DWRITE_TEXT_ALIGNMENT_CENTER:
|
|
return Alignment::Center;
|
|
case DWRITE_TEXT_ALIGNMENT_TRAILING:
|
|
return Alignment::Right;
|
|
default:
|
|
return Alignment::Left;
|
|
}
|
|
}
|
|
|
|
void SetParagraphAlignment(Alignment value)override
|
|
{
|
|
formatDataAvailable=false;
|
|
switch(value)
|
|
{
|
|
case Alignment::Left:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
|
|
break;
|
|
case Alignment::Center:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
|
|
break;
|
|
case Alignment::Right:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool SetFont(vint start, vint length, const WString& value)override
|
|
{
|
|
if(length==0) return true;
|
|
formatDataAvailable=false;
|
|
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)start;
|
|
range.length=(int)length;
|
|
HRESULT hr=textLayout->SetFontFamilyName(value.Buffer(), range);
|
|
return !FAILED(hr);
|
|
}
|
|
|
|
bool SetSize(vint start, vint length, vint value)override
|
|
{
|
|
if(length==0) return true;
|
|
formatDataAvailable=false;
|
|
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)start;
|
|
range.length=(int)length;
|
|
HRESULT hr=textLayout->SetFontSize((FLOAT)value, range);
|
|
return !FAILED(hr);
|
|
}
|
|
|
|
bool SetStyle(vint start, vint length, TextStyle value)override
|
|
{
|
|
if(length==0) return true;
|
|
formatDataAvailable=false;
|
|
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)start;
|
|
range.length=(int)length;
|
|
HRESULT hr=S_OK;
|
|
|
|
hr=textLayout->SetFontStyle(value&Italic?DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL, range);
|
|
if(FAILED(hr)) return false;
|
|
hr=textLayout->SetFontWeight(value&Bold?DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL, range);
|
|
if(FAILED(hr)) return false;
|
|
hr=textLayout->SetUnderline(value&Underline?TRUE:FALSE, range);
|
|
if(FAILED(hr)) return false;
|
|
hr=textLayout->SetStrikethrough(value&Strikeline?TRUE:FALSE, range);
|
|
if(FAILED(hr)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SetColor(vint start, vint length, Color value)override
|
|
{
|
|
if(length==0) return true;
|
|
formatDataAvailable=false;
|
|
|
|
ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(value);
|
|
usedColors.Add(value);
|
|
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)start;
|
|
range.length=(int)length;
|
|
HRESULT hr=textLayout->SetDrawingEffect(brush, range);
|
|
return !FAILED(hr);
|
|
}
|
|
|
|
bool SetBackgroundColor(vint start, vint length, Color value)override
|
|
{
|
|
SetMap(backgroundColors, start, length, value);
|
|
return true;
|
|
}
|
|
|
|
bool SetInlineObject(vint start, vint length, const InlineObjectProperties& properties)override
|
|
{
|
|
if(inlineElements.Keys().Contains(properties.backgroundImage.Obj()))
|
|
{
|
|
return false;
|
|
}
|
|
for(vint i=0;i<inlineElements.Count();i++)
|
|
{
|
|
ComPtr<WindowsDirect2DElementInlineObject> inlineObject=inlineElements.Values().Get(i);
|
|
if(start<inlineObject->GetStart()+inlineObject->GetLength() && inlineObject->GetStart()<start+length)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
formatDataAvailable=false;
|
|
|
|
ComPtr<WindowsDirect2DElementInlineObject> inlineObject=new WindowsDirect2DElementInlineObject(properties, this, start, length);
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)start;
|
|
range.length=(int)length;
|
|
HRESULT hr=textLayout->SetInlineObject(inlineObject.Obj(), range);
|
|
if(!FAILED(hr))
|
|
{
|
|
if (properties.backgroundImage)
|
|
{
|
|
IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer();
|
|
if(renderer)
|
|
{
|
|
renderer->SetRenderTarget(renderTarget);
|
|
}
|
|
inlineElements.Add(properties.backgroundImage.Obj(), inlineObject);
|
|
}
|
|
SetMap(graphicsElements, start, length, properties.backgroundImage.Obj());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ResetInlineObject(vint start, vint length)override
|
|
{
|
|
IGuiGraphicsElement* element=0;
|
|
if(GetMap(graphicsElements, start, element) && element)
|
|
{
|
|
ComPtr<WindowsDirect2DElementInlineObject> inlineObject=inlineElements[element];
|
|
DWRITE_TEXT_RANGE range;
|
|
range.startPosition=(int)inlineObject->GetStart();
|
|
range.length=(int)inlineObject->GetLength();
|
|
HRESULT hr=textLayout->SetInlineObject(NULL, range);
|
|
if(!FAILED(hr))
|
|
{
|
|
formatDataAvailable=false;
|
|
inlineElements.Remove(element);
|
|
SetMap(graphicsElements, inlineObject->GetStart(), inlineObject->GetLength(), (IGuiGraphicsElement*)0);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vint GetHeight()override
|
|
{
|
|
DWRITE_TEXT_METRICS metrics;
|
|
textLayout->GetMetrics(&metrics);
|
|
return (vint)metrics.height;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (IRenderingCallback)
|
|
***********************************************************************/
|
|
|
|
Color GetBackgroundColor(vint textPosition)override
|
|
{
|
|
Color color;
|
|
if(GetMap(backgroundColors, textPosition, color))
|
|
{
|
|
return color;
|
|
}
|
|
else
|
|
{
|
|
return Color(0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
IWindowsDirect2DRenderTarget* GetDirect2DRenderTarget()override
|
|
{
|
|
return renderTarget;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Rendering)
|
|
***********************************************************************/
|
|
|
|
bool OpenCaret(vint _caret, Color _color, bool _frontSide)override
|
|
{
|
|
if(!IsValidCaret(_caret)) return false;
|
|
if(caret!=-1) CloseCaret();
|
|
caret=_caret;
|
|
caretColor=_color;
|
|
caretFrontSide=_frontSide;
|
|
caretBrush=renderTarget->CreateDirect2DBrush(caretColor);
|
|
return true;
|
|
}
|
|
|
|
bool CloseCaret()override
|
|
{
|
|
if(caret==-1) return false;
|
|
caret=-1;
|
|
renderTarget->DestroyDirect2DBrush(caretColor);
|
|
caretBrush=0;
|
|
return true;
|
|
}
|
|
|
|
void Render(Rect bounds)override
|
|
{
|
|
paragraphOffset = bounds.LeftTop();
|
|
PrepareFormatData();
|
|
for(vint i=0;i<backgroundColors.Count();i++)
|
|
{
|
|
TextRange key=backgroundColors.Keys()[i];
|
|
Color color=backgroundColors.Values()[i];
|
|
if(color.a>0)
|
|
{
|
|
ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(color);
|
|
|
|
vint start=key.start;
|
|
if(start<0)
|
|
{
|
|
start=0;
|
|
}
|
|
|
|
while(start<charHitTestMap.Count() && start<key.end)
|
|
{
|
|
vint index=charHitTestMap[start];
|
|
DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
|
|
|
|
FLOAT x1=hitTest.left+(FLOAT)bounds.x1;
|
|
FLOAT y1=hitTest.top+(FLOAT)bounds.y1;
|
|
FLOAT x2=x1+hitTest.width;
|
|
FLOAT y2=y1+hitTest.height;
|
|
|
|
x1-=0.5f;
|
|
y1-=0.0f;
|
|
x2+=0.5f;
|
|
y2+=0.5f;
|
|
|
|
renderTarget->GetDirect2DRenderTarget()->FillRectangle(
|
|
D2D1::RectF(x1, y1, x2, y2),
|
|
brush
|
|
);
|
|
|
|
start=hitTest.textPosition+hitTest.length;
|
|
}
|
|
|
|
renderTarget->DestroyDirect2DBrush(color);
|
|
}
|
|
}
|
|
|
|
renderTarget->GetDirect2DRenderTarget()->DrawTextLayout(
|
|
D2D1::Point2F((FLOAT)bounds.Left(), (FLOAT)bounds.Top()),
|
|
textLayout.Obj(),
|
|
defaultTextColor,
|
|
D2D1_DRAW_TEXT_OPTIONS_NO_SNAP);
|
|
|
|
if(caret!=-1)
|
|
{
|
|
Rect caretBounds=GetCaretBounds(caret, caretFrontSide);
|
|
vint x=caretBounds.x1+bounds.x1;
|
|
vint y1=caretBounds.y1+bounds.y1;
|
|
vint y2=y1+caretBounds.Height();
|
|
|
|
renderTarget->GetDirect2DRenderTarget()->DrawLine(
|
|
D2D1::Point2F((FLOAT)x-0.5f, (FLOAT)y1+0.5f),
|
|
D2D1::Point2F((FLOAT)x-0.5f, (FLOAT)y2+0.5f),
|
|
caretBrush
|
|
);
|
|
renderTarget->GetDirect2DRenderTarget()->DrawLine(
|
|
D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)y1+0.5f),
|
|
D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)y2+0.5f),
|
|
caretBrush
|
|
);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Caret Helper)
|
|
***********************************************************************/
|
|
|
|
void GetLineIndexFromTextPos(vint textPos, vint& frontLineIndex, vint& backLineIndex)
|
|
{
|
|
frontLineIndex=-1;
|
|
backLineIndex=-1;
|
|
vint start=0;
|
|
vint end=lineMetrics.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
DWRITE_LINE_METRICS& metrics=lineMetrics[middle];
|
|
vint lineStart=lineStarts[middle];
|
|
vint lineEnd=lineStart+metrics.length-metrics.newlineLength;
|
|
|
|
if(textPos<lineStart)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(textPos>lineEnd)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else if(textPos==lineStart && middle!=0)
|
|
{
|
|
DWRITE_LINE_METRICS& anotherLine=lineMetrics[middle-1];
|
|
frontLineIndex=anotherLine.newlineLength==0?middle-1:middle;
|
|
backLineIndex=middle;
|
|
return;
|
|
}
|
|
else if(textPos==lineEnd && middle!=lineMetrics.Count()-1)
|
|
{
|
|
frontLineIndex=middle;
|
|
backLineIndex=metrics.newlineLength==0?middle+1:middle;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
frontLineIndex=middle;
|
|
backLineIndex=middle;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Pair<FLOAT, FLOAT> GetLineYRange(vint lineIndex)
|
|
{
|
|
DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
|
|
FLOAT top=lineTops[lineIndex];
|
|
return Pair<FLOAT, FLOAT>(top, top+line.height);
|
|
}
|
|
|
|
vint GetLineIndexFromY(vint y)
|
|
{
|
|
if(paragraphText.Length()==0) return 0;
|
|
FLOAT minY=0;
|
|
FLOAT maxY=0;
|
|
{
|
|
minY=hitTestMetrics[0].top;
|
|
DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[hitTestMetrics.Count()-1];
|
|
maxY=hitTest.top+hitTest.height;
|
|
}
|
|
|
|
if(y<minY)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(y>=maxY)
|
|
{
|
|
return lineMetrics.Count()-1;
|
|
}
|
|
|
|
vint start=0;
|
|
vint end=lineMetrics.Count()-1;
|
|
while(start<=end)
|
|
{
|
|
vint middle=(start+end)/2;
|
|
Pair<FLOAT, FLOAT> yRange=GetLineYRange(middle);
|
|
minY=yRange.key;
|
|
maxY=yRange.value;
|
|
|
|
if(y<minY)
|
|
{
|
|
end=middle-1;
|
|
}
|
|
else if(y>=maxY)
|
|
{
|
|
start=middle+1;
|
|
}
|
|
else
|
|
{
|
|
return middle;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
vint GetCaretFromXWithLine(vint x, vint lineIndex)
|
|
{
|
|
DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
|
|
vint lineStart=lineStarts[lineIndex];
|
|
vint lineEnd=lineStart+line.length-line.newlineLength;
|
|
|
|
FLOAT minLineX=0;
|
|
FLOAT maxLineX=0;
|
|
|
|
for(vint i=lineStart;i<lineEnd;)
|
|
{
|
|
vint index=charHitTestMap[i];
|
|
DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
|
|
FLOAT minX=hitTest.left;
|
|
FLOAT maxX=minX+hitTest.width;
|
|
|
|
if(minLineX>minX) minLineX=minX;
|
|
if(maxLineX<maxX) maxLineX=maxX;
|
|
|
|
if(minX<=x && x<maxX)
|
|
{
|
|
DWRITE_CLUSTER_METRICS& cluster=clusterMetrics[index];
|
|
FLOAT d1=x-minX;
|
|
FLOAT d2=maxX-x;
|
|
if(d1<=d2)
|
|
{
|
|
return cluster.isRightToLeft?i+hitTest.length:i;
|
|
}
|
|
else
|
|
{
|
|
return cluster.isRightToLeft?i:i+hitTest.length;
|
|
}
|
|
}
|
|
i+=hitTest.length;
|
|
}
|
|
|
|
if(x<minLineX) return lineStart;
|
|
if(x>=maxLineX) return lineEnd;
|
|
return lineStart;
|
|
}
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DParagraph (Caret)
|
|
***********************************************************************/
|
|
|
|
vint GetCaret(vint comparingCaret, CaretRelativePosition position, bool& preferFrontSide)override
|
|
{
|
|
PrepareFormatData();
|
|
if(position==CaretFirst) return 0;
|
|
if(position==CaretLast) return paragraphText.Length();
|
|
if(!IsValidCaret(comparingCaret)) return -1;
|
|
|
|
vint frontLineIndex=-1;
|
|
vint backLineIndex=-1;
|
|
GetLineIndexFromTextPos(comparingCaret, frontLineIndex, backLineIndex);
|
|
vint lineIndex=preferFrontSide?frontLineIndex:backLineIndex;
|
|
DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
|
|
vint lineStart=lineStarts[lineIndex];
|
|
vint lineEnd=lineStart+line.length-line.newlineLength;
|
|
|
|
switch(position)
|
|
{
|
|
case CaretLineFirst:
|
|
return lineStarts[lineIndex];
|
|
case CaretLineLast:
|
|
return lineStarts[lineIndex]+line.length-line.newlineLength;
|
|
case CaretMoveLeft:
|
|
{
|
|
if(comparingCaret==0)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(comparingCaret==lineStart)
|
|
{
|
|
vint offset=lineMetrics[lineIndex-1].newlineLength;
|
|
if(offset>0)
|
|
{
|
|
return lineStart-offset;
|
|
}
|
|
}
|
|
return hitTestMetrics[charHitTestMap[comparingCaret-1]].textPosition;
|
|
}
|
|
case CaretMoveRight:
|
|
{
|
|
if(comparingCaret==paragraphText.Length())
|
|
{
|
|
return paragraphText.Length();
|
|
}
|
|
else if(comparingCaret==lineEnd && line.newlineLength!=0)
|
|
{
|
|
return lineEnd+line.newlineLength;
|
|
}
|
|
else
|
|
{
|
|
vint index=charHitTestMap[comparingCaret];
|
|
if(index==hitTestMetrics.Count()-1) return paragraphText.Length();
|
|
return hitTestMetrics[index+1].textPosition;
|
|
}
|
|
}
|
|
case CaretMoveUp:
|
|
{
|
|
if(lineIndex==0)
|
|
{
|
|
return comparingCaret;
|
|
}
|
|
else
|
|
{
|
|
Rect bounds=GetCaretBounds(comparingCaret, preferFrontSide);
|
|
preferFrontSide=true;
|
|
return GetCaretFromXWithLine(bounds.x1, lineIndex-1);
|
|
}
|
|
}
|
|
case CaretMoveDown:
|
|
{
|
|
if(lineIndex==lineMetrics.Count()-1)
|
|
{
|
|
return comparingCaret;
|
|
}
|
|
else
|
|
{
|
|
Rect bounds=GetCaretBounds(comparingCaret, preferFrontSide);
|
|
preferFrontSide=false;
|
|
return GetCaretFromXWithLine(bounds.x1, lineIndex+1);
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
Rect GetCaretBounds(vint caret, bool frontSide)override
|
|
{
|
|
PrepareFormatData();
|
|
if(!IsValidCaret(caret)) return Rect();
|
|
if(paragraphText.Length()==0) return Rect(Point(0, 0), Size(0, GetHeight()));
|
|
|
|
vint frontLineIndex=-1;
|
|
vint backLineIndex=-1;
|
|
GetLineIndexFromTextPos(caret, frontLineIndex, backLineIndex);
|
|
vint lineIndex=frontSide?frontLineIndex:backLineIndex;
|
|
|
|
Pair<FLOAT, FLOAT> lineYRange=GetLineYRange(lineIndex);
|
|
DWRITE_LINE_METRICS& line=lineMetrics[lineIndex];
|
|
if(line.length-line.newlineLength==0)
|
|
{
|
|
return Rect(0, (vint)lineYRange.key, 0, (vint)lineYRange.value);
|
|
}
|
|
else
|
|
{
|
|
vint lineStart=lineStarts[lineIndex];
|
|
vint lineEnd=lineStart+line.length-line.newlineLength;
|
|
if(caret==lineStart)
|
|
{
|
|
frontSide=false;
|
|
}
|
|
else if(caret==lineEnd)
|
|
{
|
|
frontSide=true;
|
|
}
|
|
if(frontSide)
|
|
{
|
|
caret--;
|
|
}
|
|
|
|
vint index=charHitTestMap[caret];
|
|
DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
|
|
DWRITE_CLUSTER_METRICS& cluster=clusterMetrics[index];
|
|
if(cluster.isRightToLeft)
|
|
{
|
|
frontSide=!frontSide;
|
|
}
|
|
|
|
if(frontSide)
|
|
{
|
|
return Rect(
|
|
Point((vint)(hitTest.left+hitTest.width), (vint)hitTest.top),
|
|
Size(0, (vint)hitTest.height)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return Rect(
|
|
Point((vint)hitTest.left, (vint)hitTest.top),
|
|
Size(0, (vint)hitTest.height)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
vint GetCaretFromPoint(Point point)override
|
|
{
|
|
PrepareFormatData();
|
|
vint lineIndex=GetLineIndexFromY(point.y);
|
|
vint caret=GetCaretFromXWithLine(point.x, lineIndex);
|
|
return caret;
|
|
}
|
|
|
|
Nullable<InlineObjectProperties> GetInlineObjectFromPoint(Point point, vint& start, vint& length)override
|
|
{
|
|
DWRITE_HIT_TEST_METRICS metrics={0};
|
|
BOOL trailingHit=FALSE;
|
|
BOOL inside=FALSE;
|
|
start=-1;
|
|
length=0;
|
|
HRESULT hr=textLayout->HitTestPoint((FLOAT)point.x, (FLOAT)point.y, &trailingHit, &inside, &metrics);
|
|
if(hr==S_OK)
|
|
{
|
|
IGuiGraphicsElement* element=0;
|
|
if(GetMap(graphicsElements, metrics.textPosition, element) && element)
|
|
{
|
|
ComPtr<WindowsDirect2DElementInlineObject> inlineObject=inlineElements[element];
|
|
start=inlineObject->GetStart();
|
|
length=inlineObject->GetLength();
|
|
return inlineObject->GetProperties();
|
|
}
|
|
}
|
|
return Nullable<InlineObjectProperties>();
|
|
}
|
|
|
|
vint GetNearestCaretFromTextPos(vint textPos, bool frontSide)override
|
|
{
|
|
PrepareFormatData();
|
|
if(!IsValidTextPos(textPos)) return -1;
|
|
if(textPos==0 || textPos==paragraphText.Length()) return textPos;
|
|
|
|
vint index=charHitTestMap[textPos];
|
|
DWRITE_HIT_TEST_METRICS& hitTest=hitTestMetrics[index];
|
|
if(hitTest.textPosition==textPos)
|
|
{
|
|
return textPos;
|
|
}
|
|
else if(frontSide)
|
|
{
|
|
return hitTest.textPosition;
|
|
}
|
|
else
|
|
{
|
|
return hitTest.textPosition+hitTest.length;
|
|
}
|
|
}
|
|
|
|
bool IsValidCaret(vint caret)override
|
|
{
|
|
PrepareFormatData();
|
|
if(!IsValidTextPos(caret)) return false;
|
|
if(caret==0 || caret==paragraphText.Length()) return true;
|
|
if(hitTestMetrics[charHitTestMap[caret]].textPosition==caret) return true;
|
|
|
|
vint frontLineIndex=-1;
|
|
vint backLineIndex=-1;
|
|
GetLineIndexFromTextPos(caret, frontLineIndex, backLineIndex);
|
|
if(frontLineIndex==-1 && backLineIndex==-1) return false;
|
|
return false;
|
|
}
|
|
|
|
bool IsValidTextPos(vint textPos)
|
|
{
|
|
return 0<=textPos && textPos<=paragraphText.Length();
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DLayoutProvider
|
|
***********************************************************************/
|
|
|
|
Ptr<IGuiGraphicsParagraph> WindowsDirect2DLayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback)
|
|
{
|
|
return new WindowsDirect2DParagraph(this, text, renderTarget, callback);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSRENDERERSWINDOWSDIRECT2D.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements_windows_d2d
|
|
{
|
|
using namespace collections;
|
|
|
|
/***********************************************************************
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER
|
|
***********************************************************************/
|
|
|
|
#define IMPLEMENT_BRUSH_ELEMENT_RENDERER(TRENDERER)\
|
|
void TRENDERER::InitializeInternal()\
|
|
{\
|
|
}\
|
|
void TRENDERER::FinalizeInternal()\
|
|
{\
|
|
DestroyBrush(renderTarget);\
|
|
}\
|
|
void TRENDERER::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)\
|
|
{\
|
|
DestroyBrush(oldRenderTarget);\
|
|
CreateBrush(newRenderTarget);\
|
|
}\
|
|
TRENDERER::TRENDERER()\
|
|
:brush(0)\
|
|
{\
|
|
}\
|
|
void TRENDERER::Render(Rect bounds)\
|
|
|
|
#define IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(TRENDERER)\
|
|
void TRENDERER::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)\
|
|
{\
|
|
if(_renderTarget)\
|
|
{\
|
|
oldColor=element->GetColor();\
|
|
brush=_renderTarget->CreateDirect2DBrush(oldColor);\
|
|
}\
|
|
}\
|
|
void TRENDERER::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)\
|
|
{\
|
|
if(_renderTarget && brush)\
|
|
{\
|
|
_renderTarget->DestroyDirect2DBrush(oldColor);\
|
|
brush=0;\
|
|
}\
|
|
}\
|
|
void TRENDERER::OnElementStateChanged()\
|
|
{\
|
|
if(renderTarget)\
|
|
{\
|
|
Color color=element->GetColor();\
|
|
if(oldColor!=color)\
|
|
{\
|
|
DestroyBrush(renderTarget);\
|
|
CreateBrush(renderTarget);\
|
|
}\
|
|
}\
|
|
}\
|
|
|
|
#define IMPLEMENT_BRUSH_ELEMENT_RENDERER_LINEAR_GRADIENT_BRUSH(TRENDERER)\
|
|
void TRENDERER::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)\
|
|
{\
|
|
if(_renderTarget)\
|
|
{\
|
|
oldColor=Pair<Color, Color>(element->GetColor1(), element->GetColor2());\
|
|
brush=_renderTarget->CreateDirect2DLinearBrush(oldColor.key, oldColor.value);\
|
|
}\
|
|
}\
|
|
void TRENDERER::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)\
|
|
{\
|
|
if(_renderTarget && brush)\
|
|
{\
|
|
_renderTarget->DestroyDirect2DLinearBrush(oldColor.key, oldColor.value);\
|
|
brush=0;\
|
|
}\
|
|
}\
|
|
void TRENDERER::OnElementStateChanged()\
|
|
{\
|
|
if(renderTarget)\
|
|
{\
|
|
Pair<Color, Color> color=Pair<Color, Color>(element->GetColor1(), element->GetColor2());\
|
|
if(oldColor!=color)\
|
|
{\
|
|
DestroyBrush(renderTarget);\
|
|
CreateBrush(renderTarget);\
|
|
}\
|
|
}\
|
|
}\
|
|
|
|
/***********************************************************************
|
|
GuiSolidBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiSolidBorderElementRenderer)
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiSolidBorderElementRenderer)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
d2dRenderTarget->DrawRectangle(
|
|
D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f),
|
|
brush
|
|
);
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
d2dRenderTarget->DrawEllipse(
|
|
D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f),
|
|
brush
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiRoundBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiRoundBorderElementRenderer)
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiRoundBorderElementRenderer)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
d2dRenderTarget->DrawRoundedRectangle(
|
|
D2D1::RoundedRect(
|
|
D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f),
|
|
(FLOAT)element->GetRadius(),
|
|
(FLOAT)element->GetRadius()
|
|
),
|
|
brush
|
|
);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Gui3DBorderElementRenderer
|
|
***********************************************************************/
|
|
|
|
void Gui3DBorderElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
oldColor1=element->GetColor1();
|
|
oldColor2=element->GetColor2();
|
|
brush1=_renderTarget->CreateDirect2DBrush(oldColor1);
|
|
brush2=_renderTarget->CreateDirect2DBrush(oldColor2);
|
|
}
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
if(brush1)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldColor1);
|
|
brush1=0;
|
|
}
|
|
if(brush2)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldColor2);
|
|
brush2=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::FinalizeInternal()
|
|
{
|
|
DestroyBrush(renderTarget);
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
DestroyBrush(oldRenderTarget);
|
|
CreateBrush(newRenderTarget);
|
|
}
|
|
|
|
Gui3DBorderElementRenderer::Gui3DBorderElementRenderer()
|
|
:brush1(0)
|
|
,brush2(0)
|
|
{
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::Render(Rect bounds)
|
|
{
|
|
D2D1_RECT_F rect=D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f);
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
|
|
d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.right, rect.top), brush1);
|
|
d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.left, rect.bottom), brush1);
|
|
d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.left, rect.bottom), brush2);
|
|
d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.right, rect.top), brush2);
|
|
}
|
|
|
|
void Gui3DBorderElementRenderer::OnElementStateChanged()
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
Color color1=element->GetColor1();
|
|
Color color2=element->GetColor2();
|
|
if(oldColor1!=color1 || oldColor2!=color2)
|
|
{
|
|
DestroyBrush(renderTarget);
|
|
CreateBrush(renderTarget);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
Gui3DSplitterElementRenderer
|
|
***********************************************************************/
|
|
|
|
void Gui3DSplitterElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
oldColor1=element->GetColor1();
|
|
oldColor2=element->GetColor2();
|
|
brush1=_renderTarget->CreateDirect2DBrush(oldColor1);
|
|
brush2=_renderTarget->CreateDirect2DBrush(oldColor2);
|
|
}
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
if(brush1)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldColor1);
|
|
brush1=0;
|
|
}
|
|
if(brush2)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldColor2);
|
|
brush2=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::FinalizeInternal()
|
|
{
|
|
DestroyBrush(renderTarget);
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
DestroyBrush(oldRenderTarget);
|
|
CreateBrush(newRenderTarget);
|
|
}
|
|
|
|
Gui3DSplitterElementRenderer::Gui3DSplitterElementRenderer()
|
|
:brush1(0)
|
|
,brush2(0)
|
|
{
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::Render(Rect bounds)
|
|
{
|
|
D2D1_POINT_2F p11, p12, p21, p22;
|
|
switch(element->GetDirection())
|
|
{
|
|
case Gui3DSplitterElement::Horizontal:
|
|
{
|
|
vint y=bounds.y1+bounds.Height()/2-1;
|
|
p11=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+0.5f);
|
|
p12=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+0.5f);
|
|
p21=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+1.5f);
|
|
p22=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+1.5f);
|
|
}
|
|
break;
|
|
case Gui3DSplitterElement::Vertical:
|
|
{
|
|
vint x=bounds.x1+bounds.Width()/2-1;
|
|
p11=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y1-0.0f);
|
|
p12=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y2+0.0f);
|
|
p21=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y1-0.0f);
|
|
p22=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y2+0.0f);
|
|
}
|
|
break;
|
|
}
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
|
|
d2dRenderTarget->DrawLine(p11, p12, brush1);
|
|
d2dRenderTarget->DrawLine(p21, p22, brush2);
|
|
}
|
|
|
|
void Gui3DSplitterElementRenderer::OnElementStateChanged()
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
Color color1=element->GetColor1();
|
|
Color color2=element->GetColor2();
|
|
if(oldColor1!=color1 || oldColor2!=color2)
|
|
{
|
|
DestroyBrush(renderTarget);
|
|
CreateBrush(renderTarget);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiSolidBackgroundElementRenderer
|
|
***********************************************************************/
|
|
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiSolidBackgroundElementRenderer)
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiSolidBackgroundElementRenderer)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
d2dRenderTarget->FillRectangle(
|
|
D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
|
|
brush
|
|
);
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
d2dRenderTarget->FillEllipse(
|
|
D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f),
|
|
brush
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiGradientBackgroundElementRenderer
|
|
***********************************************************************/
|
|
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER_LINEAR_GRADIENT_BRUSH(GuiGradientBackgroundElementRenderer)
|
|
IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiGradientBackgroundElementRenderer)
|
|
{
|
|
D2D1_POINT_2F points[2];
|
|
switch(element->GetDirection())
|
|
{
|
|
case GuiGradientBackgroundElement::Horizontal:
|
|
{
|
|
points[0].x=(FLOAT)bounds.x1;
|
|
points[0].y=(FLOAT)bounds.y1;
|
|
points[1].x=(FLOAT)bounds.x2;
|
|
points[1].y=(FLOAT)bounds.y1;
|
|
}
|
|
break;
|
|
case GuiGradientBackgroundElement::Vertical:
|
|
{
|
|
points[0].x=(FLOAT)bounds.x1;
|
|
points[0].y=(FLOAT)bounds.y1;
|
|
points[1].x=(FLOAT)bounds.x1;
|
|
points[1].y=(FLOAT)bounds.y2;
|
|
}
|
|
break;
|
|
case GuiGradientBackgroundElement::Slash:
|
|
{
|
|
points[0].x=(FLOAT)bounds.x2;
|
|
points[0].y=(FLOAT)bounds.y1;
|
|
points[1].x=(FLOAT)bounds.x1;
|
|
points[1].y=(FLOAT)bounds.y2;
|
|
}
|
|
break;
|
|
case GuiGradientBackgroundElement::Backslash:
|
|
{
|
|
points[0].x=(FLOAT)bounds.x1;
|
|
points[0].y=(FLOAT)bounds.y1;
|
|
points[1].x=(FLOAT)bounds.x2;
|
|
points[1].y=(FLOAT)bounds.y2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
brush->SetStartPoint(points[0]);
|
|
brush->SetEndPoint(points[1]);
|
|
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
switch(element->GetShape())
|
|
{
|
|
case ElementShape::Rectangle:
|
|
d2dRenderTarget->FillRectangle(
|
|
D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
|
|
brush
|
|
);
|
|
break;
|
|
case ElementShape::Ellipse:
|
|
d2dRenderTarget->FillEllipse(
|
|
D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f),
|
|
brush
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiSolidLabelElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiSolidLabelElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
oldColor=element->GetColor();
|
|
brush=_renderTarget->CreateDirect2DBrush(oldColor);
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget && brush)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldColor);
|
|
brush=0;
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::CreateTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
|
|
oldFont=element->GetFont();
|
|
textFormat=resourceManager->CreateDirect2DTextFormat(oldFont);
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::DestroyTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget && textFormat)
|
|
{
|
|
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
|
|
resourceManager->DestroyDirect2DTextFormat(oldFont);
|
|
textFormat=0;
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::CreateTextLayout()
|
|
{
|
|
if(textFormat)
|
|
{
|
|
HRESULT hr=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout(
|
|
oldText.Buffer(),
|
|
(int)oldText.Length(),
|
|
textFormat->textFormat.Obj(),
|
|
0,
|
|
0,
|
|
&textLayout);
|
|
if(!FAILED(hr))
|
|
{
|
|
if(oldFont.underline)
|
|
{
|
|
DWRITE_TEXT_RANGE textRange;
|
|
textRange.startPosition=0;
|
|
textRange.length=(int)oldText.Length();
|
|
textLayout->SetUnderline(TRUE, textRange);
|
|
}
|
|
if(oldFont.strikeline)
|
|
{
|
|
DWRITE_TEXT_RANGE textRange;
|
|
textRange.startPosition=0;
|
|
textRange.length=(int)oldText.Length();
|
|
textLayout->SetStrikethrough(TRUE, textRange);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
textLayout=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::DestroyTextLayout()
|
|
{
|
|
if(textLayout)
|
|
{
|
|
textLayout->Release();
|
|
textLayout=0;
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::UpdateMinSize()
|
|
{
|
|
float maxWidth=0;
|
|
DestroyTextLayout();
|
|
bool calculateSizeFromTextLayout=false;
|
|
if(renderTarget)
|
|
{
|
|
if(element->GetWrapLine())
|
|
{
|
|
if(element->GetWrapLineHeightCalculation())
|
|
{
|
|
CreateTextLayout();
|
|
if(textLayout)
|
|
{
|
|
maxWidth=textLayout->GetMaxWidth();
|
|
if(oldMaxWidth!=-1)
|
|
{
|
|
textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
|
|
textLayout->SetMaxWidth((float)oldMaxWidth);
|
|
}
|
|
calculateSizeFromTextLayout=true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreateTextLayout();
|
|
if(textLayout)
|
|
{
|
|
maxWidth=textLayout->GetMaxWidth();
|
|
calculateSizeFromTextLayout=true;
|
|
}
|
|
}
|
|
}
|
|
if(calculateSizeFromTextLayout)
|
|
{
|
|
DWRITE_TEXT_METRICS metrics;
|
|
HRESULT hr=textLayout->GetMetrics(&metrics);
|
|
if(!FAILED(hr))
|
|
{
|
|
vint width=0;
|
|
if(!element->GetEllipse() && !element->GetWrapLine() && !element->GetMultiline())
|
|
{
|
|
width=(vint)ceil(metrics.widthIncludingTrailingWhitespace);
|
|
}
|
|
minSize=Size(width, (vint)ceil(metrics.height));
|
|
}
|
|
textLayout->SetMaxWidth(maxWidth);
|
|
}
|
|
else
|
|
{
|
|
minSize=Size();
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::FinalizeInternal()
|
|
{
|
|
DestroyTextLayout();
|
|
DestroyBrush(renderTarget);
|
|
DestroyTextFormat(renderTarget);
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
DestroyBrush(oldRenderTarget);
|
|
DestroyTextFormat(oldRenderTarget);
|
|
CreateBrush(newRenderTarget);
|
|
CreateTextFormat(newRenderTarget);
|
|
UpdateMinSize();
|
|
}
|
|
|
|
GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer()
|
|
:brush(0)
|
|
,textFormat(0)
|
|
,textLayout(0)
|
|
,oldText(L"")
|
|
,oldMaxWidth(-1)
|
|
{
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(!textLayout)
|
|
{
|
|
CreateTextLayout();
|
|
}
|
|
|
|
vint x=0;
|
|
vint y=0;
|
|
switch(element->GetHorizontalAlignment())
|
|
{
|
|
case Alignment::Left:
|
|
x=bounds.Left();
|
|
break;
|
|
case Alignment::Center:
|
|
x=bounds.Left()+(bounds.Width()-minSize.x)/2;
|
|
break;
|
|
case Alignment::Right:
|
|
x=bounds.Right()-minSize.x;
|
|
break;
|
|
}
|
|
switch(element->GetVerticalAlignment())
|
|
{
|
|
case Alignment::Top:
|
|
y=bounds.Top();
|
|
break;
|
|
case Alignment::Center:
|
|
y=bounds.Top()+(bounds.Height()-minSize.y)/2;
|
|
break;
|
|
case Alignment::Bottom:
|
|
y=bounds.Bottom()-minSize.y;
|
|
break;
|
|
}
|
|
|
|
renderTarget->SetTextAntialias(oldFont.antialias, oldFont.verticalAntialias);
|
|
|
|
if(!element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine())
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
d2dRenderTarget->DrawTextLayout(
|
|
D2D1::Point2F((FLOAT)x, (FLOAT)y),
|
|
textLayout,
|
|
brush,
|
|
D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
|
|
);
|
|
}
|
|
else
|
|
{
|
|
IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
|
|
DWRITE_TRIMMING trimming;
|
|
IDWriteInlineObject* inlineObject;
|
|
textLayout->GetTrimming(&trimming, &inlineObject);
|
|
|
|
textLayout->SetWordWrapping(element->GetWrapLine()?DWRITE_WORD_WRAPPING_WRAP:DWRITE_WORD_WRAPPING_NO_WRAP);
|
|
if(element->GetEllipse())
|
|
{
|
|
textLayout->SetTrimming(&textFormat->trimming, textFormat->ellipseInlineObject.Obj());
|
|
}
|
|
switch(element->GetHorizontalAlignment())
|
|
{
|
|
case Alignment::Left:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
|
|
break;
|
|
case Alignment::Center:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
|
|
break;
|
|
case Alignment::Right:
|
|
textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
|
|
break;
|
|
}
|
|
if(!element->GetMultiline() && !element->GetWrapLine())
|
|
{
|
|
switch(element->GetVerticalAlignment())
|
|
{
|
|
case Alignment::Top:
|
|
textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
|
|
break;
|
|
case Alignment::Center:
|
|
textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
|
|
break;
|
|
case Alignment::Bottom:
|
|
textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Rect textBounds=bounds;
|
|
if(element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine())
|
|
{
|
|
textBounds=Rect(Point(textBounds.x1, y), Size(bounds.Width(), minSize.y));
|
|
}
|
|
|
|
textLayout->SetMaxWidth((FLOAT)textBounds.Width());
|
|
textLayout->SetMaxHeight((FLOAT)textBounds.Height());
|
|
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
d2dRenderTarget->DrawTextLayout(
|
|
D2D1::Point2F((FLOAT)textBounds.Left(), (FLOAT)textBounds.Top()),
|
|
textLayout,
|
|
brush,
|
|
D2D1_DRAW_TEXT_OPTIONS_NO_SNAP
|
|
);
|
|
|
|
textLayout->SetTrimming(&trimming, inlineObject);
|
|
if(oldMaxWidth!=textBounds.Width())
|
|
{
|
|
oldMaxWidth=textBounds.Width();
|
|
UpdateMinSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiSolidLabelElementRenderer::OnElementStateChanged()
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
Color color=element->GetColor();
|
|
if(oldColor!=color)
|
|
{
|
|
DestroyBrush(renderTarget);
|
|
CreateBrush(renderTarget);
|
|
}
|
|
|
|
FontProperties font=element->GetFont();
|
|
if(oldFont!=font)
|
|
{
|
|
DestroyTextFormat(renderTarget);
|
|
CreateTextFormat(renderTarget);
|
|
}
|
|
}
|
|
oldText=element->GetText();
|
|
UpdateMinSize();
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiImageFrameElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiImageFrameElementRenderer::UpdateBitmap(IWindowsDirect2DRenderTarget* renderTarget)
|
|
{
|
|
if(renderTarget && element->GetImage())
|
|
{
|
|
INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
|
|
bitmap=renderTarget->GetBitmap(frame, element->GetEnabled());
|
|
|
|
if (element->GetStretch())
|
|
{
|
|
minSize=Size(0,0);
|
|
}
|
|
else
|
|
{
|
|
minSize=frame->GetSize();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bitmap=0;
|
|
minSize=Size(0, 0);
|
|
}
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::FinalizeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
UpdateBitmap(newRenderTarget);
|
|
}
|
|
|
|
GuiImageFrameElementRenderer::GuiImageFrameElementRenderer()
|
|
{
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(bitmap)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
D2D1_RECT_F source=D2D1::RectF(0, 0, (FLOAT)minSize.x, (FLOAT)minSize.y);
|
|
D2D1_RECT_F destination;
|
|
if(element->GetStretch())
|
|
{
|
|
INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex());
|
|
auto size = frame->GetSize();
|
|
source = D2D1::RectF(0, 0, (FLOAT)size.x, (FLOAT)size.y);
|
|
destination=D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2);
|
|
}
|
|
else
|
|
{
|
|
vint x=0;
|
|
vint y=0;
|
|
switch(element->GetHorizontalAlignment())
|
|
{
|
|
case Alignment::Left:
|
|
x=bounds.Left();
|
|
break;
|
|
case Alignment::Center:
|
|
x=bounds.Left()+(bounds.Width()-minSize.x)/2;
|
|
break;
|
|
case Alignment::Right:
|
|
x=bounds.Right()-minSize.x;
|
|
break;
|
|
}
|
|
switch(element->GetVerticalAlignment())
|
|
{
|
|
case Alignment::Top:
|
|
y=bounds.Top();
|
|
break;
|
|
case Alignment::Center:
|
|
y=bounds.Top()+(bounds.Height()-minSize.y)/2;
|
|
break;
|
|
case Alignment::Bottom:
|
|
y=bounds.Bottom()-minSize.y;
|
|
break;
|
|
}
|
|
destination=D2D1::RectF((FLOAT)x, (FLOAT)y, (FLOAT)(x+minSize.x), (FLOAT)(y+minSize.y));
|
|
}
|
|
|
|
ID2D1DeviceContext* d2dDeviceContext = nullptr;
|
|
{
|
|
HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
if (d2dDeviceContext)
|
|
{
|
|
d2dDeviceContext->Release();
|
|
}
|
|
d2dDeviceContext = nullptr;
|
|
}
|
|
}
|
|
|
|
if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0)
|
|
{
|
|
vint max = element->GetFrameIndex();
|
|
for (vint i = 0; i <= max; i++)
|
|
{
|
|
ComPtr<ID2D1Bitmap> frameBitmap=renderTarget->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled());
|
|
if (d2dDeviceContext)
|
|
{
|
|
d2dDeviceContext->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source);
|
|
}
|
|
else
|
|
{
|
|
d2dRenderTarget->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (d2dDeviceContext)
|
|
{
|
|
d2dDeviceContext->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source);
|
|
}
|
|
else
|
|
{
|
|
d2dRenderTarget->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source);
|
|
}
|
|
}
|
|
|
|
if (d2dDeviceContext)
|
|
{
|
|
d2dDeviceContext->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiImageFrameElementRenderer::OnElementStateChanged()
|
|
{
|
|
UpdateBitmap(renderTarget);
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiPolygonElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiPolygonElementRenderer::CreateGeometry()
|
|
{
|
|
oldPoints.Resize(element->GetPointCount());
|
|
if(oldPoints.Count()>0)
|
|
{
|
|
memcpy(&oldPoints[0], &element->GetPoint(0), sizeof(Point)*element->GetPointCount());
|
|
}
|
|
if(oldPoints.Count()>=3)
|
|
{
|
|
ID2D1PathGeometry* pg=0;
|
|
GetWindowsDirect2DObjectProvider()->GetDirect2DFactory()->CreatePathGeometry(&pg);
|
|
if(pg)
|
|
{
|
|
geometry=pg;
|
|
FillGeometry(Point(0, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::DestroyGeometry()
|
|
{
|
|
if(geometry)
|
|
{
|
|
geometry=0;
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::FillGeometry(Point offset)
|
|
{
|
|
if(geometry)
|
|
{
|
|
ID2D1GeometrySink* pgs=0;
|
|
geometry->Open(&pgs);
|
|
if(pgs)
|
|
{
|
|
D2D1_POINT_2F p;
|
|
p.x=(FLOAT)(oldPoints[0].x+offset.x)+0.5f;
|
|
p.y=(FLOAT)(oldPoints[0].y+offset.y)+0.5f;
|
|
pgs->BeginFigure(p, D2D1_FIGURE_BEGIN_FILLED);
|
|
for(vint i=1;i<oldPoints.Count();i++)
|
|
{
|
|
p.x=(FLOAT)(oldPoints[i].x+offset.x)+0.5f;
|
|
p.y=(FLOAT)(oldPoints[i].y+offset.y)+0.5f;
|
|
pgs->AddLine(p);
|
|
}
|
|
pgs->EndFigure(D2D1_FIGURE_END_CLOSED);
|
|
pgs->Close();
|
|
pgs->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::RecreateResource(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
if(oldRenderTarget==newRenderTarget)
|
|
{
|
|
if(oldRenderTarget)
|
|
{
|
|
if(oldBorderColor!=element->GetBorderColor())
|
|
{
|
|
oldRenderTarget->DestroyDirect2DBrush(oldBorderColor);
|
|
oldBorderColor=element->GetBorderColor();
|
|
borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor);
|
|
}
|
|
if(oldBackgroundColor!=element->GetBackgroundColor())
|
|
{
|
|
oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor);
|
|
oldBackgroundColor=element->GetBackgroundColor();
|
|
backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(oldRenderTarget)
|
|
{
|
|
oldRenderTarget->DestroyDirect2DBrush(oldBorderColor);
|
|
oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor);
|
|
}
|
|
if(newRenderTarget)
|
|
{
|
|
oldBorderColor=element->GetBorderColor();
|
|
oldBackgroundColor=element->GetBackgroundColor();
|
|
borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor);
|
|
backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::InitializeInternal()
|
|
{
|
|
oldBorderColor=element->GetBorderColor();
|
|
oldBackgroundColor=element->GetBackgroundColor();
|
|
RecreateResource(0, renderTarget);
|
|
CreateGeometry();
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::FinalizeInternal()
|
|
{
|
|
DestroyGeometry();
|
|
RecreateResource(renderTarget, 0);
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
RecreateResource(oldRenderTarget, newRenderTarget);
|
|
}
|
|
|
|
GuiPolygonElementRenderer::GuiPolygonElementRenderer()
|
|
:borderBrush(0)
|
|
,backgroundBrush(0)
|
|
,geometry(0)
|
|
{
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(renderTarget && geometry)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1;
|
|
vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1;
|
|
|
|
D2D1_MATRIX_3X2_F oldT, newT;
|
|
d2dRenderTarget->GetTransform(&oldT);
|
|
newT=D2D1::Matrix3x2F::Translation((FLOAT)offsetX, (FLOAT)offsetY);
|
|
d2dRenderTarget->SetTransform(&newT);
|
|
|
|
d2dRenderTarget->FillGeometry(geometry.Obj(), backgroundBrush);
|
|
d2dRenderTarget->DrawGeometry(geometry.Obj(), borderBrush);
|
|
d2dRenderTarget->SetTransform(&oldT);
|
|
}
|
|
}
|
|
|
|
void GuiPolygonElementRenderer::OnElementStateChanged()
|
|
{
|
|
minSize=element->GetSize();
|
|
RecreateResource(renderTarget, renderTarget);
|
|
|
|
bool polygonModified=false;
|
|
if(oldPoints.Count()!=element->GetPointCount())
|
|
{
|
|
polygonModified=true;
|
|
}
|
|
else
|
|
{
|
|
for(vint i=0;i<oldPoints.Count();i++)
|
|
{
|
|
if(oldPoints[i]!=element->GetPoint(i))
|
|
{
|
|
polygonModified=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(polygonModified)
|
|
{
|
|
DestroyGeometry();
|
|
CreateGeometry();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiColorizedTextElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiColorizedTextElementRenderer::CreateTextBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
colors.Resize(element->GetColors().Count());
|
|
for(vint i=0;i<colors.Count();i++)
|
|
{
|
|
text::ColorEntry entry=element->GetColors().Get(i);
|
|
ColorEntryResource newEntry;
|
|
|
|
newEntry.normal.text=entry.normal.text;
|
|
newEntry.normal.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.normal.text);
|
|
newEntry.normal.background=entry.normal.background;
|
|
newEntry.normal.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.normal.background);
|
|
newEntry.selectedFocused.text=entry.selectedFocused.text;
|
|
newEntry.selectedFocused.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedFocused.text);
|
|
newEntry.selectedFocused.background=entry.selectedFocused.background;
|
|
newEntry.selectedFocused.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedFocused.background);
|
|
newEntry.selectedUnfocused.text=entry.selectedUnfocused.text;
|
|
newEntry.selectedUnfocused.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedUnfocused.text);
|
|
newEntry.selectedUnfocused.background=entry.selectedUnfocused.background;
|
|
newEntry.selectedUnfocused.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedUnfocused.background);
|
|
colors[i]=newEntry;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::DestroyTextBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
for(vint i=0;i<colors.Count();i++)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].normal.text);
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].normal.background);
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].selectedFocused.text);
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].selectedFocused.background);
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].selectedUnfocused.text);
|
|
_renderTarget->DestroyDirect2DBrush(colors[i].selectedUnfocused.background);
|
|
}
|
|
colors.Resize(0);
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::CreateCaretBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
oldCaretColor=element->GetCaretColor();
|
|
caretBrush=_renderTarget->CreateDirect2DBrush(oldCaretColor);
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::DestroyCaretBrush(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
{
|
|
if(_renderTarget)
|
|
{
|
|
if(caretBrush)
|
|
{
|
|
_renderTarget->DestroyDirect2DBrush(oldCaretColor);
|
|
caretBrush=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::ColorChanged()
|
|
{
|
|
DestroyTextBrush(renderTarget);
|
|
CreateTextBrush(renderTarget);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::FontChanged()
|
|
{
|
|
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
|
|
if(textFormat)
|
|
{
|
|
resourceManager->DestroyDirect2DTextFormat(oldFont);
|
|
resourceManager->DestroyDirect2DCharMeasurer(oldFont);
|
|
}
|
|
oldFont=element->GetFont();
|
|
textFormat=resourceManager->CreateDirect2DTextFormat(oldFont);
|
|
element->GetLines().SetCharMeasurer(resourceManager->CreateDirect2DCharMeasurer(oldFont).Obj());
|
|
}
|
|
|
|
text::CharMeasurer* GuiColorizedTextElementRenderer::GetCharMeasurer()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::InitializeInternal()
|
|
{
|
|
textFormat=0;
|
|
caretBrush=0;
|
|
element->SetCallback(this);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::FinalizeInternal()
|
|
{
|
|
DestroyTextBrush(renderTarget);
|
|
DestroyCaretBrush(renderTarget);
|
|
|
|
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
|
|
if(textFormat)
|
|
{
|
|
resourceManager->DestroyDirect2DTextFormat(oldFont);
|
|
resourceManager->DestroyDirect2DCharMeasurer(oldFont);
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
DestroyTextBrush(oldRenderTarget);
|
|
DestroyCaretBrush(oldRenderTarget);
|
|
CreateTextBrush(newRenderTarget);
|
|
CreateCaretBrush(newRenderTarget);
|
|
element->GetLines().SetRenderTarget(newRenderTarget);
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
|
|
wchar_t passwordChar=element->GetPasswordChar();
|
|
Point viewPosition=element->GetViewPosition();
|
|
Rect viewBounds(viewPosition, bounds.GetSize());
|
|
vint startRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, viewBounds.y1)).row;
|
|
vint endRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, viewBounds.y2)).row;
|
|
TextPos selectionBegin=element->GetCaretBegin()<element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd();
|
|
TextPos selectionEnd=element->GetCaretBegin()>element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd();
|
|
bool focused=element->GetFocused();
|
|
|
|
renderTarget->SetTextAntialias(oldFont.antialias, oldFont.verticalAntialias);
|
|
|
|
for(vint row=startRow;row<=endRow;row++)
|
|
{
|
|
Rect startRect=element->GetLines().GetRectFromTextPos(TextPos(row, 0));
|
|
Point startPoint=startRect.LeftTop();
|
|
vint startColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, startPoint.y)).column;
|
|
vint endColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, startPoint.y)).column;
|
|
text::TextLine& line=element->GetLines().GetLine(row);
|
|
|
|
vint x=startColumn==0?0:line.att[startColumn-1].rightOffset;
|
|
for(vint column=startColumn; column<=endColumn; column++)
|
|
{
|
|
bool inSelection=false;
|
|
if(selectionBegin.row==selectionEnd.row)
|
|
{
|
|
inSelection=(row==selectionBegin.row && selectionBegin.column<=column && column<selectionEnd.column);
|
|
}
|
|
else if(row==selectionBegin.row)
|
|
{
|
|
inSelection=selectionBegin.column<=column;
|
|
}
|
|
else if(row==selectionEnd.row)
|
|
{
|
|
inSelection=column<selectionEnd.column;
|
|
}
|
|
else
|
|
{
|
|
inSelection=selectionBegin.row<row && row<selectionEnd.row;
|
|
}
|
|
|
|
bool crlf=column==line.dataLength;
|
|
vint colorIndex=crlf?0:line.att[column].colorIndex;
|
|
if(colorIndex>=colors.Count())
|
|
{
|
|
colorIndex=0;
|
|
}
|
|
ColorItemResource& color=
|
|
!inSelection?colors[colorIndex].normal:
|
|
focused?colors[colorIndex].selectedFocused:
|
|
colors[colorIndex].selectedUnfocused;
|
|
vint x2=crlf?x+startRect.Height()/2:line.att[column].rightOffset;
|
|
vint tx=x-viewPosition.x+bounds.x1;
|
|
vint ty=startPoint.y-viewPosition.y+bounds.y1;
|
|
|
|
if(color.background.a>0)
|
|
{
|
|
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)tx, (FLOAT)ty, (FLOAT)(tx+(x2-x)), (FLOAT)(ty+startRect.Height())), color.backgroundBrush);
|
|
}
|
|
if(!crlf)
|
|
{
|
|
d2dRenderTarget->DrawText(
|
|
(passwordChar?&passwordChar:&line.text[column]),
|
|
1,
|
|
textFormat->textFormat.Obj(),
|
|
D2D1::RectF((FLOAT)tx, (FLOAT)ty, (FLOAT)tx+1, (FLOAT)ty+1),
|
|
color.textBrush,
|
|
D2D1_DRAW_TEXT_OPTIONS_NO_SNAP,
|
|
DWRITE_MEASURING_MODE_GDI_NATURAL
|
|
);
|
|
}
|
|
x=x2;
|
|
}
|
|
}
|
|
|
|
if(element->GetCaretVisible() && element->GetLines().IsAvailable(element->GetCaretEnd()))
|
|
{
|
|
Point caretPoint=element->GetLines().GetPointFromTextPos(element->GetCaretEnd());
|
|
vint height=element->GetLines().GetRowHeight();
|
|
Point p1(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1);
|
|
Point p2(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1);
|
|
d2dRenderTarget->DrawLine(
|
|
D2D1::Point2F((FLOAT)p1.x+0.5f, (FLOAT)p1.y),
|
|
D2D1::Point2F((FLOAT)p2.x+0.5f, (FLOAT)p2.y),
|
|
caretBrush
|
|
);
|
|
d2dRenderTarget->DrawLine(
|
|
D2D1::Point2F((FLOAT)p1.x-0.5f, (FLOAT)p1.y),
|
|
D2D1::Point2F((FLOAT)p2.x-0.5f, (FLOAT)p2.y),
|
|
caretBrush
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiColorizedTextElementRenderer::OnElementStateChanged()
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
Color caretColor=element->GetCaretColor();
|
|
if(oldCaretColor!=caretColor)
|
|
{
|
|
DestroyCaretBrush(renderTarget);
|
|
CreateCaretBrush(renderTarget);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GuiDirect2DElementRenderer
|
|
***********************************************************************/
|
|
|
|
void GuiDirect2DElementRenderer::InitializeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiDirect2DElementRenderer::FinalizeInternal()
|
|
{
|
|
}
|
|
|
|
void GuiDirect2DElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
|
|
{
|
|
IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
|
|
ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory();
|
|
if(oldRenderTarget)
|
|
{
|
|
GuiDirect2DElementEventArgs arguments(element, oldRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect());
|
|
element->BeforeRenderTargetChanged.Execute(arguments);
|
|
}
|
|
if(newRenderTarget)
|
|
{
|
|
GuiDirect2DElementEventArgs arguments(element, newRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect());
|
|
element->AfterRenderTargetChanged.Execute(arguments);
|
|
}
|
|
}
|
|
|
|
GuiDirect2DElementRenderer::GuiDirect2DElementRenderer()
|
|
{
|
|
}
|
|
|
|
GuiDirect2DElementRenderer::~GuiDirect2DElementRenderer()
|
|
{
|
|
}
|
|
|
|
void GuiDirect2DElementRenderer::Render(Rect bounds)
|
|
{
|
|
if(renderTarget)
|
|
{
|
|
IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
|
|
ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory();
|
|
renderTarget->PushClipper(bounds);
|
|
if(!renderTarget->IsClipperCoverWholeTarget())
|
|
{
|
|
ID2D1RenderTarget* rt=renderTarget->GetDirect2DRenderTarget();
|
|
GuiDirect2DElementEventArgs arguments(element, rt, fdw, fd2d, bounds);
|
|
element->Rendering.Execute(arguments);
|
|
}
|
|
renderTarget->PopClipper();
|
|
}
|
|
}
|
|
|
|
void GuiDirect2DElementRenderer::OnElementStateChanged()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSWINDOWSDIRECT2D.CPP
|
|
***********************************************************************/
|
|
|
|
namespace vl
|
|
{
|
|
namespace presentation
|
|
{
|
|
namespace elements
|
|
{
|
|
|
|
/***********************************************************************
|
|
GuiDirect2DElement
|
|
***********************************************************************/
|
|
|
|
GuiDirect2DElement::GuiDirect2DElement()
|
|
{
|
|
}
|
|
|
|
GuiDirect2DElement::~GuiDirect2DElement()
|
|
{
|
|
}
|
|
}
|
|
|
|
namespace elements_windows_d2d
|
|
{
|
|
using namespace elements;
|
|
using namespace collections;
|
|
|
|
D2D1::ColorF GetD2DColor(Color color)
|
|
{
|
|
return D2D1::ColorF(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a/255.0f);
|
|
}
|
|
|
|
/***********************************************************************
|
|
CachedResourceAllocator
|
|
***********************************************************************/
|
|
|
|
class CachedSolidBrushAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, ComPtr<ID2D1SolidColorBrush>)
|
|
|
|
IWindowsDirect2DRenderTarget* guiRenderTarget;
|
|
public:
|
|
CachedSolidBrushAllocator()
|
|
:guiRenderTarget(0)
|
|
{
|
|
}
|
|
|
|
void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
|
|
{
|
|
guiRenderTarget=_guiRenderTarget;
|
|
}
|
|
|
|
ComPtr<ID2D1SolidColorBrush> CreateInternal(Color color)
|
|
{
|
|
ID2D1SolidColorBrush* brush=0;
|
|
ID2D1RenderTarget* renderTarget=guiRenderTarget->GetDirect2DRenderTarget();
|
|
HRESULT hr=renderTarget->CreateSolidColorBrush(GetD2DColor(color), &brush);
|
|
if(!FAILED(hr))
|
|
{
|
|
return brush;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
class CachedLinearBrushAllocator
|
|
{
|
|
typedef Pair<Color, Color> ColorPair;
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(ColorPair, ComPtr<ID2D1LinearGradientBrush>)
|
|
|
|
IWindowsDirect2DRenderTarget* guiRenderTarget;
|
|
public:
|
|
CachedLinearBrushAllocator()
|
|
:guiRenderTarget(0)
|
|
{
|
|
}
|
|
|
|
void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
|
|
{
|
|
guiRenderTarget=_guiRenderTarget;
|
|
}
|
|
|
|
ComPtr<ID2D1LinearGradientBrush> CreateInternal(ColorPair colors)
|
|
{
|
|
ID2D1RenderTarget* renderTarget=guiRenderTarget->GetDirect2DRenderTarget();
|
|
ID2D1GradientStopCollection* stopCollection=0;
|
|
{
|
|
D2D1_GRADIENT_STOP stops[2];
|
|
stops[0].color=GetD2DColor(colors.key);
|
|
stops[0].position=0.0f;
|
|
stops[1].color=GetD2DColor(colors.value);
|
|
stops[1].position=1.0f;
|
|
|
|
HRESULT hr=renderTarget->CreateGradientStopCollection(
|
|
stops,
|
|
2,
|
|
D2D1_GAMMA_2_2,
|
|
D2D1_EXTEND_MODE_CLAMP,
|
|
&stopCollection);
|
|
if(FAILED(hr)) return 0;
|
|
}
|
|
|
|
ID2D1LinearGradientBrush* brush=0;
|
|
{
|
|
D2D1_POINT_2F points[2]={{0, 0}, {0, 0}};
|
|
HRESULT hr=renderTarget->CreateLinearGradientBrush(
|
|
D2D1::LinearGradientBrushProperties(points[0], points[1]),
|
|
stopCollection,
|
|
&brush);
|
|
stopCollection->Release();
|
|
if(FAILED(hr)) return 0;
|
|
}
|
|
return brush;
|
|
}
|
|
};
|
|
|
|
class CachedTextFormatAllocator
|
|
{
|
|
private:
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr<Direct2DTextFormatPackage>)
|
|
public:
|
|
|
|
static ComPtr<IDWriteTextFormat> CreateDirect2DFont(const FontProperties& fontProperties)
|
|
{
|
|
IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
|
|
IDWriteTextFormat* format=0;
|
|
HRESULT hr=dwriteFactory->CreateTextFormat(
|
|
fontProperties.fontFamily.Buffer(),
|
|
NULL,
|
|
(fontProperties.bold?DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL),
|
|
(fontProperties.italic?DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL),
|
|
DWRITE_FONT_STRETCH_NORMAL,
|
|
(FLOAT)fontProperties.size,
|
|
L"",
|
|
&format);
|
|
if(!FAILED(hr))
|
|
{
|
|
format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
|
|
return format;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Ptr<Direct2DTextFormatPackage> CreateInternal(const FontProperties& fontProperties)
|
|
{
|
|
Ptr<Direct2DTextFormatPackage> textFormat=new Direct2DTextFormatPackage;
|
|
textFormat->textFormat=CreateDirect2DFont(fontProperties);
|
|
textFormat->trimming.granularity=DWRITE_TRIMMING_GRANULARITY_CHARACTER;
|
|
textFormat->trimming.delimiter=0;
|
|
textFormat->trimming.delimiterCount=0;
|
|
|
|
IDWriteInlineObject* ellipseInlineObject=0;
|
|
GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateEllipsisTrimmingSign(textFormat->textFormat.Obj(), &ellipseInlineObject);
|
|
textFormat->ellipseInlineObject=ellipseInlineObject;
|
|
return textFormat;
|
|
}
|
|
};
|
|
|
|
class CachedCharMeasurerAllocator
|
|
{
|
|
DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr<text::CharMeasurer>)
|
|
|
|
protected:
|
|
class Direct2DCharMeasurer : public text::CharMeasurer
|
|
{
|
|
protected:
|
|
ComPtr<IDWriteTextFormat> font;
|
|
vint size;
|
|
|
|
Size MeasureInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
Size charSize(0, 0);
|
|
IDWriteTextLayout* textLayout=0;
|
|
HRESULT hr=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout(
|
|
&character,
|
|
1,
|
|
font.Obj(),
|
|
0,
|
|
0,
|
|
&textLayout);
|
|
if(!FAILED(hr))
|
|
{
|
|
DWRITE_TEXT_METRICS metrics;
|
|
hr=textLayout->GetMetrics(&metrics);
|
|
if(!FAILED(hr))
|
|
{
|
|
charSize=Size((vint)ceil(metrics.widthIncludingTrailingWhitespace), (vint)ceil(metrics.height));
|
|
}
|
|
textLayout->Release();
|
|
}
|
|
return charSize;
|
|
}
|
|
|
|
vint MeasureWidthInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
return MeasureInternal(character, renderTarget).x;
|
|
}
|
|
|
|
vint GetRowHeightInternal(IGuiGraphicsRenderTarget* renderTarget)
|
|
{
|
|
return MeasureInternal(L' ', renderTarget).y;
|
|
}
|
|
public:
|
|
Direct2DCharMeasurer(ComPtr<IDWriteTextFormat> _font, vint _size)
|
|
:text::CharMeasurer(_size)
|
|
,size(_size)
|
|
,font(_font)
|
|
{
|
|
}
|
|
};
|
|
public:
|
|
Ptr<text::CharMeasurer> CreateInternal(const FontProperties& value)
|
|
{
|
|
return new Direct2DCharMeasurer(CachedTextFormatAllocator::CreateDirect2DFont(value), value.size);
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsDirect2DRenderTarget
|
|
***********************************************************************/
|
|
|
|
class WindowsDirect2DImageFrameCache : public Object, public INativeImageFrameCache
|
|
{
|
|
protected:
|
|
IWindowsDirect2DRenderTarget* renderTarget;
|
|
INativeImageFrame* cachedFrame;
|
|
ComPtr<ID2D1Bitmap> bitmap;
|
|
ComPtr<ID2D1Bitmap> disabledBitmap;
|
|
public:
|
|
WindowsDirect2DImageFrameCache(IWindowsDirect2DRenderTarget* _renderTarget)
|
|
:renderTarget(_renderTarget)
|
|
{
|
|
}
|
|
|
|
~WindowsDirect2DImageFrameCache()
|
|
{
|
|
}
|
|
|
|
void OnAttach(INativeImageFrame* frame)override
|
|
{
|
|
cachedFrame=frame;
|
|
ID2D1Bitmap* d2dBitmap=0;
|
|
HRESULT hr=renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap(
|
|
GetWindowsDirect2DObjectProvider()->GetWICBitmap(frame),
|
|
&d2dBitmap
|
|
);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
bitmap=d2dBitmap;
|
|
}
|
|
}
|
|
|
|
void OnDetach(INativeImageFrame* frame)override
|
|
{
|
|
renderTarget->DestroyBitmapCache(cachedFrame);
|
|
}
|
|
|
|
INativeImageFrame* GetFrame()
|
|
{
|
|
return cachedFrame;
|
|
}
|
|
|
|
ComPtr<ID2D1Bitmap> GetBitmap(bool enabled)
|
|
{
|
|
if(enabled)
|
|
{
|
|
return bitmap;
|
|
}
|
|
else
|
|
{
|
|
if(!disabledBitmap)
|
|
{
|
|
IWICBitmap* frameBitmap=GetWindowsDirect2DObjectProvider()->GetWICBitmap(cachedFrame);
|
|
ID2D1Bitmap* d2dBitmap=0;
|
|
HRESULT hr=renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap(
|
|
frameBitmap,
|
|
&d2dBitmap
|
|
);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
disabledBitmap=d2dBitmap;
|
|
|
|
WICRect rect;
|
|
rect.X=0;
|
|
rect.Y=0;
|
|
rect.Width=bitmap->GetPixelSize().width;
|
|
rect.Height=bitmap->GetPixelSize().height;
|
|
BYTE* buffer=new BYTE[rect.Width*rect.Height*4];
|
|
hr=frameBitmap->CopyPixels(&rect, rect.Width*4, rect.Width*rect.Height*4, buffer);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
vint count=rect.Width*rect.Height;
|
|
BYTE* read=buffer;
|
|
for(vint i=0;i<count;i++)
|
|
{
|
|
BYTE g=(read[0]+read[1]+read[2])/6+read[3]/2;
|
|
read[0]=g;
|
|
read[1]=g;
|
|
read[2]=g;
|
|
read+=4;
|
|
}
|
|
D2D1_RECT_U d2dRect;
|
|
d2dRect.left=0;
|
|
d2dRect.top=0;
|
|
d2dRect.right=rect.Width;
|
|
d2dRect.bottom=rect.Height;
|
|
d2dBitmap->CopyFromMemory(&d2dRect, buffer, rect.Width*4);
|
|
}
|
|
delete[] buffer;
|
|
}
|
|
}
|
|
return disabledBitmap;
|
|
}
|
|
}
|
|
};
|
|
|
|
class WindowsDirect2DRenderTarget : public Object, public IWindowsDirect2DRenderTarget
|
|
{
|
|
typedef SortedList<Ptr<WindowsDirect2DImageFrameCache>> ImageCacheList;
|
|
protected:
|
|
INativeWindow* window;
|
|
ID2D1RenderTarget* d2dRenderTarget;
|
|
List<Rect> clippers;
|
|
vint clipperCoverWholeTargetCounter;
|
|
|
|
CachedSolidBrushAllocator solidBrushes;
|
|
CachedLinearBrushAllocator linearBrushes;
|
|
ImageCacheList imageCaches;
|
|
|
|
ComPtr<IDWriteRenderingParams> noAntialiasParams;
|
|
ComPtr<IDWriteRenderingParams> horizontalAntialiasParams;
|
|
ComPtr<IDWriteRenderingParams> bidirectionalAntialiasParams;
|
|
|
|
ComPtr<IDWriteRenderingParams> CreateRenderingParams(DWRITE_RENDERING_MODE renderingMode, IDWriteRenderingParams* defaultParams, IDWriteFactory* dwriteFactory)
|
|
{
|
|
IDWriteRenderingParams* renderingParams=0;
|
|
FLOAT gamma=defaultParams->GetGamma();
|
|
FLOAT enhancedContrast=defaultParams->GetEnhancedContrast();
|
|
FLOAT clearTypeLevel=defaultParams->GetClearTypeLevel();
|
|
DWRITE_PIXEL_GEOMETRY pixelGeometry=defaultParams->GetPixelGeometry();
|
|
HRESULT hr=dwriteFactory->CreateCustomRenderingParams(
|
|
gamma,
|
|
enhancedContrast,
|
|
clearTypeLevel,
|
|
pixelGeometry,
|
|
renderingMode,
|
|
&renderingParams);
|
|
if(!FAILED(hr))
|
|
{
|
|
return renderingParams;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
public:
|
|
WindowsDirect2DRenderTarget(INativeWindow* _window)
|
|
:window(_window)
|
|
,d2dRenderTarget(0)
|
|
,clipperCoverWholeTargetCounter(0)
|
|
{
|
|
solidBrushes.SetRenderTarget(this);
|
|
linearBrushes.SetRenderTarget(this);
|
|
|
|
IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory();
|
|
IDWriteRenderingParams* defaultParams=0;
|
|
HRESULT hr=dwriteFactory->CreateRenderingParams(&defaultParams);
|
|
if(!FAILED(hr))
|
|
{
|
|
noAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, defaultParams, dwriteFactory);
|
|
horizontalAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL, defaultParams, dwriteFactory);
|
|
bidirectionalAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, defaultParams, dwriteFactory);
|
|
defaultParams->Release();
|
|
}
|
|
}
|
|
|
|
~WindowsDirect2DRenderTarget()
|
|
{
|
|
while(imageCaches.Count())
|
|
{
|
|
Ptr<WindowsDirect2DImageFrameCache> cache=imageCaches[imageCaches.Count()-1];
|
|
cache->GetFrame()->RemoveCache(this);
|
|
}
|
|
}
|
|
|
|
ID2D1RenderTarget* GetDirect2DRenderTarget()override
|
|
{
|
|
return d2dRenderTarget ? d2dRenderTarget : GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
|
|
}
|
|
|
|
ComPtr<ID2D1Bitmap> GetBitmap(INativeImageFrame* frame, bool enabled)override
|
|
{
|
|
Ptr<INativeImageFrameCache> cache=frame->GetCache(this);
|
|
if(cache)
|
|
{
|
|
return cache.Cast<WindowsDirect2DImageFrameCache>()->GetBitmap(enabled);
|
|
}
|
|
else
|
|
{
|
|
Ptr<WindowsDirect2DImageFrameCache> d2dCache=new WindowsDirect2DImageFrameCache(this);
|
|
if(frame->SetCache(this, d2dCache))
|
|
{
|
|
imageCaches.Add(d2dCache);
|
|
return d2dCache->GetBitmap(enabled);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DestroyBitmapCache(INativeImageFrame* frame)override
|
|
{
|
|
WindowsDirect2DImageFrameCache* cache=frame->GetCache(this).Cast<WindowsDirect2DImageFrameCache>().Obj();
|
|
imageCaches.Remove(cache);
|
|
}
|
|
|
|
void SetTextAntialias(bool antialias, bool verticalAntialias)override
|
|
{
|
|
ComPtr<IDWriteRenderingParams> params;
|
|
if(!antialias)
|
|
{
|
|
params=noAntialiasParams;
|
|
}
|
|
else if(!verticalAntialias)
|
|
{
|
|
params=horizontalAntialiasParams;
|
|
}
|
|
else
|
|
{
|
|
params=bidirectionalAntialiasParams;
|
|
}
|
|
if(params && d2dRenderTarget)
|
|
{
|
|
d2dRenderTarget->SetTextRenderingParams(params.Obj());
|
|
}
|
|
}
|
|
|
|
void StartRendering()override
|
|
{
|
|
d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
|
|
d2dRenderTarget->BeginDraw();
|
|
d2dRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
|
|
}
|
|
|
|
bool StopRendering()override
|
|
{
|
|
auto result = d2dRenderTarget->EndDraw();
|
|
bool deviceAvailable = false;
|
|
if (result == S_OK)
|
|
{
|
|
deviceAvailable = GetWindowsDirect2DObjectProvider()->PresentRenderTarget(window);
|
|
}
|
|
d2dRenderTarget = 0;
|
|
return deviceAvailable;
|
|
}
|
|
|
|
void PushClipper(Rect clipper)override
|
|
{
|
|
if(clipperCoverWholeTargetCounter>0)
|
|
{
|
|
clipperCoverWholeTargetCounter++;
|
|
}
|
|
else
|
|
{
|
|
Rect previousClipper=GetClipper();
|
|
Rect currentClipper;
|
|
|
|
currentClipper.x1=(previousClipper.x1>clipper.x1?previousClipper.x1:clipper.x1);
|
|
currentClipper.y1=(previousClipper.y1>clipper.y1?previousClipper.y1:clipper.y1);
|
|
currentClipper.x2=(previousClipper.x2<clipper.x2?previousClipper.x2:clipper.x2);
|
|
currentClipper.y2=(previousClipper.y2<clipper.y2?previousClipper.y2:clipper.y2);
|
|
|
|
if(currentClipper.x1<currentClipper.x2 && currentClipper.y1<currentClipper.y2)
|
|
{
|
|
clippers.Add(currentClipper);
|
|
d2dRenderTarget->PushAxisAlignedClip(
|
|
D2D1::RectF((FLOAT)currentClipper.x1, (FLOAT)currentClipper.y1, (FLOAT)currentClipper.x2, (FLOAT)currentClipper.y2),
|
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
clipperCoverWholeTargetCounter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PopClipper()override
|
|
{
|
|
if(clipperCoverWholeTargetCounter>0)
|
|
{
|
|
clipperCoverWholeTargetCounter--;
|
|
}
|
|
else if(clippers.Count()>0)
|
|
{
|
|
clippers.RemoveAt(clippers.Count()-1);
|
|
d2dRenderTarget->PopAxisAlignedClip();
|
|
}
|
|
}
|
|
|
|
Rect GetClipper()override
|
|
{
|
|
if(clippers.Count()==0)
|
|
{
|
|
return Rect(Point(0, 0), window->GetClientSize());
|
|
}
|
|
else
|
|
{
|
|
return clippers[clippers.Count()-1];
|
|
}
|
|
}
|
|
|
|
bool IsClipperCoverWholeTarget()override
|
|
{
|
|
return clipperCoverWholeTargetCounter>0;
|
|
}
|
|
|
|
ID2D1SolidColorBrush* CreateDirect2DBrush(Color color)override
|
|
{
|
|
return solidBrushes.Create(color).Obj();
|
|
}
|
|
|
|
void DestroyDirect2DBrush(Color color)override
|
|
{
|
|
solidBrushes.Destroy(color);
|
|
}
|
|
|
|
ID2D1LinearGradientBrush* CreateDirect2DLinearBrush(Color c1, Color c2)override
|
|
{
|
|
return linearBrushes.Create(Pair<Color, Color>(c1, c2)).Obj();
|
|
}
|
|
|
|
void DestroyDirect2DLinearBrush(Color c1, Color c2)override
|
|
{
|
|
linearBrushes.Destroy(Pair<Color, Color>(c1, c2));
|
|
}
|
|
};
|
|
|
|
/***********************************************************************
|
|
WindowsGDIResourceManager
|
|
***********************************************************************/
|
|
|
|
class WindowsDirect2DResourceManager : public GuiGraphicsResourceManager, public IWindowsDirect2DResourceManager, public INativeControllerListener
|
|
{
|
|
protected:
|
|
SortedList<Ptr<WindowsDirect2DRenderTarget>> renderTargets;
|
|
Ptr<WindowsDirect2DLayoutProvider> layoutProvider;
|
|
|
|
CachedTextFormatAllocator textFormats;
|
|
CachedCharMeasurerAllocator charMeasurers;
|
|
public:
|
|
WindowsDirect2DResourceManager()
|
|
{
|
|
layoutProvider=new WindowsDirect2DLayoutProvider;
|
|
}
|
|
|
|
IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
|
|
{
|
|
return GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window);
|
|
}
|
|
|
|
void RecreateRenderTarget(INativeWindow* window)override
|
|
{
|
|
NativeWindowDestroying(window);
|
|
GetWindowsDirect2DObjectProvider()->RecreateRenderTarget(window);
|
|
NativeWindowCreated(window);
|
|
}
|
|
|
|
IGuiGraphicsLayoutProvider* GetLayoutProvider()override
|
|
{
|
|
return layoutProvider.Obj();
|
|
}
|
|
|
|
void NativeWindowCreated(INativeWindow* window)override
|
|
{
|
|
WindowsDirect2DRenderTarget* renderTarget=new WindowsDirect2DRenderTarget(window);
|
|
renderTargets.Add(renderTarget);
|
|
GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, renderTarget);
|
|
}
|
|
|
|
void NativeWindowDestroying(INativeWindow* window)override
|
|
{
|
|
WindowsDirect2DRenderTarget* renderTarget=dynamic_cast<WindowsDirect2DRenderTarget*>(GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window));
|
|
GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, 0);
|
|
renderTargets.Remove(renderTarget);
|
|
}
|
|
|
|
Direct2DTextFormatPackage* CreateDirect2DTextFormat(const FontProperties& fontProperties)override
|
|
{
|
|
return textFormats.Create(fontProperties).Obj();
|
|
}
|
|
|
|
void DestroyDirect2DTextFormat(const FontProperties& fontProperties)override
|
|
{
|
|
textFormats.Destroy(fontProperties);
|
|
}
|
|
|
|
Ptr<elements::text::CharMeasurer> CreateDirect2DCharMeasurer(const FontProperties& fontProperties)override
|
|
{
|
|
return charMeasurers.Create(fontProperties);
|
|
}
|
|
|
|
void DestroyDirect2DCharMeasurer(const FontProperties& fontProperties)override
|
|
{
|
|
charMeasurers.Destroy(fontProperties);
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace elements_windows_d2d
|
|
{
|
|
IWindowsDirect2DResourceManager* windowsDirect2DResourceManager=0;
|
|
|
|
IWindowsDirect2DResourceManager* GetWindowsDirect2DResourceManager()
|
|
{
|
|
return windowsDirect2DResourceManager;
|
|
}
|
|
|
|
void SetWindowsDirect2DResourceManager(IWindowsDirect2DResourceManager* resourceManager)
|
|
{
|
|
windowsDirect2DResourceManager=resourceManager;
|
|
}
|
|
|
|
/***********************************************************************
|
|
OS Supporting
|
|
***********************************************************************/
|
|
|
|
IWindowsDirect2DObjectProvider* windowsDirect2DObjectProvider=0;
|
|
|
|
IWindowsDirect2DObjectProvider* GetWindowsDirect2DObjectProvider()
|
|
{
|
|
return windowsDirect2DObjectProvider;
|
|
}
|
|
|
|
void SetWindowsDirect2DObjectProvider(IWindowsDirect2DObjectProvider* provider)
|
|
{
|
|
windowsDirect2DObjectProvider=provider;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
NativeMain
|
|
***********************************************************************/
|
|
|
|
using namespace vl::presentation;
|
|
using namespace vl::presentation::elements;
|
|
|
|
void RendererMainDirect2D()
|
|
{
|
|
elements_windows_d2d::WindowsDirect2DResourceManager resourceManager;
|
|
SetGuiGraphicsResourceManager(&resourceManager);
|
|
elements_windows_d2d::SetWindowsDirect2DResourceManager(&resourceManager);
|
|
GetCurrentController()->CallbackService()->InstallListener(&resourceManager);
|
|
|
|
elements_windows_d2d::GuiSolidBorderElementRenderer::Register();
|
|
elements_windows_d2d::GuiRoundBorderElementRenderer::Register();
|
|
elements_windows_d2d::Gui3DBorderElementRenderer::Register();
|
|
elements_windows_d2d::Gui3DSplitterElementRenderer::Register();
|
|
elements_windows_d2d::GuiSolidBackgroundElementRenderer::Register();
|
|
elements_windows_d2d::GuiGradientBackgroundElementRenderer::Register();
|
|
elements_windows_d2d::GuiSolidLabelElementRenderer::Register();
|
|
elements_windows_d2d::GuiImageFrameElementRenderer::Register();
|
|
elements_windows_d2d::GuiPolygonElementRenderer::Register();
|
|
elements_windows_d2d::GuiColorizedTextElementRenderer::Register();
|
|
elements_windows_d2d::GuiDirect2DElementRenderer::Register();
|
|
elements::GuiDocumentElement::GuiDocumentElementRenderer::Register();
|
|
|
|
GuiApplicationMain();
|
|
elements_windows_d2d::SetWindowsDirect2DResourceManager(0);
|
|
SetGuiGraphicsResourceManager(0);
|
|
}
|