summaryrefslogtreecommitdiff
path: root/src/nigui/private/windows
diff options
context:
space:
mode:
authortrustable-code <krauter.simon@arcor.de>2017-07-06 21:55:17 +0200
committertrustable-code <krauter.simon@arcor.de>2017-07-06 21:55:17 +0200
commit911c4e13f379f887b89fd176e5878d4726484744 (patch)
treefb37cd7303da6ad53f4a08b254eedfb6893bdaa9 /src/nigui/private/windows
parentf827a1ac3e3095aec40ca7c270e154bda45c2bf4 (diff)
downloadNiGui-911c4e13f379f887b89fd176e5878d4726484744.tar.gz
NiGui-911c4e13f379f887b89fd176e5878d4726484744.zip
Change directory structure, change include concept, add nimble file
Diffstat (limited to 'src/nigui/private/windows')
-rwxr-xr-xsrc/nigui/private/windows/platform_impl.nim1278
-rwxr-xr-xsrc/nigui/private/windows/platform_types1.nim23
-rwxr-xr-xsrc/nigui/private/windows/platform_types2.nim18
-rwxr-xr-xsrc/nigui/private/windows/windows.nim452
4 files changed, 1771 insertions, 0 deletions
diff --git a/src/nigui/private/windows/platform_impl.nim b/src/nigui/private/windows/platform_impl.nim
new file mode 100755
index 0000000..c4c3010
--- /dev/null
+++ b/src/nigui/private/windows/platform_impl.nim
@@ -0,0 +1,1278 @@
+# NiGui - Win32 platform-specific code - part 3
+
+# This file will be included in "nigui.nim".
+
+# Idendifiers, which are only used in this file, are marked with a leading "p".
+
+# Imports:
+# math, os, strutils, times are imported by nigui.nim
+import windows
+import tables
+
+
+# ----------------------------------------------------------------------------------------
+# Internal Things
+# ----------------------------------------------------------------------------------------
+
+const pTopLevelWindowClass = "1"
+const pContainerWindowClass = "2"
+const pCustomControlWindowClass = "3"
+
+var pDefaultParentWindow: pointer
+var pKeyState: KeyState
+
+# needed to calculate clicks:
+var pLastMouseButtonDownControl: Control
+var pLastMouseButtonDownControlX: int
+var pLastMouseButtonDownControlY: int
+
+proc pRaiseLastOSError(showAlert = true) =
+ let e = osLastError()
+ raiseError(osErrorMsg(e).strip & " (OS Error Code: " & $e & ")", showAlert)
+
+proc pCheckGdiplusStatus(status: int32, showAlert = true) =
+ if status != 0:
+ if status == 7:
+ pRaiseLastOSError(showAlert)
+ else:
+ raiseError("A GDI+ error occured. (Status: " & $status & ")", showAlert)
+
+proc pColorToRGB32(color: Color): RGB32 =
+ result.red = color.red
+ result.green = color.green
+ result.blue = color.blue
+
+proc pRgb32ToColor(color: RGB32): Color =
+ result.red = color.red
+ result.green = color.green
+ result.blue = color.blue
+
+proc pColorToARGB(color: Color): ARGB =
+ result.red = color.red
+ result.green = color.green
+ result.blue = color.blue
+ result.alpha = color.alpha
+
+proc pUtf8ToUtf16(s: string): string =
+ # result is terminated with 2 null bytes
+ if s.len == 0:
+ return "\0"
+ var characters = MultiByteToWideChar(CP_UTF8, 0, s, s.len.int32, nil, 0) # request number of characters
+ if characters == 0: pRaiseLastOSError()
+ result = newString(characters * 2 + 1)
+ characters = MultiByteToWideChar(CP_UTF8, 0, s, s.len.int32, result, characters.int32) # do the conversion
+ result[characters * 2] = '\0'
+ result[characters * 2 + 1] = '\0'
+ if characters == 0: pRaiseLastOSError()
+
+proc pUtf16ToUtf8(s: string, searchEnd = false): string =
+ if s.len == 0:
+ return ""
+ var characters = s.len div 2
+ if searchEnd:
+ # Search end of utf16 string:
+ var i = 0
+ while i < s.len - 1 and s[i].ord != 0:
+ i.inc(2)
+ characters = i div 2
+ var bytes = WideCharToMultiByte(CP_UTF8, 0, s, characters.int32, nil, 0, nil, nil) # request number of bytes
+ if bytes == 0: pRaiseLastOSError()
+ result = newString(bytes)
+ bytes = WideCharToMultiByte(CP_UTF8, 0, s, characters.int32, result, bytes.int32, nil, nil) # do the conversion
+ if bytes == 0: pRaiseLastOSError()
+
+proc pUnicodeCharToUtf8(unicode: int): string =
+ var widestring = newString(2)
+ widestring[0] = chr(unicode mod 256)
+ widestring[1] = chr(unicode div 256)
+ result = widestring.pUtf16ToUtf8
+
+proc pUtf16ToUnicode(s: string): int = s[0].ord + s[1].ord * 256
+
+proc pShowWindow(hWnd: pointer, nCmdShow: int32) = discard ShowWindow(hWnd, nCmdShow)
+
+proc pSetWindowLong(hWnd: pointer, nIndex, dwNewLong: int32) =
+ let result = SetWindowLongA(hWnd, nIndex, dwNewLong)
+ if result == 0: pRaiseLastOSError()
+
+proc pDestroyWindow(hWnd: pointer) =
+ let result = DestroyWindow(hWnd)
+ if not result: pRaiseLastOSError()
+
+proc pSetParent(hWndChild, hWndNewParent: pointer) =
+ let result = SetParent(hWndChild, hWndNewParent)
+ if result == nil: pRaiseLastOSError()
+
+proc pSetWindowText(hWnd: pointer, s: string) =
+ let result = SetWindowTextW(hWnd, s.pUtf8ToUtf16)
+ if not result: pRaiseLastOSError()
+
+proc pGetWindowText(hWnd: pointer): string =
+ let characters = GetWindowTextLengthW(hWnd)
+ result = newString(characters * 2)
+ var res = GetWindowTextW(hWnd, result, characters * 2 + 1)
+ if res != characters: pRaiseLastOSError()
+ result = result.pUtf16ToUtf8
+
+proc pSetWindowPos(wnd: pointer, x, y, cx, cy: int, uFlags: int32 = 0) =
+ var result = SetWindowPos(wnd, nil, x.int32, y.int32, cx.int32, cy.int32, uFlags)
+ if not result: pRaiseLastOSError()
+
+proc pGetClientRect(wnd: pointer): Rect =
+ if not GetClientRect(wnd, result): pRaiseLastOSError()
+
+proc pGetWindowRect(wnd: pointer): Rect =
+ if not GetWindowRect(wnd, result): pRaiseLastOSError()
+
+proc pCreateWindowEx(dwExStyle: int32, lpClassName, lpWindowName: cstring, dwStyle: int32, x, y, nWidth, nHeight: int, hWndParent, hMenu, hInstance, lpParam: pointer): pointer =
+ result = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
+ if result == nil: pRaiseLastOSError()
+
+# proc pGetStockObject(fnObject: int32): pointer =
+ # result = GetStockObject(fnObject)
+ # if result == nil: pRaiseLastOSError()
+
+proc pCreateWindowExWithUserdata(lpClassName: cstring, dwStyle, dwExStyle: int32, hWndParent, userdata: pointer = nil): pointer =
+ result = pCreateWindowEx(dwExStyle, lpClassName, nil, dwStyle, 0, 0, 0, 0, hWndParent, nil, nil, nil)
+ if userdata != nil:
+ discard SetWindowLongPtrW(result, GWLP_USERDATA, userdata)
+ # Set default font:
+ # discard SendMessageA(result, WM_SETFONT, pGetStockObject(DEFAULT_GUI_FONT), cast[pointer](true))
+ # Set window proc:
+ # discard SetWindowLongPtrW(result, GWLP_WNDPROC, pCommonWndProc)
+
+proc pEnableVisualStyles() =
+ # Without this, controls have style of Windows 95
+ const MaxLength = 500
+ var dir = newString(MaxLength)
+ if GetSystemDirectoryA(dir[0].addr, MaxLength) == 0: pRaiseLastOSError()
+ var actCtx: ActCtx
+ actCtx.cbSize = ActCtx.sizeof.int32
+ actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID or ACTCTX_FLAG_SET_PROCESS_DEFAULT or ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID
+ actCtx.lpSource = "shell32.dll"
+ actCtx.wProcessorArchitecture = 0
+ actCtx.wLangId = 0
+ actCtx.lpAssemblyDirectory = dir
+ actCtx.lpResourceName = cast[cstring](124)
+ actCtx.lpApplicationName = nil
+ actCtx.hModule = nil
+ var context = CreateActCtxA(actCtx.addr)
+ if context == INVALID_HANDLE_VALUE: pRaiseLastOSError()
+ # has no effect:
+ # var ulpActivationCookie = false
+ # if not ActivateActCtx(context, ulpActivationCookie.addr): pRaiseLastOSError()
+
+proc pRegisterWindowClass(className: cstring, wndProc: pointer, style: int32 = 0) =
+ var class: WndClassEx
+ class.cbSize = WndClassEx.sizeof.int32
+ class.lpszClassName = className
+ class.lpfnWndProc = wndProc
+ class.style = style
+ class.cbClsExtra = 0
+ class.cbWndExtra = 0
+ class.hInstance = nil
+ class.hIcon = nil
+ class.hCursor = LoadCursorA(nil, cast[cstring](IDC_ARROW))
+ class.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)) # default background
+ class.lpszMenuName = nil
+ class.hIconSm = nil
+ if RegisterClassExA(class) == 0: pRaiseLastOSError()
+
+proc pCommonWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
+ case uMsg
+ of WM_COMMAND:
+ if wParam.hiWord == EN_CHANGE:
+ let control = cast[Control](GetWindowLongPtrW(lParam, GWLP_USERDATA))
+ var evt = new TextChangeEvent
+ control.handleTextChangeEvent(evt)
+ of WM_CTLCOLOREDIT:
+ let control = cast[Control](GetWindowLongPtrW(lParam, GWLP_USERDATA))
+ discard SetTextColor(wParam, control.textColor.pColorToRGB32())
+ # discard SetBkColor(wParam, control.backgroundColor.pColorToRGB32())
+ # does not cover complete background
+ return GetCurrentObject(wParam, OBJ_BRUSH)
+ else:
+ discard
+ result = DefWindowProcA(hWnd, uMsg, wParam, lParam)
+
+proc pKeyvalToKey(keyval: int): Key =
+ case keyval
+ of 48: Key_Number0
+ of 49: Key_Number1
+ of 50: Key_Number2
+ of 51: Key_Number3
+ of 52: Key_Number4
+ of 53: Key_Number5
+ of 54: Key_Number6
+ of 55: Key_Number7
+ of 56: Key_Number8
+ of 57: Key_Number9
+ of 65: Key_A
+ of 66: Key_B
+ of 67: Key_C
+ of 68: Key_D
+ of 69: Key_E
+ of 70: Key_F
+ of 71: Key_G
+ of 72: Key_H
+ of 73: Key_I
+ of 74: Key_J
+ of 75: Key_K
+ of 76: Key_L
+ of 77: Key_M
+ of 78: Key_N
+ of 79: Key_O
+ of 80: Key_P
+ of 81: Key_Q
+ of 82: Key_R
+ of 83: Key_S
+ of 84: Key_T
+ of 85: Key_U
+ of 86: Key_V
+ of 87: Key_W
+ of 88: Key_X
+ of 89: Key_Y
+ of 90: Key_Z
+ of 32: Key_Space
+ of 9: Key_Tab
+ of 13: Key_Return
+ of 27: Key_Escape
+ of 45: Key_Insert
+ of 46: Key_Delete
+ of 8: Key_Backspace
+ of 37: Key_Left
+ of 38: Key_Up
+ of 39: Key_Right
+ of 40: Key_Down
+ of 35: Key_End
+ of 36: Key_Home
+ of 33: Key_PageUp
+ of 34: Key_PageDown
+ else: Key_None
+
+proc pHandleWMKEYDOWN(window: Window, control: Control, wParam, lParam: pointer) =
+ var windowEvent = new WindowKeyEvent
+ windowEvent.window = window
+ windowEvent.key = pKeyvalToKey(cast[int](wParam))
+ if windowEvent.key == Key_None:
+ echo "Unkown key value: ", cast[int](wParam)
+ return
+ if not GetKeyboardState(pKeyState): pRaiseLastOSError()
+ var widestring = newString(2)
+ let scancode = int32((cast[int](lParam) shr 8) and 0xFFFFFF00)
+ let ret = ToUnicode(cast[int](wParam).int32, scancode, pKeyState, widestring, 1, 0)
+ if ret == 1:
+ windowEvent.unicode = widestring.pUtf16ToUnicode
+ windowEvent.character = windowEvent.unicode.pUnicodeCharToUtf8
+ else:
+ windowEvent.character = ""
+ window.handleKeyDownEvent(windowEvent)
+
+
+ # var windowEvent = new WindowKeyEvent
+ # windowEvent.window = window
+ # windowEvent.character = $chr(cast[int](wParam))
+ # windowEvent.key = pKeyvalToKey(cast[int](wParam))
+ # window.handleKeyDownEvent(windowEvent)
+
+ if control != nil:
+ var controlEvent = new ControlKeyEvent
+ controlEvent.control = control
+ controlEvent.key = windowEvent.key
+ controlEvent.unicode = windowEvent.unicode
+ controlEvent.character = windowEvent.character
+ control.handleKeyDownEvent(controlEvent)
+ # if controlEvent.cancel:
+ # return nil # key is still inserted in text area
+
+
+proc pWindowWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.} =
+ case uMsg
+ of WM_CLOSE:
+ let window = cast[WindowImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ window.dispose()
+ return cast[pointer](true) # keeps the window open, else the window will be destroyed
+ of WM_SIZE:
+ let window = cast[WindowImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if window != nil:
+ var rect = pGetWindowRect(window.fHandle)
+ window.width = rect.right - rect.left
+ window.height = rect.bottom - rect.top
+ rect = pGetClientRect(window.fHandle)
+ window.fClientWidth = rect.right - rect.left
+ window.fClientHeight = rect.bottom - rect.top
+ window.triggerRelayout()
+ of WM_MOVE:
+ let window = cast[WindowImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if window != nil:
+ var rect = pGetWindowRect(window.fHandle)
+ window.fX = rect.left
+ window.fY = rect.top
+ # echo "WM_MOVE: " & $rect.left & ", " & $rect.top
+ of WM_SETFOCUS:
+ discard
+ #echo "window WM_SETFOCUS"
+ # not called?
+ of WM_DROPFILES:
+ let window = cast[WindowImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ var files: seq[string] = @[]
+ let count = DragQueryFileW(wParam, 0xFFFFFFFF.uint32, nil, 0)
+ for i in 0..count - 1:
+ let characters = DragQueryFileW(wParam, i.uint32 , nil, 0)
+ var filename = newString(characters * 2)
+ discard DragQueryFileW(wParam, i.uint32, filename, characters + 1)
+ files.add(filename.pUtf16ToUtf8)
+ DragFinish(wParam)
+ var event = new DropFilesEvent
+ event.window = window
+ event.files = files
+ window.handleDropFilesEvent(event)
+ # of WM_CHAR:
+ # not triggered for all key
+ # echo "window WM_CHAR: "
+ # let window = cast[Window](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ # if window != nil:
+ # var unicode = cast[int](wParam)
+ # var event = new WindowKeyEvent
+ # event.window = window
+ # event.unicode = unicode
+ # event.character = unicode.pUnicodeCharToUtf8
+ # echo event.character[0].ord
+ # window.handleKeyDownEvent(event)
+ of WM_KEYDOWN:
+ # echo "window WM_KEYDOWN"
+
+ # if (cast[int](lParam) and 0x40000000) != 0x40000000:
+ # echo "window WM_KEYDOWN first"
+ # else:
+ # echo "window WM_KEYDOWN"
+
+ # echo int((cast[int](lParam) shr 8) and 0xFFFFFF00)
+
+ let window = cast[Window](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if window != nil:
+ pHandleWMKEYDOWN(window, nil, wParam, lParam)
+ else:
+ discard
+ result = pCommonWndProc(hWnd, uMsg, wParam, lParam)
+
+proc pContainerWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.}
+
+proc pCommonControlWndProc(origWndProc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer
+
+proc pCustomControlWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.}
+
+proc pInitGdiplus() =
+ var input: GdiplusStartupInput
+ input.GdiplusVersion = 1
+ var gidplus: pointer = nil
+ pCheckGdiplusStatus(GdiplusStartup(gidplus, input, nil))
+
+proc pGetTextSize(hDC, font: pointer, text: string): Size =
+ let wideText = text.pUtf8ToUtf16
+ discard SelectObject(hdc, font)
+ discard GetTextExtentPoint32W(hdc, wideText, (wideText.len div 2).int32, result)
+
+
+# ----------------------------------------------------------------------------------------
+# App Procedures
+# ----------------------------------------------------------------------------------------
+
+proc init(app: App) =
+ pInitGdiplus()
+ pEnableVisualStyles()
+ pRegisterWindowClass(pTopLevelWindowClass, pWindowWndProc)
+ pRegisterWindowClass(pCustomControlWindowClass, pCustomControlWndProc, CS_HREDRAW or CS_VREDRAW)
+ pRegisterWindowClass(pContainerWindowClass, pContainerWndProc)
+ pDefaultParentWindow = pCreateWindowEx(0, pTopLevelWindowClass, nil, 0, 0, 0, 0, 0, nil, nil, nil, nil)
+ app.defaultTextColor = GetSysColor(COLOR_WINDOWTEXT).pRgb32ToColor()
+ app.defaultBackgroundColor = GetSysColor(COLOR_BTNFACE).pRgb32ToColor()
+ app.defaultFontFamily = "Arial"
+ fScrollbarSize = GetSystemMetrics(SM_CXVSCROLL)
+
+proc runMainLoop() =
+ var msg: Msg
+ while GetMessageA(msg.addr, nil, 0, 0):
+ discard TranslateMessage(msg.addr)
+ discard DispatchMessageA(msg.addr)
+
+proc processEvents(app: App) =
+ var msg: Msg
+ while PeekMessageA(msg.addr, nil, 0, 0, PM_REMOVE):
+ discard TranslateMessage(msg.addr)
+ discard DispatchMessageA(msg.addr)
+
+
+# ----------------------------------------------------------------------------------------
+# Dialogs
+# ----------------------------------------------------------------------------------------
+
+proc alert(window: Window, message: string, title = "Message") =
+ var hWnd: pointer
+ if window != nil:
+ hWnd = cast[WindowImpl](window).fHandle
+ MessageBoxW(hWnd, message.pUtf8ToUtf16, title.pUtf8ToUtf16, 0)
+
+method run*(dialog: OpenFileDialog) =
+ const maxCharacters = 5000
+ dialog.files = @[]
+ var ofn: OpenFileName
+ ofn.lStructSize = OpenFileName.sizeOf.int32
+ ofn.nMaxFile = maxCharacters
+ ofn.lpstrInitialDir = getCurrentDir().pUtf8ToUtf16()
+ ofn.Flags = OFN_FILEMUSTEXIST
+ if dialog.multiple:
+ ofn.Flags = ofn.Flags or OFN_ALLOWMULTISELECT or OFN_EXPLORER
+ var s = newString(maxCharacters * 2)
+ ofn.lpstrFile = s
+ let ret = GetOpenFileNameW(ofn)
+ if ret:
+ var dirOrFirstFile: string
+ # Split selected file names:
+ while s[0].ord != 0:
+ var i = 0
+ while i < s.len - 1 and s[i].ord != 0:
+ i.inc(2)
+ let filename = s.substr(0, i - 1).pUtf16ToUtf8()
+ if dirOrFirstFile == nil:
+ dirOrFirstFile = filename
+ else:
+ dialog.files.add(dirOrFirstFile / filename)
+ s = s.substr(i + 2)
+ if dialog.files.len == 0:
+ dialog.files.add(dirOrFirstFile)
+ else:
+ let e = CommDlgExtendedError()
+ if e != 0:
+ raiseError("CommDlg Error Code: " & $e)
+
+method run(dialog: SaveFileDialog) =
+ const maxCharacters = 500
+ var ofn: OpenFileName
+ ofn.lStructSize = OpenFileName.sizeOf.int32
+ ofn.nMaxFile = maxCharacters
+ ofn.lpstrInitialDir = getCurrentDir().pUtf8ToUtf16()
+ if dialog.defaultExtension.len > 0:
+ ofn.lpstrDefExt = pUtf8ToUtf16(dialog.defaultExtension)
+ ofn.lpstrFilter = pUtf8ToUtf16(dialog.defaultExtension & "\0*." & dialog.defaultExtension & "\0All files\0*.*")
+ ofn.Flags = OFN_OVERWRITEPROMPT
+ var s = newString(maxCharacters * 2)
+ if dialog.defaultName.len > 0:
+ let temp = pUtf8ToUtf16(dialog.defaultName)
+ for i in 0..temp.len:
+ s[i] = temp[i]
+ ofn.lpstrFile = s
+ let ret = GetSaveFileNameW(ofn)
+ if ret:
+ dialog.file = pUtf16ToUtf8(s, true)
+ else:
+ dialog.file = ""
+ let e = CommDlgExtendedError()
+ if e != 0:
+ raiseError("CommDlg Error Code: " & $e)
+
+
+# ----------------------------------------------------------------------------------------
+# Timers
+# ----------------------------------------------------------------------------------------
+
+type TimerEntry = object
+ timerProc: TimerProc
+ data: pointer
+
+var pTimers = initTable[int64, TimerEntry]()
+
+proc pTimerFunction(hwnd: pointer, uMsg: int32, idEvent: pointer, dwTime: int32) {.cdecl.} =
+ discard KillTimer(hwnd, idEvent)
+ let timerEntry = pTimers.getOrDefault(cast[int](idEvent))
+ var event = new TimerEvent
+ event.timer = cast[Timer](idEvent)
+ event.data = timerEntry.data
+ timerEntry.timerProc(event)
+ pTimers.del(cast[int](idEvent))
+
+proc pRepeatingTimerFunction(hwnd: pointer, uMsg: int32, idEvent: pointer, dwTime: int32) {.cdecl.} =
+ let timerEntry = pTimers.getOrDefault(cast[int](idEvent))
+ var event = new TimerEvent
+ event.timer = cast[Timer](idEvent)
+ event.data = timerEntry.data
+ timerEntry.timerProc(event)
+
+proc startTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer =
+ result = cast[Timer](SetTimer(nil, nil, milliSeconds.int32, pTimerFunction))
+ var timerEntry: TimerEntry
+ timerEntry.timerProc = timerProc
+ timerEntry.data = data
+ pTimers[cast[int](result)] = timerEntry
+
+proc startRepeatingTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer =
+ result = cast[Timer](SetTimer(nil, nil, milliSeconds.int32, pRepeatingTimerFunction))
+ var timerEntry: TimerEntry
+ timerEntry.timerProc = timerProc
+ timerEntry.data = data
+ pTimers[cast[int](result)] = timerEntry
+
+proc stop(timer: var Timer) =
+ if cast[int](timer) != inactiveTimer:
+ let timerEntry = pTimers.getOrDefault(cast[int](timer))
+ pTimers.del(cast[int](timer))
+ discard KillTimer(nil, cast[pointer](timer))
+ timer = cast[Timer](inactiveTimer)
+
+
+# ----------------------------------------------------------------------------------------
+# Canvas
+# ----------------------------------------------------------------------------------------
+
+proc pUpdateFont(canvas: Canvas) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fFont == nil:
+ var fontFamily: pointer
+ pCheckGdiplusStatus(GdipCreateFontFamilyFromName(canvas.fontFamily.pUtf8ToUtf16(), nil, fontFamily))
+ pCheckGdiplusStatus(GdipCreateFont(fontFamily, canvas.fontSize.float, 0, UnitPixel, canvasImpl.fFont))
+ pCheckGdiplusStatus(GdipDeleteFontFamily(fontFamily))
+
+proc pDeleteFont(canvas: CanvasImpl) =
+ if canvas.fFont != nil:
+ pCheckGdiplusStatus(GdipDeleteFont(canvas.fFont))
+ canvas.fFont = nil
+
+proc pDeleteFontBrush(canvas: CanvasImpl) =
+ if canvas.fFontBrush != nil:
+ pCheckGdiplusStatus(GdipDeleteBrush(canvas.fFontBrush))
+ canvas.fFontBrush = nil
+
+proc pDeleteAreaBrush(canvas: CanvasImpl) =
+ if canvas.fAreaBrush != nil:
+ pCheckGdiplusStatus(GdipDeleteBrush(canvas.fAreaBrush))
+ canvas.fAreaBrush = nil
+
+proc pDeleteLinePen(canvas: CanvasImpl) =
+ if canvas.fLinePen != nil:
+ pCheckGdiplusStatus(GdipDeletePen(canvas.fLinePen))
+ canvas.fLinePen = nil
+
+method destroy(canvas: CanvasImpl) =
+ procCall canvas.Canvas.destroy()
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fBitmap == nil:
+ pDeleteFont(canvas)
+ pDeleteFontBrush(canvas)
+ pDeleteLinePen(canvas)
+ pDeleteAreaBrush(canvas)
+ if canvas.fGraphics != nil:
+ pCheckGdiplusStatus(GdipDeleteGraphics(canvas.fGraphics))
+
+method drawText(canvas: Canvas, text: string, x, y = 0) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fGraphics == nil:
+ raiseError("Canvas is not in drawing state.")
+ canvas.pUpdateFont()
+ if canvasImpl.fFontBrush == nil:
+ pCheckGdiplusStatus(GdipCreateSolidFill(canvas.textColor.pColorToARGB(), canvasImpl.fFontBrush))
+ var rect: RectF
+ rect.x = x.float
+ rect.y = y.float
+ pCheckGdiplusStatus(GdipDrawString(canvasImpl.fGraphics, text.pUtf8ToUtf16(), -1, canvasImpl.fFont, rect, nil, canvasImpl.fFontBrush))
+
+method drawLine(canvas: Canvas, x1, y1, x2, y2: int) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fGraphics == nil:
+ raiseError("Canvas is not in drawing state.")
+ if canvasImpl.fLinePen == nil:
+ pCheckGdiplusStatus(GdipCreatePen1(canvas.lineColor.pColorToARGB(), 1, UnitPixel, canvasImpl.fLinePen))
+ pCheckGdiplusStatus(GdipDrawLineI(canvasImpl.fGraphics, canvasImpl.fLinePen, x1.int32, y1.int32, x2.int32, y2.int32))
+
+method drawRectArea(canvas: Canvas, x, y, width, height: int) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fGraphics == nil:
+ raiseError("Canvas is not in drawing state.")
+ if canvasImpl.fAreaBrush == nil:
+ pCheckGdiplusStatus(GdipCreateSolidFill(canvas.areaColor.pColorToARGB(), canvasImpl.fAreaBrush))
+ pCheckGdiplusStatus(GdipFillRectangleI(canvasImpl.fGraphics, canvasImpl.fAreaBrush, x.int32, y.int32, width.int32, height.int32))
+
+method drawRectOutline(canvas: Canvas, x, y, width, height: int) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fGraphics == nil:
+ raiseError("Canvas is not in drawing state.")
+ var pen: pointer
+ pCheckGdiplusStatus(GdipCreatePen1(canvas.lineColor.pColorToARGB(), 1, UnitPixel, pen))
+ pCheckGdiplusStatus(GdipDrawRectangleI(canvasImpl.fGraphics, pen, x.int32, y.int32, width.int32, height.int32))
+
+method drawImage(canvas: Canvas, image: Image, x, y = 0, width, height = -1) =
+ var drawWith = image.width
+ var drawHeight = image.height
+ if width != -1:
+ drawWith = width
+ drawHeight = int(drawHeight * drawWith / image.width)
+ if height != -1:
+ drawHeight = height
+ let canvasImpl = cast[CanvasImpl](canvas)
+ let imageCanvas = cast[CanvasImpl](image.canvas)
+ if canvasImpl.fGraphics == nil:
+ raiseError("Canvas is not in drawing state.")
+ pCheckGdiplusStatus(GdipDrawImageRectI(canvasImpl.fGraphics, imageCanvas.fBitmap, x.int32, y.int32, drawWith.int32, drawHeight.int32))
+
+method setPixel(canvas: Canvas, x, y: int, color: Color) =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ if canvasImpl.fBitmap == nil:
+ if canvasImpl.fDC == nil:
+ raiseError("Canvas is not in drawing state.")
+ discard SetPixel(canvasImpl.fDC, x.int32, y.int32, color.pColorToRGB32)
+ else:
+ let imageCanvas = cast[CanvasImpl](canvas)
+ pCheckGdiplusStatus(GdipBitmapSetPixel(imageCanvas.fBitmap, x.int32, y.int32, color.pColorToARGB()))
+
+method `fontFamily=`(canvas: CanvasImpl, fontFamily: string) =
+ procCall canvas.Canvas.`fontFamily=`(fontFamily)
+ canvas.pDeleteFont()
+
+method `fontSize=`(canvas: CanvasImpl, fontSize: int) =
+ procCall canvas.Canvas.`fontSize=`(fontSize)
+ canvas.pDeleteFont()
+
+method `textColor=`(canvas: CanvasImpl, color: Color) =
+ procCall canvas.Canvas.`textColor=`(color)
+ canvas.pDeleteFontBrush()
+
+method `lineColor=`(canvas: CanvasImpl, color: Color) =
+ procCall canvas.Canvas.`lineColor=`(color)
+ pDeleteLinePen(canvas)
+
+method `areaColor=`(canvas: CanvasImpl, color: Color) =
+ procCall canvas.Canvas.`areaColor=`(color)
+ pDeleteAreaBrush(canvas)
+
+proc pGetTextSize(canvas: Canvas, text: string): Size =
+ let canvasImpl = cast[CanvasImpl](canvas)
+ canvas.pUpdateFont()
+ var rect: RectF
+ var boundingBox: RectF
+ pCheckGdiplusStatus(GdipMeasureString(canvasImpl.fGraphics, text.pUtf8ToUtf16(), -1, canvasImpl.fFont, rect, nil, boundingBox, nil, nil))
+ result.cx = boundingBox.width.int32
+ result.cy = boundingBox.height.int32
+
+method getTextLineWidth(canvas: CanvasImpl, text: string): int = canvas.pGetTextSize(text).cx
+
+method getTextLineHeight(canvas: CanvasImpl): int = canvas.pGetTextSize("a").cy
+
+
+# ----------------------------------------------------------------------------------------
+# Image
+# ----------------------------------------------------------------------------------------
+
+method resize(image: Image, width, height: int) =
+ let canvas = cast[CanvasImpl](image.canvas)
+ if canvas.fBitmap != nil:
+ pCheckGdiplusStatus(GdipDisposeImage(canvas.fBitmap), false)
+ pCheckGdiplusStatus(GdipDeleteGraphics(canvas.fGraphics))
+ canvas.fBitmap = nil
+ canvas.fGraphics = nil
+ var dc = CreateCompatibleDC(nil)
+ pCheckGdiplusStatus(GdipCreateFromHDC(dc, canvas.fGraphics))
+ pCheckGdiplusStatus(GdipCreateBitmapFromGraphics(width.int32, height.int32, canvas.fGraphics, canvas.fBitmap))
+ pCheckGdiplusStatus(GdipGetImageGraphicsContext(canvas.fBitmap, canvas.fGraphics)) # it's a new Graphic
+ image.canvas.fWidth = width
+ image.canvas.fHeight = height
+
+method loadFromFile(image: Image, filePath: string) =
+ let canvas = cast[CanvasImpl](image.canvas)
+ if canvas.fBitmap != nil:
+ pCheckGdiplusStatus(GdipDisposeImage(canvas.fBitmap), false)
+ pCheckGdiplusStatus(GdipDeleteGraphics(canvas.fGraphics))
+ canvas.fBitmap = nil
+ canvas.fGraphics = nil
+ pCheckGdiplusStatus(GdipCreateBitmapFromFile(filePath.pUtf8ToUtf16(), canvas.fBitmap), false)
+ pCheckGdiplusStatus(GdipGetImageGraphicsContext(canvas.fBitmap, canvas.fGraphics))
+ var width, height: int32
+ pCheckGdiplusStatus(GdipGetImageWidth(canvas.fBitmap, width))
+ pCheckGdiplusStatus(GdipGetImageHeight(canvas.fBitmap, height))
+ image.canvas.fWidth = width
+ image.canvas.fHeight = height
+
+method saveToPngFile(image: Image, filePath: string) =
+ let canvas = cast[CanvasImpl](image.canvas)
+ var clsidEncoder: GUID
+ clsidEncoder.Data1 = 0x557cf406
+ clsidEncoder.Data2 = 0x11d31a04
+ clsidEncoder.Data3 = 0x0000739a
+ clsidEncoder.Data4 = 0x2ef31ef8
+ pCheckGdiplusStatus(GdipSaveImageToFile(canvas.fBitmap, filePath.pUtf8ToUtf16(), clsidEncoder.addr, nil), false)
+
+method saveToJpegFile(image: Image, filePath: string, quality = 80) =
+ let canvas = cast[CanvasImpl](image.canvas)
+ var clsidEncoder: GUID
+ clsidEncoder.Data1 = 0x557cf401
+ clsidEncoder.Data2 = 0x11d31a04
+ clsidEncoder.Data3 = 0x0000739a
+ clsidEncoder.Data4 = 0x2ef31ef8
+ # TODO: pass quality
+ pCheckGdiplusStatus(GdipSaveImageToFile(canvas.fBitmap, filePath.pUtf8ToUtf16(), clsidEncoder.addr, nil), false)
+
+
+# ----------------------------------------------------------------------------------------
+# Window
+# ----------------------------------------------------------------------------------------
+
+proc init(window: WindowImpl) =
+ var dwStyle: int32 = WS_OVERLAPPEDWINDOW
+ window.fHandle = pCreateWindowExWithUserdata(pTopLevelWindowClass, dwStyle, 0, nil, cast[pointer](window))
+ DragAcceptFiles(window.fHandle, true)
+ window.Window.init()
+
+method destroy(window: WindowImpl) =
+ if window.fModalParent != nil:
+ discard EnableWindow(window.fModalParent.fHandle, true)
+ procCall window.Window.destroy()
+ pDestroyWindow(window.fHandle)
+ window.fHandle = nil
+
+method `visible=`(window: WindowImpl, visible: bool) =
+ procCall window.Window.`visible=`(visible)
+ if visible:
+ pShowWindow(window.fHandle, SW_SHOW)
+ else:
+ pShowWindow(window.fHandle, SW_HIDE)
+
+method showModal(window, parent: WindowImpl) =
+ # Set window owner, to hide it from the taskbar
+ discard SetWindowLongPtrW(window.fHandle, GWL_HWNDPARENT, parent.fHandle)
+
+ # Hide minimize and maximize buttons:
+ pSetWindowLong(window.fHandle, GWL_STYLE, WS_CAPTION or WS_THICKFRAME or WS_SYSMENU)
+ # pSetWindowLong(window.fHandle, GWL_EXSTYLE, WS_EX_TOOLWINDOW) # does not look good
+
+ window.fModalParent = parent
+ window.visible = true
+ discard EnableWindow(parent.fHandle, false)
+
+proc pUpdatePosition(window: WindowImpl) =
+ pSetWindowPos(window.fHandle, window.x, window.y, -1, -1, SWP_NOSIZE)
+ # discard MoveWindow(window.fHandle, window.x.int32, window.y.int32, window.width.int32, window.height.int32, false)
+ # no difference
+
+proc pUpdateSize(window: WindowImpl) = pSetWindowPos(window.fHandle, -1, -1, window.width, window.height, SWP_NOMOVE)
+
+method `x=`(window: WindowImpl, x: int) =
+ procCall window.Window.`x=`(x)
+ window.pUpdatePosition()
+
+method `y=`(window: WindowImpl, y: int) =
+ procCall window.Window.`y=`(y)
+ window.pUpdatePosition()
+
+method centerOnScreen(window: WindowImpl) =
+ let desktop = GetDesktopWindow()
+ var rect: Rect
+ discard SystemParametersInfoA(SPI_GETWORKAREA, 0, rect.addr, 0)
+ window.fX = rect.left + (rect.right - window.width) div 2
+ window.fY = rect.top + (rect.bottom - window.height) div 2
+ window.pUpdatePosition()
+
+ # TODO: regard multiple monitors
+ # var m = MonitorFromRect(rect, 0)
+ # var mi: MonitorInfo
+ # discard GetMonitorInfoA(m, mi)
+ # echo "GetMonitorInfoA: " & $mi.rcMonitor.left
+ # echo "GetMonitorInfoA: " & $mi.rcWork.left
+
+method `width=`*(window: WindowImpl, width: int) =
+ procCall window.Window.`width=`(width)
+ window.pUpdateSize()
+
+method `height=`*(window: WindowImpl, height: int) =
+ procCall window.Window.`height=`(height)
+ window.pUpdateSize()
+
+method `title=`(window: WindowImpl, title: string) =
+ procCall window.Window.`title=`(title)
+ pSetWindowText(window.fHandle, window.title)
+
+method `control=`(window: WindowImpl, control: ControlImpl) =
+ if window.control != nil:
+ pSetParent(cast[ControlImpl](window.control).fHandle, pDefaultParentWindow)
+ window.control.fParentWindow = nil
+ procCall window.Window.`control=`(control)
+ pSetParent(control.fHandle, window.fHandle)
+
+method `iconPath=`(window: WindowImpl, iconPath: string) =
+ procCall window.Window.`iconPath=`(iconPath)
+ var bitmap: pointer
+ pCheckGdiplusStatus(GdipCreateBitmapFromFile(iconPath.pUtf8ToUtf16(), bitmap))
+ var icon: pointer
+ pCheckGdiplusStatus(GdipGetHicon(bitmap, icon))
+ discard SendMessageA(window.fHandle, WM_SETICON, cast[pointer](ICON_BIG), icon)
+ discard SendMessageA(window.fHandle, WM_SETICON, cast[pointer](ICON_SMALL), icon)
+
+
+# ----------------------------------------------------------------------------------------
+# Control
+# ----------------------------------------------------------------------------------------
+
+method pUpdateScrollBar(control: ControlImpl)
+
+proc init(control: ControlImpl) =
+ if control.fHandle == nil:
+ var dwStyle: int32 = WS_CHILD
+ control.fHandle = pCreateWindowExWithUserdata(pCustomControlWindowClass, dwStyle, 0, pDefaultParentWindow, cast[pointer](control))
+ procCall control.Control.init()
+
+method destroy(control: ControlImpl) =
+ procCall control.Control.destroy()
+ if control.canvas != nil:
+ control.canvas.destroy()
+ pDestroyWindow(control.fHandle)
+
+method `visible=`(control: ControlImpl, visible: bool) =
+ procCall control.Control.`visible=`(visible)
+ if visible:
+ pShowWindow(control.fHandle, SW_SHOW)
+ else:
+ pShowWindow(control.fHandle, SW_HIDE)
+
+method setSize(control: ControlImpl, width, height: int) =
+ procCall control.Control.setSize(width, height)
+ pSetWindowPos(control.fHandle, -1, -1, width, height, SWP_NOMOVE)
+ pUpdateScrollBar(control)
+
+method setPosition(control: ControlImpl, x, y: int) =
+ procCall control.Control.setPosition(x, y)
+ pSetWindowPos(control.fHandle, x, y, -1, -1, SWP_NOSIZE)
+
+method pUpdateScrollBar(control: ControlImpl) =
+ if control.fScrollableWidth == -1 and control.fScrollableHeight == -1:
+ return
+ # echo "control.pUpdateScrollBar " & control.tag
+
+ # Calculation of scrollbar settings:
+
+ control.fXScrollEnabled = false
+ control.fYScrollEnabled = false
+
+ if control.scrollableWidth > control.width:
+ control.fXScrollEnabled = true
+ if control.scrollableHeight > control.height:
+ control.fYScrollEnabled = true
+
+ if control.fXScrollEnabled and not control.fYScrollEnabled and control.scrollableHeight > control.height - fScrollbarSize:
+ control.fYScrollEnabled = true
+ if control.fYScrollEnabled and not control.fXScrollEnabled and control.scrollableWidth > control.width - fScrollbarSize:
+ control.fXScrollEnabled = true
+
+ # Apply settings:
+
+ discard ShowScrollBar(control.fHandle, SB_HORZ, control.fXScrollEnabled)
+ if control.fXScrollEnabled:
+ var si: ScrollInfo
+ si.cbSize = ScrollInfo.sizeOf.int32
+ si.fMask = SIF_ALL
+ si.nMin = 0
+ si.nMax = control.fScrollableWidth.int32
+ if control.fYScrollEnabled:
+ si.nMax.inc(fScrollbarSize)
+ si.nPage = control.width.int32
+ si.nPos = control.fXScrollPos.int32
+ si.nTrackPos = 0
+ discard SetScrollInfo(control.fHandle, SB_HORZ, si, false)
+ # Ensure that scroll pos is within range:
+ control.fXScrollPos = max(min(control.fXScrollPos, si.nMax - control.width), 0)
+ else:
+ control.fXScrollPos = 0
+
+ discard ShowScrollBar(control.fHandle, SB_VERT, control.fYScrollEnabled)
+ if control.fYScrollEnabled:
+ var si: ScrollInfo
+ si.cbSize = ScrollInfo.sizeOf.int32
+ si.fMask = SIF_ALL
+ si.nMin = 0
+ si.nMax = control.fScrollableHeight.int32
+ if control.fXScrollEnabled:
+ si.nMax.inc(fScrollbarSize)
+ si.nPage = control.height.int32
+ si.nPos = control.fYScrollPos.int32
+ si.nTrackPos = 0
+ discard SetScrollInfo(control.fHandle, SB_VERT, si, false)
+ # Ensure that scroll pos is within range:
+ control.fYScrollPos = max(min(control.fYScrollPos, si.nMax - control.height), 0)
+ else:
+ control.fYScrollPos = 0
+
+method `xScrollPos=`(control: ControlImpl, xScrollPos: int) =
+ procCall control.Control.`xScrollPos=`(xScrollPos)
+ control.pUpdateScrollBar()
+ control.forceRedraw()
+
+method `yScrollPos=`(control: ControlImpl, yScrollPos: int) =
+ procCall control.Control.`yScrollPos=`(yScrollPos)
+ control.pUpdateScrollBar()
+ control.forceRedraw()
+
+method `scrollableWidth=`(control: ControlImpl, scrollableWidth: int) =
+ procCall control.Control.`scrollableWidth=`(scrollableWidth)
+ control.pUpdateScrollBar()
+
+method `scrollableHeight=`(control: ControlImpl, scrollableHeight: int) =
+ procCall control.Control.`scrollableHeight=`(scrollableHeight)
+ control.pUpdateScrollBar()
+
+method forceRedraw(control: ControlImpl) = discard InvalidateRect(control.fHandle, nil, true)
+
+proc pUpdateFont(control: ControlImpl) =
+ if control.fFont != nil:
+ discard DeleteObject(control.fFont)
+ control.fFont = CreateFontA(control.fontSize.int32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, control.fontFamily)
+ discard SendMessageA(control.fHandle, WM_SETFONT, control.fFont, cast[pointer](true))
+
+method setFontFamily(control: ControlImpl, fontFamily: string) =
+ procCall control.Control.setFontFamily(fontFamily)
+ control.pUpdateFont()
+
+method setFontSize(control: ControlImpl, fontSize: int) =
+ procCall control.Control.setFontSize(fontSize)
+ control.pUpdateFont()
+
+# method `setBackgroundColor=`(control: ControlImpl, color: Color) =
+ # procCall control.Control.setBackgroundColor(color)
+ # var brush = CreateSolidBrush(color.pColorToRGB32())
+ # discard SetClassLongPtrA(control.fHandle, GCLP_HBRBACKGROUND, brush)
+ # no effect
+
+proc pGetTextSize(control: ControlImpl, text: string): Size =
+ let hdc = GetDC(control.fHandle)
+ result = pGetTextSize(hdc, control.fFont, text)
+ discard DeleteDC(hdc)
+
+method focus(control: ControlImpl) =
+ discard SetFocus(control.fHandle)
+
+method getTextLineWidth(control: ControlImpl, text: string): int = control.pGetTextSize(text).cx
+
+method getTextLineHeight(control: ControlImpl): int = control.pGetTextSize("a").cy
+
+proc pCommonControlWndProc_Scroll(origWndProc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
+ const lineSize = 15
+ case wParam.loWord
+ of SB_THUMBPOSITION, SB_THUMBTRACK:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ if uMsg == WM_HSCROLL:
+ control.xScrollPos = wParam.hiWord
+ else:
+ control.yScrollPos = wParam.hiWord
+ of SB_LINELEFT:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if uMsg == WM_HSCROLL:
+ control.xScrollPos = control.xScrollPos - lineSize
+ else:
+ control.yScrollPos = control.yScrollPos - lineSize
+ of SB_PAGELEFT:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if uMsg == WM_HSCROLL:
+ control.xScrollPos = control.xScrollPos - control.width
+ else:
+ control.yScrollPos = control.yScrollPos - control.height
+ of SB_LINERIGHT:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if uMsg == WM_HSCROLL:
+ control.xScrollPos = control.xScrollPos + lineSize
+ else:
+ control.yScrollPos = control.yScrollPos + lineSize
+ of SB_PAGERIGHT:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if uMsg == WM_HSCROLL:
+ control.xScrollPos = control.xScrollPos + control.width
+ else:
+ control.yScrollPos = control.yScrollPos + control.height
+ else:
+ discard
+
+proc pCommonControlWndProc(origWndProc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
+ case uMsg
+ of WM_KEYDOWN:
+ let control = cast[Control](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ # echo "control WM_KEYDOWN"
+ pHandleWMKEYDOWN(control.parentWindow, control, wParam, lParam)
+
+ # of WM_KEYUP:
+ # return nil # key is still inserted in text area
+
+ of WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN:
+ let control = cast[Control](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ discard SetFocus(control.parentWindow.fHandle)
+ # TODO: request if is focusable
+ var button: MouseButton
+ var x = loWord(lParam)
+ var y = hiWord(lParam)
+ case uMsg
+ of WM_LBUTTONDOWN: button = MouseButton_Left
+ of WM_RBUTTONDOWN: button = MouseButton_Right
+ of WM_MBUTTONDOWN: button = MouseButton_Middle
+ else: discard
+ var event = new MouseButtonEvent
+ event.control = control
+ event.button = button
+ event.x = x
+ event.y = y
+ control.handleMouseButtonDownEvent(event)
+ pLastMouseButtonDownControl = control
+ pLastMouseButtonDownControlX = x
+ pLastMouseButtonDownControlY = y
+ of WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP:
+ let control = cast[Control](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ var button: MouseButton
+ var x = loWord(lParam)
+ var y = hiWord(lParam)
+ if x >= 0 and x < control.width and y >= 0 and y < control.height:
+ # send event only, when mouse was over control
+ case uMsg
+ of WM_LBUTTONUP: button = MouseButton_Left
+ of WM_RBUTTONUP: button = MouseButton_Right
+ of WM_MBUTTONUP: button = MouseButton_Middle
+ else: discard
+ var event = new MouseButtonEvent
+ event.control = control
+ event.button = button
+ event.x = x
+ event.y = y
+ control.handleMouseButtonUpEvent(event)
+ if uMsg == WM_LBUTTONUP and control == pLastMouseButtonDownControl and abs(x - pLastMouseButtonDownControlX) <= clickMaxXYMove and abs(y - pLastMouseButtonDownControlY) <= clickMaxXYMove:
+ var clickEvent = new ClickEvent
+ clickEvent.control = control
+ control.handleClickEvent(clickEvent)
+ of WM_HSCROLL, WM_VSCROLL:
+ result = pCommonControlWndProc_Scroll(origWndProc, hWnd, uMsg, wParam, lParam)
+ else:
+ discard
+ result = CallWindowProcW(origWndProc, hWnd, uMsg, wParam, lParam)
+
+proc pCustomControlWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
+ case uMsg
+ of WM_PAINT:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ var ps: PaintStruct
+ var dc = BeginPaint(hWnd, ps)
+ if dc == nil: pRaiseLastOSError()
+ var event = new DrawEvent
+ event.control = control
+ var canvas = cast[CanvasImpl](control.canvas)
+ if canvas == nil:
+ canvas = newCanvas(control)
+ else:
+ if canvas.fFont != nil:
+ discard SelectObject(dc, canvas.fFont)
+ canvas.fDC = dc
+ pCheckGdiplusStatus(GdipCreateFromHDC(dc, canvas.fGraphics))
+ discard SetBkMode(dc, TRANSPARENT)
+ control.handleDrawEvent(event)
+ discard EndPaint(hWnd, ps)
+ canvas.fDC = nil
+ canvas.fGraphics = nil
+ of WM_MOUSEWHEEL:
+ let scrolled = wParam.hiWord div 120
+ echo "wheel: " & $scrolled
+ # of WM_ERASEBKGND: # no effect
+ # return false
+ of WM_SETFOCUS:
+ # echo "control WM_SETFOCUS"
+ discard
+ else:
+ discard
+ result = pCommonControlWndProc(pCommonWndProc, hWnd, uMsg, wParam, lParam)
+
+
+# ----------------------------------------------------------------------------------------
+# Container
+# ----------------------------------------------------------------------------------------
+
+proc pContainerWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer =
+ case uMsg
+ of WM_ERASEBKGND:
+ let control = cast[ControlImpl](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ if control != nil:
+ var brush = CreateSolidBrush(control.backgroundColor.pColorToRGB32)
+ var rect = pGetClientRect(control.fHandle)
+ discard FillRect(wParam, rect, brush)
+ return
+ else:
+ discard
+ result = pCustomControlWndProc(hWnd, uMsg, wParam, lParam)
+
+proc init(container: ContainerImpl) =
+ var dwStyle: int32 = WS_CHILD
+ container.fHandle = pCreateWindowExWithUserdata(pContainerWindowClass, dwStyle, 0, pDefaultParentWindow, cast[pointer](container))
+ # ScrollWnd:
+ container.fScrollWndHandle = pCreateWindowExWithUserdata(pContainerWindowClass, dwStyle, 0, container.fHandle, cast[pointer](container))
+ pShowWindow(container.fScrollWndHandle, SW_SHOW)
+ # Inner:
+ container.fInnerHandle = pCreateWindowExWithUserdata(pContainerWindowClass, dwStyle, 0, container.fScrollWndHandle, cast[pointer](container))
+ pShowWindow(container.fInnerHandle, SW_SHOW)
+ container.Container.init()
+
+proc pUpdateScrollWnd(container: ContainerImpl) =
+ let padding = container.getPadding()
+ let width = container.width - padding.left - padding.right
+ let height = container.height - padding.top - padding.bottom
+ pSetWindowPos(container.fScrollWndHandle, padding.left, padding.top, width, height)
+
+method `frame=`(container: ContainerImpl, frame: Frame) =
+ procCall container.Container.`frame=`(frame)
+ if frame != nil:
+ pSetParent(frame.fHandle, container.fHandle)
+ container.pUpdateScrollWnd()
+
+method add(container: ContainerImpl, control: ControlImpl) =
+ procCall container.Container.add(control)
+ pSetParent(control.fHandle, container.fInnerHandle)
+
+method remove(container: ContainerImpl, control: ControlImpl) =
+ procCall container.Container.remove(control)
+ pSetParent(control.fHandle, pDefaultParentWindow)
+
+method setInnerSize(container: ContainerImpl, width, height: int) =
+ procCall container.Container.setInnerSize(width, height)
+ pSetWindowPos(container.fInnerHandle, -1, -1, width, height, SWP_NOMOVE)
+
+method setSize(container: ContainerImpl, width, height: int) =
+ procCall container.Container.setSize(width, height)
+ container.pUpdateScrollWnd()
+
+proc pSetInnerPos(container: ContainerImpl) =
+ pSetWindowPos(container.fInnerHandle, -container.xScrollPos, -container.yScrollPos, -1, -1, SWP_NOSIZE)
+
+method `xScrollPos=`(container: ContainerImpl, xScrollPos: int) =
+ procCall container.ControlImpl.`xScrollPos=`(xScrollPos)
+ container.pSetInnerPos()
+
+method `yScrollPos=`(container: ContainerImpl, yScrollPos: int) =
+ procCall container.ControlImpl.`yScrollPos=`(yScrollPos)
+ container.pSetInnerPos()
+
+
+# ----------------------------------------------------------------------------------------
+# Frame
+# ----------------------------------------------------------------------------------------
+
+proc init(frame: NativeFrame) =
+ const dwStyle = WS_CHILD or BS_GROUPBOX or WS_GROUP
+ frame.fHandle = pCreateWindowExWithUserdata("BUTTON", dwStyle, 0, pDefaultParentWindow, cast[pointer](frame))
+ frame.Frame.init()
+
+method `text=`(frame: NativeFrame, text: string) =
+ procCall frame.Frame.`text=`(text)
+ pSetWindowText(frame.fHandle, text)
+
+method naturalWidth(frame: NativeFrame): int = frame.getTextLineWidth(frame.text) + 10
+
+method getPadding(frame: NativeFrame): Spacing =
+ result = procCall frame.Frame.getPadding()
+ result.top = frame.getTextLineHeight() * frame.text.countLines + 2
+
+
+# ----------------------------------------------------------------------------------------
+# Button
+# ----------------------------------------------------------------------------------------
+
+var pButtonOrigWndProc: pointer
+
+proc pButtonWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.} =
+ case uMsg
+ of WM_KEYDOWN:
+ let button = cast[Button](GetWindowLongPtrW(hWnd, GWLP_USERDATA))
+ # if button != nil and (cast[int](wParam) == 13 or cast[int](wParam) == 32):
+ if button != nil and cast[int](wParam) == 13:
+ var event = new ClickEvent
+ event.control = button
+ button.handleClickEvent(event)
+ else:
+ discard
+ result = pCommonControlWndProc(pButtonOrigWndProc, hWnd, uMsg, wParam, lParam)
+
+proc init(button: NativeButton) =
+ button.fHandle = pCreateWindowExWithUserdata("BUTTON", WS_CHILD or WS_TABSTOP, 0, pDefaultParentWindow, cast[pointer](button))
+ # WS_TABSTOP does not work, why?
+ pButtonOrigWndProc = SetWindowLongPtrW(button.fHandle, GWLP_WNDPROC, pButtonWndProc)
+ button.Button.init()
+
+method `text=`(button: NativeButton, text: string) =
+ procCall button.Button.`text=`(text)
+ pSetWindowText(button.fHandle, text)
+
+
+# ----------------------------------------------------------------------------------------
+# Label
+# ----------------------------------------------------------------------------------------
+
+proc init(label: NativeLabel) =
+ label.fHandle = pCreateWindowExWithUserdata("STATIC", WS_CHILD or SS_CENTERIMAGE, 0, pDefaultParentWindow, cast[pointer](label))
+ label.Label.init()
+
+method `text=`(label: NativeLabel, text: string) =
+ procCall label.Label.`text=`(text)
+ pSetWindowText(label.fHandle, text)
+
+
+# ----------------------------------------------------------------------------------------
+# TextBox
+# ----------------------------------------------------------------------------------------
+
+var pTextBoxOrigWndProc: pointer
+
+proc pTextBoxWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.} =
+ result = pCommonControlWndProc(pTextBoxOrigWndProc, hWnd, uMsg, wParam, lParam)
+
+proc init(textBox: NativeTextBox) =
+ textBox.fHandle = pCreateWindowExWithUserdata("EDIT", WS_CHILD, WS_EX_CLIENTEDGE, pDefaultParentWindow, cast[pointer](textBox))
+ pTextBoxOrigWndProc = SetWindowLongPtrW(textBox.fHandle, GWLP_WNDPROC, pTextBoxWndProc)
+ textBox.TextBox.init()
+
+method text(textBox: NativeTextBox): string = pGetWindowText(textBox.fHandle)
+
+method `text=`(textBox: NativeTextBox, text: string) = pSetWindowText(textBox.fHandle, text)
+
+method naturalHeight(textBox: NativeTextBox): int = textBox.getTextLineHeight() + 9 # add padding
+
+
+# ----------------------------------------------------------------------------------------
+# TextArea
+# ----------------------------------------------------------------------------------------
+
+var pTextAreaOrigWndProc: pointer
+
+proc pTextAreaWndProc(hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.cdecl.} =
+ # Handle Ctrl+A:
+ # TODO: Move this to handleKeyDownEvent(), so it's overridable by the control
+ if uMsg == WM_KEYDOWN and cast[char](wParam) == 'A' and GetKeyState(VK_CONTROL) <= -127:
+ discard SendMessageA(hwnd, EM_SETSEL, nil, cast[pointer](-1))
+ return nil
+ result = pCommonControlWndProc(pTextAreaOrigWndProc, hWnd, uMsg, wParam, lParam)
+
+proc init(textArea: NativeTextArea) =
+ var dwStyle: int32 = WS_CHILD or ES_MULTILINE or WS_VSCROLL # with wrap
+ # var dwStyle: int32 = WS_CHILD or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL # no wrap
+ var dwExStyle: int32 = WS_EX_CLIENTEDGE
+ textArea.fHandle = pCreateWindowExWithUserdata("EDIT", dwStyle, dwExStyle, pDefaultParentWindow, cast[pointer](textArea))
+ pTextAreaOrigWndProc = SetWindowLongPtrW(textArea.fHandle, GWLP_WNDPROC, pTextAreaWndProc)
+ textArea.TextArea.init()
+
+method text(textArea: NativeTextArea): string = pGetWindowText(textArea.fHandle)
+
+method `text=`(textArea: NativeTextArea, text: string) = pSetWindowText(textArea.fHandle, text)
+
+method scrollToBottom(textArea: NativeTextArea) =
+ # select all
+ discard SendMessageA(textArea.fHandle, EM_SETSEL, nil, cast[pointer](-1))
+ # unselect and stay at the end pos
+ discard SendMessageA(textArea.fHandle, EM_SETSEL, cast[pointer](-1), cast[pointer](-1))
+ # set scrollcaret to the current pos
+ discard SendMessageA(textArea.fHandle, EM_SCROLLCARET, nil, nil)
+
+method `wrap=`(textArea: NativeTextArea, wrap: bool) =
+ procCall textArea.TextArea.`wrap=`(wrap)
+ # TODO: allow to enable/disable word draw at runtime
+ # It seems that this is not possible.
+ # Word wrap depends on whether dwStyle contains WS_HSCROLL at window creation.
+ # Changing the style later has not the wanted effect.
diff --git a/src/nigui/private/windows/platform_types1.nim b/src/nigui/private/windows/platform_types1.nim
new file mode 100755
index 0000000..ddf36cf
--- /dev/null
+++ b/src/nigui/private/windows/platform_types1.nim
@@ -0,0 +1,23 @@
+# NiGui - Win32 platform-specific code - part 1
+
+# This file will be included in "nigui.nim".
+
+type
+ WindowImpl* = ref object of Window
+ fHandle: pointer
+ fModalParent: WindowImpl
+
+ ControlImpl* = ref object of Control
+ fHandle: pointer
+ fFont: pointer
+
+ CanvasImpl* = ref object of Canvas
+ fDC: pointer
+ fBitmap: pointer
+ fGraphics: pointer
+ fFont: pointer
+ fFontBrush: pointer
+ fLinePen: pointer
+ fAreaBrush: pointer
+
+ ImageImpl* = ref object of Image
diff --git a/src/nigui/private/windows/platform_types2.nim b/src/nigui/private/windows/platform_types2.nim
new file mode 100755
index 0000000..bbe7caf
--- /dev/null
+++ b/src/nigui/private/windows/platform_types2.nim
@@ -0,0 +1,18 @@
+# NiGui - Win32 platform-specific code - part 2
+
+# This file will be included in "nigui.nim".
+
+type
+ ContainerImpl* = ref object of Container
+ fScrollWndHandle: pointer
+ fInnerHandle: pointer
+
+ NativeFrame* = ref object of Frame
+
+ NativeButton* = ref object of Button
+
+ NativeLabel* = ref object of Label
+
+ NativeTextBox* = ref object of TextBox
+
+ NativeTextArea* = ref object of TextArea
diff --git a/src/nigui/private/windows/windows.nim b/src/nigui/private/windows/windows.nim
new file mode 100755
index 0000000..543985a
--- /dev/null
+++ b/src/nigui/private/windows/windows.nim
@@ -0,0 +1,452 @@
+# NiGui - minimal Win32 binding
+
+# Some functions requires Windows XP or newer.
+# Windows type names are replaced with basic types.
+
+# Type aliases for int16:
+# ATOM, SHORT, USHORT, LANGID
+
+# Type aliases for int32:
+# int, UINT, WINUINT, DWORD, LONG, COLORREF
+
+# Type aliases for int:
+# WPARAM, LPARAM, ULONG
+
+# Type aliases for pointer:
+# WNDPROC, HINSTANCE, HICON, HCURSOR, HBRUSH, HWND, LPMSG, LRESULT, PACTCTX, HMODULE, HDC, HGDIOBJ, HFONT, HMONITOR, HGDIOBJ
+
+# Type aliases for cstring:
+# LPCTSTR, LPCWSTR
+
+{.pragma: libUser32, stdcall, dynlib: "User32.dll".}
+{.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".}
+{.pragma: libGdi32, stdcall, dynlib: "Gdi32.dll".}
+{.pragma: libShell32, stdcall, dynlib: "Shell32.dll".}
+{.pragma: libGdiplus, stdcall, dynlib: "Gdiplus.dll".}
+{.pragma: libComdlg32, stdcall, dynlib: "Comdlg32.dll".}
+
+
+# ----------------------------------------------------------------------------------------
+# Constants
+# ----------------------------------------------------------------------------------------
+
+const
+ ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID* = 4
+ ACTCTX_FLAG_RESOURCE_NAME_VALID* = 8
+ ACTCTX_FLAG_SET_PROCESS_DEFAULT* = 16
+ BN_CLICKED* = 0
+ BM_SETIMAGE* = 247
+ BS_GROUPBOX* = 0x00000007
+ COLOR_BTNFACE* = 15
+ COLOR_WINDOWTEXT* = 8
+ CP_UTF8* = 65001
+ CS_HREDRAW* = 2
+ CS_VREDRAW* = 1
+ CW_USEDEFAULT* = 0x80000000.int
+ DEFAULT_GUI_FONT* = 17
+ EM_SCROLLCARET* = 183
+ EM_SETSEL* = 177
+ EN_CHANGE* = 768
+ ES_MULTILINE* = 4
+ GCLP_HBRBACKGROUND* = -10
+ GWL_EXSTYLE* = -20
+ GWL_HINSTANCE* = -6
+ GWL_HWNDPARENT* = -8
+ GWL_STYLE* = -16
+ GWLP_USERDATA* = -21
+ GWLP_WNDPROC* = -4
+ ICON_SMALL* = 0
+ ICON_BIG* = 1
+ IDC_ARROW* = 32512
+ INVALID_HANDLE_VALUE* = cast[pointer](-1)
+ IMAGE_BITMAP* = 0
+ IMAGE_ICON* = 1
+ # LR_LOADFROMFILE* = 16
+ OBJ_BRUSH* = 2
+ PM_REMOVE* = 1
+ SB_HORZ* = 0
+ SB_THUMBPOSITION* = 4
+ SB_THUMBTRACK* = 5
+ SB_VERT* = 1
+ SB_LINELEFT* = 0
+ SB_LINERIGHT* = 1
+ SB_PAGELEFT* = 2
+ SB_PAGERIGHT* = 3
+ SIF_ALL* = 23
+ SM_CXVSCROLL* = 2
+ SPI_GETWORKAREA* = 0x0030
+ SPI_SETKEYBOARDCUES* = 0x100B
+ SS_CENTERIMAGE* = 0x00000200
+ SW_HIDE* = 0
+ SW_SHOW* = 5
+ SWP_NOMOVE* = 2
+ SWP_NOSIZE* = 1
+ VK_CONTROL* = 17
+ VK_MENU* = 18
+ WM_ACTIVATE* = 0x0006
+ WM_CHANGEUISTATE* = 0x0127
+ WM_CHAR* = 258
+ WM_CLOSE* = 16
+ WM_COMMAND* = 273
+ WM_DROPFILES* = 563
+ WM_ERASEBKGND* = 20
+ WM_HSCROLL* = 276
+ WM_KEYDOWN* = 256
+ WM_KEYUP* = 257
+ WM_LBUTTONDOWN* = 0x0201
+ WM_LBUTTONUP* = 0x0202
+ WM_MBUTTONDOWN* = 0x0207
+ WM_MBUTTONUP* = 0x0208
+ # WM_NCLBUTTONDOWN* = 161
+ # WM_NCLBUTTONUP* = 162
+ WM_MOUSEWHEEL* = 0x020A
+ WM_MOVE* = 3
+ WM_NEXTDLGCTL* = 0x0028
+ WM_PAINT* = 15
+ WM_RBUTTONDOWN* = 0x0204
+ WM_RBUTTONUP* = 0x0205
+ WM_SETFOCUS* = 0x0007
+ WM_SETFONT* = 48
+ WM_SIZE* = 5
+ WM_VSCROLL* = 277
+ WM_SETICON* = 128
+ WM_SYSKEYDOWN* = 260
+ WM_SYSCOMMAND* = 274
+ WM_CTLCOLOREDIT* = 307
+ WS_CLIPCHILDREN* = 0x02000000
+ WS_CAPTION* = 0x00C00000
+ WS_CHILD* = 0x40000000
+ WS_EX_CLIENTEDGE* = 0x00000200
+ WS_GROUP* = 0x00020000
+ WS_HSCROLL* = 0x00100000
+ WS_OVERLAPPEDWINDOW* = 0x00CF0000
+ WS_SYSMENU* = 0x00080000
+ WS_TABSTOP* = 0x00010000
+ WS_THICKFRAME* = 0x00040000
+ WS_VSCROLL* = 0x00200000
+ # DT_CALCRECT* = 1024
+ # OBJ_FONT* = 6
+ # SM_XVIRTUALSCREEN* = 76
+ # WC_LISTVIEWW* = "SysListView32"
+ # WC_TABCONTROLW* = "SysTabControl32"
+ # WC_TREEVIEWW* = "SysTreeView32"
+ # WM_CTLCOLORSTATIC* = 312
+ # WS_EX_TOOLWINDOW* = 0x00000080
+ OPAQUE* = 2
+ TRANSPARENT* = 1
+ PS_SOLID* = 0
+ PS_DASH* = 1
+ PS_DOT* = 2
+ PS_DASHDOT* = 3
+ PS_DASHDOTDOT* = 4
+ PS_NULL* = 5
+ PS_USERSTYLE* = 7
+ PS_INSIDEFRAME* = 6
+ OFN_ALLOWMULTISELECT* = 0x00000200
+ OFN_EXPLORER* = 0x00080000
+ OFN_FILEMUSTEXIST* = 0x00001000
+ OFN_OVERWRITEPROMPT* = 0x00000002
+ # UnitWorld* = 0
+ # UnitDisplay* = 1
+ UnitPixel* = 2
+ # UnitPoint* = 3
+ # UnitInch* = 4
+ # UnitDocument* = 5
+ # UnitMillimeter* = 6
+
+# ----------------------------------------------------------------------------------------
+# Types
+# ----------------------------------------------------------------------------------------
+
+type
+ WndClassEx* = object
+ cbSize*: int32
+ style*: int32
+ lpfnWndProc*: pointer
+ cbClsExtra*: int32
+ cbWndExtra*: int32
+ hInstance*: pointer
+ hIcon*: pointer
+ hCursor*: pointer
+ hbrBackground*: pointer
+ lpszMenuName*: cstring
+ lpszClassName*: cstring
+ hIconSm*: pointer
+
+ Point* = object
+ x*: int32
+ y*: int32
+
+ Size* = object
+ cx*: int32
+ cy*: int32
+
+ Rect* = object
+ left*: int32
+ top*: int32
+ right*: int32
+ bottom*: int32
+
+ RectF* = object
+ x*: cfloat
+ y*: cfloat
+ width*: cfloat
+ height*: cfloat
+
+ RGB32* = object
+ red*: byte
+ green*: byte
+ blue*: byte
+ unused: byte
+
+ ARGB* = object
+ blue*: byte
+ green*: byte
+ red*: byte
+ alpha*: byte
+
+ Msg* = object
+ hwnd*: pointer
+ message*: int32
+ wParam*: int
+ lParam*: int
+ time*: int32
+ pt*: Point
+
+ ActCtx* = object
+ cbSize*: int32
+ dwFlags*: int32
+ lpSource*: cstring
+ wProcessorArchitecture*: int16
+ wLangId*: int16
+ lpAssemblyDirectory*: cstring
+ lpResourceName*: cstring
+ lpApplicationName*: cstring
+ hModule*: pointer
+
+ ScrollInfo* = object
+ cbSize*: int32
+ fMask*: int32
+ nMin*: int32
+ nMax*: int32
+ nPage*: int32
+ nPos*: int32
+ nTrackPos*: int32
+
+ MonitorInfo * = object
+ cbSize*: int32
+ rcMonitor*: Rect
+ rcWork*: Rect
+ dwFlags*: int32
+
+ PaintStruct* = array[68, byte]
+
+ KeyState* = array[256, byte]
+
+ GdiplusStartupInput* = object
+ GdiplusVersion*: int32
+ DebugEventCallback*: pointer
+ SuppressBackgroundThread*: bool
+ SuppressExternalCodecs*: bool
+
+ OpenFileName* = object
+ lStructSize*: int32
+ hwndOwner*: pointer
+ hInstance*: pointer
+ lpstrFilter*: cstring
+ lpstrCustomFilter*: cstring
+ nMaxCustFilter*: int32
+ nFilterIndex*: int32
+ lpstrFile*: cstring
+ nMaxFile*: int32
+ lpstrFileTitle*: cstring
+ nMaxFileTitle*: int32
+ lpstrInitialDir*: cstring
+ lpstrTitle*: cstring
+ Flags*: int32
+ nFileOffset*: int16
+ nFileExtension*: int16
+ lpstrDefExt*: cstring
+ lCustData*: pointer
+ lpfnHook*: pointer
+ lpTemplateName*: cstring
+ # pvReserved: pointer
+ # dwReserved: int32
+ # FlagsEx*: int32
+
+ GUID * = object
+ Data1*: int32
+ Data2*: int32
+ Data3*: int32
+ Data4*: int32
+
+
+# ----------------------------------------------------------------------------------------
+# Replacement for Windows Macros
+# ----------------------------------------------------------------------------------------
+
+import math
+
+proc loWord*(param: pointer): int = cast[int](param) and 0x0000FFFF
+
+proc hiWord*(param: pointer): int =
+ result = (cast[int](param) shr 16) and 0xFFFF
+ if result > 2^15:
+ result = result - 2^16
+
+
+# ----------------------------------------------------------------------------------------
+# Kernel32 Procs
+# ----------------------------------------------------------------------------------------
+
+proc LoadLibraryA*(lpFileName: cstring): pointer {.importc: "LoadLibraryA", libKernel32.}
+# proc GetModuleHandleA*(lpModuleName: cstring): pointer {.importc: "GetModuleHandleA", libKernel32.}
+proc GetLastError*(): int {.importc: "GetLastError", libKernel32.}
+proc CreateActCtxA*(pActCtx: pointer): pointer {.importc: "CreateActCtxA", libKernel32.}
+proc ActivateActCtx*(pActCtx, lpCookie: pointer): bool {.importc: "ActivateActCtx", libKernel32.}
+proc GetSystemDirectoryA*(lpBuffer: pointer, uSize: int32): int32 {.importc: "GetSystemDirectoryA", libKernel32.}
+proc MultiByteToWideChar*(CodePage, dwFlags: int32, lpMultiByteStr: cstring, cbMultiByte: int32, lpWideCharStr: cstring, cchWideChar: int32): int32 {.importc: "MultiByteToWideChar", libKernel32.}
+proc WideCharToMultiByte*(CodePage, dwFlags: int32, lpWideCharStr: cstring, cchWideChar: int32, lpMultiByteStr: cstring, cbMultiByte: int32, lpDefaultChar: cstring, lpUsedDefaultChar: pointer): int32 {.importc: "WideCharToMultiByte", libKernel32.}
+
+proc MessageBoxA*(hWnd: pointer, lpText, lpCaption: cstring, uType: int) {.importc: "MessageBoxA", libUser32.}
+proc MessageBoxW*(hWnd: pointer, lpText, lpCaption: cstring, uType: int) {.importc: "MessageBoxW", libUser32.}
+
+proc RegisterClassExA*(lpwcx: var WndClassEx): int16 {.importc: "RegisterClassExA", libUser32.}
+proc CreateWindowExA*(dwExStyle: int32, lpClassName, lpWindowName: cstring, dwStyle: int32, x, y, nWidth, nHeight: int, hWndParent, hMenu, hInstance, lpParam: pointer): pointer {.importc: "CreateWindowExA", libUser32.}
+proc DestroyWindow*(hWnd: pointer): bool {.importc: "DestroyWindow", libUser32.}
+proc ShowWindow*(hWnd: pointer, nCmdShow: int32): bool {.importc: "ShowWindow", libUser32.}
+proc EnableWindow*(hWnd: pointer, bEnable: bool): bool {.importc: "EnableWindow", libUser32.}
+proc DefWindowProcA*(hWnd: pointer, uMsg: int, wParam, lParam: pointer): pointer {.importc: "DefWindowProcA", libUser32.}
+proc GetMessageA*(lpMsg, hWnd: pointer, wMsgFilterMin, wMsgFilterMax: int32): bool {.importc: "GetMessageA", libUser32.}
+proc PeekMessageA*(lpMsg, hWnd: pointer, wMsgFilterMin, wMsgFilterMax, wRemoveMsg: int32): bool {.importc: "PeekMessageA", libUser32.}
+proc TranslateMessage*(lpMsg: pointer): bool {.importc: "TranslateMessage", libUser32.}
+proc DispatchMessageA*(lpMsg: pointer): pointer {.importc: "DispatchMessageA", libUser32.}
+proc SetParent*(hWndChild, hWndNewParent: pointer): pointer {.importc: "SetParent", libUser32.}
+proc SetWindowLongA*(hWnd: pointer, nIndex, dwNewLong: int32): int32 {.importc: "SetWindowLongA", libUser32.}
+proc GetWindowLongA*(hWnd: pointer, nIndex: int32): int32 {.importc: "GetWindowLongA", libUser32.}
+proc SetWindowLongPtrW*(hWnd: pointer, nIndex: int32, dwNewLong: pointer): pointer {.importc: "SetWindowLongPtrW", libUser32.}
+proc GetWindowLongPtrW*(hWnd: pointer, nIndex: int32): pointer {.importc: "GetWindowLongPtrW", libUser32.}
+proc SetWindowTextA*(hWnd: pointer, lpString: cstring): bool {.importc: "SetWindowTextA", libUser32.}
+proc SetWindowTextW*(hWnd: pointer, lpString: cstring): bool {.importc: "SetWindowTextW", libUser32.}
+# proc GetWindowTextA*(hWnd: pointer, lpString: cstring, nMaxCount: int32): int32 {.importc: "GetWindowTextA", libUser32.}
+proc GetWindowTextW*(hWnd: pointer, lpString: cstring, nMaxCount: int32): int32 {.importc: "GetWindowTextW", libUser32.}
+# proc GetWindowTextLengthA*(hWnd: pointer): int32 {.importc: "GetWindowTextLengthA", libUser32.}
+proc GetWindowTextLengthW*(hWnd: pointer): int32 {.importc: "GetWindowTextLengthW", libUser32.}
+proc UpdateWindow*(hWnd: pointer): bool {.importc: "UpdateWindow", libUser32.}
+proc SetWindowPos*(wnd, hWndInsertAfter: pointer, x, y, cx, cy: int32, uFlags: int): bool {.importc: "SetWindowPos", libUser32.}
+proc MoveWindow*(wnd: pointer, x, y, nWidth, nHeight: int32, bRepaint: bool): bool {.importc: "MoveWindow", libUser32.}
+proc SetFocus*(hWnd: pointer): pointer {.importc: "SetFocus", libUser32.}
+proc GetWindowRect*(wnd: pointer, lpRect: var Rect): bool {.importc: "GetWindowRect", libUser32.}
+proc GetClientRect*(wnd: pointer, lpRect: var Rect): bool {.importc: "GetClientRect", libUser32.}
+proc BeginPaint*(hWnd: pointer, lpPaint: var PaintStruct): pointer {.importc: "BeginPaint", libUser32.}
+proc EndPaint*(hWnd: pointer, lpPaint: var PaintStruct): bool {.importc: "EndPaint", libUser32.}
+proc SendMessageA*(hWnd: pointer, msg: int32, wParam, lParam: pointer): pointer {.importc: "SendMessageA", libUser32.}
+# proc SendMessageW*(hWnd: pointer, msg: int32, wParam, lParam: pointer): pointer {.importc: "SendMessageW", libUser32.}
+proc PostMessageA*(hWnd: pointer, msg: int32, wParam, lParam: pointer): pointer {.importc: "PostMessageA", libUser32.}
+proc GetSysColor*(nIndex: int32): RGB32 {.importc: "GetSysColor", libUser32.}
+proc SetClassLongPtrA*(hWnd: pointer, nIndex: int32, dwNewLong: pointer): pointer {.importc: "SetClassLongPtrA", libUser32.}
+proc InvalidateRect*(hWnd: pointer, lpRect: ref Rect, bErase: bool): bool {.importc: "InvalidateRect", libUser32.}
+proc PostQuitMessage*(nExitCode: int32) {.importc: "PostQuitMessage", libUser32.}
+proc GetDesktopWindow*(): pointer {.importc: "GetDesktopWindow", libUser32.}
+proc SystemParametersInfoA*(uiAction, uiParam: int32, pvParam: pointer, fWinIni: int32): bool {.importc: "SystemParametersInfoA", libUser32.}
+proc ClientToScreen*(hWnd: pointer, lpPoint: var Point): bool {.importc: "ClientToScreen", libUser32.}
+proc AdjustWindowRect*(lpRect: var Rect, dwStyle: int32, bMenu: bool): bool {.importc: "AdjustWindowRect", libUser32.}
+proc LoadCursorA*(hInstance: pointer, lpCursorName: cstring): pointer {.importc: "LoadCursorA", libUser32.}
+proc SetScrollInfo*(hWnd: pointer, fnBar: int32, lpsi: var ScrollInfo, fRedraw: bool): int32 {.importc: "SetScrollInfo", libUser32.}
+proc GetMonitorInfoA*(hMonitor: pointer, lpmi: var MonitorInfo): bool {.importc: "GetMonitorInfoA", libUser32.}
+proc MonitorFromRect*(lprc: var Rect, dwFlags: int32): pointer {.importc: "MonitorFromRect", libUser32.}
+proc GetSystemMetrics*(nIndex: int32): int32 {.importc: "GetSystemMetrics", libUser32.}
+proc CallWindowProcW*(lpPrevWndFunc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.importc: "CallWindowProcW", libUser32.}
+proc IsDialogMessageW*(hDlg, lpMsg: pointer): bool {.importc: "IsDialogMessageW", libUser32.}
+proc GetNextDlgTabItem*(hDlg, hCtl: pointer, bPrevious: bool): pointer {.importc: "GetNextDlgTabItem", libUser32.}
+proc GetParent*(hWnd: pointer): pointer {.importc: "GetParent", libUser32.}
+proc GetDC*(hWnd: pointer): pointer {.importc: "GetDC", libUser32.}
+# proc DrawTextW*(hdc: pointer, lpchText: cstring, nCount: int32, lpRect: var Rect, uFormat: int32): int32 {.importc: "DrawTextW", libUser32.}
+proc GetKeyboardState*(lpKeyState: var KeyState): bool {.importc: "GetKeyboardState", libUser32.}
+proc ToUnicode*(wVirtKey, wScanCode: int32, lpKeyState: var KeyState, pwszBuff: cstring, cchBuff, wFlags: int32): int32 {.importc: "ToUnicode", libUser32.}
+proc ShowScrollBar*(hWnd: pointer, wBar: int32, bShow: bool): bool {.importc: "ShowScrollBar", libUser32.}
+proc LoadImageW*(hinst: pointer, lpszName: cstring, uType, cxDesired, cyDesired, fuLoad: int32): int32 {.importc: "LoadImageW", libUser32.}
+proc SetTimer*(hWnd, nIDEvent: pointer, uElapse: int32, lpTimerFunc: pointer): pointer {.importc: "SetTimer", libUser32.}
+proc KillTimer*(hWnd, nIDEvent: pointer): bool {.importc: "KillTimer", libUser32.}
+proc FillRect*(hDC: pointer, lprc: var Rect, hbr: pointer): int32 {.importc: "FillRect", libUser32.}
+proc FrameRect*(hDC: pointer, lprc: var Rect, hbr: pointer): int32 {.importc: "FrameRect", libUser32.}
+proc GetKeyState*(nVirtKey: int32): int16 {.importc: "GetKeyState", libUser32.}
+
+
+# ----------------------------------------------------------------------------------------
+# GDI Procs
+# ----------------------------------------------------------------------------------------
+
+proc DeleteDC*(hdc: pointer): bool {.importc: "DeleteDC", libGdi32.}
+proc DeleteObject*(hObject: pointer): bool {.importc: "DeleteObject", libGdi32.}
+proc GetCurrentObject*(hdc: pointer, uObjectType: int32): pointer {.importc: "GetCurrentObject", libGdi32.}
+proc SelectObject*(hdc, hgdiobj: pointer): pointer {.importc: "SelectObject", libGdi32.}
+proc TextOutW*(hdc: pointer, nXStart, nYStart: int32, lpString: cstring, cchString: int32): bool {.importc: "TextOutW", libGdi32.}
+proc CreateSolidBrush*(crColor: RGB32): pointer {.importc: "CreateSolidBrush", libGdi32.}
+proc CreatePen*(fnPenStyle, nWidth: int32, crColor: RGB32): pointer {.importc: "CreatePen", libGdi32.}
+# proc GetStockObject*(fnObject: int32): pointer {.importc: "GetStockObject", libGdi32.}
+proc CreateFontA*(nHeight, nWidth, nEscapement, nOrientation, fnWeight, fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision, fdwClipPrecision, fdwQuality, fdwPitchAndFamily: int32, lpszFace: cstring): pointer {.importc: "CreateFontA", libGdi32.}
+proc GetTextExtentPoint32W*(hdc: pointer, lpString: cstring, c: int32, lpSize: var Size): bool {.importc: "GetTextExtentPoint32W", libGdi32.}
+proc SetBkMode*(hdc: pointer, iBkMode: int32): int32 {.importc: "SetBkMode", libGdi32.}
+proc SetTextColor*(hdc: pointer, crColor: RGB32): RGB32 {.importc: "SetTextColor", libGdi32.}
+proc SetBkColor*(hdc: pointer, crColor: RGB32): RGB32 {.importc: "SetBkColor", libGdi32.}
+proc MoveToEx*(hdc: pointer, x, y: int32, lpPoint: pointer): bool {.importc: "MoveToEx", libGdi32.}
+proc LineTo*(hdc: pointer, nXEnd, nYEnd: int): bool {.importc: "LineTo", libGdi32.}
+proc CreateCompatibleDC*(hdc: pointer): pointer {.importc: "CreateCompatibleDC", libGdi32.}
+proc SetPixel*(hdc: pointer, x, y: int32, crColor: RGB32): int32 {.importc: "SetPixel", libGdi32.}
+# proc BitBlt*(hdcDest: pointer, nXDest, nYDest, nWidth, nHeight: int32, hdcSrc: pointer, nXSrc, nYSrc, dwRop: int32): bool {.importc: "BitBlt", libGdi32.}
+
+
+# ----------------------------------------------------------------------------------------
+# GDI+ Procs
+# ----------------------------------------------------------------------------------------
+
+proc GdiplusStartup*(token: var pointer, input: var GdiplusStartupInput, output: pointer): int32 {.importc: "GdiplusStartup", libGdiplus.}
+proc GdipCreateBitmapFromFile*(filename: cstring, bitmap: var pointer): int32 {.importc: "GdipCreateBitmapFromFile", libGdiplus.}
+# proc GdipLoadImageFromFile*(filename: cstring, image: var pointer): int32 {.importc: "GdipLoadImageFromFile", libGdiplus.}
+proc GdipGetHicon*(bitmap: pointer, hicon: var pointer): int32 {.importc: "GdipCreateHICONFromBitmap", libGdiplus.}
+proc GdipCreateFromHDC*(hdc: pointer, graphics: var pointer): int32 {.importc: "GdipCreateFromHDC", libGdiplus.}
+proc GdipDeleteGraphics*(graphics: pointer): int32 {.importc: "GdipDeleteGraphics", libGdiplus.}
+proc GdipDrawImageRectI*(graphics, image: pointer, x, y, width, height: int32): int32 {.importc: "GdipDrawImageRectI", libGdiplus.}
+proc GdipGetImageWidth*(image: pointer, width: var int32): int32 {.importc: "GdipGetImageWidth", libGdiplus.}
+proc GdipGetImageHeight*(image: pointer, height: var int32): int32 {.importc: "GdipGetImageHeight", libGdiplus.}
+# proc GdipGetImageDimension*(image: pointer, width, height: var float): int32 {.importc: "GdipGetImageDimension", libGdiplus.}
+proc GdipCreateBitmapFromGraphics*(width, height: int32, target: pointer, bitmap: var pointer): int32 {.importc: "GdipCreateBitmapFromGraphics", libGdiplus.}
+proc GdipBitmapSetPixel*(bitmap: pointer, x, y: int32, color: ARGB): int32 {.importc: "GdipBitmapSetPixel", libGdiplus.}
+proc GdipSaveImageToFile*(image: pointer, filename: cstring, clsidEncoder, encoderParams: pointer): int32 {.importc: "GdipSaveImageToFile", libGdiplus.}
+# proc GdipGetImageEncodersSize*(numEncoders, size: var int32): int32 {.importc: "GdipGetImageEncodersSize", libGdiplus.}
+# proc GdipGetImageEncoders*(numEncoders, size: int32, encoders: pointer): int32 {.importc: "GdipGetImageEncoders", libGdiplus.}
+proc GdipGetImageGraphicsContext*(image: pointer, graphics: var pointer): int32 {.importc: "GdipGetImageGraphicsContext", libGdiplus.} # does not exist
+proc GdipDisposeImage*(image: pointer): int32 {.importc: "GdipDisposeImage", libGdiplus.}
+proc GdipFillRectangleI*(graphics, brush: pointer, x, y, width, height: int32): int32 {.importc: "GdipFillRectangleI", libGdiplus.}
+proc GdipDrawRectangleI*(graphics, pen: pointer, x, y, width, height: int32): int32 {.importc: "GdipDrawRectangleI", libGdiplus.}
+proc GdipDrawLineI*(graphics, pen: pointer, x1, y1, x2, y2: int32): int32 {.importc: "GdipDrawLineI", libGdiplus.}
+proc GdipCreateSolidFill*(color: ARGB, brush: var pointer): int32 {.importc: "GdipCreateSolidFill", libGdiplus.}
+proc GdipDeleteBrush*(brush: pointer): int32 {.importc: "GdipDeleteBrush", libGdiplus.}
+proc GdipCreatePen1*(color: ARGB, width: float, unit: int32, pen: var pointer): int32 {.importc: "GdipCreatePen1", libGdiplus.}
+proc GdipDeletePen*(pen: pointer): int32 {.importc: "GdipDeletePen", libGdiplus.}
+proc GdipDrawString*(graphics: pointer, `string`: cstring, length: int32, font: pointer, layoutRect: var RectF, stringFormat, brush: pointer): int32 {.importc: "GdipDrawString", libGdiplus.}
+proc GdipMeasureString*(graphics: pointer, `string`: cstring, length: int32, font: pointer, layoutRect: var RectF, stringFormat: pointer, boundingBox: var RectF, codepointsFitted, linesFilled: pointer): int32 {.importc: "GdipMeasureString", libGdiplus.}
+proc GdipCreateFont*(fontFamily: pointer, emSize: cfloat, style, unit: int32, font: var pointer): int32 {.importc: "GdipCreateFont", libGdiplus.}
+proc GdipDeleteFont*(font: pointer): int32 {.importc: "GdipDeleteFont", libGdiplus.}
+proc GdipCreateFontFamilyFromName*(name: cstring, fontCollection: pointer, fontFamily: var pointer): int32 {.importc: "GdipCreateFontFamilyFromName", libGdiplus.}
+proc GdipDeleteFontFamily*(fontFamily: pointer): int32 {.importc: "GdipDeleteFontFamily", libGdiplus.}
+
+
+# ----------------------------------------------------------------------------------------
+# Shell32 Procs
+# ----------------------------------------------------------------------------------------
+
+proc DragAcceptFiles*(hWnd: pointer, fAccept: bool) {.importc: "DragAcceptFiles", libShell32.}
+proc DragQueryFileW*(hDrop: pointer, iFile: uint32, lpszFile: cstring, cch: int32): int32 {.importc: "DragQueryFileW", libShell32.}
+proc DragFinish*(hDrop: pointer) {.importc: "DragFinish", libShell32.}
+
+
+# ----------------------------------------------------------------------------------------
+# Comdlg32 Procs
+# ----------------------------------------------------------------------------------------
+
+proc CommDlgExtendedError*(): int32 {.importc: "CommDlgExtendedError", libComdlg32.}
+proc GetOpenFileNameW*(lpofn: var OpenFileName): bool {.importc: "GetOpenFileNameW", libComdlg32.}
+proc GetSaveFileNameW*(lpofn: var OpenFileName): bool {.importc: "GetSaveFileNameW", libComdlg32.}
+