Files
GacUI/Import/GacUI.Windows.cpp
2025-05-02 22:29:10 -07:00

14782 lines
408 KiB
C++

/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "GacUI.Windows.h"
/***********************************************************************
.\WINNATIVEDPIAWARENESS.CPP
***********************************************************************/
#pragma comment(lib, "Shcore.lib")
namespace vl
{
namespace presentation
{
namespace windows
{
#define USE_API(NAME, API) static auto proc_##API = (decltype(&API))(GetProcAddress(GetModuleHandle(L#NAME), #API))
void InitDpiAwareness(bool dpiAware)
{
USE_API(user32, SetProcessDpiAwarenessContext);
USE_API(shcore, SetProcessDpiAwareness);
if(proc_SetProcessDpiAwarenessContext)
{
proc_SetProcessDpiAwarenessContext(dpiAware ? DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT_UNAWARE);
return;
}
if(proc_SetProcessDpiAwareness)
{
proc_SetProcessDpiAwareness(dpiAware ? PROCESS_PER_MONITOR_DPI_AWARE : PROCESS_DPI_UNAWARE);
return;
}
}
void DpiAwared_GetDpiForMonitor(HMONITOR monitor, UINT* x, UINT* y)
{
USE_API(shcore, GetDpiForMonitor);
if (proc_GetDpiForMonitor)
{
if (proc_GetDpiForMonitor(monitor, MDT_DEFAULT, x, y) == S_OK)
{
return;
}
}
*x = 96;
*y = 96;
}
void DpiAwared_GetDpiForWindow(HWND handle, UINT* x, UINT* y)
{
USE_API(user32, GetDpiForWindow);
if (proc_GetDpiForWindow)
{
*x = *y = proc_GetDpiForWindow(handle);
}
else
{
HMONITOR monitor = MonitorFromWindow(handle, MONITOR_DEFAULTTONULL);
if (monitor == NULL)
{
*x = *y = 96;
}
else
{
DpiAwared_GetDpiForMonitor(monitor, x, y);
}
}
}
void DpiAwared_AdjustWindowRect(LPRECT rect, HWND handle, UINT dpi)
{
USE_API(user32, AdjustWindowRectExForDpi);
if (proc_AdjustWindowRectExForDpi)
{
proc_AdjustWindowRectExForDpi(rect, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE, (DWORD)GetWindowLongPtr(handle, GWL_EXSTYLE), dpi);
}
else
{
AdjustWindowRect(rect, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE);
}
}
int DpiAwared_GetSystemMetrics(int index, UINT dpi)
{
USE_API(user32, GetSystemMetricsForDpi);
if (proc_GetSystemMetricsForDpi)
{
return proc_GetSystemMetricsForDpi(index, dpi);
}
else
{
return GetSystemMetrics(index);
}
}
#undef USE_API
}
}
}
/***********************************************************************
.\WINNATIVEWINDOW.CPP
***********************************************************************/
#include <CommCtrl.h>
#pragma comment(lib, "Imm32.lib")
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Comctl32.lib")
namespace vl
{
namespace presentation
{
namespace windows
{
using namespace collections;
LPCWSTR defaultIconResourceName = nullptr;
HICON CreateWindowDefaultIcon(vint size = 0)
{
if (!defaultIconResourceName) return NULL;
return (HICON)LoadImage(GetModuleHandle(NULL), defaultIconResourceName, IMAGE_ICON, (int)size, (int)size, (size ? 0 : LR_DEFAULTSIZE) | LR_SHARED);
}
void SetWindowDefaultIcon(UINT resourceId)
{
CHECK_ERROR(defaultIconResourceName == nullptr, L"vl::presentation::windows::SetWindowDefaultIcon(UINT)#This function can only be called once.");
defaultIconResourceName = MAKEINTRESOURCE(resourceId);
}
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;
ZeroMemory(&windowClass, sizeof(windowClass));
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;
if (defaultIconResourceName)
{
windowClass.hIcon = CreateWindowDefaultIcon();
}
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_NOACTIVATE | SWP_FRAMECHANGED);
}
LONG_PTR InternalGetStyle()
{
return GetWindowLongPtr(handle, GWL_STYLE);
}
void InternalSetStyle(LONG_PTR style)
{
SetWindowLongPtr(handle, GWL_STYLE, style);
SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
LONG_PTR TurnOnStyle(LONG_PTR combination, LONG_PTR style)
{
return combination | style;
}
LONG_PTR TurnOffStyle(LONG_PTR combination, LONG_PTR style)
{
return combination & (~style);
}
bool GetExStyle(LONG_PTR exStyle)
{
return (InternalGetExStyle() & exStyle) != 0;
}
void SetExStyle(LONG_PTR exStyle, bool available)
{
if (available)
{
InternalSetExStyle(TurnOnStyle(InternalGetExStyle(), exStyle));
}
else
{
InternalSetExStyle(TurnOffStyle(InternalGetExStyle(), exStyle));
}
}
bool GetStyle(LONG_PTR style)
{
return (InternalGetStyle() & style) != 0;
}
void SetStyle(LONG_PTR style, bool available)
{
if (available)
{
InternalSetStyle(TurnOnStyle(InternalGetStyle(), style));
}
else
{
InternalSetStyle(TurnOffStyle(InternalGetStyle(), style));
}
}
#pragma push_macro("_CONTROL")
#if defined _CONTROL
#undef _CONTROL
#endif
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(VKEY::KEY_CONTROL);
info.shift = WinIsKeyPressing(VKEY::KEY_SHIFT);
info.left= WinIsKeyPressing(VKEY::KEY_LBUTTON);
info.middle= WinIsKeyPressing(VKEY::KEY_MBUTTON);
info.right = WinIsKeyPressing(VKEY::KEY_RBUTTON);
POINTS point = MAKEPOINTS(lParam);
NativePoint offset = GetClientBoundsInScreen().LeftTop();
info.x = point.x - offset.x.value;
info.y = point.y - offset.y.value;
}
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)
{
NativePoint offset = GetClientBoundsInScreen().LeftTop();
info.x = point.x - offset.x.value;
info.y = point.y - offset.y.value;
}
else
{
info.x = point.x;
info.y = point.y;
}
}
return info;
}
NativeWindowKeyInfo ConvertKey(WPARAM wParam, LPARAM lParam)
{
NativeWindowKeyInfo info;
info.code=(VKEY)wParam;
info.ctrl=WinIsKeyPressing(VKEY::KEY_CONTROL);
info.shift=WinIsKeyPressing(VKEY::KEY_SHIFT);
info.alt=WinIsKeyPressing(VKEY::KEY_MENU);
info.capslock=WinIsKeyToggled(VKEY::KEY_CAPITAL);
info.autoRepeatKeyDown = (((vuint32_t)lParam) >> 30) % 2 == 1;
return info;
}
NativeWindowCharInfo ConvertChar(WPARAM wParam)
{
NativeWindowCharInfo info;
info.code=(wchar_t)wParam;
info.ctrl=WinIsKeyPressing(VKEY::KEY_CONTROL);
info.shift=WinIsKeyPressing(VKEY::KEY_SHIFT);
info.alt=WinIsKeyPressing(VKEY::KEY_MENU);
info.capslock=WinIsKeyToggled(VKEY::KEY_CAPITAL);
return info;
}
#pragma pop_macro("_CONTROL")
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.value;
cf.ptCurrentPos.y = (int)caretPoint.y.value;
ImmSetCompositionWindow(imc, &cf);
ImmReleaseContext(handle, imc);
}
bool supressClosePopups = false;
static void ClosePopupsOf(WindowsForm* owner, SortedList<WindowsForm*>& exceptions)
{
// TODO: (enumerable) foreach
for (vint i = 0; i < owner->childWindows.Count(); i++)
{
auto popup = owner->childWindows[i];
if (popup->windowMode != Normal && popup->IsVisible())
{
if (!exceptions.Contains(popup))
{
popup->Hide(false);
}
}
ClosePopupsOf(popup, exceptions);
}
}
bool HandleMessageInternal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
{
if (!supressClosePopups)
{
bool closePopups = false;
WindowsForm* activatedWindow = nullptr;
WindowsForm* rootWindow = nullptr;
SortedList<WindowsForm*> exceptions;
switch (uMsg)
{
case WM_ACTIVATEAPP:
if (wParam == FALSE)
{
closePopups = true;
}
break;
case WM_ACTIVATE:
switch (LOWORD(wParam))
{
case WA_ACTIVE:
case WA_CLICKACTIVE:
activatedWindow = this;
closePopups = true;
break;
}
break;
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_NCLBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_NCRBUTTONDOWN:
activatedWindow = this;
closePopups = true;
break;
}
if (activatedWindow)
{
rootWindow = activatedWindow;
exceptions.Add(rootWindow);
while (auto parentWindow = rootWindow->parentWindow)
{
rootWindow = parentWindow;
exceptions.Add(parentWindow);
}
}
if (closePopups)
{
List<IWindowsForm*> allRootWindows;
GetAllCreatedWindows(allRootWindows, true);
// TODO: (enumerable) foreach
for (vint i = 0; i < allRootWindows.Count(); i++)
{
if (auto windowsForm = dynamic_cast<WindowsForm*>(allRootWindows[i]))
{
ClosePopupsOf(windowsForm, exceptions);
}
}
}
}
bool nonClient = false;
switch(uMsg)
{
// ************************************** moving and sizing
case WM_MOVING:case WM_SIZING:
{
LPRECT rawBounds=(LPRECT)lParam;
NativeRect bounds(rawBounds->left, rawBounds->top, rawBounds->right, rawBounds->bottom);
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->Moving(bounds, false, (uMsg == WM_SIZING));
}
if( rawBounds->left!=bounds.Left().value
|| rawBounds->top!=bounds.Top().value
|| rawBounds->right!=bounds.Right().value
|| rawBounds->bottom!=bounds.Bottom().value)
{
rawBounds->left=(int)bounds.Left().value;
rawBounds->top=(int)bounds.Top().value;
rawBounds->right=(int)bounds.Right().value;
rawBounds->bottom=(int)bounds.Bottom().value;
result=TRUE;
}
}
break;
case WM_MOVE:case WM_SIZE:
{
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->Moved();
}
}
break;
case WM_DPICHANGED:
{
dpiX = LOWORD(wParam);
dpiY = HIWORD(wParam);
UpdateDpiAwaredFields(false);
auto newRect = (RECT*)lParam;
MoveWindow(handle, newRect->left, newRect->top, (newRect->right - newRect->left), (newRect->bottom - newRect->top), FALSE);
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->DpiChanged(true);
}
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->DpiChanged(false);
}
}
break;
// ************************************** state
case WM_ENABLE:
{
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
if(wParam==TRUE)
{
listeners[i]->Enabled();
}
else
{
listeners[i]->Disabled();
}
}
}
break;
case WM_MOUSEACTIVATE:
if (!enabledActivate)
{
result = MA_NOACTIVATE;
return true;
}
break;
case WM_ACTIVATE:
{
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE)
{
listeners[i]->GotFocus();
listeners[i]->RenderingAsActivated();
}
else
{
listeners[i]->LostFocus();
listeners[i]->RenderingAsDeactivated();
}
}
}
break;
case WM_SHOWWINDOW:
if (lParam == 0)
{
if (wParam == TRUE)
{
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->Opened();
}
}
else
{
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->Closed();
}
}
}
break;
case WM_CLOSE:
{
bool cancel = false;
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->BeforeClosing(cancel);
}
if (!cancel)
{
// TODO: (enumerable) foreach
for (vint i = 0; i < listeners.Count(); i++)
{
listeners[i]->AfterClosing();
}
}
return cancel;
}
break;
// ************************************** mouse
case WM_NCLBUTTONDOWN:
if (!customFrameMode) break;
nonClient = true;
case WM_LBUTTONDOWN:
{
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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);
// TODO: (enumerable) foreach
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;
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->MouseEntered();
}
TrackMouse(true);
}
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->MouseMoving(info);
}
}
}
break;
// ************************************** wheel
case WM_MOUSEHWHEEL:
{
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->HorizontalWheel(info);
}
}
break;
case WM_MOUSEWHEEL:
{
NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false);
// TODO: (enumerable) foreach
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;
// TODO: (enumerable) foreach
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);
info.autoRepeatKeyDown = false;
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->KeyUp(info);
}
}
break;
case WM_KEYDOWN:
{
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->KeyDown(info);
}
}
break;
case WM_SYSKEYUP:
{
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
info.autoRepeatKeyDown = false;
if (supressingAlt && !info.ctrl && !info.shift && info.code == VKEY::KEY_MENU)
{
supressingAlt = false;
break;
}
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->KeyUp(info);
}
}
break;
case WM_SYSKEYDOWN:
{
NativeWindowKeyInfo info=ConvertKey(wParam, lParam);
if (supressingAlt && !info.ctrl && !info.shift && info.code == VKEY::KEY_MENU)
{
break;
}
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->KeyDown(info);
}
}
break;
case WM_CHAR:
{
NativeWindowCharInfo info=ConvertChar(wParam);
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->Char(info);
}
}
break;
// ************************************** painting
case WM_PAINT:
{
// TODO: (enumerable) foreach
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);
NativePoint windowLocation = GetBounds().LeftTop();
location.x -= (SHORT)windowLocation.x.value;
location.y -= (SHORT)windowLocation.y.value;
switch (PerformHitTest(From(listeners), { 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;
}
// handling custom frame
if (customFrameMode)
{
switch (uMsg)
{
case WM_NCLBUTTONDOWN:
switch (wParam)
{
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
result = 0;
return true;
}
break;
case WM_NCLBUTTONUP:
case WM_LBUTTONUP:
{
NativeWindowMouseInfo info = ConvertMouse(wParam, lParam, false, nonClient);
// TODO: (enumerable) this for-loop needs to be removed, because it is not looping, just leave the body
for (vint i = 0; i < listeners.Count(); i++)
{
switch (PerformHitTest(From(listeners), { info.x,info.y }))
{
case INativeWindowListener::ButtonMinimum:
ShowMinimized();
return false;
case INativeWindowListener::ButtonMaximum:
if (GetSizeState() == INativeWindow::Maximized)
{
ShowRestored();
}
else
{
ShowMaximized();
}
return false;
case INativeWindowListener::ButtonClose:
Hide(true);
return false;
}
}
}
break;
}
}
return false;
}
protected:
HWND handle;
WString title;
WindowsCursor* cursor = nullptr;
NativePoint caretPoint;
WindowsForm* parentWindow = nullptr;
List<WindowsForm*> childWindows;
WindowMode windowMode;
bool isMainWindow = false;
List<INativeWindowListener*> listeners;
vint mouseLastX = -1;
vint mouseLastY = -1;
bool mouseHoving = false;
Interface* graphicsHandler = nullptr;
bool customFrameMode = false;
bool enabledActivate = true;
List<Ptr<INativeMessageHandler>> messageHandlers;
bool supressingAlt = false;
Ptr<bool> flagDisposed = Ptr(new bool(false));
NativeMargin customFramePadding;
Ptr<GuiImageData> defaultIcon;
Ptr<GuiImageData> replacementIcon;
HICON replacementHIcon = NULL;
vint dpiX = 0;
vint dpiY = 0;
void UpdateDpiAwaredFields(bool refreshDpiXY)
{
if (refreshDpiXY)
{
UINT x = 0;
UINT y = 0;
DpiAwared_GetDpiForWindow(handle, &x, &y);
dpiX = (vint)x;
dpiY = (vint)y;
}
auto padding = (vint)(DpiAwared_GetSystemMetrics(SM_CXSIZEFRAME, (UINT)dpiX) + DpiAwared_GetSystemMetrics(SM_CXPADDEDBORDER, (UINT)dpiX));
customFramePadding = NativeMargin(padding, padding, padding, padding);
}
public:
WindowsForm(HWND parent, WString className, HINSTANCE hInstance, INativeWindow::WindowMode _windowMode)
: windowMode(_windowMode)
{
{
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);
}
if (windowMode == INativeWindow::Normal)
{
// use WS_POPUP in CreateWindowEx, because CW_USERDEFAULT is interpreted as 0, unlike WS_OVERLAPPED
// if this is not a popup window, replace WS_POPUP with WS_OVERLAPPED
auto style = InternalGetStyle();
style = TurnOnStyle(style, WS_OVERLAPPED);
style = TurnOffStyle(style, WS_POPUP);
InternalSetStyle(style);
}
UpdateDpiAwaredFields(true);
}
~WindowsForm()
{
if (parentWindow)
{
parentWindow->childWindows.Remove(this);
}
// TODO: (enumerable) foreach
for (vint i = childWindows.Count() - 1; i >= 0; i--)
{
childWindows[i]->SetParent(parentWindow);
}
*flagDisposed.Obj() = true;
List<INativeWindowListener*> copiedListeners;
CopyFrom(copiedListeners, listeners);
// TODO: (enumerable) foreach
for (vint i = 0; i < copiedListeners.Count(); i++)
{
INativeWindowListener* listener = copiedListeners[i];
if (listeners.Contains(listener))
{
listener->Destroyed();
}
}
DestroyWindow(handle);
}
void SetIsMainWindow()
{
isMainWindow = true;
}
void InvokeDestroying()
{
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->Destroying();
}
}
bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result)
{
#define CHECK_DISPOSED if (*flag.Obj()) return skip
auto flag = flagDisposed;
bool skip = false;
{
for (auto handler : messageHandlers)
{
handler->BeforeHandle(hwnd, uMsg, wParam, lParam, skip);
CHECK_DISPOSED;
}
if (skip)
{
return true;
}
}
skip = HandleMessageInternal(hwnd, uMsg, wParam, lParam, result);
CHECK_DISPOSED;
if (GetWindowsFormFromHandle(hwnd))
{
for (auto handler : messageHandlers)
{
handler->AfterHandle(hwnd, uMsg, wParam, lParam, skip, result);
CHECK_DISPOSED;
}
}
return skip;
#undef CHECK_DISPOSED
}
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;
}
bool IsActivelyRefreshing()override
{
return true;
}
NativeSize GetRenderingOffset()override
{
return { 0,0 };
}
Point Convert(NativePoint value)override
{
return Point((vint)value.x.value * 96 / dpiX, (vint)value.y.value * 96 / dpiY);
}
NativePoint Convert(Point value)override
{
return NativePoint(value.x * dpiX / 96, value.y * dpiY / 96);
}
Size Convert(NativeSize value)override
{
return Size((vint)value.x.value * 96 / dpiX, (vint)value.y.value * 96 / dpiY);
}
NativeSize Convert(Size value)override
{
return NativeSize(value.x * dpiX / 96, value.y * dpiY / 96);
}
Margin Convert(NativeMargin value)override
{
return Margin(
(vint)value.left.value * 96 / dpiX,
(vint)value.top.value * 96 / dpiY,
(vint)value.right.value * 96 / dpiX,
(vint)value.bottom.value * 96 / dpiY
);
}
NativeMargin Convert(Margin value)override
{
return NativeMargin(
(vint)value.left * dpiX / 96,
(vint)value.top * dpiY / 96,
(vint)value.right * dpiX / 96,
(vint)value.bottom * dpiY / 96
);
}
NativeRect GetBounds()override
{
RECT rect;
GetWindowRect(handle, &rect);
return NativeRect(rect.left, rect.top, rect.right, rect.bottom);
}
void SetBounds(const NativeRect& bounds)override
{
NativeRect newBounds=bounds;
// TODO: (enumerable) foreach
for(vint i=0;i<listeners.Count();i++)
{
listeners[i]->Moving(newBounds, true, false);
}
MoveWindow(handle, (int)newBounds.Left().value, (int)newBounds.Top().value, (int)newBounds.Width().value, (int)newBounds.Height().value, FALSE);
}
NativeSize GetClientSize()override
{
return GetClientBoundsInScreen().GetSize();
}
void SetClientSize(NativeSize size)override
{
if (customFrameMode)
{
RECT bounds;
GetWindowRect(handle, &bounds);
SetBounds(NativeRect(NativePoint(bounds.left, bounds.top), size));
}
else
{
RECT required = { 0,0,(int)size.x.value,(int)size.y.value };
RECT bounds;
GetWindowRect(handle, &bounds);
DpiAwared_AdjustWindowRect(&required, handle, (UINT)dpiX);
SetBounds(NativeRect(NativePoint(bounds.left, bounds.top), NativeSize(required.right - required.left, required.bottom - required.top)));
}
}
NativeRect GetClientBoundsInScreen()override
{
if(customFrameMode)
{
return GetBounds();
}
else
{
RECT required={0,0,0,0};
RECT bounds;
GetWindowRect(handle, &bounds);
DpiAwared_AdjustWindowRect(&required, handle, (UINT)dpiX);
return NativeRect(
NativePoint(
(bounds.left-required.left),
(bounds.top-required.top)
),
NativeSize(
(bounds.right-bounds.left)-(required.right-required.left),
(bounds.bottom-bounds.top)-(required.bottom-required.top)
)
);
}
}
WString GetTitle()override
{
return title;
}
void SetTitle(const WString& _title)override
{
title=_title;
SetWindowText(handle, title.Buffer());
}
INativeCursor* GetWindowCursor()override
{
return cursor;
}
void SetWindowCursor(INativeCursor* _cursor)override
{
WindowsCursor* newCursor=dynamic_cast<WindowsCursor*>(_cursor);
if(newCursor && cursor!=newCursor)
{
cursor=newCursor;
if(mouseHoving && IsVisible())
{
SetCursor(cursor->GetCursorHandle());
}
}
}
NativePoint GetCaretPoint()override
{
return caretPoint;
}
void SetCaretPoint(NativePoint point)override
{
caretPoint=point;
UpdateCompositionForContent();
}
INativeWindow* GetParent()override
{
return parentWindow;
}
void SetParent(INativeWindow* parent)override
{
if (parentWindow)
{
parentWindow->childWindows.Remove(this);
}
if ((parentWindow = dynamic_cast<WindowsForm*>(parent)))
{
parentWindow->childWindows.Add(this);
// ::SetParent(handle, parentWindow->handle);
SetWindowLongPtr(handle, GWLP_HWNDPARENT, (LONG_PTR)parentWindow->handle);
}
else
{
// ::SetParent(handle, NULL);
SetWindowLongPtr(handle, GWLP_HWNDPARENT, NULL);
}
}
WindowMode GetWindowMode()override
{
return windowMode;
}
void EnableCustomFrameMode()override
{
customFrameMode=true;
}
void DisableCustomFrameMode()override
{
customFrameMode=false;
}
bool IsCustomFrameModeEnabled()override
{
return customFrameMode;
}
NativeMargin GetCustomFramePadding()override
{
if (GetSizeBox() || GetTitleBar())
{
return customFramePadding;
}
else
{
return NativeMargin(0, 0, 0, 0);
}
}
Ptr<GuiImageData> GetIcon()override
{
if (replacementIcon && replacementIcon->GetImage())
{
return replacementIcon;
}
else
{
if (!defaultIcon)
{
auto icon = CreateWindowDefaultIcon(16);
if (icon == NULL)
{
icon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 16, 16, LR_SHARED);
}
if (icon != NULL)
{
defaultIcon = Ptr(new GuiImageData(CreateImageFromHICON(icon), 0));
}
}
return defaultIcon;
}
}
static double GetSizeScore(vint size)
{
if (size > 32)
{
return 32.0 / size;
}
else if (size < 32)
{
return size / 32.0 - 1;
}
else
{
return 1.0;
}
}
static vint GetBppFromFormat(const WICPixelFormatGUID& format)
{
if (format == GUID_WICPixelFormat1bppIndexed) return 1;
if (format == GUID_WICPixelFormat2bppIndexed) return 2;
if (format == GUID_WICPixelFormat4bppIndexed) return 4;
if (format == GUID_WICPixelFormat8bppIndexed) return 8;
if (format == GUID_WICPixelFormatBlackWhite) return 1;
if (format == GUID_WICPixelFormat2bppGray) return 2;
if (format == GUID_WICPixelFormat4bppGray) return 4;
if (format == GUID_WICPixelFormat8bppGray) return 8;
if (format == GUID_WICPixelFormat8bppAlpha) return 8;
if (format == GUID_WICPixelFormat16bppBGR555) return 16;
if (format == GUID_WICPixelFormat16bppBGR565) return 16;
if (format == GUID_WICPixelFormat16bppBGRA5551) return 16;
if (format == GUID_WICPixelFormat16bppGray) return 16;
if (format == GUID_WICPixelFormat24bppBGR) return 24;
if (format == GUID_WICPixelFormat24bppRGB) return 24;
if (format == GUID_WICPixelFormat32bppBGR) return 32;
if (format == GUID_WICPixelFormat32bppBGRA) return 32;
if (format == GUID_WICPixelFormat32bppPBGRA) return 32;
return -1;
}
void SetIcon(Ptr<GuiImageData> icon)override
{
replacementIcon = icon;
HICON newReplacementHIcon = NULL;
if (replacementIcon && replacementIcon->GetImage())
{
stream::MemoryStream memoryStream;
replacementIcon->GetImage()->SaveToStream(memoryStream, INativeImage::Icon);
if (memoryStream.Size() > 0)
{
newReplacementHIcon = CreateIconFromResource((PBYTE)memoryStream.GetInternalBuffer(), (DWORD)memoryStream.Size(), TRUE, 0x00030000);
if (newReplacementHIcon != NULL)
{
goto SKIP;
}
}
INativeImageFrame* selectedFrame = nullptr;
for (vint i = 0; i < replacementIcon->GetImage()->GetFrameCount(); i++)
{
auto frame = replacementIcon->GetImage()->GetFrame(i);
auto size = frame->GetSize();
if (size.x == size.y)
{
auto bitmap = GetWICBitmap(frame);
WICPixelFormatGUID format;
HRESULT hr = bitmap->GetPixelFormat(&format);
if (hr != S_OK) continue;
if (!selectedFrame)
{
selectedFrame = frame;
}
else
{
auto score = GetSizeScore(size.x);
auto scoreSelected = GetSizeScore(selectedFrame->GetSize().x);
if (score > scoreSelected)
{
selectedFrame = frame;
}
else if (score == scoreSelected)
{
WICPixelFormatGUID selectedFormat;
auto selectedBitmap = GetWICBitmap(selectedFrame);
hr = selectedBitmap->GetPixelFormat(&selectedFormat);
auto bpp = GetBppFromFormat(format);
auto bppSelected = GetBppFromFormat(selectedFormat);
if (bpp > bppSelected)
{
selectedFrame = frame;
}
}
}
}
}
if (selectedFrame)
{
bool succeeded = false;
WindowsBitmapImage newBitmap(replacementIcon->GetImage()->GetImageService(), GetWICBitmap(selectedFrame), replacementIcon->GetImage()->GetFormat());
newBitmap.SaveToStream(memoryStream, INativeImage::Bmp);
if (memoryStream.Size() > 0)
{
auto pBuffer = (char*)memoryStream.GetInternalBuffer();
tagBITMAPFILEHEADER bfh = *(tagBITMAPFILEHEADER*)pBuffer;
tagBITMAPINFOHEADER bih = *(tagBITMAPINFOHEADER*)(pBuffer + sizeof(tagBITMAPFILEHEADER));
RGBQUAD rgb = *(RGBQUAD*)(pBuffer + sizeof(tagBITMAPFILEHEADER) + sizeof(tagBITMAPINFOHEADER));
BITMAPINFO bi;
bi.bmiColors[0] = rgb;
bi.bmiHeader = bih;
char* pPixels = (pBuffer + bfh.bfOffBits);
char* ppvBits;
auto hBitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void**)&ppvBits, NULL, 0);
if (hBitmap != NULL)
{
SetDIBits(NULL, hBitmap, 0, bih.biHeight, pPixels, &bi, DIB_RGB_COLORS);
auto himl = ImageList_Create(32, 32, ILC_COLOR32, 1, 1);
if (himl != NULL)
{
int addResult = ImageList_Add(himl, hBitmap, NULL);
newReplacementHIcon = ImageList_GetIcon(himl, 0, ILD_NORMAL);
ImageList_Destroy(himl);
}
DeleteObject(hBitmap);
}
}
}
}
SKIP:
{
HICON hAppIcon = newReplacementHIcon;
if (hAppIcon == NULL) hAppIcon = CreateWindowDefaultIcon();
if (hAppIcon == NULL) hAppIcon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
bool isVisible = IsVisible();
if (isVisible) SendMessage(handle, WM_SETREDRAW, (WPARAM)FALSE, NULL);
SendMessage(handle, WM_SETICON, ICON_BIG, (LPARAM)hAppIcon);
SendMessage(handle, WM_SETICON, ICON_SMALL, (LPARAM)hAppIcon);
if (isVisible) SendMessage(handle, WM_SETREDRAW, (WPARAM)TRUE, NULL);
if (isMainWindow)
{
SendMessage(GetWindow(handle, GW_OWNER), WM_SETICON, ICON_BIG, (LPARAM)hAppIcon);
SendMessage(GetWindow(handle, GW_OWNER), WM_SETICON, ICON_SMALL, (LPARAM)hAppIcon);
}
}
if (replacementHIcon != NULL)
{
DestroyIcon(replacementHIcon);
}
replacementHIcon = newReplacementHIcon;
}
WindowSizeState GetSizeState()override
{
if(IsIconic(handle))
{
return INativeWindow::Minimized;
}
else if(IsZoomed(handle))
{
return INativeWindow::Maximized;
}
else
{
return INativeWindow::Restored;
}
}
void Show()override
{
SetForegroundWindow(handle);
ShowWindow(handle, SW_SHOWNORMAL);
}
void ShowDeactivated()override
{
ShowWindow(handle, SW_SHOWNOACTIVATE);
SetWindowPos(handle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
void ShowRestored()override
{
ShowWindow(handle, SW_RESTORE);
}
void ShowMaximized()override
{
ShowWindow(handle, SW_SHOWMAXIMIZED);
}
void ShowMinimized()override
{
ShowWindow(handle, SW_SHOWMINIMIZED);
}
void Hide(bool closeWindow)override
{
PostMessage(handle, WM_CLOSE, NULL, NULL);
}
bool IsVisible()override
{
return IsWindowVisible(handle)!=0;
}
void Enable()override
{
EnableWindow(handle, TRUE);
}
void Disable()override
{
EnableWindow(handle, FALSE);
}
bool IsEnabled()override
{
return IsWindowEnabled(handle)!=0;
}
void SetActivate()override
{
SetActiveWindow(handle);
}
bool IsActivated()override
{
return GetActiveWindow()==handle;
}
bool IsRenderingAsActivated()override
{
// TODO: should render as activated when
// is activated
// is a parent window of one that rendering as activated
return IsActivated();
}
void ShowInTaskBar()override
{
SetExStyle(WS_EX_APPWINDOW, true);
}
void HideInTaskBar()override
{
SetExStyle(WS_EX_APPWINDOW, false);
}
bool IsAppearedInTaskBar()override
{
return GetExStyle(WS_EX_APPWINDOW);
}
void EnableActivate()override
{
enabledActivate = true;
SetExStyle(WS_EX_NOACTIVATE, false);
}
void DisableActivate()override
{
enabledActivate = false;
SetExStyle(WS_EX_NOACTIVATE, true);
}
bool IsEnabledActivate()override
{
return enabledActivate;
}
bool RequireCapture()override
{
SetCapture(handle);
return true;
}
bool ReleaseCapture()override
{
::ReleaseCapture();
return true;
}
bool IsCapturing()override
{
return GetCapture()==handle;
}
bool GetMaximizedBox()override
{
return GetStyle(WS_MAXIMIZEBOX);
}
void SetMaximizedBox(bool visible)override
{
SetStyle(WS_MAXIMIZEBOX, visible);
}
bool GetMinimizedBox()override
{
return GetStyle(WS_MINIMIZEBOX);
}
void SetMinimizedBox(bool visible)override
{
SetStyle(WS_MINIMIZEBOX, visible);
}
bool GetBorder()override
{
return GetStyle(WS_BORDER);
}
void SetBorder(bool visible)override
{
SetStyle(WS_BORDER, visible);
}
bool GetSizeBox()override
{
return GetStyle(WS_SIZEBOX);
}
void SetSizeBox(bool visible)override
{
SetStyle(WS_SIZEBOX, visible);
}
bool GetIconVisible()override
{
return GetStyle(WS_SYSMENU);
}
void SetIconVisible(bool visible)override
{
SetStyle(WS_SYSMENU, visible);
}
bool GetTitleBar()override
{
return GetStyle(WS_CAPTION);
}
void SetTitleBar(bool visible)override
{
SetStyle(WS_CAPTION, visible);
}
bool GetTopMost()override
{
return GetExStyle(WS_EX_TOPMOST);
}
void SetTopMost(bool topmost)override
{
SetWindowPos(handle, (topmost ? HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
void SupressAlt()override
{
if (!supressingAlt)
{
supressingAlt = true;
PostMessage(handle, WM_SYSKEYDOWN, VK_MENU, 0);
PostMessage(handle, WM_SYSKEYUP, VK_MENU, 0);
}
}
bool InstallListener(INativeWindowListener* listener)override
{
if(listeners.Contains(listener))
{
return false;
}
else
{
listeners.Add(listener);
return true;
}
}
bool UninstallListener(INativeWindowListener* listener)override
{
if(listeners.Contains(listener))
{
listeners.Remove(listener);
return true;
}
else
{
return false;
}
}
void RedrawContent()override
{
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);
class WindowsController : public Object, public virtual INativeController, public virtual INativeWindowService
{
protected:
WinClass windowClass;
WinClass godClass;
HINSTANCE hInstance;
HWND godWindow;
Dictionary<HWND, WindowsForm*> windows;
WindowsForm* mainWindow = nullptr;
HWND mainWindowHandle = 0;
SharedCallbackService callbackService;
SharedAsyncService asyncService;
WindowsResourceService resourceService;
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)
,screenService(&GetHWNDFromNativeWindowHandle)
,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();
clipboardService.SetOwnerHandle(NULL);
DestroyWindow(godWindow);
}
WindowsForm* GetWindowsFormFromHandle(HWND hwnd)
{
vint index = windows.Keys().IndexOf(hwnd);
if (index == -1)return 0;
return windows.Values()[index];
}
void GetAllCreatedWindows(collections::List<IWindowsForm*>& createdWindows, bool rootWindowOnly)
{
if (rootWindowOnly)
{
for (auto window : windows.Values())
{
if (window->GetWindowMode() == INativeWindow::Normal)
{
createdWindows.Add(window);
}
}
}
else
{
CopyFrom(createdWindows, windows.Values());
}
}
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 (auto window : windows.Values())
{
if (window->IsVisible())
{
window->Hide(true);
}
}
List<WindowsForm*> normalWindows;
CopyFrom(
normalWindows,
From(windows.Values())
.Where([](WindowsForm* window)
{
return window->GetWindowMode() == INativeWindow::Normal;
})
);
for (auto window : normalWindows)
{
DestroyNativeWindow(window);
}
// TODO: (enumerable) foreach:reversed
for (vint i = windows.Count() - 1; i >= 0; i--)
{
auto window = windows.Values()[i];
DestroyNativeWindow(window);
}
PostQuitMessage(0);
}
}
return skipDefaultProcedure;
}
//=======================================================================
const NativeWindowFrameConfig& GetMainWindowFrameConfig()
{
return NativeWindowFrameConfig::Default;
}
const NativeWindowFrameConfig& GetNonMainWindowFrameConfig()
{
return NativeWindowFrameConfig::Default;
}
INativeWindow* CreateNativeWindow(INativeWindow::WindowMode windowMode)override
{
WindowsForm* window = new WindowsForm(godWindow, windowClass.GetName(), hInstance, windowMode);
windows.Add(window->GetWindowHandle(), window);
callbackService.InvokeNativeWindowCreated(window);
window->SetWindowCursor(resourceService.GetDefaultSystemCursor());
return window;
}
void DestroyNativeWindow(INativeWindow* window)override
{
auto windowsForm = dynamic_cast<WindowsForm*>(window);
windowsForm->InvokeDestroying();
if (windowsForm != 0 && windows.Keys().Contains(windowsForm->GetWindowHandle()))
{
callbackService.InvokeNativeWindowDestroying(window);
windows.Remove(windowsForm->GetWindowHandle());
if (mainWindow == windowsForm)
{
mainWindow = nullptr;
}
delete windowsForm;
}
}
INativeWindow* GetMainWindow()override
{
return mainWindow;
}
inline bool RunOneCycleInternal()
{
MSG message;
if (!GetMessage(&message, NULL, 0, 0)) return false;
TranslateMessage(&message);
DispatchMessage(&message);
asyncService.ExecuteAsyncTasks();
return true;
}
void Run(INativeWindow* window)override
{
mainWindow = dynamic_cast<WindowsForm*>(GetWindowsForm(window));
mainWindowHandle = mainWindow->GetWindowHandle();
mainWindow->SetIsMainWindow();
mainWindow->Show();
while (RunOneCycleInternal());
}
bool RunOneCycle()override
{
return RunOneCycleInternal();
}
INativeWindow* GetWindow(NativePoint location)override
{
POINT p;
p.x=(int)location.x.value;
p.y=(int)location.y.value;
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;
}
WString GetExecutablePath()
{
Array<wchar_t> buffer(65536);
GetModuleFileName(NULL, &buffer[0], (DWORD)buffer.Count());
return &buffer[0];
}
//=======================================================================
void InvokeGlobalTimer()
{
callbackService.InvokeGlobalTimer();
}
void InvokeClipboardUpdated()
{
callbackService.InvokeClipboardUpdated();
}
void InvokeGlobalShortcutkeyActivated(vint id)
{
callbackService.InvokeGlobalShortcutKeyActivated(id);
}
};
/***********************************************************************
Windows Procedure
***********************************************************************/
WindowsController* windowsController = nullptr;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (windowsController)
{
LRESULT result = 0;
if (windowsController->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)
{
if (windowsController)
{
switch (uMsg)
{
case WM_TIMER:
windowsController->InvokeGlobalTimer();
break;
case WM_CLIPBOARDUPDATE:
windowsController->InvokeClipboardUpdated();
break;
case WM_HOTKEY:
if (wParam > 0)
{
windowsController->InvokeGlobalShortcutkeyActivated((vint)wParam);
}
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/***********************************************************************
Windows Platform Native Controller
***********************************************************************/
void StartWindowsNativeController(HINSTANCE hInstance)
{
CHECK_ERROR(!windowsController, L"vl::presentation::windows::StartWindowsNativeController(HINSTANCE)#The Windows native controller has been started.");
windowsController = new WindowsController(hInstance);
}
INativeController* GetWindowsNativeController()
{
return windowsController;
}
IWindowsForm* GetWindowsFormFromHandle(HWND hwnd)
{
if (windowsController)
{
return windowsController->GetWindowsFormFromHandle(hwnd);
}
return nullptr;
}
IWindowsForm* GetWindowsForm(INativeWindow* window)
{
return dynamic_cast<WindowsForm*>(window);
}
void GetAllCreatedWindows(collections::List<IWindowsForm*>& windows, bool rootWindowOnly)
{
if (windowsController)
{
windowsController->GetAllCreatedWindows(windows, rootWindowOnly);
}
}
void StopWindowsNativeController()
{
CHECK_ERROR(windowsController, L"vl::presentation::windows::StopWindowsNativeController()#The Windows native controller has been stopped.");
delete windowsController;
windowsController = nullptr;
}
void EnableCrossKernelCrashing()
{
/*
"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"
DWORD DisableUserModeCallbackFilter = 1
"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\TestCppCodegen.exe"
DWORD DisableUserModeCallbackFilter = 1
*/
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);
}
}
}
}
}
}
/***********************************************************************
.\DIRECT2D\WINDIRECT2DAPPLICATION.CPP
***********************************************************************/
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "d3d11.lib")
namespace vl
{
namespace presentation
{
using namespace elements;
namespace windows
{
using namespace vl::collections;
/***********************************************************************
WindowListener
***********************************************************************/
class Direct2DWindowsNativeWindowListener : public Object, public INativeWindowListener
{
protected:
ID2D1Factory* d2dFactory;
INativeWindow* window;
bool rendering = false;
bool movedWhileRendering = false;
virtual void RebuildCanvas(NativeSize size) = 0;
public:
Direct2DWindowsNativeWindowListener(INativeWindow* _window, ID2D1Factory* _d2dFactory)
:window(_window)
,d2dFactory(_d2dFactory)
{
}
void Moved()override
{
if (rendering)
{
movedWhileRendering = true;
}
else
{
ResizeRenderTarget();
}
}
void ResizeRenderTarget()
{
RebuildCanvas(window->GetClientSize());
}
void StartRendering()
{
rendering = true;
}
void StopRendering()
{
rendering = false;
}
bool RetrieveAndResetMovedWhileRendering()
{
bool result = movedWhileRendering;
movedWhileRendering = false;
return result;
}
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;
NativeSize previousSize;
void RebuildCanvas(NativeSize 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();
{
UINT dpiX = 0;
UINT dpiY = 0;
DpiAwared_GetDpiForWindow(form->GetWindowHandle(), &dpiX, &dpiY);
tp.dpiX = (FLOAT)dpiX;
tp.dpiY = (FLOAT)dpiY;
}
HRESULT hr = d2dFactory->CreateHwndRenderTarget(
tp,
D2D1::HwndRenderTargetProperties(
form->GetWindowHandle(),
D2D1::SizeU((int)size.x.value, (int)size.y.value)
),
&renderTarget
);
if (!FAILED(hr))
{
d2dRenderTarget = renderTarget;
d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
}
}
else if (previousSize != size)
{
d2dRenderTarget->Resize(D2D1::SizeU((int)size.x.value, (int)size.y.value));
}
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 = nullptr;
}
}
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;
NativeSize previousSize;
ComPtr<IDXGIDevice> GetDXGIDevice()
{
IDXGIDevice* device = nullptr;
HRESULT hr = d3d11Device->QueryInterface(&device);
if (!SUCCEEDED(hr)) return nullptr;
return device;
}
ComPtr<IDXGISwapChain1> CreateSwapChain(IDXGIDevice* dxgiDevice)
{
ComPtr<IDXGIAdapter> dxgiAdapter;
{
IDXGIAdapter* adapter = nullptr;
HRESULT hr = dxgiDevice->GetAdapter(&adapter);
if (!SUCCEEDED(hr)) return nullptr;
dxgiAdapter = adapter;
}
ComPtr<IDXGIFactory2> dxgiFactory;
{
IDXGIFactory2* factory = nullptr;
HRESULT hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&factory);
if (!SUCCEEDED(hr)) return nullptr;
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 nullptr;
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 nullptr;
d2d1Device = device;
}
ComPtr<ID2D1DeviceContext> d2dDeviceContext;
{
ID2D1DeviceContext* deviceContext = nullptr;
HRESULT hr = d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &deviceContext);
if (!SUCCEEDED(hr)) return nullptr;
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 nullptr;
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 nullptr;
d2dBitmap = bitmap;
}
return d2dBitmap;
}
void RebuildCanvas(NativeSize 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());
IWindowsForm* form = GetWindowsForm(window);
{
UINT dpiX = 0;
UINT dpiY = 0;
DpiAwared_GetDpiForWindow(form->GetWindowHandle(), &dpiX, &dpiY);
d2dDeviceContext->SetDpi((FLOAT)dpiX, (FLOAT)dpiY);
}
previousSize = { 0,0 };
}
if (previousSize != size)
{
previousSize = size;
d2dDeviceContext->SetTarget(nullptr);
HRESULT hr = dxgiSwapChain->ResizeBuffers(
0,
(UINT)size.x.value,
(UINT)size.y.value,
DXGI_FORMAT_UNKNOWN,
0
);
if (SUCCEEDED(hr))
{
auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj());
d2dDeviceContext->SetTarget(d2dBitmap.Obj());
}
}
}
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 = nullptr;
dxgiSwapChain = nullptr;
}
}
bool PresentRenderTarget()override
{
if (d2dDeviceContext)
{
if (dxgiSwapChain)
{
DXGI_PRESENT_PARAMETERS parameters = {0};
HRESULT hr = dxgiSwapChain->Present1(1, 0, &parameters);
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;
}
}
}
~Direct2DWindowsNativeControllerListener()
{
// TODO: (enumerable) foreach on dictionary
for (vint i = 0; i < nativeWindowListeners.Count(); i++)
{
auto window = nativeWindowListeners.Keys()[i];
auto listener = nativeWindowListeners.Values()[i];
window->UninstallListener(listener.Obj());
}
}
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 nullptr;
}
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);
}
}
#ifdef _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 = Ptr(new Direct2DWindowsNativeWindowListener_1_1(window, d2dfactory1, d3d11Device.Obj()));
}
else
{
listener = Ptr(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 = nullptr;
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
{
protected:
windows::Direct2DWindowsNativeWindowListener* GetNativeWindowListener(INativeWindow* window)
{
vint index = windows::direct2DListener->nativeWindowListeners.Keys().IndexOf(window);
return index == -1
? nullptr
: windows::direct2DListener->nativeWindowListeners.Values().Get(index).Obj();
}
public:
void RecreateRenderTarget(INativeWindow* window)override
{
if (auto listener = GetNativeWindowListener(window))
{
return listener->RecreateRenderTarget();
}
}
void ResizeRenderTarget(INativeWindow* window)override
{
if (auto listener = GetNativeWindowListener(window))
{
return listener->ResizeRenderTarget();
}
}
ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window)override
{
if (auto listener = GetNativeWindowListener(window))
{
return listener->GetDirect2DRenderTarget();
}
return nullptr;
}
void StartRendering(INativeWindow* window)override
{
if (auto listener = GetNativeWindowListener(window))
{
listener->StartRendering();
if (auto renderTarget = listener->GetDirect2DRenderTarget())
{
renderTarget->BeginDraw();
renderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
}
}
}
RenderTargetFailure StopRenderingAndPresent(INativeWindow* window)override
{
if (auto listener = GetNativeWindowListener(window))
{
listener->StopRendering();
bool moved = listener->RetrieveAndResetMovedWhileRendering();
if (auto renderTarget = listener->GetDirect2DRenderTarget())
{
HRESULT hr = renderTarget->EndDraw();
if (hr == S_OK)
{
if (moved)
{
return RenderTargetFailure::ResizeWhileRendering;
}
else if (listener->PresentRenderTarget())
{
return RenderTargetFailure::None;
}
}
}
}
return RenderTargetFailure::LostDevice;
}
ID2D1Factory* GetDirect2DFactory()override
{
return vl::presentation::windows::GetDirect2DFactory();
}
IDWriteFactory* GetDirectWriteFactory()override
{
return vl::presentation::windows::GetDirectWriteFactory();
}
IWindowsDirect2DRenderTarget* GetBindedRenderTarget(INativeWindow* window)override
{
auto windowsForm = vl::presentation::windows::GetWindowsForm(window);
auto graphicsHandler = windowsForm->GetGraphicsHandler();
return dynamic_cast<IWindowsDirect2DRenderTarget*>(graphicsHandler);
}
void SetBindedRenderTarget(INativeWindow* window, IWindowsDirect2DRenderTarget* 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);
}
};
}
}
}
using namespace vl;
using namespace vl::presentation;
using namespace vl::presentation::windows;
using namespace vl::presentation::elements_windows_d2d;
int SetupWindowsDirect2DRendererInternal(bool hosted, bool raw)
{
InitDpiAwareness(true);
CoInitializeEx(NULL, COINIT_MULTITHREADED);
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
WinDirect2DApplicationDirect2DObjectProvider objectProvider;
SetWindowsDirect2DObjectProvider(&objectProvider);
EnableCrossKernelCrashing();
// create controller
GuiHostedController* hostedController = nullptr;
StartWindowsNativeController(hInstance);
auto nativeController = GetWindowsNativeController();
if (hosted)
{
hostedController = new GuiHostedController(nativeController);
SetNativeController(hostedController);
SetHostedApplication(hostedController->GetHostedApplication());
}
else
{
SetNativeController(nativeController);
}
{
// install listener
Direct2DWindowsNativeControllerListener listener;
nativeController->CallbackService()->InstallListener(&listener);
direct2DListener = &listener;
// main
RendererMainDirect2D(hostedController, raw);
// uninstall listener
direct2DListener = nullptr;
nativeController->CallbackService()->UninstallListener(&listener);
}
// destroy controller
SetNativeController(nullptr);
if (hostedController)
{
SetHostedApplication(nullptr);
delete hostedController;
}
StopWindowsNativeController();
return 0;
}
int SetupWindowsDirect2DRenderer()
{
return SetupWindowsDirect2DRendererInternal(false, false);
}
int SetupHostedWindowsDirect2DRenderer()
{
return SetupWindowsDirect2DRendererInternal(true, false);
}
int SetupRawWindowsDirect2DRenderer()
{
return SetupWindowsDirect2DRendererInternal(false, true);
}
/***********************************************************************
.\DIRECT2D\RENDERERS\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){}
GUI_DEFINE_COMPARE_OPERATORS(TextRange)
};
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;
// TODO: (enumerable) foreach:alterable(reversed) on group
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;
// TODO: (enumerable) foreach:alterable(reversed)) on dictionary
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];
// TODO: (enumerable) foreach:indexed(alterable(reversed)) on dictionary
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;
// TODO: (enumerable) foreach
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());
// TODO: (enumerable) foreach
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());
// TODO: (enumerable) foreach
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(nullptr)
,wrapLine(true)
,maxWidth(-1)
,caret(-1)
,caretFrontSide(false)
,caretBrush(0)
,formatDataAvailable(false)
,paragraphCallback(_paragraphCallback)
{
FontProperties defaultFont= windows::GetWindowsNativeController()->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->SetMaxWidth(65536);
textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);
}
graphicsElements.Add(TextRange(0, _text.Length()), nullptr);
backgroundColors.Add(TextRange(0, _text.Length()), Color(0, 0, 0, 0));
GetWindowsDirect2DResourceManager()->DestroyDirect2DTextFormat(defaultFont);
}
~WindowsDirect2DParagraph()
{
CloseCaret();
for (auto 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;
}
// TODO: (enumerable) foreach
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)ceil(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;
}
struct BackgroundRenderer
{
IWindowsDirect2DRenderTarget* renderTarget = nullptr;
ID2D1SolidColorBrush* lastBackgroundBrush = nullptr;
FLOAT lbx1, lby1, lbx2, lby2;
void RenderBackground()
{
if (lastBackgroundBrush)
{
renderTarget->GetDirect2DRenderTarget()->FillRectangle(
D2D1::RectF(lbx1, lby1, lbx2, lby2),
lastBackgroundBrush
);
lastBackgroundBrush = nullptr;
}
}
void SubmitBackground(ID2D1SolidColorBrush* brush, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2)
{
if (lastBackgroundBrush)
{
if (lastBackgroundBrush != brush)
{
RenderBackground();
}
else
{
FLOAT yc = (y1 + y2) / 2;
if (yc<lby1 || yc>lby2)
{
RenderBackground();
}
}
}
if (lastBackgroundBrush)
{
if (lbx1 > x1) lbx1 = x1;
if (lby1 > y1) lby1 = y1;
if (lbx2 < x2) lbx2 = x2;
if (lby2 < y2) lby2 = y2;
}
else
{
lastBackgroundBrush = brush;
lbx1 = x1;
lby1 = y1;
lbx2 = x2;
lby2 = y2;
}
}
};
void Render(Rect bounds)override
{
paragraphOffset = bounds.LeftTop();
PrepareFormatData();
{
BackgroundRenderer backgroundRenderer;
backgroundRenderer.renderTarget = renderTarget;
// TODO: (enumerable) foreach on dictionary
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;
backgroundRenderer.SubmitBackground(brush, x1, y1, x2, y2);
start = hitTest.textPosition + hitTest.length;
}
renderTarget->DestroyDirect2DBrush(color);
}
}
backgroundRenderer.RenderBackground();
}
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 Ptr(new WindowsDirect2DParagraph(this, text, renderTarget, callback));
}
}
}
}
/***********************************************************************
.\DIRECT2D\RENDERERS\GUIGRAPHICSRENDERERSWINDOWSDIRECT2D.CPP
***********************************************************************/
#include <math.h>
namespace vl
{
namespace presentation
{
namespace elements_windows_d2d
{
using namespace collections;
/***********************************************************************
GuiDirect2DElementRendererBase
***********************************************************************/
template<typename TElement, typename TRenderer>
void GuiDirect2DElementRendererBase<TElement, TRenderer>::InitializeInternal()
{
}
template<typename TElement, typename TRenderer>
void GuiDirect2DElementRendererBase<TElement, TRenderer>::FinalizeInternal()
{
static_cast<TRenderer*>(this)->DestroyBrush(this->renderTarget);
}
template<typename TElement, typename TRenderer>
void GuiDirect2DElementRendererBase<TElement, TRenderer>::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
{
static_cast<TRenderer*>(this)->DestroyBrush(oldRenderTarget);
static_cast<TRenderer*>(this)->CreateBrush(newRenderTarget);
}
/***********************************************************************
GuiSolidBrushElementRendererBase
***********************************************************************/
template<typename TElement, typename TRenderer, typename TBrush>
void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget)
{
oldColor = this->element->GetColor();
brush = _renderTarget->CreateDirect2DBrush(oldColor);
}
}
template<typename TElement, typename TRenderer, typename TBrush>
void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget && brush)
{
_renderTarget->DestroyDirect2DBrush(oldColor);
brush = 0;
}
}
template<typename TElement, typename TRenderer, typename TBrush>
void GuiSolidBrushElementRendererBase<TElement, TRenderer, TBrush>::OnElementStateChanged()
{
if (this->renderTarget)
{
Color color = this->element->GetColor();
if (oldColor != color)
{
DestroyBrush(this->renderTarget);
CreateBrush(this->renderTarget);
}
}
}
/***********************************************************************
GuiGradientBrushElementRendererBase
***********************************************************************/
template<typename TElement, typename TRenderer, typename TBrush>
void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget)
{
oldColor = Pair<Color, Color>(this->element->GetColor1(), this->element->GetColor2());
brush = _renderTarget->CreateDirect2DLinearBrush(oldColor.key, oldColor.value);
}
}
template<typename TElement, typename TRenderer, typename TBrush>
void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget && brush)
{
_renderTarget->DestroyDirect2DLinearBrush(oldColor.key, oldColor.value);
brush = 0;
}
}
template<typename TElement, typename TRenderer, typename TBrush>
void GuiGradientBrushElementRendererBase<TElement, TRenderer, TBrush>::OnElementStateChanged()
{
if (this->renderTarget)
{
auto color = Pair(this->element->GetColor1(), this->element->GetColor2());
if (oldColor != color)
{
DestroyBrush(this->renderTarget);
CreateBrush(this->renderTarget);
}
}
}
/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/
void GuiFocusRectangleElementRenderer::InitializeInternal()
{
}
void GuiFocusRectangleElementRenderer::FinalizeInternal()
{
focusRectangleEffect = nullptr;
}
void GuiFocusRectangleElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
{
focusRectangleEffect = nullptr;
if (newRenderTarget)
{
focusRectangleEffect = newRenderTarget->GetFocusRectangleEffect();
}
}
void GuiFocusRectangleElementRenderer::Render(Rect bounds)
{
if (focusRectangleEffect)
{
ID2D1RenderTarget* d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
ID2D1DeviceContext* d2dDeviceContext = nullptr;
HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
if (SUCCEEDED(hr))
{
FLOAT x = (FLOAT)bounds.Left();
FLOAT y = (FLOAT)bounds.Top();
FLOAT x2 = (FLOAT)bounds.Right() - 1;
FLOAT y2 = (FLOAT)bounds.Bottom() - 1;
FLOAT w = (FLOAT)bounds.Width();
FLOAT h = (FLOAT)bounds.Height();
d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y), D2D1::RectF(0, 0, w, 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y2), D2D1::RectF(0, y2 - y, w, h), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x, y + 1), D2D1::RectF(0, 1, 1, h - 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
d2dDeviceContext->DrawImage(focusRectangleEffect, D2D1::Point2F(x2, y + 1), D2D1::RectF(x2 - x, 1, w, h - 1), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_MASK_INVERT);
}
if (d2dDeviceContext)
{
d2dDeviceContext->Release();
}
}
}
void GuiFocusRectangleElementRenderer::OnElementStateChanged()
{
}
/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/
GuiSolidBorderElementRenderer::GuiSolidBorderElementRenderer()
{
}
void GuiSolidBorderElementRenderer::Render(Rect bounds)
{
ID2D1RenderTarget* d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
auto shape = element->GetShape();
switch (shape.shapeType)
{
case ElementShapeType::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 ElementShapeType::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;
case ElementShapeType::RoundRect:
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)shape.radiusX,
(FLOAT)shape.radiusY
),
brush
);
break;
}
}
/***********************************************************************
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 = nullptr;
}
if(brush2)
{
_renderTarget->DestroyDirect2DBrush(oldColor2);
brush2 = nullptr;
}
}
}
void Gui3DBorderElementRenderer::InitializeInternal()
{
}
void Gui3DBorderElementRenderer::FinalizeInternal()
{
DestroyBrush(renderTarget);
}
void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
{
DestroyBrush(oldRenderTarget);
CreateBrush(newRenderTarget);
}
Gui3DBorderElementRenderer::Gui3DBorderElementRenderer()
{
}
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 = nullptr;
}
if(brush2)
{
_renderTarget->DestroyDirect2DBrush(oldColor2);
brush2 = nullptr;
}
}
}
void Gui3DSplitterElementRenderer::InitializeInternal()
{
}
void Gui3DSplitterElementRenderer::FinalizeInternal()
{
DestroyBrush(renderTarget);
}
void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
{
DestroyBrush(oldRenderTarget);
CreateBrush(newRenderTarget);
}
Gui3DSplitterElementRenderer::Gui3DSplitterElementRenderer()
{
}
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
***********************************************************************/
GuiSolidBackgroundElementRenderer::GuiSolidBackgroundElementRenderer()
{
}
void GuiSolidBackgroundElementRenderer::Render(Rect bounds)
{
ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget();
auto shape = element->GetShape();
switch(shape.shapeType)
{
case ElementShapeType::Rectangle:
d2dRenderTarget->FillRectangle(
D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
brush
);
break;
case ElementShapeType::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;
case ElementShapeType::RoundRect:
d2dRenderTarget->FillRoundedRectangle(
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)shape.radiusX,
(FLOAT)shape.radiusY
),
brush
);
break;
}
}
/***********************************************************************
GuiGradientBackgroundElementRenderer
***********************************************************************/
GuiGradientBackgroundElementRenderer::GuiGradientBackgroundElementRenderer()
{
}
void GuiGradientBackgroundElementRenderer::Render(Rect bounds)
{
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();
auto shape = element->GetShape();
switch(shape.shapeType)
{
case ElementShapeType::Rectangle:
d2dRenderTarget->FillRectangle(
D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2),
brush
);
break;
case ElementShapeType::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;
case ElementShapeType::RoundRect:
d2dRenderTarget->FillRoundedRectangle(
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)shape.radiusX,
(FLOAT)shape.radiusY
),
brush
);
break;
}
}
/***********************************************************************
GuiInnerShadowElementRenderer
***********************************************************************/
void GuiInnerShadowElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget)
{
oldColor = element->GetColor();
transparentColor = Color(oldColor.r, oldColor.g, oldColor.b, 0);
linearBrush = _renderTarget->CreateDirect2DLinearBrush(transparentColor, oldColor);
radialBrush = _renderTarget->CreateDirect2DRadialBrush(transparentColor, oldColor);
}
}
void GuiInnerShadowElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)
{
if (_renderTarget)
{
_renderTarget->DestroyDirect2DLinearBrush(transparentColor, oldColor);
_renderTarget->DestroyDirect2DRadialBrush(transparentColor, oldColor);
linearBrush = nullptr;
radialBrush = nullptr;
}
}
void GuiInnerShadowElementRenderer::InitializeInternal()
{
}
void GuiInnerShadowElementRenderer::FinalizeInternal()
{
DestroyBrush(renderTarget);
}
void GuiInnerShadowElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)
{
DestroyBrush(oldRenderTarget);
CreateBrush(newRenderTarget);
}
GuiInnerShadowElementRenderer::GuiInnerShadowElementRenderer()
{
}
void GuiInnerShadowElementRenderer::Render(Rect bounds)
{
vint w = bounds.Width();
vint h = bounds.Height();
vint t = element->GetThickness();
vint bW = w - 2 * t;
vint bH = h - 2 * t;
if (bW > 0 && bH > 0)
{
vint x1 = bounds.Left();
vint x4 = bounds.Right();
vint x2 = x1 + t;
vint x3 = x4 - t;
vint y1 = bounds.Top();
vint y4 = bounds.Bottom();
vint y2 = y1 + t;
vint y3 = y4 - t;
auto d2dRenderTarget = renderTarget->GetDirect2DRenderTarget();
{
// top
linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y1));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x2, (FLOAT)y1, (FLOAT)x3, (FLOAT)y2), linearBrush);
}
{
// bottom
linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y3));
linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y4));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x2, (FLOAT)y3, (FLOAT)x3, (FLOAT)y4), linearBrush);
}
{
// left
linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x1, (FLOAT)y2));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y2, (FLOAT)x2, (FLOAT)y3), linearBrush);
}
{
// right
linearBrush->SetStartPoint(D2D1::Point2F((FLOAT)x3, (FLOAT)y2));
linearBrush->SetEndPoint(D2D1::Point2F((FLOAT)x4, (FLOAT)y2));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y2, (FLOAT)x4, (FLOAT)y3), linearBrush);
}
radialBrush->SetRadiusX((FLOAT)t);
radialBrush->SetRadiusY((FLOAT)t);
{
// left-top
radialBrush->SetCenter(D2D1::Point2F((FLOAT)x2, (FLOAT)y2));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y1, (FLOAT)x2, (FLOAT)y2), radialBrush);
}
{
// left-bottom
radialBrush->SetCenter(D2D1::Point2F((FLOAT)x3, (FLOAT)y2));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y1, (FLOAT)x4, (FLOAT)y2), radialBrush);
}
{
// right-top
radialBrush->SetCenter(D2D1::Point2F((FLOAT)x2, (FLOAT)y3));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x1, (FLOAT)y3, (FLOAT)x2, (FLOAT)y4), radialBrush);
}
{
// right-bottom
radialBrush->SetCenter(D2D1::Point2F((FLOAT)x3, (FLOAT)y3));
d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)x3, (FLOAT)y3, (FLOAT)x4, (FLOAT)y4), radialBrush);
}
}
}
void GuiInnerShadowElementRenderer::OnElementStateChanged()
{
if (renderTarget)
{
if (oldColor != element->GetColor())
{
DestroyBrush(renderTarget);
CreateBrush(renderTarget);
}
}
}
/***********************************************************************
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 = nullptr;
}
}
void GuiSolidLabelElementRenderer::CreateTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
{
if(_renderTarget)
{
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
oldFont=element->GetFont();
if (oldFont.fontFamily == L"") oldFont.fontFamily = windows::GetWindowsNativeController()->ResourceService()->GetDefaultFont().fontFamily;
if (oldFont.size == 0) oldFont.size = 12;
textFormat=resourceManager->CreateDirect2DTextFormat(oldFont);
}
}
void GuiSolidLabelElementRenderer::DestroyTextFormat(IWindowsDirect2DRenderTarget* _renderTarget)
{
if(_renderTarget && textFormat)
{
IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager();
resourceManager->DestroyDirect2DTextFormat(oldFont);
textFormat = nullptr;
}
}
void GuiSolidLabelElementRenderer::CreateTextLayout()
{
if (textFormat)
{
HRESULT hr = GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout(
oldText.Buffer(),
(int)oldText.Length(),
textFormat->textFormat.Obj(),
0,
0,
&textLayout);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
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);
}
}
}
void GuiSolidLabelElementRenderer::DestroyTextLayout()
{
if(textLayout)
{
textLayout->Release();
textLayout = nullptr;
}
}
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()
{
}
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;
{
HRESULT hr = textLayout->GetTrimming(&trimming, &inlineObject);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
}
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 (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount())
{
INativeImageFrame* frame = element->GetImage()->GetFrame(element->GetFrameIndex());
if (renderTarget)
{
bitmap = renderTarget->GetBitmap(frame, element->GetEnabled());
}
if (element->GetStretch())
{
minSize = Size(0, 0);
}
else
{
minSize = frame->GetSize();
}
}
else
{
bitmap = nullptr;
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;
HRESULT hr = GetWindowsDirect2DObjectProvider()->GetDirect2DFactory()->CreatePathGeometry(&pg);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
geometry = pg;
FillGeometry(Point(0, 0));
}
}
void GuiPolygonElementRenderer::DestroyGeometry()
{
if(geometry)
{
geometry = nullptr;
}
}
void GuiPolygonElementRenderer::FillGeometry(Point offset)
{
if (geometry)
{
ID2D1GeometrySink* pgs = 0;
HRESULT hr = geometry->Open(&pgs);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
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);
// TODO: (enumerable) foreach
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()
{
}
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
{
// TODO: (enumerable) foreach
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());
// TODO: (enumerable) foreach:indexed
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)
{
// TODO: (enumerable) foreach
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)
{
element->GetLines().SetCharMeasurer(nullptr);
resourceManager->DestroyDirect2DTextFormat(oldFont);
resourceManager->DestroyDirect2DCharMeasurer(oldFont);
}
oldFont = element->GetFont();
if (oldFont.fontFamily == L"") oldFont.fontFamily = windows::GetWindowsNativeController()->ResourceService()->GetDefaultFont().fontFamily;
if (oldFont.size == 0) oldFont.size = 12;
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()
{
element->SetCallback(nullptr);
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);
if (endColumn + 1 < line.dataLength && text::UTF16SPFirst(line.text[endColumn]) && text::UTF16SPSecond(line.text[startColumn + 1]))
{
endColumn++;
}
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)
{
UINT32 count = text::UTF16SPFirst(line.text[column]) && column + 1 < line.dataLength && text::UTF16SPSecond(line.text[column + 1]) ? 2 : 1;
d2dRenderTarget->DrawText(
(passwordChar ? &passwordChar : &line.text[column]),
count,
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
);
if (count == 2) column++;
}
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, element);
if(!renderTarget->IsClipperCoverWholeTarget())
{
ID2D1RenderTarget* rt=renderTarget->GetDirect2DRenderTarget();
GuiDirect2DElementEventArgs arguments(element, rt, fdw, fd2d, bounds);
element->Rendering.Execute(arguments);
}
renderTarget->PopClipper(element);
}
}
void GuiDirect2DElementRenderer::OnElementStateChanged()
{
}
}
}
}
/***********************************************************************
.\DIRECT2D\RENDERERS\GUIGRAPHICSWINDOWSDIRECT2D.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace elements
{
/***********************************************************************
GuiDirect2DElement
***********************************************************************/
GuiDirect2DElement::GuiDirect2DElement()
{
}
}
namespace elements_windows_d2d
{
using namespace elements;
using namespace collections;
using namespace windows;
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
***********************************************************************/
typedef Pair<Color, Color> ColorPair;
class CachedSolidBrushAllocator : public GuiCachedResourceAllocatorBase<CachedSolidBrushAllocator, Color, ComPtr<ID2D1SolidColorBrush>>
{
IWindowsDirect2DRenderTarget* guiRenderTarget = nullptr;
public:
CachedSolidBrushAllocator()
{
}
void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
{
guiRenderTarget = _guiRenderTarget;
}
ComPtr<ID2D1SolidColorBrush> CreateInternal(Color color)
{
ID2D1SolidColorBrush* brush = 0;
auto renderTarget = guiRenderTarget->GetDirect2DRenderTarget();
HRESULT hr = renderTarget->CreateSolidColorBrush(GetD2DColor(color), &brush);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
return brush;
}
};
class CachedLinearBrushAllocator : public GuiCachedResourceAllocatorBase<CachedLinearBrushAllocator, ColorPair, ComPtr<ID2D1LinearGradientBrush>>
{
IWindowsDirect2DRenderTarget* guiRenderTarget = nullptr;
public:
CachedLinearBrushAllocator()
{
}
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);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
}
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();
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
}
return brush;
}
};
class CachedRadialBrushAllocator : public GuiCachedResourceAllocatorBase<CachedRadialBrushAllocator, ColorPair, ComPtr<ID2D1RadialGradientBrush>>
{
IWindowsDirect2DRenderTarget* guiRenderTarget = nullptr;
public:
CachedRadialBrushAllocator()
{
}
void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget)
{
guiRenderTarget = _guiRenderTarget;
}
ComPtr<ID2D1RadialGradientBrush> 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);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
}
ID2D1RadialGradientBrush* brush = 0;
{
D2D1_POINT_2F points[2] = { {0, 0}, {0, 0} };
HRESULT hr = renderTarget->CreateRadialGradientBrush(
D2D1::RadialGradientBrushProperties(points[0], points[1], 1, 1),
stopCollection,
&brush);
stopCollection->Release();
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
}
return brush;
}
};
class CachedTextFormatAllocator : public GuiCachedResourceAllocatorBase<CachedTextFormatAllocator, 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);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
return format;
}
Ptr<Direct2DTextFormatPackage> CreateInternal(const FontProperties& fontProperties)
{
auto textFormat = Ptr(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 : public GuiCachedResourceAllocatorBase<CachedCharMeasurerAllocator, FontProperties, Ptr<text::CharMeasurer>>
{
protected:
class Direct2DCharMeasurer : public text::CharMeasurer
{
protected:
ComPtr<IDWriteTextFormat> font;
vint size;
Size MeasureInternal(text::UnicodeCodePoint codePoint, IGuiGraphicsRenderTarget* renderTarget)
{
Size charSize(0, 0);
IDWriteTextLayout* textLayout = 0;
UINT32 count = text::UTF16SPFirst(codePoint.characters[0]) && text::UTF16SPSecond(codePoint.characters[1]) ? 2 : 1;
HRESULT hr = GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout(
codePoint.characters,
count,
font.Obj(),
0,
0,
&textLayout);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
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(text::UnicodeCodePoint codePoint, IGuiGraphicsRenderTarget* renderTarget)
{
return MeasureInternal(codePoint, 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 Ptr(new Direct2DCharMeasurer(CachedTextFormatAllocator::CreateDirect2DFont(value), value.size));
}
};
/***********************************************************************
WindowsDirect2DRenderTarget
***********************************************************************/
class WindowsDirect2DImageFrameCache : public Object, public INativeImageFrameCache
{
protected:
IWindowsDirect2DRenderTarget* renderTarget = nullptr;
INativeImageFrame* cachedFrame = nullptr;
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
);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
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
);
CHECK_ERROR(SUCCEEDED(hr), L"You should check HRESULT to see why it failed.");
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 IWindowsDirect2DRenderTarget
{
typedef SortedList<Ptr<WindowsDirect2DImageFrameCache>> ImageCacheList;
protected:
INativeWindow* window;
ID2D1RenderTarget* d2dRenderTarget = nullptr;
ID2D1DeviceContext* d2dDeviceContext = nullptr;
List<Rect> clippers;
vint clipperCoverWholeTargetCounter = 0;
bool hostedRendering = false;
bool rendering = false;
CachedSolidBrushAllocator solidBrushes;
CachedLinearBrushAllocator linearBrushes;
CachedRadialBrushAllocator radialBrushes;
ImageCacheList imageCaches;
ComPtr<ID2D1Effect> focusRectangleEffect;
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 nullptr;
}
}
void StartRenderingOnNativeWindow() override
{
d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
CHECK_ERROR(d2dRenderTarget, L"vl::presentation::elements_windows_d2d::WindowsDirect2DRenderTarget::StartRenderingOnNativeWindow()#Invalid render target.");
GetWindowsDirect2DObjectProvider()->StartRendering(window);
}
RenderTargetFailure StopRenderingOnNativeWindow() override
{
CHECK_ERROR(d2dRenderTarget, L"vl::presentation::elements_windows_d2d::WindowsDirect2DRenderTarget::StopRenderingOnNativeWindow()#Invalid render target.");
auto result = GetWindowsDirect2DObjectProvider()->StopRenderingAndPresent(window);
d2dRenderTarget = nullptr;
return result;
}
Size GetCanvasSize() override
{
return window->Convert(window->GetClientSize());
}
void AfterPushedClipper(Rect clipper, Rect validArea, reflection::DescriptableObject* generator) override
{
d2dRenderTarget->PushAxisAlignedClip(
D2D1::RectF((FLOAT)validArea.x1, (FLOAT)validArea.y1, (FLOAT)validArea.x2, (FLOAT)validArea.y2),
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
}
void AfterPushedClipperAndBecameInvalid(Rect clipper, reflection::DescriptableObject* generator) override
{
}
void AfterPoppedClipperAndBecameValid(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
{
}
void AfterPoppedClipper(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
{
d2dRenderTarget->PopAxisAlignedClip();
}
public:
WindowsDirect2DRenderTarget(INativeWindow* _window)
: window(_window)
{
solidBrushes.SetRenderTarget(this);
linearBrushes.SetRenderTarget(this);
radialBrushes.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
{
auto d2dCache = Ptr(new WindowsDirect2DImageFrameCache(this));
if (frame->SetCache(this, d2dCache))
{
imageCaches.Add(d2dCache);
return d2dCache->GetBitmap(enabled);
}
else
{
return nullptr;
}
}
}
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());
}
}
ID2D1Effect* GetFocusRectangleEffect()override
{
if (!focusRectangleEffect)
{
ID2D1RenderTarget* d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window);
ID2D1DeviceContext* d2dDeviceContext = nullptr;
HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext);
if (d2dDeviceContext)
{
if (auto wicFactory = GetWICImagingFactory())
{
// this is the content of EffectBuffer.png in the same folder where this cpp file is in
static const char EffectBuffer[] =
"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x08\x00\x00\x00\x08\x08\x06\x00\x00"
"\x00\xC4\x0F\xBE\x8B\x00\x00\x00\x01\x73\x52\x47\x42\x00\xAE\xCE\x1C\xE9\x00\x00\x00\x04\x67\x41\x4D\x41\x00\x00"
"\xB1\x8F\x0B\xFC\x61\x05\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0E\xC3\x00\x00\x0E\xC3\x01\xC7\x6F\xA8\x64\x00"
"\x00\x00\x2F\x49\x44\x41\x54\x28\x53\x75\xC8\xB1\x0D\x00\x30\x0C\x02\x41\xEF\xBF\x34\xB1\x68\xA2\x47\xF8\x9B\x13"
"\x8C\xB6\xD9\x2E\x5D\x9E\x10\xA3\xE8\xF2\x84\x18\x45\x97\x27\xC4\x28\xBA\x3C\xBF\xD2\x03\xC2\xD7\x7F\x81\x23\x94"
"\x3E\x1F\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82";
auto effectImage = GetWindowsNativeController()->ImageService()->CreateImageFromMemory((void*)EffectBuffer, (vint)(sizeof(EffectBuffer) - 1));
IWICBitmap* wicEffectBitmap = effectImage ? GetWICBitmap(effectImage->GetFrame(0)) : nullptr;
if (wicEffectBitmap)
{
ID2D1Bitmap* d2dEffectBitmap = nullptr;
hr = d2dRenderTarget->CreateBitmapFromWicBitmap(wicEffectBitmap, &d2dEffectBitmap);
if (d2dEffectBitmap)
{
ID2D1Effect* d2dEffect = nullptr;
hr = d2dDeviceContext->CreateEffect(CLSID_D2D1Tile, &d2dEffect);
if (d2dEffect)
{
d2dEffect->SetInput(0, d2dEffectBitmap);
d2dEffect->SetValue(D2D1_TILE_PROP_RECT, D2D1::RectF(0, 0, 2, 2));
focusRectangleEffect = d2dEffect;
}
d2dEffectBitmap->Release();
}
}
}
d2dDeviceContext->Release();
}
}
return focusRectangleEffect.Obj();
}
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));
}
ID2D1RadialGradientBrush* CreateDirect2DRadialBrush(Color c1, Color c2)override
{
return radialBrushes.Create(Pair<Color, Color>(c1, c2)).Obj();
}
void DestroyDirect2DRadialBrush(Color c1, Color c2)override
{
radialBrushes.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 = Ptr(new WindowsDirect2DLayoutProvider);
}
IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
{
return GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window);
}
void RecreateRenderTarget(INativeWindow* window)override
{
NativeWindowDestroying(window);
GetWindowsDirect2DObjectProvider()->RecreateRenderTarget(window);
NativeWindowCreated(window);
}
void ResizeRenderTarget(INativeWindow* window)
{
GetWindowsDirect2DObjectProvider()->ResizeRenderTarget(window);
}
IGuiGraphicsLayoutProvider* GetLayoutProvider()override
{
return layoutProvider.Obj();
}
void NativeWindowCreated(INativeWindow* window)override
{
auto renderTarget = Ptr(new WindowsDirect2DRenderTarget(window));
renderTargets.Add(renderTarget);
GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, renderTarget.Obj());
}
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 = nullptr;
IWindowsDirect2DResourceManager* GetWindowsDirect2DResourceManager()
{
return windowsDirect2DResourceManager;
}
void SetWindowsDirect2DResourceManager(IWindowsDirect2DResourceManager* resourceManager)
{
windowsDirect2DResourceManager = resourceManager;
}
/***********************************************************************
OS Supporting
***********************************************************************/
IWindowsDirect2DObjectProvider* windowsDirect2DObjectProvider = nullptr;
IWindowsDirect2DObjectProvider* GetWindowsDirect2DObjectProvider()
{
return windowsDirect2DObjectProvider;
}
void SetWindowsDirect2DObjectProvider(IWindowsDirect2DObjectProvider* provider)
{
windowsDirect2DObjectProvider = provider;
}
}
}
}
/***********************************************************************
NativeMain
***********************************************************************/
using namespace vl::presentation;
using namespace vl::presentation::elements;
extern void GuiRawMain();
extern void GuiApplicationMain();
void RendererMainDirect2D(GuiHostedController* hostedController, bool raw)
{
elements_windows_d2d::WindowsDirect2DResourceManager resourceManager;
elements_windows_d2d::SetWindowsDirect2DResourceManager(&resourceManager);
windows::GetWindowsNativeController()->CallbackService()->InstallListener(&resourceManager);
auto hostedResourceManager = hostedController ? new GuiHostedGraphicsResourceManager(hostedController, &resourceManager) : nullptr;
SetGuiGraphicsResourceManager(
hostedResourceManager
? hostedResourceManager
: static_cast<IGuiGraphicsResourceManager*>(&resourceManager)
);
{
elements_windows_d2d::GuiFocusRectangleElementRenderer::Register();
elements_windows_d2d::GuiSolidBorderElementRenderer::Register();
elements_windows_d2d::Gui3DBorderElementRenderer::Register();
elements_windows_d2d::Gui3DSplitterElementRenderer::Register();
elements_windows_d2d::GuiSolidBackgroundElementRenderer::Register();
elements_windows_d2d::GuiGradientBackgroundElementRenderer::Register();
elements_windows_d2d::GuiInnerShadowElementRenderer::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();
if (hostedController) hostedController->Initialize();
if (raw)
{
GuiRawMain();
}
else
{
GuiApplicationMain();
}
if (hostedController) hostedController->Finalize();
}
SetGuiGraphicsResourceManager(nullptr);
if (hostedResourceManager) delete hostedResourceManager;
windows::GetWindowsNativeController()->CallbackService()->UninstallListener(&resourceManager);
elements_windows_d2d::SetWindowsDirect2DResourceManager(nullptr);
}
/***********************************************************************
.\GDI\WINGDI.CPP
***********************************************************************/
#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::SaveToStream(stream::IStream& Output, bool DIBV5ClipboardFormat)
{
if (FScanLines)
{
BITMAPFILEHEADER Header1;
BITMAPV5HEADER Header2;
if (!DIBV5ClipboardFormat)
{
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);
Output.Write(&Header1, 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;
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.SaveToStream(Output, false);
}
}
void WinBitmap::SaveToFile(WString FileName)
{
stream::FileStream Output(FileName, stream::FileStream::WriteOnly);
SaveToStream(Output, false);
}
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, DWORD styleCount, const DWORD* styleArray)
{
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, styleCount, styleArray);
}
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, NULL, NULL);
}
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=(ULONG_PTR)FDIBMemory;
FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, NULL, NULL);
}
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 Ptr(new WinBrush(RGB(255, 255, 255)));
}
WinPen::Ptr CreateDefaultPen()
{
return Ptr(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 Ptr(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);
}
int WinDC::SetRasterOperation(int rop2)
{
return SetROP2(FHandle, rop2);
}
/*------------------------------------------------------------------------------*/
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::GradientRectH(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_RECT* Rectangles, vint RectangleCount)
{
GradientFill(FHandle, Vertices, (int)VerticesCount, Rectangles, (int)RectangleCount, GRADIENT_FILL_RECT_H);
}
void WinDC::GradientRectV(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_RECT* Rectangles, vint RectangleCount)
{
GradientFill(FHandle, Vertices, (int)VerticesCount, Rectangles, (int)RectangleCount, GRADIENT_FILL_RECT_V);
}
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 Ptr(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 Ptr(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);
}
}
}
}
/***********************************************************************
.\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;
bool needPaintAfterResize = false;
vint DetermineBufferLength(vint minSize, vint minBound, vint maxBound, vint currentSize)
{
if(currentSize<minSize || currentSize>maxBound)
{
return minBound;
}
else
{
return currentSize;
}
}
NativeSize CalculateBufferSize()
{
NativeSize nativeWindowSize = window->GetClientSize();
Size windowSize(nativeWindowSize.x.value, nativeWindowSize.y.value);
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 NativeSize(newWidth, newHeight);
}
void RebuildCanvas(NativeSize size)
{
if (size.x < 256)size.x = 256;
if (size.y < 256)size.y = 256;
if (buffer)
{
if (buffer->GetWidth() != size.x.value || buffer->GetHeight() != size.y.value)
{
buffer = 0;
needPaintAfterResize = true;
}
}
if (!buffer)
{
buffer = Ptr(new WinBitmap(size.x.value, size.y.value, WinBitmap::vbb32Bits, true));
buffer->GetWinDC()->SetBackTransparent(true);
}
}
public:
GdiWindowsNativeWindowListener(INativeWindow* _window)
:window(_window)
{
}
void Moved()
{
RebuildCanvas(CalculateBufferSize());
}
void Paint()
{
// for the first Paint() call after Moved()
// trigger the global timer so that all GuiGraphicsHost with needToRender=true will call Render(false)
// so that to fill the buffer to prevent from seeing a black frame
// but it still leave some black color since the layout may needs more than 1 frame to finish
if (needPaintAfterResize)
{
needPaintAfterResize = false;
GetWindowsNativeController()->CallbackService()->Invoker()->InvokeGlobalTimer();
}
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;
~GdiWindowsNativeControllerListener()
{
// TODO: (enumerable) foreach on dictionary
for (vint i = 0; i < nativeWindowListeners.Count(); i++)
{
auto window = nativeWindowListeners.Keys()[i];
auto listener = nativeWindowListeners.Values()[i];
window->UninstallListener(listener.Obj());
}
}
void NativeWindowCreated(INativeWindow* window)
{
auto listener=Ptr(new GdiWindowsNativeWindowListener(window));
window->InstallListener(listener.Obj());
nativeWindowListeners.Add(window, listener);
}
void NativeWindowDestroying(INativeWindow* window)
{
auto listener=nativeWindowListeners[window];
nativeWindowListeners.Remove(window);
window->UninstallListener(listener.Obj());
}
};
GdiWindowsNativeControllerListener* gdiListener = nullptr;
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 SetupWindowsGDIRendererInternal(bool hosted, bool raw)
{
InitDpiAwareness(false);
CoInitializeEx(NULL, COINIT_MULTITHREADED);
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
WinGDIApplicationGDIObjectProvider objectProvider;
SetWindowsGDIObjectProvider(&objectProvider);
EnableCrossKernelCrashing();
// create controller
GuiHostedController* hostedController = nullptr;
StartWindowsNativeController(hInstance);
auto nativeController = GetWindowsNativeController();
if (hosted)
{
hostedController = new GuiHostedController(nativeController);
SetNativeController(hostedController);
SetHostedApplication(hostedController->GetHostedApplication());
}
else
{
SetNativeController(nativeController);
}
{
// install listener
GdiWindowsNativeControllerListener listener;
nativeController->CallbackService()->InstallListener(&listener);
gdiListener = &listener;
// main
RendererMainGDI(hostedController, raw);
// uninstall listener
gdiListener = nullptr;
nativeController->CallbackService()->UninstallListener(&listener);
}
// destroy controller
SetNativeController(nullptr);
if (hostedController)
{
SetHostedApplication(nullptr);
delete hostedController;
}
StopWindowsNativeController();
return 0;
}
int SetupWindowsGDIRenderer()
{
return SetupWindowsGDIRendererInternal(false, false);
}
int SetupHostedWindowsGDIRenderer()
{
return SetupWindowsGDIRendererInternal(true, false);
}
int SetupRawWindowsGDIRenderer()
{
return SetupWindowsGDIRendererInternal(false, true);
}
/***********************************************************************
.\GDI\RENDERERS\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=Ptr(new UniscribeParagraph);
paragraph->paragraphText=text;
auto fragment=Ptr(new UniscribeFragment(_text));
fragment->fontStyle=GetWindowsNativeController()->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+(vint)(caretBounds.Height()*1.5);
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 Ptr(new WindowsGDIParagraph(this, text, renderTarget, callback));
}
}
}
}
/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSRENDERERSWINDOWSGDI.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace elements_windows_gdi
{
using namespace windows;
using namespace collections;
/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/
void GuiFocusRectangleElementRenderer::InitializeInternal()
{
auto resourceManager = GetWindowsGDIResourceManager();
pen = resourceManager->GetFocusRectanglePen();
brush = resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
}
void GuiFocusRectangleElementRenderer::FinalizeInternal()
{
}
void GuiFocusRectangleElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
{
}
void GuiFocusRectangleElementRenderer::Render(Rect bounds)
{
int originRop2 = renderTarget->GetDC()->SetRasterOperation(R2_XORPEN);
renderTarget->GetDC()->SetPen(pen);
renderTarget->GetDC()->SetBrush(brush);
renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1);
renderTarget->GetDC()->SetRasterOperation(originRop2);
}
void GuiFocusRectangleElementRenderer::OnElementStateChanged()
{
}
/***********************************************************************
GuiSolidBorderElementRenderer
***********************************************************************/
void GuiSolidBorderElementRenderer::InitializeInternal()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldColor=element->GetColor();
pen=resourceManager->CreateGdiPen(oldColor);
brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0));
}
void GuiSolidBorderElementRenderer::FinalizeInternal()
{
auto 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);
auto shape = element->GetShape();
switch(shape.shapeType)
{
case ElementShapeType::Rectangle:
renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
break;
case ElementShapeType::Ellipse:
renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
break;
case ElementShapeType::RoundRect:
renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1, shape.radiusX * 2, shape.radiusY * 2);
break;
}
}
}
void GuiSolidBorderElementRenderer::OnElementStateChanged()
{
Color color=element->GetColor();
if(oldColor!=color)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiPen(oldColor);
oldColor=color;
pen=resourceManager->CreateGdiPen(oldColor);
}
}
/***********************************************************************
Gui3DBorderElementRenderer
***********************************************************************/
void Gui3DBorderElementRenderer::InitializeInternal()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldColor1=element->GetColor1();
oldColor2=element->GetColor2();
pen1=resourceManager->CreateGdiPen(oldColor1);
pen2=resourceManager->CreateGdiPen(oldColor2);
}
void Gui3DBorderElementRenderer::FinalizeInternal()
{
auto 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)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiPen(oldColor1);
oldColor1=color1;
pen1=resourceManager->CreateGdiPen(oldColor1);
}
Color color2=element->GetColor2();
if(oldColor2!=color2)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiPen(oldColor2);
oldColor2=color2;
pen2=resourceManager->CreateGdiPen(oldColor2);
}
}
/***********************************************************************
Gui3DSplitterElementRenderer
***********************************************************************/
void Gui3DSplitterElementRenderer::InitializeInternal()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldColor1=element->GetColor1();
oldColor2=element->GetColor2();
pen1=resourceManager->CreateGdiPen(oldColor1);
pen2=resourceManager->CreateGdiPen(oldColor2);
}
void Gui3DSplitterElementRenderer::FinalizeInternal()
{
auto 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)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiPen(oldColor1);
oldColor1=color1;
pen1=resourceManager->CreateGdiPen(oldColor1);
}
Color color2=element->GetColor2();
if(oldColor2!=color2)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiPen(oldColor2);
oldColor2=color2;
pen2=resourceManager->CreateGdiPen(oldColor2);
}
}
/***********************************************************************
GuiSolidBackgroundElementRenderer
***********************************************************************/
void GuiSolidBackgroundElementRenderer::InitializeInternal()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldColor=element->GetColor();
pen=resourceManager->CreateGdiPen(oldColor);
brush=resourceManager->CreateGdiBrush(oldColor);
}
void GuiSolidBackgroundElementRenderer::FinalizeInternal()
{
auto 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);
auto shape = element->GetShape();
switch(shape.shapeType)
{
case ElementShapeType::Rectangle:
renderTarget->GetDC()->FillRect(bounds.Left(), bounds.Top(), bounds.Right(), bounds.Bottom());
break;
case ElementShapeType::Ellipse:
renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1);
break;
case ElementShapeType::RoundRect:
renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right() - 1, bounds.Bottom() - 1, shape.radiusX * 2, shape.radiusY * 2);
break;
}
}
}
void GuiSolidBackgroundElementRenderer::OnElementStateChanged()
{
Color color=element->GetColor();
if(oldColor!=color)
{
auto 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)
{
Ptr<WinRegion> targetRegion, oldRegion, newRegion;
auto shape = element->GetShape();
switch (shape.shapeType)
{
case ElementShapeType::Ellipse:
targetRegion = Ptr(new WinRegion(bounds.x1, bounds.y1, bounds.x2 + 1, bounds.y2 + 1, false));
break;
case ElementShapeType::RoundRect:
targetRegion = Ptr(new WinRegion(bounds.x1, bounds.y1, bounds.x2 + 1, bounds.y2 + 1, shape.radiusX * 2, shape.radiusY * 2));
break;
}
if (targetRegion)
{
oldRegion = renderTarget->GetDC()->GetClipRegion();
newRegion = Ptr(new WinRegion(oldRegion, targetRegion, RGN_AND));
renderTarget->GetDC()->ClipRegion(newRegion);
}
switch (element->GetDirection())
{
case GuiGradientBackgroundElement::Horizontal:
case GuiGradientBackgroundElement::Vertical:
{
TRIVERTEX vertices[2];
GRADIENT_RECT rectangles[1];
vertices[0].x = (int)bounds.x1;
vertices[0].y = (int)bounds.y1;
vertices[1].x = (int)bounds.x2;
vertices[1].y = (int)bounds.y2;
rectangles[0].UpperLeft = 0;
rectangles[0].LowerRight = 1;
vertices[0].Red = color1.r << 8;
vertices[0].Green = color1.g << 8;
vertices[0].Blue = color1.b << 8;
vertices[0].Alpha = color1.a << 8;
vertices[1].Red = color2.r << 8;
vertices[1].Green = color2.g << 8;
vertices[1].Blue = color2.b << 8;
vertices[1].Alpha = color2.a << 8;
switch (element->GetDirection())
{
case GuiGradientBackgroundElement::Horizontal:
renderTarget->GetDC()->GradientRectH(vertices, sizeof(vertices) / sizeof(*vertices), rectangles, sizeof(rectangles) / sizeof(*rectangles));
break;
case GuiGradientBackgroundElement::Vertical:
renderTarget->GetDC()->GradientRectV(vertices, sizeof(vertices) / sizeof(*vertices), rectangles, sizeof(rectangles) / sizeof(*rectangles));
break;
}
}
break;
case GuiGradientBackgroundElement::Slash:
case GuiGradientBackgroundElement::Backslash:
{
TRIVERTEX vertices[4];
GRADIENT_TRIANGLE triangles[2];
switch (element->GetDirection())
{
case GuiGradientBackgroundElement::Slash:
vertices[0].x = (int)bounds.x2;
vertices[0].y = (int)bounds.y1;
vertices[1].x = (int)bounds.x1;
vertices[1].y = (int)bounds.y1;
vertices[2].x = (int)bounds.x2;
vertices[2].y = (int)bounds.y2;
vertices[3].x = (int)bounds.x1;
vertices[3].y = (int)bounds.y2;
break;
case GuiGradientBackgroundElement::Backslash:
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.y1;
vertices[3].x = (int)bounds.x2;
vertices[3].y = (int)bounds.y2;
break;
}
triangles[0].Vertex1 = 0;
triangles[0].Vertex2 = 1;
triangles[0].Vertex3 = 2;
triangles[1].Vertex1 = 1;
triangles[1].Vertex2 = 2;
triangles[1].Vertex3 = 3;
vertices[0].Red = color1.r << 8;
vertices[0].Green = color1.g << 8;
vertices[0].Blue = color1.b << 8;
vertices[0].Alpha = color1.a << 8;
vertices[1].Red = ((color1.r + color2.r) / 2) << 8;
vertices[1].Green = ((color1.g + color2.g) / 2) << 8;
vertices[1].Blue = ((color1.b + color2.b) / 2) << 8;
vertices[1].Alpha = ((color1.a + color2.a) / 2) << 8;
vertices[2].Red = ((color1.r + color2.r) / 2) << 8;
vertices[2].Green = ((color1.g + color2.g) / 2) << 8;
vertices[2].Blue = ((color1.b + color2.b) / 2) << 8;
vertices[2].Alpha = ((color1.a + color2.a) / 2) << 8;
vertices[3].Red = color2.r << 8;
vertices[3].Green = color2.g << 8;
vertices[3].Blue = color2.b << 8;
vertices[3].Alpha = color2.a << 8;
renderTarget->GetDC()->GradientTriangle(vertices, sizeof(vertices) / sizeof(*vertices), triangles, sizeof(triangles) / sizeof(*triangles));
}
break;
}
if (targetRegion)
{
renderTarget->GetDC()->ClipRegion(oldRegion);
}
}
}
void GuiGradientBackgroundElementRenderer::OnElementStateChanged()
{
}
/***********************************************************************
GuiSolidLabelElementRenderer
***********************************************************************/
void GuiInnerShadowElementRenderer::InitializeInternal()
{
}
void GuiInnerShadowElementRenderer::FinalizeInternal()
{
}
void GuiInnerShadowElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget)
{
}
GuiInnerShadowElementRenderer::GuiInnerShadowElementRenderer()
{
}
void GuiInnerShadowElementRenderer::Render(Rect bounds)
{
}
void GuiInnerShadowElementRenderer::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()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldFont=element->GetFont();
font=resourceManager->CreateGdiFont(oldFont);
}
void GuiSolidLabelElementRenderer::FinalizeInternal()
{
auto 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)
{
auto resourceManager=GetWindowsGDIResourceManager();
resourceManager->DestroyGdiFont(oldFont);
oldFont=fontProperties;
font=resourceManager->CreateGdiFont(oldFont);
}
UpdateMinSize();
}
/***********************************************************************
GuiImageFrameElementRenderer
***********************************************************************/
void GuiImageFrameElementRenderer::UpdateBitmap()
{
if (element->GetImage() && 0 <= element->GetFrameIndex() && element->GetFrameIndex() < element->GetImage()->GetFrameCount())
{
auto 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 = nullptr;
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)
{
auto 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()
{
auto resourceManager=GetWindowsGDIResourceManager();
pen=resourceManager->CreateGdiPen(oldPenColor);
brush=resourceManager->CreateGdiBrush(oldBrushColor);
}
void GuiPolygonElementRenderer::FinalizeInternal()
{
auto 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;
}
}
}
auto 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()
{
auto resourceManager=GetWindowsGDIResourceManager();
// TODO: (enumerable) foreach
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()
{
auto resourceManager=GetWindowsGDIResourceManager();
ColorArray newColors;
newColors.Resize(element->GetColors().Count());
// TODO: (enumerable) foreach:indexed
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()
{
auto resourceManager = GetWindowsGDIResourceManager();
if (font)
{
element->GetLines().SetCharMeasurer(nullptr);
resourceManager->DestroyGdiFont(oldFont);
resourceManager->DestroyCharMeasurer(oldFont);
font = nullptr;
}
oldFont = element->GetFont();
font = resourceManager->CreateGdiFont(oldFont);
element->GetLines().SetCharMeasurer(resourceManager->CreateCharMeasurer(oldFont).Obj());
}
void GuiColorizedTextElementRenderer::InitializeInternal()
{
auto resourceManager=GetWindowsGDIResourceManager();
oldCaretColor=element->GetCaretColor();
caretPen=resourceManager->CreateGdiPen(oldCaretColor);
element->SetCallback(this);
}
void GuiColorizedTextElementRenderer::FinalizeInternal()
{
element->SetCallback(nullptr);
auto 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);
if (text::UTF16SPFirst(line.text[endColumn]) && endColumn + 1 < line.dataLength && text::UTF16SPSecond(line.text[startColumn + 1]))
{
endColumn++;
}
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)
{
vint count = text::UTF16SPFirst(line.text[column]) && column + 1 < line.dataLength && text::UTF16SPSecond(line.text[column + 1]) ? 2 : 1;
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]), count);
}
if (count == 2) column++;
}
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)
{
auto 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, element);
if(!renderTarget->IsClipperCoverWholeTarget())
{
WinDC* dc=renderTarget->GetDC();
GuiGDIElementEventArgs arguments(element, dc, bounds);
element->Rendering.Execute(arguments);
}
renderTarget->PopClipper(element);
}
}
void GuiGDIElementRenderer::OnElementStateChanged()
{
}
}
}
}
/***********************************************************************
.\GDI\RENDERERS\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;
auto fragment=Ptr(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)
{
auto brush=Ptr(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)
{
auto brush=Ptr(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;
for (auto 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];
auto scriptItem = Ptr(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;
for (auto elementFragment : documentFragments)
{
vint elementLength=elementFragment->text.Length();
if(elementFragment->inlineObjectProperties)
{
if(elementCurrent<=currentStart && currentStart+shortLength<=elementCurrent+elementLength)
{
if(elementCurrent==currentStart)
{
auto run=Ptr(new 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)
{
auto run = Ptr(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;
auto newRun = Ptr(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
{
for (auto 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
{
auto virtualLine = Ptr(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;
for (auto run : scriptRuns)
{
for (auto 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)
{
for (auto 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()
{
for (auto 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;
for (auto 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;
for (auto fragment : documentFragments)
{
if(fragment->inlineObjectProperties)
{
if(!line)
{
line=Ptr(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=Ptr(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);
}
}
}
}
}
for (auto line : lines)
{
line->BuildUniscribeData(dc);
}
vint lineStart=0;
for (auto 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;
for (auto line : lines)
{
line->Layout(availableWidth, alignment, cy, cy);
}
// calculate paragraph bounds
vint minX=0;
vint minY=0;
vint maxX=0;
vint maxY=0;
for (auto 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;
for (auto line : lines)
{
for (auto 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();
for (auto 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;
}
auto elementFragment=Ptr(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?nullptr: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();
}
}
}
}
/***********************************************************************
.\GDI\RENDERERS\GUIGRAPHICSWINDOWSGDI.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace elements
{
/***********************************************************************
GuiGDIElement
***********************************************************************/
GuiGDIElement::GuiGDIElement()
{
}
}
namespace elements_windows_gdi
{
using namespace windows;
using namespace elements;
using namespace collections;
/***********************************************************************
WindowsGDIRenderTarget
***********************************************************************/
class WindowsGDIRenderTarget : public IWindowsGDIRenderTarget
{
protected:
INativeWindow* window = nullptr;
WinDC* dc = nullptr;
List<Rect> clippers;
vint clipperCoverWholeTargetCounter = 0;
bool hostedRendering = false;
bool rendering = false;
void ApplyClipper(Rect validArea, bool clipperExists)
{
if (clipperExists)
{
dc->ClipRegion(Ptr(new WinRegion(validArea.Left(), validArea.Top(), validArea.Right(), validArea.Bottom(), true)));
}
else
{
dc->RemoveClip();
}
}
void StartRenderingOnNativeWindow() override
{
dc = GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
}
RenderTargetFailure StopRenderingOnNativeWindow() override
{
dc = nullptr;
return RenderTargetFailure::None;
}
Size GetCanvasSize() override
{
return window->Convert(window->GetClientSize());
}
void AfterPushedClipper(Rect clipper, Rect validArea, reflection::DescriptableObject* generator) override
{
ApplyClipper(validArea, true);
}
void AfterPushedClipperAndBecameInvalid(Rect clipper, reflection::DescriptableObject* generator) override
{
}
void AfterPoppedClipperAndBecameValid(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
{
ApplyClipper(validArea, clipperExists);
}
void AfterPoppedClipper(Rect validArea, bool clipperExists, reflection::DescriptableObject* generator) override
{
ApplyClipper(validArea, clipperExists);
}
public:
WindowsGDIRenderTarget(INativeWindow* _window)
: window(_window)
{
}
WinDC* GetDC()override
{
return dc ? dc : GetWindowsGDIObjectProvider()->GetNativeWindowDC(window);
}
};
/***********************************************************************
CachedResourceAllocator
***********************************************************************/
class CachedPenAllocator : public GuiCachedResourceAllocatorBase<CachedPenAllocator, Color, Ptr<WinPen>>
{
public:
Ptr<WinPen> CreateInternal(Color color)
{
return Ptr(new WinPen(PS_SOLID, 1, RGB(color.r, color.g, color.b)));
}
};
class CachedBrushAllocator : public GuiCachedResourceAllocatorBase<CachedBrushAllocator, Color, Ptr<WinBrush>>
{
public:
Ptr<WinBrush> CreateInternal(Color color)
{
return color.a == 0 ? Ptr(new WinBrush) : Ptr(new WinBrush(RGB(color.r, color.g, color.b)));
}
};
class CachedFontAllocator : public GuiCachedResourceAllocatorBase<CachedFontAllocator, FontProperties, Ptr<WinFont>>
{
public:
static Ptr<WinFont> CreateGdiFont(const FontProperties& value)
{
vint size = value.size < 0 ? value.size : -value.size;
return Ptr(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 : public GuiCachedResourceAllocatorBase<CachedCharMeasurerAllocator, FontProperties, Ptr<text::CharMeasurer>>
{
protected:
class GdiCharMeasurer : public text::CharMeasurer
{
protected:
Ptr<WinFont> font;
vint size;
Size MeasureInternal(text::UnicodeCodePoint codePoint, IGuiGraphicsRenderTarget* renderTarget)
{
if (renderTarget)
{
WindowsGDIRenderTarget* gdiRenderTarget = dynamic_cast<WindowsGDIRenderTarget*>(renderTarget);
WinDC* dc = gdiRenderTarget->GetDC();
dc->SetFont(font);
vint count = text::UTF16SPFirst(codePoint.characters[0]) && text::UTF16SPSecond(codePoint.characters[1]) ? 2 : 1;
SIZE size = dc->MeasureBuffer(codePoint.characters, count, -1);
return Size(size.cx, size.cy);
}
else
{
return Size(0, 0);
}
}
vint MeasureWidthInternal(text::UnicodeCodePoint codePoint, IGuiGraphicsRenderTarget* renderTarget)
{
return MeasureInternal(codePoint, 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 Ptr(new GdiCharMeasurer(CachedFontAllocator::CreateGdiFont(value), value.size));
}
};
/***********************************************************************
WindowsGDIResourceManager
***********************************************************************/
class WindowsGDIImageFrameCache : public Object, public INativeImageFrameCache
{
protected:
IWindowsGDIResourceManager* resourceManager = nullptr;
INativeImageFrame* cachedFrame = nullptr;
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 = Ptr(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, (UINT)bitmap->GetLineBytes(), (UINT)(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 = Ptr(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;
Ptr<WinPen> focusRectanglePen;
CachedPenAllocator pens;
CachedBrushAllocator brushes;
CachedFontAllocator fonts;
CachedCharMeasurerAllocator charMeasurers;
ImageCacheList imageCaches;
public:
WindowsGDIResourceManager()
{
layoutProvider = Ptr(new WindowsGDILayoutProvider);
}
IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override
{
return GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window);
}
void RecreateRenderTarget(INativeWindow* window)override
{
}
void ResizeRenderTarget(INativeWindow* window)override
{
}
IGuiGraphicsLayoutProvider* GetLayoutProvider()override
{
return layoutProvider.Obj();
}
void NativeWindowCreated(INativeWindow* window)override
{
auto renderTarget = Ptr(new WindowsGDIRenderTarget(window));
renderTargets.Add(renderTarget);
GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, renderTarget.Obj());
}
void NativeWindowDestroying(INativeWindow* window)override
{
WindowsGDIRenderTarget* renderTarget = dynamic_cast<WindowsGDIRenderTarget*>(GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window));
GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, 0);
renderTargets.Remove(renderTarget);
}
Ptr<windows::WinPen> GetFocusRectanglePen()override
{
if (!focusRectanglePen)
{
DWORD styleArray[] = { 1,1 };
focusRectanglePen = Ptr(new WinPen(PS_USERSTYLE, PS_ENDCAP_FLAT, PS_JOIN_BEVEL, 1, RGB(255, 255, 255), (DWORD)(sizeof(styleArray) / sizeof(*styleArray)), styleArray));
}
return focusRectanglePen;
}
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
{
auto gdiCache = Ptr(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 = nullptr;
IWindowsGDIResourceManager* GetWindowsGDIResourceManager()
{
return windowsGDIResourceManager;
}
void SetWindowsGDIResourceManager(IWindowsGDIResourceManager* resourceManager)
{
windowsGDIResourceManager = resourceManager;
}
/***********************************************************************
OS Supporting
***********************************************************************/
IWindowsGDIObjectProvider* windowsGDIObjectProvider = nullptr;
IWindowsGDIObjectProvider* GetWindowsGDIObjectProvider()
{
return windowsGDIObjectProvider;
}
void SetWindowsGDIObjectProvider(IWindowsGDIObjectProvider* provider)
{
windowsGDIObjectProvider = provider;
}
}
}
}
/***********************************************************************
NativeMain
***********************************************************************/
using namespace vl::presentation;
using namespace vl::presentation::elements;
extern void GuiRawMain();
extern void GuiApplicationMain();
void RendererMainGDI(GuiHostedController* hostedController, bool raw)
{
elements_windows_gdi::WindowsGDIResourceManager resourceManager;
elements_windows_gdi::SetWindowsGDIResourceManager(&resourceManager);
windows::GetWindowsNativeController()->CallbackService()->InstallListener(&resourceManager);
auto hostedResourceManager = hostedController ? new GuiHostedGraphicsResourceManager(hostedController, &resourceManager) : nullptr;
SetGuiGraphicsResourceManager(
hostedResourceManager
? hostedResourceManager
: static_cast<IGuiGraphicsResourceManager*>(&resourceManager)
);
{
elements_windows_gdi::GuiFocusRectangleElementRenderer::Register();
elements_windows_gdi::GuiSolidBorderElementRenderer::Register();
elements_windows_gdi::Gui3DBorderElementRenderer::Register();
elements_windows_gdi::Gui3DSplitterElementRenderer::Register();
elements_windows_gdi::GuiSolidBackgroundElementRenderer::Register();
elements_windows_gdi::GuiGradientBackgroundElementRenderer::Register();
elements_windows_gdi::GuiInnerShadowElementRenderer::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();
if (hostedController) hostedController->Initialize();
if (raw)
{
GuiRawMain();
}
else
{
GuiApplicationMain();
}
if (hostedController) hostedController->Finalize();
}
SetGuiGraphicsResourceManager(nullptr);
if (hostedResourceManager) delete hostedResourceManager;
windows::GetWindowsNativeController()->CallbackService()->UninstallListener(&resourceManager);
elements_windows_gdi::SetWindowsGDIResourceManager(nullptr);
}
/***********************************************************************
.\SERVICESIMPL\WINDOWSCLIPBOARDSERVICE.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace windows
{
using namespace collections;
/***********************************************************************
WindowsClipboardReader
***********************************************************************/
bool WindowsClipboardReader::ContainsFormat(UINT format)
{
UINT currentFormat = 0;
while (currentFormat = ::EnumClipboardFormats(currentFormat))
{
if (currentFormat == format)
{
return true;
}
}
return false;
}
WindowsClipboardReader::WindowsClipboardReader(WindowsClipboardService* _service)
:service(_service)
{
}
WindowsClipboardReader::~WindowsClipboardReader()
{
CloseClipboard();
}
bool WindowsClipboardReader::ContainsText()
{
return ContainsFormat(CF_UNICODETEXT);
}
WString WindowsClipboardReader::GetText()
{
WString result;
HANDLE handle = ::GetClipboardData(CF_UNICODETEXT);
if (handle != 0)
{
wchar_t* buffer = (wchar_t*)::GlobalLock(handle);
result = buffer;
::GlobalUnlock(handle);
}
return result;
}
bool WindowsClipboardReader::ContainsDocument()
{
return ContainsFormat(service->WCF_Document);
}
Ptr<DocumentModel> WindowsClipboardReader::GetDocument()
{
HANDLE handle = ::GetClipboardData(service->WCF_Document);
if (handle != 0)
{
auto buffer = ::GlobalLock(handle);
auto size = ::GlobalSize(handle);
stream::MemoryWrapperStream memoryStream(buffer, (vint)size);
auto document = LoadDocumentFromClipboardStream(memoryStream);
::GlobalUnlock(handle);
return document;
}
return nullptr;
}
bool WindowsClipboardReader::ContainsImage()
{
return ContainsFormat(CF_BITMAP);
}
Ptr<INativeImage> WindowsClipboardReader::GetImage()
{
HBITMAP handle = (HBITMAP)::GetClipboardData(CF_BITMAP);
if (handle != 0)
{
return CreateImageFromHBITMAP(handle);
}
return nullptr;
}
void WindowsClipboardReader::CloseClipboard()
{
if (service->reader)
{
::CloseClipboard();
service->reader = nullptr;
}
}
/***********************************************************************
WindowsClipboardWriter
***********************************************************************/
void WindowsClipboardWriter::SetClipboardData(UINT format, stream::MemoryStream& memoryStream)
{
memoryStream.SeekFromBegin(0);
HGLOBAL data = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)memoryStream.Size());
auto buffer = ::GlobalLock(data);
memoryStream.Read(buffer, (vint)memoryStream.Size());
::GlobalUnlock(data);
::SetClipboardData(format, data);
}
WindowsClipboardWriter::WindowsClipboardWriter(WindowsClipboardService* _service)
:service(_service)
{
}
WindowsClipboardWriter::~WindowsClipboardWriter()
{
}
void WindowsClipboardWriter::SetText(const WString& value)
{
textData = value;
}
void WindowsClipboardWriter::SetDocument(Ptr<DocumentModel> value)
{
documentData = value;
if (!textData)
{
textData = documentData->GetText(true);
}
if (!imageData && documentData->paragraphs.Count() == 1)
{
Ptr<DocumentContainerRun> container = documentData->paragraphs[0];
while (container)
{
if (container->runs.Count() != 1) goto FAILED;
if (auto imageRun = container->runs[0].Cast<DocumentImageRun>())
{
imageData = imageRun->image;
break;
}
else
{
container = container->runs[0].Cast<DocumentContainerRun>();
}
}
FAILED:;
}
ModifyDocumentForClipboard(documentData);
}
void WindowsClipboardWriter::SetImage(Ptr<INativeImage> value)
{
imageData = value;
}
bool WindowsClipboardWriter::Submit()
{
if (service->reader)
{
service->reader->CloseClipboard();
}
if (!::OpenClipboard(service->ownerHandle)) return false;
::EmptyClipboard();
if (textData)
{
vint size = (textData.Value().Length() + 1) * sizeof(wchar_t);
HGLOBAL data = ::GlobalAlloc(GMEM_MOVEABLE, size);
auto buffer = (wchar_t*)::GlobalLock(data);
memcpy(buffer, textData.Value().Buffer(), size);
::GlobalUnlock(data);
::SetClipboardData(CF_UNICODETEXT, data);
}
if (documentData)
{
{
stream::MemoryStream memoryStream;
SaveDocumentToClipboardStream(documentData, memoryStream);
SetClipboardData(service->WCF_Document, memoryStream);
}
{
stream::MemoryStream memoryStream;
SaveDocumentToRtfStream(documentData, memoryStream);
SetClipboardData(service->WCF_RTF, memoryStream);
}
{
stream::MemoryStream memoryStream;
SaveDocumentToHtmlClipboardStream(documentData, memoryStream);
SetClipboardData(service->WCF_HTML, memoryStream);
}
}
if (imageData && imageData->GetFrameCount()>0)
{
if (auto wicBitmap = GetWICBitmap(imageData->GetFrame(0)))
{
UINT width = 0;
UINT height = 0;
wicBitmap->GetSize(&width, &height);
auto bitmap = Ptr(new WinBitmap((vint)width, (vint)height, WinBitmap::vbb32Bits, true));
WICRect rect;
rect.X = 0;
rect.Y = 0;
rect.Width = (INT)width;
rect.Height = (INT)height;
wicBitmap->CopyPixels(&rect, (UINT)bitmap->GetLineBytes(), (UINT)(bitmap->GetLineBytes()*height), (BYTE*)bitmap->GetScanLines()[0]);
stream::MemoryStream memoryStream;
bitmap->SaveToStream(memoryStream, true);
SetClipboardData(CF_DIBV5, memoryStream);
}
}
::CloseClipboard();
return true;
}
/***********************************************************************
WindowsClipboardService
***********************************************************************/
class WindowsFakeClipboardReader : public Object, public INativeClipboardReader
{
public:
bool ContainsText()override { return false; }
WString GetText()override { return L""; }
bool ContainsDocument()override { return false; }
Ptr<DocumentModel> GetDocument()override { return nullptr; }
bool ContainsImage()override { return false; }
Ptr<INativeImage> GetImage()override { return nullptr; }
};
WindowsClipboardService::WindowsClipboardService()
:ownerHandle(NULL)
{
WCF_Document = ::RegisterClipboardFormat(L"GacUI Document Format");
WCF_RTF = ::RegisterClipboardFormat(L"Rich Text Format");
WCF_HTML = ::RegisterClipboardFormat(L"HTML Format");
}
Ptr<INativeClipboardReader> WindowsClipboardService::ReadClipboard()
{
if (!reader)
{
if (!::OpenClipboard(ownerHandle)) return Ptr(new WindowsFakeClipboardReader);
reader = new WindowsClipboardReader(this);
}
return Ptr(reader);
}
Ptr<INativeClipboardWriter> WindowsClipboardService::WriteClipboard()
{
return Ptr(new WindowsClipboardWriter(this));
}
void WindowsClipboardService::SetOwnerHandle(HWND handle)
{
HWND oldHandle=ownerHandle;
ownerHandle=handle;
if(handle==NULL)
{
::RemoveClipboardFormatListener(oldHandle);
}
else
{
::AddClipboardFormatListener(ownerHandle);
}
}
}
}
}
/***********************************************************************
.\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;
// TODO: (enumerable) foreach
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;
}
}
}
}
/***********************************************************************
.\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()
{
}
INativeImage* WindowsImageFrame::GetImage()
{
return image;
}
Size WindowsImageFrame::GetSize()
{
UINT width=0;
UINT height=0;
frameBitmap->GetSize(&width, &height);
return Size(width, height);
}
IWICBitmap* WindowsImageFrame::GetFrameBitmap()
{
return frameBitmap.Obj();
}
void WindowsImageFrame::SaveBitmapToStream(stream::IStream& imageStream)
{
UINT width = 0;
UINT height = 0;
frameBitmap->GetSize(&width, &height);
auto bitmap = Ptr(new WinBitmap((vint)width, (vint)height, WinBitmap::vbb32Bits, true));
WICRect rect;
rect.X = 0;
rect.Y = 0;
rect.Width = (INT)width;
rect.Height = (INT)height;
frameBitmap->CopyPixels(&rect, (UINT)bitmap->GetLineBytes(), (UINT)(bitmap->GetLineBytes()*height), (BYTE*)bitmap->GetScanLines()[0]);
bitmap->SaveToStream(imageStream, false);
}
/***********************************************************************
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=Ptr(new WindowsImageFrame(this, frameDecode));
frameDecode->Release();
}
}
return frame.Obj();
}
else
{
return 0;
}
}
void CopyMetadataBody(IWICMetadataQueryReader* reader, IWICMetadataQueryWriter* writer, const WString& prefix)
{
IEnumString* enumString = nullptr;
HRESULT hr = reader->GetEnumerator(&enumString);
if (enumString)
{
while (true)
{
LPOLESTR metadataName = nullptr;
ULONG fetched = 0;
hr = enumString->Next(1, &metadataName, &fetched);
if (hr != S_OK) break;
if (fetched == 0) break;
PROPVARIANT metadataValue;
PropVariantInit(&metadataValue);
hr = reader->GetMetadataByName(metadataName, &metadataValue);
if (hr == S_OK)
{
if (metadataValue.vt == VT_UNKNOWN && metadataValue.punkVal)
{
IWICMetadataQueryReader* embeddedReader = nullptr;
hr = metadataValue.punkVal->QueryInterface<IWICMetadataQueryReader>(&embeddedReader);
if (embeddedReader)
{
CopyMetadataBody(embeddedReader, writer, prefix + metadataName);
embeddedReader->Release();
}
}
else
{
hr = writer->SetMetadataByName((prefix + metadataName).Buffer(), &metadataValue);
}
hr = PropVariantClear(&metadataValue);
}
CoTaskMemFree(metadataName);
}
enumString->Release();
}
}
template<typename TDecoder, typename TEncoder>
void CopyMetadata(TDecoder* decoder, TEncoder* encoder)
{
IWICMetadataQueryReader* reader = nullptr;
IWICMetadataQueryWriter* writer = nullptr;
HRESULT hr = decoder->GetMetadataQueryReader(&reader);
hr = encoder->GetMetadataQueryWriter(&writer);
if (reader && writer)
{
CopyMetadataBody(reader, writer, WString::Empty);
}
if (reader) reader->Release();
if (writer) writer->Release();
}
GUID GetGuidFromFormat(INativeImage::FormatType formatType)
{
switch (formatType)
{
case INativeImage::Bmp: return GUID_ContainerFormatBmp;
case INativeImage::Gif: return GUID_ContainerFormatGif;
case INativeImage::Icon: return GUID_ContainerFormatIco;
case INativeImage::Jpeg: return GUID_ContainerFormatJpeg;
case INativeImage::Png: return GUID_ContainerFormatPng;
case INativeImage::Tiff: return GUID_ContainerFormatTiff;
case INativeImage::Wmp: return GUID_ContainerFormatWmp;
default: CHECK_FAIL(L"GetGuidFromFormat(INativeImage::FormatType)#Unexpected format type.");
}
}
void MoveIStreamToStream(IStream* pIStream, stream::IStream& outputStream)
{
LARGE_INTEGER dlibMove;
dlibMove.QuadPart = 0;
HRESULT hr = pIStream->Seek(dlibMove, STREAM_SEEK_SET, NULL);
Array<char> buffer(65536);
while (true)
{
ULONG count = (ULONG)buffer.Count();
ULONG read = 0;
hr = pIStream->Read(&buffer[0], count, &read);
if (read > 0)
{
outputStream.Write(&buffer[0], (vint)read);
}
if (read != count)
{
break;
}
}
}
void WindowsImage::SaveToStream(stream::IStream& imageStream, FormatType formatType)
{
auto factory = GetWICImagingFactory();
GUID formatGUID;
HRESULT hr;
bool sameFormat = formatType == INativeImage::Unknown || formatType == GetFormat();
if (formatType == INativeImage::Unknown)
{
hr = bitmapDecoder->GetContainerFormat(&formatGUID);
if (hr != S_OK) goto FAILED;
}
else
{
formatGUID = GetGuidFromFormat(formatType);
}
{
IWICBitmapEncoder* bitmapEncoder = nullptr;
hr = factory->CreateEncoder(formatGUID, NULL, &bitmapEncoder);
if (!bitmapEncoder) goto FAILED;
IStream* pIStream = nullptr;
hr = CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
if (!pIStream)
{
bitmapEncoder->Release();
goto FAILED;
}
hr = bitmapEncoder->Initialize(pIStream, WICBitmapEncoderNoCache);
if (hr != S_OK)
{
pIStream->Release();
bitmapEncoder->Release();
goto FAILED;
}
{
UINT actualCount = 0;
Array<IWICColorContext*> colorContexts(16);
hr = bitmapDecoder->GetColorContexts((UINT)colorContexts.Count(), &colorContexts[0], &actualCount);
if (hr == S_OK)
{
if ((vint)actualCount > colorContexts.Count())
{
// TODO: (enumerable) foreach
for (vint i = 0; i < colorContexts.Count(); i++) colorContexts[i]->Release();
colorContexts.Resize((vint)actualCount);
bitmapDecoder->GetColorContexts(actualCount, &colorContexts[0], &actualCount);
}
if (actualCount > 0)
{
bitmapEncoder->SetColorContexts(actualCount, &colorContexts[0]);
for (vint i = 0; i < (vint)actualCount; i++) colorContexts[i]->Release();
}
}
}
{
IWICPalette* palette = nullptr;
hr = factory->CreatePalette(&palette);
if (palette)
{
hr = bitmapDecoder->CopyPalette(palette);
if (hr == S_OK)
{
bitmapEncoder->SetPalette(palette);
}
palette->Release();
}
}
{
IWICBitmapSource* source = nullptr;
hr = bitmapDecoder->GetPreview(&source);
if (source)
{
bitmapEncoder->SetPreview(source);
source->Release();
}
}
{
IWICBitmapSource* source = nullptr;
hr = bitmapDecoder->GetThumbnail(&source);
if (source)
{
bitmapEncoder->SetThumbnail(source);
source->Release();
}
}
if (sameFormat)
{
CopyMetadata(bitmapDecoder.Obj(), bitmapEncoder);
}
UINT frameCount = 0;
bitmapDecoder->GetFrameCount(&frameCount);
for (UINT i = 0; i < frameCount; i++)
{
IWICBitmapFrameDecode* frameDecode = nullptr;
IWICBitmapFrameEncode* frameEncode = nullptr;
hr = bitmapDecoder->GetFrame(i, &frameDecode);
hr = bitmapEncoder->CreateNewFrame(&frameEncode, NULL);
if (frameDecode && frameEncode)
{
hr = frameEncode->Initialize(NULL);
if (sameFormat)
{
CopyMetadata(frameDecode, frameEncode);
}
hr = frameEncode->WriteSource(frameDecode, NULL);
hr = frameEncode->Commit();
}
if (frameDecode) frameDecode->Release();
if (frameEncode) frameEncode->Release();
}
hr = bitmapEncoder->Commit();
bitmapEncoder->Release();
MoveIStreamToStream(pIStream, imageStream);
pIStream->Release();
}
FAILED:;
}
/***********************************************************************
WindowsBitmapImage
***********************************************************************/
WindowsBitmapImage::WindowsBitmapImage(INativeImageService* _imageService, IWICBitmap* sourceBitmap, FormatType _formatType)
:imageService(_imageService)
,formatType(_formatType)
{
frame = Ptr(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;
}
void WindowsBitmapImage::SaveToStream(stream::IStream& imageStream, FormatType formatType)
{
auto factory = GetWICImagingFactory();
if (formatType == INativeImage::Unknown)
{
formatType = INativeImage::Bmp;
}
GUID formatGUID = GetGuidFromFormat(formatType);
IWICBitmapEncoder* bitmapEncoder = nullptr;
HRESULT hr = factory->CreateEncoder(formatGUID, NULL, &bitmapEncoder);
if (!bitmapEncoder) goto FAILED;
{
IStream* pIStream = nullptr;
hr = CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
if (!pIStream)
{
bitmapEncoder->Release();
goto FAILED;
}
hr = bitmapEncoder->Initialize(pIStream, WICBitmapEncoderNoCache);
if (hr != S_OK)
{
pIStream->Release();
bitmapEncoder->Release();
goto FAILED;
}
{
IWICBitmapFrameEncode* frameEncode = nullptr;
hr = bitmapEncoder->CreateNewFrame(&frameEncode, NULL);
if (frameEncode)
{
hr = frameEncode->Initialize(NULL);
hr = frameEncode->WriteSource(frame->GetFrameBitmap(), NULL);
hr = frameEncode->Commit();
frameEncode->Release();
}
}
hr = bitmapEncoder->Commit();
bitmapEncoder->Release();
MoveIStreamToStream(pIStream, imageStream);
pIStream->Release();
}
FAILED:;
}
/***********************************************************************
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 Ptr(new WindowsImage(this, bitmapDecoder));
}
else
{
return nullptr;
}
}
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=Ptr(new WindowsImage(this, bitmapDecoder));
}
stream->Release();
}
return result;
}
Ptr<INativeImage> WindowsImageService::CreateImageFromStream(stream::IStream& imageStream)
{
stream::MemoryStream memoryStream;
char buffer[65536];
while (true)
{
vint length = imageStream.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))
{
auto image=Ptr(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))
{
auto image=Ptr(new WindowsBitmapImage(this, bitmap, INativeImage::Icon));
bitmap->Release();
return image;
}
else
{
return 0;
}
}
IWICImagingFactory* WindowsImageService::GetImagingFactory()
{
return imagingFactory.Obj();
}
/***********************************************************************
Helper Functions
***********************************************************************/
WindowsImageService* GetWindowsImageService()
{
return dynamic_cast<WindowsImageService*>(GetWindowsNativeController()->ImageService());
}
IWICImagingFactory* GetWICImagingFactory()
{
return GetWindowsImageService()->GetImagingFactory();
}
IWICBitmap* GetWICBitmap(INativeImageFrame* frame)
{
return dynamic_cast<WindowsImageFrame*>(frame)->GetFrameBitmap();
}
Ptr<INativeImage> CreateImageFromHBITMAP(HBITMAP handle)
{
return GetWindowsImageService()->CreateImageFromHBITMAP(handle);
}
Ptr<INativeImage> CreateImageFromHICON(HICON handle)
{
return GetWindowsImageService()->CreateImageFromHICON(handle);
}
}
}
}
/***********************************************************************
.\SERVICESIMPL\WINDOWSINPUTSERVICE.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace windows
{
bool WinIsKeyPressing(VKEY code)
{
return (GetKeyState((int)code)&0xF0)!=0;
}
bool WinIsKeyToggled(VKEY code)
{
return (GetKeyState((int)code)&0x0F)!=0;
}
/***********************************************************************
WindowsInputService
***********************************************************************/
WString WindowsInputService::GetKeyNameInternal(VKEY code)
{
if ((vint)code < 8) return L"?";
wchar_t name[256]={0};
vint scanCode=MapVirtualKey((int)code, MAPVK_VK_TO_VSC)<<16;
switch((vint)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((VKEY)i);
if (keyNames[i] != L"?")
{
keys.Set(keyNames[i], (VKEY)i);
}
}
}
WindowsInputService::WindowsInputService()
:ownerHandle(NULL)
,isTimerEnabled(false)
,keyNames((vint)VKEY::KEY_MAXIMUM)
{
InitializeKeyNames();
}
void WindowsInputService::SetOwnerHandle(HWND handle)
{
ownerHandle=handle;
}
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(VKEY code)
{
return WinIsKeyPressing(code);
}
bool WindowsInputService::IsKeyToggled(VKEY code)
{
return WinIsKeyToggled(code);
}
WString WindowsInputService::GetKeyName(VKEY code)
{
if (0 <= (vint)code && (vint)code < keyNames.Count())
{
return keyNames[(vint)code];
}
else
{
return WString::Unmanaged(L"?");
}
}
VKEY WindowsInputService::GetKey(const WString& name)
{
vint index = keys.Keys().IndexOf(name);
return index == -1 ? VKEY::KEY_UNKNOWN : keys.Values()[index];
}
vint WindowsInputService::RegisterGlobalShortcutKey(bool ctrl, bool shift, bool alt, VKEY key)
{
UINT modifier = 0;
if (ctrl) modifier |= MOD_CONTROL;
if (shift) modifier |= MOD_SHIFT;
if (alt) modifier |= MOD_ALT;
vint id = ++usedHotKeys;
BOOL result = RegisterHotKey(ownerHandle, (int)id, modifier, (UINT)key);
if (result == 0) return (vint)NativeGlobalShortcutKeyResult::Occupied;
return id;
}
bool WindowsInputService::UnregisterGlobalShortcutKey(vint id)
{
BOOL result = UnregisterHotKey(ownerHandle, (int)id);
return result != 0;
}
}
}
}
/***********************************************************************
.\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] = Ptr(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;
}
void WindowsResourceService::EnumerateFonts(collections::List<WString>& fonts)
{
auto proc = [](const LOGFONTW* lpelf, const TEXTMETRICW* lpntm, DWORD fontType, LPARAM lParam) -> int
{
auto&& fonts = *(collections::List<WString>*)lParam;
fonts.Add(lpelf->lfFaceName);
return 1;
};
HDC refHdc = GetDC(NULL);
EnumFontFamilies(refHdc, NULL, proc, (LPARAM)&fonts);
ReleaseDC(NULL, refHdc);
}
}
}
}
/***********************************************************************
.\SERVICESIMPL\WINDOWSSCREENSERVICE.CPP
***********************************************************************/
namespace vl
{
namespace presentation
{
namespace windows
{
/***********************************************************************
WindowsScreen
***********************************************************************/
WindowsScreen::WindowsScreen()
{
monitor=NULL;
}
NativeRect WindowsScreen::GetBounds()
{
MONITORINFOEX info;
info.cbSize=sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &info);
return NativeRect(info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom);
}
NativeRect WindowsScreen::GetClientBounds()
{
MONITORINFOEX info;
info.cbSize=sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &info);
return NativeRect(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;
}
double WindowsScreen::GetScalingX()
{
UINT x = 0, y = 0;
DpiAwared_GetDpiForMonitor(monitor, &x, &y);
return x / 96.0;
}
double WindowsScreen::GetScalingY()
{
UINT x = 0, y = 0;
DpiAwared_GetDpiForMonitor(monitor, &x, &y);
return y / 96.0;
}
/***********************************************************************
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(Ptr(new WindowsScreen));
}
data->screenService->screens[data->currentScreen]->monitor=hMonitor;
data->currentScreen++;
return TRUE;
}
void WindowsScreenService::RefreshScreenInformation()
{
// TODO: (enumerable) foreach
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)
{
// TODO: (enumerable) foreach
for(vint i=0;i<screens.Count();i++)
{
if(screens[i]->monitor==monitor)
{
return screens[i].Obj();
}
}
}
}
return 0;
}
}
}
}