/*********************************************************************** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY DEVELOPER: Zihan Chen(vczh) ***********************************************************************/ #include "GacUIWindows.h" /*********************************************************************** NATIVEWINDOW\WINDOWS\WINNATIVEWINDOW.CPP ***********************************************************************/ #pragma comment(lib, "Imm32.lib") #pragma comment(lib, "Shlwapi.lib") namespace vl { namespace presentation { namespace windows { using namespace collections; HWND GetHWNDFromNativeWindowHandle(INativeWindow* window) { if(!window) return NULL; IWindowsForm* form=GetWindowsForm(window); if(!form) return NULL; return form->GetWindowHandle(); } /*********************************************************************** WindowsClass ***********************************************************************/ class WinClass : public Object { protected: WString name; WNDCLASSEX windowClass; ATOM windowAtom; public: WinClass(WString _name, bool shadow, bool ownDC, WNDPROC procedure, HINSTANCE hInstance) { name=_name; windowClass.cbSize=sizeof(windowClass); windowClass.style=CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | (shadow?CS_DROPSHADOW:0) | (ownDC?CS_OWNDC:0); windowClass.lpfnWndProc=procedure; windowClass.cbClsExtra=0; windowClass.cbWndExtra=0; windowClass.hInstance=hInstance; windowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION); windowClass.hCursor=NULL;//LoadCursor(NULL,IDC_ARROW); windowClass.hbrBackground=GetSysColorBrush(COLOR_BTNFACE); windowClass.lpszMenuName=NULL; windowClass.lpszClassName=name.Buffer(); windowClass.hIconSm=NULL; windowAtom=RegisterClassEx(&windowClass); } bool IsAvailable() { return windowAtom!=0; } WString GetName() { return name; } ATOM GetClassAtom() { return windowAtom; } }; /*********************************************************************** WindowsForm ***********************************************************************/ class WindowsForm : public Object, public INativeWindow, public IWindowsForm { protected: LONG_PTR InternalGetExStyle() { return GetWindowLongPtr(handle, GWL_EXSTYLE); } void InternalSetExStyle(LONG_PTR exStyle) { LONG_PTR result = SetWindowLongPtr(handle, GWL_EXSTYLE, exStyle); SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); } bool GetExStyle(LONG_PTR exStyle) { LONG_PTR Long=InternalGetExStyle(); return (Long & exStyle) != 0; } void SetExStyle(LONG_PTR exStyle, bool available) { LONG_PTR Long = InternalGetExStyle(); if(available) { Long |= exStyle; } else { Long &= ~exStyle; } InternalSetExStyle(Long); } bool GetStyle(LONG_PTR style) { LONG_PTR Long = GetWindowLongPtr(handle, GWL_STYLE); return (Long & style) != 0; } void SetStyle(LONG_PTR style, bool available) { LONG_PTR Long = GetWindowLongPtr(handle, GWL_STYLE); if(available) { Long |= style; } else { Long &= ~style; } SetWindowLongPtr(handle, GWL_STYLE, Long); SetWindowPos(handle, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); } NativeWindowMouseInfo ConvertMouse(WPARAM wParam, LPARAM lParam, bool wheelMessage, bool nonClient) { NativeWindowMouseInfo info; info.nonClient = false; if (nonClient) { switch (wParam) { case HTMINBUTTON: case HTMAXBUTTON: case HTCLOSE: break; default: info.nonClient = true; break; } } if(wheelMessage) { info.wheel=GET_WHEEL_DELTA_WPARAM(wParam); wParam=GET_KEYSTATE_WPARAM(wParam); } else { info.wheel=0; } if (nonClient) { info.ctrl = WinIsKeyPressing(VK_CONTROL); info.shift = WinIsKeyPressing(VK_SHIFT); info.left= WinIsKeyPressing(MK_LBUTTON); info.middle= WinIsKeyPressing(MK_MBUTTON); info.right = WinIsKeyPressing(MK_RBUTTON); POINTS point = MAKEPOINTS(lParam); Point offset = GetClientBoundsInScreen().LeftTop(); info.x = point.x - offset.x; info.y = point.y - offset.y; } else { info.ctrl=(wParam & MK_CONTROL)!=0; info.shift=(wParam & MK_SHIFT)!=0; info.left=(wParam & MK_LBUTTON)!=0; info.middle=(wParam & MK_MBUTTON)!=0; info.right=(wParam & MK_RBUTTON)!=0; POINTS point = MAKEPOINTS(lParam); if (wheelMessage) { Point offset = GetClientBoundsInScreen().LeftTop(); info.x = point.x - offset.x; info.y = point.y - offset.y; } else { info.x = point.x; info.y = point.y; } } return info; } NativeWindowKeyInfo ConvertKey(WPARAM wParam, LPARAM lParam) { NativeWindowKeyInfo info; info.code=wParam; info.ctrl=WinIsKeyPressing(VK_CONTROL); info.shift=WinIsKeyPressing(VK_SHIFT); info.alt=WinIsKeyPressing(VK_MENU); info.capslock=WinIsKeyToggled(VK_CAPITAL); return info; } NativeWindowCharInfo ConvertChar(WPARAM wParam) { NativeWindowCharInfo info; info.code=(wchar_t)wParam; info.ctrl=WinIsKeyPressing(VK_CONTROL); info.shift=WinIsKeyPressing(VK_SHIFT); info.alt=WinIsKeyPressing(VK_MENU); info.capslock=WinIsKeyToggled(VK_CAPITAL); return info; } void TrackMouse(bool enable) { TRACKMOUSEEVENT trackMouseEvent; trackMouseEvent.cbSize=sizeof(trackMouseEvent); trackMouseEvent.hwndTrack=handle; trackMouseEvent.dwFlags=(enable?0:TME_CANCEL) | TME_HOVER | TME_LEAVE; trackMouseEvent.dwHoverTime=HOVER_DEFAULT; TrackMouseEvent(&trackMouseEvent); } void UpdateCompositionForContent() { HIMC imc = ImmGetContext(handle); COMPOSITIONFORM cf; cf.dwStyle = CFS_POINT; cf.ptCurrentPos.x = (int)caretPoint.x; cf.ptCurrentPos.y = (int)caretPoint.y; ImmSetCompositionWindow(imc, &cf); ImmReleaseContext(handle, imc); } bool HandleMessageInternal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result) { bool transferFocusEvent = false; bool nonClient = false; switch(uMsg) { case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDBLCLK: transferFocusEvent=true; } switch(uMsg) { // ************************************** moving and sizing case WM_MOVING:case WM_SIZING: { LPRECT rawBounds=(LPRECT)lParam; Rect bounds(rawBounds->left, rawBounds->top, rawBounds->right, rawBounds->bottom); for(vint i=0;iMoving(bounds, false); } if( rawBounds->left!=bounds.Left() || rawBounds->top!=bounds.Top() || rawBounds->right!=bounds.Right() || rawBounds->bottom!=bounds.Bottom()) { rawBounds->left=(int)bounds.Left(); rawBounds->top=(int)bounds.Top(); rawBounds->right=(int)bounds.Right(); rawBounds->bottom=(int)bounds.Bottom(); result=TRUE; } } break; case WM_MOVE:case WM_SIZE: { for(vint i=0;iMoved(); } } break; // ************************************** state case WM_ENABLE: { for(vint i=0;iEnabled(); } else { listeners[i]->Disabled(); } } } break; case WM_SETFOCUS: { for(vint i=0;iGotFocus(); } } break; case WM_KILLFOCUS: { for(vint i=0;iLostFocus(); } } break; case WM_ACTIVATE: { for(vint i=0;iActivated(); } else { listeners[i]->Deactivated(); } } } break; case WM_SHOWWINDOW: { if(wParam==TRUE) { for(vint i=0;iOpened(); } } else { for(vint i=0;iClosed(); } } } break; case WM_CLOSE: { bool cancel=false; for(vint i=0;iClosing(cancel); } return cancel; } break; // ************************************** mouse case WM_NCLBUTTONDOWN: if (!customFrameMode) break; nonClient = true; case WM_LBUTTONDOWN: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iLeftButtonDown(info); } } break; case WM_NCLBUTTONUP: if (!customFrameMode) break; nonClient = true; case WM_LBUTTONUP: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iLeftButtonUp(info); } } break; case WM_NCLBUTTONDBLCLK: if (!customFrameMode) break; nonClient = true; case WM_LBUTTONDBLCLK: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iLeftButtonDoubleClick(info); } } break; case WM_NCRBUTTONDOWN: if (!customFrameMode) break; nonClient = true; case WM_RBUTTONDOWN: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iRightButtonDown(info); } } break; case WM_NCRBUTTONUP: if (!customFrameMode) break; nonClient = true; case WM_RBUTTONUP: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iRightButtonUp(info); } } break; case WM_NCRBUTTONDBLCLK: if (!customFrameMode) break; nonClient = true; case WM_RBUTTONDBLCLK: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iRightButtonDoubleClick(info); } } break; case WM_NCMBUTTONDOWN: if (!customFrameMode) break; nonClient = true; case WM_MBUTTONDOWN: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iMiddleButtonDown(info); } } break; case WM_NCMBUTTONUP: if (!customFrameMode) break; nonClient = true; case WM_MBUTTONUP: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iMiddleButtonUp(info); } } break; case WM_NCMBUTTONDBLCLK: if (!customFrameMode) break; nonClient = true; case WM_MBUTTONDBLCLK: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); for(vint i=0;iMiddleButtonDoubleClick(info); } } break; case WM_NCMOUSEMOVE: if (!customFrameMode) break; nonClient = true; case WM_MOUSEMOVE: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient); if(info.x!=mouseLastX || info.y!=mouseLastY) { if(!mouseHoving) { mouseHoving=true; for(vint i=0;iMouseEntered(); } TrackMouse(true); } for(vint i=0;iMouseMoving(info); } } } break; // ************************************** wheel case WM_MOUSEHWHEEL: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false); for(vint i=0;iHorizontalWheel(info); } } break; case WM_MOUSEWHEEL: { NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, true, false); for(vint i=0;iVerticalWheel(info); } } break; // ************************************** mouse state case WM_NCMOUSELEAVE: nonClient = true; case WM_MOUSELEAVE: if (customFrameMode == nonClient) { mouseLastX=-1; mouseLastY=-1; mouseHoving=false; for(vint i=0;iMouseLeaved(); } } break; case WM_NCMOUSEHOVER: case WM_MOUSEHOVER: { TrackMouse(true); } break; // ************************************** key case WM_KEYUP: { NativeWindowKeyInfo info=ConvertKey(wParam, lParam); for(vint i=0;iKeyUp(info); } } break; case WM_KEYDOWN: { NativeWindowKeyInfo info=ConvertKey(wParam, lParam); for(vint i=0;iKeyDown(info); } } break; case WM_SYSKEYUP: { NativeWindowKeyInfo info=ConvertKey(wParam, lParam); if (supressingAlt && !info.ctrl && !info.shift && info.code == VK_MENU) { supressingAlt = false; break; } for(vint i=0;iSysKeyUp(info); } } break; case WM_SYSKEYDOWN: { NativeWindowKeyInfo info=ConvertKey(wParam, lParam); if (supressingAlt && !info.ctrl && !info.shift && info.code == VK_MENU) { break; } for(vint i=0;iSysKeyDown(info); } } break; case WM_CHAR: { NativeWindowCharInfo info=ConvertChar(wParam); for(vint i=0;iChar(info); } } break; // ************************************** painting case WM_PAINT: { for(vint i=0;iPaint(); } } break; case WM_ERASEBKGND: result = 0; return true; case WM_NCPAINT: case WM_SYNCPAINT: if(customFrameMode) { result=0; return true; } break; // ************************************** IME case WM_IME_SETCONTEXT: if(wParam==TRUE) { HIMC imc = ImmGetContext(handle); ImmAssociateContext(hwnd, imc); ImmReleaseContext(handle, imc); } break; case WM_IME_STARTCOMPOSITION: UpdateCompositionForContent(); break; // ************************************** hit test case WM_NCHITTEST: { POINTS location=MAKEPOINTS(lParam); Point windowLocation=GetBounds().LeftTop(); location.x-=(SHORT)windowLocation.x; location.y-=(SHORT)windowLocation.y; for(vint i=0;iHitTest(Point(location.x, location.y))) { case INativeWindowListener::BorderNoSizing: result=HTBORDER; return true; case INativeWindowListener::BorderLeft: result=HTLEFT; return true; case INativeWindowListener::BorderRight: result=HTRIGHT; return true; case INativeWindowListener::BorderTop: result=HTTOP; return true; case INativeWindowListener::BorderBottom: result=HTBOTTOM; return true; case INativeWindowListener::BorderLeftTop: result=HTTOPLEFT; return true; case INativeWindowListener::BorderRightTop: result=HTTOPRIGHT; return true; case INativeWindowListener::BorderLeftBottom: result=HTBOTTOMLEFT; return true; case INativeWindowListener::BorderRightBottom: result=HTBOTTOMRIGHT; return true; case INativeWindowListener::Title: result=HTCAPTION; return true; case INativeWindowListener::ButtonMinimum: result=HTMINBUTTON; return true; case INativeWindowListener::ButtonMaximum: result=HTMAXBUTTON; return true; case INativeWindowListener::ButtonClose: result=HTCLOSE; return true; case INativeWindowListener::Client: result=HTCLIENT; return true; case INativeWindowListener::Icon: result=HTSYSMENU; return true; } } } break; // ************************************** MISC case WM_SETCURSOR: { DWORD hitTestResult=LOWORD(lParam); if(hitTestResult==HTCLIENT) { HCURSOR cursorHandle=cursor->GetCursorHandle(); if(GetCursor()!=cursorHandle) { SetCursor(cursorHandle); } result=TRUE; return true; } } break; case WM_NCCALCSIZE: if((BOOL)wParam && customFrameMode) { result=0; return true; } break; case WM_NCACTIVATE: if(customFrameMode) { if(wParam==TRUE) { result=FALSE; } else { result=TRUE; } return true; } break; } if(IsWindow(hwnd)!=0) { if(transferFocusEvent && IsFocused()) { WindowsForm* window=this; while(window->parentWindow && window->alwaysPassFocusToParent) { window=window->parentWindow; } if(window!=this) { window->SetFocus(); } } } if (customFrameMode) { switch (uMsg) { case WM_NCLBUTTONDOWN: switch (wParam) { case HTMINBUTTON: case HTMAXBUTTON: case HTCLOSE: result = 0; return true; } break; case WM_LBUTTONUP: { POINTS location = MAKEPOINTS(lParam); for(vint i=0;iHitTest(Point(location.x, location.y))) { case INativeWindowListener::ButtonMinimum: ShowMinimized(); return false; case INativeWindowListener::ButtonMaximum: if (GetSizeState() == INativeWindow::Maximized) { ShowRestored(); } else { ShowMaximized(); } return false; case INativeWindowListener::ButtonClose: Hide(); return false; } } } break; } } return false; } protected: HWND handle; WString title; WindowsCursor* cursor; Point caretPoint; WindowsForm* parentWindow; bool alwaysPassFocusToParent; List listeners; vint mouseLastX; vint mouseLastY; vint mouseHoving; Interface* graphicsHandler; bool customFrameMode; List> messageHandlers; bool supressingAlt; public: WindowsForm(HWND parent, WString className, HINSTANCE hInstance) :cursor(0) ,parentWindow(0) ,alwaysPassFocusToParent(false) ,mouseLastX(-1) ,mouseLastY(-1) ,mouseHoving(false) ,graphicsHandler(0) ,customFrameMode(false) ,supressingAlt(false) { DWORD exStyle = WS_EX_APPWINDOW | WS_EX_CONTROLPARENT; DWORD style = WS_BORDER | WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; handle=CreateWindowEx(exStyle, className.Buffer(), L"", style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parent, NULL, hInstance, NULL); } ~WindowsForm() { List copiedListeners; CopyFrom(copiedListeners, listeners); for(vint i=0;iDestroyed(); } } DestroyWindow(handle); } void InvokeDestroying() { for(vint i=0;iDestroying(); } } bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result) { bool skip = false; { FOREACH(Ptr, handler, messageHandlers) { handler->BeforeHandle(hwnd, uMsg, wParam, lParam, skip); } if (skip) { return true; } } skip = HandleMessageInternal(hwnd, uMsg, wParam, lParam, result); if (GetWindowsFormFromHandle(hwnd)) { FOREACH(Ptr, handler, messageHandlers) { handler->AfterHandle(hwnd, uMsg, wParam, lParam, skip, result); } } return skip; } HWND GetWindowHandle()override { return handle; } Interface* GetGraphicsHandler()override { return graphicsHandler; } void SetGraphicsHandler(Interface* handler)override { graphicsHandler=handler; } bool InstallMessageHandler(Ptr handler)override { if (messageHandlers.Contains(handler.Obj())) { return false; } messageHandlers.Add(handler); return true; } bool UninstallMessageHandler(Ptr handler)override { vint index = messageHandlers.IndexOf(handler.Obj()); if (index == -1)return false; messageHandlers.RemoveAt(handler); return true; } Rect GetBounds() { RECT rect; GetWindowRect(handle, &rect); return Rect(rect.left, rect.top, rect.right, rect.bottom); } void SetBounds(const Rect& bounds) { Rect newBounds=bounds; for(vint i=0;iMoving(newBounds, true); } MoveWindow(handle, (int)newBounds.Left(), (int)newBounds.Top(), (int)newBounds.Width(), (int)newBounds.Height(), FALSE); } Size GetClientSize() { return GetClientBoundsInScreen().GetSize(); } void SetClientSize(Size size) { RECT required={0,0,(int)size.x,(int)size.y}; RECT bounds; GetWindowRect(handle, &bounds); AdjustWindowRect(&required, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE); SetBounds(Rect(Point(bounds.left, bounds.top), Size(required.right-required.left, required.bottom-required.top))); } Rect GetClientBoundsInScreen() { if(customFrameMode) { return GetBounds(); } else { RECT required={0,0,0,0}; RECT bounds; GetWindowRect(handle, &bounds); AdjustWindowRect(&required, (DWORD)GetWindowLongPtr(handle, GWL_STYLE), FALSE); return Rect( Point( (bounds.left-required.left), (bounds.top-required.top) ), Size( (bounds.right-bounds.left)-(required.right-required.left), (bounds.bottom-bounds.top)-(required.bottom-required.top) ) ); } } WString GetTitle() { return title; } void SetTitle(WString _title) { title=_title; SetWindowText(handle, title.Buffer()); } INativeCursor* GetWindowCursor() { return cursor; } void SetWindowCursor(INativeCursor* _cursor) { WindowsCursor* newCursor=dynamic_cast(_cursor); if(newCursor && cursor!=newCursor) { cursor=newCursor; if(mouseHoving && IsVisible()) { SetCursor(cursor->GetCursorHandle()); } } } Point GetCaretPoint() { return caretPoint; } void SetCaretPoint(Point point) { caretPoint=point; UpdateCompositionForContent(); } INativeWindow* GetParent() { return parentWindow; } void SetParent(INativeWindow* parent) { parentWindow=dynamic_cast(parent); if(parentWindow) { SetWindowLongPtr(handle, GWLP_HWNDPARENT, (LONG_PTR)parentWindow->handle); } else { SetWindowLongPtr(handle, GWLP_HWNDPARENT, NULL); } } bool GetAlwaysPassFocusToParent() { return alwaysPassFocusToParent; } void SetAlwaysPassFocusToParent(bool value) { alwaysPassFocusToParent=value; } void EnableCustomFrameMode() { customFrameMode=true; } void DisableCustomFrameMode() { customFrameMode=false; } bool IsCustomFrameModeEnabled() { return customFrameMode; } WindowSizeState GetSizeState() { if(IsIconic(handle)) { return INativeWindow::Minimized; } else if(IsZoomed(handle)) { return INativeWindow::Maximized; } else { return INativeWindow::Restored; } } void Show() { ShowWindow(handle, SW_SHOWNORMAL); } void ShowDeactivated() { ShowWindow(handle, SW_SHOWNOACTIVATE); SetWindowPos(handle,HWND_TOP,0,0,0,0,SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); } void ShowRestored() { ShowWindow(handle, SW_RESTORE); } void ShowMaximized() { ShowWindow(handle, SW_SHOWMAXIMIZED); } void ShowMinimized() { ShowWindow(handle, SW_SHOWMINIMIZED); } void Hide() { SendMessage(handle, WM_CLOSE, NULL, NULL); } bool IsVisible() { return IsWindowVisible(handle)!=0; } void Enable() { EnableWindow(handle, TRUE); } void Disable() { EnableWindow(handle, FALSE); } bool IsEnabled() { return IsWindowEnabled(handle)!=0; } void SetFocus() { ::SetFocus(handle); } bool IsFocused() { return GetFocus()==handle; } void SetActivate() { SetActiveWindow(handle); } bool IsActivated() { return GetActiveWindow()==handle; } void ShowInTaskBar() { SetExStyle(WS_EX_APPWINDOW, true); } void HideInTaskBar() { SetExStyle(WS_EX_APPWINDOW, false); } bool IsAppearedInTaskBar() { return GetExStyle(WS_EX_APPWINDOW); } void EnableActivate() { SetExStyle(WS_EX_NOACTIVATE, false); } void DisableActivate() { SetExStyle(WS_EX_NOACTIVATE, true); } bool IsEnabledActivate() { return !GetExStyle(WS_EX_NOACTIVATE); } bool RequireCapture() { SetCapture(handle); return true; } bool ReleaseCapture() { ::ReleaseCapture(); return true; } bool IsCapturing() { return GetCapture()==handle; } bool GetMaximizedBox() { return GetStyle(WS_MAXIMIZEBOX); } void SetMaximizedBox(bool visible) { SetStyle(WS_MAXIMIZEBOX, visible); } bool GetMinimizedBox() { return GetStyle(WS_MINIMIZEBOX); } void SetMinimizedBox(bool visible) { SetStyle(WS_MINIMIZEBOX, visible); } bool GetBorder() { return GetStyle(WS_BORDER); } void SetBorder(bool visible) { SetStyle(WS_BORDER, visible); } bool GetSizeBox() { return GetStyle(WS_SIZEBOX); } void SetSizeBox(bool visible) { SetStyle(WS_SIZEBOX, visible); } bool GetIconVisible() { return GetStyle(WS_SYSMENU); } void SetIconVisible(bool visible) { SetStyle(WS_SYSMENU, visible); } bool GetTitleBar() { return GetStyle(WS_CAPTION); } void SetTitleBar(bool visible) { SetStyle(WS_CAPTION, visible); } bool GetTopMost() { return GetExStyle(WS_EX_TOPMOST); } void SetTopMost(bool topmost) { SetWindowPos(handle, (topmost ? HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED); } void SupressAlt() { if (!supressingAlt) { supressingAlt = true; PostMessage(handle, WM_SYSKEYDOWN, VK_MENU, 0); PostMessage(handle, WM_SYSKEYUP, VK_MENU, 0); } } bool InstallListener(INativeWindowListener* listener) { if(listeners.Contains(listener)) { return false; } else { listeners.Add(listener); return true; } } bool UninstallListener(INativeWindowListener* listener) { if(listeners.Contains(listener)) { listeners.Remove(listener); return true; } else { return false; } } void RedrawContent() { if(graphicsHandler) { SendMessage(this->handle, WM_PAINT, NULL, NULL); } } }; /*********************************************************************** WindowsController ***********************************************************************/ LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK MouseProc(int nCode , WPARAM wParam , LPARAM lParam); class WindowsController : public Object, public virtual INativeController, public virtual INativeWindowService { protected: WinClass windowClass; WinClass godClass; HINSTANCE hInstance; HWND godWindow; Dictionary windows; INativeWindow* mainWindow; HWND mainWindowHandle; WindowsCallbackService callbackService; WindowsResourceService resourceService; WindowsAsyncService asyncService; WindowsClipboardService clipboardService; WindowsImageService imageService; WindowsScreenService screenService; WindowsInputService inputService; WindowsDialogService dialogService; public: WindowsController(HINSTANCE _hInstance) :hInstance(_hInstance) ,windowClass(L"VczhWindow", false, false, WndProc, _hInstance) ,godClass(L"GodWindow", false, false, GodProc, _hInstance) ,mainWindow(0) ,mainWindowHandle(0) ,screenService(&GetHWNDFromNativeWindowHandle) ,inputService(&MouseProc) ,dialogService(&GetHWNDFromNativeWindowHandle) { godWindow=CreateWindowEx(WS_EX_CONTROLPARENT, godClass.GetName().Buffer(), L"GodWindow", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); clipboardService.SetOwnerHandle(godWindow); inputService.SetOwnerHandle(godWindow); } ~WindowsController() { inputService.StopTimer(); inputService.StopHookMouse(); clipboardService.SetOwnerHandle(NULL); DestroyWindow(godWindow); } WindowsForm* GetWindowsFormFromHandle(HWND hwnd) { vint index = windows.Keys().IndexOf(hwnd); if (index == -1)return 0; return windows.Values()[index]; } bool HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result) { bool skipDefaultProcedure=false; vint index=windows.Keys().IndexOf(hwnd); if(index!=-1) { WindowsForm* window=windows.Values().Get(index); skipDefaultProcedure=window->HandleMessage(hwnd, uMsg, wParam, lParam, result); switch(uMsg) { case WM_CLOSE: if(!skipDefaultProcedure) { ShowWindow(window->GetWindowHandle(), SW_HIDE); if(window!=mainWindow) { skipDefaultProcedure=true; } } break; case WM_DESTROY: DestroyNativeWindow(window); break; } } if(hwnd==mainWindowHandle && uMsg==WM_DESTROY) { for(vint i=0;iIsVisible()) { windows.Values().Get(i)->Hide(); } } while(windows.Count()) { DestroyNativeWindow(windows.Values().Get(0)); } PostQuitMessage(0); } return skipDefaultProcedure; } //======================================================================= INativeWindow* CreateNativeWindow() { WindowsForm* window=new WindowsForm(godWindow, windowClass.GetName(), hInstance); windows.Add(window->GetWindowHandle(), window); callbackService.InvokeNativeWindowCreated(window); window->SetWindowCursor(resourceService.GetDefaultSystemCursor()); return window; } void DestroyNativeWindow(INativeWindow* window) { WindowsForm* windowsForm=dynamic_cast(window); windowsForm->InvokeDestroying(); if(windowsForm!=0 && windows.Keys().Contains(windowsForm->GetWindowHandle())) { callbackService.InvokeNativeWindowDestroyed(window); windows.Remove(windowsForm->GetWindowHandle()); delete windowsForm; } } INativeWindow* GetMainWindow() { return mainWindow; } void Run(INativeWindow* window) { mainWindow=window; mainWindowHandle=GetWindowsForm(window)->GetWindowHandle(); mainWindow->Show(); MSG message; while(GetMessage(&message, NULL, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); asyncService.ExecuteAsyncTasks(); } } INativeWindow* GetWindow(Point location) { POINT p; p.x=(int)location.x; p.y=(int)location.y; HWND handle=WindowFromPoint(p); vint index=windows.Keys().IndexOf(handle); if(index==-1) { return 0; } else { return windows.Values().Get(index); } } //======================================================================= INativeCallbackService* CallbackService() { return &callbackService; } INativeResourceService* ResourceService() { return &resourceService; } INativeAsyncService* AsyncService() { return &asyncService; } INativeClipboardService* ClipboardService() { return &clipboardService; } INativeImageService* ImageService() { return &imageService; } INativeScreenService* ScreenService() { return &screenService; } INativeWindowService* WindowService() { return this; } INativeInputService* InputService() { return &inputService; } INativeDialogService* DialogService() { return &dialogService; } bool IsWindowsVersionEqualOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) { OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; DWORDLONG const dwlConditionMask = VerSetConditionMask( VerSetConditionMask( VerSetConditionMask( 0, VER_MAJORVERSION, VER_GREATER_EQUAL ), VER_MINORVERSION, VER_GREATER_EQUAL ), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL ); osvi.dwMajorVersion = wMajorVersion; osvi.dwMinorVersion = wMinorVersion; osvi.wServicePackMajor = wServicePackMajor; return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; } bool IsWindowsServer() { OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION }; DWORDLONG const dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL ); return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask); } WString GetOSVersion() { const vint SystemCount = 5; DWORD SystemVersions[SystemCount] = { _WIN32_WINNT_WIN2K, _WIN32_WINNT_WINXP, _WIN32_WINNT_VISTA, _WIN32_WINNT_WIN7, _WIN32_WINNT_WIN8 }; vint SystemMaxServerPacks[SystemCount] = { 4, 3, 2, 1, 0, }; const wchar_t* SystemClientNames[SystemCount] = { L"Windows 2000", L"Windows XP", L"Windows Vista", L"Windows 7", L"Windows 8", }; const wchar_t* SystemServerNames[SystemCount] = { L"Windows Server 2003", L"Windows Server 2003 R2", L"Windows Server 2008", L"Windows Server 2008 R2", L"Windows Server 2012", }; bool isWindowsServer = IsWindowsServer(); for(vint systemIndex = SystemCount-1; systemIndex >=0; systemIndex--) { DWORD systemVersion = SystemVersions[systemIndex]; vint maxSp = SystemMaxServerPacks[systemIndex]; for(vint sp = maxSp; sp>=0; sp--) { if(IsWindowsVersionEqualOrGreater(HIBYTE(systemVersion), LOBYTE(systemVersion), (WORD)sp)) { WString systemName = isWindowsServer?SystemServerNames[systemIndex]:SystemClientNames[systemIndex]; if(sp==0) { return systemName+L";"; } else { return systemName+L";SP"+itow(sp); } } } } return L"Windows "; } WString GetExecutablePath() { Array buffer(65536); GetModuleFileName(NULL, &buffer[0], (DWORD)buffer.Count()); return &buffer[0]; } //======================================================================= void InvokeMouseHook(WPARAM message, Point location) { callbackService.InvokeMouseHook(message, location); } void InvokeGlobalTimer() { callbackService.InvokeGlobalTimer(); } void InvokeClipboardUpdated() { callbackService.InvokeClipboardUpdated(); } }; /*********************************************************************** Windows Procedure ***********************************************************************/ LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WindowsController* controller=dynamic_cast(GetCurrentController()); if(controller) { LRESULT result=0; if(controller->HandleMessage(hwnd, uMsg, wParam, lParam, result)) { return result; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); } LRESULT CALLBACK GodProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { WindowsController* controller=dynamic_cast(GetCurrentController()); if(controller) { switch(uMsg) { case WM_TIMER: controller->InvokeGlobalTimer(); break; case WM_CLIPBOARDUPDATE: controller->InvokeClipboardUpdated(); break; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); } LRESULT CALLBACK MouseProc(int nCode , WPARAM wParam , LPARAM lParam) { WindowsController* controller=dynamic_cast(GetCurrentController()); if(controller) { MSLLHOOKSTRUCT* mouseHookStruct=(MSLLHOOKSTRUCT*)lParam; Point location(mouseHookStruct->pt.x, mouseHookStruct->pt.y); controller->InvokeMouseHook(wParam, location); } return CallNextHookEx(NULL,nCode,wParam,lParam); } /*********************************************************************** Windows Platform Native Controller ***********************************************************************/ INativeController* CreateWindowsNativeController(HINSTANCE hInstance) { return new WindowsController(hInstance); } IWindowsForm* GetWindowsFormFromHandle(HWND hwnd) { auto controller = dynamic_cast(GetCurrentController()); if (controller) { return controller->GetWindowsFormFromHandle(hwnd); } return 0; } IWindowsForm* GetWindowsForm(INativeWindow* window) { return dynamic_cast(window); } void DestroyWindowsNativeController(INativeController* controller) { delete controller; } void EnableCrossKernelCrashing() { typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); const DWORD EXCEPTION_SWALLOWING = 0x1; HMODULE kernel32 = LoadLibrary(L"kernel32.dll"); tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); if (pGetPolicy && pSetPolicy) { DWORD dwFlags; if (pGetPolicy(&dwFlags)) { // Turn off the filter pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); } } } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\DIRECT2D\WINDIRECT2DAPPLICATION.CPP ***********************************************************************/ #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") #pragma comment(lib, "d3d11.lib") namespace vl { namespace presentation { namespace windows { using namespace vl::collections; /*********************************************************************** WindowListener ***********************************************************************/ class Direct2DWindowsNativeWindowListener : public Object, public INativeWindowListener { protected: ID2D1Factory* d2dFactory; INativeWindow* window; virtual void RebuildCanvas(Size size) = 0; public: Direct2DWindowsNativeWindowListener(INativeWindow* _window, ID2D1Factory* _d2dFactory) :window(_window) ,d2dFactory(_d2dFactory) { } void Moved() { RebuildCanvas(window->GetClientSize()); } virtual ID2D1RenderTarget* GetDirect2DRenderTarget() = 0; virtual void RecreateRenderTarget() = 0; virtual bool PresentRenderTarget() = 0; }; /*********************************************************************** WindowListener 1.0 ***********************************************************************/ class Direct2DWindowsNativeWindowListener_1_0 : public Direct2DWindowsNativeWindowListener { protected: ComPtr d2dRenderTarget; Size previousSize; void RebuildCanvas(Size size)override { if (size.x <= 1) size.x = 1; if (size.y <= 1) size.y = 1; if(!d2dRenderTarget) { ID2D1HwndRenderTarget* renderTarget=0; IWindowsForm* form=GetWindowsForm(window); D2D1_RENDER_TARGET_PROPERTIES tp=D2D1::RenderTargetProperties(); tp.dpiX=96; tp.dpiY=96; HRESULT hr=d2dFactory->CreateHwndRenderTarget( tp, D2D1::HwndRenderTargetProperties( form->GetWindowHandle(), D2D1::SizeU((int)size.x, (int)size.y) ), &renderTarget ); if(!FAILED(hr)) { d2dRenderTarget=renderTarget; d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); } } else if(previousSize!=size) { d2dRenderTarget->Resize(D2D1::SizeU((int)size.x, (int)size.y)); } previousSize=size; } public: Direct2DWindowsNativeWindowListener_1_0(INativeWindow* _window, ID2D1Factory* _d2dFactory) :Direct2DWindowsNativeWindowListener(_window, _d2dFactory) { } ID2D1RenderTarget* GetDirect2DRenderTarget()override { if(!d2dRenderTarget) Moved(); return d2dRenderTarget.Obj(); } void RecreateRenderTarget()override { if (d2dRenderTarget) { d2dRenderTarget = 0; } } bool PresentRenderTarget()override { return true; } }; /*********************************************************************** WindowListener 1.1 ***********************************************************************/ class Direct2DWindowsNativeWindowListener_1_1 : public Direct2DWindowsNativeWindowListener { protected: ComPtr d2dFactory1; ID3D11Device* d3d11Device; ComPtr dxgiDevice; ComPtr dxgiSwapChain; ComPtr d2dDeviceContext; Size previousSize; ComPtr GetDXGIDevice() { IDXGIDevice* device = nullptr; HRESULT hr = d3d11Device->QueryInterface(&device); if (!SUCCEEDED(hr)) return 0; return device; } ComPtr CreateSwapChain(IDXGIDevice* dxgiDevice) { ComPtr dxgiAdapter; { IDXGIAdapter* adapter = nullptr; HRESULT hr = dxgiDevice->GetAdapter(&adapter); if (!SUCCEEDED(hr)) return 0; dxgiAdapter = adapter; } ComPtr dxgiFactory; { IDXGIFactory2* factory = nullptr; HRESULT hr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&factory); if (!SUCCEEDED(hr)) return 0; dxgiFactory = factory; } IWindowsForm* form = GetWindowsForm(window); ComPtr dxgiSwapChain; { DXGI_SWAP_CHAIN_DESC1 props = {}; props.Format = DXGI_FORMAT_B8G8R8A8_UNORM; props.SampleDesc.Count = 1; props.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; props.BufferCount = 2; IDXGISwapChain1* swapChain = nullptr; HRESULT hr = dxgiFactory->CreateSwapChainForHwnd(d3d11Device, form->GetWindowHandle(), &props, nullptr, nullptr, &swapChain); if (!SUCCEEDED(hr)) return 0; dxgiSwapChain = swapChain; } return dxgiSwapChain; } ComPtr CreateDeviceContext(IDXGIDevice* dxgiDevice) { ComPtr d2d1Device; { ID2D1Device* device = nullptr; HRESULT hr = d2dFactory1->CreateDevice(dxgiDevice, &device); if (!SUCCEEDED(hr)) return 0; d2d1Device = device; } ComPtr d2dDeviceContext; { ID2D1DeviceContext* deviceContext = nullptr; HRESULT hr = d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &deviceContext); if (!SUCCEEDED(hr)) return 0; d2dDeviceContext = deviceContext; } return d2dDeviceContext; } ComPtr CreateBitmap(IDXGISwapChain1* swapChain, ID2D1DeviceContext* deviceContext) { ComPtr dxgiSurface; { IDXGISurface* surface = nullptr; HRESULT hr = swapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&surface); if (!SUCCEEDED(hr))return 0; dxgiSurface = surface; } ComPtr d2dBitmap; { auto props = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)); ID2D1Bitmap1* bitmap = nullptr; HRESULT hr = deviceContext->CreateBitmapFromDxgiSurface(dxgiSurface.Obj(), props, &bitmap); if (!SUCCEEDED(hr)) return 0; d2dBitmap = bitmap; } return d2dBitmap; } void RebuildCanvas(Size size)override { if (size.x <= 1) size.x = 1; if (size.y <= 1) size.y = 1; if(!d2dDeviceContext) { if (!dxgiDevice) { dxgiDevice = GetDXGIDevice(); } if (!dxgiSwapChain) { dxgiSwapChain = CreateSwapChain(dxgiDevice.Obj()); } d2dDeviceContext = CreateDeviceContext(dxgiDevice.Obj()); auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj()); d2dDeviceContext->SetTarget(d2dBitmap.Obj()); d2dDeviceContext->SetDpi(96, 96); } else if(previousSize!=size) { d2dDeviceContext->SetTarget(nullptr); HRESULT hr = dxgiSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); if (SUCCEEDED(hr)) { auto d2dBitmap = CreateBitmap(dxgiSwapChain.Obj(), d2dDeviceContext.Obj()); d2dDeviceContext->SetTarget(d2dBitmap.Obj()); } } previousSize=size; } public: Direct2DWindowsNativeWindowListener_1_1(INativeWindow* _window, ComPtr _d2dFactory1, ID3D11Device* _d3d11Device) :Direct2DWindowsNativeWindowListener(_window, _d2dFactory1.Obj()) ,d2dFactory1(_d2dFactory1) ,d3d11Device(_d3d11Device) { } ID2D1RenderTarget* GetDirect2DRenderTarget()override { if(!d2dDeviceContext) Moved(); return d2dDeviceContext.Obj(); } void RecreateRenderTarget()override { if (d2dDeviceContext) { d2dDeviceContext = 0; dxgiSwapChain = 0; } } bool PresentRenderTarget()override { if (d2dDeviceContext) { if (dxgiSwapChain) { DXGI_PRESENT_PARAMETERS parameters = {0}; HRESULT hr = dxgiSwapChain->Present1(1, 0, ¶meters); return hr == S_OK || hr == DXGI_STATUS_OCCLUDED; } } return false; } }; /*********************************************************************** ControllerListener ***********************************************************************/ class Direct2DWindowsNativeControllerListener : public Object, public INativeControllerListener { public: Dictionary> nativeWindowListeners; ComPtr d2dFactory; ComPtr dwrite; ComPtr 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(&factory)); if(!FAILED(hr)) { dwrite=factory; } } } ComPtr CreateD3D11Device(D3D_DRIVER_TYPE driverType) { UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; #ifdef _DEBUG flags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; ID3D11Device* device = nullptr; HRESULT hr = D3D11CreateDevice( nullptr, driverType, nullptr, flags, featureLevels, sizeof(featureLevels) / sizeof(*featureLevels), D3D11_SDK_VERSION, &device, nullptr, nullptr); if (SUCCEEDED(hr)) { return device; } else if (device) { device->Release(); } return 0; } void NativeWindowCreated(INativeWindow* window) { ComPtr d2dfactory1; { ID2D1Factory1* factory = nullptr; HRESULT hr = windows::GetDirect2DFactory()->QueryInterface(&factory); if (SUCCEEDED(hr)) { d2dfactory1 = factory; } } Ptr listener; if (d2dfactory1) { if (!d3d11Device) { d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_HARDWARE); if (!d3d11Device) { d3d11Device = CreateD3D11Device(D3D_DRIVER_TYPE_WARP); } } #if _DEBUG CHECK_ERROR(d3d11Device, L"Direct2DWindowsNativeControllerListener::NativeWindowCreated(INativeWindow*)#" L"Failed to create Direct3D 11 Device. " L"If you are running in Debug mode on Windows 10, please ensure the optional feature [Graphics Tools] is correctly installed. " L"This error will be skipped in Release mode and GacUI will fallback to use Direct2D 1.0, but you still need to check your Windows SDK Installation." ); #endif } if (d2dfactory1 && d3d11Device) { listener = new Direct2DWindowsNativeWindowListener_1_1(window, d2dfactory1, d3d11Device.Obj()); } else { listener = new Direct2DWindowsNativeWindowListener_1_0(window, d2dFactory.Obj()); } window->InstallListener(listener.Obj()); nativeWindowListeners.Add(window, listener); } void NativeWindowDestroying(INativeWindow* window) { Ptr listener=nativeWindowListeners[window]; nativeWindowListeners.Remove(window); window->UninstallListener(listener.Obj()); } }; Direct2DWindowsNativeControllerListener* direct2DListener=0; Direct2DWindowsNativeWindowListener* GetNativeWindowListener(INativeWindow* window) { vint index = direct2DListener->nativeWindowListeners.Keys().IndexOf(window); return index == -1 ? 0 : direct2DListener->nativeWindowListeners.Values().Get(index).Obj(); } ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window) { if (auto listener = GetNativeWindowListener(window)) { return listener->GetDirect2DRenderTarget(); } return 0; } void RecreateNativeWindowDirect2DRenderTarget(INativeWindow* window) { if (auto listener = GetNativeWindowListener(window)) { return listener->RecreateRenderTarget(); } } bool PresentNativeWindowDirect2DRenderTarget(INativeWindow* window) { if (auto listener = GetNativeWindowListener(window)) { return listener->PresentRenderTarget(); } return true; } ID2D1Factory* GetDirect2DFactory() { return direct2DListener->d2dFactory.Obj(); } IDWriteFactory* GetDirectWriteFactory() { return direct2DListener->dwrite.Obj(); } ID3D11Device* GetD3D11Device() { return direct2DListener->d3d11Device.Obj(); } } namespace elements_windows_d2d { /*********************************************************************** OS Supporting ***********************************************************************/ class WinDirect2DApplicationDirect2DObjectProvider : public IWindowsDirect2DObjectProvider { public: void RecreateRenderTarget(INativeWindow* window) { vl::presentation::windows::RecreateNativeWindowDirect2DRenderTarget(window); } bool PresentRenderTarget(INativeWindow* window) { return vl::presentation::windows::PresentNativeWindowDirect2DRenderTarget(window); } ID2D1RenderTarget* GetNativeWindowDirect2DRenderTarget(INativeWindow* window) { return vl::presentation::windows::GetNativeWindowDirect2DRenderTarget(window); } ID2D1Factory* GetDirect2DFactory() { return vl::presentation::windows::GetDirect2DFactory(); } IDWriteFactory* GetDirectWriteFactory() { return vl::presentation::windows::GetDirectWriteFactory(); } IWindowsDirect2DRenderTarget* GetBindedRenderTarget(INativeWindow* window) { return dynamic_cast(vl::presentation::windows::GetWindowsForm(window)->GetGraphicsHandler()); } void SetBindedRenderTarget(INativeWindow* window, IWindowsDirect2DRenderTarget* renderTarget) { vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget); } IWICImagingFactory* GetWICImagingFactory() { return vl::presentation::windows::GetWICImagingFactory(); } IWICBitmap* GetWICBitmap(INativeImageFrame* frame) { return vl::presentation::windows::GetWICBitmap(frame); } }; } } } using namespace vl; using namespace vl::presentation; using namespace vl::presentation::windows; using namespace vl::presentation::elements_windows_d2d; int WinMainDirect2D(HINSTANCE hInstance, void(*RendererMain)()) { EnableCrossKernelCrashing(); // create controller INativeController* controller=CreateWindowsNativeController(hInstance); SetCurrentController(controller); { // install listener Direct2DWindowsNativeControllerListener listener; controller->CallbackService()->InstallListener(&listener); direct2DListener=&listener; // main RendererMain(); // uninstall listener direct2DListener=0; controller->CallbackService()->UninstallListener(&listener); } // destroy controller DestroyWindowsNativeController(controller); return 0; } int SetupWindowsDirect2DRenderer() { CoInitializeEx(NULL, COINIT_MULTITHREADED); HINSTANCE hInstance=(HINSTANCE)GetModuleHandle(NULL); WinDirect2DApplicationDirect2DObjectProvider objectProvider; SetWindowsDirect2DObjectProvider(&objectProvider); return WinMainDirect2D(hInstance, &RendererMainDirect2D); } /*********************************************************************** NATIVEWINDOW\WINDOWS\GDI\WINGDI.CPP ***********************************************************************/ #include #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;iGetHandle(), FHandle); if(Object) { DeleteObject(Object); } } WinBitmap::WinBitmap(vint Width, vint Height, BitmapBits Bits, bool DIBSections) { Constructor(Width, Height, Bits, DIBSections); } WinBitmap::WinBitmap(WString FileName, bool Use32Bits, bool DIBSections) { FBits=Use32Bits?vbb32Bits:vbb24Bits; HBITMAP TempBmp=(HBITMAP)LoadImage(NULL, FileName.Buffer(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); HDC TempDC=CreateCompatibleDC(NULL); BITMAP TempRec; GetObject(TempBmp, sizeof(BITMAP), &TempRec); DeleteObject(SelectObject(TempDC, TempBmp)); Constructor(TempRec.bmWidth, TempRec.bmHeight, FBits, DIBSections); BitBlt(FDC->GetHandle(), 0, 0, TempRec.bmWidth, TempRec.bmHeight, TempDC, 0, 0, SRCCOPY); DeleteObject(TempDC); DeleteObject(TempBmp); } WinBitmap::~WinBitmap() { if(FScanLines) { delete[] FScanLines; } if(FHandle) { DeleteObject(FHandle); } delete FDC; } void WinBitmap::SaveToFile(WString FileName) { if(FScanLines) { BITMAPFILEHEADER Header1; BITMAPV5HEADER Header2; { Header1.bfType='M'*256+'B'; Header1.bfSize=(int)(sizeof(Header1)+sizeof(Header2)+GetLineBytes()*FHeight); Header1.bfReserved1=0; Header1.bfReserved2=0; Header1.bfOffBits=sizeof(Header2)+sizeof(Header1); } { memset(&Header2, 0, sizeof(Header2)); Header2.bV5Size=sizeof(Header2); Header2.bV5Width=(int)FWidth; Header2.bV5Height=-(int)FHeight; Header2.bV5Planes=1; Header2.bV5BitCount=(int)GetBitsFromBB(FBits); Header2.bV5Compression=BI_RGB; Header2.bV5CSType=LCS_sRGB; Header2.bV5Intent=LCS_GM_GRAPHICS; } stream::FileStream Output(FileName, stream::FileStream::WriteOnly); Output.Write(&Header1, sizeof(Header1)); Output.Write(&Header2, sizeof(Header2)); for(vint i=0;iCopy(0, 0, FWidth, FHeight, FDC, 0, 0); Temp.SaveToFile(FileName); } } WinDC* WinBitmap::GetWinDC() { return FDC; } vint WinBitmap::GetWidth() { return FWidth; } vint WinBitmap::GetHeight() { return FHeight; } vint WinBitmap::GetLineBytes() { return GetLineBytes(FWidth, FBits); } BYTE** WinBitmap::GetScanLines() { return FScanLines; } HBITMAP WinBitmap::GetBitmap() { return FHandle; } WinBitmap::BitmapBits WinBitmap::GetBitmapBits() { return FBits; } void WinBitmap::FillCompatibleHeader(BITMAPINFOHEADER* Header) { FillBitmapInfoHeader(FWidth, FHeight, FBits, Header); } bool WinBitmap::CanBuildAlphaChannel() { return FScanLines!=0 && FBits==vbb32Bits; } bool WinBitmap::IsAlphaChannelBuilt() { return FAlphaChannelBuilt; } void WinBitmap::BuildAlphaChannel(bool autoPremultiply) { if(CanBuildAlphaChannel() && !FAlphaChannelBuilt) { FAlphaChannelBuilt=true; if(autoPremultiply) { for(vint i=0;iGetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true); Temp.GetWinDC()->Draw(0, 0, DIB); vint HeaderSize=sizeof(BITMAPINFOHEADER); FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()]; Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory); memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes()); FHandle=CreateDIBPatternBrushPt(FDIBMemory, DIB_RGB_COLORS); DWORD Error=GetLastError(); } WinBrush::~WinBrush() { DeleteObject(FHandle); if(FDIBMemory) { delete[] FDIBMemory; } } HBRUSH WinBrush::GetHandle() { return FHandle; } /********************************************************************************************************* WinPen *********************************************************************************************************/ WinPen::WinPen(vint Style, vint Width, COLORREF Color) { FDIBMemory=0; FHandle=CreatePen((int)Style, (int)Width, (int)Color); } WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Width, COLORREF Color) { FDIBMemory=0; LOGBRUSH Brush; Brush.lbColor=Color; Brush.lbStyle=BS_SOLID; Brush.lbHatch=0; FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0); } WinPen::WinPen(vint Style, vint EndCap, vint Join, vint Hatch, vint Width, COLORREF Color) { FDIBMemory=0; LOGBRUSH Brush; Brush.lbColor=Color; Brush.lbStyle=BS_HATCHED; Brush.lbHatch=Hatch; FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0); } WinPen::WinPen(WinBitmap::Ptr DIB, vint Style, vint EndCap, vint Join, vint Width) { WinBitmap Temp(DIB->GetWidth(), DIB->GetHeight(), WinBitmap::vbb24Bits, true); Temp.GetWinDC()->Draw(0, 0, DIB); vint HeaderSize=sizeof(BITMAPINFOHEADER); FDIBMemory=new unsigned char[HeaderSize+Temp.GetHeight()*Temp.GetLineBytes()]; Temp.FillCompatibleHeader((BITMAPINFOHEADER*)FDIBMemory); memcpy(FDIBMemory+HeaderSize, Temp.GetScanLines()[0], Temp.GetHeight()*Temp.GetLineBytes()); LOGBRUSH Brush; Brush.lbColor=RGB(0, 0, 0); Brush.lbStyle=BS_DIBPATTERNPT; Brush.lbHatch=(LONG)FDIBMemory; FHandle=ExtCreatePen((int)(PS_GEOMETRIC|Style|EndCap|Join), (int)Width, &Brush, 0, 0); } WinPen::~WinPen() { DeleteObject(FHandle); if(FDIBMemory) { delete[] FDIBMemory; } } HPEN WinPen::GetHandle() { return FHandle; } /********************************************************************************************************* WinFont *********************************************************************************************************/ WinFont::WinFont(WString Name, vint Height, vint Width, vint Escapement, vint Orientation, vint Weight, bool Italic, bool Underline, bool StrikeOut, bool Antialise) { FFontInfo.lfHeight=(int)Height; FFontInfo.lfWidth=(int)Width; FFontInfo.lfEscapement=(int)Escapement; FFontInfo.lfOrientation=(int)Orientation; FFontInfo.lfWeight=(int)Weight; FFontInfo.lfItalic=Italic?TRUE:FALSE; FFontInfo.lfUnderline=Underline?TRUE:FALSE; FFontInfo.lfStrikeOut=StrikeOut?TRUE:FALSE; FFontInfo.lfCharSet=DEFAULT_CHARSET; FFontInfo.lfOutPrecision=OUT_DEFAULT_PRECIS; FFontInfo.lfClipPrecision=CLIP_DEFAULT_PRECIS; FFontInfo.lfQuality=Antialise?CLEARTYPE_QUALITY:NONANTIALIASED_QUALITY; FFontInfo.lfPitchAndFamily=DEFAULT_PITCH | FF_DONTCARE; wcsncpy_s(FFontInfo.lfFaceName, sizeof(FFontInfo.lfFaceName)/sizeof(*FFontInfo.lfFaceName), Name.Buffer(), LF_FACESIZE-1); FHandle=CreateFontIndirect(&FFontInfo); } WinFont::WinFont(LOGFONT* FontInfo) { FFontInfo=*FontInfo; FHandle=CreateFontIndirect(&FFontInfo); } WinFont::~WinFont() { DeleteObject(FHandle); } HFONT WinFont::GetHandle() { return FHandle; } LOGFONT* WinFont::GetInfo() { return &FFontInfo; } /********************************************************************************************************* IWinResourceService *********************************************************************************************************/ WinBrush::Ptr CreateDefaultBrush() { return new WinBrush(RGB(255, 255, 255)); } WinPen::Ptr CreateDefaultPen() { return new WinPen(PS_SOLID, 0, RGB(0, 0, 0)); } WinFont::Ptr CreateDefaultFont() { NONCLIENTMETRICS NonClientMetrics; NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0); if(!*NonClientMetrics.lfMessageFont.lfFaceName) { NonClientMetrics.cbSize=sizeof(NONCLIENTMETRICS)-sizeof(NonClientMetrics.iPaddedBorderWidth); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, NonClientMetrics.cbSize, &NonClientMetrics, 0); } return new WinFont(&NonClientMetrics.lfMessageFont); } class DefaultResourceService : public Object, public IWinResourceService { public: static IWinResourceService* _DefaultResourceService; WinPen::Ptr GetDefaultPen() { return CreateDefaultPen(); } WinBrush::Ptr GetDefaultBrush() { return CreateDefaultBrush(); } WinFont::Ptr GetDefaultFont() { return CreateDefaultFont(); } } _DRS; IWinResourceService* DefaultResourceService::_DefaultResourceService=&_DRS; IWinResourceService* GetDefaultResourceService() { return DefaultResourceService::_DefaultResourceService; } void SetDefaultResourceService(IWinResourceService* Service) { DefaultResourceService::_DefaultResourceService=Service; } /********************************************************************************************************* WinDC *********************************************************************************************************/ void WinDC::Init() { FPen=GetDefaultResourceService()->GetDefaultPen(); FOldPen=(HPEN)SelectObject(FHandle, FPen->GetHandle()); FBrush=GetDefaultResourceService()->GetDefaultBrush(); FOldBrush=(HBRUSH)SelectObject(FHandle, FBrush->GetHandle()); FFont=GetDefaultResourceService()->GetDefaultFont(); FOldFont=(HFONT)SelectObject(FHandle, FFont->GetHandle()); SetGraphicsMode(FHandle, GM_ADVANCED); } WinDC::WinDC() { FHandle=0; FOldPen=0; FOldBrush=0; FOldFont=0; } WinDC::~WinDC() { SelectObject(FHandle, FOldFont); SelectObject(FHandle, FOldBrush); SelectObject(FHandle, FOldPen); } HDC WinDC::GetHandle() { return FHandle; } WinPen::Ptr WinDC::GetPen() { return FPen; } WinBrush::Ptr WinDC::GetBrush() { return FBrush; } WinFont::Ptr WinDC::GetFont() { return FFont; } void WinDC::SetPen(WinPen::Ptr Pen) { SelectObject(FHandle, Pen->GetHandle()); FPen=Pen; } void WinDC::SetBrush(WinBrush::Ptr Brush) { SelectObject(FHandle, Brush->GetHandle()); FBrush=Brush; } void WinDC::SetFont(WinFont::Ptr Font) { SelectObject(FHandle, Font->GetHandle()); FFont=Font; } COLORREF WinDC::GetBackColor() { return GetBkColor(FHandle); } void WinDC::SetBackColor(COLORREF Color) { SetBkColor(FHandle, Color); } COLORREF WinDC::GetTextColor() { return ::GetTextColor(FHandle); } void WinDC::SetTextColor(COLORREF Color) { ::SetTextColor(FHandle, Color); } bool WinDC::GetBackTransparent() { return GetBkMode(FHandle)==TRANSPARENT; } void WinDC::SetBackTransparent(bool Transparent) { SetBkMode(FHandle, Transparent?TRANSPARENT:OPAQUE); } POINT WinDC::GetBrushOrigin() { POINT Point; GetBrushOrgEx(FHandle, &Point); return Point; } void WinDC::SetBrushOrigin(POINT Point) { SetBrushOrgEx(FHandle, Point.x, Point.y, NULL); } /*------------------------------------------------------------------------------*/ void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount) { TextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount); } void WinDC::DrawBuffer(vint X, vint Y, const wchar_t* Text, vint CharCount, vint TabWidth, vint TabOriginX) { int realTabWidth=(int)TabWidth; TabbedTextOut(FHandle, (int)X, (int)Y, Text, (int)CharCount, 1, &realTabWidth, (int)TabOriginX); } void WinDC::DrawBuffer(RECT Rect, const wchar_t* Text, vint CharCount, UINT Format) { DrawText(FHandle, Text, (int)CharCount, &Rect, Format); } void WinDC::DrawString(vint X, vint Y, WString Text) { DrawBuffer(X, Y, Text.Buffer(), Text.Length()); } void WinDC::DrawString(vint X, vint Y, WString Text, vint TabWidth, vint TabOriginX) { DrawBuffer(X, Y, Text.Buffer(), Text.Length(), TabWidth, TabOriginX); } void WinDC::DrawString(RECT Rect, WString Text, UINT Format) { DrawBuffer(Rect, Text.Buffer(), Text.Length(), Format); } SIZE WinDC::MeasureString(WString Text, vint TabSize) { return MeasureBuffer(Text.Buffer(), Text.Length(), TabSize); } SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint CharCount, vint TabSize) { SIZE Size; if(TabSize==-1) { GetTextExtentPoint32(FHandle, Text, (int)CharCount, &Size); } else { int realTabSize=(int)TabSize; DWORD Result=GetTabbedTextExtent(FHandle, Text, (int)CharCount, 1, &realTabSize); Size.cx=LOWORD(Result); Size.cy=HIWORD(Result); } return Size; } SIZE WinDC::MeasureBuffer(const wchar_t* Text, vint TabSize) { return MeasureBuffer(Text, wcslen(Text), TabSize); } SIZE WinDC::MeasureWrapLineString(WString Text, vint MaxWidth) { return MeasureWrapLineBuffer(Text.Buffer(), Text.Length(), MaxWidth); } SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint CharCount, vint MaxWidth) { SIZE size = {0}; vint lineCount=0; const wchar_t* reading=Text; INT* dx=new INT[CharCount]; while(*reading) { INT fit=0; GetTextExtentExPoint(FHandle, reading, (int)(CharCount-(reading-Text)), (int)MaxWidth, &fit, dx, &size); reading+=fit; lineCount++; } delete dx; size.cx=0; size.cy*=(int)lineCount; return size; } SIZE WinDC::MeasureWrapLineBuffer(const wchar_t* Text, vint MaxWidth) { return MeasureWrapLineBuffer(Text, wcslen(Text), MaxWidth); } void WinDC::FillRegion(WinRegion::Ptr Region) { FillRgn(FHandle, Region->GetHandle(), FBrush->GetHandle()); } void WinDC::FrameRegion(WinRegion::Ptr Region, vint BlockWidth, vint BlockHeight) { FrameRgn(FHandle, Region->GetHandle(), FBrush->GetHandle(), (int)BlockWidth, (int)BlockHeight); } void WinDC::MoveTo(vint X, vint Y) { ::MoveToEx(FHandle, (int)X, (int)Y, NULL); } void WinDC::LineTo(vint X, vint Y) { ::LineTo(FHandle, (int)X, (int)Y); } void WinDC::Rectangle(vint Left, vint Top, vint Right, vint Bottom) { ::Rectangle(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom); } void WinDC::Rectangle(RECT Rect) { ::Rectangle(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom); } void WinDC::FocusRectangle(vint Left, vint Top, vint Right, vint Bottom) { RECT Rect; Rect.left=(int)Left; Rect.top=(int)Top; Rect.right=(int)Right; Rect.bottom=(int)Bottom; ::DrawFocusRect(FHandle, &Rect); } void WinDC::FocusRectangle(RECT Rect) { ::DrawFocusRect(FHandle, &Rect); } void WinDC::FillRect(vint Left, vint Top, vint Right, vint Bottom) { RECT Rect; Rect.left=(int)Left; Rect.top=(int)Top; Rect.right=(int)Right; Rect.bottom=(int)Bottom; ::FillRect(FHandle, &Rect, FBrush->GetHandle()); } void WinDC::FillRect(RECT Rect) { ::FillRect(FHandle, &Rect, FBrush->GetHandle()); } void WinDC::Ellipse(vint Left, vint Top, vint Right, vint Bottom) { ::Ellipse(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom); } void WinDC::Ellipse(RECT Rect) { ::Ellipse(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom); } void WinDC::RoundRect(vint Left, vint Top, vint Right, vint Bottom, vint EllipseWidth, vint EllipseHeight) { ::RoundRect(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)EllipseWidth, (int)EllipseHeight); } void WinDC::RoundRect(RECT Rect, vint EllipseWidth, vint EllipseHeight) { ::RoundRect(FHandle, (int)Rect.left, (int)Rect.top, (int)Rect.right, (int)Rect.bottom, (int)EllipseWidth, (int)EllipseHeight); } void WinDC::PolyLine(const POINT* Points, vint Count) { ::Polyline(FHandle, Points, (int)Count); } void WinDC::PolyLineTo(const POINT* Points, vint Count) { ::PolylineTo(FHandle, Points, (int)Count); } void WinDC::PolyGon(const POINT* Points, vint Count) { ::Polygon(FHandle, Points, (int)Count); } void WinDC::PolyBezier(const POINT* Points, vint Count) { ::PolyBezier(FHandle, Points, (int)Count); } void WinDC::PolyBezierTo(const POINT* Points, vint Count) { ::PolyBezierTo(FHandle, Points, (int)Count); } void WinDC::PolyDraw(const POINT* Points, const BYTE* Actions, vint PointCount) { ::PolyDraw(FHandle, Points, Actions, (int)PointCount); } void WinDC::Arc(RECT Bound, POINT Start, POINT End) { ::Arc(FHandle, Bound.left, Bound.top, Bound.right, Bound.bottom, Start.x, Start.y, End.x, End.y); } void WinDC::Arc(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY) { ::Arc(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY); } void WinDC::ArcTo(RECT Bound, POINT Start, POINT End) { ::ArcTo(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y); } void WinDC::ArcTo(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY) { ::ArcTo(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY); } void WinDC::AngleArc(vint X, vint Y, vint Radius, float StartAngle, float SweepAngle) { ::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, StartAngle, SweepAngle); } void WinDC::AngleArc(vint X, vint Y, vint Radius, double StartAngle, double SweepAngle) { ::AngleArc(FHandle, (int)X, (int)Y, (int)Radius, (float)StartAngle, (float)SweepAngle); } void WinDC::Chord(RECT Bound, POINT Start, POINT End) { ::Chord(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y); } void WinDC::Chord(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY) { ::Chord(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY); } void WinDC::Pie(RECT Bound, POINT Start, POINT End) { ::Pie(FHandle, (int)Bound.left, (int)Bound.top, (int)Bound.right, (int)Bound.bottom, (int)Start.x, (int)Start.y, (int)End.x, (int)End.y); } void WinDC::Pie(vint Left, vint Top, vint Right, vint Bottom, vint StartX, vint StartY, vint EndX, vint EndY) { ::Pie(FHandle, (int)Left, (int)Top, (int)Right, (int)Bottom, (int)StartX, (int)StartY, (int)EndX, (int)EndY); } void WinDC::GradientTriangle(TRIVERTEX* Vertices, vint VerticesCount, GRADIENT_TRIANGLE* Triangles, vint TriangleCount) { GradientFill(FHandle, Vertices, (int)VerticesCount, Triangles, (int)TriangleCount, GRADIENT_FILL_TRIANGLE); } /*------------------------------------------------------------------------------*/ void WinDC::BeginPath() { ::BeginPath(FHandle); } void WinDC::EndPath() { ::EndPath(FHandle); } void WinDC::ClosePath() { ::CloseFigure(FHandle); } void WinDC::WidenPath() { ::WidenPath(FHandle); } void WinDC::DiscardPath() { ::AbortPath(FHandle); } void WinDC::DrawPath() { ::StrokePath(FHandle); } void WinDC::FillPath() { ::FillPath(FHandle); } void WinDC::DrawAndFillPath() { ::StrokeAndFillPath(FHandle); } WinRegion::Ptr WinDC::RegionFromPath() { return new WinRegion(::PathToRegion(FHandle)); } /*------------------------------------------------------------------------------*/ bool WinDC::PointInClip(POINT Point) { return PtVisible(FHandle, Point.x, Point.y)==TRUE; } bool WinDC::RectInClip(RECT Rect) { return RectVisible(FHandle, &Rect)==TRUE; } void WinDC::ClipPath(vint CombineMode) { SelectClipPath(FHandle, (int)CombineMode); } void WinDC::ClipRegion(WinRegion::Ptr Region) { SelectClipRgn(FHandle, Region->GetHandle()); } void WinDC::RemoveClip() { SelectClipRgn(FHandle, NULL); } void WinDC::MoveClip(vint OffsetX, vint OffsetY) { OffsetClipRgn(FHandle, (int)OffsetX, (int)OffsetY); } void WinDC::CombineClip(WinRegion::Ptr Region, vint CombineMode) { ExtSelectClipRgn(FHandle, Region->GetHandle(), (int)CombineMode); } void WinDC::IntersetClipRect(RECT Rect) { ::IntersectClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom); } void WinDC::ExcludeClipRect(RECT Rect) { ::ExcludeClipRect(FHandle, Rect.left, Rect.top, Rect.right, Rect.bottom); } WinRegion::Ptr WinDC::GetClipRegion() { HRGN Handle=CreateRectRgn(0, 0, 1, 1); GetClipRgn(FHandle, Handle); return new WinRegion(Handle); } RECT WinDC::GetClipBoundRect() { RECT Rect; GetClipBox(FHandle, &Rect); return Rect; } /*------------------------------------------------------------------------------*/ WinTransform WinDC::GetTransform() { XFORM Transform; GetWorldTransform(FHandle, &Transform); return Transform; } void WinDC::SetTransform(const WinTransform& Transform) { SetWorldTransform(FHandle, Transform.GetHandle()); } /*------------------------------------------------------------------------------*/ void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY, DWORD DrawROP) { HDC SourceHandle=Source?Source->GetHandle():0; BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)DrawROP); } void WinDC::Copy(RECT dstRect, WinDC* Source, POINT srcPos, DWORD DrawROP) { HDC SourceHandle=Source?Source->GetHandle():0; BitBlt(FHandle, dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, SourceHandle, srcPos.x, srcPos.y, DrawROP); } void WinDC::Copy(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, DWORD DrawROP) { HDC SourceHandle=Source?Source->GetHandle():0; StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, SourceHandle, (int)srcX, (int)srcY, (int)srcW, (int)srcH, (int)DrawROP); } void WinDC::Copy(RECT dstRect, WinDC* Source, RECT srcRect, DWORD DrawROP) { HDC SourceHandle=Source?Source->GetHandle():0; StretchBlt( FHandle , dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, SourceHandle, srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, DrawROP); } void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC* Source, vint srcX, vint srcY, vint srcW, vint srcH) { POINT Pt[3]; Pt[0]=UpperLeft; Pt[1]=UpperRight; Pt[2]=LowerLeft; PlgBlt(FHandle, Pt, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, 0, 0, 0); } void WinDC::Copy(POINT UpperLeft, POINT UpperRight, POINT LowerLeft, WinDC*Source, RECT srcRect) { POINT Pt[3]; Pt[0]=UpperLeft; Pt[1]=UpperRight; Pt[2]=LowerLeft; PlgBlt(FHandle, Pt, Source->GetHandle(), srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, 0, 0, 0); } void WinDC::CopyTrans(vint dstX, vint dstY, vint dstW, vint dstH, WinDC* Source, vint srcX, vint srcY , vint srcW, vint srcH, COLORREF Color) { TransparentBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Source->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Color); } void WinDC::CopyTrans(RECT dstRect, WinDC* Source, RECT srcRect, COLORREF Color) { TransparentBlt( FHandle , dstRect.left, dstRect.top, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, Source->GetHandle() , srcRect.left, srcRect.top, srcRect.right-srcRect.left, srcRect.bottom-srcRect.top, Color); } /*------------------------------------------------------------------------------*/ void WinDC::Draw(vint dstX, vint dstY, WinMetaFile* MetaFile) { Draw(dstX, dstY, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile); } void WinDC::Draw(POINT Pos, WinMetaFile* MetaFile) { Draw(Pos.x, Pos.y, MetaFile->GetWidth(), MetaFile->GetHeight(), MetaFile); } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinMetaFile* MetaFile) { RECT Rect; Rect.left=(int)dstX; Rect.top=(int)dstY; Rect.right=(int)(dstX+dstW); Rect.bottom=(int)(dstY+dstH); Draw(Rect, MetaFile); } void WinDC::Draw(RECT Rect, WinMetaFile* MetaFile) { PlayEnhMetaFile(FHandle, MetaFile->GetHandle(), &Rect); } /*------------------------------------------------------------------------------*/ void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap) { vint dstW=Bitmap->GetWidth(); vint dstH=Bitmap->GetHeight(); vint srcX=0; vint srcY=0; if(!Bitmap->IsAlphaChannelBuilt()) { BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY); } else { vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap) { vint dstX=Pos.x; vint dstY=Pos.y; vint dstW=Bitmap->GetWidth(); vint dstH=Bitmap->GetHeight(); vint srcX=0; vint srcY=0; if(!Bitmap->IsAlphaChannelBuilt()) { BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY); } else { vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap) { vint srcX=0; vint srcY=0; vint srcW=Bitmap->GetWidth(); vint srcH=Bitmap->GetHeight(); if(!Bitmap->IsAlphaChannelBuilt()) { StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY); } else { BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap) { vint dstX=Rect.left; vint dstY=Rect.top; vint dstW=Rect.right-Rect.left; vint dstH=Rect.bottom-Rect.top; vint srcX=0; vint srcY=0; vint srcW=Bitmap->GetWidth(); vint srcH=Bitmap->GetHeight(); if(!Bitmap->IsAlphaChannelBuilt()) { StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY); } else { BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY) { if(!Bitmap->IsAlphaChannelBuilt()) { BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY); } else { vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos) { vint dstX=Rect.left; vint dstY=Rect.top; vint dstW=Rect.right-Rect.left; vint dstH=Rect.bottom-Rect.top; vint srcX=Pos.x; vint srcY=Pos.y; if(!Bitmap->IsAlphaChannelBuilt()) { BitBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, SRCCOPY); } else { vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH) { if(!Bitmap->IsAlphaChannelBuilt()) { StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY); } else { BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect) { vint dstX=dstRect.left; vint dstY=dstRect.top; vint dstW=dstRect.right-dstRect.left; vint dstH=dstRect.bottom-dstRect.top; vint srcX=srcRect.left; vint srcY=srcRect.top; vint srcW=srcRect.right-srcRect.left; vint srcH=srcRect.bottom-srcRect.top; if(!Bitmap->IsAlphaChannelBuilt()) { StretchBlt(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, SRCCOPY); } else { BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=255; Blend.AlphaFormat=AC_SRC_ALPHA; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } } /*------------------------------------------------------------------------------*/ void WinDC::Draw(vint dstX, vint dstY, WinBitmap::Ptr Bitmap, unsigned char Alpha) { vint dstW=Bitmap->GetWidth(); vint dstH=Bitmap->GetHeight(); vint srcX=0; vint srcY=0; vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(POINT Pos, WinBitmap::Ptr Bitmap, unsigned char Alpha) { vint dstX=Pos.x; vint dstY=Pos.y; vint dstW=Bitmap->GetWidth(); vint dstH=Bitmap->GetHeight(); vint srcX=0; vint srcY=0; vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, unsigned char Alpha) { vint srcX=0; vint srcY=0; vint srcW=Bitmap->GetWidth(); vint srcH=Bitmap->GetHeight(); BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, unsigned char Alpha) { vint dstX=Rect.left; vint dstY=Rect.top; vint dstW=Rect.right-Rect.left; vint dstH=Rect.bottom-Rect.top; vint srcX=0; vint srcY=0; vint srcW=Bitmap->GetWidth(); vint srcH=Bitmap->GetHeight(); BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, unsigned char Alpha) { vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(RECT Rect, WinBitmap::Ptr Bitmap, POINT Pos, unsigned char Alpha) { vint dstX=Rect.left; vint dstY=Rect.top; vint dstW=Rect.right-Rect.left; vint dstH=Rect.bottom-Rect.top; vint srcX=Pos.x; vint srcY=Pos.y; vint srcW=dstW; vint srcH=dstH; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(vint dstX, vint dstY, vint dstW, vint dstH, WinBitmap::Ptr Bitmap, vint srcX, vint srcY, vint srcW, vint srcH, unsigned char Alpha) { BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } void WinDC::Draw(RECT dstRect, WinBitmap::Ptr Bitmap, RECT srcRect, unsigned char Alpha) { vint dstX=dstRect.left; vint dstY=dstRect.top; vint dstW=dstRect.right-dstRect.left; vint dstH=dstRect.bottom-dstRect.top; vint srcX=srcRect.left; vint srcY=srcRect.top; vint srcW=srcRect.right-srcRect.left; vint srcH=srcRect.bottom-srcRect.top; BLENDFUNCTION Blend; Blend.BlendOp=AC_SRC_OVER; Blend.BlendFlags=0; Blend.SourceConstantAlpha=Alpha; Blend.AlphaFormat=Bitmap->IsAlphaChannelBuilt()?AC_SRC_ALPHA:0; AlphaBlend(FHandle, (int)dstX, (int)dstY, (int)dstW, (int)dstH, Bitmap->GetWinDC()->GetHandle(), (int)srcX, (int)srcY, (int)srcW, (int)srcH, Blend); } /********************************************************************************************************* WinControlDC *********************************************************************************************************/ WinControlDC::WinControlDC(HWND Handle) { FControlHandle=Handle; FHandle=GetDC(FControlHandle); Init(); } WinControlDC::~WinControlDC() { ReleaseDC(FControlHandle, FHandle); } /********************************************************************************************************* WinProxyDC *********************************************************************************************************/ WinProxyDC::WinProxyDC() { FHandle=NULL; } WinProxyDC::~WinProxyDC() { } void WinProxyDC::Initialize(HDC Handle) { FHandle=Handle; Init(); } /********************************************************************************************************* WinImageDC *********************************************************************************************************/ WinImageDC::WinImageDC() { FHandle=CreateCompatibleDC(NULL); Init(); } WinImageDC::~WinImageDC() { DeleteDC(FHandle); } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\GDI\WINGDIAPPLICATION.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { using namespace vl::collections; class GdiWindowsNativeWindowListener : public Object, public INativeWindowListener { protected: Ptr buffer; INativeWindow* window; vint DetermineBufferLength(vint minSize, vint minBound, vint maxBound, vint currentSize) { if(currentSizemaxBound) { return minBound; } else { return currentSize; } } Size CalculateBufferSize() { Size windowSize=window->GetClientSize(); Size minBounds(windowSize.x*5/4, windowSize.y*5/4); Size maxBounds(windowSize.x*3/2, windowSize.y*3/2); Size currentSize=buffer?Size(buffer->GetWidth(), buffer->GetHeight()):Size(0, 0); vint newWidth=DetermineBufferLength(windowSize.x, minBounds.x, maxBounds.x, currentSize.x); vint newHeight=DetermineBufferLength(windowSize.y, minBounds.y, maxBounds.y, currentSize.y); return Size(newWidth, newHeight); } void RebuildCanvas(Size size) { if(size.x<256)size.x=256; if(size.y<256)size.y=256; if(buffer) { if(buffer->GetWidth()!=size.x || buffer->GetHeight()!=size.y) { buffer=0; } } if(!buffer) { buffer=new WinBitmap(size.x, size.y, WinBitmap::vbb32Bits, true); buffer->GetWinDC()->SetBackTransparent(true); } } public: GdiWindowsNativeWindowListener(INativeWindow* _window) :window(_window) { } void Moved() { RebuildCanvas(CalculateBufferSize()); } void Paint() { IWindowsForm* form=GetWindowsForm(window); WinControlDC controlDC(form->GetWindowHandle()); controlDC.Draw(0, 0, buffer); } WinDC* GetWinDC() { if(!buffer) Moved(); return buffer->GetWinDC(); } }; class GdiWindowsNativeControllerListener : public Object, public INativeControllerListener { public: Dictionary> nativeWindowListeners; void NativeWindowCreated(INativeWindow* window) { Ptr listener=new GdiWindowsNativeWindowListener(window); window->InstallListener(listener.Obj()); nativeWindowListeners.Add(window, listener); } void NativeWindowDestroying(INativeWindow* window) { Ptr listener=nativeWindowListeners[window]; nativeWindowListeners.Remove(window); window->UninstallListener(listener.Obj()); } }; GdiWindowsNativeControllerListener* gdiListener=0; WinDC* GetNativeWindowDC(INativeWindow* window) { vint index=gdiListener->nativeWindowListeners.Keys().IndexOf(window); return index==-1?0:gdiListener->nativeWindowListeners.Values().Get(index)->GetWinDC(); } HDC GetNativeWindowHDC(INativeWindow* window) { WinDC* dc=GetNativeWindowDC(window); return dc?dc->GetHandle():NULL; } } namespace elements_windows_gdi { /*********************************************************************** OS Supporting ***********************************************************************/ class WinGDIApplicationGDIObjectProvider : public IWindowsGDIObjectProvider { protected: IMLangFontLink2* mLangFontLink; public: WinGDIApplicationGDIObjectProvider() :mLangFontLink(0) { CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMLangFontLink2, (void**)&mLangFontLink); } ~WinGDIApplicationGDIObjectProvider() { mLangFontLink->Release(); } windows::WinDC* GetNativeWindowDC(INativeWindow* window)override { return vl::presentation::windows::GetNativeWindowDC(window); } IWindowsGDIRenderTarget* GetBindedRenderTarget(INativeWindow* window)override { return dynamic_cast(vl::presentation::windows::GetWindowsForm(window)->GetGraphicsHandler()); } void SetBindedRenderTarget(INativeWindow* window, IWindowsGDIRenderTarget* renderTarget)override { vl::presentation::windows::GetWindowsForm(window)->SetGraphicsHandler(renderTarget); } IWICImagingFactory* GetWICImagingFactory()override { return vl::presentation::windows::GetWICImagingFactory(); } IWICBitmap* GetWICBitmap(INativeImageFrame* frame)override { return vl::presentation::windows::GetWICBitmap(frame); } IMLangFontLink2* GetMLangFontLink()override { return mLangFontLink; } }; } } } using namespace vl; using namespace vl::presentation; using namespace vl::presentation::windows; using namespace vl::presentation::elements_windows_gdi; int WinMainGDI(HINSTANCE hInstance, void(*RendererMain)()) { EnableCrossKernelCrashing(); // create controller INativeController* controller=CreateWindowsNativeController(hInstance); SetCurrentController(controller); { // install listener GdiWindowsNativeControllerListener listener; controller->CallbackService()->InstallListener(&listener); gdiListener=&listener; // main RendererMain(); // uninstall listener gdiListener=0; controller->CallbackService()->UninstallListener(&listener); } // destroy controller DestroyWindowsNativeController(controller); return 0; } int SetupWindowsGDIRenderer() { CoInitializeEx(NULL, COINIT_MULTITHREADED); HINSTANCE hInstance=(HINSTANCE)GetModuleHandle(NULL); WinGDIApplicationGDIObjectProvider objectProvider; SetWindowsGDIObjectProvider(&objectProvider); return WinMainGDI(hInstance, &RendererMainGDI); } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSASYNCSERVICE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { using namespace collections; /*********************************************************************** WindowsAsyncService::TaskItem ***********************************************************************/ WindowsAsyncService::TaskItem::TaskItem() :semaphore(0) { } WindowsAsyncService::TaskItem::TaskItem(Semaphore* _semaphore, const Func& _proc) :semaphore(_semaphore) ,proc(_proc) { } WindowsAsyncService::TaskItem::~TaskItem() { } /*********************************************************************** WindowsAsyncService::DelayItem ***********************************************************************/ WindowsAsyncService::DelayItem::DelayItem(WindowsAsyncService* _service, const Func& _proc, bool _executeInMainThread, vint milliseconds) :service(_service) ,proc(_proc) ,status(INativeDelay::Pending) ,executeTime(DateTime::LocalTime().Forward(milliseconds)) ,executeInMainThread(_executeInMainThread) { } WindowsAsyncService::DelayItem::~DelayItem() { } INativeDelay::ExecuteStatus WindowsAsyncService::DelayItem::GetStatus() { return status; } bool WindowsAsyncService::DelayItem::Delay(vint milliseconds) { SPIN_LOCK(service->taskListLock) { if(status==INativeDelay::Pending) { executeTime=DateTime::LocalTime().Forward(milliseconds); return true; } } return false; } bool WindowsAsyncService::DelayItem::Cancel() { SPIN_LOCK(service->taskListLock) { if(status==INativeDelay::Pending) { if(service->delayItems.Remove(this)) { status=INativeDelay::Canceled; return true; } } } return false; } /*********************************************************************** WindowsAsyncService ***********************************************************************/ WindowsAsyncService::WindowsAsyncService() :mainThreadId(Thread::GetCurrentThreadId()) { } WindowsAsyncService::~WindowsAsyncService() { } void WindowsAsyncService::ExecuteAsyncTasks() { DateTime now=DateTime::LocalTime(); Array items; List> executableDelayItems; SPIN_LOCK(taskListLock) { CopyFrom(items, taskItems); taskItems.RemoveRange(0, items.Count()); for(vint i=delayItems.Count()-1;i>=0;i--) { Ptr item=delayItems[i]; if(now.filetime>=item->executeTime.filetime) { item->status=INativeDelay::Executing; executableDelayItems.Add(item); delayItems.RemoveAt(i); } } } FOREACH(TaskItem, item, items) { item.proc(); if(item.semaphore) { item.semaphore->Release(); } } FOREACH(Ptr, item, executableDelayItems) { if(item->executeInMainThread) { item->proc(); item->status=INativeDelay::Executed; } else { InvokeAsync([=]() { item->proc(); item->status=INativeDelay::Executed; }); } } } bool WindowsAsyncService::IsInMainThread() { return Thread::GetCurrentThreadId()==mainThreadId; } void WindowsAsyncService::InvokeAsync(const Func& proc) { ThreadPoolLite::Queue(proc); } void WindowsAsyncService::InvokeInMainThread(const Func& proc) { SPIN_LOCK(taskListLock) { TaskItem item(0, proc); taskItems.Add(item); } } bool WindowsAsyncService::InvokeInMainThreadAndWait(const Func& proc, vint milliseconds) { Semaphore semaphore; semaphore.Create(0, 1); SPIN_LOCK(taskListLock) { TaskItem item(&semaphore, proc); taskItems.Add(item); } if(milliseconds<0) { return semaphore.Wait(); } else { return semaphore.WaitForTime(milliseconds); } } Ptr WindowsAsyncService::DelayExecute(const Func& proc, vint milliseconds) { Ptr delay; SPIN_LOCK(taskListLock) { delay=new DelayItem(this, proc, false, milliseconds); delayItems.Add(delay); } return delay; } Ptr WindowsAsyncService::DelayExecuteInMainThread(const Func& proc, vint milliseconds) { Ptr delay; SPIN_LOCK(taskListLock) { delay=new DelayItem(this, proc, true, milliseconds); delayItems.Add(delay); } return delay; } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSCALLBACKSERVICE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { /*********************************************************************** WindowsCallbackService ***********************************************************************/ WindowsCallbackService::WindowsCallbackService() { } bool WindowsCallbackService::InstallListener(INativeControllerListener* listener) { if(listeners.Contains(listener)) { return false; } else { listeners.Add(listener); return true; } } bool WindowsCallbackService::UninstallListener(INativeControllerListener* listener) { if(listeners.Contains(listener)) { listeners.Remove(listener); return true; } else { return false; } } void WindowsCallbackService::InvokeMouseHook(WPARAM message, Point location) { switch(message) { case WM_LBUTTONDOWN: { for(vint i=0;iLeftButtonDown(location); } } break; case WM_LBUTTONUP: { for(vint i=0;iLeftButtonUp(location); } } break; case WM_RBUTTONDOWN: { for(vint i=0;iRightButtonDown(location); } } break; case WM_RBUTTONUP: { for(vint i=0;iRightButtonUp(location); } } break; case WM_MOUSEMOVE: { for(vint i=0;iMouseMoving(location); } } break; } } void WindowsCallbackService::InvokeGlobalTimer() { for(vint i=0;iGlobalTimer(); } } void WindowsCallbackService::InvokeClipboardUpdated() { for(vint i=0;iClipboardUpdated(); } } void WindowsCallbackService::InvokeNativeWindowCreated(INativeWindow* window) { for(vint i=0;iNativeWindowCreated(window); } } void WindowsCallbackService::InvokeNativeWindowDestroyed(INativeWindow* window) { for(vint i=0;iNativeWindowDestroying(window); } } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSCLIPBOARDSERVICE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { /*********************************************************************** WindowsClipboardService ***********************************************************************/ WindowsClipboardService::WindowsClipboardService() :ownerHandle(NULL) { } void WindowsClipboardService::SetOwnerHandle(HWND handle) { HWND oldHandle=ownerHandle; ownerHandle=handle; if(handle==NULL) { RemoveClipboardFormatListener(oldHandle); } else { AddClipboardFormatListener(ownerHandle); } } bool WindowsClipboardService::ContainsText() { if(OpenClipboard(ownerHandle)) { UINT format=0; bool contains=false; while(format=EnumClipboardFormats(format)) { if(format==CF_TEXT || format==CF_UNICODETEXT) { contains=true; break; } } CloseClipboard(); return contains; } return false; } WString WindowsClipboardService::GetText() { if(OpenClipboard(ownerHandle)) { WString result; HANDLE handle=GetClipboardData(CF_UNICODETEXT); if(handle!=0) { wchar_t* buffer=(wchar_t*)GlobalLock(handle); result=buffer; GlobalUnlock(handle); } CloseClipboard(); return result; } return L""; } bool WindowsClipboardService::SetText(const WString& value) { if(OpenClipboard(ownerHandle)) { EmptyClipboard(); vint size=(value.Length()+1)*sizeof(wchar_t); HGLOBAL data=GlobalAlloc(GMEM_MOVEABLE, size); wchar_t* buffer=(wchar_t*)GlobalLock(data); memcpy(buffer, value.Buffer(), size); GlobalUnlock(data); SetClipboardData(CF_UNICODETEXT, data); CloseClipboard(); return true; } return false; } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSDIALOGSERVICE.CPP ***********************************************************************/ #include #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;iFW_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& selectionFileNames, vint& selectionFilterIndex, FileDialogTypes dialogType, const WString& title, const WString& initialFileName, const WString& initialDirectory, const WString& defaultExtension, const WString& filter, FileDialogOptions options) { Array 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 filterSeparators; for(vint i=0;i filterBuffer(filter.Length()+2); vint index=0; for(vint i=0;i #pragma comment(lib, "WindowsCodecs.lib") namespace vl { namespace presentation { namespace windows { using namespace collections; /*********************************************************************** WindowsImageFrame ***********************************************************************/ void WindowsImageFrame::Initialize(IWICBitmapSource* bitmapSource) { IWICImagingFactory* factory=GetWICImagingFactory(); ComPtr converter; { IWICFormatConverter* formatConverter=0; HRESULT hr=factory->CreateFormatConverter(&formatConverter); if(SUCCEEDED(hr)) { converter=formatConverter; converter->Initialize( bitmapSource, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom); } } IWICBitmap* bitmap=0; IWICBitmapSource* convertedBitmapSource=0; if(converter) { convertedBitmapSource=converter.Obj(); } else { convertedBitmapSource=bitmapSource; } HRESULT hr=factory->CreateBitmapFromSource(convertedBitmapSource, WICBitmapCacheOnLoad, &bitmap); if(SUCCEEDED(hr)) { frameBitmap=bitmap; } } WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmapFrameDecode* frameDecode) :image(_image) { Initialize(frameDecode); } WindowsImageFrame::WindowsImageFrame(INativeImage* _image, IWICBitmap* sourceBitmap) :image(_image) { Initialize(sourceBitmap); } WindowsImageFrame::~WindowsImageFrame() { for(vint i=0;iOnDetach(this); } } INativeImage* WindowsImageFrame::GetImage() { return image; } Size WindowsImageFrame::GetSize() { UINT width=0; UINT height=0; frameBitmap->GetSize(&width, &height); return Size(width, height); } bool WindowsImageFrame::SetCache(void* key, Ptr cache) { vint index=caches.Keys().IndexOf(key); if(index!=-1) { return false; } caches.Add(key, cache); cache->OnAttach(this); return true; } Ptr WindowsImageFrame::GetCache(void* key) { vint index=caches.Keys().IndexOf(key); return index==-1?0:caches.Values().Get(index); } Ptr WindowsImageFrame::RemoveCache(void* key) { vint index=caches.Keys().IndexOf(key); if(index==-1) { return 0; } Ptr cache=caches.Values().Get(index); cache->OnDetach(this); caches.Remove(key); return cache; } IWICBitmap* WindowsImageFrame::GetFrameBitmap() { return frameBitmap.Obj(); } /*********************************************************************** WindowsImage ***********************************************************************/ WindowsImage::WindowsImage(INativeImageService* _imageService, IWICBitmapDecoder* _bitmapDecoder) :imageService(_imageService) ,bitmapDecoder(_bitmapDecoder) { UINT count=0; bitmapDecoder->GetFrameCount(&count); frames.Resize(count); } WindowsImage::~WindowsImage() { } INativeImageService* WindowsImage::GetImageService() { return imageService; } INativeImage::FormatType WindowsImage::GetFormat() { GUID formatGUID; HRESULT hr=bitmapDecoder->GetContainerFormat(&formatGUID); if(SUCCEEDED(hr)) { if(formatGUID==GUID_ContainerFormatBmp) { return INativeImage::Bmp; } else if(formatGUID==GUID_ContainerFormatPng) { return INativeImage::Png; } else if(formatGUID==GUID_ContainerFormatGif) { return INativeImage::Gif; } else if(formatGUID==GUID_ContainerFormatJpeg) { return INativeImage::Jpeg; } else if(formatGUID==GUID_ContainerFormatIco) { return INativeImage::Icon; } else if(formatGUID==GUID_ContainerFormatTiff) { return INativeImage::Tiff; } else if(formatGUID==GUID_ContainerFormatWmp) { return INativeImage::Wmp; } } return INativeImage::Unknown; } vint WindowsImage::GetFrameCount() { return frames.Count(); } INativeImageFrame* WindowsImage::GetFrame(vint index) { if(0<=index && index& frame=frames[index]; if(!frame) { IWICBitmapFrameDecode* frameDecode=0; HRESULT hr=bitmapDecoder->GetFrame((int)index, &frameDecode); if(SUCCEEDED(hr)) { frame=new WindowsImageFrame(this, frameDecode); frameDecode->Release(); } } return frame.Obj(); } else { return 0; } } /*********************************************************************** WindowsBitmapImage ***********************************************************************/ WindowsBitmapImage::WindowsBitmapImage(INativeImageService* _imageService, IWICBitmap* sourceBitmap, FormatType _formatType) :imageService(_imageService) ,formatType(_formatType) { frame = new WindowsImageFrame(this, sourceBitmap); } WindowsBitmapImage::~WindowsBitmapImage() { } INativeImageService* WindowsBitmapImage::GetImageService() { return imageService; } INativeImage::FormatType WindowsBitmapImage::GetFormat() { return formatType; } vint WindowsBitmapImage::GetFrameCount() { return 1; } INativeImageFrame* WindowsBitmapImage::GetFrame(vint index) { return index==0?frame.Obj():0; } /*********************************************************************** WindowsImageService ***********************************************************************/ WindowsImageService::WindowsImageService() { IWICImagingFactory* factory=0; HRESULT hr = CoCreateInstance( #if defined(WINCODEC_SDK_VERSION2) CLSID_WICImagingFactory1, #else CLSID_WICImagingFactory, #endif NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&factory ); if(SUCCEEDED(hr)) { imagingFactory=factory; } } WindowsImageService::~WindowsImageService() { } Ptr WindowsImageService::CreateImageFromFile(const WString& path) { IWICBitmapDecoder* bitmapDecoder=0; HRESULT hr=imagingFactory->CreateDecoderFromFilename( path.Buffer(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &bitmapDecoder); if(SUCCEEDED(hr)) { return new WindowsImage(this, bitmapDecoder); } else { return 0; } } Ptr WindowsImageService::CreateImageFromMemory(void* buffer, vint length) { Ptr result; ::IStream* stream=SHCreateMemStream((const BYTE*)buffer, (int)length); if(stream) { IWICBitmapDecoder* bitmapDecoder=0; HRESULT hr=imagingFactory->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnDemand, &bitmapDecoder); if(SUCCEEDED(hr)) { result=new WindowsImage(this, bitmapDecoder); } stream->Release(); } return result; } Ptr WindowsImageService::CreateImageFromStream(stream::IStream& stream) { stream::MemoryStream memoryStream; char buffer[65536]; while(true) { vint length=stream.Read(buffer, sizeof(buffer)); memoryStream.Write(buffer, length); if(length!=sizeof(buffer)) { break; } } return CreateImageFromMemory(memoryStream.GetInternalBuffer(), (vint)memoryStream.Size()); } Ptr WindowsImageService::CreateImageFromHBITMAP(HBITMAP handle) { IWICBitmap* bitmap=0; HRESULT hr=imagingFactory->CreateBitmapFromHBITMAP(handle, NULL, WICBitmapUseAlpha, &bitmap); if(SUCCEEDED(hr)) { Ptr image=new WindowsBitmapImage(this, bitmap, INativeImage::Bmp); bitmap->Release(); return image; } else { return 0; } } Ptr WindowsImageService::CreateImageFromHICON(HICON handle) { IWICBitmap* bitmap=0; HRESULT hr=imagingFactory->CreateBitmapFromHICON(handle, &bitmap); if(SUCCEEDED(hr)) { Ptr image=new WindowsBitmapImage(this, bitmap, INativeImage::Icon); bitmap->Release(); return image; } else { return 0; } } IWICImagingFactory* WindowsImageService::GetImagingFactory() { return imagingFactory.Obj(); } /*********************************************************************** Helper Functions ***********************************************************************/ IWICImagingFactory* GetWICImagingFactory() { return dynamic_cast(GetCurrentController()->ImageService())->GetImagingFactory(); } IWICBitmap* GetWICBitmap(INativeImageFrame* frame) { return dynamic_cast(frame)->GetFrameBitmap(); } Ptr CreateImageFromHBITMAP(HBITMAP handle) { return dynamic_cast(GetCurrentController()->ImageService())->CreateImageFromHBITMAP(handle); } Ptr CreateImageFromHICON(HICON handle) { return dynamic_cast(GetCurrentController()->ImageService())->CreateImageFromHICON(handle); } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSINPUTSERVICE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { bool WinIsKeyPressing(vint code) { return (GetKeyState((int)code)&0xF0)!=0; } bool WinIsKeyToggled(vint code) { return (GetKeyState((int)code)&0x0F)!=0; } /*********************************************************************** WindowsInputService ***********************************************************************/ WString WindowsInputService::GetKeyNameInternal(vint code) { if (code < 8) return L"?"; wchar_t name[256]={0}; vint scanCode=MapVirtualKey((int)code, MAPVK_VK_TO_VSC)<<16; switch(code) { case VK_INSERT: case VK_DELETE: case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT: case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: scanCode|=1<<24; break; case VK_CLEAR: case VK_LSHIFT: case VK_RSHIFT: case VK_LCONTROL: case VK_RCONTROL: case VK_LMENU: case VK_RMENU: return L"?"; } GetKeyNameText((int)scanCode, name, sizeof(name)/sizeof(*name)); return name[0]?name:L"?"; } void WindowsInputService::InitializeKeyNames() { for (vint i = 0; i < keyNames.Count(); i++) { keyNames[i] = GetKeyNameInternal(i); if (keyNames[i] != L"?") { keys.Set(keyNames[i], i); } } } WindowsInputService::WindowsInputService(HOOKPROC _mouseProc) :ownerHandle(NULL) ,mouseHook(NULL) ,isTimerEnabled(false) ,mouseProc(_mouseProc) ,keyNames(146) { InitializeKeyNames(); } void WindowsInputService::SetOwnerHandle(HWND handle) { ownerHandle=handle; } void WindowsInputService::StartHookMouse() { if(!IsHookingMouse()) { mouseHook=SetWindowsHookEx(WH_MOUSE_LL, mouseProc, NULL, NULL); } } void WindowsInputService::StopHookMouse() { if(IsHookingMouse()) { UnhookWindowsHookEx(mouseHook); mouseHook=NULL; } } bool WindowsInputService::IsHookingMouse() { return mouseHook!=NULL; } void WindowsInputService::StartTimer() { if(!IsTimerEnabled()) { SetTimer(ownerHandle, 1, 16, NULL); isTimerEnabled=true; } } void WindowsInputService::StopTimer() { if(IsTimerEnabled()) { KillTimer(ownerHandle, 1); isTimerEnabled=false; } } bool WindowsInputService::IsTimerEnabled() { return isTimerEnabled; } bool WindowsInputService::IsKeyPressing(vint code) { return WinIsKeyPressing(code); } bool WindowsInputService::IsKeyToggled(vint code) { return WinIsKeyToggled(code); } WString WindowsInputService::GetKeyName(vint code) { if (0 <= code && 0 < keyNames.Count()) { return keyNames[code]; } else { return L"?"; } } vint WindowsInputService::GetKey(const WString& name) { vint index = keys.Keys().IndexOf(name); return index == -1 ? -1 : keys.Values()[index]; } } } } /*********************************************************************** NATIVEWINDOW\WINDOWS\SERVICESIMPL\WINDOWSRESOURCESERVICE.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace windows { /*********************************************************************** WindowsCursor ***********************************************************************/ WindowsCursor::WindowsCursor(HCURSOR _handle) :handle(_handle) ,isSystemCursor(false) ,systemCursorType(INativeCursor::Arrow) { } WindowsCursor::WindowsCursor(SystemCursorType type) :handle(NULL) ,isSystemCursor(true) ,systemCursorType(type) { LPWSTR id=NULL; switch(type) { case SmallWaiting: id=IDC_APPSTARTING; break; case LargeWaiting: id=IDC_WAIT; break; case Arrow: id=IDC_ARROW; break; case Cross: id=IDC_CROSS; break; case Hand: id=IDC_HAND; break; case Help: id=IDC_HELP; break; case IBeam: id=IDC_IBEAM; break; case SizeAll: id=IDC_SIZEALL; break; case SizeNESW: id=IDC_SIZENESW; break; case SizeNS: id=IDC_SIZENS; break; case SizeNWSE: id=IDC_SIZENWSE; break; case SizeWE: id=IDC_SIZEWE; break; } handle=(HCURSOR)LoadImage(NULL, id, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE|LR_SHARED); } bool WindowsCursor::IsSystemCursor() { return isSystemCursor; } INativeCursor::SystemCursorType WindowsCursor::GetSystemCursorType() { return systemCursorType; } HCURSOR WindowsCursor::GetCursorHandle() { return handle; } /*********************************************************************** WindowsResourceService ***********************************************************************/ WindowsResourceService::WindowsResourceService() { { systemCursors.Resize(INativeCursor::SystemCursorCount); for(vint i=0;icurrentScreen==data->screenService->screens.Count()) { data->screenService->screens.Add(new WindowsScreen()); } data->screenService->screens[data->currentScreen]->monitor=hMonitor; data->currentScreen++; return TRUE; } void WindowsScreenService::RefreshScreenInformation() { for(vint i=0;imonitor=NULL; } MonitorEnumProcData data; data.screenService=this; data.currentScreen=0; EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)(&data)); } vint WindowsScreenService::GetScreenCount() { RefreshScreenInformation(); return GetSystemMetrics(SM_CMONITORS); } INativeScreen* WindowsScreenService::GetScreen(vint index) { RefreshScreenInformation(); return screens[index].Obj(); } INativeScreen* WindowsScreenService::GetScreen(INativeWindow* window) { RefreshScreenInformation(); HWND hwnd=handleRetriver(window); if(hwnd) { HMONITOR monitor=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); if(monitor!=NULL) { for(vint i=0;imonitor==monitor) { return screens[i].Obj(); } } } } return 0; } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSLAYOUTPROVIDERWINDOWSGDI.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements_windows_gdi { using namespace elements; using namespace collections; using namespace windows; /*********************************************************************** WindowsGDIParagraph ***********************************************************************/ class WindowsGDIParagraph : public Object, public IGuiGraphicsParagraph, protected UniscribeRun::IRendererCallback { protected: IGuiGraphicsLayoutProvider* provider; Ptr paragraph; WString text; IWindowsGDIRenderTarget* renderTarget; vint caret; Color caretColor; bool caretFrontSide; Ptr 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(_renderTarget)) ,caret(-1) ,caretFrontSide(false) ,paragraphDC(nullptr) ,paragraphCallback(_paragraphCallback) { paragraph=new UniscribeParagraph; paragraph->paragraphText=text; Ptr fragment=new UniscribeFragment(_text); fragment->fontStyle=GetCurrentController()->ResourceService()->GetDefaultFont(); paragraph->documentFragments.Add(fragment); } ~WindowsGDIParagraph() { CloseCaret(); } IGuiGraphicsLayoutProvider* GetProvider()override { return provider; } IGuiGraphicsRenderTarget* GetRenderTarget()override { return renderTarget; } bool GetWrapLine()override { return true; } void SetWrapLine(bool value)override { } vint GetMaxWidth()override { return paragraph->lastAvailableWidth; } void SetMaxWidth(vint value)override { paragraph->BuildUniscribeData(renderTarget->GetDC()); paragraph->Layout(value, paragraph->paragraphAlignment); } Alignment GetParagraphAlignment()override { return paragraph->paragraphAlignment; } void SetParagraphAlignment(Alignment value)override { paragraph->BuildUniscribeData(renderTarget->GetDC()); paragraph->Layout(paragraph->lastAvailableWidth, value); } bool SetFont(vint start, vint length, const WString& value)override { if(length==0) return true; if(0<=start && start=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=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=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=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=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=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=0 && 0<=start+length && start+length<=text.Length()) { if (auto inlineObject = paragraph->ResetInlineObject(start, length)) { if (auto element = inlineObject.Value().backgroundImage) { auto renderer=element->GetRenderer(); if(renderer) { renderer->SetRenderTarget(0); } } return true; } } return false; } vint GetHeight()override { PrepareUniscribeData(); return paragraph->bounds.Height(); } bool OpenCaret(vint _caret, Color _color, bool _frontSide)override { if(!IsValidCaret(_caret)) return false; if(caret!=-1) CloseCaret(); caret=_caret; caretColor=_color; caretFrontSide=_frontSide; caretPen=GetWindowsGDIResourceManager()->CreateGdiPen(caretColor); return true; } bool CloseCaret()override { if(caret==-1) return false; caret=-1; GetWindowsGDIResourceManager()->DestroyGdiPen(caretColor); caretPen=0; return true; } void Render(Rect bounds)override { PrepareUniscribeData(); paragraphDC = renderTarget->GetDC(); paragraphOffset = bounds.LeftTop(); paragraph->Render(this, true); paragraph->Render(this, false); paragraphDC = 0; if(caret!=-1) { Rect caretBounds=GetCaretBounds(caret, caretFrontSide); vint x=caretBounds.x1+bounds.x1; vint y1=caretBounds.y1+bounds.y1; vint y2=y1+caretBounds.Height(); WinDC* dc=renderTarget->GetDC(); dc->SetPen(caretPen); dc->MoveTo(x-1, y1); dc->LineTo(x-1, y2); dc->MoveTo(x, y1); dc->LineTo(x, y2); } } vint GetCaret(vint comparingCaret, CaretRelativePosition position, bool& preferFrontSide)override { PrepareUniscribeData(); return paragraph->GetCaret(comparingCaret, position, preferFrontSide); } Rect GetCaretBounds(vint caret, bool frontSide)override { PrepareUniscribeData(); return paragraph->GetCaretBounds(caret, frontSide); } vint GetCaretFromPoint(Point point)override { PrepareUniscribeData(); return paragraph->GetCaretFromPoint(point); } Nullable 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 WindowsGDILayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback) { return new WindowsGDIParagraph(this, text, renderTarget, callback); } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSRENDERERSWINDOWSGDI.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements_windows_gdi { using namespace windows; using namespace collections; /*********************************************************************** GuiSolidBorderElementRenderer ***********************************************************************/ void GuiSolidBorderElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldColor=element->GetColor(); pen=resourceManager->CreateGdiPen(oldColor); brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0)); } void GuiSolidBorderElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); resourceManager->DestroyGdiBrush(Color(0, 0, 0, 0)); } void GuiSolidBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void GuiSolidBorderElementRenderer::Render(Rect bounds) { if(oldColor.a>0) { renderTarget->GetDC()->SetBrush(brush); renderTarget->GetDC()->SetPen(pen); switch(element->GetShape()) { case ElementShape::Rectangle: renderTarget->GetDC()->Rectangle(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1); break; case ElementShape::Ellipse: renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1); break; } } } void GuiSolidBorderElementRenderer::OnElementStateChanged() { Color color=element->GetColor(); if(oldColor!=color) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); oldColor=color; pen=resourceManager->CreateGdiPen(oldColor); } } /*********************************************************************** GuiRoundBorderElementRenderer ***********************************************************************/ void GuiRoundBorderElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldColor=element->GetColor(); pen=resourceManager->CreateGdiPen(oldColor); brush=resourceManager->CreateGdiBrush(Color(0, 0, 0, 0)); } void GuiRoundBorderElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); resourceManager->DestroyGdiBrush(Color(0, 0, 0, 0)); } void GuiRoundBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void GuiRoundBorderElementRenderer::Render(Rect bounds) { if(oldColor.a>0) { vint ellipse=element->GetRadius()*2; renderTarget->GetDC()->SetBrush(brush); renderTarget->GetDC()->SetPen(pen); renderTarget->GetDC()->RoundRect(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1, ellipse, ellipse); } } void GuiRoundBorderElementRenderer::OnElementStateChanged() { Color color=element->GetColor(); if(oldColor!=color) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); oldColor=color; pen=resourceManager->CreateGdiPen(oldColor); } } /*********************************************************************** Gui3DBorderElementRenderer ***********************************************************************/ void Gui3DBorderElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldColor1=element->GetColor1(); oldColor2=element->GetColor2(); pen1=resourceManager->CreateGdiPen(oldColor1); pen2=resourceManager->CreateGdiPen(oldColor2); } void Gui3DBorderElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor1); resourceManager->DestroyGdiPen(oldColor2); } void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void Gui3DBorderElementRenderer::Render(Rect bounds) { if(oldColor1.a>0) { renderTarget->GetDC()->SetPen(pen1); renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1); renderTarget->GetDC()->LineTo(bounds.x2, bounds.y1); renderTarget->GetDC()->MoveTo(bounds.x1, bounds.y1); renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2); } if(oldColor2.a>0) { renderTarget->GetDC()->SetPen(pen2); renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1); renderTarget->GetDC()->LineTo(bounds.x1, bounds.y2-1); renderTarget->GetDC()->MoveTo(bounds.x2-1, bounds.y2-1); renderTarget->GetDC()->LineTo(bounds.x2-1, bounds.y1); } } void Gui3DBorderElementRenderer::OnElementStateChanged() { Color color1=element->GetColor1(); if(oldColor1!=color1) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor1); oldColor1=color1; pen1=resourceManager->CreateGdiPen(oldColor1); } Color color2=element->GetColor2(); if(oldColor2!=color2) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor2); oldColor2=color2; pen2=resourceManager->CreateGdiPen(oldColor2); } } /*********************************************************************** Gui3DSplitterElementRenderer ***********************************************************************/ void Gui3DSplitterElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldColor1=element->GetColor1(); oldColor2=element->GetColor2(); pen1=resourceManager->CreateGdiPen(oldColor1); pen2=resourceManager->CreateGdiPen(oldColor2); } void Gui3DSplitterElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor1); resourceManager->DestroyGdiPen(oldColor2); } void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void Gui3DSplitterElementRenderer::Render(Rect bounds) { Point p11, p12, p21, p22; switch(element->GetDirection()) { case Gui3DSplitterElement::Horizontal: { vint y=bounds.y1+bounds.Height()/2-1; p11=Point(bounds.x1, y); p12=Point(bounds.x2, y); p21=Point(bounds.x1, y+1); p22=Point(bounds.x2, y+1); } break; case Gui3DSplitterElement::Vertical: { vint x=bounds.x1+bounds.Width()/2-1; p11=Point(x, bounds.y1); p12=Point(x, bounds.y2); p21=Point(x+1, bounds.y1); p22=Point(x+1, bounds.y2); } break; } if(oldColor1.a>0) { renderTarget->GetDC()->SetPen(pen1); renderTarget->GetDC()->MoveTo(p11.x, p11.y); renderTarget->GetDC()->LineTo(p12.x, p12.y); } if(oldColor2.a>0) { renderTarget->GetDC()->SetPen(pen2); renderTarget->GetDC()->MoveTo(p21.x, p21.y); renderTarget->GetDC()->LineTo(p22.x, p22.y); } } void Gui3DSplitterElementRenderer::OnElementStateChanged() { Color color1=element->GetColor1(); if(oldColor1!=color1) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor1); oldColor1=color1; pen1=resourceManager->CreateGdiPen(oldColor1); } Color color2=element->GetColor2(); if(oldColor2!=color2) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor2); oldColor2=color2; pen2=resourceManager->CreateGdiPen(oldColor2); } } /*********************************************************************** GuiSolidBackgroundElementRenderer ***********************************************************************/ void GuiSolidBackgroundElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldColor=element->GetColor(); pen=resourceManager->CreateGdiPen(oldColor); brush=resourceManager->CreateGdiBrush(oldColor); } void GuiSolidBackgroundElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); resourceManager->DestroyGdiBrush(oldColor); } void GuiSolidBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void GuiSolidBackgroundElementRenderer::Render(Rect bounds) { if(oldColor.a>0) { renderTarget->GetDC()->SetPen(pen); renderTarget->GetDC()->SetBrush(brush); switch(element->GetShape()) { case ElementShape::Rectangle: renderTarget->GetDC()->FillRect(bounds.Left(), bounds.Top(), bounds.Right(), bounds.Bottom()); break; case ElementShape::Ellipse: renderTarget->GetDC()->Ellipse(bounds.Left(), bounds.Top(), bounds.Right()-1, bounds.Bottom()-1); break; } } } void GuiSolidBackgroundElementRenderer::OnElementStateChanged() { Color color=element->GetColor(); if(oldColor!=color) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldColor); resourceManager->DestroyGdiBrush(oldColor); oldColor=color; pen=resourceManager->CreateGdiPen(oldColor); brush=resourceManager->CreateGdiBrush(oldColor); } } /*********************************************************************** GuiGradientBackgroundElementRenderer ***********************************************************************/ void GuiGradientBackgroundElementRenderer::InitializeInternal() { } void GuiGradientBackgroundElementRenderer::FinalizeInternal() { } void GuiGradientBackgroundElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } void GuiGradientBackgroundElementRenderer::Render(Rect bounds) { Color color1=element->GetColor1(); Color color2=element->GetColor2(); if(color1.a>0 || color2.a>0) { TRIVERTEX vertices[4]; GRADIENT_TRIANGLE triangles[2]; vertices[0].x=(int)bounds.x1; vertices[0].y=(int)bounds.y1; vertices[1].x=(int)bounds.x1; vertices[1].y=(int)bounds.y2; vertices[2].x=(int)bounds.x2; vertices[2].y=(int)bounds.y2; vertices[3].x=(int)bounds.x2; vertices[3].y=(int)bounds.y1; triangles[0].Vertex1=0; triangles[0].Vertex2=1; triangles[0].Vertex3=2; triangles[1].Vertex1=0; triangles[1].Vertex2=2; triangles[1].Vertex3=3; if(element->GetDirection()==GuiGradientBackgroundElement::Horizontal) { vertices[0].Red=color1.r<<8; vertices[0].Green=color1.g<<8; vertices[0].Blue=color1.b<<8; vertices[0].Alpha=0xFF00; vertices[1].Red=color1.r<<8; vertices[1].Green=color1.g<<8; vertices[1].Blue=color1.b<<8; vertices[1].Alpha=0xFF00; vertices[2].Red=color2.r<<8; vertices[2].Green=color2.g<<8; vertices[2].Blue=color2.b<<8; vertices[2].Alpha=0xFF00; vertices[3].Red=color2.r<<8; vertices[3].Green=color2.g<<8; vertices[3].Blue=color2.b<<8; vertices[3].Alpha=0xFF00; } else { vertices[0].Red=color1.r<<8; vertices[0].Green=color1.g<<8; vertices[0].Blue=color1.b<<8; vertices[0].Alpha=0xFF00; vertices[1].Red=color2.r<<8; vertices[1].Green=color2.g<<8; vertices[1].Blue=color2.b<<8; vertices[1].Alpha=0xFF00; vertices[2].Red=color2.r<<8; vertices[2].Green=color2.g<<8; vertices[2].Blue=color2.b<<8; vertices[2].Alpha=0xFF00; vertices[3].Red=color1.r<<8; vertices[3].Green=color1.g<<8; vertices[3].Blue=color1.b<<8; vertices[3].Alpha=0xFF00; } switch(element->GetShape()) { case ElementShape::Rectangle: { renderTarget->GetDC()->GradientTriangle(vertices, 6, triangles, 2); } break; case ElementShape::Ellipse: { Ptr ellipseRegion=new WinRegion(bounds.x1, bounds.y1, bounds.x2+1, bounds.y2+1, false); Ptr oldRegion=renderTarget->GetDC()->GetClipRegion(); Ptr newRegion=new WinRegion(oldRegion, ellipseRegion, RGN_AND); renderTarget->GetDC()->ClipRegion(newRegion); renderTarget->GetDC()->GradientTriangle(vertices, 6, triangles, 2); renderTarget->GetDC()->ClipRegion(oldRegion); } break; } } } void GuiGradientBackgroundElementRenderer::OnElementStateChanged() { } /*********************************************************************** GuiSolidLabelElementRenderer ***********************************************************************/ void GuiSolidLabelElementRenderer::UpdateMinSize() { if(renderTarget) { renderTarget->GetDC()->SetFont(font); SIZE size={0}; const WString& text=element->GetText(); if(element->GetWrapLine()) { if(element->GetWrapLineHeightCalculation()) { if(oldMaxWidth==-1 || text.Length()==0) { size=renderTarget->GetDC()->MeasureBuffer(L" "); } else { size=renderTarget->GetDC()->MeasureWrapLineString(text, oldMaxWidth); } } } else { size=text.Length()==0 ?renderTarget->GetDC()->MeasureBuffer(L" ") :renderTarget->GetDC()->MeasureString(text) ; } minSize=Size((element->GetEllipse()?0:size.cx), size.cy); } else { minSize=Size(); } } void GuiSolidLabelElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); oldFont=element->GetFont(); font=resourceManager->CreateGdiFont(oldFont); } void GuiSolidLabelElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiFont(oldFont); } void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { UpdateMinSize(); } GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer() :oldMaxWidth(-1) { } void GuiSolidLabelElementRenderer::Render(Rect bounds) { Color color=element->GetColor(); if(color.a>0) { renderTarget->GetDC()->SetFont(font); renderTarget->GetDC()->SetTextColor(RGB(color.r, color.g, color.b)); UINT format=DT_NOPREFIX; RECT rect; rect.left=(int)bounds.Left(); rect.top=(int)bounds.Top(); rect.right=(int)bounds.Right(); rect.bottom=(int)bounds.Bottom(); if(element->GetMultiline() || element->GetWrapLine()) { format|=DT_EDITCONTROL; } else { format|=DT_SINGLELINE; switch(element->GetVerticalAlignment()) { case Alignment::Top: format|=DT_TOP; break; case Alignment::Center: format|=DT_VCENTER; break; case Alignment::Bottom: format|=DT_BOTTOM; break; } } switch(element->GetHorizontalAlignment()) { case Alignment::Left: format|=DT_LEFT; break; case Alignment::Center: format|=DT_CENTER; break; case Alignment::Right: format|=DT_RIGHT; break; } if(element->GetWrapLine()) { format|=DT_WORDBREAK; } if(element->GetEllipse()) { format|=DT_END_ELLIPSIS; } renderTarget->GetDC()->DrawString(rect, element->GetText(), format); if(oldMaxWidth!=bounds.Width()) { oldMaxWidth=bounds.Width(); UpdateMinSize(); } } } void GuiSolidLabelElementRenderer::OnElementStateChanged() { FontProperties fontProperties=element->GetFont(); if(oldFont!=fontProperties) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiFont(oldFont); oldFont=fontProperties; font=resourceManager->CreateGdiFont(oldFont); } UpdateMinSize(); } /*********************************************************************** GuiImageFrameElementRenderer ***********************************************************************/ void GuiImageFrameElementRenderer::UpdateBitmap() { if(element->GetImage()) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); bitmap=resourceManager->GetBitmap(frame, element->GetEnabled()); if (element->GetStretch()) { minSize=Size(0,0); } else { minSize=frame->GetSize(); } } else { bitmap=0; minSize=Size(0, 0); } } void GuiImageFrameElementRenderer::InitializeInternal() { UpdateBitmap(); } void GuiImageFrameElementRenderer::FinalizeInternal() { } void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } GuiImageFrameElementRenderer::GuiImageFrameElementRenderer() { } void GuiImageFrameElementRenderer::Render(Rect bounds) { if(bitmap) { WinDC* dc=renderTarget->GetDC(); Rect source(0, 0, minSize.x, minSize.y); Rect destination; if(element->GetStretch()) { INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); source = Rect(Point(0, 0), frame->GetSize()); destination=Rect(bounds.x1, bounds.y1, bounds.x2, bounds.y2); } else { vint x=0; vint y=0; switch(element->GetHorizontalAlignment()) { case Alignment::Left: x=bounds.Left(); break; case Alignment::Center: x=bounds.Left()+(bounds.Width()-minSize.x)/2; break; case Alignment::Right: x=bounds.Right()-minSize.x; break; } switch(element->GetVerticalAlignment()) { case Alignment::Top: y=bounds.Top(); break; case Alignment::Center: y=bounds.Top()+(bounds.Height()-minSize.y)/2; break; case Alignment::Bottom: y=bounds.Bottom()-minSize.y; break; } destination=Rect(x, y, x+minSize.x, y+minSize.y); } if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); vint max=element->GetFrameIndex(); for(vint i=0;i<=max;i++) { Ptr frameBitmap=resourceManager->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled()); dc->Draw( destination.Left(), destination.Top(), destination.Width(), destination.Height(), frameBitmap, source.Left(), source.Top(), source.Width(), source.Height() ); } } else { dc->Draw( destination.Left(), destination.Top(), destination.Width(), destination.Height(), bitmap, source.Left(), source.Top(), source.Width(), source.Height() ); } } } void GuiImageFrameElementRenderer::OnElementStateChanged() { UpdateBitmap(); } /*********************************************************************** GuiPolygonElementRenderer ***********************************************************************/ void GuiPolygonElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); pen=resourceManager->CreateGdiPen(oldPenColor); brush=resourceManager->CreateGdiBrush(oldBrushColor); } void GuiPolygonElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldPenColor); resourceManager->DestroyGdiBrush(oldBrushColor); } void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } GuiPolygonElementRenderer::GuiPolygonElementRenderer() :points(0) ,pointCount(0) ,oldPenColor(0, 0, 0, 0) ,oldBrushColor(0, 0, 0, 0) { } GuiPolygonElementRenderer::~GuiPolygonElementRenderer() { if(points) delete[] points; } void GuiPolygonElementRenderer::Render(Rect bounds) { if(pointCount>=3 && (oldPenColor.a || oldBrushColor.a)) { vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1; vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1; for(vint i=0;iGetDC()->SetPen(pen); renderTarget->GetDC()->SetBrush(brush); renderTarget->GetDC()->PolyGon(points, pointCount); for(vint i=0;iGetSize(); { if(points) { delete[] points; points=0; } pointCount=element->GetPointCount(); if(pointCount>0) { points=new POINT[pointCount]; for(vint i=0;iGetPoint(i); points[i].x=(int)p.x; points[i].y=(int)p.y; } } } IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); if(oldPenColor!=element->GetBorderColor() || !pen) { resourceManager->DestroyGdiPen(oldPenColor); oldPenColor=element->GetBorderColor(); pen=resourceManager->CreateGdiPen(oldPenColor); } if(oldBrushColor!=element->GetBackgroundColor() || !brush) { resourceManager->DestroyGdiPen(oldBrushColor); oldBrushColor=element->GetBackgroundColor(); brush=resourceManager->CreateGdiBrush(oldBrushColor); } } /*********************************************************************** GuiColorizedTextElementRenderer ***********************************************************************/ void GuiColorizedTextElementRenderer::DestroyColors() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); for(vint i=0;iDestroyGdiBrush(colors[i].normal.background); resourceManager->DestroyGdiBrush(colors[i].selectedFocused.background); resourceManager->DestroyGdiBrush(colors[i].selectedUnfocused.background); } } void GuiColorizedTextElementRenderer::ColorChanged() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); ColorArray newColors; newColors.Resize(element->GetColors().Count()); for(vint i=0;iGetColors().Get(i); ColorEntryResource newEntry; newEntry.normal.text=entry.normal.text; newEntry.normal.background=entry.normal.background; newEntry.normal.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.normal.background); newEntry.selectedFocused.text=entry.selectedFocused.text; newEntry.selectedFocused.background=entry.selectedFocused.background; newEntry.selectedFocused.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.selectedFocused.background); newEntry.selectedUnfocused.text=entry.selectedUnfocused.text; newEntry.selectedUnfocused.background=entry.selectedUnfocused.background; newEntry.selectedUnfocused.backgroundBrush=resourceManager->CreateGdiBrush(newEntry.selectedUnfocused.background); newColors[i]=newEntry; } DestroyColors(); CopyFrom(colors, newColors); } void GuiColorizedTextElementRenderer::FontChanged() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); if(font) { element->GetLines().SetCharMeasurer(0); resourceManager->DestroyGdiFont(oldFont); resourceManager->DestroyCharMeasurer(oldFont); font=0; } oldFont=element->GetFont(); font=resourceManager->CreateGdiFont(oldFont); element->GetLines().SetCharMeasurer(resourceManager->CreateCharMeasurer(oldFont).Obj()); } void GuiColorizedTextElementRenderer::InitializeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); element->SetCallback(this); oldCaretColor=element->GetCaretColor(); caretPen=resourceManager->CreateGdiPen(oldCaretColor); } void GuiColorizedTextElementRenderer::FinalizeInternal() { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); if(font) { resourceManager->DestroyGdiFont(oldFont); resourceManager->DestroyCharMeasurer(oldFont); } resourceManager->DestroyGdiPen(oldCaretColor); DestroyColors(); } void GuiColorizedTextElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { element->GetLines().SetRenderTarget(newRenderTarget); } void GuiColorizedTextElementRenderer::Render(Rect bounds) { if(renderTarget) { WinDC* dc=renderTarget->GetDC(); dc->SetFont(font); wchar_t passwordChar=element->GetPasswordChar(); Point viewPosition=element->GetViewPosition(); Rect viewBounds(viewPosition, bounds.GetSize()); vint startRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, viewBounds.y1)).row; vint endRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, viewBounds.y2)).row; TextPos selectionBegin=element->GetCaretBegin()GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd(); TextPos selectionEnd=element->GetCaretBegin()>element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd(); bool focused=element->GetFocused(); Ptr lastBrush=0; for(vint row=startRow;row<=endRow;row++) { Rect startRect=element->GetLines().GetRectFromTextPos(TextPos(row, 0)); Point startPoint=startRect.LeftTop(); vint startColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, startPoint.y)).column; vint endColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, startPoint.y)).column; text::TextLine& line=element->GetLines().GetLine(row); vint x=startColumn==0?0:line.att[startColumn-1].rightOffset; for(vint column=startColumn; column<=endColumn; column++) { bool inSelection=false; if(selectionBegin.row==selectionEnd.row) { inSelection=(row==selectionBegin.row && selectionBegin.column<=column && column=colors.Count()) { colorIndex=0; } ColorItemResource& color= !inSelection?colors[colorIndex].normal: focused?colors[colorIndex].selectedFocused: colors[colorIndex].selectedUnfocused; vint x2=crlf?x+startRect.Height()/2:line.att[column].rightOffset; vint tx=x-viewPosition.x+bounds.x1; vint ty=startPoint.y-viewPosition.y+bounds.y1; if(color.background.a) { if(lastBrush!=color.backgroundBrush) { lastBrush=color.backgroundBrush; dc->SetBrush(lastBrush); } dc->FillRect(tx, ty, tx+(x2-x), ty+startRect.Height()); } if(!crlf) { if(color.text.a) { dc->SetTextColor(RGB(color.text.r, color.text.g, color.text.b)); dc->DrawBuffer(tx, ty, (passwordChar?&passwordChar:&line.text[column]), 1); } } x=x2; } } if(element->GetCaretVisible() && element->GetLines().IsAvailable(element->GetCaretEnd())) { Point caretPoint=element->GetLines().GetPointFromTextPos(element->GetCaretEnd()); vint height=element->GetLines().GetRowHeight(); dc->SetPen(caretPen); dc->MoveTo(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1); dc->LineTo(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1); dc->MoveTo(caretPoint.x-1-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1); dc->LineTo(caretPoint.x-1-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1); } } } void GuiColorizedTextElementRenderer::OnElementStateChanged() { Color caretColor=element->GetCaretColor(); if(oldCaretColor!=caretColor) { IWindowsGDIResourceManager* resourceManager=GetWindowsGDIResourceManager(); resourceManager->DestroyGdiPen(oldCaretColor); oldCaretColor=caretColor; caretPen=resourceManager->CreateGdiPen(oldCaretColor); } } /*********************************************************************** GuiGDIElementRenderer ***********************************************************************/ void GuiGDIElementRenderer::InitializeInternal() { } void GuiGDIElementRenderer::FinalizeInternal() { } void GuiGDIElementRenderer::RenderTargetChangedInternal(IWindowsGDIRenderTarget* oldRenderTarget, IWindowsGDIRenderTarget* newRenderTarget) { } GuiGDIElementRenderer::GuiGDIElementRenderer() { } GuiGDIElementRenderer::~GuiGDIElementRenderer() { } void GuiGDIElementRenderer::Render(Rect bounds) { if(renderTarget) { renderTarget->PushClipper(bounds); if(!renderTarget->IsClipperCoverWholeTarget()) { WinDC* dc=renderTarget->GetDC(); GuiGDIElementEventArgs arguments(element, dc, bounds); element->Rendering.Execute(arguments); } renderTarget->PopClipper(); } } void GuiGDIElementRenderer::OnElementStateChanged() { } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSUNISCRIBE.CPP ***********************************************************************/ #pragma comment(lib, "usp10.lib") namespace vl { namespace presentation { namespace elements_windows_gdi { using namespace regex; /*********************************************************************** UniscribeFragment ***********************************************************************/ UniscribeFragment::UniscribeFragment(const WString& _text) :text(_text) { colors.Add(UniscribeColorRange(0, text.Length()), UniscribeColor(Color(0, 0, 0), Color(0, 0, 0, 0))); } WString UniscribeFragment::GetFingerprint() { return fontStyle.fontFamily+L"#" +itow(fontStyle.size)+L"#" +(fontStyle.bold?L"B":L"N")+L"#" +(fontStyle.italic?L"I":L"N")+L"#" +(fontStyle.underline?L"U":L"N")+L"#" +(fontStyle.strikeline?L"S":L"N")+L"#" ; } void UniscribeFragment::CutColors(vint start, vint length) { vint end=start+length; for(vint i=colors.Count()-1;i>=0;i--) { UniscribeColorRange key=colors.Keys()[i]; if(key.startstart?key.start:start; vint s3=key.end=0;i--) { UniscribeColorRange key=colors.Keys()[i]; if(key.start=-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.end) { start=middle+1; } else { return colors.Values()[middle]; } } return UniscribeColor(); } Ptr UniscribeFragment::Copy(vint start, vint length) { vint end=start+length; Ptr fragment=new UniscribeFragment(length==0?L"":text.Sub(start, length)); fragment->fontStyle=fontStyle; fragment->fontObject=fontObject; if(length!=0) { fragment->colors.Clear(); CutColors(start, length); for(vint i=0;icolors.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& breakings, List& 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(charIndexa.fRTL) { glyphCount=-glyphCount; glyphIndex-=glyphCount-1; } bool available=true; for(vint i=0;iGetHandle():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;ia; 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;iMeasureBuffer(runText, glyphLength, -1); glyphAdvances[lastGlyphIndex]=size.cx; lastCharIndex=i; lastGlyphIndex++; } } } for(vint i=0;istartFromLine; vint currentFromItem=startFromItem; while(++currentFromItemlength) { 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& breakings) { ClearUniscribeData(); dc->SetFont(documentFragment->fontObject); List 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;ifontStyle.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+clusterLengthIsRightToLeft()) { 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;jGetWinDC(); RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex]; if(fragment.length==0) return; vint startFromFragmentBounds=0; vint accumulatedWidth=0; while(startFromFragmentBoundsIsRightToLeft()) { clusterStart=nextCluster+1; clusterCount=cluster-nextCluster; } else { clusterStart=cluster; clusterCount=nextCluster-cluster; } vint clusterWidth=0; for(vint i=0;iIsRightToLeft()) { x=fragment.bounds.x2-accumulatedWidth-clusterWidth; } else { x=fragment.bounds.x1+accumulatedWidth; } RECT rect; rect.left=(int)(x+offsetX); rect.top=(int)(fragment.bounds.Top()+offsetY); rect.right=(int)(rect.left+clusterWidth); rect.bottom=(int)(rect.top+fragment.bounds.Height()*1.5); UniscribeColor color=documentFragment->GetCharColor(charIndex+startFromFragment); if(renderBackground) { Color backgroundColor=color.backgroundColor; if(backgroundColor.a>0) { Ptr brush=new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b)); dc->SetBrush(brush); dc->FillRect(rect); } } else { Color fontColor=color.fontColor; dc->SetFont(documentFragment->fontObject); dc->SetTextColor(RGB(fontColor.r, fontColor.g, fontColor.b)); if(needFontFallback) { dc->DrawBuffer(rect.left, rect.top, runText+charIndex, charLength); } else { HRESULT hr=ScriptTextOut( dc->GetHandle(), &scriptCache, rect.left, rect.top, 0, &rect, &wholeGlyph.sa, NULL, 0, &wholeGlyph.glyphs[clusterStart], (int)(clusterCount), &wholeGlyph.glyphAdvances[clusterStart], NULL, &wholeGlyph.glyphOffsets[clusterStart] ); } } startFromFragmentBounds+=charLength; accumulatedWidth+=clusterWidth; } } /*********************************************************************** UniscribeEmbeddedObjectRun ***********************************************************************/ UniscribeEmbeddedObjectRun::UniscribeEmbeddedObjectRun() { } UniscribeEmbeddedObjectRun::~UniscribeEmbeddedObjectRun() { } bool UniscribeEmbeddedObjectRun::BuildUniscribeData(WinDC* dc, List& breakings) { breakings.Add(0); return true; } vint UniscribeEmbeddedObjectRun::SumWidth(vint charStart, vint charLength) { return properties.size.x; } vint UniscribeEmbeddedObjectRun::SumHeight() { return properties.size.y; } vint UniscribeEmbeddedObjectRun::SumTextHeight() { return 0; } void UniscribeEmbeddedObjectRun::SearchForLineBreak(vint tempStart, vint maxWidth, bool firstRun, vint& charLength, vint& charAdvances) { if (firstRun || properties.size.x <= maxWidth) { charLength = length - tempStart; charAdvances = properties.size.x; } else { charLength = 0; charAdvances = 0; } } void UniscribeEmbeddedObjectRun::Render(IRendererCallback* callback, vint fragmentBoundsIndex, vint offsetX, vint offsetY, bool renderBackground) { auto dc = callback->GetWinDC(); RunFragmentBounds& fragment=fragmentBounds[fragmentBoundsIndex]; if(renderBackground) { RECT rect; rect.left=(int)(fragment.bounds.Left()+offsetX)-2; rect.top=(int)(fragment.bounds.Top()+offsetY)-2; rect.right=(int)(fragment.bounds.Right()+offsetX)+2; rect.bottom=(int)(fragment.bounds.Bottom()+offsetY)+2; Color backgroundColor=documentFragment->colors.Values()[0].backgroundColor; if(backgroundColor.a>0) { Ptr brush=new WinBrush(RGB(backgroundColor.r, backgroundColor.g, backgroundColor.b)); dc->SetBrush(brush); dc->FillRect(rect); } } else { if (properties.backgroundImage) { Rect bounds=fragment.bounds; bounds.x1+=offsetX; bounds.x2+=offsetX; bounds.y1+=offsetY; bounds.y2+=offsetY; IGuiGraphicsRenderer* renderer=properties.backgroundImage->GetRenderer(); if(renderer) { renderer->Render(bounds); } } if (properties.callbackId != -1) { if (auto paragraphCallback = callback->GetParagraphCallback()) { auto offset = callback->GetParagraphOffset(); vint x = fragment.bounds.x1 + offsetX - offset.x; vint y = fragment.bounds.y1 + offsetY - offset.y; auto size = paragraphCallback->OnRenderInlineObject(properties.callbackId, Rect(Point(x, y), fragment.bounds.GetSize())); properties.size = size; } } } } /*********************************************************************** UniscribeVirtualLine ***********************************************************************/ UniscribeVirtualLine::UniscribeVirtualLine() :startFromLine(0) ,length(0) ,runText(0) ,firstRunIndex(-1) ,firstRunBoundsIndex(-1) ,lastRunIndex(-1) ,lastRunBoundsIndex(-1) { } /*********************************************************************** UniscribeLine ***********************************************************************/ UniscribeLine::UniscribeLine() :startFromParagraph(0) { } void UniscribeLine::ClearUniscribeData() { scriptItems.Clear(); scriptRuns.Clear(); virtualLines.Clear(); } bool UniscribeLine::BuildUniscribeData(WinDC* dc) { lineText=L""; ClearUniscribeData(); vint current=0; List fragmentStarts; FOREACH(Ptr, 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 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 scriptItem=new UniscribeItem; scriptItem->startFromLine=item.iCharPos; scriptItem->length=items[i+1].iCharPos-item.iCharPos; scriptItem->itemText=lineText.Buffer()+item.iCharPos; scriptItem->scriptItem=item; if(!scriptItem->BuildUniscribeData()) { goto BUILD_UNISCRIBE_DATA_FAILED; } scriptItems.Add(scriptItem); } } { // use item and document fragment information to produce runs // one item is constructed by one or more runs // characters in each run contains the same style vint fragmentIndex=0; vint fragmentStart=0; for(vint i=0;i scriptItem=scriptItems[i]; vint currentStart=scriptItem->startFromLine; while(currentStartstartFromLine+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, elementFragment, documentFragments) { vint elementLength=elementFragment->text.Length(); if(elementFragment->inlineObjectProperties) { if(elementCurrent<=currentStart && currentStart+shortLength<=elementCurrent+elementLength) { if(elementCurrent==currentStart) { auto run=MakePtr(); run->documentFragment=fragment; run->scriptItem=scriptItem.Obj(); run->startFromLine=currentStart; run->startFromFragment=currentStart-fragmentStarts[fragmentIndex]; run->length=elementLength; run->runText=lineText.Buffer()+currentStart; run->properties=elementFragment->inlineObjectProperties.Value(); scriptRuns.Add(run); } skip=true; break; } } elementCurrent+=elementLength; } } if(!skip) { Ptr run=new UniscribeTextRun; run->documentFragment=fragment; run->scriptItem=scriptItem.Obj(); run->startFromLine=currentStart; run->startFromFragment=currentStart-fragmentStarts[fragmentIndex]; run->length=shortLength; run->runText=lineText.Buffer()+currentStart; scriptRuns.Add(run); } currentStart+=shortLength; } } // for each run, generate shape information vint runIndex=0; while(runIndex run=scriptRuns[runIndex]; List breakings; if(!run->BuildUniscribeData(dc, breakings)) { goto BUILD_UNISCRIBE_DATA_FAILED; } else if(breakings.Count()>1) { if(Ptr textRun=run.Cast()) { scriptRuns.RemoveAt(runIndex); for(vint i=0;ilength-start:breakings[i+1]-start; Ptr newRun=new UniscribeTextRun; newRun->documentFragment=run->documentFragment; newRun->scriptItem=run->scriptItem; newRun->startFromLine=start+run->startFromLine; newRun->startFromFragment=start+run->startFromFragment; newRun->length=length; newRun->runText=run->runText+newRun->startFromLine-run->startFromLine; scriptRuns.Insert(runIndex+i, newRun); } continue; } } runIndex++; } } } return true; BUILD_UNISCRIBE_DATA_FAILED: ClearUniscribeData(); return false; } void UniscribeLine::Layout(vint availableWidth, Alignment alignment, vint top, vint& totalHeight) { vint cx=0; vint cy=top; virtualLines.Clear(); if(scriptRuns.Count()==0) { // if this line doesn't contains any run, skip and render a blank line vint height=(vint)(documentFragments[0]->fontStyle.size*1.5); bounds=Rect(Point(cx, cy), Size(0, height)); cy+=height; } else { FOREACH(Ptr, 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(startRunSearchForLineBreak(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(startRunSumHeight(); if(maxHeightSumTextHeight(); if(maxTextHeight levels(availableLastRun-startRun+1); Array runVisualToLogical(levels.Count()); Array 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);jfragmentBounds.Count();j++) { UniscribeRun::RunFragmentBounds& fragmentBounds=run->fragmentBounds[j]; fragmentBounds.bounds.x1+=cxOffset; fragmentBounds.bounds.x2+=cxOffset; } } } // create a virtual line { Ptr virtualLine=new UniscribeVirtualLine; virtualLine->firstRunIndex=startRun; virtualLine->firstRunBoundsIndex=startRunFragmentCount; virtualLine->lastRunIndex=availableLastRun; virtualLine->lastRunBoundsIndex=scriptRuns[availableLastRun]->fragmentBounds.Count()-1; UniscribeRun* firstRun=scriptRuns[virtualLine->firstRunIndex].Obj(); UniscribeRun* lastRun=scriptRuns[virtualLine->lastRunIndex].Obj(); UniscribeRun::RunFragmentBounds& firstBounds=firstRun->fragmentBounds[virtualLine->firstRunBoundsIndex]; UniscribeRun::RunFragmentBounds& lastBounds=lastRun->fragmentBounds[virtualLine->lastRunBoundsIndex]; virtualLine->startFromLine=firstRun->startFromLine+firstBounds.startFromRun; virtualLine->length=lastRun->startFromLine+lastBounds.startFromRun+lastBounds.length-virtualLine->startFromLine; virtualLine->runText=lineText.Buffer()+virtualLine->startFromLine; bool updateBounds=false; for(vint i=startRun;i<=availableLastRun;i++) { UniscribeRun* run=scriptRuns[i].Obj(); for(vint j=(i==startRun?startRunFragmentCount:0);jfragmentBounds.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.x2bounds.x2=fragmentBounds.bounds.x2; if(virtualLine->bounds.y1>fragmentBounds.bounds.y1) virtualLine->bounds.y1=fragmentBounds.bounds.y1; if(virtualLine->bounds.y2bounds.y2=fragmentBounds.bounds.y2; } else { virtualLine->bounds=fragmentBounds.bounds; updateBounds=true; } } } virtualLines.Add(virtualLine); } cx=0; cy+=(vint)(maxHeight + maxTextHeight*0.5); } startRun=lastRun; startRunOffset=lastRunOffset; } // calculate line bounds vint minX=0; vint minY=top; vint maxX=0; vint maxY=top; FOREACH(Ptr, run, scriptRuns) { FOREACH(UniscribeRun::RunFragmentBounds, fragmentBounds, run->fragmentBounds) { Rect bounds=fragmentBounds.bounds; if(minX>bounds.Left()) minX=bounds.Left(); if(minY>bounds.Top()) minX=bounds.Top(); if(maxX, run, scriptRuns) { for(vint i=0;ifragmentBounds.Count();i++) { run->Render(callback, i, offsetX, offsetY, renderBackground); } } } /*********************************************************************** UniscribeParagraph ***********************************************************************/ UniscribeParagraph::UniscribeParagraph() :lastAvailableWidth(-1) ,paragraphAlignment(Alignment::Left) ,built(false) { } UniscribeParagraph::~UniscribeParagraph() { ClearUniscribeData(); } /*********************************************************************** UniscribeParagraph (Initialization) ***********************************************************************/ void UniscribeParagraph::ClearUniscribeData() { FOREACH(Ptr, 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> fonts; FOREACH(Ptr, 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 line; FOREACH(Ptr, fragment, documentFragments) { if(fragment->inlineObjectProperties) { if(!line) { line=new UniscribeLine; lines.Add(line); } line->documentFragments.Add(fragment); } else { RegexMatch::List textLines; regexLine.Split(fragment->text, true, textLines); for(vint i=0;iResult(); WString text=rs.Value(); if(i>0) { line=0; } if(!line) { line=new UniscribeLine; lines.Add(line); } if(textLines.Count()==1) { line->documentFragments.Add(fragment); } else { Ptr runFragment=fragment->Copy(rs.Start(), rs.Length()); line->documentFragments.Add(runFragment); } } } } } FOREACH(Ptr, line, lines) { line->BuildUniscribeData(dc); } vint lineStart=0; FOREACH(Ptr, line, lines) { line->startFromParagraph=lineStart; lineStart+=line->lineText.Length()+2; } return true; } void UniscribeParagraph::Layout(vint availableWidth, Alignment alignment) { if(lastAvailableWidth==availableWidth && paragraphAlignment==alignment) { return; } lastAvailableWidth=availableWidth; paragraphAlignment=alignment; vint cy=0; FOREACH(Ptr, line, lines) { line->Layout(availableWidth, alignment, cy, cy); } // calculate paragraph bounds vint minX=0; vint minY=0; vint maxX=0; vint maxY=0; FOREACH(Ptr, line, lines) { Rect bounds=line->bounds; if(minX>bounds.Left()) minX=bounds.Left(); if(minY>bounds.Top()) minX=bounds.Top(); if(maxX, line, lines) { FOREACH(Ptr, fragment, line->documentFragments) { vint size=fragment->fontStyle.size/3; if(size>offsetY) { offsetY=size; } } } bounds=Rect(minX, minY, maxX, maxY+offsetY); } void UniscribeParagraph::Render(UniscribeRun::IRendererCallback* callback, bool renderBackground) { auto offset = callback->GetParagraphOffset(); FOREACH(Ptr, 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;itext.Length(); if(current<=start && start 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 fragment=documentFragments[fs]; vint length=fragment->text.Length(); if(ss==0) { if(se==length) { f1=f2=fs; } else { f1=f2=fs; Ptr leftFragment=fragment->Copy(0, se); Ptr 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 leftFragment=fragment->Copy(0, ss); Ptr 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 leftFragment=fragment->Copy(0, ss); Ptr middleFragment=fragment->Copy(ss, se-ss); Ptr 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 fragmentStart=documentFragments[fs]; Ptr fragmentEnd=documentFragments[fe]; if(ss==0) { f1=fs; } else { f1=fs+1; fe++; vint length=fragmentStart->text.Length(); Ptr leftFragment=fragmentStart->Copy(0, ss); Ptr 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 leftFragment=fragmentStart->Copy(0, se); Ptr 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 fragment=documentFragments[f]; vint start=f==fs?ss:0; vint end=f==fe?se:fragment->text.Length(); fragment->CutColors(start, end-start); fragment->UpdateOverlappedColors(start, end-start, colorField, color); fragment->DefragmentColors(); } } /*********************************************************************** UniscribeParagraph (Formatting) ***********************************************************************/ bool UniscribeParagraph::SetFont(vint start, vint length, const WString& value) { vint fs, ss, fe, se, f1, f2; SearchFragment(start, length, fs, ss, fe, se); if(CutFragment(fs, ss, fe, se, f1, f2)) { for(vint i=f1;i<=f2;i++) { documentFragments[i]->fontStyle.fontFamily=value; } built=false; return true; } else { return false; } } bool UniscribeParagraph::SetSize(vint start, vint length, vint value) { vint fs, ss, fe, se, f1, f2; SearchFragment(start, length, fs, ss, fe, se); if(CutFragment(fs, ss, fe, se, f1, f2)) { for(vint i=f1;i<=f2;i++) { documentFragments[i]->fontStyle.size=value; } built=false; return true; } else { return false; } } bool UniscribeParagraph::SetStyle(vint start, vint length, bool bold, bool italic, bool underline, bool strikeline) { vint fs, ss, fe, se, f1, f2; SearchFragment(start, length, fs, ss, fe, se); if(CutFragment(fs, ss, fe, se, f1, f2)) { for(vint i=f1;i<=f2;i++) { documentFragments[i]->fontStyle.bold=bold; documentFragments[i]->fontStyle.italic=italic; documentFragments[i]->fontStyle.underline=underline; documentFragments[i]->fontStyle.strikeline=strikeline; } built=false; return true; } else { return false; } } bool UniscribeParagraph::SetColor(vint start, vint length, Color value) { vint fs, ss, fe, se; SearchFragment(start, length, fs, ss, fe, se); if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false; CutFragmentColors(fs, ss, fe, se, &UniscribeColor::fontColor, value); return true; } bool UniscribeParagraph::SetBackgroundColor(vint start, vint length, Color value) { vint fs, ss, fe, se; SearchFragment(start, length, fs, ss, fe, se); if(fs==-1 || ss==-1 || fe==-1 || se==-1) return false; CutFragmentColors(fs, ss, fe, se, &UniscribeColor::backgroundColor, value); return true; } bool UniscribeParagraph::SetInlineObject(vint start, vint length, const IGuiGraphicsParagraph::InlineObjectProperties& properties) { vint fs, ss, fe, se, f1, f2; SearchFragment(start, length, fs, ss, fe, se); if(CutFragment(fs, ss, fe, se, f1, f2)) { WString text; for(vint i=f1;i<=f2;i++) { text+=documentFragments[f1]->text; } Ptr elementFragment=new UniscribeFragment(text); for(vint i=f1;i<=f2;i++) { elementFragment->cachedTextFragment.Add(documentFragments[f1]); documentFragments.RemoveAt(f1); } elementFragment->inlineObjectProperties=properties; documentFragments.Insert(f1, elementFragment); built=false; return true; } else { return false; } } InlineObject UniscribeParagraph::ResetInlineObject(vint start, vint length) { vint fs, ss, fe, se; SearchFragment(start, length, fs, ss, fe, se); Ptr fragment = documentFragments[fs]; if(fs==fe && ss==0 && se==fragment->text.Length() && fragment->inlineObjectProperties) { documentFragments.RemoveAt(fs); for(vint i=0;icachedTextFragment.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 line=lines[middle]; vint lineStart=line->startFromParagraph; vint lineEnd=line->startFromParagraph+line->lineText.Length(); if(textPoslineEnd) { 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 line=lines[lineIndex]; vint start=0; vint end=line->virtualLines.Count()-1; while(start<=end) { vint middle=(start+end)/2; Ptr vline=line->virtualLines[middle]; vint lineStart=line->startFromParagraph+vline->startFromLine; vint lineEnd=line->startFromParagraph+vline->startFromLine+vline->length; if(textPoslineEnd) { 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 line=lines[lineIndex]; vint start=0; vint end=line->scriptItems.Count()-1; while(start<=end) { vint middle=(start+end)/2; Ptr item=line->scriptItems[middle]; vint lineStart=line->startFromParagraph+item->startFromLine; vint lineEnd=line->startFromParagraph+item->startFromLine+item->length; if(textPoslineEnd) { 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 line=lines[lineIndex]; if(line->startFromParagraph<=caret && caret<=line->startFromParagraph+line->lineText.Length()) { if(line->lineText==L"") return line->bounds; Ptr virtualLine=line->virtualLines[virtualLineIndex]; for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++) { Ptr run=line->scriptRuns[i]; if(Ptr textRun=run.Cast()) { 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(boundsStart0) { 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;gwholeGlyph.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 line=lines[lineIndex]; if(line->lineText==L"") return line->startFromParagraph; Ptr run=line->scriptRuns[runIndex].Cast(); UniscribeRun::RunFragmentBounds& bounds=run->fragmentBounds[runBoundsIndex]; vint startFromFragmentBounds=0; vint accumulatedWidth=0; while(startFromFragmentBoundsSearchSingleChar(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;iwholeGlyph.glyphAdvances[i+clusterStart]; } if(run->scriptItem->scriptItem.a.fRTL) { vint x2=bounds.bounds.x2-accumulatedWidth; vint x1=x2-clusterWidth; if(x1<=x && xstartFromParagraph+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 && xstartFromParagraph+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 line=lines[lineIndex]; if(line->virtualLines.Count()==0) return line->startFromParagraph; Ptr virtualLine=line->virtualLines[virtualLineIndex]; if(xbounds.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 run=line->scriptRuns[i]; if(Ptr textRun=run.Cast()) { 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 && xfragmentBounds[0].bounds; if(bounds.x1<=x && xstartFromParagraph+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 line=lines[lineIndex]; if(line->virtualLines.Count()==0) return InlineObject(); Ptr virtualLine=line->virtualLines[virtualLineIndex]; if(xbounds.x1) return InlineObject(); if(x>=virtualLine->bounds.x2) return InlineObject(); for(vint i=virtualLine->firstRunIndex;i<=virtualLine->lastRunIndex;i++) { Ptr run=line->scriptRuns[i]; if(auto elementRun=run.Cast()) { Rect bounds=run->fragmentBounds[0].bounds; if(bounds.x1<=x && xstartFromParagraph+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 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(ybounds.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=maxY) { start=middle+1; } else { return middle; } } return -1; } vint UniscribeParagraph::GetVirtualLineIndexFromY(vint y, vint lineIndex) { Ptr line=lines[lineIndex]; if(ybounds.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=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 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 virtualLine=virtualLineIndex==-1?0:line->virtualLines[virtualLineIndex]; switch(position) { case IGuiGraphicsParagraph::CaretLineFirst: return line->startFromParagraph+virtualLine->startFromLine; case IGuiGraphicsParagraph::CaretLineLast: return line->startFromParagraph+virtualLine->startFromLine+virtualLine->length; case IGuiGraphicsParagraph::CaretMoveUp: { if(frontLine==0 && virtualLineIndex<=0) return comparingCaret; Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide); if(bounds.Height()!=0) { if(virtualLineIndex<=0) { frontLine--; virtualLineIndex=lines[frontLine]->virtualLines.Count()-1; } else { virtualLineIndex--; } preferFrontSide=true; return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex); } } break; case IGuiGraphicsParagraph::CaretMoveDown: { if(frontLine==lines.Count()-1 && virtualLineIndex==line->virtualLines.Count()-1) return comparingCaret; Rect bounds=GetCaretBoundsWithLine(comparingCaret, frontLine, virtualLineIndex, preferFrontSide); if(bounds.Height()!=0) { if(virtualLineIndex==line->virtualLines.Count()-1) { frontLine++; virtualLineIndex=0; } else { virtualLineIndex++; } preferFrontSide=false; return GetCaretFromXWithLine(bounds.x1, frontLine, virtualLineIndex); } } break; } return -1; } Rect UniscribeParagraph::GetCaretBounds(vint caret, bool frontSide) { if(!IsValidCaret(caret)) return Rect(); vint frontLine=0; vint backLine=0; GetLineIndexFromTextPos(caret, frontLine, backLine); vint frontVirtualLine=0; vint backVirtualLine=0; GetVirtualLineIndexFromTextPos(caret, frontLine, frontVirtualLine, backVirtualLine); vint virtualLineIndex=frontSide?frontVirtualLine:backVirtualLine; return GetCaretBoundsWithLine(caret, frontLine, virtualLineIndex, frontSide); } vint UniscribeParagraph::GetCaretFromPoint(Point point) { vint lineIndex=GetLineIndexFromY(point.y); if(lineIndex==-1) return -1; Ptr 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 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 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 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;ilength;i++) { if(item->charLogattrs[i].fCharStop) return i+line->startFromParagraph+item->startFromLine; } return line->startFromParagraph+item->startFromLine+item->length; } } bool UniscribeParagraph::IsValidCaret(vint caret) { if(!IsValidTextPos(caret)) return false; return GetNearestCaretFromTextPos(caret, true)==caret; } bool UniscribeParagraph::IsValidTextPos(vint textPos) { return 0<=textPos && textPos<=paragraphText.Length(); } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSGDI\GUIGRAPHICSWINDOWSGDI.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements { /*********************************************************************** GuiGDIElement ***********************************************************************/ GuiGDIElement::GuiGDIElement() { } GuiGDIElement::~GuiGDIElement() { } } namespace elements_windows_gdi { using namespace windows; using namespace elements; using namespace collections; /*********************************************************************** WindowsGDIRenderTarget ***********************************************************************/ class WindowsGDIRenderTarget : public Object, public IWindowsGDIRenderTarget { protected: INativeWindow* window; WinDC* dc; List clippers; vint clipperCoverWholeTargetCounter; void ApplyClipper() { if(clipperCoverWholeTargetCounter==0) { if(clippers.Count()==0) { dc->RemoveClip(); } else { Rect clipper=GetClipper(); dc->ClipRegion(new WinRegion(clipper.Left(), clipper.Top(), clipper.Right(), clipper.Bottom(), true)); } } } public: WindowsGDIRenderTarget(INativeWindow* _window) :window(_window) ,dc(0) ,clipperCoverWholeTargetCounter(0) { } WinDC* GetDC()override { return dc?dc:GetWindowsGDIObjectProvider()->GetNativeWindowDC(window); } void StartRendering()override { dc=GetWindowsGDIObjectProvider()->GetNativeWindowDC(window); } bool StopRendering()override { dc = 0; return true; } void PushClipper(Rect clipper)override { if(clipperCoverWholeTargetCounter>0) { clipperCoverWholeTargetCounter++; } else { Rect previousClipper=GetClipper(); Rect currentClipper; currentClipper.x1=(previousClipper.x1>clipper.x1?previousClipper.x1:clipper.x1); currentClipper.y1=(previousClipper.y1>clipper.y1?previousClipper.y1:clipper.y1); currentClipper.x2=(previousClipper.x20) { if(clipperCoverWholeTargetCounter>0) { clipperCoverWholeTargetCounter--; } else { clippers.RemoveAt(clippers.Count()-1); } ApplyClipper(); } } Rect GetClipper()override { if(clippers.Count()==0) { return Rect(Point(0, 0), window->GetClientSize()); } else { return clippers[clippers.Count()-1]; } } bool IsClipperCoverWholeTarget()override { return clipperCoverWholeTargetCounter>0; } }; /*********************************************************************** CachedResourceAllocator ***********************************************************************/ class CachedPenAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, Ptr) public: Ptr CreateInternal(Color color) { return new WinPen(PS_SOLID, 1, RGB(color.r, color.g, color.b)); } }; class CachedBrushAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, Ptr) public: Ptr CreateInternal(Color color) { return color.a==0?new WinBrush:new WinBrush(RGB(color.r, color.g, color.b)); } }; class CachedFontAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr) public: static Ptr CreateGdiFont(const FontProperties& value) { vint size=value.size<0?value.size:-value.size; return new WinFont(value.fontFamily, size, 0, 0, 0, (value.bold?FW_BOLD:FW_NORMAL), value.italic, value.underline, value.strikeline, value.antialias); } Ptr CreateInternal(const FontProperties& value) { return CreateGdiFont(value); } }; class CachedCharMeasurerAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr) protected: class GdiCharMeasurer : public text::CharMeasurer { protected: Ptr font; vint size; Size MeasureInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget) { if(renderTarget) { WindowsGDIRenderTarget* gdiRenderTarget=dynamic_cast(renderTarget); WinDC* dc=gdiRenderTarget->GetDC(); dc->SetFont(font); SIZE size=dc->MeasureBuffer(&character, 1, -1); return Size(size.cx, size.cy); } else { return Size(0, 0); } } vint MeasureWidthInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget) { return MeasureInternal(character, renderTarget).x; } vint GetRowHeightInternal(IGuiGraphicsRenderTarget* renderTarget) { if(renderTarget) { return MeasureInternal(L' ', renderTarget).y; } else { return size; } } public: GdiCharMeasurer(Ptr _font, vint _size) :text::CharMeasurer(_size) ,size(_size) ,font(_font) { } }; public: Ptr CreateInternal(const FontProperties& value) { return new GdiCharMeasurer(CachedFontAllocator::CreateGdiFont(value), value.size); } }; /*********************************************************************** WindowsGDIResourceManager ***********************************************************************/ class WindowsGDIImageFrameCache : public Object, public INativeImageFrameCache { protected: IWindowsGDIResourceManager* resourceManager; INativeImageFrame* cachedFrame; Ptr bitmap; Ptr disabledBitmap; public: WindowsGDIImageFrameCache(IWindowsGDIResourceManager* _resourceManager) :resourceManager(_resourceManager) { } ~WindowsGDIImageFrameCache() { } void OnAttach(INativeImageFrame* frame)override { cachedFrame=frame; Size size=frame->GetSize(); bitmap=new WinBitmap(size.x, size.y, WinBitmap::vbb32Bits, true); IWICBitmap* wicBitmap=GetWindowsGDIObjectProvider()->GetWICBitmap(frame); WICRect rect; rect.X=0; rect.Y=0; rect.Width=(int)size.x; rect.Height=(int)size.y; wicBitmap->CopyPixels(&rect, (int)bitmap->GetLineBytes(), (int)(bitmap->GetLineBytes()*size.y), (BYTE*)bitmap->GetScanLines()[0]); bitmap->BuildAlphaChannel(false); } void OnDetach(INativeImageFrame* frame)override { resourceManager->DestroyBitmapCache(cachedFrame); } INativeImageFrame* GetFrame() { return cachedFrame; } Ptr GetBitmap(bool enabled) { if(enabled) { return bitmap; } else { if(!disabledBitmap) { vint w=bitmap->GetWidth(); vint h=bitmap->GetHeight(); disabledBitmap=new WinBitmap(w, h, WinBitmap::vbb32Bits, true); for(vint y=0;yGetScanLines()[y]; BYTE* write=disabledBitmap->GetScanLines()[y]; for(vint x=0;xBuildAlphaChannel(false); } return disabledBitmap; } } }; class WindowsGDIResourceManager : public GuiGraphicsResourceManager, public IWindowsGDIResourceManager, public INativeControllerListener { typedef SortedList> ImageCacheList; protected: SortedList> renderTargets; Ptr layoutProvider; CachedPenAllocator pens; CachedBrushAllocator brushes; CachedFontAllocator fonts; CachedCharMeasurerAllocator charMeasurers; ImageCacheList imageCaches; public: WindowsGDIResourceManager() { layoutProvider=new WindowsGDILayoutProvider; } IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override { return GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window); } void RecreateRenderTarget(INativeWindow* window)override { } IGuiGraphicsLayoutProvider* GetLayoutProvider()override { return layoutProvider.Obj(); } void NativeWindowCreated(INativeWindow* window)override { WindowsGDIRenderTarget* renderTarget=new WindowsGDIRenderTarget(window); renderTargets.Add(renderTarget); GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, renderTarget); } void NativeWindowDestroying(INativeWindow* window)override { WindowsGDIRenderTarget* renderTarget=dynamic_cast(GetWindowsGDIObjectProvider()->GetBindedRenderTarget(window)); GetWindowsGDIObjectProvider()->SetBindedRenderTarget(window, 0); renderTargets.Remove(renderTarget); } Ptr CreateGdiPen(Color color)override { return pens.Create(color); } void DestroyGdiPen(Color color)override { pens.Destroy(color); } Ptr CreateGdiBrush(Color color)override { return brushes.Create(color); } void DestroyGdiBrush(Color color)override { brushes.Destroy(color); } Ptr CreateGdiFont(const FontProperties& fontProperties)override { return fonts.Create(fontProperties); } void DestroyGdiFont(const FontProperties& fontProperties)override { fonts.Destroy(fontProperties); } Ptr CreateCharMeasurer(const FontProperties& fontProperties)override { return charMeasurers.Create(fontProperties); } void DestroyCharMeasurer(const FontProperties& fontProperties)override { charMeasurers.Destroy(fontProperties); } Ptr GetBitmap(INativeImageFrame* frame, bool enabled)override { Ptr cache=frame->GetCache(this); if(cache) { return cache.Cast()->GetBitmap(enabled); } else { WindowsGDIImageFrameCache* gdiCache=new WindowsGDIImageFrameCache(this); if(frame->SetCache(this, gdiCache)) { return gdiCache->GetBitmap(enabled); } else { return 0; } } } void DestroyBitmapCache(INativeImageFrame* frame)override { WindowsGDIImageFrameCache* cache=frame->GetCache(this).Cast().Obj(); imageCaches.Remove(cache); } }; } namespace elements_windows_gdi { IWindowsGDIResourceManager* windowsGDIResourceManager=0; IWindowsGDIResourceManager* GetWindowsGDIResourceManager() { return windowsGDIResourceManager; } void SetWindowsGDIResourceManager(IWindowsGDIResourceManager* resourceManager) { windowsGDIResourceManager=resourceManager; } /*********************************************************************** OS Supporting ***********************************************************************/ IWindowsGDIObjectProvider* windowsGDIObjectProvider=0; IWindowsGDIObjectProvider* GetWindowsGDIObjectProvider() { return windowsGDIObjectProvider; } void SetWindowsGDIObjectProvider(IWindowsGDIObjectProvider* provider) { windowsGDIObjectProvider=provider; } } } } /*********************************************************************** NativeMain ***********************************************************************/ using namespace vl::presentation; using namespace vl::presentation::elements; void RendererMainGDI() { elements_windows_gdi::WindowsGDIResourceManager resourceManager; SetGuiGraphicsResourceManager(&resourceManager); elements_windows_gdi::SetWindowsGDIResourceManager(&resourceManager); GetCurrentController()->CallbackService()->InstallListener(&resourceManager); elements_windows_gdi::GuiSolidBorderElementRenderer::Register(); elements_windows_gdi::GuiRoundBorderElementRenderer::Register(); elements_windows_gdi::Gui3DBorderElementRenderer::Register(); elements_windows_gdi::Gui3DSplitterElementRenderer::Register(); elements_windows_gdi::GuiSolidBackgroundElementRenderer::Register(); elements_windows_gdi::GuiGradientBackgroundElementRenderer::Register(); elements_windows_gdi::GuiSolidLabelElementRenderer::Register(); elements_windows_gdi::GuiImageFrameElementRenderer::Register(); elements_windows_gdi::GuiPolygonElementRenderer::Register(); elements_windows_gdi::GuiColorizedTextElementRenderer::Register(); elements_windows_gdi::GuiGDIElementRenderer::Register(); elements::GuiDocumentElement::GuiDocumentElementRenderer::Register(); GuiApplicationMain(); elements_windows_gdi::SetWindowsGDIResourceManager(0); SetGuiGraphicsResourceManager(0); } /*********************************************************************** GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSLAYOUTPROVIDERWINDOWSDIRECT2D.CPP ***********************************************************************/ namespace vl { namespace presentation { using namespace elements; using namespace collections; namespace elements_windows_d2d { /*********************************************************************** WindowsDirect2DElementInlineObject ***********************************************************************/ class WindowsDirect2DElementInlineObject : public IDWriteInlineObject { public: class IRendererCallback : public Interface { public: virtual Color GetBackgroundColor(vint textPosition) = 0; virtual IWindowsDirect2DRenderTarget* GetDirect2DRenderTarget() = 0; virtual Point GetParagraphOffset() = 0; virtual IGuiGraphicsParagraphCallback* GetParagraphCallback() = 0; }; protected: vint counter; IGuiGraphicsParagraph::InlineObjectProperties properties; IRendererCallback* rendererCallback; vint start; vint length; public: WindowsDirect2DElementInlineObject( const IGuiGraphicsParagraph::InlineObjectProperties& _properties, IRendererCallback* _rendererCallback, vint _start, vint _length ) :counter(1) ,properties(_properties) ,rendererCallback(_rendererCallback) ,start(_start) ,length(_length) { } ~WindowsDirect2DElementInlineObject() { if (properties.backgroundImage) { IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer(); if(graphicsRenderer) { graphicsRenderer->SetRenderTarget(0); } } } vint GetStart() { return start; } vint GetLength() { return length; } const IGuiGraphicsParagraph::InlineObjectProperties& GetProperties() { return properties; } Ptr GetElement() { return properties.backgroundImage; } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject ) { if(ppvObject) { *ppvObject=NULL; } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE AddRef(void) { ++counter; return S_OK; } ULONG STDMETHODCALLTYPE Release(void) { if(--counter==0) { delete this; } return S_OK; } STDMETHOD(Draw)( void* clientDrawingContext, IDWriteTextRenderer* renderer, FLOAT originX, FLOAT originY, BOOL isSideways, BOOL isRightToLeft, IUnknown* clientDrawingEffect )override { Rect bounds(Point((vint)originX, (vint)originY), properties.size); if (properties.backgroundImage) { IGuiGraphicsRenderer* graphicsRenderer=properties.backgroundImage->GetRenderer(); if(graphicsRenderer) { graphicsRenderer->Render(bounds); } } Color color=rendererCallback->GetBackgroundColor(start); if(color.a!=0) { color.a/=2; if(IWindowsDirect2DRenderTarget* renderTarget=rendererCallback->GetDirect2DRenderTarget()) { ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(color); renderTarget->GetDirect2DRenderTarget()->FillRectangle( D2D1::RectF(bounds.x1-0.5f, bounds.y1-0.5f, bounds.x2+0.5f, bounds.y2+0.5f), brush ); renderTarget->DestroyDirect2DBrush(color); } } if (properties.callbackId != -1) { if (auto callback = rendererCallback->GetParagraphCallback()) { auto offset = rendererCallback->GetParagraphOffset(); auto size = callback->OnRenderInlineObject(properties.callbackId, Rect(Point(bounds.x1 - offset.x, bounds.y1 - offset.y), bounds.GetSize())); properties.size = size; } } return S_OK; } STDMETHOD(GetMetrics)( DWRITE_INLINE_OBJECT_METRICS* metrics )override { metrics->width=(FLOAT)properties.size.x; metrics->height=(FLOAT)properties.size.y; metrics->baseline=(FLOAT)(properties.baseline==-1?properties.size.y:properties.baseline); metrics->supportsSideways=TRUE; return S_OK; } STDMETHOD(GetOverhangMetrics)( DWRITE_OVERHANG_METRICS* overhangs )override { overhangs->left=0; overhangs->right=0; overhangs->top=0; overhangs->bottom=0; return S_OK; } STDMETHOD(GetBreakConditions)( DWRITE_BREAK_CONDITION* breakConditionBefore, DWRITE_BREAK_CONDITION* breakConditionAfter )override { switch(properties.breakCondition) { case IGuiGraphicsParagraph::StickToPreviousRun: *breakConditionBefore=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; *breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK; break; case IGuiGraphicsParagraph::StickToNextRun: *breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK; *breakConditionAfter=DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; break; default: *breakConditionBefore=DWRITE_BREAK_CONDITION_CAN_BREAK; *breakConditionAfter=DWRITE_BREAK_CONDITION_CAN_BREAK; } return S_OK; } }; /*********************************************************************** WindowsDirect2DParagraph ***********************************************************************/ class WindowsDirect2DParagraph : public Object, public IGuiGraphicsParagraph, public WindowsDirect2DElementInlineObject::IRendererCallback { protected: struct TextRange { vint start; vint end; TextRange(){} TextRange(vint _start, vint _end):start(_start),end(_end){} bool operator==(const TextRange& range) const { return start==range.start; } bool operator!=(const TextRange& range) const { return start!=range.start; } bool operator<(const TextRange& range) const { return start(const TextRange& range) const { return start>range.start; } bool operator>=(const TextRange& range) const { return start>=range.start; } }; typedef Dictionary> InlineElementMap; typedef Dictionary ColorMap; typedef Dictionary GraphicsElementMap; protected: IGuiGraphicsLayoutProvider* provider; ID2D1SolidColorBrush* defaultTextColor; IDWriteFactory* dwriteFactory; IWindowsDirect2DRenderTarget* renderTarget; WString paragraphText; ComPtr textLayout; bool wrapLine; vint maxWidth; List usedColors; InlineElementMap inlineElements; GraphicsElementMap graphicsElements; ColorMap backgroundColors; vint caret; Color caretColor; bool caretFrontSide; ID2D1SolidColorBrush* caretBrush; bool formatDataAvailable; Array lineMetrics; Array lineStarts; Array lineTops; Array clusterMetrics; Array hitTestMetrics; Array charHitTestMap; Point paragraphOffset; IGuiGraphicsParagraphCallback* paragraphCallback; /*********************************************************************** WindowsDirect2DParagraph (Ranges) ***********************************************************************/ template void CutMap(Dictionary& map, vint start, vint length) { vint end=start+length; for(vint i=map.Count()-1;i>=0;i--) { TextRange key=map.Keys()[i]; if(key.startstart?key.start:start; vint s3=key.end void UpdateOverlappedMap(Dictionary& map, vint start, vint length, const T& value) { vint end=start+length; for(vint i=map.Count()-1;i>=0;i--) { TextRange key=map.Keys()[i]; if(key.start void DefragmentMap(Dictionary& map) { vint lastIndex=map.Count()-1; T lastValue=map.Values()[lastIndex]; for(vint i=map.Count()-2;i>=-1;i--) { if(i==-1 || map.Values()[i]!=lastValue) { if(lastIndex-i>0) { vint start=map.Keys()[i+1].start; vint end=map.Keys()[lastIndex].end; TextRange key(start, end); for(vint j=lastIndex;j>i;j--) { map.Remove(map.Keys()[j]); } map.Add(key, lastValue); } lastIndex=i; if(i!=-1) { lastValue=map.Values()[i]; } } } } template void SetMap(Dictionary& map, vint start, vint length, const T& value) { CutMap(map, start, length); UpdateOverlappedMap(map, start, length, value); DefragmentMap(map); } template bool GetMap(Dictionary& 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.end) { start=middle+1; } else { value=map.Values()[middle]; return true; } } return false; } /*********************************************************************** WindowsDirect2DParagraph (Layout Retriving) ***********************************************************************/ void PrepareFormatData() { if(!formatDataAvailable) { formatDataAvailable=true; { UINT32 lineCount=0; textLayout->GetLineMetrics(NULL, 0, &lineCount); lineMetrics.Resize(lineCount); if(lineCount>0) { textLayout->GetLineMetrics(&lineMetrics[0], lineCount, &lineCount); } lineStarts.Resize(lineCount); lineTops.Resize(lineCount); vint start=0; FLOAT top=0; for(vint i=0;iGetClusterMetrics(NULL, 0, &clusterCount); clusterMetrics.Resize(clusterCount); if(clusterCount>0) { textLayout->GetClusterMetrics(&clusterMetrics[0], clusterCount, &clusterCount); } } { vint textPos=0; hitTestMetrics.Resize(clusterMetrics.Count()); for(vint i=0;iHitTestTextPosition((UINT32)textPos, FALSE, &x, &y, &metrics); textPos+=metrics.length; } } { charHitTestMap.Resize(paragraphText.Length()); for(vint i=0;iGetDirectWriteFactory()) ,renderTarget(dynamic_cast(_renderTarget)) ,paragraphText(_text) ,textLayout(0) ,wrapLine(true) ,maxWidth(-1) ,caret(-1) ,caretFrontSide(false) ,caretBrush(0) ,formatDataAvailable(false) ,paragraphCallback(_paragraphCallback) { FontProperties defaultFont=GetCurrentController()->ResourceService()->GetDefaultFont(); Direct2DTextFormatPackage* package=GetWindowsDirect2DResourceManager()->CreateDirect2DTextFormat(defaultFont); defaultTextColor=renderTarget->CreateDirect2DBrush(Color(0, 0, 0)); usedColors.Add(Color(0, 0, 0)); IDWriteTextLayout* rawTextLayout=0; HRESULT hr=dwriteFactory->CreateTextLayout( _text.Buffer(), (int)_text.Length(), package->textFormat.Obj(), 0, 0, &rawTextLayout); if(!FAILED(hr)) { textLayout=rawTextLayout; textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP); } graphicsElements.Add(TextRange(0, _text.Length()), 0); backgroundColors.Add(TextRange(0, _text.Length()), Color(0, 0, 0, 0)); GetWindowsDirect2DResourceManager()->DestroyDirect2DTextFormat(defaultFont); } ~WindowsDirect2DParagraph() { CloseCaret(); FOREACH(Color, color, usedColors) { renderTarget->DestroyDirect2DBrush(color); } } IGuiGraphicsLayoutProvider* GetProvider()override { return provider; } IGuiGraphicsRenderTarget* GetRenderTarget()override { return renderTarget; } Point GetParagraphOffset()override { return paragraphOffset; } IGuiGraphicsParagraphCallback* GetParagraphCallback()override { return paragraphCallback; } /*********************************************************************** WindowsDirect2DParagraph (Formatting) ***********************************************************************/ bool GetWrapLine()override { return wrapLine; } void SetWrapLine(bool value)override { if(wrapLine!=value) { wrapLine=value; textLayout->SetWordWrapping(value?DWRITE_WORD_WRAPPING_WRAP:DWRITE_WORD_WRAPPING_NO_WRAP); formatDataAvailable=false; } } vint GetMaxWidth()override { return maxWidth; } void SetMaxWidth(vint value)override { if(maxWidth!=value) { maxWidth=value; textLayout->SetMaxWidth(value==-1?65536:(FLOAT)value); formatDataAvailable=false; } } Alignment GetParagraphAlignment()override { switch(textLayout->GetTextAlignment()) { case DWRITE_TEXT_ALIGNMENT_LEADING: return Alignment::Left; case DWRITE_TEXT_ALIGNMENT_CENTER: return Alignment::Center; case DWRITE_TEXT_ALIGNMENT_TRAILING: return Alignment::Right; default: return Alignment::Left; } } void SetParagraphAlignment(Alignment value)override { formatDataAvailable=false; switch(value) { case Alignment::Left: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); break; case Alignment::Center: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); break; case Alignment::Right: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING); break; } } bool SetFont(vint start, vint length, const WString& value)override { if(length==0) return true; formatDataAvailable=false; DWRITE_TEXT_RANGE range; range.startPosition=(int)start; range.length=(int)length; HRESULT hr=textLayout->SetFontFamilyName(value.Buffer(), range); return !FAILED(hr); } bool SetSize(vint start, vint length, vint value)override { if(length==0) return true; formatDataAvailable=false; DWRITE_TEXT_RANGE range; range.startPosition=(int)start; range.length=(int)length; HRESULT hr=textLayout->SetFontSize((FLOAT)value, range); return !FAILED(hr); } bool SetStyle(vint start, vint length, TextStyle value)override { if(length==0) return true; formatDataAvailable=false; DWRITE_TEXT_RANGE range; range.startPosition=(int)start; range.length=(int)length; HRESULT hr=S_OK; hr=textLayout->SetFontStyle(value&Italic?DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL, range); if(FAILED(hr)) return false; hr=textLayout->SetFontWeight(value&Bold?DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL, range); if(FAILED(hr)) return false; hr=textLayout->SetUnderline(value&Underline?TRUE:FALSE, range); if(FAILED(hr)) return false; hr=textLayout->SetStrikethrough(value&Strikeline?TRUE:FALSE, range); if(FAILED(hr)) return false; return true; } bool SetColor(vint start, vint length, Color value)override { if(length==0) return true; formatDataAvailable=false; ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(value); usedColors.Add(value); DWRITE_TEXT_RANGE range; range.startPosition=(int)start; range.length=(int)length; HRESULT hr=textLayout->SetDrawingEffect(brush, range); return !FAILED(hr); } bool SetBackgroundColor(vint start, vint length, Color value)override { SetMap(backgroundColors, start, length, value); return true; } bool SetInlineObject(vint start, vint length, const InlineObjectProperties& properties)override { if(inlineElements.Keys().Contains(properties.backgroundImage.Obj())) { return false; } for(vint i=0;i inlineObject=inlineElements.Values().Get(i); if(startGetStart()+inlineObject->GetLength() && inlineObject->GetStart() 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 inlineObject=inlineElements[element]; DWRITE_TEXT_RANGE range; range.startPosition=(int)inlineObject->GetStart(); range.length=(int)inlineObject->GetLength(); HRESULT hr=textLayout->SetInlineObject(NULL, range); if(!FAILED(hr)) { formatDataAvailable=false; inlineElements.Remove(element); SetMap(graphicsElements, inlineObject->GetStart(), inlineObject->GetLength(), (IGuiGraphicsElement*)0); return true; } else { return false; } } return false; } vint GetHeight()override { DWRITE_TEXT_METRICS metrics; textLayout->GetMetrics(&metrics); return (vint)metrics.height; } /*********************************************************************** WindowsDirect2DParagraph (IRenderingCallback) ***********************************************************************/ Color GetBackgroundColor(vint textPosition)override { Color color; if(GetMap(backgroundColors, textPosition, color)) { return color; } else { return Color(0, 0, 0, 0); } } IWindowsDirect2DRenderTarget* GetDirect2DRenderTarget()override { return renderTarget; } /*********************************************************************** WindowsDirect2DParagraph (Rendering) ***********************************************************************/ bool OpenCaret(vint _caret, Color _color, bool _frontSide)override { if(!IsValidCaret(_caret)) return false; if(caret!=-1) CloseCaret(); caret=_caret; caretColor=_color; caretFrontSide=_frontSide; caretBrush=renderTarget->CreateDirect2DBrush(caretColor); return true; } bool CloseCaret()override { if(caret==-1) return false; caret=-1; renderTarget->DestroyDirect2DBrush(caretColor); caretBrush=0; return true; } void Render(Rect bounds)override { paragraphOffset = bounds.LeftTop(); PrepareFormatData(); for(vint i=0;i0) { ID2D1SolidColorBrush* brush=renderTarget->CreateDirect2DBrush(color); vint start=key.start; if(start<0) { start=0; } while(startGetDirect2DRenderTarget()->FillRectangle( D2D1::RectF(x1, y1, x2, y2), brush ); start=hitTest.textPosition+hitTest.length; } renderTarget->DestroyDirect2DBrush(color); } } renderTarget->GetDirect2DRenderTarget()->DrawTextLayout( D2D1::Point2F((FLOAT)bounds.Left(), (FLOAT)bounds.Top()), textLayout.Obj(), defaultTextColor, D2D1_DRAW_TEXT_OPTIONS_NO_SNAP); if(caret!=-1) { Rect caretBounds=GetCaretBounds(caret, caretFrontSide); vint x=caretBounds.x1+bounds.x1; vint y1=caretBounds.y1+bounds.y1; vint y2=y1+caretBounds.Height(); renderTarget->GetDirect2DRenderTarget()->DrawLine( D2D1::Point2F((FLOAT)x-0.5f, (FLOAT)y1+0.5f), D2D1::Point2F((FLOAT)x-0.5f, (FLOAT)y2+0.5f), caretBrush ); renderTarget->GetDirect2DRenderTarget()->DrawLine( D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)y1+0.5f), D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)y2+0.5f), caretBrush ); } } /*********************************************************************** WindowsDirect2DParagraph (Caret Helper) ***********************************************************************/ void GetLineIndexFromTextPos(vint textPos, vint& frontLineIndex, vint& backLineIndex) { frontLineIndex=-1; backLineIndex=-1; vint start=0; vint end=lineMetrics.Count()-1; while(start<=end) { vint middle=(start+end)/2; DWRITE_LINE_METRICS& metrics=lineMetrics[middle]; vint lineStart=lineStarts[middle]; vint lineEnd=lineStart+metrics.length-metrics.newlineLength; if(textPoslineEnd) { 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 GetLineYRange(vint lineIndex) { DWRITE_LINE_METRICS& line=lineMetrics[lineIndex]; FLOAT top=lineTops[lineIndex]; return Pair(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=maxY) { return lineMetrics.Count()-1; } vint start=0; vint end=lineMetrics.Count()-1; while(start<=end) { vint middle=(start+end)/2; Pair yRange=GetLineYRange(middle); minY=yRange.key; maxY=yRange.value; 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;iminX) minLineX=minX; if(maxLineX=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 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 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 inlineObject=inlineElements[element]; start=inlineObject->GetStart(); length=inlineObject->GetLength(); return inlineObject->GetProperties(); } } return Nullable(); } 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 WindowsDirect2DLayoutProvider::CreateParagraph(const WString& text, IGuiGraphicsRenderTarget* renderTarget, elements::IGuiGraphicsParagraphCallback* callback) { return new WindowsDirect2DParagraph(this, text, renderTarget, callback); } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSRENDERERSWINDOWSDIRECT2D.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements_windows_d2d { using namespace collections; /*********************************************************************** IMPLEMENT_BRUSH_ELEMENT_RENDERER ***********************************************************************/ #define IMPLEMENT_BRUSH_ELEMENT_RENDERER(TRENDERER)\ void TRENDERER::InitializeInternal()\ {\ }\ void TRENDERER::FinalizeInternal()\ {\ DestroyBrush(renderTarget);\ }\ void TRENDERER::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget)\ {\ DestroyBrush(oldRenderTarget);\ CreateBrush(newRenderTarget);\ }\ TRENDERER::TRENDERER()\ :brush(0)\ {\ }\ void TRENDERER::Render(Rect bounds)\ #define IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(TRENDERER)\ void TRENDERER::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)\ {\ if(_renderTarget)\ {\ oldColor=element->GetColor();\ brush=_renderTarget->CreateDirect2DBrush(oldColor);\ }\ }\ void TRENDERER::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)\ {\ if(_renderTarget && brush)\ {\ _renderTarget->DestroyDirect2DBrush(oldColor);\ brush=0;\ }\ }\ void TRENDERER::OnElementStateChanged()\ {\ if(renderTarget)\ {\ Color color=element->GetColor();\ if(oldColor!=color)\ {\ DestroyBrush(renderTarget);\ CreateBrush(renderTarget);\ }\ }\ }\ #define IMPLEMENT_BRUSH_ELEMENT_RENDERER_LINEAR_GRADIENT_BRUSH(TRENDERER)\ void TRENDERER::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget)\ {\ if(_renderTarget)\ {\ oldColor=Pair(element->GetColor1(), element->GetColor2());\ brush=_renderTarget->CreateDirect2DLinearBrush(oldColor.key, oldColor.value);\ }\ }\ void TRENDERER::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget)\ {\ if(_renderTarget && brush)\ {\ _renderTarget->DestroyDirect2DLinearBrush(oldColor.key, oldColor.value);\ brush=0;\ }\ }\ void TRENDERER::OnElementStateChanged()\ {\ if(renderTarget)\ {\ Pair color=Pair(element->GetColor1(), element->GetColor2());\ if(oldColor!=color)\ {\ DestroyBrush(renderTarget);\ CreateBrush(renderTarget);\ }\ }\ }\ /*********************************************************************** GuiSolidBorderElementRenderer ***********************************************************************/ IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiSolidBorderElementRenderer) IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiSolidBorderElementRenderer) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); switch(element->GetShape()) { case ElementShape::Rectangle: d2dRenderTarget->DrawRectangle( D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f), brush ); break; case ElementShape::Ellipse: d2dRenderTarget->DrawEllipse( D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f), brush ); break; } } /*********************************************************************** GuiRoundBorderElementRenderer ***********************************************************************/ IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiRoundBorderElementRenderer) IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiRoundBorderElementRenderer) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); d2dRenderTarget->DrawRoundedRectangle( D2D1::RoundedRect( D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f), (FLOAT)element->GetRadius(), (FLOAT)element->GetRadius() ), brush ); } /*********************************************************************** Gui3DBorderElementRenderer ***********************************************************************/ void Gui3DBorderElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { oldColor1=element->GetColor1(); oldColor2=element->GetColor2(); brush1=_renderTarget->CreateDirect2DBrush(oldColor1); brush2=_renderTarget->CreateDirect2DBrush(oldColor2); } } void Gui3DBorderElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { if(brush1) { _renderTarget->DestroyDirect2DBrush(oldColor1); brush1=0; } if(brush2) { _renderTarget->DestroyDirect2DBrush(oldColor2); brush2=0; } } } void Gui3DBorderElementRenderer::InitializeInternal() { } void Gui3DBorderElementRenderer::FinalizeInternal() { DestroyBrush(renderTarget); } void Gui3DBorderElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { DestroyBrush(oldRenderTarget); CreateBrush(newRenderTarget); } Gui3DBorderElementRenderer::Gui3DBorderElementRenderer() :brush1(0) ,brush2(0) { } void Gui3DBorderElementRenderer::Render(Rect bounds) { D2D1_RECT_F rect=D2D1::RectF((FLOAT)bounds.x1+0.5f, (FLOAT)bounds.y1+0.5f, (FLOAT)bounds.x2-0.5f, (FLOAT)bounds.y2-0.5f); ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.right, rect.top), brush1); d2dRenderTarget->DrawLine(D2D1::Point2F(rect.left, rect.top), D2D1::Point2F(rect.left, rect.bottom), brush1); d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.left, rect.bottom), brush2); d2dRenderTarget->DrawLine(D2D1::Point2F(rect.right, rect.bottom), D2D1::Point2F(rect.right, rect.top), brush2); } void Gui3DBorderElementRenderer::OnElementStateChanged() { if(renderTarget) { Color color1=element->GetColor1(); Color color2=element->GetColor2(); if(oldColor1!=color1 || oldColor2!=color2) { DestroyBrush(renderTarget); CreateBrush(renderTarget); } } } /*********************************************************************** Gui3DSplitterElementRenderer ***********************************************************************/ void Gui3DSplitterElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { oldColor1=element->GetColor1(); oldColor2=element->GetColor2(); brush1=_renderTarget->CreateDirect2DBrush(oldColor1); brush2=_renderTarget->CreateDirect2DBrush(oldColor2); } } void Gui3DSplitterElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { if(brush1) { _renderTarget->DestroyDirect2DBrush(oldColor1); brush1=0; } if(brush2) { _renderTarget->DestroyDirect2DBrush(oldColor2); brush2=0; } } } void Gui3DSplitterElementRenderer::InitializeInternal() { } void Gui3DSplitterElementRenderer::FinalizeInternal() { DestroyBrush(renderTarget); } void Gui3DSplitterElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { DestroyBrush(oldRenderTarget); CreateBrush(newRenderTarget); } Gui3DSplitterElementRenderer::Gui3DSplitterElementRenderer() :brush1(0) ,brush2(0) { } void Gui3DSplitterElementRenderer::Render(Rect bounds) { D2D1_POINT_2F p11, p12, p21, p22; switch(element->GetDirection()) { case Gui3DSplitterElement::Horizontal: { vint y=bounds.y1+bounds.Height()/2-1; p11=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+0.5f); p12=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+0.5f); p21=D2D1::Point2F((FLOAT)bounds.x1, (FLOAT)y+1.5f); p22=D2D1::Point2F((FLOAT)bounds.x2, (FLOAT)y+1.5f); } break; case Gui3DSplitterElement::Vertical: { vint x=bounds.x1+bounds.Width()/2-1; p11=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y1-0.0f); p12=D2D1::Point2F((FLOAT)x+0.5f, (FLOAT)bounds.y2+0.0f); p21=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y1-0.0f); p22=D2D1::Point2F((FLOAT)x+1.5f, (FLOAT)bounds.y2+0.0f); } break; } ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); d2dRenderTarget->DrawLine(p11, p12, brush1); d2dRenderTarget->DrawLine(p21, p22, brush2); } void Gui3DSplitterElementRenderer::OnElementStateChanged() { if(renderTarget) { Color color1=element->GetColor1(); Color color2=element->GetColor2(); if(oldColor1!=color1 || oldColor2!=color2) { DestroyBrush(renderTarget); CreateBrush(renderTarget); } } } /*********************************************************************** GuiSolidBackgroundElementRenderer ***********************************************************************/ IMPLEMENT_BRUSH_ELEMENT_RENDERER_SOLID_COLOR_BRUSH(GuiSolidBackgroundElementRenderer) IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiSolidBackgroundElementRenderer) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); switch(element->GetShape()) { case ElementShape::Rectangle: d2dRenderTarget->FillRectangle( D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2), brush ); break; case ElementShape::Ellipse: d2dRenderTarget->FillEllipse( D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f), brush ); break; } } /*********************************************************************** GuiGradientBackgroundElementRenderer ***********************************************************************/ IMPLEMENT_BRUSH_ELEMENT_RENDERER_LINEAR_GRADIENT_BRUSH(GuiGradientBackgroundElementRenderer) IMPLEMENT_BRUSH_ELEMENT_RENDERER(GuiGradientBackgroundElementRenderer) { D2D1_POINT_2F points[2]; switch(element->GetDirection()) { case GuiGradientBackgroundElement::Horizontal: { points[0].x=(FLOAT)bounds.x1; points[0].y=(FLOAT)bounds.y1; points[1].x=(FLOAT)bounds.x2; points[1].y=(FLOAT)bounds.y1; } break; case GuiGradientBackgroundElement::Vertical: { points[0].x=(FLOAT)bounds.x1; points[0].y=(FLOAT)bounds.y1; points[1].x=(FLOAT)bounds.x1; points[1].y=(FLOAT)bounds.y2; } break; case GuiGradientBackgroundElement::Slash: { points[0].x=(FLOAT)bounds.x2; points[0].y=(FLOAT)bounds.y1; points[1].x=(FLOAT)bounds.x1; points[1].y=(FLOAT)bounds.y2; } break; case GuiGradientBackgroundElement::Backslash: { points[0].x=(FLOAT)bounds.x1; points[0].y=(FLOAT)bounds.y1; points[1].x=(FLOAT)bounds.x2; points[1].y=(FLOAT)bounds.y2; } break; } brush->SetStartPoint(points[0]); brush->SetEndPoint(points[1]); ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); switch(element->GetShape()) { case ElementShape::Rectangle: d2dRenderTarget->FillRectangle( D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2), brush ); break; case ElementShape::Ellipse: d2dRenderTarget->FillEllipse( D2D1::Ellipse(D2D1::Point2F((bounds.x1+bounds.x2)/2.0f, (bounds.y1+bounds.y2)/2.0f), bounds.Width()/2.0f, bounds.Height()/2.0f), brush ); break; } } /*********************************************************************** GuiSolidLabelElementRenderer ***********************************************************************/ void GuiSolidLabelElementRenderer::CreateBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { oldColor=element->GetColor(); brush=_renderTarget->CreateDirect2DBrush(oldColor); } } void GuiSolidLabelElementRenderer::DestroyBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget && brush) { _renderTarget->DestroyDirect2DBrush(oldColor); brush=0; } } void GuiSolidLabelElementRenderer::CreateTextFormat(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager(); oldFont=element->GetFont(); textFormat=resourceManager->CreateDirect2DTextFormat(oldFont); } } void GuiSolidLabelElementRenderer::DestroyTextFormat(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget && textFormat) { IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager(); resourceManager->DestroyDirect2DTextFormat(oldFont); textFormat=0; } } void GuiSolidLabelElementRenderer::CreateTextLayout() { if(textFormat) { HRESULT hr=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout( oldText.Buffer(), (int)oldText.Length(), textFormat->textFormat.Obj(), 0, 0, &textLayout); if(!FAILED(hr)) { if(oldFont.underline) { DWRITE_TEXT_RANGE textRange; textRange.startPosition=0; textRange.length=(int)oldText.Length(); textLayout->SetUnderline(TRUE, textRange); } if(oldFont.strikeline) { DWRITE_TEXT_RANGE textRange; textRange.startPosition=0; textRange.length=(int)oldText.Length(); textLayout->SetStrikethrough(TRUE, textRange); } } else { textLayout=0; } } } void GuiSolidLabelElementRenderer::DestroyTextLayout() { if(textLayout) { textLayout->Release(); textLayout=0; } } void GuiSolidLabelElementRenderer::UpdateMinSize() { float maxWidth=0; DestroyTextLayout(); bool calculateSizeFromTextLayout=false; if(renderTarget) { if(element->GetWrapLine()) { if(element->GetWrapLineHeightCalculation()) { CreateTextLayout(); if(textLayout) { maxWidth=textLayout->GetMaxWidth(); if(oldMaxWidth!=-1) { textLayout->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP); textLayout->SetMaxWidth((float)oldMaxWidth); } calculateSizeFromTextLayout=true; } } } else { CreateTextLayout(); if(textLayout) { maxWidth=textLayout->GetMaxWidth(); calculateSizeFromTextLayout=true; } } } if(calculateSizeFromTextLayout) { DWRITE_TEXT_METRICS metrics; HRESULT hr=textLayout->GetMetrics(&metrics); if(!FAILED(hr)) { vint width=0; if(!element->GetEllipse() && !element->GetWrapLine() && !element->GetMultiline()) { width=(vint)ceil(metrics.widthIncludingTrailingWhitespace); } minSize=Size(width, (vint)ceil(metrics.height)); } textLayout->SetMaxWidth(maxWidth); } else { minSize=Size(); } } void GuiSolidLabelElementRenderer::InitializeInternal() { } void GuiSolidLabelElementRenderer::FinalizeInternal() { DestroyTextLayout(); DestroyBrush(renderTarget); DestroyTextFormat(renderTarget); } void GuiSolidLabelElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { DestroyBrush(oldRenderTarget); DestroyTextFormat(oldRenderTarget); CreateBrush(newRenderTarget); CreateTextFormat(newRenderTarget); UpdateMinSize(); } GuiSolidLabelElementRenderer::GuiSolidLabelElementRenderer() :brush(0) ,textFormat(0) ,textLayout(0) ,oldText(L"") ,oldMaxWidth(-1) { } void GuiSolidLabelElementRenderer::Render(Rect bounds) { if(!textLayout) { CreateTextLayout(); } vint x=0; vint y=0; switch(element->GetHorizontalAlignment()) { case Alignment::Left: x=bounds.Left(); break; case Alignment::Center: x=bounds.Left()+(bounds.Width()-minSize.x)/2; break; case Alignment::Right: x=bounds.Right()-minSize.x; break; } switch(element->GetVerticalAlignment()) { case Alignment::Top: y=bounds.Top(); break; case Alignment::Center: y=bounds.Top()+(bounds.Height()-minSize.y)/2; break; case Alignment::Bottom: y=bounds.Bottom()-minSize.y; break; } renderTarget->SetTextAntialias(oldFont.antialias, oldFont.verticalAntialias); if(!element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine()) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); d2dRenderTarget->DrawTextLayout( D2D1::Point2F((FLOAT)x, (FLOAT)y), textLayout, brush, D2D1_DRAW_TEXT_OPTIONS_NO_SNAP ); } else { IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory(); DWRITE_TRIMMING trimming; IDWriteInlineObject* inlineObject; textLayout->GetTrimming(&trimming, &inlineObject); textLayout->SetWordWrapping(element->GetWrapLine()?DWRITE_WORD_WRAPPING_WRAP:DWRITE_WORD_WRAPPING_NO_WRAP); if(element->GetEllipse()) { textLayout->SetTrimming(&textFormat->trimming, textFormat->ellipseInlineObject.Obj()); } switch(element->GetHorizontalAlignment()) { case Alignment::Left: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); break; case Alignment::Center: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); break; case Alignment::Right: textLayout->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING); break; } if(!element->GetMultiline() && !element->GetWrapLine()) { switch(element->GetVerticalAlignment()) { case Alignment::Top: textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); break; case Alignment::Center: textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); break; case Alignment::Bottom: textLayout->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_FAR); break; } } Rect textBounds=bounds; if(element->GetEllipse() && !element->GetMultiline() && !element->GetWrapLine()) { textBounds=Rect(Point(textBounds.x1, y), Size(bounds.Width(), minSize.y)); } textLayout->SetMaxWidth((FLOAT)textBounds.Width()); textLayout->SetMaxHeight((FLOAT)textBounds.Height()); ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); d2dRenderTarget->DrawTextLayout( D2D1::Point2F((FLOAT)textBounds.Left(), (FLOAT)textBounds.Top()), textLayout, brush, D2D1_DRAW_TEXT_OPTIONS_NO_SNAP ); textLayout->SetTrimming(&trimming, inlineObject); if(oldMaxWidth!=textBounds.Width()) { oldMaxWidth=textBounds.Width(); UpdateMinSize(); } } } void GuiSolidLabelElementRenderer::OnElementStateChanged() { if(renderTarget) { Color color=element->GetColor(); if(oldColor!=color) { DestroyBrush(renderTarget); CreateBrush(renderTarget); } FontProperties font=element->GetFont(); if(oldFont!=font) { DestroyTextFormat(renderTarget); CreateTextFormat(renderTarget); } } oldText=element->GetText(); UpdateMinSize(); } /*********************************************************************** GuiImageFrameElementRenderer ***********************************************************************/ void GuiImageFrameElementRenderer::UpdateBitmap(IWindowsDirect2DRenderTarget* renderTarget) { if(renderTarget && element->GetImage()) { INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); bitmap=renderTarget->GetBitmap(frame, element->GetEnabled()); if (element->GetStretch()) { minSize=Size(0,0); } else { minSize=frame->GetSize(); } } else { bitmap=0; minSize=Size(0, 0); } } void GuiImageFrameElementRenderer::InitializeInternal() { } void GuiImageFrameElementRenderer::FinalizeInternal() { } void GuiImageFrameElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { UpdateBitmap(newRenderTarget); } GuiImageFrameElementRenderer::GuiImageFrameElementRenderer() { } void GuiImageFrameElementRenderer::Render(Rect bounds) { if(bitmap) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); D2D1_RECT_F source=D2D1::RectF(0, 0, (FLOAT)minSize.x, (FLOAT)minSize.y); D2D1_RECT_F destination; if(element->GetStretch()) { INativeImageFrame* frame=element->GetImage()->GetFrame(element->GetFrameIndex()); auto size = frame->GetSize(); source = D2D1::RectF(0, 0, (FLOAT)size.x, (FLOAT)size.y); destination=D2D1::RectF((FLOAT)bounds.x1, (FLOAT)bounds.y1, (FLOAT)bounds.x2, (FLOAT)bounds.y2); } else { vint x=0; vint y=0; switch(element->GetHorizontalAlignment()) { case Alignment::Left: x=bounds.Left(); break; case Alignment::Center: x=bounds.Left()+(bounds.Width()-minSize.x)/2; break; case Alignment::Right: x=bounds.Right()-minSize.x; break; } switch(element->GetVerticalAlignment()) { case Alignment::Top: y=bounds.Top(); break; case Alignment::Center: y=bounds.Top()+(bounds.Height()-minSize.y)/2; break; case Alignment::Bottom: y=bounds.Bottom()-minSize.y; break; } destination=D2D1::RectF((FLOAT)x, (FLOAT)y, (FLOAT)(x+minSize.x), (FLOAT)(y+minSize.y)); } ID2D1DeviceContext* d2dDeviceContext = nullptr; { HRESULT hr = d2dRenderTarget->QueryInterface(&d2dDeviceContext); if (!SUCCEEDED(hr)) { if (d2dDeviceContext) { d2dDeviceContext->Release(); } d2dDeviceContext = nullptr; } } if(element->GetImage()->GetFormat()==INativeImage::Gif && element->GetFrameIndex()>0) { vint max = element->GetFrameIndex(); for (vint i = 0; i <= max; i++) { ComPtr frameBitmap=renderTarget->GetBitmap(element->GetImage()->GetFrame(i), element->GetEnabled()); if (d2dDeviceContext) { d2dDeviceContext->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source); } else { d2dRenderTarget->DrawBitmap(frameBitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source); } } } else { if (d2dDeviceContext) { d2dDeviceContext->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, source); } else { d2dRenderTarget->DrawBitmap(bitmap.Obj(), destination, 1.0f, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, source); } } if (d2dDeviceContext) { d2dDeviceContext->Release(); } } } void GuiImageFrameElementRenderer::OnElementStateChanged() { UpdateBitmap(renderTarget); } /*********************************************************************** GuiPolygonElementRenderer ***********************************************************************/ void GuiPolygonElementRenderer::CreateGeometry() { oldPoints.Resize(element->GetPointCount()); if(oldPoints.Count()>0) { memcpy(&oldPoints[0], &element->GetPoint(0), sizeof(Point)*element->GetPointCount()); } if(oldPoints.Count()>=3) { ID2D1PathGeometry* pg=0; GetWindowsDirect2DObjectProvider()->GetDirect2DFactory()->CreatePathGeometry(&pg); if(pg) { geometry=pg; FillGeometry(Point(0, 0)); } } } void GuiPolygonElementRenderer::DestroyGeometry() { if(geometry) { geometry=0; } } void GuiPolygonElementRenderer::FillGeometry(Point offset) { if(geometry) { ID2D1GeometrySink* pgs=0; geometry->Open(&pgs); if(pgs) { D2D1_POINT_2F p; p.x=(FLOAT)(oldPoints[0].x+offset.x)+0.5f; p.y=(FLOAT)(oldPoints[0].y+offset.y)+0.5f; pgs->BeginFigure(p, D2D1_FIGURE_BEGIN_FILLED); for(vint i=1;iAddLine(p); } pgs->EndFigure(D2D1_FIGURE_END_CLOSED); pgs->Close(); pgs->Release(); } } } void GuiPolygonElementRenderer::RecreateResource(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { if(oldRenderTarget==newRenderTarget) { if(oldRenderTarget) { if(oldBorderColor!=element->GetBorderColor()) { oldRenderTarget->DestroyDirect2DBrush(oldBorderColor); oldBorderColor=element->GetBorderColor(); borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor); } if(oldBackgroundColor!=element->GetBackgroundColor()) { oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor); oldBackgroundColor=element->GetBackgroundColor(); backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor); } } } else { if(oldRenderTarget) { oldRenderTarget->DestroyDirect2DBrush(oldBorderColor); oldRenderTarget->DestroyDirect2DBrush(oldBackgroundColor); } if(newRenderTarget) { oldBorderColor=element->GetBorderColor(); oldBackgroundColor=element->GetBackgroundColor(); borderBrush=newRenderTarget->CreateDirect2DBrush(oldBorderColor); backgroundBrush=newRenderTarget->CreateDirect2DBrush(oldBackgroundColor); } } } void GuiPolygonElementRenderer::InitializeInternal() { oldBorderColor=element->GetBorderColor(); oldBackgroundColor=element->GetBackgroundColor(); RecreateResource(0, renderTarget); CreateGeometry(); } void GuiPolygonElementRenderer::FinalizeInternal() { DestroyGeometry(); RecreateResource(renderTarget, 0); } void GuiPolygonElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { RecreateResource(oldRenderTarget, newRenderTarget); } GuiPolygonElementRenderer::GuiPolygonElementRenderer() :borderBrush(0) ,backgroundBrush(0) ,geometry(0) { } void GuiPolygonElementRenderer::Render(Rect bounds) { if(renderTarget && geometry) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); vint offsetX=(bounds.Width()-minSize.x)/2+bounds.x1; vint offsetY=(bounds.Height()-minSize.y)/2+bounds.y1; D2D1_MATRIX_3X2_F oldT, newT; d2dRenderTarget->GetTransform(&oldT); newT=D2D1::Matrix3x2F::Translation((FLOAT)offsetX, (FLOAT)offsetY); d2dRenderTarget->SetTransform(&newT); d2dRenderTarget->FillGeometry(geometry.Obj(), backgroundBrush); d2dRenderTarget->DrawGeometry(geometry.Obj(), borderBrush); d2dRenderTarget->SetTransform(&oldT); } } void GuiPolygonElementRenderer::OnElementStateChanged() { minSize=element->GetSize(); RecreateResource(renderTarget, renderTarget); bool polygonModified=false; if(oldPoints.Count()!=element->GetPointCount()) { polygonModified=true; } else { for(vint i=0;iGetPoint(i)) { polygonModified=true; break; } } } if(polygonModified) { DestroyGeometry(); CreateGeometry(); } } /*********************************************************************** GuiColorizedTextElementRenderer ***********************************************************************/ void GuiColorizedTextElementRenderer::CreateTextBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { colors.Resize(element->GetColors().Count()); for(vint i=0;iGetColors().Get(i); ColorEntryResource newEntry; newEntry.normal.text=entry.normal.text; newEntry.normal.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.normal.text); newEntry.normal.background=entry.normal.background; newEntry.normal.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.normal.background); newEntry.selectedFocused.text=entry.selectedFocused.text; newEntry.selectedFocused.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedFocused.text); newEntry.selectedFocused.background=entry.selectedFocused.background; newEntry.selectedFocused.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedFocused.background); newEntry.selectedUnfocused.text=entry.selectedUnfocused.text; newEntry.selectedUnfocused.textBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedUnfocused.text); newEntry.selectedUnfocused.background=entry.selectedUnfocused.background; newEntry.selectedUnfocused.backgroundBrush=_renderTarget->CreateDirect2DBrush(newEntry.selectedUnfocused.background); colors[i]=newEntry; } } } void GuiColorizedTextElementRenderer::DestroyTextBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { for(vint i=0;iDestroyDirect2DBrush(colors[i].normal.text); _renderTarget->DestroyDirect2DBrush(colors[i].normal.background); _renderTarget->DestroyDirect2DBrush(colors[i].selectedFocused.text); _renderTarget->DestroyDirect2DBrush(colors[i].selectedFocused.background); _renderTarget->DestroyDirect2DBrush(colors[i].selectedUnfocused.text); _renderTarget->DestroyDirect2DBrush(colors[i].selectedUnfocused.background); } colors.Resize(0); } } void GuiColorizedTextElementRenderer::CreateCaretBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { oldCaretColor=element->GetCaretColor(); caretBrush=_renderTarget->CreateDirect2DBrush(oldCaretColor); } } void GuiColorizedTextElementRenderer::DestroyCaretBrush(IWindowsDirect2DRenderTarget* _renderTarget) { if(_renderTarget) { if(caretBrush) { _renderTarget->DestroyDirect2DBrush(oldCaretColor); caretBrush=0; } } } void GuiColorizedTextElementRenderer::ColorChanged() { DestroyTextBrush(renderTarget); CreateTextBrush(renderTarget); } void GuiColorizedTextElementRenderer::FontChanged() { IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager(); if(textFormat) { resourceManager->DestroyDirect2DTextFormat(oldFont); resourceManager->DestroyDirect2DCharMeasurer(oldFont); } oldFont=element->GetFont(); textFormat=resourceManager->CreateDirect2DTextFormat(oldFont); element->GetLines().SetCharMeasurer(resourceManager->CreateDirect2DCharMeasurer(oldFont).Obj()); } text::CharMeasurer* GuiColorizedTextElementRenderer::GetCharMeasurer() { return 0; } void GuiColorizedTextElementRenderer::InitializeInternal() { textFormat=0; caretBrush=0; element->SetCallback(this); } void GuiColorizedTextElementRenderer::FinalizeInternal() { DestroyTextBrush(renderTarget); DestroyCaretBrush(renderTarget); IWindowsDirect2DResourceManager* resourceManager=GetWindowsDirect2DResourceManager(); if(textFormat) { resourceManager->DestroyDirect2DTextFormat(oldFont); resourceManager->DestroyDirect2DCharMeasurer(oldFont); } } void GuiColorizedTextElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { DestroyTextBrush(oldRenderTarget); DestroyCaretBrush(oldRenderTarget); CreateTextBrush(newRenderTarget); CreateCaretBrush(newRenderTarget); element->GetLines().SetRenderTarget(newRenderTarget); } void GuiColorizedTextElementRenderer::Render(Rect bounds) { if(renderTarget) { ID2D1RenderTarget* d2dRenderTarget=renderTarget->GetDirect2DRenderTarget(); wchar_t passwordChar=element->GetPasswordChar(); Point viewPosition=element->GetViewPosition(); Rect viewBounds(viewPosition, bounds.GetSize()); vint startRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, viewBounds.y1)).row; vint endRow=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, viewBounds.y2)).row; TextPos selectionBegin=element->GetCaretBegin()GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd(); TextPos selectionEnd=element->GetCaretBegin()>element->GetCaretEnd()?element->GetCaretBegin():element->GetCaretEnd(); bool focused=element->GetFocused(); renderTarget->SetTextAntialias(oldFont.antialias, oldFont.verticalAntialias); for(vint row=startRow;row<=endRow;row++) { Rect startRect=element->GetLines().GetRectFromTextPos(TextPos(row, 0)); Point startPoint=startRect.LeftTop(); vint startColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x1, startPoint.y)).column; vint endColumn=element->GetLines().GetTextPosFromPoint(Point(viewBounds.x2, startPoint.y)).column; text::TextLine& line=element->GetLines().GetLine(row); vint x=startColumn==0?0:line.att[startColumn-1].rightOffset; for(vint column=startColumn; column<=endColumn; column++) { bool inSelection=false; if(selectionBegin.row==selectionEnd.row) { inSelection=(row==selectionBegin.row && selectionBegin.column<=column && column=colors.Count()) { colorIndex=0; } ColorItemResource& color= !inSelection?colors[colorIndex].normal: focused?colors[colorIndex].selectedFocused: colors[colorIndex].selectedUnfocused; vint x2=crlf?x+startRect.Height()/2:line.att[column].rightOffset; vint tx=x-viewPosition.x+bounds.x1; vint ty=startPoint.y-viewPosition.y+bounds.y1; if(color.background.a>0) { d2dRenderTarget->FillRectangle(D2D1::RectF((FLOAT)tx, (FLOAT)ty, (FLOAT)(tx+(x2-x)), (FLOAT)(ty+startRect.Height())), color.backgroundBrush); } if(!crlf) { d2dRenderTarget->DrawText( (passwordChar?&passwordChar:&line.text[column]), 1, textFormat->textFormat.Obj(), D2D1::RectF((FLOAT)tx, (FLOAT)ty, (FLOAT)tx+1, (FLOAT)ty+1), color.textBrush, D2D1_DRAW_TEXT_OPTIONS_NO_SNAP, DWRITE_MEASURING_MODE_GDI_NATURAL ); } x=x2; } } if(element->GetCaretVisible() && element->GetLines().IsAvailable(element->GetCaretEnd())) { Point caretPoint=element->GetLines().GetPointFromTextPos(element->GetCaretEnd()); vint height=element->GetLines().GetRowHeight(); Point p1(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y-viewPosition.y+bounds.y1+1); Point p2(caretPoint.x-viewPosition.x+bounds.x1, caretPoint.y+height-viewPosition.y+bounds.y1-1); d2dRenderTarget->DrawLine( D2D1::Point2F((FLOAT)p1.x+0.5f, (FLOAT)p1.y), D2D1::Point2F((FLOAT)p2.x+0.5f, (FLOAT)p2.y), caretBrush ); d2dRenderTarget->DrawLine( D2D1::Point2F((FLOAT)p1.x-0.5f, (FLOAT)p1.y), D2D1::Point2F((FLOAT)p2.x-0.5f, (FLOAT)p2.y), caretBrush ); } } } void GuiColorizedTextElementRenderer::OnElementStateChanged() { if(renderTarget) { Color caretColor=element->GetCaretColor(); if(oldCaretColor!=caretColor) { DestroyCaretBrush(renderTarget); CreateCaretBrush(renderTarget); } } } /*********************************************************************** GuiDirect2DElementRenderer ***********************************************************************/ void GuiDirect2DElementRenderer::InitializeInternal() { } void GuiDirect2DElementRenderer::FinalizeInternal() { } void GuiDirect2DElementRenderer::RenderTargetChangedInternal(IWindowsDirect2DRenderTarget* oldRenderTarget, IWindowsDirect2DRenderTarget* newRenderTarget) { IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory(); ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory(); if(oldRenderTarget) { GuiDirect2DElementEventArgs arguments(element, oldRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect()); element->BeforeRenderTargetChanged.Execute(arguments); } if(newRenderTarget) { GuiDirect2DElementEventArgs arguments(element, newRenderTarget->GetDirect2DRenderTarget(), fdw, fd2d, Rect()); element->AfterRenderTargetChanged.Execute(arguments); } } GuiDirect2DElementRenderer::GuiDirect2DElementRenderer() { } GuiDirect2DElementRenderer::~GuiDirect2DElementRenderer() { } void GuiDirect2DElementRenderer::Render(Rect bounds) { if(renderTarget) { IDWriteFactory* fdw=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory(); ID2D1Factory* fd2d=GetWindowsDirect2DObjectProvider()->GetDirect2DFactory(); renderTarget->PushClipper(bounds); if(!renderTarget->IsClipperCoverWholeTarget()) { ID2D1RenderTarget* rt=renderTarget->GetDirect2DRenderTarget(); GuiDirect2DElementEventArgs arguments(element, rt, fdw, fd2d, bounds); element->Rendering.Execute(arguments); } renderTarget->PopClipper(); } } void GuiDirect2DElementRenderer::OnElementStateChanged() { } } } } /*********************************************************************** GRAPHICSELEMENT\WINDOWSDIRECT2D\GUIGRAPHICSWINDOWSDIRECT2D.CPP ***********************************************************************/ namespace vl { namespace presentation { namespace elements { /*********************************************************************** GuiDirect2DElement ***********************************************************************/ GuiDirect2DElement::GuiDirect2DElement() { } GuiDirect2DElement::~GuiDirect2DElement() { } } namespace elements_windows_d2d { using namespace elements; using namespace collections; D2D1::ColorF GetD2DColor(Color color) { return D2D1::ColorF(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a/255.0f); } /*********************************************************************** CachedResourceAllocator ***********************************************************************/ class CachedSolidBrushAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(Color, ComPtr) IWindowsDirect2DRenderTarget* guiRenderTarget; public: CachedSolidBrushAllocator() :guiRenderTarget(0) { } void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget) { guiRenderTarget=_guiRenderTarget; } ComPtr CreateInternal(Color color) { ID2D1SolidColorBrush* brush=0; ID2D1RenderTarget* renderTarget=guiRenderTarget->GetDirect2DRenderTarget(); HRESULT hr=renderTarget->CreateSolidColorBrush(GetD2DColor(color), &brush); if(!FAILED(hr)) { return brush; } else { return 0; } } }; class CachedLinearBrushAllocator { typedef Pair ColorPair; DEFINE_CACHED_RESOURCE_ALLOCATOR(ColorPair, ComPtr) IWindowsDirect2DRenderTarget* guiRenderTarget; public: CachedLinearBrushAllocator() :guiRenderTarget(0) { } void SetRenderTarget(IWindowsDirect2DRenderTarget* _guiRenderTarget) { guiRenderTarget=_guiRenderTarget; } ComPtr CreateInternal(ColorPair colors) { ID2D1RenderTarget* renderTarget=guiRenderTarget->GetDirect2DRenderTarget(); ID2D1GradientStopCollection* stopCollection=0; { D2D1_GRADIENT_STOP stops[2]; stops[0].color=GetD2DColor(colors.key); stops[0].position=0.0f; stops[1].color=GetD2DColor(colors.value); stops[1].position=1.0f; HRESULT hr=renderTarget->CreateGradientStopCollection( stops, 2, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, &stopCollection); if(FAILED(hr)) return 0; } ID2D1LinearGradientBrush* brush=0; { D2D1_POINT_2F points[2]={{0, 0}, {0, 0}}; HRESULT hr=renderTarget->CreateLinearGradientBrush( D2D1::LinearGradientBrushProperties(points[0], points[1]), stopCollection, &brush); stopCollection->Release(); if(FAILED(hr)) return 0; } return brush; } }; class CachedTextFormatAllocator { private: DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr) public: static ComPtr CreateDirect2DFont(const FontProperties& fontProperties) { IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory(); IDWriteTextFormat* format=0; HRESULT hr=dwriteFactory->CreateTextFormat( fontProperties.fontFamily.Buffer(), NULL, (fontProperties.bold?DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL), (fontProperties.italic?DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL), DWRITE_FONT_STRETCH_NORMAL, (FLOAT)fontProperties.size, L"", &format); if(!FAILED(hr)) { format->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); return format; } else { return 0; } } Ptr CreateInternal(const FontProperties& fontProperties) { Ptr textFormat=new Direct2DTextFormatPackage; textFormat->textFormat=CreateDirect2DFont(fontProperties); textFormat->trimming.granularity=DWRITE_TRIMMING_GRANULARITY_CHARACTER; textFormat->trimming.delimiter=0; textFormat->trimming.delimiterCount=0; IDWriteInlineObject* ellipseInlineObject=0; GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateEllipsisTrimmingSign(textFormat->textFormat.Obj(), &ellipseInlineObject); textFormat->ellipseInlineObject=ellipseInlineObject; return textFormat; } }; class CachedCharMeasurerAllocator { DEFINE_CACHED_RESOURCE_ALLOCATOR(FontProperties, Ptr) protected: class Direct2DCharMeasurer : public text::CharMeasurer { protected: ComPtr font; vint size; Size MeasureInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget) { Size charSize(0, 0); IDWriteTextLayout* textLayout=0; HRESULT hr=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory()->CreateTextLayout( &character, 1, font.Obj(), 0, 0, &textLayout); if(!FAILED(hr)) { DWRITE_TEXT_METRICS metrics; hr=textLayout->GetMetrics(&metrics); if(!FAILED(hr)) { charSize=Size((vint)ceil(metrics.widthIncludingTrailingWhitespace), (vint)ceil(metrics.height)); } textLayout->Release(); } return charSize; } vint MeasureWidthInternal(wchar_t character, IGuiGraphicsRenderTarget* renderTarget) { return MeasureInternal(character, renderTarget).x; } vint GetRowHeightInternal(IGuiGraphicsRenderTarget* renderTarget) { return MeasureInternal(L' ', renderTarget).y; } public: Direct2DCharMeasurer(ComPtr _font, vint _size) :text::CharMeasurer(_size) ,size(_size) ,font(_font) { } }; public: Ptr CreateInternal(const FontProperties& value) { return new Direct2DCharMeasurer(CachedTextFormatAllocator::CreateDirect2DFont(value), value.size); } }; /*********************************************************************** WindowsDirect2DRenderTarget ***********************************************************************/ class WindowsDirect2DImageFrameCache : public Object, public INativeImageFrameCache { protected: IWindowsDirect2DRenderTarget* renderTarget; INativeImageFrame* cachedFrame; ComPtr bitmap; ComPtr disabledBitmap; public: WindowsDirect2DImageFrameCache(IWindowsDirect2DRenderTarget* _renderTarget) :renderTarget(_renderTarget) { } ~WindowsDirect2DImageFrameCache() { } void OnAttach(INativeImageFrame* frame)override { cachedFrame=frame; ID2D1Bitmap* d2dBitmap=0; HRESULT hr=renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap( GetWindowsDirect2DObjectProvider()->GetWICBitmap(frame), &d2dBitmap ); if(SUCCEEDED(hr)) { bitmap=d2dBitmap; } } void OnDetach(INativeImageFrame* frame)override { renderTarget->DestroyBitmapCache(cachedFrame); } INativeImageFrame* GetFrame() { return cachedFrame; } ComPtr GetBitmap(bool enabled) { if(enabled) { return bitmap; } else { if(!disabledBitmap) { IWICBitmap* frameBitmap=GetWindowsDirect2DObjectProvider()->GetWICBitmap(cachedFrame); ID2D1Bitmap* d2dBitmap=0; HRESULT hr=renderTarget->GetDirect2DRenderTarget()->CreateBitmapFromWicBitmap( frameBitmap, &d2dBitmap ); if(SUCCEEDED(hr)) { disabledBitmap=d2dBitmap; WICRect rect; rect.X=0; rect.Y=0; rect.Width=bitmap->GetPixelSize().width; rect.Height=bitmap->GetPixelSize().height; BYTE* buffer=new BYTE[rect.Width*rect.Height*4]; hr=frameBitmap->CopyPixels(&rect, rect.Width*4, rect.Width*rect.Height*4, buffer); if(SUCCEEDED(hr)) { vint count=rect.Width*rect.Height; BYTE* read=buffer; for(vint i=0;iCopyFromMemory(&d2dRect, buffer, rect.Width*4); } delete[] buffer; } } return disabledBitmap; } } }; class WindowsDirect2DRenderTarget : public Object, public IWindowsDirect2DRenderTarget { typedef SortedList> ImageCacheList; protected: INativeWindow* window; ID2D1RenderTarget* d2dRenderTarget; List clippers; vint clipperCoverWholeTargetCounter; CachedSolidBrushAllocator solidBrushes; CachedLinearBrushAllocator linearBrushes; ImageCacheList imageCaches; ComPtr noAntialiasParams; ComPtr horizontalAntialiasParams; ComPtr bidirectionalAntialiasParams; ComPtr CreateRenderingParams(DWRITE_RENDERING_MODE renderingMode, IDWriteRenderingParams* defaultParams, IDWriteFactory* dwriteFactory) { IDWriteRenderingParams* renderingParams=0; FLOAT gamma=defaultParams->GetGamma(); FLOAT enhancedContrast=defaultParams->GetEnhancedContrast(); FLOAT clearTypeLevel=defaultParams->GetClearTypeLevel(); DWRITE_PIXEL_GEOMETRY pixelGeometry=defaultParams->GetPixelGeometry(); HRESULT hr=dwriteFactory->CreateCustomRenderingParams( gamma, enhancedContrast, clearTypeLevel, pixelGeometry, renderingMode, &renderingParams); if(!FAILED(hr)) { return renderingParams; } else { return 0; } } public: WindowsDirect2DRenderTarget(INativeWindow* _window) :window(_window) ,d2dRenderTarget(0) ,clipperCoverWholeTargetCounter(0) { solidBrushes.SetRenderTarget(this); linearBrushes.SetRenderTarget(this); IDWriteFactory* dwriteFactory=GetWindowsDirect2DObjectProvider()->GetDirectWriteFactory(); IDWriteRenderingParams* defaultParams=0; HRESULT hr=dwriteFactory->CreateRenderingParams(&defaultParams); if(!FAILED(hr)) { noAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, defaultParams, dwriteFactory); horizontalAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL, defaultParams, dwriteFactory); bidirectionalAntialiasParams=CreateRenderingParams(DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, defaultParams, dwriteFactory); defaultParams->Release(); } } ~WindowsDirect2DRenderTarget() { while(imageCaches.Count()) { Ptr cache=imageCaches[imageCaches.Count()-1]; cache->GetFrame()->RemoveCache(this); } } ID2D1RenderTarget* GetDirect2DRenderTarget()override { return d2dRenderTarget ? d2dRenderTarget : GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window); } ComPtr GetBitmap(INativeImageFrame* frame, bool enabled)override { Ptr cache=frame->GetCache(this); if(cache) { return cache.Cast()->GetBitmap(enabled); } else { Ptr d2dCache=new WindowsDirect2DImageFrameCache(this); if(frame->SetCache(this, d2dCache)) { imageCaches.Add(d2dCache); return d2dCache->GetBitmap(enabled); } else { return 0; } } } void DestroyBitmapCache(INativeImageFrame* frame)override { WindowsDirect2DImageFrameCache* cache=frame->GetCache(this).Cast().Obj(); imageCaches.Remove(cache); } void SetTextAntialias(bool antialias, bool verticalAntialias)override { ComPtr params; if(!antialias) { params=noAntialiasParams; } else if(!verticalAntialias) { params=horizontalAntialiasParams; } else { params=bidirectionalAntialiasParams; } if(params && d2dRenderTarget) { d2dRenderTarget->SetTextRenderingParams(params.Obj()); } } void StartRendering()override { d2dRenderTarget = GetWindowsDirect2DObjectProvider()->GetNativeWindowDirect2DRenderTarget(window); d2dRenderTarget->BeginDraw(); d2dRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black)); } bool StopRendering()override { auto result = d2dRenderTarget->EndDraw(); bool deviceAvailable = false; if (result == S_OK) { deviceAvailable = GetWindowsDirect2DObjectProvider()->PresentRenderTarget(window); } d2dRenderTarget = 0; return deviceAvailable; } void PushClipper(Rect clipper)override { if(clipperCoverWholeTargetCounter>0) { clipperCoverWholeTargetCounter++; } else { Rect previousClipper=GetClipper(); Rect currentClipper; currentClipper.x1=(previousClipper.x1>clipper.x1?previousClipper.x1:clipper.x1); currentClipper.y1=(previousClipper.y1>clipper.y1?previousClipper.y1:clipper.y1); currentClipper.x2=(previousClipper.x2PushAxisAlignedClip( D2D1::RectF((FLOAT)currentClipper.x1, (FLOAT)currentClipper.y1, (FLOAT)currentClipper.x2, (FLOAT)currentClipper.y2), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE ); } else { clipperCoverWholeTargetCounter++; } } } void PopClipper()override { if(clipperCoverWholeTargetCounter>0) { clipperCoverWholeTargetCounter--; } else if(clippers.Count()>0) { clippers.RemoveAt(clippers.Count()-1); d2dRenderTarget->PopAxisAlignedClip(); } } Rect GetClipper()override { if(clippers.Count()==0) { return Rect(Point(0, 0), window->GetClientSize()); } else { return clippers[clippers.Count()-1]; } } bool IsClipperCoverWholeTarget()override { return clipperCoverWholeTargetCounter>0; } ID2D1SolidColorBrush* CreateDirect2DBrush(Color color)override { return solidBrushes.Create(color).Obj(); } void DestroyDirect2DBrush(Color color)override { solidBrushes.Destroy(color); } ID2D1LinearGradientBrush* CreateDirect2DLinearBrush(Color c1, Color c2)override { return linearBrushes.Create(Pair(c1, c2)).Obj(); } void DestroyDirect2DLinearBrush(Color c1, Color c2)override { linearBrushes.Destroy(Pair(c1, c2)); } }; /*********************************************************************** WindowsGDIResourceManager ***********************************************************************/ class WindowsDirect2DResourceManager : public GuiGraphicsResourceManager, public IWindowsDirect2DResourceManager, public INativeControllerListener { protected: SortedList> renderTargets; Ptr layoutProvider; CachedTextFormatAllocator textFormats; CachedCharMeasurerAllocator charMeasurers; public: WindowsDirect2DResourceManager() { layoutProvider=new WindowsDirect2DLayoutProvider; } IGuiGraphicsRenderTarget* GetRenderTarget(INativeWindow* window)override { return GetWindowsDirect2DObjectProvider()->GetBindedRenderTarget(window); } void RecreateRenderTarget(INativeWindow* window)override { NativeWindowDestroying(window); GetWindowsDirect2DObjectProvider()->RecreateRenderTarget(window); NativeWindowCreated(window); } IGuiGraphicsLayoutProvider* GetLayoutProvider()override { return layoutProvider.Obj(); } void NativeWindowCreated(INativeWindow* window)override { WindowsDirect2DRenderTarget* renderTarget=new WindowsDirect2DRenderTarget(window); renderTargets.Add(renderTarget); GetWindowsDirect2DObjectProvider()->SetBindedRenderTarget(window, renderTarget); } void NativeWindowDestroying(INativeWindow* window)override { WindowsDirect2DRenderTarget* renderTarget=dynamic_cast(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 CreateDirect2DCharMeasurer(const FontProperties& fontProperties)override { return charMeasurers.Create(fontProperties); } void DestroyDirect2DCharMeasurer(const FontProperties& fontProperties)override { charMeasurers.Destroy(fontProperties); } }; } namespace elements_windows_d2d { IWindowsDirect2DResourceManager* windowsDirect2DResourceManager=0; IWindowsDirect2DResourceManager* GetWindowsDirect2DResourceManager() { return windowsDirect2DResourceManager; } void SetWindowsDirect2DResourceManager(IWindowsDirect2DResourceManager* resourceManager) { windowsDirect2DResourceManager=resourceManager; } /*********************************************************************** OS Supporting ***********************************************************************/ IWindowsDirect2DObjectProvider* windowsDirect2DObjectProvider=0; IWindowsDirect2DObjectProvider* GetWindowsDirect2DObjectProvider() { return windowsDirect2DObjectProvider; } void SetWindowsDirect2DObjectProvider(IWindowsDirect2DObjectProvider* provider) { windowsDirect2DObjectProvider=provider; } } } } /*********************************************************************** NativeMain ***********************************************************************/ using namespace vl::presentation; using namespace vl::presentation::elements; void RendererMainDirect2D() { elements_windows_d2d::WindowsDirect2DResourceManager resourceManager; SetGuiGraphicsResourceManager(&resourceManager); elements_windows_d2d::SetWindowsDirect2DResourceManager(&resourceManager); GetCurrentController()->CallbackService()->InstallListener(&resourceManager); elements_windows_d2d::GuiSolidBorderElementRenderer::Register(); elements_windows_d2d::GuiRoundBorderElementRenderer::Register(); elements_windows_d2d::Gui3DBorderElementRenderer::Register(); elements_windows_d2d::Gui3DSplitterElementRenderer::Register(); elements_windows_d2d::GuiSolidBackgroundElementRenderer::Register(); elements_windows_d2d::GuiGradientBackgroundElementRenderer::Register(); elements_windows_d2d::GuiSolidLabelElementRenderer::Register(); elements_windows_d2d::GuiImageFrameElementRenderer::Register(); elements_windows_d2d::GuiPolygonElementRenderer::Register(); elements_windows_d2d::GuiColorizedTextElementRenderer::Register(); elements_windows_d2d::GuiDirect2DElementRenderer::Register(); elements::GuiDocumentElement::GuiDocumentElementRenderer::Register(); GuiApplicationMain(); elements_windows_d2d::SetWindowsDirect2DResourceManager(0); SetGuiGraphicsResourceManager(0); }